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
相关产品推荐
相关产品推荐

