函数 -> 装饰器
函数的4个核心概念
1.函数可以赋与变量
def func(message): print('Got a message: {}'.format(message)) send_message = func send_message('hello world') #输出 #Got a message: hello world
2.函数可以当作函数的参数
def get_message(message): return 'Got a message: ' + message def root_call(func, message): print(func(message)) root_call(get_message, 'hello world') 输出 #Got a message: hello world
3.函数里嵌套函数
def func(message): def get_message(message): print('Got a message: {}'.format(message)) return get_message(message) func('hello world') 输出 #Got a message: hello world
4.函数作为函数返回值(闭包)
def func_closure(): def get_message(message): print('Got a message: {}'.format(message)) return get_message send_message = func_closure() send_message('hello world') #输出 #Got a message: hello world
简单装饰器
例
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper def greet(): print('hello world') greet = my_decorator(greet) greet()
使用语法糖 @
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
带有参数的装饰器
直接在 wrapper函数中加上参数
def my_decorator(func): def wrapper(message): print('wrapper of decorator') func(message) return wrapper @my_decorator #相当于 greet == wrapper(message) def greet(message): print(message) greet('hello world') # 输出 #wrapper of decorator #hello world
这个装饰器只能用在有一个参数的函数,如果想对任意参数的函数通用,可以这么写
def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper
带自定义参数的装饰器
利用装饰器自定义参数这特性,实现重复执行装饰器内部函数
def repeat(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator') func(*args, **kwargs) return wrapper return my_decorator @repeat(4) def greet(message): print(message) greet('hello world') # 输出: # wrapper of decorator # hello world # wrapper of decorator # hello world # wrapper of decorator # hello world # wrapper of decorator # hello world
原函数还是原函数?
greet.__name__ #输出 'wrapper' help(greet) # 输出 Help on function wrapper in module __main__: wrapper(*args, **kwargs)
可以看出,原函数的原信息会被wrapper取代
如果不想其改变,那么可用内置装饰器@functools.wraps将原函数的元信息拷贝过去。
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
装饰器的嵌套
@decorator1 @decorator2 @decorator3 def func(): ... #相当于 decorator1(decorator2(decorator3(func)))
import functools def my_decorator1(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator1') func(*args, **kwargs) return wrapper def my_decorator2(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator2') func(*args, **kwargs) return wrapper @my_decorator1 @my_decorator2 def greet(message): print(message) greet('hello world') # 输出 # execute decorator1 # execute decorator2 # hello world
装饰器的实例用法
1)身份验证,不登录不允许操作
import functools def authenticate(func): @functools.wraps(func) def wrapper(*args, **kwargs): request = args[0] if check_user_logged_in(request): # 如果用户处于登录状态 return func(*args, **kwargs) # 执行函数 post_comment() else: raise Exception('Authentication failed') return wrapper @authenticate def post_comment(request, ...) ...
2)日志记录 可测试函数的执行时间
import time import functools def log_execution_time(func): def wrapper(*args, **kwargs): start = time.perf_counter() res = func(*args, **kwargs) end = time.perf_counter() print('{} took {} ms'.format(func.__name__, (end - start) * 1000)) return res return wrapper @log_execution_time def calculate_similarity(items): ...
3) 合法性检测
import functools def validation_check(input): @functools.wraps(func) def wrapper(*args, **kwargs): ... # 检查输入是否合法 @validation_check def neural_network_training(param1, param2, ...): ... LRU cache. @lru_cache缓存装饰器 @lru_cache def check(param1, param2, ...) # 检查用户设备类型,版本号等等 ...
4) try...excaption
class ServerDebugHelper: @classmethod def debug(cls): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except: import traceback print(traceback.format_exc()) return wrapper return decorator
参考
极客时间《Python核心技术与实战》专栏