You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python 3.7+中如何可靠判断方法是否被装饰器包裹?

可靠判断方法是否被装饰的实现方案

问题背景

在Python 3.7+环境下,现有装饰器与类定义如下(修正原代码中@dec()的语法错误,装饰器无需加括号):

import functools

def dec(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        print("wrapped")
        return method(*args, **kwargs)
    return wrapper

class A:
    @dec
    def f1(self):
        print(is_wrapped())

    def f2(self):
        print(is_wrapped())

需求是:

  • 调用A().f1()时输出True
  • 调用A().f2()时输出False

但原有is_wrapped实现存在漏洞:若f2的调用者局部变量中包含未被装饰的f2引用,会导致判断结果错误。

已知限制条件:

  • is_wrapped直接被目标方法调用,inspect.currentframe().f_back指向该方法的栈帧
  • is_wrapped无法接收任何参数
  • 装饰器必须使用functools.wraps处理被包裹函数

可行解决方案

核心思路是给被装饰后的方法添加唯一标识,is_wrapped通过检查该标识来准确判断方法是否被装饰,不受调用者局部变量干扰。

方案1:自定义装饰标记(推荐)

1. 修改装饰器,添加自定义属性

在装饰器中给生成的wrapper方法添加一个自定义标记属性:

import functools

def dec(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        print("wrapped")
        return method(*args, **kwargs)
    # 添加自定义标记,标识该方法是被dec装饰后的实例
    wrapper._is_decorated_by_dec = True
    return wrapper

2. 实现is_wrapped函数

通过栈帧获取当前调用的方法对象,检查是否存在自定义标记:

import inspect

def is_wrapped():
    caller_frame = inspect.currentframe().f_back
    try:
        # 从栈帧中获取调用方法的实例和方法名
        instance = caller_frame.f_self
        method_name = caller_frame.f_code.co_name
        # 获取实例上绑定的实际方法对象
        current_method = getattr(instance, method_name)
        # 检查是否存在装饰器添加的标记
        return hasattr(current_method, '_is_decorated_by_dec')
    finally:
        # 手动删除栈帧引用,避免内存泄漏
        del caller_frame

方案2:利用functools.wraps的__wrapped__属性

无需修改装饰器,直接检查方法是否存在__wrapped__属性(functools.wraps会自动给wrapper添加该属性,指向原始方法):

import inspect

def is_wrapped():
    caller_frame = inspect.currentframe().f_back
    try:
        instance = caller_frame.f_self
        method_name = caller_frame.f_code.co_name
        current_method = getattr(instance, method_name)
        # 检查方法是否被functools.wraps包裹(即是否是装饰后的方法)
        return hasattr(current_method, '__wrapped__')
    finally:
        del caller_frame

测试验证

执行以下代码:

a = A()
a.f1()  # 先输出"wrapped",再输出True
a.f2()  # 输出False

两种方案均能满足需求,且不受调用者局部变量的干扰。方案1的自定义标记更明确,可避免与其他装饰器的__wrapped__属性产生混淆;方案2无需修改原有装饰器,更简洁。

内容的提问来源于stack exchange,提问作者Tomer Wolberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:26:16