Python提高:關於閉包和裝飾器
閉包
閉包是指在一個函式中定義了一個另外一個函式,內函式裡運用了外函式的臨時變數,並且外函式的返回值是內函式的引用,這樣就構成一個閉包。 例如以下:
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,實現了裝飾器複製程式碼