如果在函数的子函数中需要调用外部变量,一般会看见一个nonlocal声明,类似下面这种:
def outer_function():x = 10def inner_function():nonlocal xx += 1print(x)inner_function()outer_function()
在这个例子中,inner_function 引用了外部函数 outer_function 中的变量 x,并对其进行了修改。由于涉及修改外部函数的变量,所以需要使用 nonlocal 关键字。
如果在 inner_function 中不使用 nonlocal,而直接对 x 进行修改,Python 将认为 x 是一个新的局部变量,并在 inner_function 中创建一个新的局部变量 x,而不会修改外部函数 outer_function 中的 x。
这个时候运行会出现报错:
到上面为止都很容易理解。
但是迷惑的点在于我经常看见子函数应用外部变量,但没有进行nonlocal的声明,比如下面这种:
def outer_function():x = [10]def inner_function():x.append(2)print(x)inner_function()
outer_function()
这里inner_function 也引用了外部函数 outer_function 中的变量 x,并对其进行了修改,但并没有声明nonlocal,这是为什么?
原因是:
在这个特定的情况下,x是一个可变对象(列表),而不是不可变对象。在 Python 中,对于可变对象(如列表、字典等),可以在不使用 nonlocal 的情况下在函数内部修改它们的值,因为它们是通过引用传递的。
当你在函数内部修改一个可变对象时,实际上是在原地修改这个对象,而不是重新绑定变量。因此,对于列表 x,你可以在函数内部追加或删除元素,而无需使用 nonlocal。
如果 x是一个不可变对象,例如数字、字符串或元组,你将需要使用 nonlocal 关键字,因为对于不可变对象,重新绑定变量是唯一的方式来修改它们的值。
总的来说,对于可变对象,你可以在函数内部修改它们的值,而不需要 nonlocal 关键字。而对于不可变对象,你通常需要使用 nonlocal 或者返回修改后的值并重新赋给外部变量。
需要注意的事,就算在函数内部引用一个外部的可变对象变量,如果涉及到对该变量的重新赋值,也需要加nonlocal
def outer_function():x = [1,2,3]def inner_function():nonlocal xx = x+[2]# x.append(2)print(x)inner_function()outer_function()
def outer_function():x = [1,2,3]def inner_function():# nonlocal x# x = x+[2]x.append(2)print(x)inner_function()outer_function()
要理解这两者的区别,关键是对于x = []这句的理解,在这里,x 是一个变量,它存储了对列表 [1, 2, 3] 的引用。这表示 x 指向了内存中存储这个列表的位置。而x + [2] 创建了一个新的列表 [1, 2, 3, 2],然后将这个新列表赋给了变量 x。现在,变量 x 指向了一个新的列表对象,发生了重新赋值。所以存在这种重新赋值的情况时,需要加一个nonlocal,这样才能在子函数外部接收到。