装饰器类与装饰器函数的区别

13 投票
3 回答
4451 浏览
提问于 2025-04-16 09:44

我想这就是它们的叫法,不过我会给一些例子以防万一。

装饰器类:

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 个回答

2

这个提到的类装饰器的实现和函数的实现有一点小区别:它在处理方法时会出错。

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()

这样做会输出

class B:...

something
method

之所以这样能工作,是因为当你访问B().mymethod时,首先会调用__get__,它会提供一个绑定的方法。然后再调用__call__

总结一下,只要你定义了__get__,类和函数的实现就可以用同样的方式来使用。想了解更多信息,可以查看Python Cookbook的第9.9个食谱。

3

这主要是个人喜好的问题。大多数Python程序使用一种叫做“鸭子类型”的方式,它们并不在乎你调用的东西是一个函数还是其他类型的实例,只要它是可以被调用的就行。而任何有__call__()方法的东西都是可以被调用的。

使用函数风格的装饰器有几个好处:

  • 当你的装饰器不返回一个包装函数时(也就是说,它在做一些事情后返回原始函数,比如设置一个属性),代码看起来会更简洁。

  • 不需要明确保存原始函数的引用,因为闭包会自动处理这个问题。

  • 大多数帮助你创建装饰器的工具,比如functools.wraps()或者Michele Simionato的保留签名的decorator模块,都是与函数风格的装饰器兼容的。

  • 虽然可能有一些程序不使用鸭子类型,而是期待一个函数类型,所以返回一个函数来替代另一个函数在理论上是“更安全”的。

基于这些原因,我大多数时候使用函数风格的装饰器。不过,作为一个反例,这里有一个最近的例子,对我来说类风格的装饰器更自然。

14

如果你能写一个函数来实现你的装饰器,那就最好用函数来做。不过,并不是所有的装饰器都能简单地用函数来写,比如当你需要保存一些内部状态的时候。

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

我见过很多人(包括我自己)为了只用函数来写装饰器而费尽心思。我到现在也不明白为什么,使用类的开销通常是微不足道的。

撰写回答