<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      理解Python中的裝飾器

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

       

      @makebold
      @makeitalic
      def say():
         return "Hello"

       

      打印出如下的輸出:

      <b><i>Hello<i></b>

       

      你會怎么做?最后給出的答案是:

       

      def makebold(fn):
          def wrapped():
              return "<b>" + fn() + "</b>"
          return wrapped
       
      def makeitalic(fn):
          def wrapped():
              return "<i>" + fn() + "</i>"
          return wrapped
       
      @makebold
      @makeitalic
      def hello():
          return "hello world"
       
      print hello() ## 返回 <b><i>hello world</i></b>

       

      現(xiàn)在我們來看看如何從一些最基礎(chǔ)的方式來理解Python的裝飾器。英文討論參考Here

      裝飾器是一個很著名的設(shè)計模式,經(jīng)常被用于有切面需求的場景,較為經(jīng)典的有插入日志、性能測試、事務(wù)處理等。裝飾器是解決這類問題的絕佳設(shè)計,有了裝飾器,我們就可以抽離出大量函數(shù)中與函數(shù)功能本身無關(guān)的雷同代碼并繼續(xù)重用。概括的講,裝飾器的作用就是為已經(jīng)存在的對象添加額外的功能。

      1.1. 需求是怎么來的?

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

       

       

      def foo():
          print 'in foo()'
      foo()

       

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

       

      import time
      def foo():
          start = time.clock()
          print 'in foo()'
          end = time.clock()
          print 'used:', end - start
       
      foo()

       

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

      怎么辦呢?如果把以上新增加的代碼復(fù)制到foo2里,這就犯了大忌了~復(fù)制什么的難道不是最討厭了么!而且,如果B君繼續(xù)看了其他的函數(shù)呢?

      1.2. 以不變應(yīng)萬變,是變也

      還記得嗎,函數(shù)在Python中是一等公民,那么我們可以考慮重新定義一個函數(shù)timeit,將foo的引用傳遞給他,然后在timeit中調(diào)用foo并進(jìn)行計時,這樣,我們就達(dá)到了不改動foo定義的目的,而且,不論B君看了多少個函數(shù),我們都不用去修改函數(shù)定義了!

       

      import time
       
      def foo():
          print 'in foo()'
       
      def timeit(func):
          start = time.clock()
          func()
          end =time.clock()
          print 'used:', end - start
       
      timeit(foo)

       

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

      1.3. 最大限度地少改動!

      既然如此,我們就來想想辦法不修改調(diào)用的代碼;如果不修改調(diào)用代碼,也就意味著調(diào)用foo()需要產(chǎn)生調(diào)用timeit(foo)的效果。我們可以想到將timeit賦值給foo,但是timeit似乎帶有一個參數(shù)……想辦法把參數(shù)統(tǒng)一吧!如果timeit(foo)不是直接產(chǎn)生調(diào)用效果,而是返回一個與foo參數(shù)列表一致的函數(shù)的話……就很好辦了,將timeit(foo)的返回值賦值給foo,然后,調(diào)用foo()的代碼完全不用修改!

       

      #-*- coding: UTF-8 -*-
      import time
       
      def foo():
          print 'in foo()'
       
      # 定義一個計時器,傳入一個,并返回另一個附加了計時功能的方法
      def timeit(func):
           
          # 定義一個內(nèi)嵌的包裝函數(shù),給傳入的函數(shù)加上計時功能的包裝
          def wrapper():
              start = time.clock()
              func()
              end =time.clock()
              print 'used:', end - start
           
          # 將包裝后的函數(shù)返回
          return wrapper
       
      foo = timeit(foo)
      foo()

       

      這樣,一個簡易的計時器就做好了!我們只需要在定義foo以后調(diào)用foo之前,加上foo = timeit(foo),就可以達(dá)到計時的目的,這也就是裝飾器的概念,看起來像是foo被timeit裝飾了。在在這個例子中,函數(shù)進(jìn)入和退出時需要計時,這被稱為一個橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。與傳統(tǒng)編程習(xí)慣的從上往下執(zhí)行方式相比較而言,像是在函數(shù)執(zhí)行的流程中橫向地插入了一段邏輯。在特定的業(yè)務(wù)領(lǐng)域里,能減少大量重復(fù)代碼。面向切面編程還有相當(dāng)多的術(shù)語,這里就不多做介紹,感興趣的話可以去找找相關(guān)的資料。

      這個例子僅用于演示,并沒有考慮foo帶有參數(shù)和有返回值的情況,完善它的重任就交給你了 :)

      上面這段代碼看起來似乎已經(jīng)不能再精簡了,Python于是提供了一個語法糖來降低字符輸入量。

       

      import time
       
      def timeit(func):
          def wrapper():
              start = time.clock()
              func()
              end =time.clock()
              print 'used:', end - start
          return wrapper
       
      @timeit
      def foo():
          print 'in foo()'
       
      foo()

       

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

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

      要理解python的裝飾器,我們首先必須明白在Python中函數(shù)也是被視為對象。這一點很重要。先看一個例子:

       

      def shout(word="yes") :
          return word.capitalize()+" !"
       
      print shout()
      # 輸出 : 'Yes !'
       
      # 作為一個對象,你可以把函數(shù)賦給任何其他對象變量 
       
      scream = shout
       
      # 注意我們沒有使用圓括號,因為我們不是在調(diào)用函數(shù)
      # 我們把函數(shù)shout賦給scream,也就是說你可以通過scream調(diào)用shout
       
      print scream()
      # 輸出 : 'Yes !'
       
      # 還有,你可以刪除舊的名字shout,但是你仍然可以通過scream來訪問該函數(shù)
       
      del shout
      try :
          print shout()
      except NameError, e :
          print e
          #輸出 : "name 'shout' is not defined"
       
      print scream()
      # 輸出 : 'Yes !'

       

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

       

      def talk() :
       
          # 你可以在talk中定義另外一個函數(shù)
          def whisper(word="yes") :
              return word.lower()+"...";
       
          # ... 并且立馬使用它
       
          print whisper()
       
      # 你每次調(diào)用'talk',定義在talk里面的whisper同樣也會被調(diào)用
      talk()
      # 輸出 :
      # yes...
       
      # 但是"whisper" 不會單獨存在:
       
      try :
          print whisper()
      except NameError, e :
          print e
          #輸出 : "name 'whisper' is not defined"*

       

      函數(shù)引用

      從以上兩個例子我們可以得出,函數(shù)既然作為一個對象,因此:

      1. 其可以被賦給其他變量

      2. 其可以被定義在另外一個函數(shù)內(nèi)

      這也就是說,函數(shù)可以返回一個函數(shù),看下面的例子:

       

      def getTalk(type="shout") :
       
          # 我們定義另外一個函數(shù)
          def shout(word="yes") :
              return word.capitalize()+" !"
       
          def whisper(word="yes") :
              return word.lower()+"...";
       
          # 然后我們返回其中一個
          if type == "shout" :
              # 我們沒有使用(),因為我們不是在調(diào)用該函數(shù)
              # 我們是在返回該函數(shù)
              return shout
          else :
              return whisper
       
      # 然后怎么使用呢 ?
       
      # 把該函數(shù)賦予某個變量
      talk = getTalk()     
       
      # 這里你可以看到talk其實是一個函數(shù)對象:
      print talk
      #輸出 : <function shout at 0xb7ea817c>
       
      # 該對象由函數(shù)返回的其中一個對象:
      print talk()
       
      # 或者你可以直接如下調(diào)用 :
      print getTalk("whisper")()
      #輸出 : yes...

       

      還有,既然可以返回一個函數(shù),我們可以把它作為參數(shù)傳遞給函數(shù):

       

      def doSomethingBefore(func) :
          print "I do something before then I call the function you gave me"
          print func()
       
      doSomethingBefore(scream)
      #輸出 :
      #I do something before then I call the function you gave me
      #Yes !

       

      這里你已經(jīng)足夠能理解裝飾器了,其他它可被視為封裝器。也就是說,它能夠讓你在裝飾前后執(zhí)行代碼而無須改變函數(shù)本身內(nèi)容。

      手工裝飾

      那么如何進(jìn)行手動裝飾呢?

       

      # 裝飾器是一個函數(shù),而其參數(shù)為另外一個函數(shù)
      def my_shiny_new_decorator(a_function_to_decorate) :
       
          # 在內(nèi)部定義了另外一個函數(shù):一個封裝器。
          # 這個函數(shù)將原始函數(shù)進(jìn)行封裝,所以你可以在它之前或者之后執(zhí)行一些代碼
          def the_wrapper_around_the_original_function() :
       
              # 放一些你希望在真正函數(shù)執(zhí)行前的一些代碼
              print "Before the function runs"
       
              # 執(zhí)行原始函數(shù)
              a_function_to_decorate()
       
              # 放一些你希望在原始函數(shù)執(zhí)行后的一些代碼
              print "After the function runs"
       
          #在此刻,"a_function_to_decrorate"還沒有被執(zhí)行,我們返回了創(chuàng)建的封裝函數(shù)
          #封裝器包含了函數(shù)以及其前后執(zhí)行的代碼,其已經(jīng)準(zhǔn)備完畢
          return the_wrapper_around_the_original_function
       
      # 現(xiàn)在想象下,你創(chuàng)建了一個你永遠(yuǎn)也不遠(yuǎn)再次接觸的函數(shù)
      def a_stand_alone_function() :
          print "I am a stand alone function, don't you dare modify me"
       
      a_stand_alone_function()
      #輸出: I am a stand alone function, don't you dare modify me
       
      # 好了,你可以封裝它實現(xiàn)行為的擴(kuò)展。可以簡單的把它丟給裝飾器
      # 裝飾器將動態(tài)地把它和你要的代碼封裝起來,并且返回一個新的可用的函數(shù)。
      a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
      a_stand_alone_function_decorated()
      #輸出 :
      #Before the function runs
      #I am a stand alone function, don't you dare modify me
      #After the function runs

       

      現(xiàn)在你也許要求當(dāng)每次調(diào)用a_stand_alone_function時,實際調(diào)用卻是a_stand_alone_function_decorated。實現(xiàn)也很簡單,可以用my_shiny_new_decorator來給a_stand_alone_function重新賦值。

       

      a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
      a_stand_alone_function()
      #輸出 :
      #Before the function runs
      #I am a stand alone function, don't you dare modify me
      #After the function runs
       
      # And guess what, that's EXACTLY what decorators do !
      

       

      裝飾器揭秘

      前面的例子,我們可以使用裝飾器的語法:

       

      @my_shiny_new_decorator
      def another_stand_alone_function() :
          print "Leave me alone"
       
      another_stand_alone_function()
      #輸出 :
      #Before the function runs
      #Leave me alone
      #After the function runs

       

      當(dāng)然你也可以累積裝飾:

       

      def bread(func) :
          def wrapper() :
              print "</''''''\>"
              func()
              print "<\______/>"
          return wrapper
       
      def ingredients(func) :
          def wrapper() :
              print "#tomatoes#"
              func()
              print "~salad~"
          return wrapper
       
      def sandwich(food="--ham--") :
          print food
       
      sandwich()
      #輸出 : --ham--
      sandwich = bread(ingredients(sandwich))
      sandwich()
      #outputs :
      #</''''''\>
      # #tomatoes#
      # --ham--
      # ~salad~
      #<\______/>

       

      使用python裝飾器語法:

       

      @bread
      @ingredients
      def sandwich(food="--ham--") :
          print food
       
      sandwich()
      #輸出 :
      #</''''''\>
      # #tomatoes#
      # --ham--
      # ~salad~
      #<\______/>

       

      裝飾器的順序很重要,需要注意:

       

      @ingredients
      @bread
      def strange_sandwich(food="--ham--") :
          print food
       
      strange_sandwich()
      #輸出 :
      ##tomatoes#
      #</''''''\>
      # --ham--
      #<\______/>
      # ~salad~

       

      最后回答前面提到的問題:

       

      # 裝飾器makebold用于轉(zhuǎn)換為粗體
      def makebold(fn):
          # 結(jié)果返回該函數(shù)
          def wrapper():
              # 插入一些執(zhí)行前后的代碼
              return "<b>" + fn() + "</b>"
          return wrapper
       
      # 裝飾器makeitalic用于轉(zhuǎn)換為斜體
      def makeitalic(fn):
          # 結(jié)果返回該函數(shù)
          def wrapper():
              # 插入一些執(zhí)行前后的代碼
              return "<i>" + fn() + "</i>"
          return wrapper
       
      @makebold
      @makeitalic
      def say():
          return "hello"
       
      print say()
      #輸出: <b><i>hello</i></b>
       
      # 等同于
      def say():
          return "hello"
      say = makebold(makeitalic(say))
       
      print say()
      #輸出: <b><i>hello</i></b>

       

      內(nèi)置的裝飾器

      內(nèi)置的裝飾器有三個,分別是staticmethod、classmethod和property,作用分別是把類中定義的實例方法變成靜態(tài)方法、類方法和類屬性。由于模塊里可以定義函數(shù),所以靜態(tài)方法和類方法的用處并不是太多,除非你想要完全的面向?qū)ο缶幊獭6鴮傩砸膊皇遣豢苫蛉钡模琂ava沒有屬性也一樣活得很滋潤。從我個人的Python經(jīng)驗來看,我沒有使用過property,使用staticmethod和classmethod的頻率也非常低。

       

      class Rabbit(object):
           
          def __init__(self, name):
              self._name = name
           
          @staticmethod
          def newRabbit(name):
              return Rabbit(name)
           
          @classmethod
          def newRabbit2(cls):
              return Rabbit('')
           
          @property
          def name(self):
              return self._name

       

      這里定義的屬性是一個只讀屬性,如果需要可寫,則需要再定義一個setter:

       

      @name.setter
      def name(self, name):
          self._name = name

       

      functools模塊

      functools模塊提供了兩個裝飾器。這個模塊是Python 2.5后新增的,一般來說大家用的應(yīng)該都高于這個版本。

      2.3.1. wraps(wrapped[, assigned][, updated]): 
      這是一個很有用的裝飾器。看過前一篇反射的朋友應(yīng)該知道,函數(shù)是有幾個特殊屬性比如函數(shù)名,在被裝飾后,上例中的函數(shù)名foo會變成包裝函數(shù)的名字wrapper,如果你希望使用反射,可能會導(dǎo)致意外的結(jié)果。這個裝飾器可以解決這個問題,它能將裝飾過的函數(shù)的特殊屬性保留。

       

      import time
      import functools
       
      def timeit(func):
          @functools.wraps(func)
          def wrapper():
              start = time.clock()
              func()
              end =time.clock()
              print 'used:', end - start
          return wrapper
       
      @timeit
      def foo():
          print 'in foo()'
       
      foo()
      print foo.__name__

       

      首先注意第5行,如果注釋這一行,foo.__name__將是'wrapper'。另外相信你也注意到了,這個裝飾器竟然帶有一個參數(shù)。實際上,他還有另外兩個可選的參數(shù),assigned中的屬性名將使用賦值的方式替換,而updated中的屬性名將使用update的方式合并,你可以通過查看functools的源代碼獲得它們的默認(rèn)值。對于這個裝飾器,相當(dāng)于wrapper = functools.wraps(func)(wrapper)。

      2.3.2. total_ordering(cls): 
      這個裝飾器在特定的場合有一定用處,但是它是在Python 2.7后新增的。它的作用是為實現(xiàn)了至少__lt__、__le__、__gt__、__ge__其中一個的類加上其他的比較方法,這是一個類裝飾器。如果覺得不好理解,不妨仔細(xì)看看這個裝飾器的源代碼:

       

       def total_ordering(cls):
      54      """Class decorator that fills in missing ordering methods"""
      55      convert = {
      56          '__lt__': [('__gt__', lambda self, other: other < self),
      57                     ('__le__', lambda self, other: not other < self),
      58                     ('__ge__', lambda self, other: not self < other)],
      59          '__le__': [('__ge__', lambda self, other: other <= self),
      60                     ('__lt__', lambda self, other: not other <= self),
      61                     ('__gt__', lambda self, other: not self <= other)],
      62          '__gt__': [('__lt__', lambda self, other: other > self),
      63                     ('__ge__', lambda self, other: not other > self),
      64                     ('__le__', lambda self, other: not self > other)],
      65          '__ge__': [('__le__', lambda self, other: other >= self),
      66                     ('__gt__', lambda self, other: not other >= self),
      67                     ('__lt__', lambda self, other: not self >= other)]
      68      }
      69      roots = set(dir(cls)) & set(convert)
      70      if not roots:
      71          raise ValueError('must define at least one ordering operation: < > <= >=')
      72      root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__
      73      for opname, opfunc in convert[root]:
      74          if opname not in roots:
      75              opfunc.__name__ = opname
      76              opfunc.__doc__ = getattr(int, opname).__doc__
      77              setattr(cls, opname, opfunc)
      78      return cls
      posted @ 2016-07-01 23:36  狂師  閱讀(3288)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 精品国产一区二区三区蜜臀| 免费人成视频在线| 国产精品国产三级国产午| 无码日韩精品91超碰| 精品亚洲国产成人av在线| 骚虎视频在线观看| 亚洲 日韩 国产 制服 在线| 成人亚洲综合av天堂| 九九热精品在线观看视频| gogogo高清在线观看视频中文| 国产av一区二区不卡| 亚洲人成亚洲人成在线观看| 欧美乱强伦xxxx孕妇| 国精品人妻无码一区免费视频电影| jizz国产免费观看| 高清不卡一区二区三区| 纳雍县| 偷拍久久大胆的黄片视频| 久久人与动人物a级毛片 | 国产福利视频区一区二区| 清远市| 亚洲欧美牲交| 免费午夜无码片在线观看影院| 暖暖 在线 日本 免费 中文| 自拍偷在线精品自拍偷免费| 老司机性色福利精品视频| 高级艳妇交换俱乐部小说 | 国产成年码av片在线观看| 人妻中文字幕精品系列| 天天躁日日躁狠狠躁中文字幕| 国产视频 视频一区二区| 午夜福利看片在线观看| 久久精品国产亚洲αv忘忧草| 乌克兰丰满女人a级毛片右手影院| 又大又粗又爽18禁免费看| 亚洲成人动漫在线| 久久婷婷成人综合色综合| 亚洲色大成网站WWW国产| 中文字幕亚洲人妻一区| av无码精品一区二区乱子| 天堂av资源在线免费|