Decorators
“装饰器是一种对函数或者类指定代码管理的一种方式”...“装饰器本身是可调用的函数,并且返回一个可调用的函数” - Mark Lutz
一个函数对象是可调用的。所以,上面那句表达可以翻译为:一个装饰器是一个函数,它的参数为一个函数对象,返回值也为一个函数对象,在这个过程,可以对输入函数做必要的修改,从而增强它的功能。
“实际上,你可以使用Python的装饰器来实现装饰器模式,但是那是一个极端限制它使用的一个例子。我认为,Python的装饰器很像宏...我认为说在一门语言中宏的作用是提供了一种方法去修改语言的成分,这应该是比较恰当的。那就是装饰器在Python所做的--它修改函数,类的装饰器则修改整个类。这就是为什么他们经常提供一个简单的可选方法到元类上。” - Brue Eckel在Decorators I: Introduction to Python Decorators这样阐述到。
装饰器以不修改函数本身的前提下包装了一个函数。包装的结果为?
>>> a=10 >>> def f(): ... pass ... >>> class MyClass(): ... pass ... >>> print dir() ['MyClass', '__builtins__', '__doc__', '__name__', '__package__', 'a', 'f']正如我们看到的,f()是一个对象,和类(MyClass)还有变量(a)没有什么不同。
def func(): print "func()" funcObj = func funcObj()函数可以以任何其它对象类型,比如字符串,整数,列表等传入。Python中的函数的另一方面为它可以接收一个函数作为参数并且返回一个新的函数对象。如下所示。
def myFunction(in_function): def out_function(): pass return out_function在这里myFunction实际上就是一个装饰器,因为根据定义:一个装饰器是一个函数,它可以将一个函数对象作为它的参数,并且返回一个函数对象。
def myFunction(in_function): def out_function(): print "Entry: ", in_function.__name__ in_function() print "Exit: ", in_function.__name__ return out_function然后,我们该怎么调用我们的装饰器呢?
def simple_function(): pass enhanced_function = myFunction(simple_function)在许多情况下,我们将返回的函数对象取与输入函数相同的名字。所以,尤其地,代码应该看起来如下:
def simple_function(): pass simple_function = myFunction(simple_function)如果我们应用装饰器语法到上面的代码中:
@myFunction def simple_function(): pass注意:第一行的@myFunction不是一个装饰器,而是一个装饰器行或者是一个注释行。@标志装饰器的应用。装饰器是一个函数它接受另一个函数作为参数,并且返回一个新的函数。在我们的case中,就是myFunction。
simple_function = myFunction(simple_function)装饰器(myFunction)重新绑定装饰器的结果到函数名。所以,当后面simple_function被调用,它实际上调用的是装饰器返回的对象。
>>> class A: ... def s(x): ... print(x) ... s = staticmethod(s) ... >>> A.s(10) 10使用装饰器来表达同样的意思则看起来是:
>>> class A: ... @staticmethod ... def s(x): ... print(x) ... >>> A.s(10) 10另外一个例子:假设我们有两个函数,它们的定义如下:
>>> def wrapper(f): ... return f ... >>> def foo(): ... pass ...
>>> foo = wrapper(foo)所以,它是一个装饰器:
>>> @wrapper ... def foo(): ... pass用一个装饰器定义如下:
def decorator(f): #process function return f它自动地匹配到:
@decorator def f(arg): return arg*arg f(123) # output 15129与下面的形式有相同的效果:
def f(arg): print arg*arg f=decorator(f)装饰器是一个可调用对象并且返回一个可调用对象,该对象有与f有相同数量的参数。
f(123)到
decorator(f)(123)上面两个函数产生相同的结果。
def dollar(fn): def new(*args): return '$' + str(fn(*args)) return new @dollar def price(amount, tax_rates): return amount + amount*tax_rate print price(100,0.1)输出为:
$110
dollar装饰器函数取price()函数,经过修改内部工作,从原先的price()返回一个增强的输出。注意:装饰器使得我们可以在不对price()函数做任何修改的前提下实现这个功能。
所以,装饰器像一个wrapper,在目标函数执行的前后修改代码的行为,而且不需要更改函数本身,并增强了原来函数的功能。
我们也可以有一个英镑的功能:
def pound(fn): def new(*args): return (u"\u00A3").encode('utf-8') + str(fn(*args)) return '$' + str(fn(*args)) return new @pound def price(amount, tax_rate): return amount + amount*tax_rate print price(100, 0.2)
£110
def count(f): def inner(*args, **kargs): inner.counter += 1 return f(*args, **kargs) inner.counter = 0 return inner @count def my_fnc(): pass if __name__ == '__main__': my_fnc() my_fnc() my_fnc() print 'my_fnc.counter=', my_fnc.counter
my_fnc.counter= 3
import time def timer(f): def inner(*args, **kargs): t = time.time() ret = f(*args, **kargs) print 'timer = %s' %(time.time()-t) return ret return inner @timer def my_func(): pass if __name__ == '__main__': my_func()Output:
timer = 5.96046447754e-06例子4 - 装饰器链
def bold(f): def wrapped(): return '<b>' + f() + '</b>' return wrapped def italic(f): def wrapped(): return '<i>' + f() + '</i>' return wrapped @bold @italic def hello(): return 'hello' print hello()输出:
<b><i>hello</i></b>