原文链接: http://www.kylin-ux.com/2017/04/17/language-python-装饰器

装饰器的顺序

1
2
3
4
5
@a
@b
@c
def f ():
pass

等效于

1
f = a(b(c(f)))

1 最初, foo功能为打印’foo’

1
2
def foo():
print('foo')

2 后来由于各种原因, 需要在原功能基础上添加日志输出

1
2
3
def foo():
print('foo')
print('logging.info foo')

如果有N个函数都需要添加日志输出…

3 所以添加一个函数, 先打印日志输出, 再调用foo

1
2
3
def log_func(func):
print('logging.info {0}'.format(func.__name__))
func()

现在不用修改N个函数了, 但, 原所有调用foo()的地方, 需要改为log_func(foo)
还是要改很多地方… 对, 我们可以用sed批量修改

4 现在来个返回函数的函数

1
2
3
4
5
6
def log_func_d(func):
def wrapper(*args, **kwargs):
print('logging.info {0}'.format(func.__name__))
return func(*args, **kwargs)
return wrapper

现在不用修改调用foo()的地方了, 但需要foo = log_func_d(foo)

5 [装饰器]上场

1
2
3
4
5
6
@log_func_d
def foo_d():
print('foo')
foo_d()
print(foo_d.__name__) # wrapper

foo = log_func_d(foo)还是不够简洁, 换成@log_func_d, python提供的语法糖

6 带参数的[装饰器]

1
2
3
4
5
6
7
8
9
10
11
12
def use_func_d_arg(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
return decorator
@use_func_d_arg(level="warn")
def foo(name='foo'):
print("i am %s" % name)

7 类装饰器

1
2
3
4
5
6
7
8
9
10
11
class Foo():
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')

拥有__call__方法的类

8 functools, 原函数的元信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def log_func_d_f(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
print('logging.info {0}'.format(func.__name__))
return func(*args, **kwargs)
return wrapper
@log_func_d_f
def foo_d_f():
print('')
print(foo_d_f.__name__) # foo_d_f

对比5和8, print的结果, 5输出wrapper, 8输出foo_d_f