为啥 Python 3.8.0 允许在不使用“非本地”变量的情况下从封闭函数范围更改可变类型?
Posted
技术标签:
【中文标题】为啥 Python 3.8.0 允许在不使用“非本地”变量的情况下从封闭函数范围更改可变类型?【英文标题】:Why does Python 3.8.0 allow to change mutable types from enclosing function scope without using "nonlocal" variable?为什么 Python 3.8.0 允许在不使用“非本地”变量的情况下从封闭函数范围更改可变类型? 【发布时间】:2021-01-27 02:46:09 【问题描述】:在以下 Python 3.8.0.0 脚本中,不允许从子/嵌套函数的封闭函数范围更改不可变变量,但是,在不使用子/嵌套函数的非本地声明的情况下修改可变类型元素可以正常工作。谁能解释一下,为什么会这样?
def func():
func_var1 = 18
func_var2 = 'Python'
func_var3 = 1,2,3,4,5,6
func_var4 = [1,2,3,4,5,6]
func_var5 = 'P': 'Python', 'J': 'Java'
def sub_func():
nonlocal func_var1
func_var1 = 20
print(func_var1)
nonlocal func_var2
func_var2 = 'Java'
# For mutable types, why does it allow to update variable from enclosing function scope without nonlocal declaration?
func_var3.add(7)
print(func_var3)
func_var4.append(7)
print(func_var4)
func_var5.update('G':'Go')
func_var5['R'] = 'Ruby'
print(func_var5)
sub_func()
func()
输出
20
1, 2, 3, 4, 5, 6, 7
[1, 2, 3, 4, 5, 6, 7]
'P': 'Python', 'J': 'Java', 'G': 'Go', 'R': 'Ruby'
【问题讨论】:
【参考方案1】:关于全局/非局部名称和 (im) 可变对象的规则在 Python 中关于读写是不对称的。请注意,在 Python 中,变量的 name 和名称指向的 object/值之间存在(有时)重要的区别,因此在下面我将尝试明确这一点,避免使用含糊不清的术语“变量”:
所有名称就是我们所说的“read-global”(和“read-nonlocal”),因为它们总是可以是在不使用global
/nonlocal
关键字的情况下进行查找,并检索到它们的对象。
相反,外部范围内的名称不能重新分配,即它们不能被覆盖以指向某个新对象。然而,在极少数情况下,我们确实需要这种能力,因此 Python 通过 global
/nonlocal
关键字使其成为可能。我们可以说代码
global x
使 x
"write-global" (同时仍保持其状态为 "read-global")。
您发现的事实是,虽然外部范围内的 name 无法从内部范围内重新分配新对象,但对象本身实际上可能从内部范围内发生变异范围(当然,如果所讨论的对象是可变的)。实际上,可变对象本身不属于任何给定范围。名称有范围,对象没有。
注意
这对于 Python 3.8 甚至 Python 3 来说都不是特别的。但是对于 Python 语言来说却是特别的。 这对嵌套函数并不特殊,但也适用于例如从更深的范围(如函数或类)引用的模块级变量。【讨论】:
【参考方案2】:您不会更改变量的值。但是变量是对可变对象的引用,您可以更改这些对象。例如func_var4
是对一个可变对象列表的引用。您将一个元素添加到列表中。这会更改列表,但 func_var4
仍指向同一个列表。此操作未更改变量func_var4
。
【讨论】:
以上是关于为啥 Python 3.8.0 允许在不使用“非本地”变量的情况下从封闭函数范围更改可变类型?的主要内容,如果未能解决你的问题,请参考以下文章
双非本硕“统计学”连读,带你讲述一下我的 “Python编程学习” 之路!
为啥允许在 Python 中使用 super 从 __init__ 返回一个值?