Python中用Decorator來簡化元編程的教程

少勞多得

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

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

1234 class C: def foo(cls, y): print "classmethod", cls, y foo = classmethod(foo)

雖然 classmethod() 是內置函數,但並無獨特之處;您也可以使用自己的方法轉換函數。例如:清單 2. 典型的 「舊式」 方法的轉換

123456789 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

1234567 class C: @classmethoddef foo(cls, y): print "classmethod", cls, y @enhanceddef bar(self, x): print "some method says:", x

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

1234567 @synchronized@loggingdef 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

123456789101112 >>> 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

1234567891011 >>> 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

12345678910111213 >>> 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

1234567 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

您可能感興趣的文章:

  • Python的另外幾種語言實現
  • python中使用xlrd、xlwt操作excel表格詳解
  • Python中實現常量(Const)功能
  • 推薦閱讀:

    切爾西隊史上第一次連續三場0比0,重點是...
    Flowpython針對lambda的一個修正
    Python語法基礎(二)
    django配置mysql資料庫問題解決
    Python寫碼注意事項

    TAG:編程 | Python | 教程 | 元編程 |