閉包
閉包其實利用了函數嵌套的概念,一般函數在內部定義一個變量,在外部由於作用域的關係是調用不到的,而閉包是將變量包起來,不管在哪裏都可以調用的到。
函數的嵌套定義:函數內定義了另外一個函數
# 示例1
name='Alice'
def outer():
def inner():
print(name)
print(inner.__closure__)
return inner
其中outer稱爲外部函數,inner稱爲內部函數。
對於一個函數,outer是其函數名,outer()爲函數的調用,python中函數名可以用做函數的參數也可以作爲函數的返回值。
那麼什麼是閉包呢?
閉包滿足的三個條件:
1. 必須是嵌套函數;
2. 內部函數必須引用外部函數中的變量
3. 外部函數返回了內部函數或者外部函數調用了內部函數
查看是否爲閉包:__closure__
#示例2
def outer():
name = 'Alice'
def inner():
print(name)
print(inner.__closure__) #(<cell at 0x000001B4AEF3A0D8: str object at 0x000001B4AEF190F0>,)
return inner
可以試一下示例1的__closure__
值,值是None,所以示例1並不是閉包,原因是內部函數沒有引用外部函數的變量。
內部函數的調用
f=outer() #調用外部函數,返回內部函數並存給f
f() #調用內部函數
注意:
內函數不能直接被調用。
閉包中被內部函數引用的變量,不會因爲外部函數結束而被釋放掉,而是一直存在內存中,直到內部函數被調用結束。
閉包變量
局部變量的作用域在函數內部,如果內部函數想修改外部函數的變量,有兩種方法:
1.將變量聲明爲nonlocal
,這表明變量不僅在函數內部生效,還在上一級函數中生效
def outer():
name = 'Alice'
def inner():
nonlocal name
name='Haha'
print(name)
return inner
f=outer()
f()
2.把閉包變量改成可變類型數據進行修改,比如列表。
def outer():
name = ['Alice']
def inner():
name=['Haha']
print(name[0])
return inner
f=outer()
f()
注意:
1.nonlocal
關鍵字只能用於嵌套函數中,並且外部函數中定義了相應的局部變量
2.外函數被調用一次返回了內函數,因此實際上閉包變量只有一份,以後每次調用內函數,都使用同一份閉包變量,一旦在內部函數修改了閉包變量,則這個閉包變量的值就已經修改了,不是最初的值了。
裝飾器
裝飾器本質上是一個函數,使用了閉包的特性,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。
現有函數:
def sleep_f():
time.sleep(3)
如果要計算該函數的運行時間,代碼如下
start_time=time.time()
sleep_f()
end_time=time.time()
print("程序運行了{0:.4f}秒".format(end_time-start_time))
缺點:
1.調用sleep_f()依舊無法打印運行時間。
2.如果還有其他很多函數要顯示運行時間,同樣的代碼得寫很多遍。
解決方法:使用裝飾器。
定義裝飾器如下:
def timer(func):
def inner(*args, **kwargs):
start_time=time.time()
func(*args, **kwargs)
end_time=time.time()
print("程序運行了{0:.4f}秒".format(end_time-start_time))
return inner
裝飾器裝飾函數的方法:函數的上一行加 @裝飾器名,其中,@符號是裝飾器的語法糖
裝飾器使用的兩種方法:
裝飾器不帶參數
# 被裝飾函數不帶參數
@timer
def sleep_f():
time.sleep(3)
#調用sleep_f,這時候就會打印運行時間了
sleep_f()
# 被裝飾函數帶參數
@timer
def add(a,b):
print(a+b)
time.sleep(2)
add(2,3)
裝飾器帶參數
timer
裝飾器打印了函數的運行時間,如果還想打印日誌,即想要裝飾器含參數,需要在timer
的外層再裝飾一層函數
def flog(name):
def timer(func):
def inner(*args, **kwargs):
print("調用{}模塊的{}函數".format(name,inner.__name__))
start_time=time.time()
func(*args, **kwargs)
end_time=time.time()
print("程序運行了{0:.4f}秒".format(end_time-start_time))
return inner
return timer
@flog('module_a')
def add(a,b):
print(a+b)
time.sleep(2)
# 調用
add(6,7)
推薦閱讀
你點的每個好看,我都認真當成了喜歡