1. 程式人生 > >python 裝飾器理解

python 裝飾器理解

文章先由stackoverflow上面的一個問題引起吧,如果使用如下的程式碼:

  1. @makebold
  2. @makeitalic
  3. def say():  
  4.    return"Hello"

打印出如下的輸出:

  1. <b><i>Hello<i></b>  

你會怎麼做?最後給出的答案是:

  1. def makebold(fn):  
  2.     def wrapped():  
  3.         return"<b>" + fn() + "</b>"
  4.     return wrapped  
  5. def makeitalic(fn):  
  6.     def wrapped():  
  7.         return"<i>" + fn() + "</i>"
  8.     return wrapped  
  9. @makebold
  10. @makeitalic
  11. def hello():  
  12.     return"hello world"
  13. print hello() ## 返回 <b><i>hello world</i></b>

現在我們來看看如何從一些最基礎的方式來理解Python的裝飾器。英文討論參考

Here

裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較為經典的有插入日誌、效能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函式中與函式功能本身無關的雷同程式碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的物件新增額外的功能。

1.1. 需求是怎麼來的?

裝飾器的定義很是抽象,我們來看一個小例子。

  1. def foo():  
  2.     print'in foo()'
  3. foo()  

這是一個很無聊的函式沒錯。但是突然有一個更無聊的人,我們稱呼他為B君,說我想看看執行這個函式用了多長時間,好吧,那麼我們可以這樣做:

  1. import time  
  2. def foo():  
  3.     start = time.clock()  
  4.     print'in foo()'
  5.     end = time.clock()  
  6.     print'used:', end - start  
  7. foo()  

很好,功能看起來無懈可擊。可是蛋疼的B君此刻突然不想看這個函數了,他對另一個叫foo2的函式產生了更濃厚的興趣。

怎麼辦呢?如果把以上新增加的程式碼複製到foo2裡,這就犯了大忌了~複製什麼的難道不是最討厭了麼!而且,如果B君繼續看了其他的函式呢?

1.2. 以不變應萬變,是變也

還記得嗎,函式在Python中是一等公民,那麼我們可以考慮重新定義一個函式timeit,將foo的引用傳遞給他,然後在timeit中呼叫foo並進行計時,這樣,我們就達到了不改動foo定義的目的,而且,不論B君看了多少個函式,我們都不用去修改函式定義了!

  1. import time  
  2. def foo():  
  3.     print'in foo()'
  4. def timeit(func):  
  5.     start = time.clock()  
  6.     func()  
  7.     end =time.clock()  
  8.     print'used:', end - start  
  9. timeit(foo)  

看起來邏輯上並沒有問題,一切都很美好並且運作正常!……等等,我們似乎修改了呼叫部分的程式碼。原本我們是這樣呼叫的:foo(),修改以後變成了:timeit(foo)。這樣的話,如果foo在N處都被呼叫了,你就不得不去修改這N處的程式碼。或者更極端的,考慮其中某處呼叫的程式碼無法修改這個情況,比如:這個函式是你交給別人使用的。

1.3. 最大限度地少改動!

既然如此,我們就來想想辦法不修改呼叫的程式碼;如果不修改呼叫程式碼,也就意味著呼叫foo()需要產生呼叫timeit(foo)的效果。我們可以想到將timeit賦值給foo,但是timeit似乎帶有一個引數……想辦法把引數統一吧!如果timeit(foo)不是直接產生呼叫效果,而是返回一個與foo引數列表一致的函式的話……就很好辦了,將timeit(foo)的返回值賦值給foo,然後,呼叫foo()的程式碼完全不用修改!

  1. #-*- coding: UTF-8 -*-
  2. import time  
  3. def foo():  
  4.     print'in foo()'
  5. # 定義一個計時器,傳入一個,並返回另一個附加了計時功能的方法
  6. def timeit(func):  
  7.     # 定義一個內嵌的包裝函式,給傳入的函式加上計時功能的包裝
  8.     def wrapper():  
  9.         start = time.clock()  
  10.         func()  
  11.         end =time.clock()  
  12.         print'used:', end - start  
  13.     # 將包裝後的函式返回
  14.     return wrapper  
  15. foo = timeit(foo)  
  16. foo()  

這樣,一個簡易的計時器就做好了!我們只需要在定義foo以後呼叫foo之前,加上foo = timeit(foo),就可以達到計時的目的,這也就是裝飾器的概念,看起來像是foo被timeit裝飾了。在在這個例子中,函式進入和退出時需要計時,這被稱為一個橫切面(Aspect),這種程式設計方式被稱為面向切面的程式設計(Aspect-Oriented Programming)。與傳統程式設計習慣的從上往下執行方式相比較而言,像是在函式執行的流程中橫向地插入了一段邏輯。在特定的業務領域裡,能減少大量重複程式碼。面向切面程式設計還有相當多的術語,這裡就不多做介紹,感興趣的話可以去找找相關的資料。

這個例子僅用於演示,並沒有考慮foo帶有引數和有返回值的情況,完善它的重任就交給你了 :)

上面這段程式碼看起來似乎已經不能再精簡了,Python於是提供了一個語法糖來降低字元輸入量。

  1. import time  
  2. def timeit(func):  
  3.     def wrapper():  
  4.         start = time.clock()  
  5.         func()  
  6.         end =time.clock()  
  7.         print'used:', end - start  
  8.     return wrapper  
  9. @timeit
  10. def foo():  
  11.     print'in foo()'
  12. foo()  

重點關注第11行的@timeit,在定義上加上這一行與另外寫foo = timeit(foo)完全等價,千萬不要以為@有另外的魔力。除了字元輸入少了一些,還有一個額外的好處:這樣看上去更有裝飾器的感覺。

-------------------

要理解python的裝飾器,我們首先必須明白在Python中函式也是被視為物件。這一點很重要。先看一個例子:

  1. def shout(word="yes") :  
  2.     return word.capitalize()+" !"
  3. print shout()  
  4. # 輸出 : 'Yes !'
  5. # 作為一個物件,你可以把函式賦給任何其他物件變數 
  6. scream = shout  
  7. # 注意我們沒有使用圓括號,因為我們不是在呼叫函式
  8. # 我們把函式shout賦給scream,也就是說你可以通過scream呼叫shout
  9. print scream()  
  10. # 輸出 : 'Yes !'
  11. # 還有,你可以刪除舊的名字shout,但是你仍然可以通過scream來訪問該函式
  12. del shout  
  13. try :  
  14.     print shout()  
  15. except NameError, e :  
  16.     print e  
  17.     #輸出 : "name 'shout' is not defined"
  18. print scream()  
  19. # 輸出 : 'Yes !'

我們暫且把這個話題放旁邊,我們先看看python另外一個很有意思的屬性:可以在函式中定義函式:

  1. def talk() :  
  2.     # 你可以在talk中定義另外一個函式
  3.     def whisper(word="yes") :  
  4.         return word.lower()+"...";  
  5.     

    相關推薦

    Python裝飾理解

    python裝飾器 高階函數 out 發現 分享 打印 內部 存儲 -i 本文介紹Python其中一個強大的功能--裝飾器 裝飾器本質上就是一個函數,在不修改源代碼,調用方法的前提下,用來給其他函數添加功能的函數 想象一下,你已經開發完成一個功能,並且投入應用中,卻發

    Python 裝飾理解與使用

    Python裝飾器 本質是函式 為其他函式新增附加功能。裝飾器本身也是Python的一個重點,所以無論如何你必須弄懂它。裝飾器的存在其實就是為了在需要新增新功能時不影響之前版本的使用的同時來增加新功能,其實也是一種“偷懶”的辦法。它的使用場景較多,比如:插入日誌、效能測試、事務處理、快取

    python 裝飾理解

    文章先由stackoverflow上面的一個問題引起吧,如果使用如下的程式碼: @makebold @makeitalic def say():      return"Hello" 打印出如下的輸出: <b><i>

    Python裝飾的通俗理解

    python 裝飾器 python裝飾器 在學習Python的過程中,我相信有很多人和我一樣,對Python的裝飾器一直覺得很困惑,我也是困惑了好久,並通過思考和查閱才能略有領悟,我希望以下的內容會對你有幫助,我也努力通過通俗的方式使得對Python裝飾器的理解更加的透徹,很多人對裝飾器難以理解,

    如何理解python裝飾

    () 如何 lee 簡單的 存在 port print pytho -s 如何理解裝飾器python 學習遇到的第一個難點是裝飾器。裝飾器的作用是不大規模改動代碼的情況下,增加功能。作用:為已經存在的對象添加額外的功能特點:不需要對對象做任何的代碼上的變動。以一個例子來講裝

    python裝飾三種裝飾模式的簡單理解

    學設計模式中有個裝飾模式,用java實現起來不是很難,但是遠遠沒有python簡單,難怪越來越火了! 這裡就簡單討論下python的幾種裝飾模式: 一 無參裝飾器: # 裝飾器 import time # 裝飾器,記錄函式執行時間 def decorator01(fun): def w

    理解 Python 裝飾看這一篇就夠了

    講 Python 裝飾器前,我想先舉個例子,雖有點汙,但跟裝飾器這個話題很貼切。 每個人都有的內褲主要功能是用來遮羞,但是到了冬天它沒法為我們防風禦寒,咋辦?我們想到的一個辦法就是把內褲改造一下,讓它變得更厚更長,這樣一來,它不僅有遮羞功能,還能提供保暖,不過有個問題,這個內褲被我們改造成了

    理解Python裝飾

    裝飾器本質上是一個函式,該函式用來處理其他函式,它可以讓其他函式在不需要修改程式碼的前提下增加額外的功能,裝飾器的返回值也是一個函式物件。它經常用於有切面需求的場景,比如:插入日誌、效能測試、事務處理、快取、許可權校驗等應用場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大

    Python裝飾為什麼難理解

    無論專案中還是面試都離不開裝飾器話題,裝飾器的強大在於它能夠在不修改原有業務邏輯的情況下對程式碼進行擴充套件,許可權校驗、使用者認證、日誌記錄、效能測試、事務處理、快取等都是裝飾器的絕佳應用場景,它能夠最大程度地對程式碼進行復用。 但為什麼初學者對裝飾器的理解如此困難,我認為本質上是對Pyt

    12步教你理解Python裝飾

    或許你已經用過裝飾器,它的使用方式非常簡單但理解起來困難(其實真正理解的也很簡單),想要理解裝飾器,你需要懂點函數語言程式設計的概念,python函式的定義以及函式呼叫的語法規則等,雖然我沒法把裝飾器變得簡單,但是我希望可以通過下面的步驟讓你由淺入深明白裝飾器是什麼。假定你擁有最基本的Pyt

    python裝飾測試與理解

    第一,裝飾器基礎語句 # 這是一個基本的裝飾器測試檔案 def deco(func): def mod(): func() print("I'm mod") #mod函式在接收的func函式基礎上,後置增加

    [轉]理解Python裝飾

    作者:xlzd 連結:http://www.zhihu.com/question/26930016/answer/81263287 來源:知乎 著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。簡單來講,可以不嚴謹地把Python的裝飾器看做一個包裝函式的函式。比如,有一個函式:def fu

    如何理解Python裝飾

    Python 裝飾器使用非常地簡單。任何會使用 Python 函式的人都可以學習使用裝飾器: Python 123@somedecoratordefsome_function():print("Check it out, I

    python裝飾的簡單理解

    1、裝飾器是什麼? 裝飾器,顧名思義,就是用來“裝飾”的。 它長這個樣: @func_name 它能裝飾的東西有:函式、類 2,為何需要裝飾器? 先來打個比方,內褲可以用來遮羞,但是到了冬天它沒法為我們防風禦寒,聰明的人們發明了長褲,有了長褲後寶寶再也不冷了,裝飾器就

    如何理解python裝飾:讀《簡單 12 步理解 Python 裝飾》筆記

    原文:簡單 12 步理解 Python 裝飾器 筆記: 要想理解python裝飾器,需理解以下幾點(本人愚笨,需時刻提醒自己): 1、面向物件!時刻牢記python是面向物件的,python中,一切都是“物件(object)” 2、理解函式的定義和呼叫的區別,理解“賦值不等於呼叫

    python裝飾簡單理解

    裝飾器定義:1、把一個函式名當作實參傳給另外一個函式(在不修改裝飾函式原始碼的情況下為其新增功能)         2、返回值中包含函式名(不改變函式呼叫方式) 原始碼: 1 def f(): 2 def f(): 3 time.sleep(2) 4 print('i am

    Python 裝飾理解心得

    原文連結:http://www.cnblogs.com/ifantastic/archive/2012/12/09/2809325.html   1. 什麼是裝飾器?   顧名思義,裝飾器就是在方法上方標一個帶有@符號的方法名,以此來對被裝飾的方法進行點綴改造。

    python裝飾與AOP程式設計,個人理解

    Python裝飾器是一個很出名的設計模式,它主要的功能就是不改變函式已有功能的情況下對函式起到一個錦上添花的作業,使函式的功能更加豐富,在插入日誌,效能測試,快取機制和許可權驗證都是比較好的左右。不同的函式可以使用同一個裝飾器,所以它和函式本身不存在什麼必然的聯絡。AOP(面

    Python裝飾的本質理解

    我又回來了,深夜更文,神情倉促。。。最近在華為做openstack相關的一些專案,順便進修了一下Python知識,看到裝飾器這一塊時懵懂極了,網上找的大概都是例子,經過仔細研究揣摩,總結出了裝飾器使用的本質規則,以備後查,希望對Python裝飾器有疑問的朋友有幫助。 首先要

    Python-裝飾以及對帶有引數的裝飾理解

    請編寫一個decorator,能在函式呼叫的前後打印出’begin call’和’end call’的日誌。 再思考一下能否寫出一個@log的decorator,使它既支援: @log def f(): pass 又支援: @log('e