类装饰器与函数装饰器

时间:2011-07-13 08:20:00

标签: python decorator

在python中有两种声明装饰器的方法:

基于类

class mydecorator(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *k, **kw):
        # before f actions
        self.f(*k, **kw)
        # after f actions

基于功能

def mydecorator(f):
    def decorator(*k, **kw):
        # before f actions
        f(*k, **kw)
        # after f actions

    return decorator          

这些声明之间有什么区别吗? 在哪些情况下应该使用它们?

5 个答案:

答案 0 :(得分:19)

如果你想在装饰器中保持状态,你应该使用一个类。

例如,这不起作用

def mydecorator(f):
    x = 0 
    def decorator():
        x += 1 # x is a nonlocal name and cant be modified
        return f(x)
    return decorator 

有很多解决方法,但最简单的方法是使用类

class mydecorator(object):
    def __init__(self, f):
        self.f = f
        self.x = 0

    def __call__(self, *k, **kw):
        self.x += 1
        return f(self.x)

答案 1 :(得分:19)

当您创建一个可调用的函数返回另一个可调用函数时,函数方法更容易,更便宜。有两个主要区别:

  1. 函数方法可以自动使用方法,而如果您使用类方法,则必须阅读描述符并定义__get__方法。
  2. 类方法使状态更容易。您可以使用闭包,尤其是在Python 3中,但通常首选类方法来保持状态。
  3. 此外,函数方法允许您在修改或存储原始函数后返回它。

    然而,装饰者可以返回除可调用之外的其他内容或更多而不是可调用的内容。通过课程,您可以:

    1. 将方法和属性添加到已修饰的可调用对象,或对它们执行操作(嗯 - 哦)。
    2. 创建在放入课程时以特殊方式行事的描述符(例如classmethodproperty
    3. 使用继承来实现类似但不同的装饰器。
    4. 如果您有任何疑问,请问自己:您是否希望装饰者返回一个功能完全像函数应该的功能?使用返回函数的函数。您是否希望装饰器返回一个自定义对象,该对象可以执行更多功能或与功能不同的功能?创建一个类并将其用作装饰器。

答案 2 :(得分:6)

事实上,没有“两种方式”。只有一种方法(定义一个可调用对象)或者在python中创建一个可调用对象的方法(它可以是其他对象的方法,lambda表达式的结果,'部分'对象,任何是可调用)。

函数定义是制作可调用对象的最简单方法,并且作为最简单的方法,在大多数情况下可能是最好的。使用类为您提供了更多可能来干净地编写更复杂的案例(即使在最简单的情况下它看起来相当优雅),但它的作用并不明显。

答案 3 :(得分:2)

不,有两种方法可以制作可调用对象。一个是def一个函数,它显然是可调用的。另一种方法是在类中定义一个__call__方法,这将使它的实例可以调用。类本身是可调用的对象。

装饰器只不过是一个可调用对象,意图接受一个函数作为其唯一参数并返回一些可调用的东西。语法如下:

@decorate
def some_function(...):
    ...

只是一种更好的写作方式:

def some_function(...):
    ...

some_function = decorate(some_function)

你给出的基于类的例子不是一个函数,它接受一个函数并返回一个函数,它是一个标准的vanilla decorator,它是一个用一个实例初始化的类,它的实例是可调用的。对我来说,如果你实际上并没有将它作为一个类使用它,这有点奇怪(它是否有其他方法?它的状态是否会发生变化?你是否制作了几个具有由类封装的常见行为的实例?)。但正常使用你的装饰功能并不能说明差异(除非它是一个特别具有侵略性的装饰者),所以对你来说感觉更自然。

答案 4 :(得分:1)

我们来试试吧!

test_class = """
class mydecorator_class(object):
    def __init__(self, f):
        self.f = f

    def __call__(self, *k, **kw):
        # before f actions
        print 'hi class'
        self.f(*k, **kw)
        print 'goodbye class'
        # after f actions

@mydecorator_class
def cls():
    print 'class'

cls()
"""

test_deco = """
def mydecorator_func(f):
    def decorator(*k, **kw):
        # before f actions
        print 'hi function'
        f(*k, **kw)
        print 'goodbye function'
        # after f actions
    return decorator

@mydecorator_func
def fun():
    print 'func'

fun()
"""

if __name__ == "__main__":
    import timeit
    r = timeit.Timer(test_class).timeit(1000)
    r2 = timeit.Timer(test_deco).timeit(1000)
    print r, r2

我有这样的结果:0.0499339103699 0.0824959278107

这意味着类装饰速度快2倍?