电竞比分网-中国电竞赛事及体育赛事平台

分享

Python和Decorator(裝飾器)模式

 CodeNutter 2016-08-04

先給出一個(gè)四人團(tuán)對(duì)Decorator mode的定義:動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。

再來(lái)說(shuō)說(shuō)這個(gè)模式的好處:認(rèn)證,權(quán)限檢查,記日志,檢查參數(shù),加鎖,等等等等,這些功能和系統(tǒng)業(yè)務(wù)無(wú)關(guān),但又是系統(tǒng)所必須的,說(shuō)的更明白一點(diǎn),就是面向方面的編程(AOP)。AOP把與業(yè)務(wù)無(wú)關(guān)的代碼十分干凈的從系統(tǒng)中切割出來(lái),但是Decorator mode的強(qiáng)大遠(yuǎn)不止于此,本文的重點(diǎn)在于Decorator modePython中的應(yīng)用,所以就不再過(guò)多描述Decorator mode本身了,要想深入了解該模式,請(qǐng)參考四人團(tuán)的經(jīng)典之作《設(shè)計(jì)模式》。

PythonDecorator mode可以按照像其它編程語(yǔ)言如C++, Java等的樣子來(lái)實(shí)現(xiàn),但是Python在應(yīng)用裝飾概念方面的能力上遠(yuǎn)不止于此,Python提供了一個(gè)語(yǔ)法和一個(gè)編程特性來(lái)加強(qiáng)這方面的功能。Python提供的語(yǔ)法就是裝飾器語(yǔ)法(decorator),如下:

@aoo

def  foo(): pass

def  aoo(fn):

        return fn

這里不對(duì)裝飾器語(yǔ)法做過(guò)多的解釋,因?yàn)檠b飾器語(yǔ)法也是基于我將要介紹的另一個(gè)編程特性,當(dāng)我介紹完另一個(gè)編程特性后,相信你會(huì)對(duì)裝飾器語(yǔ)法有更深入的認(rèn)識(shí)。

一個(gè)十分重要的編程特性“閉包”(closure)隆重登場(chǎng)(題外話:據(jù)說(shuō)“閉包”已經(jīng)進(jìn)入java下一版的特性候選列表了)

Python,PHPPerl,Ruby,JavaScript等動(dòng)態(tài)語(yǔ)言中,都已經(jīng)實(shí)現(xiàn)了閉包特性,為什么這個(gè)特性那么重要呢?我們先來(lái)看看它的通俗一些的定義:

OO編程范式中的對(duì)象是“整合了函數(shù)的數(shù)據(jù)對(duì)象”,那么閉包就是“整合了數(shù)據(jù)的函數(shù)對(duì)象”

源文檔 <http://blog.csdn.net/muzisoft2008/archive/2009/03/30/4036744.aspx>

借用一個(gè)非常好的說(shuō)法來(lái)做個(gè)總結(jié)(4):對(duì)象是附有行為的數(shù)據(jù),而閉包是附有數(shù)據(jù)的行為。

源文檔 <http://www.ibm.com/developerworks/cn/linux/l-cn-closure/index.html>

這篇文章更詳細(xì)的介紹了閉包

閉包的概念、形式與應(yīng)用

源文檔 <http://www.ibm.com/developerworks/cn/linux/l-cn-closure/index.html>

下面左邊的foo1只是一個(gè)普通的內(nèi)嵌函數(shù),而右邊的boo則是一個(gè)閉包,

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,當(dāng)aoo返回后,只要返回值(boo)一直存在,則對(duì)b的引用就會(huì)一直存在。

上面的知識(shí)可能需要花些時(shí)間消化,如果你覺(jué)得已經(jīng)掌握了這些知識(shí),下面就回歸正題,看看這些語(yǔ)言特性是怎樣來(lái)實(shí)現(xiàn)Python裝飾的概念的。

還是讓我們先看一個(gè)簡(jiǎn)單的例子,然后逐步深入。這個(gè)例子就是加鎖,怎樣實(shí)現(xiàn)加鎖的功能?

具體需求是這樣的:我有一個(gè)對(duì)象,實(shí)現(xiàn)了某些功能并提供了一些接口供其它模塊調(diào)用,這個(gè)對(duì)象是運(yùn)行在并發(fā)的環(huán)境中的,因此我需要對(duì)接口的調(diào)用進(jìn)行同步,第一版的代碼如下:

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()

       

這版代碼的問(wèn)題很明顯,那就是每個(gè)接口函數(shù)都有相同的加鎖/解鎖代碼,重復(fù)的代碼帶來(lái)的是更多的鍵入,更多的閱讀,更多的維護(hù),以及更多的修改,最主要的是,程序員本應(yīng)集中在業(yè)務(wù)上的的精力被分散了,而且請(qǐng)注意,真正的業(yè)務(wù)代碼在距離函數(shù)定義2次縮進(jìn)處開(kāi)始,即使你的顯示器是寬屏,這也會(huì)帶來(lái)一些閱讀上的困難。

你直覺(jué)的認(rèn)為,可以把這些代碼收進(jìn)一個(gè)函數(shù)中,以達(dá)到復(fù)用的目的,但是請(qǐng)注意,這些代碼不是一個(gè)完整同一的代碼塊,而是在中間嵌入了業(yè)務(wù)代碼

現(xiàn)在我們用裝飾器語(yǔ)法來(lái)改進(jìn)這部分代碼,得到第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

       

一個(gè)裝飾器函數(shù)的第一個(gè)參數(shù)是所要裝飾的那個(gè)函數(shù)對(duì)象,而且裝飾器函數(shù)必須返回一個(gè)函數(shù)對(duì)象。如sync函數(shù),當(dāng)其裝飾interface1時(shí),參數(shù)func的值就是interface1,返回值是wrapper,但類Foo實(shí)例的interface1被調(diào)用時(shí),實(shí)際調(diào)用的是wrapper函數(shù),在wrapper函數(shù)體中間接調(diào)用實(shí)際的interface1;當(dāng)interface2被調(diào)用時(shí),也調(diào)用的是wrapper函數(shù),不過(guò)由于在裝飾時(shí)func已經(jīng)變成interface2,所以會(huì)間接地調(diào)用到實(shí)際的interface2函數(shù)。

使用裝飾器語(yǔ)法的好處:

  • 代碼量大大的減少了,更少的代碼意味著更少的維護(hù),更少的閱讀,更少的鍵入,好處不一而足(可復(fù)用,可維護(hù))
  • 用戶基本上將絕大部分精力放在了業(yè)務(wù)代碼上,而且少了加減鎖的代碼,可讀性也提高了

缺點(diǎn):

  • 業(yè)務(wù)對(duì)象Foo中有一個(gè)非業(yè)務(wù)數(shù)據(jù)成員lock,很礙眼;
  • 相當(dāng)程度的耦合,wrapper的第一個(gè)參數(shù)必須是對(duì)象本身,而且被裝飾的對(duì)象中必須有一個(gè)lock對(duì)象存在,這給客戶對(duì)象添加了限制,使用起來(lái)不是很舒服。

我們可以更進(jìn)一步想一想:

  • lock對(duì)象必須要放在Foo中嗎?
  • 為每個(gè)接口函數(shù)都鍵入@sync還是很煩人的重復(fù)性人工工作,如果漏添加一個(gè),還是會(huì)造成莫名其妙的運(yùn)行時(shí)錯(cuò)誤,為什么不集中處理呢?

為了解決上述的缺點(diǎn),第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

       

對(duì)對(duì)象的功能裝飾是一個(gè)更一般的功能,不僅限于為接口加鎖,我用2個(gè)類來(lái)完成這一功能,DecorateClass是一個(gè)基類,只定義了遍歷并應(yīng)用裝飾功能的算法代碼(template method),LockerDecorator實(shí)現(xiàn)了為對(duì)象加鎖的功能,其中iter是迭代器,定義了怎樣遍歷對(duì)象中的成員(包括數(shù)據(jù)成員和成員函數(shù)),filter是過(guò)濾器,定義了符合什么規(guī)則的成員才能成為一個(gè)接口,operate是執(zhí)行函數(shù),具體實(shí)施了為對(duì)象接口加鎖的功能。

而在業(yè)務(wù)類Foo__init__函數(shù)中,只需要在最后添加一行代碼:LockerDecorator(self).decorate(),就可以完成為對(duì)象加鎖的功能。

如果你的對(duì)象提供的接口有特殊性,完全可以通過(guò)直接改寫(xiě)filter或者繼承LockerDecorator并覆蓋filter的方式來(lái)實(shí)現(xiàn);此外,如果要使用其他的裝飾功能,可以寫(xiě)一個(gè)繼承自DecorateClass的類,并實(shí)現(xiàn)iter,filteroperate三個(gè)函數(shù)即可。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多