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

Python装饰器实战指南:从入门到精通

admin2个月前 (03-20)Python73

Python 装饰器是许多开发者既熟悉又陌生的功能。熟悉是因为我们在框架中经常看到 @符号,陌生是因为很多人只是知其然不知其所以然。本文将从零开始,通过实际案例深入讲解装饰器的工作原理和应用场景。

装饰器的本质是一个函数,它接受一个函数作为参数,并返回一个新的函数。这种"高阶函数"的特性使得我们可以在不修改原函数代码的情况下,为函数添加额外功能。装饰器最常见的应用场景包括日志记录、性能计时、权限验证、缓存等。

让我们从一个最简单的装饰器开始。下面这个 timer_decorator 可以测量函数的执行时间:

import time
import functools

def timer_decorator(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

@timer_decorator
def calculate_fibonacci(n):
    """计算斐波那契数列"""
    if n <= 1:
        return n
    return calculate_fibonacci(n-1) + calculate_fibonacci(n-2)

calculate_fibonacci(30)

运行上面的代码,你会看到每个函数调用的执行时间被自动打印出来。这里有几个关键点需要注意:首先,我们使用 functools.wraps 来保留原函数的元信息(如函数名、文档字符串等);其次,wrapper 函数使用 *args 和 **kwargs 来接收任意参数,保证装饰器可以用于各种函数。

装饰器不仅接受函数参数,还可以接受自定义参数。这在需要配置装饰器行为时非常有用。例如,我们可以创建一个重试装饰器,指定重试次数和延迟时间:

import time
import functools

def retry(max_attempts=3, delay=1, exceptions=(Exception,)):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if attempt < max_attempts - 1:
                        print(f"第 {attempt + 1} 次尝试失败,{delay} 秒后重试...")
                        time.sleep(delay)
            raise Exception(f"重试 {max_attempts} 次后仍然失败") from last_exception
        return wrapper
    return decorator

@retry(max_attempts=3, delay=2)
def unstable_api_call():
    """模拟不稳定的 API 调用"""
    import random
    if random.random() < 0.7:
        raise ConnectionError("网络连接失败")
    return {"status": "success", "data": "获取到数据"}

带参数的装饰器需要三层嵌套:最外层接收装饰器参数,中间层接收被装饰函数,最内层是实际的包装函数。这种模式虽然看起来复杂,但理解后就能灵活应用。

类装饰器是另一个强大的工具。与函数装饰器不同,类装饰器使用 __call__ 方法来实现装饰功能,并且可以保存状态。下面是一个计数装饰器的实现,可以记录函数被调用的次数:

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
        functools.update_wrapper(self, func)
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} 已被调用 {self.count} 次")
        return self.func(*args, **kwargs)

@CountCalls
def process_data(data):
    """处理数据"""
    return [x * 2 for x in data]

process_data([1, 2, 3])
process_data([4, 5, 6])
process_data([7, 8, 9])

类装饰器的优势在于可以维护状态。上面的例子中,每次调用函数时计数器都会增加。这种模式在需要跟踪函数调用历史的场景中非常有用。

装饰器在实际项目中的应用非常广泛。缓存装饰器可以显著提升性能,特别是在计算密集型或 I/O 密集型操作中。下面是一个简单的缓存装饰器实现:

def cache_decorator(max_size=128):
    def decorator(func):
        cache = {}
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = str(args) + str(sorted(kwargs.items()))
            if key in cache:
                print(f"从缓存中获取: {func.__name__}({args}, {kwargs})")
                return cache[key]
            
            if len(cache) >= max_size:
                cache.clear()
                print("缓存已满,清空缓存")
            
            result = func(*args, **kwargs)
            cache[key] = result
            return result
        
        wrapper.cache = cache
        return wrapper
    return decorator

@cache_decorator(max_size=10)
def expensive_computation(x):
    """模拟耗时计算"""
    time.sleep(0.1)
    return x * x

print(expensive_computation(5))
print(expensive_computation(5))
print(expensive_computation(10))

装饰器可以叠加使用,执行顺序是从下到上。这个特性让我们可以组合多个装饰器来实现复杂功能。例如,我们可以同时使用日志、缓存和计时装饰器:

def log_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"函数返回: {result}")
        return result
    return wrapper

@timer_decorator
@cache_decorator(max_size=5)
@log_decorator
def complex_operation(n):
    """复杂操作示例"""
    time.sleep(0.05)
    return sum(range(n))

complex_operation(100)
complex_operation(100)

掌握装饰器是成为高级 Python 开发者的必经之路。通过本文的学习,你应该理解了装饰器的工作原理、带参数的装饰器、类装饰器以及实际应用场景。在实际项目中,合理使用装饰器可以让代码更加简洁、可维护、可扩展。记住,装饰器的核心思想是"在不修改原函数的情况下添加功能",这一理念在软件设计中非常重要。

相关文章

[Python 教程] Python 多线程编程指南

Python 多线程编程指南 Python 的 threading 模块提供多线程支持。本文介绍多线程编程的基础和实用技巧。 一、创建线程 import threading import time...

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

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

Python 上下文管理器实战指南:优雅处理资源的艺术

# Python 上下文管理器实战指南:优雅处理资源的艺术 在 Python 编程中,资源的获取与释放是一个永恒的主题。文件操作、数据库连接、网络请求、锁的获取...这些场景都遵循相同的模式:打开资...

Python装饰器完全指南:从原理到实践

Python装饰器是Python中最优雅的特性之一,它允许我们在不修改函数源代码的情况下,动态地扩展函数的功能。本文将从基础概念出发,深入讲解装饰器的原理、用法和最佳实践,帮助你真正掌握这一强大的Py...

Python 列表推导式与生成器的高级用法

在日常 Python 开发中,我们经常需要对序列数据进行转换和过滤。传统的循环写法虽然直观,但往往代码冗长。Python 的列表推导式提供了一种优雅的替代方案,它不仅代码更简洁,而且执行效率通常更高。...

发表评论

访客

看不清,换一张

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