閉包:https://zhuanlan.zhihu.com/p/93846887
裝飾器:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
https://www.cnblogs.com/yaoqingzhuan/p/10628592.html
python參考手冊(cè):相關(guān)章節(jié)
一.預(yù)備知識(shí)
1.作用域、內(nèi)嵌函數(shù)、生命周期
python中的每個(gè)函數(shù)都是一個(gè)新的作用域彤叉,或者理解為命名空間
一般而言,函數(shù)中定義的變量浮庐、內(nèi)嵌函數(shù)等贰逾,其生命周期即所在函數(shù)的作用域母廷,當(dāng)函數(shù)執(zhí)行完畢后搅轿,函數(shù)內(nèi)所定義變量富玷、內(nèi)嵌函數(shù)等都應(yīng)該會(huì)消失。而在下一次調(diào)用時(shí)雀鹃,又會(huì)被重新創(chuàng)建励两。
2.變量搜索
當(dāng)在函數(shù)中訪問(wèn)一個(gè)新的變量時(shí),python會(huì)在當(dāng)前命名空間中尋找該變量是否存在傅瞻。如果不存在則會(huì)從上一級(jí)命名空間中搜尋,直到頂層命名空間俭正。比如:
但是在函數(shù)中對(duì)一個(gè)變量進(jìn)行定義或者賦值時(shí)掸读,python只會(huì)在當(dāng)前命名空間中搜尋該變量,如果不存在儿惫,則會(huì)創(chuàng)建一個(gè)新的變量。如果上一級(jí)命名空間中存在同名的變量留搔,那么上一級(jí)同名變量會(huì)在當(dāng)前作用于中被覆蓋铛铁。
二.閉包
1.定義
將組成函數(shù)的語(yǔ)句和這些語(yǔ)句執(zhí)行環(huán)境打包在一起時(shí),得到的對(duì)象稱為閉包括眠。
2.使用場(chǎng)景
函數(shù)在python中是第一類對(duì)象倍权。也就是說(shuō)可以把他們當(dāng)作參數(shù)傳遞給其他函數(shù)、放在數(shù)據(jù)結(jié)構(gòu)中当船、以及作為函數(shù)的返回結(jié)果等默辨。(這些場(chǎng)景都會(huì)觸發(fā)閉包)
把函數(shù)當(dāng)作數(shù)據(jù)處理時(shí),它將隱式地?cái)y帶與定義該函數(shù)的周圍環(huán)境相關(guān)的信息(該函數(shù)所需的所有信息)廓奕。
3.舉例說(shuō)明
- 示例1:閉包影響自由變量的綁定方式
# foo.py
x = 42
def callf(func):
return func()
# 調(diào)用代碼
import foo
x = 37
def helloworld():
return "Hello world, x is %d" %x
fool.callf(helloworld)
輸出結(jié)果:
Hello world, x is 37
-
示例2:閉包捕捉函數(shù)執(zhí)行所需的整個(gè)環(huán)境
從作用域的角度:foo實(shí)際上是調(diào)用了內(nèi)嵌函數(shù)inner(), 當(dāng)執(zhí)行到inner中的print(x)語(yǔ)句時(shí)桌粉,在inner()中沒(méi)有搜尋到x,然后會(huì)在outer()命名空間中搜尋患亿,找到x后進(jìn)行打印。
從生命周期的角度步藕,foo的值為outer函數(shù)的返回值,當(dāng)執(zhí)行foo()時(shí)沾歪,outer函數(shù)已經(jīng)執(zhí)行完畢了雾消,此時(shí)其作用于內(nèi)定義的變量x也應(yīng)該已經(jīng)銷毀,因此執(zhí)行foo()時(shí)狂窑,當(dāng)執(zhí)行到print(x)時(shí)桑腮,應(yīng)該會(huì)出錯(cuò)。但實(shí)際上并沒(méi)有破讨。
這其實(shí)是python支持函數(shù)閉包的特性。
三.裝飾器
閉包會(huì)捕捉內(nèi)部的環(huán)境信息采呐,因此還可以用于包裝現(xiàn)有函數(shù)搁骑。
1.定義
裝飾器是一個(gè)函數(shù)(也可以是類或者對(duì)象)又固,其主要用途是包裝另一個(gè)函數(shù)或者類。這種包裝的首要目的是光明睜大的修改或者增強(qiáng)被包裝對(duì)象的行為乏冀。
2.分類
1.函數(shù)裝飾器
根據(jù)裝飾器本身是否接受參數(shù)洋只,可以分為:帶參數(shù)的裝飾器和不帶參數(shù)的裝飾器
A.不帶參數(shù)的裝飾器:包裝的函數(shù)func本身,作為裝飾器的入?yún)ⅰ?/p>
@trace
def square(x):
return x*x
#其等價(jià)于:
def square(x):
return x*x
square = trace(square)
# trace代碼的實(shí)現(xiàn)如下
# 備注:內(nèi)部函數(shù)callf作為返回值肢扯,是一個(gè)閉包
def trace(func):
def callf(*args, **kwargs):
debug_log.write('calling args:%s' % args)
r = func(*args, **kwargs)
debug_log.write('%s returned' % r)
return r
return callf
B.帶參數(shù)的裝飾器(多一層對(duì)裝飾器參數(shù)的處理)
首先:參數(shù)param作為裝飾器的入?yún)⒌4福祷氐谝粚雍瘮?shù):W。(第一層:對(duì)裝飾器本身參數(shù)的處理)
然后:將包裝的函數(shù)func本身肛循,作為W的入?yún)⒁瘢瑐鬟f執(zhí)行累舷。(第二層:對(duì)包裝函數(shù)的處理,不帶參數(shù)的裝飾器析孽,只有該層)
# 裝飾器帶參數(shù)的版本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
# 其含義是:
now = log('execute')(now)
# 裝飾器本身不帶參數(shù)的版本:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# 其含義是:
now = log(now)
2.使用類作為裝飾器
類也可以作為裝飾器害捕。
3.使用對(duì)象作為裝飾器
根據(jù)裝飾器的語(yǔ)法尝盼,對(duì)象也可以作為裝飾器。使用對(duì)象裝飾器有時(shí)候會(huì)更加靈活盾沫,例如能夠方便的定制和添加參數(shù)。
4.裝飾器裝飾類
接受類作為輸入佩捞,并返回類作為輸出
備注:
由此可見(jiàn)一忱,@本身是一個(gè)python的語(yǔ)法糖谭确。它只是按照固定格式進(jìn)行展開(kāi),展開(kāi)后只要符合python語(yǔ)法逐哈,不論是:類、對(duì)象禀梳、函數(shù)等肠骆,都允許靈活的搭配使用。