閉包: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)境
image.png
從作用域的角度: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)?,返回第一層函?shù):W。(第一層:對(duì)裝飾器本身參數(shù)的處理)
然后:將包裝的函數(shù)func本身,作為W的入?yún)?,傳遞執(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ù)等,都允許靈活的搭配使用。
