第 6 章:函数与作用域
6.1 函数对象
原理讲解
函数是一等公民(First-class Citizen):
┌─────────────────────────────────────────────────────────┐
│ Python 函数对象 │
├─────────────────────────────────────────────────────────┤
│ │
│ 函数在 Python 中是完全的对象: │
│ - 可以赋值给变量 │
│ - 可以作为参数传递 │
│ - 可以作为返回值 │
│ - 可以存储在数据结构中 │
│ │
│ 函数对象属性: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ __code__ → 代码对象 (字节码) │ │
│ │ __globals__ → 全局命名空间 │ │
│ │ __defaults__ → 默认参数 │ │
│ │ __kwdefaults__ → 关键字默认参数 │ │
│ │ __closure__ → 闭包单元格 │ │
│ │ __dict__ → 函数属性 │ │
│ │ __name__ → 函数名 │ │
│ │ __doc__ → 文档字符串 │ │
│ │ __module__ → 模块名 │ │
│ │ __annotations__ → 类型注解 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
闭包原理
闭包(Closure):函数 + 环境
┌─────────────────────────────────────────────────────────┐
│ 闭包工作原理 │
├─────────────────────────────────────────────────────────┤
│ │
│ def make_multiplier(n): # 外层函数 │
│ def multiplier(x): # 内层函数 │
│ return x * n # 引用外层变量 │
│ return multiplier # 返回内层函数 │
│ │
│ 内存布局: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ make_multiplier 调用时: │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ 栈帧:n = 5 │ │ │
│ │ │ ┌─────────────────────────────────┐ │ │ │
│ │ │ │ multiplier 函数对象 │ │ │ │
│ │ │ │ __closure__ → [cell(n=5)] │ │ │ │
│ │ │ └─────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ │ ↓ 返回 │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ times5 = multiplier (带环境 n=5) │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 即使 make_multiplier 返回,n 仍然被保留! │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
闭包的单元格(Cell)结构:
// CPython 源码
typedef struct {
PyObject_HEAD
PyObject *ob_ref; // 引用的对象
} PyCellObject;
查看闭包信息:
def make_multiplier(n):
def multiplier(x):
return x * n
return multiplier
times5 = make_multiplier(5)
# 查看闭包
print(f"闭包内容:{times5.__closure__}")
print(f"单元格:{times5.__closure__[0]}")
print(f"单元格值:{times5.__closure__[0].cell_contents}")
# 查看代码对象的自由变量
print(f"自由变量:{times5.__code__.co_freevars}")
closure 和 code
def outer(x):
def inner(y):
return x + y
return inner
closure_func = outer(10)
# __closure__ - 闭包变量
print("__closure__:", closure_func.__closure__)
# (<cell at 0x...: int object at 0x...>,)
# __code__ - 代码对象
code = closure_func.__code__
print("\n__code__ 属性:")
print(f" co_varnames: {code.co_varnames}") # 局部变量
print(f" co_freevars: {code.co_freevars}") # 自由变量
print(f" co_cellvars: {code.co_cellvars}") # 单元格变量
print(f" co_argcount: {code.co_argcount}") # 参数数量
6.2 作用域规则
LEGB 规则
Python 的作用域查找顺序:
┌─────────────────────────────────────────────────────────┐
│ LEGB 作用域规则 │
├─────────────────────────────────────────────────────────┤
│ │
│ L - Local (局部作用域) │
│ │ 当前函数内部的变量 │
│ │ │
│ E - Enclosing (外层函数作用域) │
│ │ 嵌套函数中外层函数的变量 │
│ │ │
│ G - Global (全局作用域) │
│ │ 模块级别的变量 │
│ │ │
│ B - Built-in (内置作用域) │
│ │ Python 内置名称 (len, print, etc.) │
│ │ │
│ 查找顺序:L → E → G → B │
│ │
│ 示例: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ x = "global" # G │ │
│ │ │ │
│ │ def outer(): │ │
│ │ x = "enclosing" # E │ │
│ │ def inner(): │ │
│ │ x = "local" # L │ │
│ │ print(x) # 输出:local │ │
│ │ inner() │ │
│ │ │ │
│ │ outer() │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
global 和 nonlocal
# global: 修改全局变量
count = 0
def increment():
global count
count += 1
increment()
print(count) # 1
# 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
作用域链图示:
┌─────────────────────────────────────────────────────────┐
│ 作用域链示例 │
├─────────────────────────────────────────────────────────┤
│ │
│ x = "G" # 全局 │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ def outer(): │ │
│ │ x = "E" # 外层 │ │
│ │ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ def inner(): │ │ │
│ │ │ x = "L" # 局部 │ │ │
│ │ │ print(x) # L → 找到,停止 │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ def inner2(): │ │ │
│ │ │ print(x) # L(无) → E → 找到 │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ def inner3(): │ │ │
│ │ │ print(y) # L(无) → E(无) → G │ │ │
│ │ │ → B(无) → NameError! │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
6.3 装饰器原理
装饰器的本质
装饰器 = 高阶函数
┌─────────────────────────────────────────────────────────┐
│ 装饰器工作原理 │
├─────────────────────────────────────────────────────────┤
│ │
│ 语法糖: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ @decorator │ │
│ │ def func(): │ │
│ │ pass │ │
│ │ │ │
│ │ 等价于: │ │
│ │ def func(): │ │
│ │ pass │ │
│ │ func = decorator(func) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 装饰器执行时机: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 1. 模块加载时(函数定义时) │ │
│ │ @decorator │ │
│ │ def func(): ... ← decorator(func) 立即执行 │ │
│ │ │ │
│ │ 2. 函数调用时 │ │
│ │ func() ← 调用的是装饰后的函数 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
装饰器栈
多个装饰器的执行顺序:
@decorator_a
@decorator_b
@decorator_c
def func():
pass
# 等价于
func = decorator_a(decorator_b(decorator_c(func)))
# 执行顺序(从下到上):
# 1. decorator_c(func)
# 2. decorator_b(result_c)
# 3. decorator_a(result_b)
# 调用顺序(从上到下):
# func() → decorator_a 的 wrapper → decorator_b 的 wrapper → decorator_c 的 wrapper → func
装饰器栈图示:
┌─────────────────────────────────────────────────────────┐
│ 装饰器栈 │
├─────────────────────────────────────────────────────────┤
│ │
│ 定义时(从内到外): │
│ ┌─────────────────────────────────────────────────┐ │
│ │ func │ │
│ │ ↓ │ │
│ │ decorator_c(func) │ │
│ │ ↓ │ │
│ │ decorator_b(decorator_c(func)) │ │
│ │ ↓ │ │
│ │ decorator_a(decorator_b(decorator_c(func))) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 调用时(从外到内): │
│ ┌─────────────────────────────────────────────────┐ │
│ │ decorator_a wrapper │ │
│ │ ↓ │ │
│ │ decorator_b wrapper │ │
│ │ ↓ │ │
│ │ decorator_c wrapper │ │
│ │ ↓ │ │
│ │ 原始 func │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
functools.wraps
为什么需要 wraps:
from functools import wraps
# 不使用 wraps
def decorator_no_wraps(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator_no_wraps
def my_func():
"""这是 my_func 的文档"""
pass
print(my_func.__name__) # 'wrapper' (错误!)
print(my_func.__doc__) # None (错误!)
# 使用 wraps
def decorator_with_wraps(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator_with_wraps
def my_func2():
"""这是 my_func2 的文档"""
pass
print(my_func2.__name__) # 'my_func2' (正确!)
print(my_func2.__doc__) # '这是 my_func2 的文档' (正确!)
wraps 做了什么:
# functools.wraps 等价于
def wraps(func):
def decorator(wrapper):
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
wrapper.__module__ = func.__module__
wrapper.__qualname__ = func.__qualname__
wrapper.__annotations__ = func.__annotations__
wrapper.__wrapped__ = func
wrapper.__dict__.update(func.__dict__)
return wrapper
return decorator
6.4 实践:手写装饰器
实验代码
# examples/chapter-06/decorator_examples.py
import time
import functools
from typing import Callable, Any
print("=" * 70)
print("装饰器实践示例")
print("=" * 70)
# ========== 1. 基础装饰器 ==========
print("\n【示例 1】基础装饰器 - 计时器")
print("-" * 50)
def timer(func: Callable) -> Callable:
"""计算函数执行时间的装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 执行时间:{end - start:.6f} 秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(0.1)
slow_function()
# ========== 2. 带参数的装饰器 ==========
print("\n【示例 2】带参数的装饰器 - 重试机制")
print("-" * 50)
def retry(max_attempts: int = 3, delay: float = 1.0):
"""失败时重试的装饰器"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
print(f"尝试 {attempt + 1}/{max_attempts} 失败:{e}")
if attempt < max_attempts - 1:
time.sleep(delay)
raise last_exception
return wrapper
return decorator
@retry(max_attempts=3, delay=0.1)
def might_fail():
import random
if random.random() < 0.7:
raise ValueError("随机失败")
return "成功!"
try:
result = might_fail()
print(f"结果:{result}")
except Exception as e:
print(f"最终失败:{e}")
# ========== 3. 类装饰器 ==========
print("\n【示例 3】类装饰器 - 单例模式")
print("-" * 50)
class Singleton:
"""单例模式装饰器"""
def __init__(self, cls):
self.cls = cls
self.instance = None
functools.update_wrapper(self, cls)
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@Singleton
class Database:
def __init__(self):
print("数据库连接创建")
def query(self, sql):
return f"执行:{sql}"
db1 = Database()
db2 = Database()
print(f"db1 is db2: {db1 is db2}") # True
# ========== 4. 装饰器链 ==========
print("\n【示例 4】装饰器链 - 多重装饰")
print("-" * 50)
def log_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
def validate_types(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for i, arg in enumerate(args):
if not isinstance(arg, (int, float)):
raise TypeError(f"参数 {i} 类型错误:{type(arg)}")
return func(*args, **kwargs)
return wrapper
@log_call
@validate_types
@timer
def add(a, b):
return a + b
print(f"结果:{add(1, 2)}")
# ========== 5. 查看装饰器效果 ==========
print("\n【示例 5】装饰器 introspection")
print("-" * 50)
@timer
def decorated_func():
"""这是一个被装饰的函数"""
pass
print(f"函数名:{decorated_func.__name__}")
print(f"文档:{decorated_func.__doc__}")
print(f"原始函数:{decorated_func.__wrapped__}")
实验练习
练习 1:实现缓存装饰器
import functools
def memoize(func):
"""缓存函数结果的装饰器"""
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
# 暴露缓存信息
wrapper.cache_info = lambda: f"缓存大小:{len(cache)}"
wrapper.cache_clear = lambda: cache.clear()
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试
print(f"fib(10) = {fibonacci(10)}")
print(fibonacci.cache_info())
print(f"fib(20) = {fibonacci(20)}")
print(fibonacci.cache_info())
练习 2:实现权限检查装饰器
from functools import wraps
class User:
def __init__(self, name, role):
self.name = name
self.role = role
_current_user = None
def set_current_user(user):
global _current_user
_current_user = user
def require_role(required_role):
"""需要特定角色才能访问的装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if _current_user is None:
raise PermissionError("未登录")
if _current_user.role != required_role:
raise PermissionError(f"需要 {required_role} 角色")
return func(*args, **kwargs)
return wrapper
return decorator
@require_role("admin")
def delete_user(user_id):
return f"删除用户 {user_id}"
# 测试
set_current_user(User("Alice", "user"))
try:
delete_user(1)
except PermissionError as e:
print(f"权限错误:{e}")
set_current_user(User("Bob", "admin"))
print(delete_user(1))
练习 3:实现异步装饰器
import asyncio
import time
from functools import wraps
def async_timer(func):
"""异步函数计时装饰器"""
@wraps(func)
async def wrapper(*args, **kwargs):
start = time.perf_counter()
result = await func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 执行时间:{end - start:.6f} 秒")
return result
return wrapper
@async_timer
async def async_sleep(seconds):
await asyncio.sleep(seconds)
return "完成"
# 运行
asyncio.run(async_sleep(0.5))
常见问题
Q1: 装饰器会影响性能吗?
A: 会,但通常可忽略:
- 每次调用增加一层函数调用
- 复杂装饰器(如重试、缓存)影响更大
- 热点代码慎用装饰器
Q2: 如何调试被装饰的函数?
A:
# 使用 __wrapped__ 访问原始函数
decorated_func.__wrapped__()
# 使用 functools.wraps 保留元数据
# 调试器可以看到原始函数名和文档
Q3: 装饰器能否带多个参数?
A:
@decorator(arg1, arg2, kwarg1=value)
def func():
pass
# 装饰器工厂模式
def decorator(arg1, arg2, kwarg1=default):
def actual_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 使用 arg1, arg2, kwarg1
return func(*args, **kwargs)
return wrapper
return actual_decorator
Q4: 类方法如何装饰?
A:
class MyClass:
@classmethod
@decorator
def class_method(cls):
pass
@staticmethod
@decorator
def static_method():
pass
@property
@decorator
def prop(self):
pass
# 注意装饰器顺序!@classmethod/@staticmethod 应在最内层
Q5: 如何创建可配置的装饰器类?
A:
class DecoratorClass:
def __init__(self, config_option):
self.config_option = config_option
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 使用 self.config_option
return func(*args, **kwargs)
return wrapper
@DecoratorClass(config_option="value")
def func():
pass
本章小结
- 函数是完全的对象,有属性和方法
- 闭包 = 函数 + 环境,通过__closure__访问
- LEGB 规则决定变量查找顺序
- global 修改全局变量,nonlocal 修改外层变量
- 装饰器是高阶函数,在定义时执行
- functools.wraps 保留原函数元数据
- 装饰器可以链式使用,从内到外执行
下一章预告
第 7 章将深入探讨 生成器与协程,包括迭代器协议、yield 原理和 async/await。


被折叠的 条评论
为什么被折叠?



