2016-12-22 58 views
1
def some_func(a): 
    def access_a(): 
     print(a) 
    access_a() 

輸出值a。但是,如果我想在這樣的嵌套函數改變a嵌套函數更改變量在外部函數不起作用

def some_func(a): 
    def change_a(): 
     a += 1 
     print(a) 
    change_a() 

它引發UnboundLocalError例外。

我知道a是一個非本地變量,但爲什麼我可以在不聲明nonlocal a的情況下訪問它?

+0

您是否嘗試過在功能'全球了'? – Fomalhaut

+2

這是同樣的事情,用'global':您可以訪問一個全局變量,而是試圖修改,而不會'全球var'將失敗。 – ForceBru

+3

無論採用何種方法來解決這個問題,像這樣伸出援手並導致任意的副作用都不是很好的編程練習。 – mVChr

回答

2

Python的範圍規則101:

  1. 在函數體結合的名稱被認爲local除非顯式地聲明global(Python的2.x和3.x)或nonlocal(Python的3.x的只)。無論分配發生在函數的正文中,這都是真實的。試圖在綁定之前讀取局部變量當然是一個錯誤。

  2. 如果一個名字被讀取但沒有被綁定到一個函數的主體中,它會在封閉的作用域(外部函數(如果有的話)是全局作用域)中被查找。 NB:函數參數是事實上的本地名稱,所以它們永遠不會在封閉範圍內查找。

注意a += 1主要是針對a = a + 1的快捷方式,所以在你的例子a是本地(綁定在函數體,而不是明確地聲明的全局或外地),但你嘗試讀它(的a = a+1的RHS )在它被綁定之前。

在Python 3,你可以用nonlocal語句解決這個問題:

>>> def outer(a): 
... def change(): 
...  nonlocal a 
...  a += 1 
... print("before : {}".format(a)) 
... change() 
... print ("after : {}".format(a)) 
... 
>>> outer(42) 
before : 42 
after : 43 

Python 2裏沒有nonlocal所以官方hack是住變量在一個可變的容器(通常爲list,但任何可變對象會做):

>>> def outer(a): 
...  _a = [a] 
...  def change(): 
...   _a[0] += 1 
...  print("before : {}".format(_a[0])) 
...  change() 
...  print ("after : {}".format(_a[0])) 
... 
>>> outer(42) 
before : 42 
after : 43 

這是相當難看,至少可以說。

現在雖然閉包是很方便,他們大多是對象的功能對應:一組功能之間共享狀態,同時保持這種狀態的封裝方式,所以如果你發現你需要一個nonlocal變量也許一個適當的類可能是一個更乾淨的解決方案(儘管可能不適用於您的示例,它不會返回內部函數,但僅在內部使用它)。

0

當您使用+=運營商,有一個新的價值a的分配。這在口譯員眼中變成了本地人。

1

我對你有兩種解決方案:

#first one: 
# try with list, compound data types dict/list 
def some_func(a): 
    def change_a(): 
     a[0] += 1 
     print(a[0]) 
    change_a() 
some_func([1]) 
>>> 2 


#second one 
#reference pointer 
from ctypes import * 
def some_func_ctypes(a): 
    def change_a(): 
     a[0] += 1 
     print a.contents, a[0] 
    change_a() 

i = c_int(1) 
pi = pointer(i) 
some_func_ctypes(pi) 

>>> c_int(2) 2 
+0

OP的要求是爲什麼他可以讀一個非本地變量,但不能_rebind_它...... –

+0

@bruno,你做了一個很好的解釋/答案,thx。我應該再解釋一次嗎?我想它不再需要,但另一種解決方案對未來的讀者來說會很好。 –

相關問題