python decorator
decorator(装饰器)绝对是Python学习道路上的一个拦路虎。想要理解它,先看两个概念。
nested function(内嵌函数)
def helper(num): def is_even(arg): return arg % 2 == 0 if is_even(num): print(num, 'is even') else: print(num, 'is odd')
类似于函数局部变量,内嵌函数就是函数的局部函数。
Python的内嵌函数有个特点,它可以访问自身作用域外围的变量。
def helper(num): def is_even(): return num % 2 == 0 if is_even(): print(num, 'is even') else: print(num, 'is odd')
内嵌函数is_even可以直接访问外围参数num。
函数可以作为返回值
def helper(type): def helper_lower(arg): return arg.lower() def helper_upper(arg): return arg.upper() if type == 'lower': return helper_lower else: return helper_upper mylower = helper('lower') print(mylower) print(mylower('HELLO WORLD')) myupper = helper('upper') print(myupper) print(myupper('hello world'))
<function helper.<locals>.helper_lower at 0x106aee598>
hello world
<function helper.<locals>.helper_upper at 0x106aee730>
HELLO WORLD
现在回到装饰器。
装饰器的作用是,在不改变函数原有实现的基础上,给函数增加新的功能。
举个例子:
def decorator(func): print('before the function runs...') func() print('after the function runs...') def hello(): print('hello, i am foo')
现在执行decorator(hello),输出如下:
before the function runs...
hello, i am foo
after the function runs...
但是有个问题,为了这个新功能,用户需要把所有调用hello的地方换成decorator(hello)。
可以通过返回内嵌函数的方式解决:
def decorator(func): def wrapper(): print('before the function runs...') func() print('after the function runs...') return wrapper def hello(): print('hello, i am foo') hello = decorator(hello) hello()
如果原先的函数hello有参数怎么办呢?
def decorator(func): def wrapper(name): print('before the function runs...') func(name) print('after the function runs...') return wrapper def hello(name): print('hello, i am', name) hello = decorator(hello) hello('logan')
参数传递细节
hello('logan')
decorator(hello)('logan')
wrapper('logan')
通用版本的decorator如下:
def decorator(func): def wrapper(*args, **kw): print('before the function runs...') func(*args, **kw) print('after the function runs...') return wrapper
python为了简化hello = decorator(hello),使用了@符号。
def decorator(func): def wrapper(*args, **kw): print('before the function runs...') func(*args, **kw) print('after the function runs...') return wrapper @decorator def hello(name): print('hello, i am', name) hello('logan')
另一种实现decorator的方法是functools.wraps
import functools def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('before the function runs...') func(*args, **kw) print('after the function runs...') return wrapper @decorator def hello(name): print('hello, i am', name) hello('logan')
参考资料: