Python - decorator

Decorators

“装饰器是一种对函数或者类指定代码管理的一种方式”...“装饰器本身是可调用的函数,并且返回一个可调用的函数” - Mark Lutz

一个函数对象是可调用的。所以,上面那句表达可以翻译为:一个装饰器是一个函数,它的参数为一个函数对象,返回值也为一个函数对象,在这个过程,可以对输入函数做必要的修改,从而增强它的功能。

“实际上,你可以使用Python的装饰器来实现装饰器模式,但是那是一个极端限制它使用的一个例子。我认为,Python的装饰器很像宏...我认为说在一门语言中宏的作用是提供了一种方法去修改语言的成分,这应该是比较恰当的。那就是装饰器在Python所做的--它修改函数,类的装饰器则修改整个类。这就是为什么他们经常提供一个简单的可选方法到元类上。” - Brue Eckel在Decorators I: Introduction to Python Decorators这样阐述到。

装饰器以不修改函数本身的前提下包装了一个函数。包装的结果为?

  • 增加函数的功能。
  • 修改函数的行为。
Function Decorators
让我们以Python的一个函数开始。为了理解装饰器,我们需要知道Python函数的能力范围。在Python中所有东西都是一个对象。函数也不例外。
>>> 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
然后,我们该怎么调用我们的装饰器呢?
让我们看下面的例子。我们放了一个simple_function到装饰器(myFunction)中,作为它的一个参数,得到了一个enhanced_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()替换掉。
同样,注意这一行:
simple_function = myFunction(simple_function)
装饰器(myFunction)重新绑定装饰器的结果到函数名。所以,当后面simple_function被调用,它实际上调用的是装饰器返回的对象。
当我们定义了一个静态方法时,我们见识了重新绑定(rebinding):
>>> 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
... 
然后,wrapper可以用来重新绑定foo()函数像下面这样:
>>> 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)
上面两个函数产生相同的结果。

例子1 - 增加$符号到price()函数的返回值
如我们想要一个函数增加一个前缀,装饰器可以帮助我们:
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
例子2 - 一个函数被调用了多少次?
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
例子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>


本文翻译自 Python - decorator

你可能感兴趣的:(Python - decorator)