类装饰器与函数装饰器
在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 个回答
7
其实没有“两个方法”。只有一种方法(定义一个可调用的对象),或者说有很多种方法可以在Python中创建一个可调用的对象(它可以是其他对象的方法、一个lambda表达式的结果、一个“部分”对象,或者任何可以被调用的东西)。
定义函数是创建可调用对象最简单的方法,而且作为最简单的方法,在大多数情况下也是最好的。使用类可以让你有更多的选择来优雅地处理更复杂的情况(即使在最简单的情况下,看起来也很优雅),但它的作用可能不是那么明显。
21
当你想创建一个可以调用的东西,并且这个东西又能返回另一个可以调用的东西时,使用函数的方法会更简单、更省事。主要有两个不同之处:
- 使用函数的方法可以自动适用于类中的方法,而如果你用类的方法,就需要了解一些描述符的知识,还得定义一个
__get__
方法。 - 类的方法更容易保持状态。你可以使用闭包,特别是在 Python 3 中,但一般来说,使用类的方法更适合保持状态。
此外,使用函数的方法可以在修改或存储后返回原始函数。
不过,装饰器可以返回的不仅仅是一个可调用的东西,或者可以返回一些更多的东西。使用类的话,你可以:
- 给被装饰的可调用对象添加方法和属性,或者对它们进行操作(这可就麻烦了)。
- 创建描述符,这些描述符在放到类中时会以特殊的方式工作(比如
classmethod
和property
)。 - 使用继承来实现相似但又不同的装饰器。
如果你有疑问,可以问自己:你希望你的装饰器返回一个完全像函数那样工作的函数吗?那就用返回函数的方式。如果你希望你的装饰器返回一个自定义的对象,做一些不同于函数的事情,那就创建一个类并把它当作装饰器使用。
20
如果你想在装饰器中保持状态,最好使用一个类。
举个例子,这样做是行不通的:
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)