Python中的閉包與裝飾器的用法詳解
閉包
Python函數(shù)是支持嵌套的。如果在一個(gè)內(nèi)部函數(shù)中對(duì)外部函數(shù)作用域(非全局作用域)的變量進(jìn)行引用,那么內(nèi)部函數(shù)就會(huì)被稱(chēng)為閉包。
閉包需要滿足如下3個(gè)條件:
1.存在于兩個(gè)嵌套關(guān)系的函數(shù)中,并且閉包是內(nèi)部函數(shù);
2.內(nèi)部函數(shù)引用了外部函數(shù)的變量(自由變量);
3.外部函數(shù)會(huì)把內(nèi)部函數(shù)的函數(shù)名稱(chēng)返回。
示例:
裝飾器
1、裝飾器本質(zhì)上是一個(gè)Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。它經(jīng)常用于有切面需求的場(chǎng)景,比如:插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限校驗(yàn)等場(chǎng)景。裝飾器是解決這類(lèi)問(wèn)題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。
2、裝飾器是一個(gè)函數(shù),它需要接收一個(gè)參數(shù),該參數(shù)表示被修飾的函數(shù)。
例如,有如下一個(gè)裝飾器函數(shù):
def w1(func): print(‘正在裝飾') def inner(): print(‘正在驗(yàn)證權(quán)限') return inner()
- 裝飾器是個(gè)嵌套函數(shù)
- 內(nèi)部函數(shù)是一個(gè)閉包。
- 外部函數(shù)接收的是被修飾的函數(shù)(func)
3、通過(guò)在函數(shù)定義的前面添加@符號(hào)和裝飾器名,實(shí)現(xiàn)裝飾器對(duì)函數(shù)的包裝。
給f1函數(shù)加上裝飾器,示例如下:
@w1 def f1(): print('f1')
此時(shí),程序會(huì)自動(dòng)編譯生成調(diào)用裝飾器函數(shù)的代碼,等價(jià)于: f1 = w1(f1)
多個(gè)裝飾器
多個(gè)裝飾器應(yīng)用在一個(gè)函數(shù)上,調(diào)用順序是從下至上。
@w1 @w2 def f1(): print(‘---f1---')
執(zhí)行順序:先執(zhí)行@w2,后執(zhí)行@w1
帶參數(shù)的裝飾器
假設(shè)我們前文的裝飾器需要完成的功能不僅僅是能在進(jìn)入某個(gè)函數(shù)后打出log信息,而且還需指定log的級(jí)別,那么裝飾器就會(huì)是這樣的。
def logging(level): def wrapper(func): def inner_wrapper(*args, **kwargs): print "[{level}]: enter function {func}()".format( level=level, func=func.__name__) return func(*args, **kwargs) return inner_wrapper return wrapper @logging(level='INFO') def say(something): print "say {}!".format(something) # 如果沒(méi)有使用@語(yǔ)法,等同于 # say = logging(level='INFO')(say) @logging(level='DEBUG') def do(something): print "do {}...".format(something) if __name__ == '__main__': say('hello') do("my work")
- 當(dāng)帶參數(shù)的裝飾器被打在某個(gè)函數(shù)上時(shí),比如@logging(level=‘DEBUG’),它其實(shí)是一個(gè)函數(shù),會(huì)馬上被執(zhí)行,只要這個(gè)它返回的結(jié)果是一個(gè)裝飾器時(shí),那就沒(méi)問(wèn)題。
基于類(lèi)實(shí)現(xiàn)的裝飾器
裝飾器函數(shù)其實(shí)是這樣一個(gè)接口約束,它必須接受一個(gè)callable對(duì)象作為參數(shù),然后返回一個(gè)callable對(duì)象。
在Python中一般callable對(duì)象都是函數(shù),但也有例外。只要某個(gè)對(duì)象重載了__call__()方法,那么這個(gè)對(duì)象就是callable的。
class Test(): def __call__(self): print 'call me!' t = Test() t() # call me
像__call__
這樣前后都帶下劃線的方法在Python中被稱(chēng)為內(nèi)置方法,有時(shí)候也被稱(chēng)為魔法方法。
重載這些魔法方法一般會(huì)改變對(duì)象的內(nèi)部行為。上面這個(gè)例子就讓一個(gè)類(lèi)對(duì)象擁有了被調(diào)用的行為。
回到裝飾器上的概念上來(lái),裝飾器要求接受一個(gè)callable對(duì)象,并返回一個(gè)callable對(duì)象(不太嚴(yán)謹(jǐn),詳見(jiàn)后文)。
那么用類(lèi)來(lái)實(shí)現(xiàn)也是也可以的。我們可以讓類(lèi)的構(gòu)造函數(shù)__init__()
接受一個(gè)函數(shù),然后重載__call__()
并返回一個(gè)函數(shù),也可以達(dá)到裝飾器函數(shù)的效果。
class logging(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print "[DEBUG]: enter function {func}()".format( func=self.func.__name__) return self.func(*args, **kwargs) @logging def say(something): print "say {}!".format(something)
帶參數(shù)的類(lèi)裝飾器
如果需要通過(guò)類(lèi)形式實(shí)現(xiàn)帶參數(shù)的裝飾器,那么會(huì)比前面的例子稍微復(fù)雜一點(diǎn)。那么在構(gòu)造函數(shù)里接受的就不是一個(gè)函數(shù),而是傳入的參數(shù)。通過(guò)類(lèi)把這些參數(shù)保存起來(lái)。然后在重載__call__方法是就需要接受一個(gè)函數(shù)并返回一個(gè)函數(shù)。
class logging(object): def __init__(self, level='INFO'): self.level = level def __call__(self, func): # 接受函數(shù) def wrapper(*args, **kwargs): print "[{level}]: enter function {func}()".format( level=self.level, func=func.__name__) func(*args, **kwargs) return wrapper #返回函數(shù) @logging(level='INFO') def say(something): print "say {}!".format(something)
內(nèi)置的裝飾器
內(nèi)置的裝飾器和普通的裝飾器原理是一樣的,只不過(guò)返回的不是函數(shù),而是類(lèi)對(duì)象。 在了解這個(gè)裝飾器前,你需要知道在不使用裝飾器怎么寫(xiě)一個(gè)屬性。
def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x # create a property x = property(getx, setx, delx, "I am doc for x property")
以上就是一個(gè)Python屬性的標(biāo)準(zhǔn)寫(xiě)法,其實(shí)和Java挺像的,但是太羅嗦。有了@語(yǔ)法糖,能達(dá)到一樣的效果但看起來(lái)更簡(jiǎn)單。
@property def x(self): ... # 等同于 def x(self): ... x = property(x)
帶有返回值的裝飾器
def func(functionName): def func_in(): return functionName() return func_in @func def test(): return ‘itheima'
到此這篇關(guān)于Python中的閉包與裝飾器的用法詳解的文章就介紹到這了,更多相關(guān)Python裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PyTorch中的方法torch.randperm()示例介紹
在 PyTorch 中,torch.randperm(n) 函數(shù)用于生成一個(gè)從 0 到 n-1 的隨機(jī)排列的整數(shù)序列,這篇文章主要介紹了PyTorch中的方法torch.randperm()介紹,需要的朋友可以參考下2024-05-05詳解pandas數(shù)據(jù)合并與重塑(pd.concat篇)
這篇文章主要介紹了詳解pandas數(shù)據(jù)合并與重塑(pd.concat篇),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07

深入理解python中sort()與sorted()的區(qū)別

關(guān)于python3安裝pip及requests庫(kù)的導(dǎo)入問(wèn)題

使用grpc實(shí)現(xiàn)golang后端和python服務(wù)間通信

Python+turtle繪制對(duì)稱(chēng)圖形的示例代碼