一、Python 作用域规则:LEGB 原则
Python 查找变量时遵循 LEGB 规则,按以下顺序搜索:
- Local(局部作用域)- 当前函数内部
- Enclosing(嵌套作用域)- 外层嵌套函数
- Global(全局作用域)- 当前模块级别
- Built-in(内置作用域)- Python 内置命名空间
代码示例:LEGB 规则演示
# 全局变量
x = "global"
def outer():
# 嵌套作用域
x = "enclosing"
def inner():
# 局部作用域
x = "local"
print(f"inner: {x}")
inner()
print(f"outer: {x}")
outer()
print(f"global: {x}")
# 输出:
# inner: local
# outer: enclosing
# global: global
二、global 与 nonlocal 关键字
2.1 global 关键字
用于在函数内部修改全局变量:
count = 0
def increment():
global count
count += 1
print(f"Count: {count}")
increment() # Count: 1
increment() # Count: 2
print(f"Final: {count}") # Final: 2
2.2 nonlocal 关键字
用于在嵌套函数中修改外层函数的变量:
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
三、闭包(Closure)详解
闭包是指一个函数对象,它可以访问并"记住"其定义时的外部作用域中的变量,即使外部函数已经执行完毕。
闭包的三个条件:
- 必须有嵌套函数
- 内层函数引用外层函数的变量
- 外层函数返回内层函数
3.1 基础闭包示例
def make_multiplier(factor):
"""创建一个乘法器闭包"""
def multiply(number):
return number * factor
return multiply
# 创建不同的乘法器
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
print(double(10)) # 20
3.2 闭包的实际应用:装饰器基础
def timer_decorator(func):
"""简单的计时装饰器"""
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间:{end - start:.4f}秒")
return result
return wrapper
@timer_decorator
def slow_function():
total = 0
for i in range(1000000):
total += i
return total
result = slow_function()
print(f"结果:{result}")
3.3 闭包陷阱:循环变量绑定
# ❌ 错误示例
functions = []
for i in range(3):
functions.append(lambda: i)
print([f() for f in functions]) # [2, 2, 2] - 都是 2!
# ✅ 正确示例:使用默认参数绑定
functions = []
for i in range(3):
functions.append(lambda x=i: x)
print([f() for f in functions]) # [0, 1, 2] - 正确!
# ✅ 或使用闭包
functions = []
for i in range(3):
def make_func(val):
return lambda: val
functions.append(make_func(i))
print([f() for f in functions]) # [0, 1, 2] - 正确!
四、实战案例:用闭包实现状态保持
4.1 简易缓存系统
def create_cache():
"""创建一个带缓存功能的函数"""
cache = {}
def get_or_compute(key, compute_func):
if key not in cache:
print(f"计算 {key}...")
cache[key] = compute_func()
else:
print(f"从缓存获取 {key}")
return cache[key]
return get_or_compute
# 使用缓存
cache = create_cache()
def expensive_compute():
return sum(range(1000000))
print(cache("sum", expensive_compute)) # 首次计算
print(cache("sum", expensive_compute)) # 从缓存获取
4.2 函数调用计数器
def count_calls(func):
"""统计函数调用次数的装饰器"""
count = 0
def wrapper(*args, **kwargs):
nonlocal count
count += 1
print(f"{func.__name__} 被调用 {count} 次")
return func(*args, **kwargs)
wrapper.call_count = lambda: count
return wrapper
@count_calls
def greet(name):
return f"Hello, {name}!"
print(greet("Alice"))
print(greet("Bob"))
print(f"总调用次数:{greet.call_count()}")
五、closure 属性:查看闭包变量
Python 允许我们检查函数的闭包内容:
def outer(x):
def inner(y):
return x + y
return inner
closure_func = outer(10)
# 查看闭包信息
print(f"闭包变量数:{len(closure_func.__closure__)}")
print(f"闭包值:{closure_func.__closure__[0].cell_contents}")
print(f"调用结果:{closure_func(5)}") # 15
📝 总结
| 概念 | 用途 | 关键字 |
|---|---|---|
| 局部作用域 | 函数内部变量 | - |
| 全局作用域 | 模块级变量 | global |
| 嵌套作用域 | 外层函数变量 | nonlocal |
| 闭包 | 保持状态、工厂函数 | - |
核心要点:
- 牢记 LEGB 查找顺序
- 修改外部变量时使用
global或nonlocal - 闭包可以"记住"创建时的环境
- 注意循环中闭包的变量绑定问题