2014-10-16 86 views
1

此代碼不起作用:的Python:嵌套函數和變量的作用域

def lol(): 
    i = 1 
    def _lol(): 
     i += 1 
    _lol() 
lol() 

錯誤:

local variable 'i' referenced before assignment 

但是,下面的代碼工作正常:

def lol(): 
    i = [1] 
    def _lol(): 
     i[0] += 1 
    _lol() 
lol() 

爲什麼那?

回答

4

Python範圍適合3類 - local,nonlocalglobal。默認情況下,函數只能在本地作用域中更改引用(引用是使用賦值運算符創建的)。

你可以自由地發生變異你有一個參考的對象這就是爲什麼第二個例子作品(i是列表[1]的引用,那麼你改變/突變它的第一項)。總之,你是突變i引用的對象,你不是試圖改變引用。需要注意的是,你可以給一個函數訪問通過global關鍵字來改變在全球範圍內的參考:

i = 1 
def func(): 
    global i # If you comment this out, i doesn't get changed in the global scope 
    i = 2 

func() 
print(i) # 2 -- 1 if the global statement is commented out. 

注意python3.x添加nonlocal關鍵字。它與global的作用相同,但與非本地範圍相同。例如

def foo(): 
    i = 1 # nonlocal to bar 
    def bar(): 
     nonlocal i 
     print(i) 
     i += 1 
    return bar 

bar1 = foo() 
bar1() # 1 
bar1() # 2 
bar1() # 3 

bar2 = foo() 
bar2() # 1 
bar2() # 2 

bar1() # 4 bar2 doesn't influence bar1 at all. 

增強運營商

這是更先進一些,但提供給希望能幫助回答有關像+=運營商的問題。考慮這樣的情況:

x = [] 
def func(): 
    x += [1] 

你可能認爲這個工作 - 畢竟,x += [1]的列表x真的只是x.extend([1])吧?。不幸的是,這並不完全。我們可以使用dis.dis反彙編func以查看更多信息。

>>> dis.dis(func) 
    2   0 LOAD_FAST    0 (x) 
       3 LOAD_CONST    1 (1) 
       6 BUILD_LIST    1 
       9 INPLACE_ADD   
      10 STORE_FAST    0 (x)  ### IMPORTANT! 
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE   

注意字節碼指令STORE_FAST?基本上說,將INPLACE_ADD的結果存儲在本地字典中的名稱x中。換句話說,你寫的:

x += [1] 

但是Python執行:

x = x.__iadd__([1]) 

爲什麼? __iadd__應該在適當的位置操作,爲什麼它需要重新命名爲__iadd__的返回值?重新綁定的部分是問題 - 即這段代碼工作:

x = [] 
def func(): 
    x.__iadd__([1]) 

答案是因爲Python有不可變對象和__iadd__需要與他們合作過。因此,__iadd__可以返回「self」以外的對象。這最終是非常有用的。考慮i = 1; i += 1。此調用僅適用於int.__iadd__被允許返回一個新的整數。

在更深入的討論其實這是我的所有時間在計算器上最upvoted答案,可以發現here

+0

你能不能也包括這樣的:'我= []; def x():i + = [2]' - 爲什麼這不起作用?列表上的'+ ='不會重新分配任何東西...... – georg 2014-10-16 16:09:24

+0

@georg - 當然可以。該任務只是有點隱藏。如果你使用'dis'反彙編這個函數,你會看到'STORE_FAST ...(i)'指令。 'i + = something'調用'i .__ iadd __(something)',並將返回值存儲在本地範圍內的名稱'i'中。如果沒有,'+ ='不適用於不可變對象(即'int')。注意['cls .__ iadd__'應該(但不一定)爲可變對象返回'self',爲不可變對象返回一個新實例](https://docs.python.org/2/reference/datamodel.html #object .__ iadd__)。 – mgilson 2014-10-16 16:17:27

+0

謝謝))我的意思是,你不認爲這是值得添加到你的答案? – georg 2014-10-16 16:24:33