前幾天學(xué)習(xí)python裝飾器時唁桩,看各種例子,上來就是一個嵌套函數(shù)报辱,還返回一個函數(shù)對象(返回內(nèi)嵌函數(shù))单山,學(xué)得我是一臉懵逼啊,覺得太難懂了米奸。今天又來學(xué)習(xí),才發(fā)現(xiàn)慢睡,原來要理解裝飾器铡溪,先得了解閉包的知識。沒錯髓涯,就是閉包哈扮。
閉包又是個什么東西膀驹佟包各?怎么好像更難懂啊。別著急髓棋。。膳犹。其實我之前沒搞懂的那個嵌套函數(shù)就是閉包的寫法,下面記錄了學(xué)習(xí)筆記须床,來一起學(xué)習(xí)吧渐裂。
閉包(closure)
認(rèn)識閉包
咱們用好理解一點的Python的語言介紹一下,一個閉包就是你調(diào)用了一個函數(shù)A族阅,這個函數(shù)A返回了一個函數(shù)B給你膝捞。這個返回的函數(shù)B就叫做閉包。你在調(diào)用函數(shù)A的時候傳遞的參數(shù)就是自由變量
舉個栗子:
def func(name):
def inner_func(age):
print 'name:', name, 'age:', age
return inner_func
bb = func('lucy')
bb(26)
# >>> name: lucy age: 26
這里面調(diào)用func的時候就產(chǎn)生了一個閉包——inner_func,并且該閉包持有自由變量——name鲤遥,因此這也意味著林艘,當(dāng)函數(shù)func的生命周期結(jié)束之后,name這個變量依然存在钢坦,因為它被閉包引用了咕村,所以不會被回收。
閉包的作用
閉包的最大特點是可以將父函數(shù)的變量與內(nèi)部函數(shù)綁定懈涛,并返回綁定變量后的函數(shù)(也即閉包)批钠,此時即便生成閉包的環(huán)境(父函數(shù))已經(jīng)釋放,閉包仍然存在埋心,這個過程很像類(父函數(shù))生成實例(閉包),不同的是父函數(shù)只在調(diào)用時執(zhí)行闲坎,執(zhí)行完畢后其環(huán)境就會釋放茬斧,而類則在文件執(zhí)行時創(chuàng)建,一般程序執(zhí)行完畢后作用域才釋放项秉,因此對一些需要重用的功能且不足以定義為類的行為,使用閉包會比使用類占用更少的資源怖喻,且更輕巧靈活岁诉。
練習(xí)
如下代碼,求和涕癣,求平均數(shù):
#!/usr/bin/python
# -*-coding:utf-8-*-
def my_sum(*arg):
a = sum(arg)
print a
return a
def my_average(*arg):
b = sum(arg)/len(arg)
print b
return b
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
當(dāng)調(diào)用my_sum時傳入的參數(shù)有str,調(diào)用my_average時傳入的參數(shù)為空時代碼會報錯恬叹,所有增加代碼判斷傳入的參數(shù)
#!/usr/bin/python
# -*-coding:utf-8-*-
def my_sum(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
a = sum(arg)
print a
return a
def my_average(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
b = sum(arg)/len(arg)
print b
return b
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6') # 這2個調(diào)用都會報錯同眯,所有要加判斷,來判斷輸入的參數(shù)
my_average()
那么問題來了须蜗,以上2個函數(shù),每個函數(shù)中對于傳入?yún)?shù)的判斷代碼是重復(fù)的菱农,一旦修改柿估,就需要多個地方修改,工作量就大了的妖,對于代碼的維護也不便。那么怎么優(yōu)化代碼了嫂粟,這時候就可以引入閉包了
#!/usr/bin/python
# -*-coding:utf-8-*-
def my_sum(*arg):
a = sum(arg)
print a
return a
def my_average(*arg):
b = sum(arg)/len(arg)
print b
return b
def dec(func):
def in_dec(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
func(*arg)
return in_dec
my_sum = dec(my_sum)
my_average =dec(my_average)
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6') # 這2個調(diào)用都會報錯,所有要加判斷零抬,來判斷輸入的參數(shù)
my_average()
裝飾器(Decorator)
認(rèn)識裝飾器
“裝飾器的功能是將被裝飾的函數(shù)當(dāng)作參數(shù)傳遞給與裝飾器對應(yīng)的函數(shù)(名稱相同的函數(shù))宽涌,并返回包裝后的被裝飾的函數(shù)”
其實裝飾器就是對閉包的使用
特點:
1、裝飾器是用例裝飾函數(shù)褥芒;
2嫡良、返回一個函數(shù)對象;
3寝受、被標(biāo)識函數(shù)標(biāo)識符指向:返回的函數(shù)對象;
4京闰、語法糖 @
練習(xí)
上面閉包的例子甩苛,使用裝飾器后的代碼如下:
#!/usr/bin/python
# -*-coding:utf-8-*-
def dec(func):
def in_dec(*arg):
if len(arg) == 0:
return 0
for val in arg:
if not isinstance(val, int):
return 0
func(*arg)
return in_dec
@dec
def my_sum(*arg):
a = sum(arg)
print a
return a
@dec
def my_average(*arg):
b = sum(arg)/len(arg)
print b
return b
# my_sum = dec(my_sum)
# my_average =dec(my_average)
my_sum(1,2,3,4,5)
my_average(1,2,3,4,5)
my_sum(1,2,3,4,5,'6') # 這2個調(diào)用都會報錯讯蒲,所有要加判斷,來判斷輸入的參數(shù)
my_average()
實際就是將調(diào)用dec函數(shù)墨林,使用@dec代替
# my_sum = dec(my_sum)
# my_average =dec(my_average)
調(diào)用過程分析:
1旭等、@dec后,my_sum作為一個參數(shù)傳給dec隙袁,dec(my_sum) 返回一個函數(shù)對象in_dec
2、返回的函數(shù)對象in_dec猜揪,由誰來接收呢惭墓?my_sum 原來指向的是求和函數(shù),但現(xiàn)在my_sum 作為一個參數(shù)傳給了dec函數(shù)划咐,然后他當(dāng)作一個enclosing作用域的變量(需要了解函數(shù)作用域LEGB)钧萍,被in_dec函數(shù)所使用,so风瘦,my_sum 重新賦了值万搔,my_sum = in_dec
3、my_average()實際是調(diào)用in_dec(),且裝飾器里重新調(diào)用了my_average()函數(shù)