Jaglawz: 聽講Python一切都是對象,是嗎?python
Pylego: 是的,像函數也是對象。閉包
Jaglawz: 那麼函數也能夠有本身的屬性了?app
Pylego: 固然,像下面這樣寫是能夠的:函數
def foo(): print('I am foo') def bar(): print('I am bar') foo.bar = bar foo.bar()
Jaglawz: 這都行,那是否是函數也能夠像普通對象同樣看成參數傳遞也能夠看成對象來返回?code
Pylego: 是的,好比下面的用法:對象
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 """
Jaglawz: 好吧,看成參數傳遞和返回我理解了,可是我對wrapper函數的print(string)這個string的查找感到迷惑。內存
Pylego: 不錯嘛!這都被你看出來了,那你知道Python做用域的LEGB原則嗎?作用域
Jaglawz: 我知道是知道能夠我就是對那個E(Enclosing)做用域不是很理解。開發
Pylego: 那就對了,你能夠在剛纔代碼的基礎上運行下面的代碼:string
print(foo.__closure__) # 輸出:(<cell at 0x7fc50f45afd8: function object at 0x7fc50f4168c0>, <cell at 0x7fc50f45aec0: str object at 0x7fc50c065fc0>)
Jaglawz: 咦,這兩個內存地址是啥傢伙?
Pyelgo: 這就是wrapper函數引用的外層函數(就是deco函數啦)的兩個變量:string和func啊!
Jaglawz: 也就是說內層函數(在本例中就是wrapper啦)會把外層函數(在本例用就是deco啦)做用域裏面的對象放到__closure__屬性裏,以供本身查找?
Pylego: 是的,可是不是全部外層函數做用域的對象都會放到內層函數的__closure__屬性裏,僅限本身用到的,這個__closure__就是enclosing做用域啦!
Jaglawz: 原來enclosing做用域是這樣的,明白了。
Pyelgo: 若是內部函數引用到外層函數做用域的對象,這個內部函數就稱爲閉包。
Jaglawz: 原來閉包就是這傢伙,很簡單嘛!
Jaglawz: 咦,我想到內部函數有一個妙用,你看看是否是這樣啊,好比說我想輸出一個函數的運行時間又不想去破壞這個函數的代碼,是否是能夠這樣寫:
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)很麻煩,特意爲你準備了語法糖,來接糖:
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),名字起得多形象啊,裝飾函數嘛!
Jaglawz: 你這麼一說,我就以爲裝飾器咋這麼簡單呢!問題是我看到不少人的裝飾器還帶參數,還有人用類當裝飾器,這又是咋回事呢?
Pylego: 你問題咋恁些?我先吃個飯,下次有空再聊哈!