2012-01-19 33 views
25

對於下面的Python 2.7的代碼「分配之前本地變量引用」:賦給變量從父功能:

#!/usr/bin/python 

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(): 
     c += 3 
     print "funcB", c 

    def funcC(): 
     print "funcC", c 

    print "c", c 
    funcB() 
    c += 2 
    funcC() 
    c += 2 
    funcB() 
    c += 2 
    funcC() 
    print "end" 

funcA() 

我收到以下錯誤:

File "./a.py", line 9, in funcB 
    c += 3 
UnboundLocalError: local variable 'c' referenced before assignment 

但是當我註釋掉在funcBc += 3,我得到下面的輸出:

funcA 
c 0 
funcB 0 
funcC 2 
funcB 4 
funcC 6 
end 

是不是c中的funcB+=funcC=這兩種情況下被訪問?爲什麼不爲一個拋出錯誤,而是爲另一個拋出錯誤?

我沒有選擇讓c成爲全局變量,然後在funcB中聲明global c。無論如何,關鍵是不要讓c遞增funcB但爲什麼它拋出錯誤funcB而不是funcC雖然兩者都是訪問變量這是局部或全局。

+0

傳遞c作爲參數... – joaquin

+0

我修改了一些代碼現在是問題的正確版本。 – crk

+0

此鏈接也有一些信息,http://docs.python.org/faq/programming。html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value –

回答

49

你們看到這裏的區別在訪問和分配變量之間。在Python 2.x中,您只能分配到最內層範圍或全局範圍中的變量(後者通過使用全局語句來完成)。您可以訪問任何封閉作用域中的變量,但不能訪問封閉作用域中的變量,然後在最內層或全局作用域中將其分配給它。

這意味着,如果有任何分配給一個名稱的函數中,該名稱必須已經在最裏面的範圍定義的訪問之前的名稱(除非使用global語句)。在你的代碼行c += 3基本上等同於以下內容:

tmp = c 
c = tmp + 3 

因爲在功能的分配cc在功能其它每次出現只會看在局部範圍內進行funcB 。這就是爲什麼你看到錯誤,你試圖訪問c以獲得其當前值爲+=,但在本地範圍c尚未定義。

在Python 3中,您可以通過使用nonlocal statement來解決此問題,該工具允許您分配不在當前範圍內但不在全局範圍內的變量。

您的代碼將是這個樣子,有一個類似的線在funcC頂部:

def funcB(): 
     nonlocal c 
     c += 3 
     ... 

在Python 2.x的,這是不是一種選擇,唯一的方式,您可以更改值非局部變量的情況就是它是可變的。

要做到這一點,最簡單的辦法就是換你的價值在列表中,然後修改並訪問列表的第一個元素在你以前只是使用的變量名的每一個地方:

def funcA(): 
    print "funcA" 
    c = [0] 
    def funcB(): 
     c[0] += 3 
     print "funcB", c[0] 

    def funcC(): 
     c[0] = 5 
     print "funcC", c[0] 

    print "c", c[0] 
    funcB() 
    funcC() 
    funcB() 
    funcC() 
    print "end" 

funcA() 

... ...和輸出:

funcA 
c 0 
funcB 3 
funcC 5 
funcB 8 
funcC 5 
end 
+4

多好的解釋!這個Python的一個黑暗的角落,通過僅僅**存在**的任務**任何地方**在一個函數中(很接近我的情況下)只是讓我瘋狂,當我拼命搜索其他SO答案的解釋以及外部互聯網。當我遇到一些反例**時,開車更加瘋狂,而不是產生該死的錯誤,並解釋說在Python中訪問全局變量都是可以的!最後,在這裏開悟!^_^ –

6

Isn't 'c' being accessed in both cases of '+=' in funcB and '=' in funcC?

沒有,funcC使得一個新的變量,也稱爲c=在這方面與+=不同。

爲了讓你(可能)想要的行爲,在一個單一的元素列表中輸入變數起來:

def outer(): 
    c = [0] 
    def inner(): 
     c[0] = 3 
    inner() 
    print c[0] 

將打印3

編輯:您將要通過c作爲參數。 Python 2沒有其他辦法,AFAIK,以獲得所需的行爲。 Python 3爲這些情況引入了nonlocal關鍵字。

0

試試這個:

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(c): 
     c += 3 
     print "funcB", c 

    def funcC(c): 
     c = 5 
     print "funcC", c 

    print "c", c 
    funcB(c) 
    funcC(c) 
    funcB(c) 
    funcC(c) 
    print "end" 

funcA() 

如果你想記住則c值:

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(c): 
     c += 3 
     print "funcB", c 
     return c 

    def funcC(c): 
     c = 5 
     print "funcC", c 
     return c 

    print "c", c 
    c = funcB(c) 
    c = funcC(c) 
    c = funcB(c) 
    c = funcC(c) 
    print "end" 

funcA() 

將產生:

funcA 
c 0 
funcB 3 
funcC 5 
funcB 8 
funcC 5 
end 

C:\Python26\ 
2

1)不是c中的funcB +=funcC=這兩種情況下被訪問?

沒有,因爲c += 3是一樣的:

c = c + 3 
    ^
    | 
and funcB does not know what this c is 

2)我沒有做c一個全局變量,然後在funcB宣佈global c的選擇。

請不要那樣做,只是改變:

def funcB(): 

有:

def funcB(c): 

,並呼籲funcB(c)在後面的代碼。

注:你也應該cosider定義funcBfuncCfuncA

0

另一個骯髒的解決方法,然而,不要求你做C全局。一切都一樣,但:

def funcB(): 
    globals()['c'] += 3 
    print "funcB", c 
+0

好,整個問題的方式不是很優雅,但只是爲了使其工作 - 這種方法的作品。 – dmytro