2011-10-28 64 views
38

假設我有以下Python代碼:Python中的嵌套函數覆蓋變量

def outer(): 
    string = "" 
    def inner(): 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

我想打電話外()返回!「字符串是由一個嵌套函數改變」,但我得到「」 。我得出這樣的結論:Python認爲string = "string was changed by a nested function!"這行是對inner()局部變量的新變量的聲明。我的問題是:我如何告訴Python它應該使用outer()字符串?我無法使用global關鍵字,因爲該字符串不是全局的,它只是位於外部作用域中。想法?

+0

可能相關:http://stackoverflow.com/q/146359/212218 – 2011-10-28 23:48:20

回答

35

在Python 3.x中,你可以使用關鍵字nonlocal

def outer(): 
    string = "" 
    def inner(): 
     nonlocal string 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

在Python 2.x中,你可以使用一個列表與一個單一的元素,並覆蓋該單個元素:

def outer(): 
    string = [""] 
    def inner(): 
     string[0] = "String was changed by a nested function!" 
    inner() 
    return string[0] 
+0

+1至於爲什麼後者的工作原理如下:Item賦值(與屬性賦值一起)不是變量賦值 - 'x [...] = ...'只是從任何範圍內獲得'x',並調用其'__setitem__'方法。一個同樣有效的,但最初更加詳細的選項是創建一個對象(用戶定義的類,因爲'object()'不允許添加屬性),其唯一目的是擁有一個屬性。 – delnan

+2

您可以使用'x = type(「」,(),{})()'創建這樣的「名稱空間對象」。 :) –

+0

因此,'nonlocal'的意思是:「在外部範圍中尋找這個變量」 - 如果在outer()範圍中找不到該變量,Python是否會繼續尋找封閉範圍? Python最終會試圖在全局範圍內找到變量嗎? – Ord

2

爲了增加Sven's answer

在Python 2.x中,你只能從客棧讀外部範圍變量呃範圍。分配只會創建一個隱藏外部範圍一個的新本地(即內部範圍)變量。

如果你想讀取和修改,您可以使用dict牽你的變量在外部範圍,然後通過字典訪問它們在內部範圍,也使您的代碼相當乾淨和可讀性多個存在外範圍瓦爾:

def outer(): 
    # hold some text, plus the number of spaces in the text 
    vars = {'text': 'Some text.', 'num_spaces': 1} 
    def inner(): 
     # add some more text 
     more_text = ' Then some more text.' 
     vars['text'] += more_text 
     # keep track of the number of spaces 
     vars['num_spaces'] += more_text.count(' ') 
    inner() 
    return vars['text'], vars['num_spaces'] 

輸出:

>>> outer() 
('Some text. Then some more text.', 5) 
19

您也可以解決這個問題,通過使用功能屬性:

def outer(): 
    def inner(): 
     inner.string = "String was changed by a nested function!" 
    inner.string = "" 
    inner() 
    return inner.string 

澄清:這個工作在兩個蟒蛇2.x和3.x的

+3

這實際上是一個好主意。 – Alice

3

這種情況發生在我身上的時候,當我寫一個函數時,我突然意識到擁有一個較小的輔助函數可能是一個好主意,但在其他地方並不是很有用。這自然使我想要將它定義爲嵌套函數。

但我有JAVA匿名對象(即:定義一個可運行的)的經驗,並且規則是匿名對象將其外部環境的硬拷貝,在這種情況下外部作用域的變量。因此,如果外變量是一個不可變的(intchar),它們不能被匿名對象進行修改,因爲它們是由複製,而如果它是一個可變(collectionobjects),它們是可以改變的...,因爲它們被複製爲「指針」(它們在內存中的地址)

如果您瞭解編程,可以將其視爲按值傳遞並按引用傳遞。

在Python中,它是非常相似的。x=123是一個賦值,他們給變量XA新的含義(不修改舊X),list[i]/dict[key]是對象的訪問操作,他們真的修改事情

得出結論,你需要一個可變對象...爲了修改(即使您可以使用[]訪問元組,但由於其不可用,所以不能在此使用它)