Python Decorator
Decorator, 装饰器。Decorator的用途在于允许在函数和类中嵌入或者修改代码。
classmethod and staticmethod
staticmethod(function)
这将返回一个function的静态方法。把类的一个方法的声明为静态方法,具体用法如下:
class C:
@staticmethod
def f(arg1, arg2, ...): ...
或者
class C:
def f(arg1, arg2, ...): ...
f = staticmethod(f)
类的静态方法既可以被类调用也可以被类的实例调用,例如:
C.f(...)
或者:
C(...).f(...)
注意,staticmethod 并不将类自身或其示例接收为隐含的第一个参数,在这一点上staticmethod 和 classmethod 存在区别。
classmethod(function)
这将返回function的一个类方法。具体用法如下:
class C:
@classmethod
def f(cls, arg1, arg2, ...): ...
或者:
class C:
def f(cls, arg1, arg2, ...): ...
f = classmethod(f)
类的classmethod既可以被类调用也可以被类的实例调用,例如:
C.f(...)
或者:
C(...).f(...)
注意,classmethod 像将类的示例函数一样,将类自身接收为隐含的第一个参数。
Python Decorators for Functions and Methods
函数decorator
一个函数decorators用于函数定义,它位于在函数定义之前的一行。例如:
@decoFunc
def aFunc():
print('a Func is called')
当编译器经过这段代码时,aFunc()
被编译然后将结果函数对象传递给decoFunc
代码,后者创建一个类函数对象并取代原来的aFunc()
。根据这一说法,那么:
@A
@B
@C
def func(arg1, arg2, ...): ...
等价于
def func(arg1, arg2, ...): ...
func = A(B(C(func)))
例如:
def decFunc(f):
def inner(f):
return 100
return inner
@decFunc
def aFunc(x):
print('value x is %d'%(x))
print('aFunc called')
print(aFunc(10))
程序的输出结果为
100
并没有输出
aFunc called
也就是说,在装饰器函数中,函数aFunc
的行为被改变了。
类decorator
与函数decorator类似,也可以将类作为decorator,只要实现__call__
方法即可。
一个类如果实现了__call__
方法,那么这个类的对象便是可调用的。例如:
class decCls():
def __call__():
print('__call__ of decCls called.')
decCls()()
运行,输出:
__call__ of decCls called.
下来举一个将类作为decorator的例子:
class decCls():
def __init__(self, f):
self.f = f
def __call__(self, x):
print('decorator called')
return 10000
@decCls
def aFunc(x):
print('value x is %d'%(x))
print('aFunc called')
print(aFunc(10))
此处就相当于
def aFunc(x):
print('value x is %d'%(x))
print('aFunc called')
aFunc = decCls(aFunc)
那么在调用aFunc
的时候,其实是在调用decCls(aFunc)
,也就是运行的是
decCls(aFunc)(10)
最后相当于调用的是装饰器类decCls
的__call__
方法。
decorator的用途
decorator的一个很大的用途在于可以“掌控”一个函数的运行。看下面这段代码:
def fn_decorator(fn):
def wrapper(*args):
print('call wrapper')
return fn(*args)
return wrapper
@fn_decorator
def fn(...):
# ...
在这儿,调用fn
时其实是在调用wrapper
,因此,wrapper
是可以控制fn
是否运行的。
Decorator带参数
之前的Decorator都是没有参数的,那么,有参数的Decorator又是如何工作的呢?
如果是单个带参数的Decorator,那么:
@dec(param)
def aFunc():
#...
等价于
def aFunc():
aFunc = dec(param)(aFunc)
如果是多重Decorator,并且带参数,那么有:
@dec_a(params1)
@dec_b(params2)
@dec_c(params3)
def method(args):
pass
等价于
def method(args):
pass
method = dec_a(params1)(dec_b(params2)(dec_c(params)(method)))
可见,Decorator还是很灵活很强大的。
应用举例
定义一个装饰器(Decorator)来测量函数的执行时间:
import time
def fn_timer(f):
def wrapper(*args):
t0 = time.time()
ans = f(*args)
t1 = time.time()
print('f start at %f, end at %f, running %f'%(t0,t1,t1-t0))
return ans
return wrapper
使用这个装饰器的例子:
@fn_timer
def sum_fn(n):
ans = 0
for i in range(0, n):
ans += i
return ans
print(sum_fn(10000000))
这样,便可以在不改变sum_fn
的接口和内部实现的前提下实现新的功能。
functools.wraps
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
This is a convenience function for invoking update_wrapper() as a function decorator when defining a wrapper function. It is equivalent to partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated). For example:
>>> from functools import wraps
>>> def my_decorator(f):
... @wraps(f)
... def wrapper(*args, **kwds):
... print('Calling decorated function')
... return f(*args, **kwds)
... return wrapper
...
>>> @my_decorator
... def example():
... """Docstring"""
... print('Called example function')
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'
Without the use of this decorator factory, the name of the example function would have been ‘wrapper’, and the docstring of the original example() would have been lost.