python语言中的AOP利器:装饰器
一、前言
面向切面编程(AOP)是一种编程思想,与OOP并不矛盾,只是它们的关注点相同。面向对象的目的在于抽象和管理,而面向切面的目的在于解耦和复用。
举两个大家都接触过的AOP的例子:
1)java中mybatis的@Transactional注解,大家知道被这个注解注释的函数立即就能获得DB的事务能力。
2)python中的with threading.Lock(),大家知道,被这个with代码块包裹的部分立即获得同步的锁机制。
这样我们把事务和加锁这两种与业务无关的逻辑抽象出来,在逻辑上解耦,并且可以轻松的做到代码复用。
二、上下文管理器contextlib
当然你可以使用with上下文管理器实现一些AOP的思想,这里有个模块叫contextlib可以帮助你简易的实现上下文管理器。
上下文管理最常见的例子是with open('file') as fh,回收打开句柄的例子。
这种方式还是比较麻烦的,下面我们看一下python中的装饰器怎么样实现AOP编程。
三、装饰器:AOP的语法糖
python中的装饰器就是设计来实现切面注入功能的。下面给出几个例子,这几个例子都是在生产环境验证过的。
其中的任务管理机是伪代码,需要自己实现写数据库的逻辑。
1、重试逻辑
只要do函数被@retry_exp装饰,便可以获得指数退避的重试能力。
@retry_exp(max_retries=10)
def do():
# do whatever
pass
那retry_exp是如何实现的呢?
def retry_exp(max_retries=3, max_wait_interval=10, period=1, rand=False):
def _retry(func):
def __retry(*args, **kwargs):
MAX_RETRIES = max_retries
MAX_WAIT_INTERVAL = max_wait_interval
PERIOD = period
RAND = rand
retries = 0
error = None
ok = False
while retries < MAX_RETRIES:
try:
ret = func(*args, **kwargs)
ok = True
return ret
except Exception, ex:
error = ex
finally:
if not ok:
sleep_time = min(2 ** retries * PERIOD if not RAND else randint(0, 2 ** retries) * PERIOD, MAX_WAIT_INTERVAL)
time.sleep(sleep_time)
retries += 1
if retries == MAX_RETRIES:
if error:
raise error
else:
raise Exception("unknown")
return __retry
return _retry
2、降级开关
只要do函数被@degrade装饰,就会安装app名称校验redis里的开关,一旦发现开关关闭,则do函数不被执行,也就是降级。
@degrade
def do(app):
# do whatever
pass
那么degrade是怎样实现的呢?
def degrade(app):
def _wrapper(function):
def __wrapper(*args, **kwargs):
value = None
try:
redis = codis_pool.get_connection()
value = redis.get("dmonitor:degrade:%s" % app)
except Exception, _:
logger.info(traceback.format_exc())
if not value or int(value) != 1:
function()
logger.info("[degrade] is_on: %s" % app)
else:
logger.info("[degrade] is_off: %s" % app)
return __wrapper
return _wrapper
3、任务状态机
这个是最常用的,我们需要跟踪落盘DB一个任务的执行状态(等待调度,执行中,执行成功,执行失败)
一旦do方法被@tasks_decorator装饰,就获得了这样的能力。对item_param(是个json)中task_id指明的任务进行状态管理。
@tasks_decorator
def do(item_param):
# do whatever
pass
tasks_decorator是怎样实现的呢?
def tasks_decorator(function):
def _wrap(*args, **kwargs):
param_dict = kwargs.get('item_param')
task_id = param_dict.get('task_id')
try:
param_dict.update({'status': TaskStatus.Waiting, 'start_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
try:
manager_dao.save_task(param_dict)
except Exception, ex:
pass
_update_task_status(task_id, TaskStatus.Doing)
function(*args, **kwargs)
_update_task_status(task_id, TaskStatus.Done)
except Exception as e:
time.sleep(0.5)
_update_task_status(task_id, TaskStatus.Fail, unicode(e.message))
raise
return _wrap
4、全局唯一性
在分布式+异步环境中,如果想保证exactly once是需要额外的逻辑的,其实主要是实现唯一键,一旦唯一键实现了,就可以使用公共缓存redis进行唯一键判定了。
do函数被unique装饰,那么对于task_id对应的任务,全局只会执行一次。
@unique
def do(task_id):
# do whatever
pass
unique是怎样实现的呢?
def unique(function):
def _wrap(*args, **kwargs):
task_id = kwargs.get('task_id')
try:
redis = codis_pool.get_connection()
key = "unique:%s" % task_id
if not redis.setnx(key):
redis.expire(key, 24*60*60)
function(*args, **kwargs)
except Exception as e:
logger.error(traceback.format_exc())
raise
return _wrap
四、总结
AOP在少量增加代码复杂度的前提下,显著的获得以下优点:
1、使得功能逻辑和业务逻辑解耦,功能和业务的修改完全独立,代码结构清晰,开发方便
2、一键注入,代码复用程度高,扩展方便
python语言中的AOP利器:装饰器的更多相关文章
- Python之命名空间、闭包、装饰器
一.命名空间 1. 命名空间 命名空间是一个字典,key是变量名(包括函数.模块.变量等),value是变量的值. 2. 命名空间的种类和查找顺序 - 局部命名空间:当前函数 - 全局命名空间:当前模 ...
- Python基础(七) python自带的三个装饰器
说到装饰器,就不得不说python自带的三个装饰器: 1.@property 将某函数,做为属性使用 @property 修饰,就是将方法,变成一个属性来使用. class A(): @prope ...
- Python之面向对象:闭包和装饰器
一.闭包 1. 如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包. def outter(): name='python' def inner(): print na ...
- Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列)
Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列) 一丶带参数的装饰器 def wrapper_out(pt): def wrapper(func): ...
- Python语言中enumerate()及zip()函数的使用例子
在Python编程语言中,enumerate()及zip()是两个常用的内置函数,这两个函数功能类似,但又有所区别,下面通过两个例子分别进行说明. enumerate()函数 该函数在字面上是枚举.列 ...
- 面向切面编程AOP——加锁、cache、logging、trace、同步等这些较通用的操作,如果都写一个类,则每个用到这些功能的类使用多继承非常难看,AOP就是解决这个问题的,python AOP就是装饰器
面向切面编程(AOP)是一种编程思想,与OOP并不矛盾,只是它们的关注点相同.面向对象的目的在于抽象和管理,而面向切面的目的在于解耦和复用. 举两个大家都接触过的AOP的例子: 1)java中myba ...
- python(十一)面向切面编程AOP和装饰器
二.装饰器 装饰器可以在给函数修改功能的同时并不改变这个函数本身.(以下用的都是python2.7) 首先,在python里面函数是对象,在下面的函数里"fun"是函数也是对象可以 ...
- python学习笔记-(八)装饰器、生成器&迭代器
本节课程内容概览: 1.装饰器 2.列表生成式&迭代器&生成器 3.json&pickle数据序列化 1. 装饰器 1.1 定义: 本质上是个函数,功能是装饰其他函数—就是为其 ...
- Python【第四课】 装饰器
本篇内容 什么是装饰器 装饰器需要遵循的原则 实现装饰器的知识储备 高阶函数 函数嵌套 闭包函数 无参函数 装饰器示例 1.什么是装饰器 器即函数 装饰即修饰,意指为其他函数添加新功能 装饰器定义:本 ...
随机推荐
- Keepalived+LVS 实现高负载均衡Web集群
一.原理及简介: 1.1 Keepalived简介 Keepalived是Linux下一个轻量级别的高可用解决方案.Keepalived起初是为LVS设计的,专门用来监控集群系统中各个服务节 ...
- windows下安装配置python + selenium 来驱动firefox
第一步,首先下载安装python ,我下载的是3.5版本,这个版本,自带了pip工具,不需要安装pip了 :) 链接地址:python 3.5 第二步,执行pip install selenium 安 ...
- 对把JDK源码的一些注解,笔记
//对treeMap的红黑树理解注解. 2017.02.16 by 何锦彬 JDK,1.7.51 /** From CLR */ private void fixAfterInsertion(Entr ...
- bzoj 1598: [Usaco2008 Mar]牛跑步 [k短路 A*] [学习笔记]
1598: [Usaco2008 Mar]牛跑步 题意:k短路 ~~貌似A*的题目除了x数码就是k短路~~ \[ f(x) = g(x) + h(x) \] \(g(x)\)为到达当前状态实际代价,\ ...
- BZOJ 3684: 大朋友和多叉树 [拉格朗日反演 多项式k次幂 生成函数]
3684: 大朋友和多叉树 题意: 求有n个叶子结点,非叶节点的孩子数量\(\in S, a \notin S\)的有根树个数,无标号,孩子有序. 鏼鏼鏼! 树的OGF:\(T(x) = \sum_{ ...
- 深入浅出Hadoop之mapreduce
卿哥原创,转载请注明出处,谢谢 之前已经作出预告,那么今天就聊聊mapreduce,起源于Google的map reduce paper, 而后经历了mapreduce 1,和构建于yarn上的map ...
- 安卓中圆角背景图被拉伸的解决方案——.9.png
举个例子: 从网上找了一张图片 如果我们直接用这张蓝色的图来做登录按钮的背景.将这个图片设为背景以后 我们可以发现四个角全部变形了,一点也不美观.针对此问题,我们通过.9图来解决. 首先我们先了解一下 ...
- Orleans之EventSourcing
Orleans之EventSourcing 这是Orleans系列文章中的一篇.首篇文章在此 引入: 如果没有意外,我再这篇文章中用ES代替EventSourcing,如果碰到"事件回溯&q ...
- jQuery smartMenu右键自定义上下文菜单插件
http://www.zhangxinxu.com/wordpress/?p=1667 <%@ page contentType="text/html; charset=UTF-8&q ...
- javascript函数大全
JavaScript函数大全 1.document.write(""); 输出语句2.JS中的注释为//3.传统的HTML文档顺序是:document->html->( ...