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

Ada*_*kin 39 python methods arguments function decorator

我见过很多Python装饰器的例子:

  • 函数样式装饰器(包装函数)
  • 类风格装饰(实施__init__,__get____call__)
  • 不带参数的装饰器
  • 带参数的装饰器
  • "方法友好"的装饰器(即可以在类中装饰方法)
  • "函数友好"的装饰器(可以装饰普通函数
  • 可以装饰方法和功能的装饰器

但我从来没有见过一个可以完成上述所有工作的例子,而且我很难从特定问题的各种答案中综合出来(比如这一个,这一个,或者这一个(它有一个最好的答案)我曾见过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()
Run Code Online (Sandbox Code Playgroud)

我希望看到:

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!
Run Code Online (Sandbox Code Playgroud)

编辑:如果重要,我正在使用Python 2.7.

Sve*_*ach 36

你不需要乱用描述符.它足以在__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
            fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % self.arg
        return decorated
Run Code Online (Sandbox Code Playgroud)

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

@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为缺少OP的关键方面是有另一级别的可调用:调用MyDecorator并调用其结果,返回我们存储在类/模块中的对象(稍后会调用多次). (5认同)
  • @MikeGraham:这是完全正确的,我不理解第二级间接. (3认同)

Mik*_*ham 12

你错过了一个级别.

考虑一下代码

class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"
Run Code Online (Sandbox Code Playgroud)

它与此代码完全相同

class Foo(object):
    def bar(self):
        print "in bar!"
    bar = MyDecorator("foo baby!")(bar)
Run Code Online (Sandbox Code Playgroud)

因此MyDecorator.__init__调用,"foo baby!"然后MyDecorator使用该函数调用该对象bar.

也许你的意思是实现更像的东西

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
Run Code Online (Sandbox Code Playgroud)


Vin*_*kal 7

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

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
Run Code Online (Sandbox Code Playgroud)