2013-04-21 56 views
5

我讀過其他一些SO(PythonScopeglobals don't need global),但沒有任何內容似乎可以解釋得如我所願,並且我在思考過程中遇到了麻煩通過與否PyDocs告訴我,回答我的問題:Python + =與.extend()在一個全局變量上的函數內

myList = [1] 

def foo(): 
    myList = myList + [2, 3] 
def bar(): 
    myList.extend([2, 3]) 
def baz(): 
    myList += [2, 3] 

現在,可以理解,

>>> foo() 
UnboundLocalError 

bar() # works 
myList # shows [1, 2, 3] 

但隨後

>>> baz() 
UnboundLocalError 

但是我想,這東西就像+=隱式調用的方法運營,在這種情況下extend(),但錯誤意味着,由於某種原因,它實際上並沒有把+=extends()。這與Python解析應該如何工作一致嗎?

我原以爲調用等價於方法操作符的函數,它們在所有情況下都是等價的。相反,它似乎將+=視爲實際的分配運算符。除此之外,這並不完全正確,因爲如果我做了什麼(當然做作):

myList = range(50000000) # wait a second or two on my laptop before returning 
myList += [0]   # returns instantly 
myList = myList + [1] # wait a second or two before returning 

所有這些預計,如果+=實際上只是調用extend()

有沒有辦法,我缺少明確規定,myListbaz()需要被當作一個局部變量,並因此+=不能被隱式轉換成一些更細的區分(或非常明顯的一點...)一個extend(),使其識別全局變量?

回答

3

+=不會隱式調用extend()。首先,它是一個augmented assignment operator

如果你看一下部分上assignment它說:對象到單個目標

分配是遞歸定義如下。

如果目標是一個標識符(名稱):

如果名稱不在當前碼塊的全局聲明發生:名稱被綁定到對象中的當前本地名稱空間中。 否則:名稱被綁定到當前全局名稱空間中的對象。

由於一個增強的任務是:

增強分配是組合,在單個語句,一個二元運算和一個賦值語句:

它起着遵守同樣的規則。 正如可以看到:

>>> def baz(): 
     myList += [2, 3] 


>>> dis.dis(baz) 
    2   0 LOAD_FAST    0 (myList) 
       3 LOAD_CONST    1 (2) 
       6 LOAD_CONST    2 (3) 
       9 BUILD_LIST    2 
      12 INPLACE_ADD   
      13 STORE_FAST    0 (myList) 
      16 LOAD_CONST    0 (None) 
      19 RETURN_VALUE 

增強賦值語句對目標(其不同於正常的賦值語句,不能是拆包)和表達式列表,執行特定於該類型分配的二進制運算兩個操作數,並將結果分配給原始目標。目標只計算一次..

第一次調用改掉評估myList,這導致LOAD_FAST因爲沒有global聲明它被認爲是一個局部變量:

LOAD_FAST(var_num)

將對本地co_varnames[var_num]的引用推送到堆棧上。

找不到這個錯誤。如果它找到了,然後我們得到的oppcode INPLACE_ADD,它調用方法myList.__iadd__做擴展的工作,一旦這個操作完成,結果將被分配回變量,但我們從來沒有得到這麼多。

無論如何,你不應該真的操縱global,從你的函數返回新的結果或將它作爲參數傳遞。

+0

啊,好的,現在更有意義了,謝謝你的解釋。而且我必須更多地研究一下反彙編器,因爲熟悉這些會幫助我。是的,我不會在實踐中這樣做 - 我犯了一次錯誤,不知道爲什麼我得到了這個輸出,所以我很好奇,但後來做了正常的事情,只是作爲參數傳遞它。謝謝! – dwanderson 2013-04-21 15:22:59

0

當你改變列表時,你應該說全局myList。通過mutate我的意思是改變參考。第一個例子和第三一個是基本相同的,你只需要使用+ =速記

myList = [1] 

def foo(): 
    global myList 
    myList = myList + [2, 3] 
def bar(): 
    myList.extend([2, 3]) 
def baz(): 
    global myList 
    myList += [2, 3] 

foo() 
bar() 
baz() 
+0

大範圍的例子表明我的第一個例子(L = L + M)不等於我的第三個例子(L + = M),否則他們都會花費相當可觀的時間,如果它們是)改變參考。 – dwanderson 2013-04-21 03:27:18

+0

顯然你是正確的,+ =不會改變參考。我用'is'操作符來檢查這個。 – marcadian 2013-04-21 03:42:49

+0

我的意思是,如果需要調整列表大小,但用戶/代碼不是_requiring_參考更改,我認爲+ =/extend()/ append()_could_會隱式更改引用運算符。感謝您發佈一個答案並繼續備份,我很感激! – dwanderson 2013-04-21 03:53:09