11_作用域闭包与可变对象
解释名字查找、局部变量、闭包捕获和对象共享。
Python 基础专题 11:作用域、闭包与可变对象
学习目标:解释名字查找、局部变量、闭包捕获和对象共享。
核心知识
变量是指向对象的名字。函数内赋值默认创建局部名字;读取名字时会沿局部、外围函数、模块全局、内置命名空间的方向查找,常称 LEGB。若函数内对某名字赋值,Python 会把它视为局部变量;在赋值前读取会触发 UnboundLocalError。
def make_counter():
count = 0
def next_value():
nonlocal count
count += 1
return count
return next_value
counter = make_counter()
assert (counter(), counter()) == (1, 2)
nonlocal 修改最近外围函数的绑定,global 修改模块级绑定;大量使用会让状态难追踪,可优先通过参数和返回值传递数据。闭包会保留其引用的外层变量。循环中创建闭包时,若希望每个函数固定当轮的值,可用默认参数 lambda x=i: x,因为普通闭包通常在调用时读取变量。
可变对象如列表、字典可被多个名字引用:b = a 不复制列表,修改 b 也会看到 a 变化。a.copy() 是浅复制;嵌套可变对象仍共享。需要深复制时可用 copy.deepcopy,但应先明确哪些对象本来就该共享。
逐步理解
区分“重新绑定名字”和“修改对象”:items = [] 在函数内通常只改变局部名字,items.append(1) 则可能改变调用者共享的列表。闭包 make_counter 在外层函数结束后还能访问保存的 count,因为这个变量仍被内层函数引用。状态越多,越需要明确谁拥有它以及何时修改它。
动手练习
练习:预测 a=[1]; b=a; b.append(2) 后 a 的内容。答案:[1, 2],两个名字指向同一个列表。