一、前言

面向切面编程(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利器:装饰器的更多相关文章

  1. Python之命名空间、闭包、装饰器

    一.命名空间 1. 命名空间 命名空间是一个字典,key是变量名(包括函数.模块.变量等),value是变量的值. 2. 命名空间的种类和查找顺序 - 局部命名空间:当前函数 - 全局命名空间:当前模 ...

  2. Python基础(七) python自带的三个装饰器

    说到装饰器,就不得不说python自带的三个装饰器: 1.@property   将某函数,做为属性使用 @property 修饰,就是将方法,变成一个属性来使用. class A(): @prope ...

  3. Python之面向对象:闭包和装饰器

    一.闭包 1. 如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包. def outter(): name='python' def inner(): print na ...

  4. Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列)

    Python进阶(七)----带参数的装饰器,多个装饰器修饰同一个函数和递归简单案例(斐波那契数列) 一丶带参数的装饰器 def wrapper_out(pt): def wrapper(func): ...

  5. Python语言中enumerate()及zip()函数的使用例子

    在Python编程语言中,enumerate()及zip()是两个常用的内置函数,这两个函数功能类似,但又有所区别,下面通过两个例子分别进行说明. enumerate()函数 该函数在字面上是枚举.列 ...

  6. 面向切面编程AOP——加锁、cache、logging、trace、同步等这些较通用的操作,如果都写一个类,则每个用到这些功能的类使用多继承非常难看,AOP就是解决这个问题的,python AOP就是装饰器

    面向切面编程(AOP)是一种编程思想,与OOP并不矛盾,只是它们的关注点相同.面向对象的目的在于抽象和管理,而面向切面的目的在于解耦和复用. 举两个大家都接触过的AOP的例子: 1)java中myba ...

  7. python(十一)面向切面编程AOP和装饰器

    二.装饰器 装饰器可以在给函数修改功能的同时并不改变这个函数本身.(以下用的都是python2.7) 首先,在python里面函数是对象,在下面的函数里"fun"是函数也是对象可以 ...

  8. python学习笔记-(八)装饰器、生成器&迭代器

    本节课程内容概览: 1.装饰器 2.列表生成式&迭代器&生成器 3.json&pickle数据序列化 1. 装饰器 1.1 定义: 本质上是个函数,功能是装饰其他函数—就是为其 ...

  9. Python【第四课】 装饰器

    本篇内容 什么是装饰器 装饰器需要遵循的原则 实现装饰器的知识储备 高阶函数 函数嵌套 闭包函数 无参函数 装饰器示例 1.什么是装饰器 器即函数 装饰即修饰,意指为其他函数添加新功能 装饰器定义:本 ...

随机推荐

  1. MySQL常见SQL语句用法

    标签(linux): mysql 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 表字段类型 TINYINT 微小整数类型,可存储的容量为1字节 INT 整数类型 ...

  2. KVM虚拟机绑定物理CPU进行性能调优

    PS:前提是虚拟cpu总个数不多于物理cpu总个数.   绑定方法: 虚拟机的虚拟cpu有1个,实体机物理cpu有8个,可以通过以下方式绑定cpu,绑定的动作不一定要在虚机启动时,可以在任何时候: 绑 ...

  3. Tensorflow ActiveFunction激活函数解析

    Active Function 激活函数 原创文章,请勿转载哦~!! 觉得有用的话,欢迎一起讨论相互学习~Follow Me Tensorflow提供了多种激活函数,在CNN中,人们主要是用tf.nn ...

  4. 面向切面编程之手动JDK代理方式

    需求描述: 抽取dao层开启和提交事物交由代理类一并执行 分析: 假如UserDao接口中有很多方法,例如addUser().deleteUser().updateUser()等等,需要频繁的和数据库 ...

  5. Python之Django rest_Framework(2)

    实例化: v1 = ["view.xxx.path.Role","view.xxx.path.Group",] 可以循环,循环出来的每一个不能实例化 如果把v1 ...

  6. 制作 alipay-sdk-java包到本地仓库

    一.首先 搭建好maven 基础环境,本地可以运行maven 命令 从支付宝官网上下载sdk https://doc.open.alipay.com/doc2/detail?treeId=54& ...

  7. bzoj 2095: [Poi2010]Bridges [混合图欧拉回路]

    2095: [Poi2010]Bridges 二分答案,混合图欧拉路判定 一开始想了一个上下界网络流模型,然后发现不用上下界网络流也可以 对于无向边,强制从\(u \rightarrow v\),计算 ...

  8. BZOJ 1018: [SHOI2008]堵塞的交通traffic [线段树 区间信息]

    1018: [SHOI2008]堵塞的交通traffic Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 3064  Solved: 1027[Submi ...

  9. 【Oracle】虚拟表Dual

    Dual是个虚拟表,用来构成SELECT语句的语法规则,Oracle保证Dual里面永远只有一条记录.可以用它来做很多事情,例如,查看当前用户:用来调用系统函数:得到序列的下一个值或者当前值:可以用作 ...

  10. LeetCode - 596. Classes More Than 5 Students

    There is a table courses with columns: student and class Please list out all classes which have more ...