首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一文搞定 Python 装饰器

一文搞定 Python 装饰器

作者头像
shengjk1
发布2025-05-16 15:27:34
发布2025-05-16 15:27:34
28000
代码可运行
举报
文章被收录于专栏:码字搬砖码字搬砖
运行总次数:0
代码可运行

一、背景

最近趁着有时间,搞了一下 MCP,MCP server 中定义的 Function ,想要被 Client 使用,必须添加 @mcp.tool()。这其实是 python 的装饰器

二、python 中常见的装饰器的使用方式

Python装饰器是一种动态增强函数或类功能的高阶编程工具,其核心思想是通过包装函数或类来实现代码复用。以下是装饰器的主要类型及实现方式,结合代码示例详细说明:


一、基础装饰器

实现原理:通过嵌套函数包裹原函数,在调用前后添加额外逻辑。 代码示例

代码语言:javascript
代码运行次数:0
运行
复制
def simple_decorator(func):
    def wrapper(*args, **kwargs):
        print("函数执行前")
        result = func(*args, **kwargs)
        print("函数执行后")
        return result
    return wrapper

@simple_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

输出

代码语言:javascript
代码运行次数:0
运行
复制
函数执行前
Hello, Alice!
函数执行后

二、带参数的装饰器

实现原理:通过三层嵌套函数,外层接收装饰器参数,中间层接收原函数,内层实现逻辑。 代码示例

代码语言:javascript
代码运行次数:0
运行
复制
def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

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

greet("Bob")

输出

代码语言:javascript
代码运行次数:0
运行
复制
Hi, Bob!
Hi, Bob!
Hi, Bob!

三、类装饰器

实现原理:通过类的 __call__ 方法让实例可调用,并维护装饰器状态。 代码示例

代码语言:javascript
代码运行次数:0
运行
复制
class CallCounter:
    def __init__(self, func):
        self.func = func
        self.calls = 0

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

@CallCounter
def multiply(a, b):
    return a * b

print(multiply(2, 3))  # 调用次数:1 → 6
print(multiply(4, 5))  # 调用次数:2 → 20

四、装饰器堆叠

执行顺序:装饰器按从下到上的顺序应用,执行时外层先执行。 代码示例

代码语言:javascript
代码运行次数:0
运行
复制
def decorator1(func):
    def wrapper():
        print("装饰器1前置")
        func()
        print("装饰器1后置")
    return wrapper

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

@decorator1
@decorator2
def target():
    print("目标函数")

target()

输出

代码语言:javascript
代码运行次数:0
运行
复制
装饰器1前置
装饰器2前置
目标函数
装饰器2后置
装饰器1后置

五、保留元信息的装饰器

问题:装饰器会覆盖原函数的 __name__ 等元信息。 解决方案:使用 functools.wraps 装饰器。 代码示例

代码语言:javascript
代码运行次数:0
运行
复制
from functools import wraps

def log_decorator(func):
    @wraps(func)  # 保留原函数元信息
    def wrapper(*args, **kwargs):
        print(f"调用函数:{func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def calculate(a, b):
    """加法运算"""
    return a + b

print(calculate.__name__)  # 输出:calculate
print(calculate.__doc__)   # 输出:加法运算

六、内置装饰器

Python内置的三个常用装饰器:

  1. @staticmethod:定义静态方法,无需实例化对象即可调用。
  2. @classmethod:定义类方法,首个参数为类本身(cls)。
  3. @property:将方法转为属性访问。
代码语言:javascript
代码运行次数:0
运行
复制
class MyClass:
    @staticmethod
    def static_method():
        print("静态方法")

    @classmethod
    def class_method(cls):
        print(f"类方法,类名:{cls.__name__}")

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

七、实际应用场景
  1. 性能测试
代码语言:javascript
代码运行次数:0
运行
复制
import time

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

@timer
def complex_calculation():
    time.sleep(1.5)
  1. 权限校验
代码语言:javascript
代码运行次数:0
运行
复制
def admin_required(func):
    def wrapper(user, *args, **kwargs):
        if user.role != 'admin':
            raise PermissionError("需要管理员权限")
        return func(user, *args, **kwargs)
    return wrapper

@admin_required
def delete_user(user, user_id):
    print(f"删除用户 {user_id}")
  1. 缓存优化
代码语言:javascript
代码运行次数:0
运行
复制
def memoize(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

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

八、总结

装饰器的核心在于通过函数或类的组合实现代码复用,常见类型包括基础装饰器、参数化装饰器、类装饰器等。实际应用中需注意元信息保留、执行顺序等问题。通过灵活组合,可大幅提升代码的可维护性和扩展性。

三、优势和劣势

Python装饰器是Python语言中极具特色的功能,它通过高阶函数和闭包机制实现了对函数或类的动态增强。以下是装饰器在实际开发中的核心优势和劣势分析,结合多个技术文档进行深度解读:


一、装饰器的核心优势
1. 代码复用与模块化

装饰器通过将横切关注点(如日志记录、权限校验)从业务逻辑中抽离,显著提升代码复用率。例如:

代码语言:javascript
代码运行次数:0
运行
复制
from functools import wraps

def log_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[LOG] 调用 {func.__name__},参数:{args}")
        result = func(*args, **kwargs)
        return result
    return wrapper

@log_decorator
def process_data(data):
    # 业务逻辑
    pass
  • 优势:所有需要日志记录的函数只需添加@log_decorator,避免重复编写日志代码。
  • 应用场景:日志记录、权限校验、缓存管理等。
2. 非侵入式代码增强

装饰器在不修改原函数代码的情况下动态添加功能,保持核心逻辑的纯净性。例如:

代码语言:javascript
代码运行次数:0
运行
复制
def retry(max_attempts=3):
    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
            return wrapper
        return decorator

@retry(max_attempts=5)
def api_call():
    # 网络请求逻辑
    pass
  • 优势:无需修改api_call内部逻辑即可实现重试机制。
3. 提升代码可读性

通过装饰器将辅助功能与业务逻辑分离,使代码结构更清晰:

代码语言:javascript
代码运行次数:0
运行
复制
@cache
@validate_input
def compute(x, y):
    # 核心计算逻辑
    pass
  • 优势:装饰器链明确展示功能层次(先校验输入,再缓存结果)。
4. 性能优化能力

内置装饰器(如@lru_cache)可直接提升性能:

代码语言:javascript
代码运行次数:0
运行
复制
from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
  • 优势:通过缓存避免重复计算,时间复杂度从O(2^n)降至O(n)。
5. 灵活的参数化设计

装饰器支持参数传递,实现动态功能定制:

代码语言:javascript
代码运行次数:0
运行
复制
def throttle(delay):
    def decorator(func):
        last_call = 0
        @wraps(func)
        def wrapper(*args, **kwargs):
            nonlocal last_call
            current = time.time()
            if current - last_call < delay:
                raise Exception("调用频率过高")
            last_call = current
            return func(*args, **kwargs)
        return wrapper
    return decorator

@throttle(delay=1)
def high_frequency_api():
    pass
  • 优势:通过参数delay灵活控制函数调用频率。

二、装饰器的核心劣势
1. 调试复杂性

装饰器会修改函数的调用栈,导致调试时难以追踪原始函数:

代码语言:javascript
代码运行次数:0
运行
复制
@decorator_a
@decorator_b
def target():
    pass

# 实际调用顺序:decorator_a → decorator_b → target
  • 问题:调试器显示的调用链可能为wrapper_a → wrapper_b → target,掩盖原始函数名。
2. 性能开销

多层装饰器嵌套可能引入额外函数调用开销:

代码语言:javascript
代码运行次数:0
运行
复制
@log
@cache
@validate
def heavy_computation():
    pass
  • 问题:每个装饰器的包装函数都会增加调用层级,对高频调用函数影响显著。
3. 元信息丢失风险

未使用@wraps的装饰器会覆盖原函数的__name____doc__等元数据:

代码语言:javascript
代码运行次数:0
运行
复制
def bad_decorator(func):
    def wrapper():
        return func()
    return wrapper

@bad_decorator
def original():
    """原始文档"""
    pass

print(original.__name__)  # 输出:wrapper(而非original)
  • 解决方案:强制使用from functools import wraps保留元信息。
4. 动态装饰限制

装饰器无法直接应用于已实例化的对象:

代码语言:javascript
代码运行次数:0
运行
复制
obj = ExistingClass()
# 无法直接对obj.method进行装饰
  • 限制:需通过类继承或猴子补丁(monkey-patching)间接实现。
5. 过度设计风险

滥用装饰器可能导致代码结构复杂化:

代码语言:javascript
代码运行次数:0
运行
复制
@decorator1
@decorator2
@decorator3
@decorator4
def over_decorated():
    pass
  • 问题:装饰器链过长会降低代码可读性和维护性。

三、最佳实践建议

单一职责原则 每个装饰器只负责一个功能(如日志、缓存),避免功能混杂。

性能敏感场景慎用 高频调用函数尽量减少装饰器层级,必要时通过__slots__或C扩展优化。

强制保留元信息 所有装饰器内部函数必须使用@wraps(func)

文档与类型提示 为装饰器添加详细文档和参数类型注解:

代码语言:javascript
代码运行次数:0
运行
复制
def retry(max_attempts: int = 3) -> Callable:
    """重试装饰器"""
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            # 实现逻辑
            pass
        return wrapper
    return decorator

四、总结

Python装饰器是一把双刃剑: ✅ 优势:通过非侵入式增强、模块化设计和灵活的参数化,显著提升代码质量和开发效率。 ❌ 劣势:调试困难、性能开销和过度设计风险需谨慎对待。

合理运用装饰器需遵循"适度原则",在代码简洁性、可维护性和性能之间找到平衡点。

四、总结

文章主要围绕Python装饰器展开,详细讲解了其多种类型及实现方式,通过丰富的代码示例让读者清晰理解装饰器的使用。同时,深入探讨了装饰器在实际开发中的优势,如代码复用与模块化、非侵入式代码增强、提升代码可读性、性能优化能力、灵活的参数化设计等,以及劣势,包括调试复杂性、性能开销、元信息丢失风险、动态装饰限制、过度设计风险等,并给出了相应的最佳实践建议,以指导读者合理运用装饰器,平衡代码简洁性、可维护性和性能。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
  • 二、python 中常见的装饰器的使用方式
    • 一、基础装饰器
    • 二、带参数的装饰器
    • 三、类装饰器
    • 四、装饰器堆叠
    • 五、保留元信息的装饰器
    • 六、内置装饰器
    • 七、实际应用场景
    • 八、总结
  • 三、优势和劣势
    • 一、装饰器的核心优势
      • 1. 代码复用与模块化
      • 2. 非侵入式代码增强
      • 3. 提升代码可读性
      • 4. 性能优化能力
      • 5. 灵活的参数化设计
    • 二、装饰器的核心劣势
      • 1. 调试复杂性
      • 2. 性能开销
      • 3. 元信息丢失风险
      • 4. 动态装饰限制
      • 5. 过度设计风险
    • 三、最佳实践建议
    • 四、总结
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档