装饰器类与装饰器函数的区别
我想这就是它们的叫法,不过我会给一些例子以防万一。
装饰器类:
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 个回答
这个提到的类装饰器的实现和函数的实现有一点小区别:它在处理方法时会出错。
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个食谱。
这主要是个人喜好的问题。大多数Python程序使用一种叫做“鸭子类型”的方式,它们并不在乎你调用的东西是一个函数还是其他类型的实例,只要它是可以被调用的就行。而任何有__call__()方法的东西都是可以被调用的。
使用函数风格的装饰器有几个好处:
当你的装饰器不返回一个包装函数时(也就是说,它在做一些事情后返回原始函数,比如设置一个属性),代码看起来会更简洁。
不需要明确保存原始函数的引用,因为闭包会自动处理这个问题。
大多数帮助你创建装饰器的工具,比如
functools.wraps()或者Michele Simionato的保留签名的decorator模块,都是与函数风格的装饰器兼容的。虽然可能有一些程序不使用鸭子类型,而是期待一个函数类型,所以返回一个函数来替代另一个函数在理论上是“更安全”的。
基于这些原因,我大多数时候使用函数风格的装饰器。不过,作为一个反例,这里有一个最近的例子,对我来说类风格的装饰器更自然。
如果你能写一个函数来实现你的装饰器,那就最好用函数来做。不过,并不是所有的装饰器都能简单地用函数来写,比如当你需要保存一些内部状态的时候。
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
我见过很多人(包括我自己)为了只用函数来写装饰器而费尽心思。我到现在也不明白为什么,使用类的开销通常是微不足道的。