Python 类装饰器与函数装饰器
在python中,有两种方法可以声明decorator: 基于类的Python 类装饰器与函数装饰器,python,decorator,Python,Decorator,在python中,有两种方法可以声明decorator: 基于类的 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 d
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
基于功能的
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
这些声明之间有什么区别吗?
在哪种情况下应该使用每种方法?事实上,没有“两种方法”。只有一种方法(定义一个可调用对象)或python中的多种方法可以创建一个可调用对象(它可以是其他对象的方法、lambda表达式的结果、“部分”对象、任何可调用的对象) 函数定义是创建可调用对象的最简单方法,作为最简单的方法,在大多数情况下可能是最好的方法。使用类可以为更复杂的情况(即使在最简单的情况下,它看起来也很优雅)清晰地编写代码,但它的作用并不明显。不,有(不止)两种方法可以生成可调用对象。一种是定义一个函数,它显然是可调用的。另一种方法是在类中定义一个
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法,这将使其实例可调用。类本身是可调用的对象
decorator只不过是一个可调用对象,旨在接受函数作为其唯一参数并返回可调用的内容。以下语法:
@decorate
def some_function(...):
...
这只是一种稍微好一点的写作方式:
def some_function(...):
...
some_function = decorate(some_function)
您给出的基于类的示例不是一个接受函数并返回函数(即bog标准香草装饰器)的函数,而是一个使用可调用实例的函数初始化的类。对我来说,如果你不是把它当作一个类来使用,这有点奇怪(它有其他方法吗?它的状态是否改变?你是否制作了几个实例,这些实例具有类封装的常见行为?)。但是,正常使用修饰函数并不能区分两者之间的区别(除非它是一个特别具有侵入性的修饰器),所以,请做您觉得更自然的事情。让我们测试一下吧
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
这意味着deco级的速度要快2倍 如果要在装饰器中保持状态,应该使用类 例如,这不起作用
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)
当您创建一个可调用函数并返回另一个可调用函数时,函数方法更简单、更便宜。有两个主要区别:
\uuuu get\uu
方法classmethod
,property
)如果您有任何疑问,请扪心自问:您是否希望您的装饰程序返回一个与函数应该返回的功能完全相同的函数?使用返回函数的函数。您希望装饰程序返回一个自定义对象,该对象执行更多的操作还是与函数不同的操作?创建一个类并将其用作装饰器。很有趣,但我想它是特定于实现的。使用其他python实现(Jython、PyPy)甚至不同版本的CPython,您可能会得到完全不同的结果。另外,这可能是运行“mydecorator_func”和类定义代码的时间不同。这只是应用程序的启动时间,通常并不重要。修饰后的函数调用速度在这两种情况下可能是相同的。对于类,您可以使用描述符协议@phant0m:另一方面,对于类,您必须使用描述符协议(否则您的装饰程序将无法处理方法)。@Rosh:嗯,是的,您在这里有一个非常强大的点:)+1令人震惊的是,没有其他人注意到方法问题+我有一个非常完整的答案。晚到派对,但这可能是公认的答案。“函数方法自动与方法一起工作”,这是一个显著的区别。非常感谢。我认为最简单的方法是说
非局部x;x+=1
。或者这在低于3的Python版本中不起作用是的,看起来Python3中引入了非本地的。更多关于方法修饰符限制的信息可以在这里找到答案。由多个模块使用的有状态修饰符的使用取决于使用它的其他模块在程序其他地方导入的顺序,包括在完全不相关的模块中!所以我的第一条经验法则是,如果你想让装饰师保持状态,“停下来想想这是否是最好的方法”。有时当然是这样,在这种情况下,您应该使用类装饰器。