[英]Writing (and not) to global variable in Python
來自動態較少的C ++,我在理解這個Python(2.7)代碼的行為時遇到了一些麻煩。
注意:我知道這是糟糕的編程風格/邪惡,但我想了解它。
vals = [1,2,3]
def f():
vals[0] = 5
print 'inside', vals
print 'outside', vals
f()
print 'outside', vals
此代碼運行時沒有錯誤,並且f
操縱(看似)全局列表。 這與我之前的理解相反,即必須將函數中要操作(而不僅僅是讀取)的全局變量聲明為全局變量global ...
另一方面,如果我用vals += [5,6]
替換vals[0] = 5
,則執行失敗並帶有UnboundLocalError
除非我向f
添加global vals
UnboundLocalError
。 這也是我在第一種情況下預期會發生的情況。
你能解釋一下這種行為嗎?
為什么我可以在第一種情況下操縱vals
? 為什么第二種操作失敗而第一種操作失敗?
更新:評論中評論說vals.extend(...)
在沒有global
情況下工作。 這增加了我的困惑 - 為什么+=
與extend
調用不同?
只有在嘗試更改變量引用的對象時才需要global
。 因為vals[0] = 5
更改實際對象而不是引用,所以不會引發錯誤。 但是,使用vals += [5, 6]
,解釋器會嘗試查找局部變量,因為它無法更改全局變量。
令人困惑的是,使用帶有列表的+=
運算符會修改原始列表,如vals[0] = 5
。 而vals += [5, 6]
失敗, vals.extend([5, 6])
起作用。 我們可以尋求dis.dis
的幫助來借給我們一些線索。
>>> def a(): v[0] = 1
>>> def b(): v += [1]
>>> def c(): v.extend([1])
>>> import dis
>>> dis.dis(a)
1 0 LOAD_CONST 1 (1)
3 LOAD_GLOBAL 0 (v)
6 LOAD_CONST 2 (0)
9 STORE_SUBSCR
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis.dis(b)
1 0 LOAD_FAST 0 (v)
3 LOAD_CONST 1 (1)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (v)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
d
>>> dis.dis(c)
1 0 LOAD_GLOBAL 0 (v)
3 LOAD_ATTR 1 (extend)
6 LOAD_CONST 1 (1)
9 BUILD_LIST 1
12 CALL_FUNCTION 1
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
我們可以看到函數a
和c
使用LOAD_GLOBAL
,而b
嘗試使用LOAD_FAST
。 我們現在可以看到為什么使用+=
將不起作用 - 解釋器嘗試將v
作為局部變量加載,因為它具有就地添加的默認行為。 因為它無法知道v
是否是列表,所以它基本上假定該行與v = v + [1]
。
如果要分配給外部作用域中的變量,則需要global
。 如果不使用global
,Python在執行賦值時會將vals
視為局部變量。
+=
是賦值(一個增量賦值 )和vals += [5, 6]
是相當於讀取vals
,然后追加[5, 6]
到該值和所得到的列表分配回原始vals
。 因為vals += [5,6]
沒有global
語句,所以Python會看到賦值並將vals
視為本地。 您沒有創建一個名為vals
的局部變量,但是您嘗試將其附加到UnboundLocalError
。
但是對於閱讀來說,沒有必要使用global
。 該變量將首先在本地查找,如果在本地范圍內找不到,則在外部范圍內查找,依此類推。 由於您正在處理引用類型,因此在執行讀取時會返回引用。 您可以通過該引用更改對象的內容。
這就是.extend()
工作原理(因為它在引用上調用並作用於對象本身),而vals += [5, 6]
失敗(因為vals
既不是局部的也不是標記為global
)。
這是一個嘗試的修改示例(使用本地vals
清除UnboundLocalError
):
vals = [1, 2, 3]
def f():
vals = []
vals += [5,6]
print 'inside', vals
print 'outside', vals
f()
print 'outside', vals
只要您不更改對象引用,Python就會保留全局對象。 相比
In [114]: vals = [1,2,3]
In [116]: id(vals)
Out[116]: 144255596
In [118]: def func():
vals[0] = 5
return id(vals)
.....:
In [119]: func()
Out[119]: 144255596
In [120]: def func_update():
vals = vals
return id(vals)
.....:
In [121]: func_update()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
/homes/markg/<ipython-input-121-f1149c600a85> in <module>()
----> 1 func_update()
/homes/markg/<ipython-input-120-257ba6ff792a> in func_update()
1 def func_update():
----> 2 vals = vals
3 return id(vals)
UnboundLocalError: local variable 'vals' referenced before assignment
你嘗試賦值的那一刻,Python認為vals是局部變量 - 而且(oops)它不存在!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.