Python編程中運(yùn)用閉包時(shí)所需要注意的一些地方
寫下這篇博客,起源于Tornado郵件群組的這個(gè)問題how to use outer variable in inner method,這里面老外的回答很有參考價(jià)值,關(guān)鍵點(diǎn)基本都說到了。我在這里用一些有趣的例子來做些解析,簡要的闡述下Python的閉包規(guī)則,首先看一個(gè)經(jīng)典的例子:
def foo(): a = 1 def bar(): a = a + 1 # print a + 1 # b = a + 1 # a = 1 print id(a) bar() print a, id(a)
在Python2.x上運(yùn)行這個(gè)函數(shù)會(huì)報(bào)UnboundLocalError: local variable 'a' referenced before assignment即本地變量在引用前未定義,如何來理解這個(gè)錯(cuò)誤呢?PEP 227里面介紹到,Python解析器在搜索一個(gè)變量的定義時(shí)是根據(jù)如下三級(jí)規(guī)則來查找的:
The Python 2.0 definition specifies exactly three namespaces to check for each name — the local namespace, the global namespace, and the builtin namespace.
這里的local實(shí)際上可能還有多級(jí),上面的代碼就是一個(gè)例子,下面通過對(duì)代碼做些簡單的修改來一步步理解這里面的規(guī)律:
- 如果將a = a + 1這句換成print a + 1或者b = a + 1,是不會(huì)有問題的,即在內(nèi)部函數(shù)bar內(nèi),外部函數(shù)foo里的a實(shí)際是可見的,可以引用。
- 將a = a + 1換成 a = 1也是沒有問題的,但是如果你將兩處出現(xiàn)的a的id打印出來你會(huì)發(fā)現(xiàn),其實(shí)這兩個(gè)a不是一回事,在內(nèi)部函數(shù)bar里面,本地的a = 1定義了在bar函數(shù)范圍內(nèi)的新的一個(gè)局部變量,因?yàn)槊趾屯獠亢瘮?shù)foo里面的變量a名字相同,導(dǎo)致外部函數(shù)foo里的a在內(nèi)部函數(shù)bar里實(shí)際已不可見。
- 再來說a = a + 1出錯(cuò)是怎么回事,首先a = xxx這種形式,Python解析器認(rèn)為要在內(nèi)部函數(shù)bar內(nèi)創(chuàng)建一個(gè)新的局部變量a,同時(shí)外部函數(shù)foo里的a在bar里已不可見,而解析器對(duì)接下來對(duì)右邊的a + 1的解析就是用本地的變量a加1,而這時(shí)左邊的a即本地的變量a還沒有創(chuàng)建(等右邊賦值呢),因此就這就產(chǎn)生了一個(gè)是雞生蛋還是蛋生雞的問題,導(dǎo)致了上面說的UnboundLocalError的錯(cuò)誤。
要解決這個(gè)問題,在Python2.x里主要有兩個(gè)方案:
用別名替代比如b = a + 1,內(nèi)部函數(shù)bar內(nèi)只引用外部函數(shù)foo里的a。
將foo里的a設(shè)成一個(gè)容器,如list
def foo(): a = [1, ] def bar(): a[0] = a[0] + 1 bar() print a[0]
當(dāng)然這有些時(shí)候還是很不方便,因此在Python3.x中引入了一個(gè)nonloacal的關(guān)鍵字來解決這個(gè)問題,只要在a = a + 1前加一句nonloacal a即可,即顯式的指定a不是內(nèi)部函數(shù)bar內(nèi)的本地變量,這樣就可以在bar內(nèi)正常的使用和再賦值外部函數(shù)foo內(nèi)的變量a了。
在搜索Python閉包相關(guān)的材料中,我在StackOverflow上發(fā)現(xiàn)一個(gè)有趣的有關(guān)Python閉包的問題,有興趣的可以思考思考做做看,結(jié)果應(yīng)該是什么?你預(yù)期的結(jié)果是什么,若不一致,如果要得到你預(yù)期的結(jié)果應(yīng)該怎么改?
flist = [] for i in xrange(3): def func(x): return x * i flist.append(func) for f in flist: print f(2)
相關(guān)文章
Python內(nèi)置函數(shù) next的具體使用方法
這篇文章主要介紹了Python內(nèi)置函數(shù) next的具體使用方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
利用Python爬蟲實(shí)現(xiàn)搶購某寶秒殺商品
這篇文章主要介紹了利用Python爬蟲實(shí)現(xiàn)搶購某寶秒殺商品,文章基于python的相關(guān)資料展開詳細(xì)的內(nèi)容介紹具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-06-06
python實(shí)現(xiàn)Mysql數(shù)據(jù)庫批量新增數(shù)據(jù)的場景分析
這篇文章主要介紹了python實(shí)現(xiàn)Mysql數(shù)據(jù)庫批量新增數(shù)據(jù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03

