此代码无效:
def lol():
i = 1
def _lol():
i += 1
_lol()
lol()
错误:
local variable 'i' referenced before assignment
但是,以下代码可以正常工作:
def lol():
i = [1]
def _lol():
i[0] += 1
_lol()
lol()
为什么?
答案 0 :(得分:4)
Python范围分为3个类别 - local
,nonlocal
和global
。默认情况下,函数只能更改本地范围中的引用(使用赋值运算符创建引用)。
你可以 mutate 一个你引用的对象,这就是为什么第二个例子有效的原因(i
是对列表[1]
的引用,然后你改变/改变它的第一项)。简而言之,您变异 i
引用的对象,您不是要更改引用。请注意,您可以通过global
关键字提供函数访问权限以更改全局范围中的引用:
i = 1
def func():
global i # If you comment this out, i doesn't get changed in the global scope
i = 2
func()
print(i) # 2 -- 1 if the global statement is commented out.
请注意,python3.x添加了nonlocal
关键字。它与global
完全相同,但与非本地范围相同。 e.g。
def foo():
i = 1 # nonlocal to bar
def bar():
nonlocal i
print(i)
i += 1
return bar
bar1 = foo()
bar1() # 1
bar1() # 2
bar1() # 3
bar2 = foo()
bar2() # 1
bar2() # 2
bar1() # 4 bar2 doesn't influence bar1 at all.
这有点高级,但希望有助于回答有关+=
等运营商的问题。考虑一下这个案例:
x = []
def func():
x += [1]
您可能希望这样做 - 毕竟,列表x += [1]
的{{1}}实际上只是x
,对吗?。不幸的是,它并不完全。我们可以使用x.extend([1])
反汇编func
以查看更多内容。
dis.dis
注意字节码指令STORE_FAST
?基本上说,将INPLACE_ADD
的结果存储在本地字典中的名称>>> dis.dis(func)
2 0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 (1)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (x) ### IMPORTANT!
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
中。换句话说,你写:
x
但是python执行 1 :
x += [1]
为什么呢? x = x.__iadd__([1])
应该运行到位,为什么需要将名称重新绑定到__iadd__
的返回值?重新绑定部分是问题 - 即,此代码将工作:
__iadd__
答案是因为python具有不可变对象,x = []
def func():
x.__iadd__([1])
也需要使用它们。因此,__iadd__
可以返回“__iadd__
”以外的对象。这最终会非常有用。考虑self
。此调用仅起作用,因为允许i = 1; i += 1
返回新的整数。
1 更深入地讨论这个问题实际上是我在StackOverflow上最受欢迎的答案,可以找到here