Python3系列:闭包和装饰器
一、闭包基础概念
1.1 什么是闭包?
闭包是指内部函数引用了外部函数的变量,并且外部函数返回内部函数的一种编程结构。
# 1.1.1 最简单的闭包
def outer_function(msg):
"""外部函数"""
message = msg # 外部函数的变量
def inner_function():
"""内部函数 - 闭包"""
print(message) # 引用了外部变量
return inner_function # 返回内部函数
# 创建闭包
hello_func = outer_function("Hello World!")
hello_func() # 输出: Hello World!
# 创建不同的闭包
hi_func = outer_function("Hi there!")
hi_func() # 输出: Hi there!
# 每个闭包都记住了自己的环境
hello_func() # 仍然输出: Hello World!
1.2 闭包的特点
def make_counter():
"""创建一个计数器"""
count = 0 # 外部函数的变量
def counter():
nonlocal count # 声明使用外部变量
count += 1
return count
return counter
# 创建两个独立的计数器
counter1 = make_counter()
counter2 = make_counter()
print(counter1()) # 1
print(counter1()) # 2
print(counter1()) # 3
print(counter2()) # 1 (独立的,从0开始)
print(counter2()) # 2
# 验证闭包的特性
print(counter1.__closure__) # 显示闭包中引用的变量
print(counter1.__closure__[0].cell_contents) # 访问闭包变量的值
二、闭包的核心机制
2.1 变量的绑定与生命周期
def power_factory(exponent):
"""创建幂函数的工厂"""
# exponent 被绑定在闭包中
def power(base):
return base ** exponent
return power
# 创建不同的幂函数
square = power_factory(2) # 平方函数
cube = power_factory(3) # 立方函数
sqrt = power_factory(0.5) # 平方根函数
print(square(5)) # 25
print(cube(5)) # 125
print(sqrt(25)) # 5.0
# exponent 变量在外部函数返回后仍然存在
print(square.__closure__[0].cell_contents) # 2
print(cube.__closure__[0].cell_contents) # 3
2.2 nonlocal 关键字详解
def make_advanced_counter():
"""演示 nonlocal 的必要性"""
count = 0
total = 0
def increment(amount=1):
nonlocal count, total # 声明这些变量来自外部作用域
count += amount
total += amount
return count
def decrement(amount=1):
nonlocal count, total
count -= amount
total += amount # 绝对值累加
return count
def get_stats():
return {
'current': count,
'total_operations': total,
'average': total / (count + total) if count else 0
}
# 返回多个内部函数
return {
'inc': increment,
'dec': decrement,
'stats': get_stats
}
# 使用
counter = make_advanced_counter()
print(counter['inc'](5)) # 5
print(counter['inc'](3)) # 8
print(counter['dec'](2)) # 6
print(counter['stats']()) # {'current': 6, 'total_operations': 10, 'average': 0.625}
三、闭包的常见应用场景
3.1 数据隐藏与封装
def create_bank_account(initial_balance=0):
"""创建银行账户(闭包实现私有数据balance的访问)"""
balance = initial_balance
transactions = []
def deposit(amount):
nonlocal balance
if amount <= 0:
raise ValueError("存款金额必须为正数")
balance += amount
transactions.append(('deposit', amount, balance))
return balance
def withdraw(amount):
nonlocal balance
if amount <= 0:
raise ValueError("取款金额必须为正数")
if amount > balance:
raise ValueError("余额不足")
balance -= amount
transactions.append(('withdraw', amount, balance))
return balance
def get_balance():
return balance
def get_history():
return transactions.copy()
def reset():
nonlocal balance
balance = 0
transactions.clear()
# 返回操作接口
return {
'deposit': deposit,
'withdraw': withdraw,
'balance': get_balance,
'history': get_history,
'reset': reset
}
# 使用账户
account = create_bank_account(1000)
account['deposit'](500)
account['withdraw'](200)
print(account['balance']()) # 1300
print(account['history']()) # [('deposit', 500, 1500), ('withdraw', 200, 1300)]
# 无法直接访问私有数据
# print(account.balance) # 错误!
# print(account.transactions) # 错误!
3.2 装饰器实现
装饰器的本质定义
装饰器:接受一个函数作为参数,并返回一个新的函数(或修改后的原函数)的可调用对象。
import time
from functools import wraps
def timer_decorator(func):
"""计时器装饰器(闭包经典应用)"""
@wraps(func)
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
def retry_decorator(max_retries=3, delay=1):
"""带参数的重试装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_retries - 1:
raise
print(f"第 {attempt + 1} 次失败,重试中...")
time.sleep(delay)
return None
return wrapper
return decorator
@timer_decorator
def slow_function():
time.sleep(0.5)
return "完成"
@retry_decorator(max_retries=3, delay=0.5)
def unstable_function():
import random
if random.random() < 0.7: # 70% 概率失败
raise ValueError("随机失败")
return "成功"
# 测试
slow_function()
print(unstable_function())
使用 @wraps 的铁律:
-
✅ 当你的装饰器创建了新的包装函数时,必须用
@wraps -
❌ 当你的装饰器直接返回原函数时,不需要
@wraps
# 需要 @wraps
def decorator(func): @wraps(func) def wrapper(*args, **kwargs): # ⭐ 创建了新函数 return func(*args, **kwargs) return wrapper # 不需要 @wraps def decorator(func): print(f"注册: {func.__name__}") # ⭐ 没创建新函数 return func

浙公网安备 33010602011771号