Python装饰器和闭包
闭包基础
什么是闭包?
闭包是一个函数对象,它可以访问其定义所在作用域中的变量,即使该作用域已经结束。闭包让你可以在一个内部函数中访问外部函数的作用域。
闭包示例
def make_counter(start=0):
"""创建一个计数器闭包"""
count = [start] # 使用列表存储状态
def counter():
count[0] += 1
return count[0]
return counter
# 使用闭包
counter1 = make_counter(5)
print(counter1()) # 输出: 6
print(counter1()) # 输出: 7
counter2 = make_counter(10)
print(counter2()) # 输出: 11
print(counter1()) # 输出: 8
注意事项
Python3中的闭包可以使用nonlocal关键字修改外部函数的变量,但要谨慎使用,以免造成代码难以理解。
装饰器基础
什么是装饰器?
装饰器是一个可调用对象,它可以包装另一个函数或类,并且可以在不修改原始函数代码的情况下扩展其功能。
# 基本装饰器
def log_calls(func):
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"函数 {func.__name__} 返回: {result}")
return result
return wrapper
@log_calls
def add(a, b):
return a + b
# 使用装饰器
result = add(3, 5) # 自动打印日志
装饰器工作原理
使用@语法糖等同于:
def add(a, b):
return a + b
add = log_calls(add) # 手动装饰
装饰器参数
带参数的装饰器
def repeat(times=1):
"""创建一个可以指定重复次数的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"你好, {name}!")
# 调用函数
greet("小明") # 会打印三次问候
保留函数元信息
from functools import wraps
def log_calls(func):
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
return func(*args, **kwargs)
return wrapper
类装饰器
装饰类的方法
class Cache:
def __init__(self):
self._cache = {}
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
if key not in self._cache:
self._cache[key] = func(*args, **kwargs)
return self._cache[key]
return wrapper
# 使用类装饰器
@Cache()
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
装饰整个类
def singleton(cls):
"""实现单例模式的装饰器"""
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("初始化数据库连接...")
实际应用
1. 性能监控装饰器
import time
import functools
def measure_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} 执行时间: {end_time - start_time:.4f} 秒")
return result
return wrapper
@measure_time
def process_data(data):
time.sleep(1) # 模拟耗时操作
return len(data)
2. 缓存装饰器
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return wrapper
@memoize
def compute_expensive(n):
print(f"计算 {n}")
return sum(i * i for i in range(n))
3. 权限验证装饰器
def require_permission(permission):
def decorator(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if permission in user.permissions:
return func(user, *args, **kwargs)
else:
raise PermissionError(f"用户没有{permission}权限")
return wrapper
return decorator
@require_permission("admin")
def delete_user(current_user, user_id):
print(f"删除用户 {user_id}")
4. 参数验证装饰器
def validate_types(**expected_types):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 检查参数类型
for arg_name, expected_type in expected_types.items():
if arg_name in kwargs:
if not isinstance(kwargs[arg_name], expected_type):
raise TypeError(f"{arg_name} 必须是 {expected_type}")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(age=int, name=str)
def register_user(name, age):
print(f"注册用户: {name}, 年龄: {age}")
练习与实践
练习1:重试装饰器
实现一个装饰器,在函数失败时自动重试指定次数。
def retry(max_attempts=3, delay=1):
"""
装饰器:在函数失败时自动重试
max_attempts: 最大重试次数
delay: 重试间隔(秒)
"""
# 在此编写你的代码
pass
练习2:日志装饰器
创建一个装饰器,记录函数的调用信息到文件。
def log_to_file(filename):
"""
装饰器:记录函数调用到文件
filename: 日志文件名
"""
# 在此编写你的代码
pass
练习3:缓存装饰器
实现一个带有过期时间的缓存装饰器。
def cache_with_timeout(timeout_seconds):
"""
装饰器:缓存函数结果,并在指定时间后过期
timeout_seconds: 缓存过期时间(秒)
"""
# 在此编写你的代码
pass