Python中嵌套函数覆盖变量
假设我有以下的Python代码:
def outer():
string = ""
def inner():
string = "String was changed by a nested function!"
inner()
return string
我希望调用outer()时能返回“字符串被嵌套函数修改了!”,但实际上我得到的是“”。我得出的结论是,Python认为这一行string = "string was changed by a nested function!"
是在inner()函数里声明了一个新的变量。我的问题是:我该如何告诉Python使用outer()里的字符串呢?我不能使用global
关键字,因为这个字符串并不是全局的,它只是存在于外部作用域中。有什么想法吗?
4 个回答
这事儿我遇到得太多了。每次我在写一个函数的时候,突然想到可以写一个小的辅助函数,但这个辅助函数在别的地方用不到。这时候我就想把它放在里面,作为一个嵌套函数。
我之前有过在JAVA中使用匿名对象的经验(比如定义一个可运行的对象),当时的规则是,匿名对象会把它外面的环境“硬拷贝”一份,也就是外面变量的值。所以如果外面的变量是不可变的(比如 int
或 char
),它们就不能被匿名对象修改,因为它们是按值拷贝的;而如果是可变的(比如 collection
或 objects
),它们就可以被修改,因为它们是按指针拷贝的(也就是它们在内存中的地址)。
如果你对编程有了解,可以把它理解为按值传递和按引用传递。
在Python中,情况也是差不多的。x=123
是一个赋值操作,它给变量x赋予了一个新含义(并不是修改原来的x),而 list[i]/dict[key]
是对象访问操作,它们实际上是修改了东西。
总之,你需要一个可变对象……才能进行修改(即使你可以用 [] 访问一个元组,但在这里不能用,因为元组是不可变的)。
你也可以通过使用函数属性来解决这个问题:
def outer():
def inner():
inner.string = "String was changed by a nested function!"
inner.string = ""
inner()
return inner.string
说明一下:这个方法在 Python 2.x 和 3.x 中都可以用。
在Python 3.x中,你可以使用nonlocal
这个关键词:
def outer():
string = ""
def inner():
nonlocal string
string = "String was changed by a nested function!"
inner()
return string
在Python 2.x中,你可以用一个只包含一个元素的列表,并且可以覆盖那个唯一的元素:
def outer():
string = [""]
def inner():
string[0] = "String was changed by a nested function!"
inner()
return string[0]