python总结(一):AOP与装饰器
来源:互联网 发布:雅思8.5 知乎 编辑:程序博客网 时间:2024/06/05 22:35
如果有AOP的编程经验,理解Python的装饰器就是分分钟的事。既然是装饰器,那么对被装饰的对象来说,一定是功能得到了增强,按方法能增强的地方进行划分,又可以分为以下四类:
1. 方法调用前;
2. 方法调用后;
3. 方法调用前后(环绕);
4. 方法调用异常;
我们以一个简单的加法运算来进行说明,按方法的增强点依次进行功能增强,首先看加法的代码,非常简单:
def add (a, b): return a + b
1. 方法调用前
在调用之前检查方法的参数是否合规,或者判断是否有相应的执行权限,这都是常用的手法,本例中检验第一个参数是否为正数,如果是,则执行加法,否则抛出错误,那我们可以定义如下的装饰器:
def before(func): # 只能通过参数声明的方式获取参数 def check(a, *args): # 如果小于0,抛出异常 if(a < 0): raise Exception('a is less than zero!') else: return func(a, *args) # 记住,返回的一定是函数 return check
在这里,最难的就是怎么获取方法的参数,不要期待有反射的方式,更不要企图通过方法的属性可以获取到参数(这句话我说错了,还真有这样的方法),一定要通过变长参数的方式(元组与字典)才能获取到所有的实参。
现在继续执行添加注解与调用方式,就会出现你想要的结果,如下:
@beforedef add (a, b): return a + b# 自动会抛出错误print add(-1, 2)
2. 方法调用后与装饰器参数
这一般用于对结果进行处理,如类型转换,将对象转换为JSON字符串,或者结果判断等,如下:
# 携带参数的写法def after (num): # 注意嵌套层次 def afterProxy(func): # 修改返回结果 def addMore (*args): # 对结果进行包装 result = func(* args) + num return result return addMore return afterProxy
请记住上面的调用层次,一定是先执行参数的方法,返回没有参数的装饰函数,最后返回封装后的结果函数,等同于after(5)(before(add(a, b))),调用方式如下:
# 等同于after(5)(before(add(a, b)))# 可以不同时使用,装饰之间没有依赖关系@after(5)@beforedef add (a, b): return a + b# 返回结果为8print add(1, 2)
3. 方法调用前后(环绕)
这个方式应用场景最多,比如事务的管理、数据库连接的自动开始与关闭等,并且前两项装饰器能干的事情它都能干,所以也是最强大的装饰器。原理也很简单,就是在方法执行前做一些事情,在方法执行后做一些事情,如下:
# 定义环绕装饰def round(func): def roundProxy(*args): print 'before execute' print func(*args) print 'end execute' return roundProxy# 执行调用add(1, 2)# 输出结果为# before execute# 8# end execute
4. 方法调用异常
这种装饰器一般用于方法调用出现异常时,所以一定会携带装饰器参数,且参数为异常类型,然后处理逻辑会放在异常捕获语句中,示例如下:
# 定义异常装饰器# ex为异常类型def mathExcept(ex): def exceptProxy(func): def divFunc(*args): try: return func(*args) # 捕获异常 except ex: # 处理业务逻辑 return 0 return divFunc return exceptProxy# 添加异常装饰器@mathExcept(ZeroDivisionError)def div(a, b): if(b == 0): raise ZeroDivisionError('b is zero') else: return a / b# 调用结果print div(3, 0)
经过异常装饰器处理后,你会返现,现在3除以0也不报错了,而是返回0,所以这适合统一的异常处理。
5. 装饰器的顺序
经过上面的例子,我们明白,装饰器就是一个个的函数,而它们声明的顺序也就是函数调用的顺序,所以如果它们对函数的返回结果与参数非常敏感,那么一定要仔细斟酌它们的顺序,以上面的例子为例,如果将装饰器的顺序改为如下,则会出现参数合规错误:
@after(5)@before@rounddef add (a, b): return a + b
为什么呢?因为执行round函数后,返回结果与参数都为None,所以执行after函数必然出错。
6. 能保留原始信息的@wraps
在很多权威的书籍里,都推荐大家使用@wraps注解,这样能保留方法的原始信息,例如“name”,“doc”等,甚至还能wrapped属性对包装的函数进行还原(据我测试多次,无法实现,版本2.7.14),写法如下:
# 引入wrapsfrom functools import wrapsdef before(func): @wraps(func) def check(a, *args): # 如果小于0,抛出异常 if(a < 0): raise Exception('a is less than zero!') else: return func(a, *args) # 记住,返回的一定是函数 return check@beforedef add (a, b): return a + b# 现在可以获取函数的元数据信息print add.__name__print add.__doc__
结论
- 装饰器就是函数;
- 在携带参数时,请记得再务必包装一层;
- 装饰器注重调用的顺序,所以在装饰器的设计中,最好有详细的设计规范。
- 结合AOP的切入点理解装饰器会更容易。
- python总结(一):AOP与装饰器
- python 装饰器与AOP
- python 装饰器与AOP
- 装饰器与AOP
- Python闭包与装饰器总结
- Python的AOP利器:装饰器
- python装饰器学习总结
- Python 装饰器归纳总结
- Python迭代器与装饰器
- Python装饰器:简单装饰,带参数装饰与类装饰器
- python装饰器的学习笔记一
- 一文弄懂python装饰器
- Python装饰器----应用示例(一)
- Python装饰器模式学习总结
- Python中装饰器的总结
- Python 装饰器记录总结 (终极版)
- python总结(四):类装饰器与方法的动态添加
- Python 装饰器与面向切面编程
- Git学习日记(end)
- Elevator
- 2017 ACM/ICPC Asia Regional Qingdao Online:1001:Apple(几何+JAVA大数)
- docker第一篇
- 第三周项目3求集合并集
- python总结(一):AOP与装饰器
- shell 向函数传递数组和从函数返回数组的一个细节问题
- Android 使用CMake编译NDK(一)CMakeLists.txt
- Servlet第六篇【Session介绍、API、生命周期、应用】
- STM32F0X单片机的内部FLASH问题
- 在spring引入log4j
- C/C++ 之 指针(1.基本类型指针)
- 第二周项目3-体验复杂度
- c++穷举法求最大公约数和最小公倍数的实例中如何调用max和min函数