1

在以下 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'}
4

2 回答 2

1

关于全局/非局部名称和(im)可变对象的规则在 Python 中关于读写是不对称的。请注意,在 Python中,变量的名称和名称所指向的对象/值之间存在(有时)重要的区别,因此在下文中,我将尝试明确这一点,并避免使用模棱两可的术语“变量” “:

  • 所有名称都是我们所说的“ read -global”(和“ read -nonlocal”),在某种意义上,它们总是可以在不使用global/nonlocal关键字的情况下查找,并检索它们的对象。
  • 相反,外部范围内的名称不能被重新分配,即它们不能被覆盖以指向某个新对象。然而,在极少数情况下,我们确实需要这种能力,因此 Python 通过global/nonlocal关键字使其成为可能。我们可以说代码
    global x
    
    使xwrite -global”(同时仍保持其状态为“ read -global”)。
  • 您发现的事实是,虽然外部范围的名称不能从内部范围内重新分配一个新对象,但对象本身实际上可能从内部范围内发生变异(如果所讨论的对象是可变的,当然)。实际上,可变对象本身不属于任何给定范围。名称有范围,对象没有。

注意

  • 这对于 Python 3.8 甚至 Python 3 来说都不是特别的。但是对于 Python 语言来说却是特别的。
  • 这对于嵌套函数并不特殊,但也适用于从更深的范围(如函数或类)引用的模块级变量。
于 2020-10-12T20:31:40.163 回答
0

您不会更改变量的值。但是变量是对可变对象的引用,您可以更改这些对象。例如func_var4,对作为可变对象的列表的引用。您将一个元素添加到列表中。这会更改列表,但func_var4仍指向同一个列表。func_var4此操作未更改变量。

于 2020-10-12T19:07:53.573 回答