2013-04-22 91 views
3

比方說,我有以下功能:衝突的變量和函數名

def xplusy(x, y): 
    return x+y 

def xplus1(x): 
    xplusy = xplusy(x, 1) 
    return xplusy 

現在如果我叫a = xplus1(4)它引發以下錯誤:

UnboundLocalError: local variable 'xplusy' referenced before assignment 

的錯誤是因爲命名衝突,如果我重新定義xplus1如下:

def xplus1(x): 
    s = xplusy(x, 1) 
    return s 

它工作正常。

爲什麼會這樣:編譯器不能正確區分變量和函數調用?

任何方法嗎?

+3

爲什麼給你的生活複雜化?請給它另一個名字。 – Maroun 2013-04-22 06:50:26

+1

你可以這樣做'return xplusy(x,1)':'def xplus1(x):return xplusy(x,1)' – 2013-04-22 06:52:07

+0

在其他語言中通常是很自然的事情。改變它並不是一個問題,但如果它是一個向代碼中添加幾個字符的問題,我很好。爲什麼它發生在理論上還不清楚...... – sashkello 2013-04-22 06:52:20

回答

12

在Python中,函數是數據,鍵入是動態的。這意味着下面幾行是有效的Python:

def func(x): 
    return x + 3 

func = 3 

func現在是int類型。原始功能func不再被引用。 func最初是一項功能,這一事實對於將來可以分配哪些類型的數據沒有任何影響。 (這是「動態類型」的意思)。

因此,由於沒有靜態類型,「函數」是一個有效的數據類型,因此Python解釋器區分函數和由相同名稱引用的一段數據。因此,在給定的範圍內,沒有辦法使用相同的非限定變量名來表示兩個不同的事物。

在您的特定情況下,如果在你的xplus1函數的代碼意味着什麼,那將意味着「計算的xplusy(x,1)價值和分配是看重給變量xplusy - 從而失去了參考功能xplusy 「。但是,在函數範圍內,解釋器不會讓您對該範圍之外的變量進行賦值,因此它假定通過編寫賦值語句,引入一個新的局部變量xplusy。但是,局部變量尚未定義,因此您嘗試調用它,xplusy(x,1)失敗。全局定義的函數不被稱爲回退函數,因爲再次,您不能將兩個非限定名稱相同並指向同一範圍內的不同數據。


另一個例子展示了規則(我實際上同時與提示我試圖構建這樣的回答玩弄纔剛剛發現)「一個範圍內的變量名沒有重複」:

>>> def f1(): 
...  a = xplusy(3,4) 
...  xplusy = 5 
...  print xplusy 
... 
>>> f1() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 2, in f1 
UnboundLocalError: local variable 'xplusy' referenced before assignment 
>>> def f1(): 
...  a = xplusy(3,4) 
...  print a 
... 
>>> f1() 
7 

這表明它確實是需要唯一名稱的範圍,而不是陳述


編輯:這是一個非常酷的職位,說明這一點和其他範圍界定相關行爲:http://me.veekun.com/blog/2011/04/24/gotcha-python-scoping-closures/

+0

謝謝,現在最好的解釋... – sashkello 2013-04-22 07:11:20

+0

模塊名稱也是如此嗎?我正在維護一個python腳本,它有一個與模塊名稱相同的變量。 (時間,如果你想知道) – Jack 2014-07-10 16:05:43

+0

@傑克我不知道你在問什麼。確實,'import'語句將模塊名稱(它們不是*變量名稱)分配給變量,因此它們不受與模塊共享相同名稱的全局變量或局部變量的影響;也就是'時間= 3;從時間進口時間作爲epoch_time;打印時間;打印epoch_time()'將打印'3',然後是從紀元開始的秒數。但是,'時間= 3;從時間進口時間;打印時間「將打印'<內置函數時間>',因爲變量'time'由導入語句重新分配。 – 2014-07-10 17:42:55

1

發生這種情況的原因是因爲xplusy作爲全局變量存在於全局變量中,除非您明確說xplusyglobal,否則無法更改它。

​​

然而,這將使xplusy指的是intfloat或任何xplusy返回的第一次,這意味着在第一時間之後會拋出TypeError秒。

更Python的方式來做到這一點最有可能是

def xplus1(x): 
    return xplusy(x,1) 

或使用functools模塊:

from functools import partial 
xplus1 = partial(xplusy,y=1) #since you wanted to override y with 1 

,如果你不關心哪個參數被覆蓋,你可以簡單地做

xplus1 = partial(xplusy,1) 
1

有些情況,例如回調,你提到的功能離子通過它們的靜態名稱(funcName而不是funcName())。因此在Python中,變量名稱是爲該函數保留的。很像你將如何使用存儲在變量中的LAMBDA函數。

3

在python函數是第一類對象,這意味着它是任何其他對象。

更多有關What are 「first class」 objects?

+4

那麼,函數也是Common Lisp中的第一類對象,但Common Lisp有一個單獨的函數名稱空間。 – 2013-04-22 07:02:11

1

在Python,xplusy可以引用任何東西。你可以這樣做:

def xplusy(x, y): 
    return x+y 

def otherfunction(x, y): 
    return x*y 

def xplus1(x): 
    xplusy = otherfunction 
    return xplusy(x, 1) 

而且xplusy變量將參考otherfunctionxplus1範圍。

xplus1(2) 
>>> 2 

結果爲2 * 1,而不是2 + 1小心assignments.Use儘可能多的變量,因爲你需要。

+0

感謝你,看起來很奇怪:) – sashkello 2013-04-22 07:03:43