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

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

admin4周前 (03-19)Python45

装饰器(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 装饰器实战:从基础到高级应用的完整指南

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

Python 装饰器完全指南:从原理到实战的 5 个核心场景

装饰器(Decorator)是 Python 中最具魅力的特性之一。它允许我们在不修改原函数代码的前提下,动态地添加功能。但很多开发者对装饰器的理解仅停留在在函数上面加个@符号的层面。今天,我们将从底...

Python 中利用 functools.lru_cache 实现高效缓存:从入门到进阶

Python 中利用 functools.lru_cache 实现高效缓存:从入门到进阶 在日常 Python 开发中,我们经常会遇到重复计算相同输入的问题,比如递归计算斐波那契数列、多次调用相同参...

Python装饰器实战:从零到精通的5个经典场景

Python装饰器(Decorator)是一个非常强大且优雅的语言特性,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。本文将通过5个实战场景,带你深入理解装饰器的原理和应用。 一、装饰...

Python 异步编程实战指南:从入门到精通

Python 异步编程实战指南:从入门到精通 简介 在现代 Python 开发中,异步编程已经成为构建高性能应用程序的核心技能。特别是在处理 I/O 密集型任务(如网络请求...

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

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

发表评论

访客

看不清,换一张

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