Python装饰器实战:从入门到封装通用工具

0 阅读8分钟

免费编程软件「python+pycharm」 链接:pan.quark.cn/s/48a86be2f…

一、装饰器初体验:给函数穿"外套"

想象你有一件普通白T恤(原始函数),现在想给它加上图案(额外功能)却不改变衣服本身。Python装饰器就像这件"魔法外套",能动态为函数添加功能而不修改原代码。

1.1 最简单的装饰器

def simple_decorator(func):
    def wrapper():
        print("外套前襟:添加日志")
        func()
        print("外套后背:记录耗时")
    return wrapper

@simple_decorator
def say_hello():
    print("Hello World!")

say_hello()

转存失败,建议直接上传图片文件

输出结果:

外套前襟:添加日志
Hello World!
外套后背:记录耗时

转存失败,建议直接上传图片文件

这个例子展示了装饰器的核心机制:@simple_decorator相当于say_hello = simple_decorator(say_hello),将原函数包裹在wrapper中。

1.2 处理带参数的函数

当被装饰函数需要参数时,用*args**kwargs实现万能适配:

def param_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"准备调用{func.__name__},参数:{args}, {kwargs}")
        return func(*args, **kwargs)
    return wrapper

@param_decorator
def add(a, b, c=0):
    return a + b + c

print(add(1, 2, c=3))  # 输出:准备调用add,参数:(1, 2), {'c': 3} \n 6

转存失败,建议直接上传图片文件

二、装饰器进阶:解决三大痛点

2.1 保留函数元信息

直接使用装饰器会丢失原函数的__name____doc__等信息,用functools.wraps修复:

from functools import wraps

def safe_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """这是包装函数的文档"""
        print("安全检查通过")
        return func(*args, **kwargs)
    return wrapper

@safe_decorator
def calculate(x):
    """计算平方"""
    return x ** 2

print(calculate.__name__)  # 输出:calculate(而非wrapper)
print(calculate.__doc__)   # 输出:计算平方(而非包装函数的文档)

转存失败,建议直接上传图片文件

2.2 带参数的装饰器

当需要为不同函数配置不同参数时(如日志级别、缓存时间),使用三层嵌套结构:

def configurable_decorator(level="INFO"):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f"[{level}] 调用函数: {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@configurable_decorator(level="DEBUG")
def fetch_data():
    return "数据获取成功"

fetch_data()  # 输出:[DEBUG] 调用函数: fetch_data

转存失败,建议直接上传图片文件

2.3 多个装饰器叠加

装饰器执行顺序遵循"从下往上装饰,从上往下执行"原则:

def decor1(func):
    def wrapper():
        print("装饰器1前")
        func()
        print("装饰器1后")
    return wrapper

def decor2(func):
    def wrapper():
        print("装饰器2前")
        func()
        print("装饰器2后")
    return wrapper

@decor1
@decor2
def target():
    print("目标函数执行")

target()
"""
输出:
装饰器1前
装饰器2前
目标函数执行
装饰器2后
装饰器1后
"""

转存失败,建议直接上传图片文件

三、实战场景:打造企业级工具库

3.1 性能监控装饰器

自动统计函数执行时间并生成可视化报告:

import time
from functools import wraps

def performance_monitor(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        duration = time.perf_counter() - start
        
        # 实际项目中可集成Prometheus等监控系统
        print(f"⏱️ {func.__name__} 执行耗时: {duration:.4f}秒")
        return result
    return wrapper

@performance_monitor
def complex_calculation(n):
    return sum(i*i for i in range(n))

complex_calculation(1000000)  # 输出:⏱️ complex_calculation 执行耗时: 0.1234秒

转存失败,建议直接上传图片文件

3.2 缓存装饰器(Memoization)

对重复计算结果进行缓存,特别适合递归函数:

def cache_decorator(func):
    cache = {}
    @wraps(func)
    def wrapper(n):
        if n not in cache:
            cache[n] = func(n)
            print(f"💾 缓存命中缺失,计算并存储结果 for {n}")
        else:
            print(f"🎯 缓存命中 for {n}")
        return cache[n]
    return wrapper

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

print(fibonacci(10))  # 第二次调用fibonacci(5)等会直接从缓存读取

转存失败,建议直接上传图片文件

3.3 权限验证装饰器

在Web开发中验证用户权限的经典实现:

def role_required(required_role):
    def decorator(func):
        @wraps(func)
        def wrapper(user, *args, **kwargs):
            if user.get("role") != required_role:
                raise PermissionError(f"需要{required_role}权限")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

# 测试用例
admin = {"role": "admin", "name": "Alice"}
guest = {"role": "guest", "name": "Bob"}

@role_required("admin")
def delete_user(user, user_id):
    print(f"{user['name']} 删除了用户 {user_id}")

try:
    delete_user(guest, 123)  # 抛出PermissionError
except PermissionError as e:
    print(e)  # 输出:需要admin权限

转存失败,建议直接上传图片文件

四、高级技巧:装饰器工厂与类装饰器

4.1 动态配置的装饰器工厂

根据运行时参数生成不同行为的装饰器:

def retry_decorator(max_attempts=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    print(f"⚠️ 尝试 {attempt+1} 失败,{delay}秒后重试...")
                    time.sleep(delay)
        return wrapper
    return decorator

@retry_decorator(max_attempts=5, delay=2)
def unstable_api_call():
    import random
    if random.random() < 0.7:
        raise ConnectionError("模拟网络故障")
    return "成功获取数据"

print(unstable_api_call())  # 可能需要多次重试

转存失败,建议直接上传图片文件

4.2 类装饰器实现

当需要维护状态时,类装饰器比函数更合适:

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

@CallCounter
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")  # 输出:📊 函数 greet 已被调用 1次 \n Hello, Alice!
greet("Bob")    # 输出:📊 函数 greet 已被调用 2次 \n Hello, Bob!

转存失败,建议直接上传图片文件

五、企业级封装:通用工具库设计

将常用装饰器封装成可配置的Python包:

my_decorators/
├── __init__.py
├── core.py          # 基础装饰器实现
├── config.py        # 默认配置
└── utils.py         # 辅助工具

转存失败,建议直接上传图片文件

core.py示例

from functools import wraps
import time
from .config import DEFAULT_CACHE_SIZE

def timing(func):
    """基础性能监控装饰器"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"⏱️ {func.__name__} executed in {time.time()-start:.4f}s")
        return result
    return wrapper

def cached(max_size=DEFAULT_CACHE_SIZE):
    """可配置大小的缓存装饰器"""
    cache = {}
    def decorator(func):
        @wraps(func)
        def wrapper(*args):
            if args in cache:
                return cache[args]
            result = func(*args)
            if len(cache) >= max_size:
                cache.popitem()  # 简单LRU实现
            cache[args] = result
            return result
        return wrapper
    return decorator

转存失败,建议直接上传图片文件

使用示例

from my_decorators.core import timing, cached

@timing
@cached(max_size=100)
def expensive_computation(x):
    return sum(i*i for i in range(x))

print(expensive_computation(10000))  # 首次计算较慢
print(expensive_computation(10000))  # 第二次直接从缓存读取

转存失败,建议直接上传图片文件

六、最佳实践与避坑指南

  1. 性能考量:装饰器中的操作会影响所有被装饰函数,避免在wrapper中做耗时操作

  2. 异常处理:确保装饰器正确传播异常,避免静默失败

  3. 文档规范:为装饰器编写清晰的docstring,说明其行为和参数

  4. 测试覆盖:特别测试装饰器叠加、参数传递等边界情况

  5. 类型提示:使用Python 3.5+的类型注解提升可维护性:

    from typing import Callable, Any, TypeVar
    
    F = TypeVar('F', bound=Callable[..., Any])
    
    def type_safe_decorator(func: F) -> F:
        @wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            # 类型安全的实现
            return func(*args, **kwargs)
        return wrapper  # type: ignore[return-value]
    

    转存失败,建议直接上传图片文件

七、总结与展望

装饰器作为Python最强大的特性之一,其应用场景远不止于此。随着异步编程的普及,async装饰器、基于描述符的更复杂装饰器等高级用法正在涌现。掌握装饰器的核心思想——"在不修改原函数的情况下扩展功能",将使你的代码更加优雅、可维护且易于扩展。

建议从简单场景开始实践,逐步尝试封装自己的装饰器工具库。记住:好的装饰器应该像空气一样存在——使用时感受不到它的存在,但离开它代码将变得笨重不堪。