四种作用域:LEGB

Python 查找变量按这个顺序:

L Local       当前函数内部
E Enclosing   外层函数(嵌套时)
G Global      模块顶层(文件全局)
B Built-in    内置(print / len / range 等)
x = "global"

def outer():
    x = "enclosing"

    def inner():
        x = "local"
        print(x)        # local
    inner()
    print(x)            # enclosing

outer()
print(x)                # global

函数里能"看见"全局,但默认不能"改"

count = 0

def add():
    print(count)        # OK,能读
    count += 1          # UnboundLocalError!

为什么?只要函数体里有 count = ... 赋值,Python 就把 count 当成局部变量——但读那行时局部变量还没值。

global:声明"我要改全局变量"

count = 0

def add():
    global count
    count += 1

add()
add()
print(count)            # 2

⚠️ 慎用 global——容易让代码难以追踪。优先:返回值 + 重新赋值。

nonlocal:改外层函数的变量

def make_counter():
    count = 0

    def increment():
        nonlocal count       # 不是 global!是上一层函数的
        count += 1
        return count

    return increment

counter = make_counter()
print(counter())    # 1
print(counter())    # 2
print(counter())    # 3

闭包

内层函数"记住"了外层的变量——这就是闭包

def multiplier(n):
    def mul(x):
        return x * n      # 记住了外层的 n
    return mul

double = multiplier(2)
triple = multiplier(3)

double(5)     # 10
triple(5)     # 15

闭包是装饰器、回调函数的基础。

实战:避免命名冲突

# 不好:函数内变量名和参数撞车
def f(list):              # 覆盖了内置 list!
    list.append(1)

# 好:换个名字
def f(items):
    items.append(1)

下一篇讲文件读写——把数据存到磁盘。