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

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

admin2个月前 (03-19)Python88

装饰器(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 实战:图像与视频文件处理

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

Python 装饰器的 5 个实用技巧,让你的代码更优雅

在 Python 编程中,装饰器(Decorator)是一个强大而优雅的工具。很多初学者对装饰器的理解停留在@staticmethod 或@classmethod 这类内置装饰器上,但实际上,自定义装...

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

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

Python 装饰器进阶:从入门到实战,写出更灵活的函数增强技巧

# Python 装饰器进阶:从入门到实战,写出更灵活的函数增强技巧 ## 简介 很多 Python 开发者都听过装饰器,也知道怎么写简单的装饰器。但大多数人对装饰器的进阶用法,比如带参数的装饰器、...

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

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

Python 装饰器高级实战:从基础到精通的5个实用技巧

引言:为什么要深入掌握装饰器? 装饰器是 Python 中最优雅的元编程工具之一,它能在不修改原函数代码的情况下,动态地增加功能。很多开发者都知道如何使用 @timer 计时或 @cache 缓存,...

发表评论

访客

看不清,换一张

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