本帖最后由 阿奇_o 于 2023-1-16 15:16 编辑
自己吃饱后,滚回去翻书复习了。。这里涉及几个技术术语和概念:
作用域、自由变量、闭包、可变数据类型和不可变数据类型
我尝试着解释吧:
作用域,我就不多解释了,分:局部(本地)作用域、全局作用域。(基本就看变量被定义相对位置)
自由变量free variable:指 没有在本地作用域里进行绑定的
非全局变量,如你这里b函数作用域里的cnt
闭包closure:是一个"函数"(或叫一种机制),它可以保存自由变量的绑定。
为什么要这样?—— 因为调用时,因为a函数的return b的缘故,a函数的作用域已经不存在了!
所以,为了能继续使用原定义的变量绑定,特别地,采用了【闭包】这一函数/机制来保存这些"自由变量"。
但问题是。。如果你在"内部函数"里修改、更新(重新绑定)这些变量 的时候,它就会创建的是"本地变量"(local variable),而非"自由变量"
—— 本地变量,就要遵守本地变量的"先声明后使用"的规则(其实任何变量实质上都要),于是你就看到了那样的报错。
那 cnt.append('qwq') 的情况,为什么没报错?—— 因为这不是更新或修改cnt,而是更新了其内部的元素。
cnt = [] 是列表,是可变类型,append()并没有改变cnt(它还是它,id没变),所以 cnt 依然是自由变量。
而 对于不可变类型的 cnt = 0 , 后cnt += 1 或 cnt = cnt + 1 这样的情况,就
重新绑定cnt了,这会
隐式地创建局部变量,
于是,cnt并没有成为"自由变量",故 执行时,它会报错 UnboundLocalError: local variable 'cnt' referenced before assignment。
其实,你在 cnt.append('qwq') 后,加多一句 cnt = cnt[:] ,执行时,基于上述原因,它同样会报错。
- def f2():
- cnt = []
- def b():
- cnt.append(10)
- cnt = cnt[:]
- return b
- t = f2()
- t()
- Traceback (most recent call last):
- File "<pyshell#27>", line 1, in <module>
- t()
- File "<pyshell#25>", line 4, in b
- cnt.append(10)
- UnboundLocalError: local variable 'cnt' referenced before assignment
复制代码
如果你能理解这个,应该可以"有点明白了"。。 哈哈
ps: 我想我没完全解释清楚。。主要是太细了。。另外也需要先搞明白有些前置知识和概念。。
ps2: 偷偷告诉你,你可以通过 函数对象的 __code__属性来查看其包含的 局部变量和自由变量, 如
t.__code__.co_varnames # 局部(本地)变量有哪些?
t.__code__.co_freevars # 自由变量有吗?有哪些?