Python裝飾器是怎麼回事?

2021-06-28 22:08:28 字數 3621 閱讀 8786

python裝飾器在python開發過程中,經常被使用。今天看了廖老師的文章之後,對裝飾器有了更深的理解。整理如下。

比如有乙個現成的函式,它是有別人寫的,我們只能呼叫它不能修改它。但是我又想在我呼叫它的時候能夠在函式呼叫前和呼叫後做一些事情,對該函式進行功能擴充套件。這種在**執行期間動態增加函式功能的需求,我們可以通過「裝飾器」(decorator)來實現。

在實際應用中,比如測試函式的呼叫,我們想每次執行測試**之前進行測試資料的準備,在測試函式執行後進行測試資料的清理。通過裝飾器就可以滿足我們的需求。在py.test單元測試框架中的fixture其實就是裝飾器。

先定義乙個now函式,用來獲取當前的時間。

import time

def now():

print time.localtime()

now()

執行上面這段**,輸出如下:

time.struct_time(tm_year=2015, tm_mon=3, tm_mday=3, tm_hour=16, tm_min=35, tm_sec=16, tm_wday=1, tm_yday=62, tm_isdst=0)
現在,我想要增強now()函式的功能,比如,在函式呼叫前後自動列印日誌,但又不希望修改now()函式的定義。我們要定義乙個能列印日誌的裝飾器decorator,可以定義如下:

def fixture1(func):

print 'before invoking %s' %func.__name__

func(*args,**kw)

print 'after invoking %s' %func.__name__

觀察上面的

@fixture1

def now():

print time.localtime()

呼叫now()函式,不僅會執行now()函式本身,還會在執行now()函式前後列印一行日誌:

before invoking now

time.struct_time(tm_year=2015, tm_mon=3, tm_mday=3, tm_hour=16, tm_min=35, tm_sec=16, tm_wday=1, tm_yday=62, tm_isdst=0)

after invoking now

@fixture1放到now()函式的定義處,相當於執行了語句:

now = fixture1(now)
由於fixtrue1()是乙個decorator,返回乙個函式,所以,原來的now()函式仍然存在,只是現在同名的now變數指向了新的函式,於是呼叫now()將執行新函式,即在fixture1()函式中返回的

我們知道函式都有__name__等屬性,但你去看經過decorator裝飾之後的函式,它們的__name__已經從原來的'now'變成了

>>> now.__name__

functools.wraps就是幹這個事的,在定義@functools.wraps(func)即可。

import functools

def fixture1(func):

@functools.wraps(func)

print 'before invoking %s' %func.__name__

func(*args,**kw)

print 'after invoking %s' %func.__name__

如果decorator本身需要傳入引數,那就需要編寫乙個返回decorator的高階函式,寫出來會更複雜。比如,要自定義輸入日誌的文字。寫完了不帶引數的裝飾器,在不帶引數的裝飾器最外邊再巢狀一層形成3曾巢狀的decorator。

import functools

def fixture2(arg):

def decorator(func):

@functools.wraps(func)

print 'before invoking %s() with %s' % (func.__name__,arg)

func(*args,**kw)

print 'after invoking %s() with %s' % (func.__name__,arg)

return decorator

與不帶引數的裝飾器對比,就是在不帶引數的裝飾器增加了兩行**:

def fixture2(arg):

......

return decorator

能否寫出乙個@fixture的decorator,使它既支援:

@fixture

def now():

print time.localtime()

又支援:

@fixture('parameter')

def now():

print time.localtime()

想都不用想,肯定能了。**如下:

def fixture(arg):

if hasattr(arg,'__call__'):

@functools.wraps(arg)

print 'before invoking %s' %arg.__name__

arg(*args,**kw)

print 'after invoking %s' %arg.__name__

def decorator(func):

@functools.wraps(func)

print 'before invoking %s() with %s' % (func.__name__,arg)

func(*args,**kw)

print 'after invoking %s() with %s' % (func.__name__,arg)

return decorator

就是在帶引數的裝飾器裡面加乙個if語句:

if hasattr(arg,'__call__'):
if語句體裡面寫的就是不帶引數的decorator。

這裡對if語句進行解釋下:

對於任何可呼叫的方法都具有__call__屬性,也就是說,我們可以通過:

hasattr(arg,'__call__')
判斷arg是否是乙個可呼叫方法,如果是乙個可呼叫的方法那麼hasattr應該返回true,如果不是應該返回false。如果arg是乙個可呼叫的方法,那麼fixture就應該是乙個不帶引數的decorator,而如果arg不是乙個可呼叫的方法,那麼fixture應該是乙個帶引數的decorator。

Share Memory協議是怎麼回事

細心的朋友會發現,sql server 2005所支援的網路庫協議中多了乙個share memory協議,那麼它是用在什麼場合的呢?下面是官方文件的一些介紹 從執行在同一臺計算機上的客戶端到 microsoft sql server 的連線使用共享記憶體協議。共享記憶體沒有可配置的屬性。始終會先嘗試...

TBox怎麼回事

新公司有一款tbox的產品,但我對於tbox了解甚少,對車聯網也連線甚少。對網路上的一些資料,我也應該做一些整合,加上這段時間在公司接觸到的事情,完成這篇部落格,寫部落格也是一種激勵自己學習的手段。tbox上通雲端tsp telematics service provider,遠端服務提供商 下連汽...

炒股到底是怎麼回事?

到底是怎麼回事?很久就聽說 有人賺死,有人虧死。只是聽別人說,自己沒多大興趣,因為聽說風險太大,並且虧的可能性很大,呵呵,誰願意去做八成可能虧的買賣呢?但昨天在sohu上看到一條新聞,引起我對它的興趣,所以今天了解了一下。其實我覺得道理好像很簡單,就是你拿一部分錢跟你兄弟說 兄弟,咱們合夥搞一莊生意...