来自动态性较差的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[0] = 5
替换为vals += [5,6]
,则执行会因UnboundLocalError
而失败,除非我向global vals
添加f
。这也是我在第一种情况下预期会发生的事情。
你能解释一下这种行为吗?
为什么我可以在第一种情况下操纵vals
?为什么第二种操作失败而第一种操作失败?
更新
评论中注明vals.extend(...)
在没有global
的情况下有效。这增加了我的困惑 - 为什么+=
与extend
的调用区别对待?
答案 0 :(得分:5)
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]
相同。
答案 1 :(得分:2)
global
。如果您不使用global
,Python在执行赋值时会将vals
视为局部变量。
+=
是一项作业(augmented assignment),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
答案 2 :(得分:0)
只要不更改对象引用,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)它不存在!