Python 装饰器深度解析:从基础到高级应用

0 阅读3分钟

Python 装饰器深度解析:从基础到高级应用

引言

装饰器是 Python 中最强大且优雅的特性之一。它允许我们在不修改函数源代码的情况下,扩展或修改函数的行为。本文将从基础到高级,系统地介绍 Python 装饰器的原理和应用。

一、装饰器基础

1.1 什么是装饰器

装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。

def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

@my_decorator
def say_hello():
    print("你好!")

say_hello()
# 输出:
# 函数执行前
# 你好!
# 函数执行后

1.2 带参数的装饰器

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"你好,{name}!")

greet("张三")
# 输出 3 次:你好,张三!

二、常用装饰器模式

2.1 计时装饰器

import time
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 耗时: {end - start:.4f}秒")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "完成"

result = slow_function()
# 输出:slow_function 耗时: 1.0012秒

2.2 缓存装饰器

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# 使用缓存,性能大幅提升
print(fibonacci(100))  # 几乎瞬间完成

2.3 权限检查装饰器

def require_auth(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        user = kwargs.get('user')
        if not user or not user.is_authenticated:
            raise PermissionError("需要登录")
        return func(*args, **kwargs)
    return wrapper

@require_auth
def delete_user(user_id, user=None):
    print(f"删除用户 {user_id}")

三、类装饰器

3.1 装饰类方法

class MathOperations:
    @staticmethod
    def add(x, y):
        return x + y
    
    @classmethod
    def from_string(cls, string):
        return cls()
    
    @property
    def value(self) -> int:
        return self._value
    
    @value.setter
    def value(self, val: int):
        if val < 0:
            raise ValueError("值不能为负")
        self._value = val

3.2 类装饰器

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

@CountCalls
def process_data(data):
    return data * 2

process_data(5)  # 调用次数: 1
process_data(10) # 调用次数: 2

四、高级应用

4.1 装饰器链

@timer
@lru_cache(maxsize=128)
def expensive_function(n):
    time.sleep(0.1)
    return n ** 2

# 第一次调用较慢,之后使用缓存
expensive_function(10)
expensive_function(10)  # 使用缓存,几乎瞬时完成

4.2 参数化装饰器工厂

def retry(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(max_attempts=3, delay=2)
def unstable_api_call():
    # 模拟不稳定的 API 调用
    import random
    if random.random() < 0.7:
        raise ConnectionError("API 调用失败")
    return "成功"

4.3 上下文管理装饰器

from contextlib import contextmanager

@contextmanager
def database_transaction():
    print("开始事务")
    try:
        yield
        print("提交事务")
    except Exception:
        print("回滚事务")
        raise

with database_transaction():
    print("执行数据库操作")

五、实战案例

5.1 Web 框架中的路由装饰器

class Router:
    def __init__(self):
        self.routes = {}
    
    def route(self, path):
        def decorator(func):
            self.routes[path] = func
            return func
        return decorator

app = Router()

@app.route('/users')
def get_users():
    return {"users": ["Alice", "Bob"]}

@app.route('/posts')
def get_posts():
    return {"posts": []}

5.2 日志装饰器

import logging
from functools import wraps

def log_calls(level=logging.INFO):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            logging.log(level, f"调用 {func.__name__}")
            try:
                result = func(*args, **kwargs)
                logging.log(level, f"{func.__name__} 成功")
                return result
            except Exception as e:
                logging.error(f"{func.__name__} 失败: {e}")
                raise
        return wrapper
    return decorator

@log_calls(logging.DEBUG)
def process_order(order_id):
    print(f"处理订单 {order_id}")

总结

Python 装饰器是一个强大的工具,掌握它可以:

  1. 提高代码复用性:将通用逻辑抽离为装饰器
  2. 增强可读性:使用 @ 语法清晰表达意图
  3. 分离关注点:将核心逻辑与辅助功能分离
  4. 灵活扩展:不修改原函数即可添加功能

通过合理使用装饰器,可以写出更加优雅、可维护的 Python 代码。