對於初學者而言,閉包的概念一直都是很難理解的。
我在學習的時候,網上查了那么多資料,依然是一知半解。
直到后來我看了這篇文章:http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/
這是一篇很好的文章,由淺入深的告訴你到底閉包,裝飾器都是什么東西。
E文不過關?沒關系,我來用我自己的理解和你們聊聊命名空間、閉包和裝飾器的那些事兒。
前提:
閱讀這篇文章的前提,是你要對Python,有基礎的理解,如果你有興趣,但是對python還沒有理解,我推薦你去讀下面的教程:
www.liaoxuefeng.com
同時,微信公眾號:easypython,中文名:說人話的python分享,也是很不錯的教程,建議有一定入門基礎的同學嘗試。
說人話的python實戰計划:http://study.163.com/course/introduction/1002794001.htm
開始:
命名空間
命名空間(namespace)這個概念,有時也被稱為“作用域”,在大多數講解閉包的時候都會有所涉及。
那什么是命名空間/作用域呢?
這兩個概念,是用來描述一個變量的屬性的,它們的意思,就是字面意思:“這個變量起名字的地方/這個變量起作用的域”.
我們用實例來說明一下:
def func(): x = 1
簡單的函數,這個函數定義了一個叫做x的變量,且給了它一個值:1
那么,如果我們print這個變量呢?(我用注釋來表示輸出)
def func(): x = 1 print(x) func() # 1
好像完全是廢話嘛!print(x)不顯示1,難道還顯示別的?
那好,我們換一種情況,如果我們在函數體外面print(x)呢?
def func(): x = 1 print(x) # Traceback 后面一大堆我省略了... # NameError: name 'x' is not defined
報了一個錯誤,錯誤的類型是name error,名稱錯誤,錯誤的細節是:x沒有定義。
看到這里,細心的朋友應該就知道我說這么多廢話是什么意思啦!
x定義在函數體內部嘛,它的“作用范圍”就僅僅是函數體內才能用!
你想把X打印出來?想把x加1?想把x乘以2?對不起,請到函數體內部去執行,外面,python不認識它。
說到python不認識它,其實是指“python的解釋器”不認識它,解釋器具體不多解釋,你可以簡單的理解為你電腦的python程序。
好吧好吧,python不認識它,那么如果,我換一種情況呢?我把兩個語句交換一下位置?
x = 1 def func(): print(x) func() # 1
咦?你這個騙子,你不是說python不認識它嗎?現在x不在函數體內定義了(事實上函數體內什么定義都沒有~)
你怎么又認識它了?
問到這個問題,就得說一說python程序,讀取一個變量的方法了。
簡單的說,四個字:由內而外。
python先去查函數體內部:
1.我找到了,打印!
2.我找不到,我去找外面,找到了,打印!
3.我找不到,我去找外面,我還是找不到! NameError: 我找不到變量,你丫沒定義吧?
看,就是這么個順序。
那么找不到的x變量去哪里了?
比如這個程序:
def func(): x = 1 print(x)
x呢?x去哪里了?
答案是x被python解釋器給“回收”了,所謂回收,你可以簡單的理解成刪除了、殺死了。
這里要引入一個概念,就是變量的“存活時間”
我寫一下你就了解了(為了方便的標記函數體的結束,我寫了一個返回字符串‘done’的return語句):
def func(): x = 1 # x的存活時間開始,在x=1這行下面操作x才可以! return 'Done' # 函數體結束,x被python殺死了。
就是如上代碼中所表述的意思:
一個函數體內變量的存活時間,從定義它的那一刻開始,直到函數體結束為止。
我們可以通過一個很方便的、python給定我們的方法,來判斷函數體內部還“活着”的變量有哪些,這個方法是locals()
我們看一個代碼:
def func(): a = '哎呀' b = '我們' c = '還活着' print(locals()) func() # {'a': '哎呀', 'c': '還活着', 'b': '我們'}
看,locals()其實是一個字典,他列出了所有的“變量:變量值”這樣的“鍵-值對”,而且字典是無序的。
locals()里面存的全部都是當前“活着”的變量,我們看另外一個例子:
def func(): a = '哎呀' b = '我們' c = '還活着' print(locals()) d = '我還沒出生' func() # {'b': '我們', 'c': '還活着', 'a': '哎呀'}
看,locals()里面就沒有變量d什么事情了,因為d在那個時候“還沒出生”呢。
那么外部的變量呢?
x = 1 def func(): a = '內部' print(locals()) print(x) func() # {'a': '內部'} # 1
看到了吧,內部變量只有一個,locals也打印出來了,但是x還是正常的可以打印。
因為python在內部找不到,於是就去外面找了,外部的變量,可以用globals()檢查到,但是這個很長…………
x = 1 def func(): a = '內部' print(locals()) print(globals()) func() # {'a': '內部'} # ....(一大堆我就不粘貼了)..... 'x': 1}
命名空間/作用域的概念就是這樣,它決定了python程序“去哪里”找到變量。