Python decorator 装饰器
装饰器模式(Decorator Pattern)是设计模式中的一种,主要目的是提供了这样一种操作,可以在不需要改变函数实现代码的情况下,用来修改或者完善它的功能。
Python 里的 decorator 也是基于这样的设计,比如:
@log
def test_func(a, b):
pass
原来的 test_func
函数,经过 log
装饰器后,变成了 log(test_func)
,所有调用 test_func
的地方,现在都变成了调用 log(test_func)
,从而实现了在保持原来 test_func
的实现、以及使用的地方不变的情况下,添加了新的功能。用示意图,大概是这样的:
+----------+
| x x x x x| <-- log
+----------+ +----------+
|//////////| ==> |//////////|
|//////////| |//////////|
+----------+ +----------+
| y y y y y| <-- log
+----------+
test_func ==> test_func (with decorator)
^ ^
| |
| |
Demo -----------------/
举例
举例一下代码、以及对它的调用:
def test_func(a, b):
print('%d + %d = %d' % (a, b, a + b))
test_func(1, 2)
会得到这样的输出:
1 + 2 = 3
简单的装饰器
可以这样定义并使用 decorator(以补丁方式突出增加的代码):
+def log(func):
+ def wrapper(*args, **kwargs):
+ print('before call')
+ func(*args, **kwargs)
+ print('after call')
+ return wrapper
+
+@log
def test_func(a, b):
print('%d + %d = %d' % (a, b, a + b))
test_func(1, 2)
这样,通过一个语法 @log
装饰了 test_func
,可以得到这样的结果:
before call
1 + 2 = 3
after call
可以看到,装饰器可以在调用真正函数的前面和后面,加入代码逻辑实现新的功能。
test_func 和 wrapper 的关系
在代码里打印这两个函数的 id:
def log(func):
def wrapper(*args, **kwargs):
+ print('id(func) = %r' % (id(func)))
+ print('id(wrapper) = %r' % (id(wrapper)))
print('before call')
func(*args, **kwargs)
print('after call')
return wrapper
@log
def test_func(a, b):
+ print('id(test_func) = %r' % (id(test_func)))
print('%d + %d = %d' % (a, b, a + b))
test_func(1, 2)
+print('id(test_func) = %r' % (id(test_func)))
可以得到这样的输出(id 取决于实际运行时):
id(func) = 140717307679808
id(wrapper) = 140717307679968
before call
id(test_func) = 140717307679968
1 + 2 = 3
after call
id(test_func) = 140717307679968
可以看到出现了两个不同的 id,其中 func(即原来的 test_func)
是一个 id,而 wrapper
和 装饰过的 test_func
是另一个 id。所以这时的 test_func
实际上是 log(test_func)
,或者说是 log(test_func).wrapper
。
带参数的装饰器
装饰器 decorator 是允许带参数的,比如:
def log(x, y):
def decorator(func):
def wrapper(*args, **kwargs):
print('x=%r, y=%r' % (x, y))
print('before call')
func(*args, **kwargs)
print('after call')
return wrapper
return decorator
@log('XXX', 'YYYY')
def test_func(a, b):
print('%d + %d = %d' % (a, b, a + b))
test_func(1, 2)
这里的 test_func
经过装饰器后,实际上是 log(..).decorator(test_func).wrapper
,输出是这样的:
x='XXX', y='YYYY'
before call
1 + 2 = 3
after call
基于类实现的装饰器
装饰器可以用类来实现,比如:
class log:
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self, func):
def wrapper(*args, **kwargs):
print('x=%r, y=%r' % (self.x, self.y))
print('before call')
func(*args, **kwargs)
print('after call')
return wrapper
@log('XXX', 'YYYY')
def test_func(a, b):
print('%d + %d = %d' % (a, b, a + b))
这里的 test_func
经过装饰器后,实际上是 log(..)(test_func).wrapper
。输出同上。