寫(xiě)下這篇博客,起源于Tornado郵件群組的這個(gè)問(wèn)題how to use outer variable in inner method,這里面老外的回答很有參考價(jià)值,關(guān)鍵點(diǎn)基本都說(shuō)到了。我在這里用一些有趣的例子來(lái)做些解析,簡(jiǎ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即本地變量在引用前未定義,如何來(lái)理解這個(gè)錯(cuò)誤呢?PEP 227里面介紹到,Python解析器在搜索一個(gè)變量的定義時(shí)是根據(jù)如下三級(jí)規(guī)則來(lái)查找的:
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è)例子,下面通過(guò)對(duì)代碼做些簡(jiǎn)單的修改來(lái)一步步理解這里面的規(guī)律:
要解決這個(gè)問(wèn)題,在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)鍵字來(lái)解決這個(gè)問(wèn)題,只要在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閉包的問(wèn)題,有興趣的可以思考思考做做看,結(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)
新聞熱點(diǎn)
疑難解答
圖片精選