(四)函数中的名称查找与作用域
1. 局部名称与外层名称
# goal 位于模块层;函数只读取它,没有给它重新赋值。
goal = 30
def is_met(minutes):
result = minutes >= goal
return result
print(is_met(35))
# for 不建立独立的函数作用域,循环结束后 day 仍在当前作用域中。
for day in range(2):
last_day = day
print(day, last_day)输出 True 和 1 1。minutes、result 属于这次函数调用的局部名称;模块层没有创建名为 result 的变量。for、if 的缩进负责语句组织,不会像一次函数调用那样创建新的局部作用域。推导式则有自己的循环变量作用范围,[x * x for x in values] 中的 x 不会替换外面的同名绑定。
在普通函数中查找一个名称,可以按 局部 → 外层函数 → 当前模块 → 内置名称 阅读,常缩写为 LEGB。调用者自己的局部变量不会插入这条查找路线。把列表命名为 sum 后,同一作用域里再写 sum(values),找到的就是列表而不是内置求和函数;名称尽量避开 list、dict、sum 这些已经在用的工具名。
2. 赋值确定局部名称,global 指向模块绑定
# 函数中的赋值建立局部 goal,模块层 goal 保持原值。
goal = 30
def choose_goal():
goal = 45
return goal
print(choose_goal(), goal)结果是 45 30。若在 choose_goal 中先写 print(goal),后面仍保留 goal = 45,Python 会报告 UnboundLocalError:这个函数中存在对 goal 的局部赋值,前面的读取也被解释为读取局部名称,但当时还没有给它值。
需要重新绑定模块层名称时,可以明确写 global:
# global 让下面的赋值更新模块层的 calls。
calls = 0
def record_call():
global calls
calls += 1
record_call()
record_call()
print(calls)得到 2。普通统计函数把状态作为参数交进来、作为结果交出去,会更容易看清数据来源;这里用 global 展示跨作用域重新绑定的动作。只对模块层列表执行 append,修改的是对象,并没有重新绑定列表名称,因此与这段计数代码的动作不同。名称绑定与解析