装饰器类和装饰器函数之间的区别

时间:2011-01-10 18:55:10

标签: python syntax decorator

我想这就是他们被调用的方式,但我会举例说明。

装饰者类:

class decorator(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print 'something'
        self.func(*args, **kwargs)

装饰功能:

def decorator(func):
    def wrapper(*args, **kwargs):
        print 'something'
        return func(*args, **kwargs)
    return wrapper

使用其中一种只是品味问题?有什么实际区别吗?

3 个答案:

答案 0 :(得分:13)

如果您可以编写一个函数来实现装饰器,那么您应该更喜欢它。但并非所有装饰器都可以轻松编写为函数 - 例如,当您想要存储某些内部状态时。

class counted(object):
    """ counts how often a function is called """
    def __init__(self, func):
        self.func = func
        self.counter = 0

    def __call__(self, *args, **kwargs):
        self.counter += 1
        return self.func(*args, **kwargs)


@counted
def something():
    pass

something()
print something.counter

我见过人们(包括我自己)经历了荒谬的努力,只能用功能来编写装饰器。我仍然不知道为什么,一个班级的开销通常完全可以忽略不计。

答案 1 :(得分:3)

这通常只是品味问题。大多数Python程序使用duck typing并不关心他们调用的东西是函数还是其他类型的实例,只要它是可调用的。任何__call__()方法都可以调用。

使用函数式装饰器有一些优点:

  • 当装饰者没有返回包装函数时(例如,在对其执行某些操作后返回原始函数,例如设置属性),会更清晰。

  • 无需显式保存对原始函数的引用,因为这是由闭包完成的。

  • 大多数帮助您制作装饰器的工具,例如functools.wraps()或Michele Simionato的保留签名decorator模块,都可以使用函数式装饰器。

  • 可能有一些程序在那里不使用duck typing,但实际上期望一个函数类型,所以返回一个函数来替换一个函数在理论上是“更安全的”。

由于这些原因,我大多数时候都使用函数式装饰器。然而,作为一个反例,here是一个最近的例子,其中类风格的装饰对我来说更自然。

答案 2 :(得分:0)

提议的类装饰器实现与函数实现略有不同:它会在方法上失败

class Decorator(object):
def __init__(self, func):
    self.func = func

def __call__(self, *args, **kwargs):
    print('something')
    self.func(*args, **kwargs)

class A:
    @Decorator
    def mymethod(self):
        print("method")

A().mymethod()

将提高 TypeError: mymethod() missing 1 required positional argument: 'self'

要添加对方法的支持,您需要实现 __get__

import types
class Decorator2(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('something')
        self.func(*args, **kwargs)

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return types.MethodType(self, instance)

class B:

    @Decorator2
    def mymethod(self):
        print("method")

B().mymethod()

会输出

B 类:...

something
method

它起作用的原因是当您访问 B().mymethod 时,首先调用 __get__ 并提供绑定方法。然后 __call__ 被调用

总而言之,如果您定义了 __get__,类和函数实现可以以相同的方式使用。有关详细信息,请参阅 Python 食谱配方 9.9。