闭包(closures)是Python中的一种强大机制,允许创建带有来自周围上下文的“记忆”数据的函数。
在函数式编程语言和Python中,函数是第一类对象。函数可以返回一个新函数,该函数会“闭合”其外部作用域中的变量。
开发人员经常混淆闭合变量在closure内部是如何“存在”的,何时读取或写入它们,以及如何安全地处理它们(特别是与可变对象相关时)。
如果内部函数使用外部函数的变量,它们会自动“被记住”——即使外部函数已经完成工作。对于读取变量这一点很明显,但如果需要更改值——就需要使用关键字nonlocal。在处理可变对象(例如lists, dicts)时,这尤其是一个风险区域。
示例:
def outer(): count = 0 def inner(): nonlocal count count += 1 return count return inner counter = outer() print(counter()) # 1 print(counter()) # 2
关键特点:
nonlocal(否则此操作会创建一个局部变量)。可以在函数内部不使用nonlocal修改闭合变量的值吗?
不可以。如果尝试在没有指定nonlocal的情况下分配新值,Python会将其视为创建一个新的局部变量,而旧值不会被改变。
示例:
def make_counter(): count = 0 def inner(): count += 1 # 错误 UnboundLocalError! return count return inner
可以通过closure将参数传递到外部作用域吗?
可以,closure会“记住”所有可在外部作用域中访问的变量,包括类的属性、全局变量等。但更改这些变量需要特别的努力(例如使用nonlocal或global)。
在closure内部,关于可变对象发生了什么?
如果闭合了一个引用可变对象的变量,例如列表,您可以在不使用nonlocal的情况下修改其内容,但如果您尝试重新赋值变量——就需要使用nonlocal。
示例:
def make_appender(): result = [] def append(x): result.append(x) # 可以! return result return append f = make_appender() print(f(1)) # [1] print(f(2)) # [1, 2]
开发人员编写闭包,在没有nonlocal的情况下更改变量——导致UnboundLocalError。
优点:
缺点:
在closure中明确使用nonlocal以管理状态。
优点:
缺点: