一、python函數(shù)作用域LEGB
python解釋器查找變量的原則(順序):
L→E→G→B
L:Local函數(shù)內(nèi)部作用域
E:enclosing函數(shù)內(nèi)部與內(nèi)嵌函數(shù)之間
G:gobal全局作用域
B:build-in內(nèi)置作用域
example:
value1 = 5
def my_func():
value2 = 6
print(id(value2))
def in_func():
a = max(value1, value2)
print(a)
return in_func
上面示例代碼中max函數(shù)為python的內(nèi)建函數(shù),在in_func
函數(shù)中墙基,max
這個(gè)變量python解釋器首先會(huì)在in_func
這個(gè)函數(shù)中查找寂纪,即local函數(shù)內(nèi)部作用域中查找胆建,然后按順序再到E-G-B查找甥啄,最后在build-in內(nèi)置作用域查找到活尊。
二灰粮、閉包
1仔涩、概念:內(nèi)部函數(shù)中對(duì)enclosing作用域的變量進(jìn)行引用
上述例子中my_func
函數(shù)中的內(nèi)部函數(shù)in_func
引用了enclosing作用域中的value2
變量,這就叫做閉包粘舟。
由于函數(shù)執(zhí)行完后變量會(huì)被回收熔脂,當(dāng)執(zhí)行完my_func
這個(gè)函數(shù)后,value2
變量會(huì)被回收柑肴。那么如果我們?cè)俅握{(diào)用in_func
這個(gè)函數(shù)時(shí)霞揉,需用引用到value2
這個(gè)變量,是否會(huì)報(bào)錯(cuò)晰骑?
看以下代碼:
example:
value1 = 5
def my_func():
value2 = 6
print(id(value2)) #打印value2的ID值
def in_func():
a = max(value1, value2)
print(a)
return in_func
f = my_func() #my_func返回的是in_func适秩,此時(shí)f指向in_func函數(shù)
f() #f()相當(dāng)于in_func(),調(diào)用了infunc函數(shù)
print(f.__closure__)
ouput:
501351024
6
(<cell at 0x0000000000AF8D98: int object at 0x000000001DE20270>,)
如果內(nèi)部函數(shù)引用了enclosing作用域的變量硕舆,會(huì)將變量添加到函數(shù)__closure__
的屬性中去秽荞。當(dāng)再次查找這個(gè)變量時(shí),會(huì)直接去函數(shù)__closure__
的屬性中查找抚官。我們可以看到代碼的輸出結(jié)果第一行即為value2
的內(nèi)存地址(501351024轉(zhuǎn)換為16進(jìn)制:1DE20270)和__closure__
屬性中的int對(duì)象的地址是一樣的( int object at 0x000000001DE20270)扬跋。
2、那么閉包到底有什么用呢凌节?
我們來(lái)看下以下兩段代碼:
(1)胁住、
def func_100(value):
passline = 60
if value >= passline:
print('pass')
else:
print('failed')
def func_150(value):
passline = 90
if value >= passline:
print('pass')
else:
print('failed')
func100(59)
func150(89)
(2)趁猴、
def set_passline(passline):
def in_func(value):
if value >= passline:
print('pass')
else:
print('failed')
return in_func
f_100 = set_passline(60) #passline=60被存儲(chǔ)在f_100的__closure__屬性中
f_150 = set_passline(90) #passline=90被存儲(chǔ)在f_150的__closure__屬性中
f_100(59)
f_150(89)
兩段代碼都能正確判斷滿(mǎn)分是100或150時(shí),分?jǐn)?shù)是否及格彪见。但是第二段代碼儡司,由于運(yùn)用閉包,代碼復(fù)用性更高余指。
3捕犬、閉包更高級(jí)的應(yīng)用
將閉包的概念中的變量變成函數(shù),同樣適用酵镜。即:內(nèi)部函數(shù)中對(duì)enclosing作用域的函數(shù)進(jìn)行引
example:
def my_sum(*args):
return(sum(args))
def my_average(*args):
return sum(args)/len(args)
def dec(func):
def in_dec(*args):
if len(args) == 0: #對(duì)參數(shù)進(jìn)行判斷碉碉,如果沒(méi)有參數(shù)直接返回0
return 0
for i in args:
if not isinstance(i, int): #對(duì)參數(shù)進(jìn)行判斷,如果有一個(gè)參數(shù)不是int類(lèi)型淮韭,直接返回0
return 0
return func(*args) #此處對(duì)enclosing作用域的func函數(shù)進(jìn)行引用
return in_dec
evo_my_sum = dec(my_sum)
evo_my_average = dec(my_average)
PS:此處求和函數(shù)(my_sum)和求平均值函數(shù)(my_average)只對(duì)參數(shù)是否int類(lèi)型進(jìn)行判斷垢粮。
我們來(lái)分析下代碼:evo_my_sum = dec(my_sum)
由于函數(shù)dec
返回的是in_dec
函數(shù)
所以evo_my_sum
指向的是in_dec
函數(shù)
=》evo_my_sum = in_dec
=》evo_my_sum(1, 2, 3) = in_dec(1, 2, 3)
那么in_dec(1, 2, 3)
即evo_my_sum(1, 2, 3)
會(huì)對(duì)求和的參數(shù)先進(jìn)行判斷后,再調(diào)用my_sum
函數(shù)靠粪。
同樣道理:
evo_my_average
會(huì)對(duì)求平均的參數(shù)先進(jìn)行判斷后蜡吧,再調(diào)用my_average
函數(shù)。
這樣就可以對(duì)參數(shù)統(tǒng)一進(jìn)行判斷后再各自調(diào)用不同的函數(shù)占键。
三昔善、裝飾器
裝飾器是用來(lái)裝飾函數(shù)的,它返回一個(gè)函數(shù)對(duì)象畔乙。語(yǔ)法:@Decorator
現(xiàn)在我們已經(jīng)定義了一個(gè)my_sum
函數(shù)
def my_sum(*args):
return(sum(args))
假設(shè)我們要增加my_sum
函數(shù)的功能君仆,比如,在函數(shù)調(diào)用前對(duì)參數(shù)進(jìn)行一個(gè)判斷牲距,但又不希望修改my_sum
函數(shù)的定義返咱,這種在代碼運(yùn)行期間動(dòng)態(tài)增加功能的方式,稱(chēng)之為“裝飾器”(Decorator)牍鞠。
我們要定義一個(gè)能判斷參數(shù)的decorator洛姑,如下:
def dec(func):
def in_dec(*args):
if len(args) == 0:
return 0
for i in args:
if not isinstance(i, int):
return 0
return func(*args)
return in_dec
按照python裝飾器的語(yǔ)法:
@dec
def my_sum(*args):
return(sum(args))
這樣,調(diào)用my_sum
函數(shù)皮服,不僅會(huì)運(yùn)行my_sum
函數(shù)本身,還會(huì)在運(yùn)行my_sum
函數(shù)前参咙,對(duì)參數(shù)進(jìn)行判斷龄广。
把@dec放到my_sum
函數(shù)的定義處,相當(dāng)于執(zhí)行了語(yǔ)句:
my_sum = dec(my_sum)
看到這里是否覺(jué)得有點(diǎn)熟悉蕴侧?
其實(shí)這個(gè)語(yǔ)句和上述閉包的高級(jí)應(yīng)用是一樣的择同,只不過(guò)那部分把my_sum
改成了evo_my_sum
。
裝飾器到這里還差最后一步:
my_sum
函數(shù)經(jīng)過(guò)裝飾后净宵,由于dec(my_sum)
返回的是in_dec
函數(shù)敲才,此時(shí)my_sum.__name__
屬性將從‘my_sum’變成‘in_dec’裹纳,為了保證此屬性不變,需在in_dec
函數(shù)定義前加上語(yǔ)句@functools.wraps(func)
紧武,保證my_sum.__name__
屬性不變剃氧。否則,有些依賴(lài)函數(shù)簽名的代碼執(zhí)行就會(huì)出錯(cuò)阻星。一個(gè)完整的decorator的寫(xiě)法如下:
import functools
def dec(func):
@functools.wraps(func)
def in_dec(*args):
if len(args) == 0:
return 0
for i in args:
if not isinstance(i, int):
return 0
return func(*args)
return in_dec
decorator可以增強(qiáng)函數(shù)的功能朋鞍,定義起來(lái)雖然有點(diǎn)復(fù)雜,但使用起來(lái)非常靈活和方便妥箕。
以上是觀看慕課網(wǎng)《python裝飾器》以及廖雪峰教程python裝飾器的總結(jié)滥酥。