繁体   English   中英

Python 基于类的装饰器,带有可以装饰方法或函数的参数

[英]Python Class Based Decorator with parameters that can decorate a method or a function

我见过很多 Python 装饰器的例子,它们是:

  • 函数风格装饰器(包装函数)
  • 类样式装饰器(实现__init____get____call__
  • 不带参数的装饰器
  • 带参数的装饰器
  • “方法友好”的装饰器(即可以装饰类中的方法)
  • “函数友好”的装饰器(可以装饰一个普通的函数
  • 可以装饰方法和函数的装饰器

但是我从未见过可以完成上述所有操作的单个示例,而且我无法从特定问题的各种答案(例如this onethis onethis one (具有最佳答案之一)进行综合)我曾经在 SO) ) 上看到过如何将上述所有内容结合起来。

我想要的是一个基于类的装饰器,它可以装饰一个方法或一个函数,并且至少需要一个额外的参数 即,以便以下工作:

class MyDecorator(object):
    def __init__(self, fn, argument):
        self.fn = fn
        self.arg = argument

    def __get__(self, ....):
        # voodoo magic for handling distinction between method and function here

    def __call__(self, *args, *kwargs):
        print "In my decorator before call, with arg %s" % self.arg
        self.fn(*args, **kwargs)
        print "In my decorator after call, with arg %s" % self.arg


class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"


@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"

some_other_function()
Foo().bar()

我希望看到:

In my decorator before call, with arg some other func!
in some other function!
In my decorator after call, with arg some other func!
In my decorator before call, with arg foo baby!
in bar!
In my decorator after call, with arg foo baby!

编辑:如果重要的话,我使用的是 Python 2.7。

你不需要弄乱描述符。 __call__()方法中创建一个包装函数并返回它就足够了。 标准 Python 函数始终可以充当方法或函数,具体取决于上下文:

class MyDecorator(object):
    def __init__(self, argument):
        self.arg = argument

    def __call__(self, fn):
        @functools.wraps(fn)
        def decorated(*args, **kwargs):
            print "In my decorator before call, with arg %s" % self.arg
            result = fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % self.arg
            return result
        return decorated

关于当这个装饰器这样使用时发生了什么的一些解释:

@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"

第一行创建了一个MyDecorator的实例并传递了"some other func!" 作为__init__()的参数。 让我们称这个实例为my_decorator 接下来,未装饰的函数对象——让我们称之为bare_func被创建并传递给装饰器实例,因此my_decorator(bare_func)被执行。 这将调用MyDecorator.__call__() ,它将创建并返回一个包装函数。 最后这个包装函数被分配给名称some_other_function

你缺少一个级别。

考虑代码

class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"

它与此代码相同

class Foo(object):
    def bar(self):
        print "in bar!"
    bar = MyDecorator("foo baby!")(bar)

所以MyDecorator.__init__被调用为"foo baby!" 然后使用函数bar调用MyDecorator对象。

也许你的意思是实现更像

import functools

def MyDecorator(argument):
    class _MyDecorator(object):
        def __init__(self, fn):
            self.fn = fn

        def __get__(self, obj, type=None):
            return functools.partial(self, obj)

        def __call__(self, *args, **kwargs):
            print "In my decorator before call, with arg %s" % argument
            self.fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % argument

    return _MyDecorator

在您的装饰器类型列表中,您错过了可能带参数也可能不带参数的装饰器。 我认为这个例子涵盖了除“函数风格装饰器(包装函数)”之外的所有类型

class MyDecorator(object):

    def __init__(self, argument):
        if hasattr('argument', '__call__'):
            self.fn = argument
            self.argument = 'default foo baby'
        else:
            self.argument = argument

    def __get__(self, obj, type=None):
        return functools.partial(self, obj)

    def __call__(self, *args, **kwargs):
        if not hasattr(self, 'fn'):
            self.fn = args[0]
            return self
        print "In my decorator before call, with arg %s" % self.argument
        self.fn(*args, **kwargs)
        print "In my decorator after call, with arg %s" % self.argument


class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"

class Bar(object):
    @MyDecorator
    def bar(self):
        print "in bar!"

@MyDecorator
def add(a, b):
    print a + b

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM