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

Python 装饰器从入门到实战:5 个实用场景详解

admin13小时前Python10

装饰器(Decorator)是 Python 中一种优雅的设计模式,它允许我们在不修改原函数代码的前提下,动态地给函数添加功能。想象一下,你有一个已经写好的函数,现在想给它添加日志记录、性能监控或权限检查等功能,装饰器就是为此而生的工具。

理解装饰器的关键在于明白它本质上是一个"函数的包装器"。它接收一个函数作为输入,返回一个新的函数,这个新函数在调用原函数前后可以执行额外的逻辑。这种设计遵循了开放封闭原则——对扩展开放,对修改封闭。

让我们从一个最简单的装饰器开始。假设我们想给多个函数添加执行时间统计功能,传统做法是在每个函数内部添加计时代码,但这会导致代码重复。使用装饰器,我们可以将计时逻辑封装一次,然后应用到任意函数上:

python
import time
from functools import wraps

def timer_decorator(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:.4f}秒")
        return result
    return wrapper

@timer_decorator
def calculate_sum(n):
    return sum(i * i for i in range(n))

@timer_decorator
def slow_operation():
    time.sleep(1)
    return "完成"

calculate_sum(1000000)
slow_operation()

这个示例展示了装饰器的基本结构:外层函数接收被装饰的函数,内层 wrapper 函数负责执行额外逻辑。使用@wraps 装饰器可以保留原函数的元信息,如函数名、文档字符串等,这在调试时非常重要。

实际开发中,我们经常需要带参数的装饰器。比如实现一个可配置的重试机制,当函数执行失败时自动重试指定次数:

python
import random
from functools import wraps

def retry(max_attempts=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts >= max_attempts:
                        raise
                    print(f"第{attempts}次重试,等待{delay}秒...")
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=0.5)
def unstable_api():
    if random.random() < 0.7:
        raise ConnectionError("网络波动")
    return "API 调用成功"

print(unstable_api())

带参数的装饰器需要三层嵌套:最外层接收装饰器参数,中间层接收被装饰函数,最内层是实际执行的 wrapper。这种模式在实现可配置的行为时非常有用。

缓存是另一个装饰器的经典应用场景。对于计算密集型或 IO 密集型函数,我们可以缓存之前的计算结果,避免重复执行:

python
from functools import wraps

def simple_cache(func):
    cache = {}
    
    @wraps(func)
    def wrapper(*args):
        if args in cache:
            print(f"从缓存命中:{args}")
            return cache[args]
        result = func(*args)
        cache[args] = result
        print(f"缓存新结果:{args}")
        return result
    return wrapper

@simple_cache
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))
print(fibonacci(10)) # 直接从缓存读取

这个简单的缓存装饰器展示了闭包的力量——cache 字典在 wrapper 函数外部定义,但在内部被引用,因此可以在多次函数调用之间保持状态。实际项目中,Python 标准库的 functools.lru_cache 提供了更完善的实现。

权限验证是 Web 开发中的常见需求。使用装饰器,我们可以将权限检查逻辑与业务逻辑分离,使代码更加清晰:

python
from functools import wraps

class User:
    def __init__(self, name, role):
        self.name = name
        self.role = role

current_user = None

def require_role(required_role):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if current_user is None:
                raise PermissionError("用户未登录")
            if current_user.role != required_role:
                raise PermissionError(f"需要{required_role}权限")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@require_role("admin")
def delete_user(user_id):
    return f"删除用户 {user_id}"

@require_role("user")
def view_profile():
    return f"查看 {current_user.name} 的个人资料"

current_user = User("张三", "admin")
print(delete_user(123))

最后一个实用场景是日志记录。在生产环境中,记录函数的输入输出对于排查问题至关重要:

python
import logging
from functools import wraps
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        logger.info(f"[{timestamp}] 调用 {func.__name__}")
        logger.info(f"参数:args={args}, kwargs={kwargs}")
        try:
            result = func(*args, **kwargs)
            logger.info(f"返回:{result}")
            return result
        except Exception as e:
            logger.error(f"异常:{e}")
            raise
    return wrapper

@log_calls
def divide(a, b):
    return a / b

divide(10, 2)

通过以上 5 个场景,我们可以看到装饰器的强大之处:它将横切关注点(如日志、缓存、权限)与核心业务逻辑分离,使代码更加模块化、可维护。掌握装饰器后,你可以编写出更加优雅和 Pythonic 的代码。

装饰器的学习曲线可能有些陡峭,但一旦理解其本质——函数的高阶应用和闭包的结合——就会发现它是 Python 中最优雅的特性之一。建议从简单的装饰器开始练习,逐步过渡到带参数和类装饰器,最终能够灵活运用这一强大工具。

相关文章

[Python 教程] OpenCV-Python 入门:图像处理基础详解

OpenCV-Python 入门:图像处理基础详解OpenCV 是一个跨平台计算机视觉库,轻量级且高效,支持 Python 接口。本文将系统介绍 OpenCV 的核心概念和基础操作。一、OpenCV...

[Python 教程] OpenCV 实战:图像与视频文件处理

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

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

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

[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...

发表评论

访客

看不清,换一张

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