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

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

admin7小时前Python2

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 教程] OpenCV 实战:图像与视频文件处理

OpenCV 实战:图像与视频文件处理本文详细介绍如何使用 OpenCV 处理图像和视频文件,包括读取、显示、保存等操作。一、图像文件操作1.1 读取图像import cv2 #&nb...

[Python 教程] OpenCV 绘图教程:图形与文本标注

OpenCV 绘图教程:图形与文本标注本文介绍如何在 OpenCV 中绘制各种图形和添加文本,用于图像标注和可视化。一、绘制基本图形1.1 创建画布import cv2 import&nb...

[Python 教程] Pandas 数据分析实战

Pandas 数据分析实战 Pandas 是 Python 数据分析的核心库,提供 DataFrame 和 Series 数据结构。本文介绍 Pandas 的实用技巧。 一、创建 DataFrame...

[Python 教程] Matplotlib 数据可视化教程

Matplotlib 数据可视化教程 Matplotlib 是 Python 最常用的绘图库。本文介绍常用图表的绘制方法。 一、基础设置 import matplotlib.pyplot as pl...

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

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

[Python 教程] Python 网络请求与爬虫基础

Python 网络请求与爬虫基础 requests 是 Python 最常用的 HTTP 库。本文介绍网络请求和爬虫的基础知识。 一、基础请求 import requests # GET 请求 r...

发表评论

访客

看不清,换一张

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