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

Python中inspect为何将装饰后的类方法识别为函数?

问题分析

你遇到的核心问题是:在装饰器的wrapper里检查的是类定义阶段的原始函数(比如print_value),它本身就是function类型;而Python中只有当函数被绑定到实例后,才会转换成method对象。另外,虽然@wraps(func)会复制原始函数的元数据,但你的检查逻辑方向错了——不该判断原始函数的类型,而是要判断当前调用是否属于方法调用。

解决方案

以下是几种可行的修正方式,按需选择:

方式一:通过调用参数判断是否为方法调用

直接检查调用时的第一个参数是否为类实例,且该实例的类中包含当前函数作为成员:

from functools import wraps
import inspect
from typing import Any, Callable, Optional


def my_decorator(func: Optional[Callable] = None, **decorator_kwargs):
    def decorate(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            is_method_call = False
            if args:
                first_arg = args[0]
                # 验证第一个参数是类实例,且当前函数是该类的成员方法
                if hasattr(first_arg, '__class__'):
                    class_obj = first_arg.__class__
                    if func.__name__ in dir(class_obj):
                        # 处理装饰器嵌套的情况,unwrap获取原始函数
                        class_func = inspect.unwrap(getattr(class_obj, func.__name__))
                        if class_func is func:
                            is_method_call = True
            
            if is_method_call:
                print(f"{func.__name__} is a method")
            elif inspect.isfunction(func):
                print(f"{func.__name__} is a function")
            else:
                raise Exception(f"{func.__name__} is neither a function nor a method")
            
            return func(*args, **kwargs)
        return wrapper
    if func:
        return decorate(func)
    return decorate

class TestClass:
    def __init__(self, value: Any):
        self.value = value
    
    @my_decorator(foo="bar")
    def print_value(self, second_value: str):
        print(f"Printing Value {self.value}, {second_value}")
        return self.value

def main():
    test_class = TestClass(45)
    test_class.print_value("spam")
    
    # 测试普通函数
    @my_decorator
    def test_func():
        pass
    test_func()

if __name__ == "__main__":
    main()

方式二:手动实现描述符协议确保绑定方法转换

如果希望装饰后的函数能被Python自动转换成绑定方法,可以给wrapper添加__get__方法,实现描述符协议:

from functools import wraps
import inspect
import types
from typing import Any, Callable, Optional


def my_decorator(func: Optional[Callable] = None, **decorator_kwargs):
    def decorate(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 直接检查当前wrapper是否为绑定方法
            if inspect.ismethod(wrapper):
                print(f"{func.__name__} is a method")
            elif inspect.isfunction(wrapper):
                print(f"{func.__name__} is a function")
            else:
                raise Exception(f"{func.__name__} is neither a function nor a method")
            
            return func(*args, **kwargs)
        
        # 实现__get__方法,确保实例访问时返回绑定方法
        def __get__(self, instance, owner=None):
            if instance is None:
                return wrapper
            return types.MethodType(wrapper, instance)
        
        wrapper.__get__ = __get__
        return wrapper
    if func:
        return decorate(func)
    return decorate

# 后续TestClass和main函数同方式一

方式三:通过函数的__qualname__判断(简单场景)

如果你的代码中没有嵌套函数,可以直接通过__qualname__是否包含类名来判断:

@wraps(func)
def wrapper(*args, **kwargs):
    if '.' in func.__qualname__:
        print(f"{func.__name__} is a method")
    elif inspect.isfunction(func):
        print(f"{func.__name__} is a function")
    else:
        raise Exception(f"{func.__name__} is neither a function nor a method")
    return func(*args, **kwargs)
运行结果

以上代码运行后会输出:

print_value is a method
Printing Value 45, spam
test_func is a function

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:29:54