函数装饰器
-
简单装饰器
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper def greet(): print('hello world') greet = my_decorator(greet) greet() # 输出 # wrapper of decorator # hello world
上述代码在 Python 中有更简单、更优雅的表示:
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper @my_decorator def greet(): print('hello world') greet() # 输出 # wrapper of decorator # hello world
-
带参数的装饰器
def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper @my_decorator def greet(message): print(message) greet('hello world') # 输出 # wrapper of decorator # hello world
-
自定义参数的装饰器
def repeat(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator {}'.format(i)) func(*args, **kwargs) return wrapper return my_decorator @repeat(4) def greet(message): print(message) greet('hello world') # 输出: # wrapper of decorator 0 # hello world # wrapper of decorator 1 # hello world # wrapper of decorator 2 # hello world # wrapper of decorator 3 # hello world
-
原函数还是原函数吗?
试着打印出 greet() 函数的一些元信息:
greet.__name__ ## 输出 'wrapper' help(greet) # 输出 Help on function wrapper in module __main__: wrapper(*args, **kwargs)
greet()
函数被装饰以后,它的元信息变了。元信息告诉我们“它不再是以前的那个greet()
函数,而是被wrapper()
函数取代了”。为了解决这个问题,通常使用内置的装饰器
@functools.wrap
,它会帮助保留原函数的元信息(也就是将原函数的元信息,拷贝到对应的装饰器函数里)。import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper @my_decorator def greet(message): print(message) greet.__name__ # 输出 'greet'
类装饰器
实际上,类也可以作为装饰器。类装饰器主要依赖于函数__call__()
,每当你调用一个类的示例时,函数__call__()
就会被执行一次。
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs)
@Count
def example():
print("hello world")
example()
# 输出
num of calls is: 1
hello world
example()
# 输出
num of calls is: 2
hello world
我们定义了类 Count,初始化时传入原函数 func()
,而__call__()
函数表示让变量 num_calls 自增 1,然后打印,并且调用原函数。因此,在我们第一次调用函数 example()
时,num_calls 的值是 1,而在第二次调用时,它的值变成了 2
装饰器的应用
-
身份认证 authenticate
-
日志记录
-
输入合理性检查 validation_check
-
缓存 lru_cache
通常使用缓存装饰器,来包裹这些检查函数,避免其被反复调用,进而提高程序运行效率,比如写成下面这样
@lru_cache def check(param1, param2, ...) # 检查用户设备类型,版本号等等 ...