当前位置:首页 > Python > 正文内容

Python 装饰器实战技巧:让代码更优雅高效

admin2周前 (03-23)Python19

装饰器是 Python 中最优雅的特性之一,它允许我们在不修改函数源代码的情况下,动态地增强函数的功能。这种"切面编程"的思想让代码更加模块化、可维护。本文将带你深入理解装饰器的各种用法和实战技巧。

一、装饰器基础原理

装饰器本质上是一个接受函数作为参数的高阶函数,它返回一个新的函数来替代原函数。这种语法糖让代码更加简洁直观。

import time
from functools import wraps

def timer(func):
    """计算函数执行时间的装饰器"""
    @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.5)
    return "完成"

# 调用
print(slow_function())

输出结果:

slow_function 执行时间: 0.500321秒
完成

这里的 @wraps(func) 装饰器非常重要,它会保留原函数的 __name__、__doc__ 等元数据,避免函数签名丢失。

二、参数化装饰器

有时我们需要为装饰器传递参数,比如设置重试次数、缓存大小等。这需要创建一个装饰器工厂函数。

def repeat(times=3):
    """重复执行函数指定次数"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(times=5)
def generate_random():
    """生成随机数"""
    import random
    return random.randint(1, 100)

print(f"生成结果: {generate_random()}")

参数化装饰器的好处在于灵活性。我们可以根据不同需求配置装饰器行为:

def validate_types(*type_args, **type_kwargs):
    """参数类型验证装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 验证位置参数
            for i, (arg, expected_type) in enumerate(zip(args, type_args)):
                if not isinstance(arg, expected_type):
                    raise TypeError(
                        f"参数 {i} 应该是 {expected_type},实际是 {type(arg)}"
                    )
            # 验证关键字参数
            for key, value in kwargs.items():
                if key in type_kwargs:
                    if not isinstance(value, type_kwargs[key]):
                        raise TypeError(
                            f"参数 {key} 应该是 {type_kwargs[key]},实际是 {type(value)}"
                        )
            return func(*args, **kwargs)
        return wrapper
    return decorator

@validate_types(int, str, name=str)
def process_data(id, title, name="default"):
    """处理数据"""
    return f"ID: {id}, Title: {title}, Name: {name}"

print(process_data(1, "测试", name="小明"))
try:
    process_data("错误", "测试")  # 会抛出 TypeError
except TypeError as e:
    print(f"类型错误: {e}")

三、装饰器堆叠

Python 允许为同一个函数应用多个装饰器,装饰器的执行顺序是从下往上(由内向外)。

def log_calls(func):
    """记录函数调用"""
    call_count = 0
    @wraps(func)
    def wrapper(*args, **kwargs):
        nonlocal call_count
        call_count  = 1
        print(f"调用 #{call_count}: {func.__name__}({args})")
        return func(*args, **kwargs)
    return wrapper

def cache_result(func):
    """简单的缓存装饰器"""
    cache = {}
    @wraps(func)
    def wrapper(*args, **kwargs):
        key = str(args)   str(sorted(kwargs.items()))
        if key in cache:
            print("从缓存读取")
            return cache[key]
        result = func(*args, **kwargs)
        cache[key] = result
        return result
    return wrapper

@log_calls
@cache_result
@timer
def expensive_calculation(n):
    """模拟耗时计算"""
    time.sleep(0.1)
    return n ** 2

print(f"结果: {expensive_calculation(5)}")
print(f"结果: {expensive_calculation(5)}")  # 从缓存读取
print(f"结果: {expensive_calculation(10)}")

装饰器堆叠的强大之处在于可以组合多个独立的关注点,每个装饰器专注于一个特定功能。

四、类装饰器

除了函数装饰器,Python 还支持类装饰器。类装饰器通常用于修改类的行为或添加新功能。

def singleton(cls):
    """单例模式装饰器"""
    instances = {}
    @wraps(cls)
    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("创建数据库连接实例")
        self.connected = False
    
    def connect(self):
        if not self.connected:
            print("连接数据库...")
            self.connected = True
        return self.connected

# 使用单例
db1 = Database()
db1.connect()

db2 = Database()
print(f"是否为同一实例: {db1 is db2}")  # 输出: True

另一个实用的类装饰器场景是自动添加方法:

def add_repr(cls):
    """自动添加 __repr__ 方法"""
    original_init = cls.__init__
    
    def __repr__(self):
        attrs = []
        for key, value in self.__dict__.items():
            attrs.append(f"{key}={repr(value)}")
        return f"{cls.__name__}({", ".join(attrs)})"
    
    cls.__repr__ = __repr__
    return cls

@add_repr
class User:
    def __init__(self, name, age, email):
        self.name = name
        self.age = age
        self.email = email

user = User("张三", 25, "zhangsan@example.com")
print(user)  # 输出: User(name="张三", age=25, email="zhangsan@example.com")

五、实战应用场景

1. 权限验证:在 Web 框架中,装饰器广泛用于路由保护和权限检查。

def require_permission(permission):
    """权限验证装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            current_user = kwargs.get("user")
            if not current_user:
                raise PermissionError("需要登录")
            if permission not in current_user.get("permissions", []):
                raise PermissionError(f"需要 {permission} 权限")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@require_permission("admin")
def delete_resource(resource_id, user=None):
    return f"资源 {resource_id} 已删除"

# 模拟管理员用户
admin_user = {"name": "admin", "permissions": ["admin", "edit"]}
print(delete_resource(123, user=admin_user))

2. API 请求重试:在网络请求中,自动重试失败的请求。

def retry(max_attempts=3, delay=1):
    """自动重试装饰器"""
    def decorator(func):
        @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} 次尝试失败: {e}")
                    if attempt < max_attempts - 1:
                        time.sleep(delay)
            raise last_exception
        return wrapper
    return decorator

@retry(max_attempts=3, delay=2)
def unstable_api_call():
    """模拟不稳定的 API 调用"""
    import random
    if random.random() < 0.5:  # 50% 失败率
        raise ConnectionError("网络连接失败")
    return "API 响应成功"

print(unstable_api_call())

3. 性能监控:在生产环境中记录函数调用次数和性能指标。

class PerformanceMonitor:
    """性能监控类装饰器"""
    def __init__(self, func):
        self.func = func
        self.call_count = 0
        self.total_time = 0
        wraps(func)(self)
    
    def __call__(self, *args, **kwargs):
        self.call_count  = 1
        start = time.perf_counter()
        result = self.func(*args, **kwargs)
        end = time.perf_counter()
        self.total_time  = (end - start)
        return result
    
    def stats(self):
        avg_time = self.total_time / self.call_count if self.call_count else 0
        return {
            "name": self.func.__name__,
            "calls": self.call_count,
            "total_time": self.total_time,
            "avg_time": avg_time
        }

@PerformanceMonitor
def compute_factorial(n):
    """计算阶乘"""
    if n <= 1:
        return 1
    return n * compute_factorial(n - 1)

compute_factorial(10)
compute_factorial(5)
print(compute_factorial.stats())

六、最佳实践

1. 始终使用 functools.wraps:保留被装饰函数的元数据。

2. 保持装饰器简单:每个装饰器只做一件事,遵循单一职责原则。

3. 考虑性能影响:装饰器会增加函数调用开销,在性能敏感的代码中要谨慎使用。

4. 提供清晰的文档:为装饰器编写详细的文档字符串,说明其用途和参数。

5. 使用类装饰器管理状态:当需要在装饰器中维护状态时,类装饰器是更好的选择。

总结

装饰器是 Python 中体现"优雅胜于丑陋"哲学的完美示例。通过合理使用装饰器,我们可以将横切关注点(日志、缓存、验证、重试等)从业务逻辑中分离出来,让代码更加清晰、可维护。掌握装饰器技巧,将让你的 Python 代码上升到一个新的层次。

在实际项目中,建议从简单场景开始应用装饰器,逐步积累经验。当你发现某个功能需要在多个函数中重复时,就是考虑使用装饰器的好时机。记住,好的装饰器应该像语法糖一样自然,而不是增加代码复杂度的负担。

相关文章

[Python 教程] NumPy 数组操作详解

NumPy 数组操作详解 NumPy 是 Python 科学计算的基础库,提供高性能的多维数组对象。本文详细介绍 NumPy 数组的核心操作。 一、创建数组 import numpy as np...

Python 装饰器实用技巧:从入门到精通

装饰器是 Python 最强大的特性之一,但也是很多开发者感到困惑的概念。简单来说,装饰器是一个函数,它接受另一个函数作为输入,并返回一个新的函数。使用装饰器,你可以在不修改原函数代码的情况下,为其添...

Python 装饰器实战:从基础到高级应用的完整指南

装饰器是 Python 中最优雅也最强大的特性之一。它允许你在不修改原函数代码的前提下,动态地添加功能。本文将带你从装饰器的基础概念出发,逐步掌握其在实际开发中的高级应用技巧。许多初学者对装饰器感到困...

Python装饰器完全指南:从基础到高级应用

装饰器是 Python 中最强大也最容易被误解的特性之一。很多初学者听说过装饰器,但总是感觉云里雾里,不敢在实际项目中使用。本文从最基础的概念讲起,逐步深入到高级应用场景,通过大量原创示例代码帮助...

Python 装饰器进阶:从理解到实战

装饰器是 Python 中一个非常强大的特性,它允许你在不修改原函数代码的情况下,为函数添加额外的功能。很多开发者虽然用过装饰器,但对其底层原理和高级用法理解不深。本文将从基础出发,深入讲解装饰器的工...

深入理解 Python 上下文管理器:从基础到高级应用

Python 的 with 语句和上下文管理器是每个开发者都应该掌握的高级技巧,但很多初学者对它的理解仅仅停留在文件操作层面。本文将深入讲解上下文管理器的原理、多种实现方式,以及在实际开发中的高级应用...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。