簡體   English   中英

在Python中編寫(而不是)全局變量

[英]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

我們可以看到函數ac使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM