python decorator心得體會,pythondecorator

來源:互聯網
上載者:User

python decorator心得體會,pythondecorator
python decorator心得體會

  • 前言
  • 用途
前言

此小短文來源於qq群一位朋友的問題,問題如下:

下面這段代碼的功能是什嗎?def log(func):    def wrapper(*args, **kw):        print 'call %s():' % func.__name__        return func(*args, **kw)    return wrapper

經過仔細分析,這個log函數接受func作為參數,然後返回一個內建函式wrapper,同時如果外部使用者繼續使用返回的wrapper繼續調用的話,實際上會通過print列印日誌,然後調用最先傳遞的參數func。

一個簡單的測試例子如下

def addAB(a, b):    return a+bprint log(addAB)(1,21) 執行輸出結果如下:call addAB(): 22

對於上面的例子,拆解成兩個部分分析。第一部分:使用log(addAB),它將addAB作為參數傳遞到log中,也就是說func=addAB,然後會返回內部的wrapper函數。第二部分: log(addAB)(1,21)被轉換為wrapper(1,21),wrapper使用的可變參數形式,可以接受任意參數,wrapper函數首先列印print,然後調用func(*args, **kw),也就是相當於調用addAB(1,21),這也就可以說明print log(addAB)(1,21)的列印結果輸出。

通過log的封裝,對函數調用首先列印日誌,然後再回到原始的函數調用。通過這個很簡潔的函數,不需要修改func的代碼,就可以對func函數實現調用日誌的功能。後來,問這個問題的朋友告訴我了一種更簡潔的方案,不需要顯式的使用log,例子如下

@log    def addAB(a, b):    return a+bprint addAB(1,21)執行結果輸出與上面相同

這種奇特的文法立刻燃起了我的興趣,@log到底執行了什嗎?於是開始查看相關文檔,找了一些文檔,知道這種文法叫做decorator,常用在staticmethod和classmethod中。
python文檔如下:

  • functools
  • term-decorator
  • staticmethod
  • types
用途給方法添加新的功能

這個前面已經介紹過了,請參見給方法添加新的功能。

前面的例子通過decorator的封裝,可以給函數的調用添加日誌。當然你可以針對自己的需求,增加自己想要的功能。在python cookbook中有通過 decorator 添加了諸如跟蹤、日誌記錄、儲存/緩衝、線程鎖定以及輸出重新導向之類的功能(本人未看過),但是根據上面的內容,應該可以看出是可以做到的吧。

例子1:輸出增加html格式輸出

def h1(func):    def wrapper(*args, **kw):        return '<h1>'+func(*args, **kw)+'</h1>'    return wrapperdef li(func):    def wrapper(*args, **kw):        return '<li>'+func(*args, **kw)+'</li>'    return wrapper    @h1@lidef foo():    return 'hello world'print foo() 執行輸出如下<h1><li>hello world</li></h1>

可以看出經過兩個decorator方法,輸出結果確實增加了html標準文法格式。

例子2:用作快取作業

cacheDic={}def cache(func):    def wrapper(*args, **kw):        #print 'args=', args, ' kw=',kw        if args in cacheDic:            return cacheDic[args]        else:            cacheDic[args] = func(*args, **kw)            return cacheDic[args]    return wrapper#@cachedef fib(n):    if n < 2:        return 1    else:        return fib(n-1) + fib(n-2)import timeits = '[fib(x) for x in range(20)]'s_cache='[cache(fib)(x) for x in range(20)]'print timeit.timeit(stmt=s, setup='from __main__ import fib', number=100)print timeit.timeit(stmt=s_cache, setup='from __main__ import fib, cache', number=100)    輸出結果如下:0.5700328350070.00699996948242

可以看出使用了cache之後,時間減少了100倍。這個是使用的timeit的統計方法。由於如果使用了@cache的方式,自動給fib函數增加了cache功能。為了表示對比,這裡使用了顯式的cache方案,使用了cache(fib)(x)這樣的調用方式,它等價於使用@cache。

給類增加,刪除,修改方法
  • 角度1:模組載入時修改
    通過改寫類的__new__方法,可給類增加方法,刪除方法,修改方法。基本方向是覆蓋__new__方法,修改__new__方法的class參數,例如給class參數賦值新的方法,即可以給類增加新的method,或者del掉class的某一個方法,即可以刪除類的方法,或者直接將類的某一個方法用的新的方法代替,即可以修改方法。不過需要注意的是,這些修改都是在new方法調用的時候完成的,即建立類對象的時候完成的,並不需要建立任何執行個體變數,或者說實在模組載入期間完成。

  • 角度2:運行期修改
    可以通過decorator裝飾器,在建立完執行個體對象之後調用方法的時刻,使其調用另外的實現代碼,同樣可以實現修改方法的目的。

具體請看下面的一個例子:

def minus(self, x, y):    return x-y;def multi(self, x, y):    return x*y;def div(self, x, y):    return x*1.0/y;def sayHello(self, *args, **kw):    print 'hello', self.__class__.__name__def runSayHelloDecorator(func):    def wrapper(self, *args, **kw):        func(*args, **kw)        print 'in runtime, i have teached you to say hello runtime'    return wrapperdef change_pc_method(func):    print 'change'    if func.__name__ != '__new__':        return func #do not change other name method    #here goes other add or delete or change method        def wrapper(cls, *args, **kw):        #add        cls.minus = minus        cls.multi = multi        cls.div   = div        #change        cls.sayHello = sayHello        #delete        if hasattr(cls, 'to_delete'):            del cls.to_delete        return super(cls.__class__, cls).__new__(cls, *args, **kw)        return wrapperclass PC(object):    def add(self, x, y):        return x+y;     @change_pc_method    def __new__():        pass    def to_delete():        print 'this method to be deleted'    def sayHello():        print 'i can not say Hello, you can help me?'    @runSayHelloDecorator    def runSayHello():        print 'in runtime, i can not say hello'pc = PC()print pc.add(3,2),print pc.minus(3,2),print pc.div(3,2)pc.sayHello()pc.runSayHello()try:    pc.to_delete()except Exception, e:    print epc2=PC()    程式輸出結果如下:change5 1 1.5hello PCin runtime, i can not say helloin runtime, i have teached you to say hello runtime'PC' object has no attribute 'to_delete'

上面的例子修改通過change_pc_method這種decorator覆蓋了類的new方法,在其中對cls類對象增加了minus,multi,div三個方法,同時將其預設的sayHello的實現進行了修改。還有,通過運行期修飾函數調用的方式修改了runSayHello的實現。

注意:以上有一個小小的缺陷,在覆蓋new方法修改sayHello中,將原有的sayHello給拋棄了。暫時並沒有想到什麼好的方案來做,很多時候原有的實現通常是有用的,這需要一些改進。如果您知道,請告知我。

參數化的decorator

參數化為運行時的決策提供了棲息地。本人並沒有深刻理解這一思想的妙處,僅擺放在這裡,便於後記。

#arg_wrapper, func_wrapper, wrapper#以後命名統一如此命名:arg, func,wrapperdef arg_wrapper(word):    def func_wrapper(func):        def wrapper(self, *args, **kw):            print self, word            return func( *args, **kw) #func本身已經攜帶self參數        return wrapper    return func_wrapperdef FooMake(word):    class Foo(object):        @arg_wrapper(word)        def say():            pass    return Foo()foo1 = FooMake('this')foo2 = FooMake('that')foo1.say()foo2.say()程式輸出結果:<decorator_parameter.Foo object at 0x6ffffe60bd0> this<decorator_parameter.Foo object at 0x6ffffe74c50> that

可以看出:say方法經過word參數化後具有不同的行為。不過本人還未知曉其用途

更改方法的預設調用行為

使用最基本的函數功能改變操作,通過裝飾器擴充已有函數的功能。

def func_square(func):def wrapper(arg):    if hasattr(arg, '__getitem__'):        return map(func, arg);    return func(arg)    return wrapper@func_squaredef square(x):    return x*x;print square(10)    print square([i for i in range(10)])程式輸出結果:100[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

原始的square方法只能對單個元素求平方,通過擴充square的功能,還可以對列表的所有元素求平方。

2和3的整合

可愛的python一文中,提出了2和3的整合的一些引子。出於練習的角度,想寫一個自動化一些的工具,奈何沒有激情了,先就這樣吧。

文章核心思想借用來源:可愛的 Python: Decorator 簡化元編程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.