標籤:python 函數 裝飾器 執行過程
0.說明
在自己好好總結並對Python裝飾器的執行過程進行分解之前,對於裝飾器雖然理解它的基本工作方式,但對於存在複雜參數的裝飾器(裝飾器和函數本身都有參數),總是會感到很模糊,即使這會弄懂了,下一次也很快忘記,其實本質上還是沒有多花時間去搞懂其中的細節問題。
雖然網路上已經有很多這樣的文章,但顯然都是別人的思想,因此自己總是記不牢,所以花點時間自己好好整理一下。
最近在對《Python核心編程》做總結,收穫了不少,下面分享一下我自己對於Python裝飾器的理解,後面還提供了一個較為複雜的Python裝飾器的執行過程的分解,可以參考一下。
1.Python裝飾器的出現
在沒有裝飾器之前,如果要在類中定義一個靜態方法,需要使用下面的方法:
class MyClass(object): def staticFoo(): staticFoo = staticmethod(staticFoo)
即要在該靜態方法中加入類似staticmethod()內建函數將該方法轉換為靜態方法,這顯然非常麻煩,而有了裝飾器之後,就可以寫成下面這樣:
class MyClass(object): @staticmethod def staticFoo(): pass
這樣就簡潔很多了。
2.Python裝飾器類型與理解
(1)無參數裝飾器
下面的情況:
@fdef foo(): pass
其實就相當於:
def foo(): passfoo = g(foo)
下面的情況:
@g@fdef foo(): pass
就相當於:
def foo(): passfoo = g(f(foo))
(2)含參數裝飾器
下面的情況:
@decomaker(deco_args)def foo(): pass
就相當於:
def foo(): passfoo = decomaker(deco_args)(foo)
用這樣的思想去理解就非常好理解了:decomaker()用deco_args做了些事並返回函數對象,而該函數對象正是以foo作為其參數的裝飾器。
下面多個裝飾器的例子也是按這樣的思想去理解。
下面的情況:
@deco1(deco_arg)@deco2()def foo(): pass
就相當於:
def foo(): passfoo = deco1(deco_arg)(deco2(foo))
3.Python裝飾器執行過程的手動分解
OK,有了上面的理論基礎,理解下面一個較為複雜的裝飾器就很容易了:
from functools import wrapsdef log(text): def decorator(func): @wraps(func) #it works like:wraper.__name__ = func.__name__ def wrapper(*args, **kwargs): print ‘%s %s():‘ % (text, func.__name__) return func(*args, **kwargs) return wrapper return decorator@log(‘Hello‘)def now(area): print area, ‘2016-01-23‘ now(‘Beijing‘)print ‘The name of function now() is:‘, now.__name__
執行如下:
/usr/bin/python2.7 /home/xpleaf/PycharmProjects/decorator_test/dec10.pyHello now():Beijing 2016-01-23The name of function now() is: now
對於該程式的執行過程,可以分析如下: 1.先執行log(‘Hello‘)函數,此時返回了一個新的函數,只不過其中的text變數被替換為‘Hello‘,所以用來裝飾now函數的新的裝飾器如下: def decorator(func): @wraps(func) #it works like:wraper.__name__ = func.__name__ def wrapper(*args, **kwargs): print ‘%s %s():‘ % (‘Hello‘, func.__name__) return func(*args, **kwargs) return wrapper 2.所以此時的now函數,就相當於: now = decorator(now) 3.即now就相當於: def now(*args, **kwargs): print ‘%s %s():‘ % (‘Hello‘, old_now.__name__) return old_now(*args, **kwargs)# 現在的函數名稱變為了now而不是wrapper,是因為使用了wraps裝飾器 所以,輸出的結果也就非常好理解了。 |
關於wraps,它也是一個裝飾器,使用它的作用是,被我們用自訂裝飾器修改後的函數,它的函數名稱,即func.__name__跟原來是一樣的,而它的工作原理正如上面所提及的,即:
wraper.__name__ = func.__name__
也就是說,使用wraps可以不改變原來函數的屬性,當然,上面只是簡單說明了一下其工作原理,詳細的可以參考wraps的原始碼。
在GitHub上給出了10個理解裝飾器的例子,可以參考一下:https://github.com/xpleaf/decorator
本文選中我的《Python回顧與整理》系列博文中的《Python回顧與整理9:函數和函數式編程》
本文出自 “香飄葉子” 部落格,請務必保留此出處http://xpleaf.blog.51cto.com/9315560/1763567
對Python裝飾器的個人理解方法