此代码无效:
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()
这是为什么?
最佳答案
Python 作用域分为 3 类——local
、nonlocal
和 global
。默认情况下,函数只能更改局部范围内的引用(引用是使用赋值运算符创建的)。
您可以自由改变您引用的对象,这就是第二个示例起作用的原因(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
做同样的事情,但对非本地范围。例如
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]
对于列表 x
实际上只是 x.extend([1 ])
,对吧?。不幸的是,事实并非如此。我们可以使用 dis.dis
反汇编 func
以进一步了解发生了什么。
>>> 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
注意字节码指令STORE_FAST
?这基本上就是说,存储 INPLACE_ADD
的结果在本地字典中的名称 x
中。换句话说,你写:
x += [1]
但是 python 执行1:
x = x.__iadd__([1])
为什么? __iadd__
应该就地运行,那么为什么它需要将名称重新绑定(bind)到 __iadd__
的返回值呢?重新绑定(bind)部分是问题所在——也就是说,这段代码会工作:
x = []
def func():
x.__iadd__([1])
答案是因为 python 具有不可变对象(immutable对象),__iadd__
也需要与它们一起工作。因此,__iadd__
可以返回“self
”以外的对象。这最终非常有用。考虑 i = 1;我 += 1
。此调用之所以有效,是因为 int.__iadd__
被允许返回一个新的整数。
1更深入地讨论这个问题实际上是我在 StackOverflow 上投票最多的答案,可以在 here 上找到
关于Python:嵌套函数和变量范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26408941/