python装饰器理解与实践
通俗地讲,装饰器就是对函数的重定义(不严谨,但有助于理解)。通常是将原函数包装成一个新函数并赋给原函数,以便在函数执行时自动进行包装添加的相应处理(如鉴权功能,运行时统计等)。语法糖@简化了赋值处理
talk is cheap, show me the code
一、函数装饰器
下面主要实现三个装饰器
- 纯注册用,不改变函数行为
- 普通装饰,后续调用函数时会改变行为
- 带参数的装饰器,根据参数来生成不同的装饰行为(不用定义多个装饰器)
def once(func): """ 当调用@once时相当于执行了一次 func = once(func), 即对func重新赋值, 实际就是func = func, 后续调用还是调用func 因为赋值给func是func本身, 所以后续调用不会有任何附加作用 适用于初始化状态(例如初始化时加载路由) :param func: 可执行函数 :return: func, 返回函数本身 """ # 初始化操作, 可执行任意操作 print('once init') return func def ever(func): """ 当调用@ever时相当于执行了一次 func = ever(func), 这里func赋值后, 实际是 func = wrapper, 后续调用实际调用的wrapper :param func: :return: """ # 初始化操作, 可执行任意操作 print('ever init') def wrapper1(*args): print('before ever') ret = func(*args) print('after ever') return ret return wrapper1 def aka(key='key'): """ 当调用@aka('key')时相当于执行了一次 func = aka('key')(func) aka('key')取到的是dec, 所以实际是 func = dec(func) 而dec(func)返回的是wrapper, 所以最终是 func = wrapper, 后续调用实际调用wrapper 注意这里的装饰器调用是 @aka('key') 而不是装饰器函数本身 :param key: :return: """ # 这里可以进行初始化操作, 可执行任意操作 print('aka init out dec -- key:', key) def dec(func): # 也可以在这里进行初始化操作, 可执行任意操作 print('aka init in dec -- key:', key) def wrapper2(*args): print('before aka') ret = func(*args) print('after aka') return ret return wrapper2 return dec print('初始化**********************************') @once def tes_once(): print('tes_once real:', tes_once.__name__) @ever def tes_ever(): print('tes_ever real:', tes_ever.__name__) @once @ever @aka('key') def tes_multi(): """ 相当于 tes_multi = once(ever(aka('key')(tes_multi))) 注意这里是有调用顺序的 先由aka包装成 wrapper2 函数, 即 tes_multi = once(ever(wrapper2)) 再由ever包装成 wrapper1 函数, 即 tes_multi = once(wrapper1) 再由once包装 :return: """ print('tes_all real:', tes_multi.__name__) print('调用************************************') tes_once() print('---------------') tes_ever() print('---------------') tes_multi() print('****************************************')
运行结果
运行结果如下: 初始化********************************** once init ever init aka init out dec -- key: key aka init in dec -- key: key ever init once init 调用************************************ tes_once real: tes_once --------------- before ever tes_ever real: wrapper1 after ever --------------- before ever before aka tes_all real: wrapper1 after aka after ever ****************************************
从上面的结果看到函数名__name__并没有指向自身,而是指向了赋给他的包装函数名(tes_ever real: wrapper1, tes_all real: wrapper1)。也就是说使用装饰器时会丢失原函数的元数据。
那么要怎么保留原函数的元数据呢?
functools模块中的wraps装饰函数提供了相应处理,我们只需调用@wraps(func)
即可
from functools import wraps def new_ever(func): @wraps(func) def wrapper1(*args): print('before new_ever') ret = func(*args) print('after new_ever') return ret return wrapper1 def new_aka(key='key'): def dec(func): @wraps(func) def wrapper2(*args): print('before new_aka') ret = func(*args) print('after new_aka') return ret return wrapper2 return dec @new_ever def tes_new_ever(): print('tes_new_ever real:', tes_new_ever.__name__) @new_aka('key') def tes_new_aka(): print('tes_new_aka real:', tes_new_aka.__name__) print('调用************************************') tes_new_ever() print('---------------') tes_new_aka() print('****************************************')
运行结果如下:
调用************************************ before new_ever tes_new_ever real: tes_new_ever after new_ever --------------- before new_aka tes_new_aka real: tes_new_aka after new_aka ****************************************
我们可以看到函数名已经正确指向自己
二、类装饰器
这个感觉还没遇到过使用环境,不过有看到一个用来管理被装饰函数运行次数的例子
class Decorator(object): def __init__(self, max): self.max = max self.count = 0 # 这里让对象能够像函数一样被调用 def __call__(self, func): self.func = func return self.call_func def call_func(self, *args, **kwargs): if self.count < self.max: self.func(*args, **kwargs) self.count += 1 else: print('{} run more than {} times'.format(self.func.__name__, self.max)) @Decorator(5) def do_some(): print('do some') for i in range(10): do_some()
运行结果:
do some do some do some do some do some do_some run more than 5 times do_some run more than 5 times do_some run more than 5 times do_some run more than 5 times do_some run more than 5 times
这里可以看出,可以用类装饰器对函数进行状态管理
类装饰器一下子搞不清楚适用哪方面怎么用,以后碰到再更新吧
原文地址:https://www.jianshu.com/p/e93fea508268
相关推荐
-
菜鸟学Python之django-simple-captcha使用 python基础
2020-5-31
-
Python的数据库操作(Sqlalchemy) python基础
2019-10-9
-
Python 3.7.0 正式版发布,新特性翻译 python基础
2019-2-22
-
Python基础之公共方法 python基础
2019-9-11
-
【译】PEP 318–函数和方法的装饰器 python基础
2020-5-31
-
python多线程 python基础
2019-1-10
-
一步一步教你如何用Python做词云 python基础
2019-7-2
-
Python档案袋( 时间 和 随机数 模块 ) python基础
2019-10-9
-
python 高阶函数 python基础
2019-9-2
-
玩转京东支付(python) python基础
2020-7-4