Jaglo: 聽講Python一切都是物件,是嗎?
Pylego: 是的,像函式也是物件。
Jaglo: 那麼函式也可以有自己的屬性了?
Pylego: 當然,像下面這樣寫是可以的:
1 2 3 4 5 6 7 8 |
def foo(): print('I am foo') def bar(): print('I am bar') foo.bar = bar foo.bar() |
Jaglo: 這都行,那是不是函式也可以像普通物件一樣當作引數傳遞也可以當作物件來返回?
Pylego: 是的,比如下面的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def deco(func): string = 'I am deco' def wrapper(): print(string) func() return wrapper def foo(): print('I am foo') foo = deco(foo) foo() """ 輸出: I am deco Iam foo """ |
Jaglo: 好吧,當作引數傳遞和返回我理解了,但是我對wrapper函式的print(string)這個string的查詢感到迷惑。
Pylego: 不錯嘛!這都被你看出來了,那你知道Python作用域的LEGB原則嗎?
Jaglo: 我知道是知道可以我就是對那個E(Enclosing)作用域不是很理解。
Pylego: 那就對了,你可以在剛才程式碼的基礎上執行下面的程式碼:
1 2 |
print(foo.__closure__) # 輸出:(, ) |
Jaglo: 咦,這兩個記憶體地址是啥傢伙?
Pyelgo: 這就是wrapper函式引用的外層函式(就是deco函式啦)的兩個變數:string和func啊!
Jaglo: 也就是說內層函式(在本例中就是wrapper啦)會把外層函式(在本例用就是deco啦)作用域裡面的物件放到__closure__屬性裡,以供自己查詢?
Pylego: 是的,但是不是所有外層函式作用域的物件都會放到內層函式的__closure__屬性裡,僅限自己用到的,這個__closure__就是enclosing作用域啦!
Jaglo: 原來enclosing作用域是這樣的,明白了。
Pyelgo: 如果內部函式引用到外層函式作用域的物件,這個內部函式就稱為閉包。
Jaglo: 原來閉包就是這傢伙,很簡單嘛!
Jaglo: 咦,我想到內部函式有一個妙用,你看看是不是這樣啊,比如說我想輸出一個函式的執行時間又不想去破壞這個函式的程式碼,是不是可以這樣寫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import time def time_machine(func): def wrapper(*args, **kwargs): start_time = time.time() func(*args, **kwargs) print(u'共耗時: %s秒' % (time.time()-start_time)) return wrapper def foo(): time.sleep(3) foo = time_machine(foo) foo() |
Pylego: 你這智商要衝出宇宙的節奏啊!但是Python的開發者早就想到每次foo = time_machine(foo)很麻煩,特地為你準備了語法糖,來接糖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import time def time_machine(func): def wrapper(*args, **kwargs): start_time = time.time() func(*args, **kwargs) print(u'共耗時: %s秒' % (time.time()-start_time)) return wrapper @time_machine def foo(): time.sleep(3) """ 也就是說: @time_machine def foo(): pass 相當於: foo = time_machine(foo),這裡是重點,這裡是重點,這裡是重點,以後的談話能不能理解就看你對這個語句可理解! """ foo() |
Pylego: time_machine就是裝飾器(decorator),名字起得多形象啊,裝飾函式嘛!
Jaglo: 你這麼一說,我就覺得裝飾器咋這麼簡單呢!問題是我看到很多人的裝飾器還帶引數,還有人用類當裝飾器,這又是咋回事呢?
Pylego: 你問題咋恁些?我先吃個飯,下次有空再聊哈!
未完,待續。。。