设为首页 收藏本站
查看: 497|回复: 0

[经验分享] Python和Decorator(装饰器)模式

[复制链接]

尚未签到

发表于 2015-4-24 10:00:20 | 显示全部楼层 |阅读模式
  先给出一个四人团对Decorator mode的定义:动态地给一个对象添加一些额外的职责。

再来说说这个模式的好处:认证,权限检查,记日志,检查参数,加锁,等等等等,这些功能和系统业务无关,但又是系统所必须的,说的更明白一点,就是面向方面的编程(AOP)。AOP把与业务无关的代码十分干净的从系统中切割出来,但是Decorator mode的强大远不止于此,本文的重点在于Decorator mode在Python中的应用,所以就不再过多描述Decorator mode本身了,要想深入了解该模式,请参考四人团的经典之作《设计模式》。

在Python中Decorator mode可以按照像其它编程语言如C++, Java等的样子来实现,但是Python在应用装饰概念方面的能力上远不止于此,Python提供了一个语法和一个编程特性来加强这方面的功能。Python提供的语法就是装饰器语法(decorator),如下:

@aoo
def foo(): pass
def aoo(fn):
return fn这里不对装饰器语法做过多的解释,因为装饰器语法也是基于我将要介绍的另一个编程特性,当我介绍完另一个编程特性后,相信你会对装饰器语法有更深入的认识。

一个十分重要的编程特性“闭包”(closure)隆重登场(题外话:据说“闭包”已经进入java下一版的特性候选列表了)

在Python,PHP,Perl,Ruby,JavaScript等动态语言中,都已经实现了闭包特性,为什么这个特性那么重要呢?我们先来看看它的通俗一些的定义:

OO编程范式中的对象是“整合了函数的数据对象”,那么闭包就是“整合了数据的函数对象”

源文档

借用一个非常好的说法来做个总结(注4):对象是附有行为的数据,而闭包是附有数据的行为。

源文档

这篇文章更详细的介绍了闭包
  

闭包的概念、形式与应用

源文档

下面左边的foo1只是一个普通的内嵌函数,而右边的boo则是一个闭包,


def  foo(x):

        y = x

        def  foo1 ():

                a = 1

                return a

        return  foo1



def  aoo(a, b):

        c = a

        def  boo (x):

                x = b + 1

                return x

        return boo


boo的特殊性在于引用了外部变量b,当aoo返回后,只要返回值(boo)一直存在,则对b的引用就会一直存在。

上面的知识可能需要花些时间消化,如果你觉得已经掌握了这些知识,下面就回归正题,看看这些语言特性是怎样来实现Python中装饰的概念的。

还是让我们先看一个简单的例子,然后逐步深入。这个例子就是加锁,怎样实现加锁的功能?

具体需求是这样的:我有一个对象,实现了某些功能并提供了一些接口供其它模块调用,这个对象是运行在并发的环境中的,因此我需要对接口的调用进行同步,第一版的代码如下:class Foo(object):
def __init__(self, …):
self.lock = threading.Lock()
def  interface1(self, …):
self.lock.acquire()
try:
do something
finally:
self.lock.release()
def  interface2(self, …):
same as interface1()

这版代码的问题很明显,那就是每个接口函数都有相同的加锁/解锁代码,重复的代码带来的是更多的键入,更多的阅读,更多的维护,以及更多的修改,最主要的是,程序员本应集中在业务上的的精力被分散了,而且请注意,真正的业务代码在距离函数定义2次缩进处开始,即使你的显示器是宽屏,这也会带来一些阅读上的困难。

你直觉的认为,可以把这些代码收进一个函数中,以达到复用的目的,但是请注意,这些代码不是一个完整同一的代码块,而是在中间嵌入了业务代码。

现在我们用装饰器语法来改进这部分代码,得到第2版代码:def    sync(func):
def   wrapper(*args, **kv):
self = args[0]
self.lock.acquire()
try:
return func(*args, **kv)
finally:
self.lock.release()
return wrapper
class Foo(object):
def __init__(self, …):
self.lock = threading.Lock()
@sync
def  interface1(self, …):
do something
@sync
def  interface2(self, …):
do something

一个装饰器函数的第一个参数是所要装饰的那个函数对象,而且装饰器函数必须返回一个函数对象。如sync函数,当其装饰interface1时,参数func的值就是interface1,返回值是wrapper,但类Foo实例的interface1被调用时,实际调用的是wrapper函数,在wrapper函数体中间接调用实际的interface1;当interface2被调用时,也调用的是wrapper函数,不过由于在装饰时func已经变成interface2,所以会间接地调用到实际的interface2函数。

使用装饰器语法的好处:

代码量大大的减少了,更少的代码意味着更少的维护,更少的阅读,更少的键入,好处不一而足(可复用,可维护)

用户基本上将绝大部分精力放在了业务代码上,而且少了加减锁的代码,可读性也提高了

缺点:

业务对象Foo中有一个非业务数据成员lock,很碍眼;

相当程度的耦合,wrapper的第一个参数必须是对象本身,而且被装饰的对象中必须有一个lock对象存在,这给客户对象添加了限制,使用起来不是很舒服。

我们可以更进一步想一想:

lock对象必须要放在Foo中吗?

为每个接口函数都键入@sync还是很烦人的重复性人工工作,如果漏添加一个,还是会造成莫名其妙的运行时错误,为什么不集中处理呢?

为了解决上述的缺点,第3版代码如下:class DecorateClass(object):
def decorate(self):
for name, fn in self.iter():
if not self.filter(name, fn):
continue
self.operate(name, fn)
class LockerDecorator(DecorateClass):
def __init__(self, obj, lock = threading.RLock()):
self.obj = obj
self.lock = lock
def iter(self):
return [(name, getattr(self.obj, name)) for name in dir(self.obj)]
def filter(self, name, fn):
if not name.startswith('_') and callable(fn):
return True
else:
return False
def operate(self, name, fn):
def locker(*args, **kv):
self.lock.acquire()
try:
return fn(*args, **kv)
finally:
self.lock.release()
setattr(self.obj, name, locker)
class Foo(object):
def __init__(self, …):

LockerDecorator(self).decorate()
def  interface1(self, …):
do something
def  interface2(self, …):
do something
… 对对象的功能装饰是一个更一般的功能,不仅限于为接口加锁,我用2个类来完成这一功能,DecorateClass是一个基类,只定义了遍历并应用装饰功能的算法代码(template method),LockerDecorator实现了为对象加锁的功能,其中iter是迭代器,定义了怎样遍历对象中的成员(包括数据成员和成员函数),filter是过滤器,定义了符合什么规则的成员才能成为一个接口,operate是执行函数,具体实施了为对象接口加锁的功能。

而在业务类Foo的__init__函数中,只需要在最后添加一行代码:LockerDecorator(self).decorate(),就可以完成为对象加锁的功能。

如果你的对象提供的接口有特殊性,完全可以通过直接改写filter或者继承LockerDecorator并覆盖filter的方式来实现;此外,如果要使用其他的装饰功能,可以写一个继承自DecorateClass的类,并实现iter,filter和operate三个函数即可。





转自:http://www.iyunv.com/tqsummer/archive/2011/01/24/1943314.html

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-60205-1-1.html 上篇帖子: python 多线程编程 下篇帖子: python os模块总结
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表