Python装饰器的进阶应用与实战技巧
装饰器(Decorator)是Python中最优雅的特性之一,它允许我们在不修改原有函数代码的前提下,为函数添加额外的功能。本文将深入探讨装饰器的进阶用法,展示如何编写实用的装饰器来提升代码质量。
在实际开发中,装饰器常用于日志记录、性能监测、权限校验、缓存管理等场景。通过本文的学习,你将掌握装饰器的核心概念,并能够编写出自己的实用装饰器。
一、装饰器的基础原理
装饰器本质上是一个接受函数作为参数,并返回一个新函数的高阶函数。这个新函数通常会在调用原函数前后执行一些额外的操作
基本的装饰器模板如下:
def my_decorator(func):
def wrapper(*args, **kwargs):
# 在原函数执行前的操作
result = func(*args, **kwargs)
# 在原函数执行后的操作
return result
return wrapper
使用 functools.wraps 保留原函数的元数据,这是一个最佳实践:
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 装饰器逻辑
return func(*args, **kwargs)
return wrapper
二、带参数的装饰器
实际开发中,我们经常需要创建可以接收参数的装饰器。下面是一个可以重试失败的装饰器示例:
import functools
import time
def retry(max_attempts=3, delay=1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"重试 {attempt 1}/{max_attempts} 失败,{e}")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_operation():
import random
if random.random() < 0.7:
raise ConnectionError("网络不稳定")
return "操作成功"
这个装饰器可以自动重试失败的函数,适用于网络请求、数据库连接等不稳定操作
三、单例模式的装饰器
单例模式可以确保一个类只有一个实例,在缓存、数据库连接池等场景中非常有用。下面是一个单例缓存装饰器:
import functools
def singleton(class_):
instances = {}
def get_instance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return get_instance
@singleton
class Database:
def __init__(self):
self.connected = False
self.connection_count = 0
def connect(self):
if not self.connected:
self.connected = True
self.connection_count = 1
print(f"建立新的数据库连接 ({self.connection_count})")
else:
print("使用已有的连接")
使用示例:
db1 = Database()
db1.connect()
# 输出: 建立新的数据库连接 (1)
db2 = Database()
db2.connect()
# 输出: 使用已有的连接
print(db1 is db2) # True
四、智能缓存装饰器
缓存是提升性能的常用手段,下面是一个支持过期时间的智能缓存装饰器:
import functools
import time
from datetime import datetime, timedelta
def timed_cache(seconds=60):
def decorator(func):
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 生成缓存键
key = str(args) str(sorted(kwargs.items()))
# 检查缓存
now = datetime.now()
if key in cache:
cached_time, value = cache[key]
if now - cached_time < timedelta(seconds=seconds):
return value
# 计算并缓存
result = func(*args, **kwargs)
cache[key] = (now, result)
return result
return wrapper
return decorator
@timed_cache(seconds=5)
def expensive_computation(n):
print(f"计算 fibonacci({n})…")
time.sleep(1) # 模拟昂贵操作
if n <= 1:
return n
return expensive_computation(n-1) expensive_computation(n-2)
五、性能监测装饰器
性能监测是优化代码的重要工具,下面是一个可以记录函数执行时间的装饰器:
import functools
import time
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def log_execution_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
%duration = end_time - start_time
logger.info(f"函数 {func.__name__} 执行时间: {duration:.4f} 秒")
return result
return wrapper
@log_execution_time
def process_data(data):
time.sleep(0.5)
return [x * 2 for x in data]
六、权限校验装饰器
在Web应用或API开发中,权限校验是必不可少的。下面是一个基于角色的权限校验装饰器:
import functools
def require_role(*required_roles):
def decorator(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if not hasattr(user, 'roles'):
raise PermissionError("用户缺角色信息")
if not any(role in user.roles for role in required_roles):
raise PermissionError(f"需要以下角色之一: {required_roles}")
return func(user, *args, **kwargs)
return wrapper
return decorator
class User:
def __init__(self, name, roles):
self.name = name
self.roles = roles
@require_role("admin", "editor")
def edit_article(user, article_id):
):
return f"{user.name} 正在编辑文章 {article_id}"
admin_user = User("张三", ["admin"])
guest_user = User("李四", ["guest"])
edit_article(admin_user, 123) # 成功
edit_article(guest_user, 123) # 抛出 PermissionError
七、装饰器链与组合
Python支持多个装饰器的叠加使用,它们会从内到外依次执行:
@log_execution_time
@retry(max_attempts=2)
@timed_cache(seconds=30)
def complex_api_call(url):
# 模拟API调用
time.sleep(0.2)
return f"Data from {url}"
执行顺序是: timed_cache → retry → log_execution_time
八、类装饰器
除了函数装饰器,Python还支持类装饰器,它可以用来修改类的行为:
def add_str_method(cls):
def __str__(self):
return f"[{cls.__name__}] {self.__dict__}"
cls.__str__ = __str__
return cls
@add_str_method
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("王五", 30)
print(p) # [Person] {\name': 