欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Decorator 簡化元編程

系統 1880 0
Python 使元編程成為可能,不過每個版本的 Python 都有一些細微的區別(并且不是完全兼容),這使我們實現元編程的道路變得更加崎嶇。一類函數對象的使用由來已久,同樣還有一些技術用于探索和實現魔術般的屬性。在版本 2.2 中,Python 增加了一種很有幫助的定制元類機制,但是其代價就是令用戶絞盡腦汁。最近,在 2.4 版本中,Python 增加了 “decorator” ,這是適于執行大部分元編程的最新方式 —— 也是到目前為止對用戶最友好的方式。
<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->

少勞多得

Decorator 與 Python 之前引入的元編程抽象有著某些共同之處:即使沒有這些技術,您也一樣可以實現它們所提供的功能。正如 Michele Simionato 和我在 可愛的 Python 專欄的早期文章 中指出的那樣,即使在 Python 1.5 中,也可以實現 Python 類的創建,而不需要使用 “元類” 掛鉤。

Decorator 根本上的平庸與之非常類似。Decorator 所實現的功能就是修改緊接 Decorator 之后定義的函數和方法。這總是可能的,但這種功能主要是由 Python 2.2 中引入的 classmethod() staticmethod() 內置函數驅動的。在舊式風格中,您可以調用 classmethod() ,如下所示:



清單 1. 典型的 “舊式” classmethod
                    
class C:
    def foo(cls, y):
        print "classmethod", cls, y
    foo = classmethod(foo)

          

雖然 classmethod() 是內置函數,但并無獨特之處;您也可以使用自己的方法轉換函數。例如:



清單 2. 典型的 “舊式” 方法的轉換
                    
def enhanced(meth):
    def new(self, y):
        print "I am enhanced"
        return meth(self, y)
    return new
class C:
    def bar(self, x):
        print "some method says:", x
    bar = enhanced(bar)

          

decorator 所做的一切就是使您避免重復使用方法名,并且將 decorator 放在方法定義中第一處提及其名稱的地方。例如:



清單 3. 典型的 “舊式” classmethod
                    
class C:
    @classmethod
    def foo(cls, y):
        print "classmethod", cls, y
    @enhanced
    def bar(self, x):
        print "some method says:", x

          

decorator 也可以用于正則函數,采用的是與類中的方法相同的方式。令人驚奇的是,這一切是如此簡單(嚴格來說,甚至有些不必要),只需要對語法進行簡單修改,所有東西就可以工作得更好,并且使得程序的論證更加輕松。通過在方法定義的函數之前列出多個 decorator,即可將 decorator 鏈接在一起;良好的判斷可以有助于防止將 過多 decorator 鏈接在一起,不過有時候將幾個 decorator 鏈接在一起是有意義的:



清單 4. 鏈接 decorator
                    
@synchronized
@logging
def myfunc(arg1, arg2, ...):
    # ...do something
# decorators are equivalent to ending with:
#    myfunc = synchronized(logging(myfunc))
# Nested in that declaration order

          

Decorator 只是一個語法糖,如果您過于急切,那么它就會使您搬起石頭砸了自己的腳。decorator 其實就是一個至少具有一個參數的函數 —— 程序員要負責確保 decorator 的返回內容仍然是一個有意義的函數或方法,并且實現了原函數為使連接有用而做的事情。例如,下面就是 decorator 兩個不正確的用法:



清單 5. 沒有返回函數的錯誤 decorator
                    
>>> def spamdef(fn):
...     print "spam, spam, spam"
...
>>> @spamdef
... def useful(a, b):
...     print a**2 + b**2
...
spam, spam, spam
>>> useful(3, 4)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: 'NoneType' object is not callable

          

decorator 可能會返回一個函數,但這個函數與未修飾的函數之間不存在有意義的關聯:



清單 6. 忽略傳入函數的 decorator
                    
>>> def spamrun(fn):
...     def sayspam(*args):
...         print "spam, spam, spam"
...     return sayspam
...
>>> @spamrun
... def useful(a, b):
...     print a**2 + b**2
...
>>> useful(3,4)
spam, spam, spam

          

最后,一個表現更良好的 decorator 可以在某些方面增強或修改未修飾函數的操作:



清單 7. 修改未修飾函數行為的 decorator
                    
>>> def addspam(fn):
...     def new(*args):
...         print "spam, spam, spam"
...         return fn(*args)
...     return new
...
>>> @addspam
... def useful(a, b):
...     print a**2 + b**2
...
>>> useful(3,4)
spam, spam, spam
25

          

您可能會質疑, useful() 到底有多么有用? addspam() 真的是那樣出色的 增強 嗎?但這種機制至少符合您通常能在有用的 decorator 中看到的那種模式。





回頁首


高級抽象簡介

根據我的經驗,元類應用最多的場合就是在類實例化之后對類中的方法進行修改。decorator 目前并不允許您修改類實例化 本身 ,但是它們可以修改依附于類的方法。這并不能讓您在實例化過程中動態添加或刪除方法或類屬性,但是它讓這些方法可以在運行時根據環境的條件來變更其行為。現在從技術上來說,decorator 是在運行 class 語句時應用的,對于頂級類來說,它更接近于 “編譯時” 而非 “運行時”。但是安排 decorator 的運行時決策與創建類工廠一樣簡單。例如:



清單 8. 健壯但卻深度嵌套的 decorator
                    
def arg_sayer(what):
    def what_sayer(meth):
        def new(self, *args, **kws):
            print what
            return meth(self, *args, **kws)
        return new
    return what_sayer

def FooMaker(word):
    class Foo(object):
        @arg_sayer(word)
        def say(self): pass
    return Foo()

foo1 = FooMaker('this')
foo2 = FooMaker('that')
print type(foo1),; foo1.say()  # prints: <class '__main__.Foo'> this
print type(foo2),; foo2.say()  # prints: <class '__main__.Foo'> that

          

@arg_sayer() 繞了很多彎路,但只獲得非常有限的結果,不過對于它所闡明的幾方面來說,這是值得的:

  • Foo.say() 方法對于不同的實例有不同的行為。在這個例子中,不同之處只是一個數據值,可以輕松地通過其他方式改變這個值;不過原則上來說,decorator 可以根據運行時的決策來徹底重寫這個方法。

  • 本例中未修飾的 Foo.say() 方法是一個簡單的占位符,其整個行為都是由 decorator 決定的。然而,在其他情況下,decorator 可能會將未修飾的方法與一些新功能相 結合

  • 正如我們已經看到的一樣, Foo.say() 的修改是通過 FooMaker() 類工廠在運行時嚴格確定的。可能更加典型的情況是在頂級定義類中使用 decorator,這些類只依賴于編譯時可用的條件(這通常就足夠了)。

  • decorator 都是參數化的。或者更確切地說, arg_sayer() 本身根本就不是一個真正的 decorator; arg_sayer() 所返回的 函數 —— what_sayer() 就是一個使用了閉包來封裝其數據的 decorator 函數。參數化的 decorator 較為常見,但是它們將所需的函數嵌套為三層。




回頁首


邁進元類領域

正如上一節中介紹的一樣,decorator 并不能完全取代元類掛鉤,因為它們只修改了方法,而未添加或刪除方法。實際上,這樣說并不完全正確。作為一個 Python 函數,decorator 完全可以實現其他 Python 代碼所實現的任何功能。通過修飾一個類的 .__new__() 方法(甚至是其占位符版本),您實際上可以更改附加到該類的方法。盡管尚未在現實中看到這種模式,不過我認為它有著某種必然性,甚至可以作為 _metaclass_ 指派的一項改進:



清單 9. 添加和刪除方法的 decorator
                    
def flaz(self): return 'flaz'     # Silly utility method
def flam(self): return 'flam'     # Another silly method

def change_methods(new):
    "Warning: Only decorate the __new__() method with this decorator"
    if new.__name__ != '__new__':
        return new  # Return an unchanged method
    def __new__(cls, *args, **kws):
        cls.flaz = flaz
        cls.flam = flam
        if hasattr(cls, 'say'): del cls.say
        return super(cls.__class__, cls).__new__(cls, *args, **kws)
    return __new__

class Foo(object):
    @change_methods
    def __new__(): pass
    def say(self): print "Hi me:", self

foo = Foo()
print foo.flaz()  # prints: flaz
foo.say()         # AttributeError: 'Foo' object has no attribute 'say'

          

change_methods() decorator 示例中,我們添加并刪除了幾個固定的方法,不過這是毫無意義的。在更現實的情況中,應使用上一節中提到的幾個模式。例如,參數化的 decorator 可以接受一個能表示要添加或刪除的方法的數據結構;或者由數據庫查詢之類的某些環境特性做出這一決策。這種對附加方法的操作也可以像之前一樣打包到一個函數工廠中,這將使最終決策延遲到運行時。這些新興技術也許比 _metaclass_ 指派更加萬能。例如,您可以調用一個增強了的 change_methods() ,如下所示:



清單 10. 增強的 change_methods()
                    
class Foo(object):
    @change_methods(add=(foo, bar, baz), remove=(fliz, flam))
    def __new__(): pass

          





回頁首


修改調用模型

您將看到,有關 decorator 的最典型的例子可能是使一個函數或方法來實現 “其他功能”,同時完成其基本工作。例如,在諸如 Python Cookbook Web 站點(請參見 參考資料 中的鏈接)之類的地方,您可以看到 decorator 添加了諸如跟蹤、日志記錄、存儲/緩存、線程鎖定以及輸出重定向之類的功能。與這些修改相關(但實質略有區別)的是修飾 “之前” 和 “之后”。對于修飾之前/之后來說,一種有趣的可能性就是檢查傳遞給函數的參數和函數返回值的類型。如果這些類型并非如我們預期的一樣,那么這種 type_check() decorator 就可能會觸發一個異常,或者采取一些糾正操作。

與這種 decorator 前/后類似的情況,我想到了 R 編程語言和 NumPy 特有的函數的 “elementwise” 應用。在這些語言中,數學函數通常應用于元素序列中的 每個元素 ,但也會應用于單個數字。

當然, map() 函數、列表內涵(list-comprehension)和最近的生成器內涵(generator-comprehension 都可以讓您實現 elementwise 應用。但是這需要較小的工作區來獲得類似于 R 語言的行為: map() 所返回的序列類型通常是一個列表;如果您傳遞的是單個元素而不是一個序列,那么調用將失敗。例如:



清單 11. map() 調用失敗
                    
>>> from math import sqrt
>>> map(sqrt, (4, 16, 25))
[2.0, 4.0, 5.0]
>>> map(sqrt, 144)
TypeError: argument 2 to map() must support iteration

          

創建一個可以 “增強” 普通數值函數的 decorator 并不困難:



清單 12. 將函數轉換成 elementwise 函數
                    
def elementwise(fn):
    def newfn(arg):
        if hasattr(arg,'__getitem__'):  # is a Sequence
            return type(arg)(map(fn, arg))
        else:
            return fn(arg)
    return newfn

@elementwise
def compute(x):
    return x**3 - 1

print compute(5)        # prints: 124
print compute([1,2,3])  # prints: [0, 7, 26]
print compute((1,2,3))  # prints: (0, 7, 26)

          

當然,簡單地編寫一個具有不同返回類型的 compute() 函數并不困難;畢竟 decorator 只需占據幾行。但是作為對面向方面編程的一種認可,這個例子讓我們可以 分離 那些在不同層次上運作的關注事項。我們可以編寫各種數值計算函數,希望它們都可轉換成 elementwise 調用模型,而不用考慮參數類型測試和返回值類型強制轉換的細節。

對于那些對單個事物或事物序列(此時要保留序列類型)進行操作的函數來說, elementwise() decorator 均可同樣出色地發揮作用。作為一個練習,您可嘗試去解決如何允許相同的修飾后調用來接受和返回迭代器(提示:如果您只是想迭代一次完整的 elementwise 計算,那么當且僅當傳入的是一個迭代對象時,才能這樣簡化一些。)

您將碰到的大多數優秀的 decorator 都在很大程度上采用了這種組合正交關注的范例。傳統的面向對象編程,尤其是在諸如 Python 之類允許多重繼承的語言中,都會試圖使用一個繼承層次結構來模塊化關注事項。然而,這僅會從一個祖先那里獲取一些方法,而從其他祖先那里獲取其他方法,因此需要采用一種概念,使關注事項比在面向方面的思想中更加分散。要充分利用生成器,就要考慮一些與混搭方法不同的問題:可以處于方法本身的 “核心” 之外的關注事項為依據,使 方法以不同方式工作。





回頁首


修飾 decorator

分享這篇文章……

digg 將這篇文章提交到 Digg
del.icio.us 發布到 del.icio.us
提交到 Slashdot!

在結束本文之前,我想為您介紹一種確實非常出色的 Python 模塊,名為 decorator ,它是由與我合著過一些圖書的 Michele Simionato 編寫的。該模塊使 decorator 的開發變得更加美妙。 decorator 模塊的主要組件具有某種自反式的優雅,它是一個稱為 decorator() 的 decorator。與未修飾的函數相比,使用 @decorator 修飾過的函數可以通過一種更簡單的方式編寫。(相關資料請參看 參考資料 )。

Michele 已經為自己的模塊編寫了很好的文檔,因此這里不再贅述;不過我非常樂意介紹一下它所解決的基本問題。 decorator 模塊有兩大主要優勢。一方面,它使您可以編寫出嵌套層次更少的 decorator,如果沒有這個模塊,您就只能使用更多層次(“平面優于嵌套”);但更加有趣的是這樣一個事實:它使得修飾過的函數可以真正地與其在元數據中未修飾的版本相匹配,這是我的例子中沒有做到的。例如,回想一下我們上面使用過的簡單 “跟蹤” decorator addspam()



清單 13. 一個簡單的 decorator 是如何造成元數據崩潰的
                    
>>> def useful(a, b): return a**2 + b**2
>>> useful.__name__
'useful'
>>> from inspect import getargspec
>>> getargspec(useful)
(['a', 'b'], None, None, None)
>>> @addspam
... def useful(a, b): return a**2 + b**2
>>> useful.__name__
'new'
>>> getargspec(useful)
([], 'args', None, None)

          

盡管這個修飾過的函數 的確完成 了自己增強過的工作,但若進一步了解,就會發現這并不是完全正確的,尤其是對于那些關心這種細節的代碼分析工具或 IDE 來說更是如此。使用 decorator ,我們就可以改進這些問題:



清單 14. decorator 更聰明的用法
                    
>>> from decorator import decorator
>>> @decorator
... def addspam(f, *args, **kws):
...     print "spam, spam, spam"
...     return f(*args, **kws)
>>> @addspam
... def useful(a, b): return a**2 + b**2
>>> useful.__name__
'useful'
>>> getargspec(useful)
(['a', 'b'], None, None, None)

          

這對于編寫 decorator 更加有利,同時,其保留行為的元數據的也更出色了。當然,閱讀 Michele 開發這個模塊所使用的全部資料會使您回到大腦混沌的世界,我們將這留給 Simionato 博士一樣的宇宙學家好了。

Decorator 簡化元編程


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 久久99综合| 美女网站在线观看视频18 | 欧美日韩高清不卡免费观看 | 三级特黄 | 免费久久久久 | 久草视频大全 | 91精品国产综合久久精品 | 综合二区 | 黑人性xxxⅹxxbbbbb | 四虎影片国产精品8848 | 久久涩综合 | 中文在线免费观看 | 日韩国产精品一区二区三区 | 欧美日韩在线免费观看 | 91免费无限观看 | 草莓视频69 | 青青草一区 | 亚洲经典三级 | 波多野结衣在线网址 | 国产目拍亚洲精品区一区 | 久草最新视频 | 欧美午夜免费观看福利片 | 中文字幕在线一区 | 天天干 夜夜操 | 国产精品视频在线观看 | 久久影院一区二区三区 | 97精品国产高清久久久久蜜芽 | 一区二区三区波多野结衣 | 双凤奇案 | 亚洲毛片网站 | 亚洲涩综合 | 久久se精品一区精品二区 | a级欧美 | 久久国产综合 | 国产黄的网站免费 | 亚洲色图在线视频 | 中文字幕国产精品 | 一区二区免费视频观看 | 人人爱国产 | 精品综合 | 欧美一级久久久久久久大片 |