閉包
閉包是指在一個函式中定義了一個另外一個函式,內函式裡運用了外函式的臨時變數,並且外函式的返回值是內函式的引用,這樣就構成一個閉包。 例如以下:
def callFunc():
n = 1
def show():
print('show: ', n)
return show
s = callFunc()
s()複製程式碼
去掉了全域性變數的使用。而且將 show 函式封裝在了 callFunc 函式內部,使外部不可見,不能使用 show 函式,隱藏了實現細節
程式在執行時,callFunc 函式返回了內部定義的 show 函式,並且 在 show 函式內部使用了外部函式的變數。
在 show 函式返回時,儲存了當前的執行環境,也就是會在 show 函式中使用的外部變數 n 。
因為 n 是一個 callFunc 函式中的區域性變數,正常情況下 callFunc 函式執行結束後,n 就會被釋放。
但是現在因為 callFunc 函式中返回了 show 函式,show 函式在外部還會再執行,所以程式會將 show 函式所需的執行環境儲存下來。
這種形式就是閉包。
裝飾器
裝飾器本身也是一個函式 ,作用是為現有存在的函式,在不改變函式的基礎上去增加一些功能進行裝飾。
裝飾器實際上就是一個函式 ,這個函式以閉包的形式定義使用 @裝飾器函式名 形式來裝飾。
比如現在一個專案中,有很多函式 ,由於專案越來越大,功能越來越多,導致程式越來越慢。
其中一個功能函式功能,實現一百萬次的累加。
def my_count():
s = 0
for i in range(1000001):
s += i
print('sum : ', s)複製程式碼
現在想計算一下函式的執行時間,如何解決?如何能應用到所有函式上?
解決辦法:
import time
def count_time(func):
def wrapper(): #wrapper 裝飾
start = time.time()
func()
end = time.time()
print('共計執行:%s 秒'%(end - start)) # 使用%d顯示,取整後是0秒,因為不到一秒
return wrapper
@count_time # 這實際就相當於解決方法3中的 my_count = count_tiem(my_count)
def my_count():
s = 0
for i in range(10000001):
s += i
print('sum : ', s)
my_count()複製程式碼
這樣實現的好處是,定義好了閉包函式後。只需要通過 @xxx 形式的裝飾器語法,將 @xxx 加到要裝飾的函式前即可。
萬能裝飾器
def setFunc(func):
def wrapper(*args, **kwargs): # 接收不同的引數
print('wrapper context')
return func(*args, *kwargs) # 再原樣傳回給被裝飾的函式
return wrapper
@setFunc
def show(name, age):
print(name,age)
show('tom',12)
複製程式碼
通過可變引數和關鍵字引數來接收不同的引數型別。
類實現裝飾形式
通過類的定義也可以實現裝飾器形式。
在類中通過使用 __init__ 和 __call__方法來實現
class Test(object):
# 通過初始化方法,將要被裝飾的函式傳進來並記錄下來
def __init__(self, func):
self.__func = func
# 重寫 __call__ 方法來實現裝飾內容
def __call__(self, *args, **kwargs):
print('wrapper context')
self.__func(*args, **kwargs)
# 實際通過類的魔法方法call來實現
@Test # --> show = Test(show) show由原來引用函式,裝飾後變成引用Test裝飾類的物件
def show():
pass
show() # 物件呼叫方法,實際上是呼叫魔法方法call,實現了裝飾器複製程式碼