如何有效检测描述符是否已解包及判断获取的classmethod类型?
关于Python描述符的两个问题解答
问题1:如何有效检测一个描述符是否已被解包?
首先得明确:描述符的“解包”指的是当我们通过类或实例访问描述符时,Python自动调用其__get__方法返回绑定后的结果,而非描述符对象本身;反之,直接拿到描述符对象本身的状态就是“未被解包”。
这里有几个实用的检测思路:
基于描述符协议与类型判断
先判断对象是否实现了描述符协议(即拥有__get__、__set__或__delete__中至少一个方法)。如果是描述符,再看它是否已经被绑定:import inspect import types def is_descriptor_unwrapped(obj): # 第一步:判断是否是描述符 is_descriptor = any(hasattr(obj, meth) for meth in ('__get__', '__set__', '__delete__')) if not is_descriptor: # 不是描述符,说明已经是解包后的结果(或本身就不是描述符) return True # 第二步:判断是否已被绑定(解包) # 内置描述符(classmethod/staticmethod)解包后会变成method/function类型 return isinstance(obj, (types.MethodType, types.FunctionType, inspect.abstractmethod))通过属性特征判断
未被解包的原始描述符通常保留着描述特有的属性(比如classmethod实例的__func__指向原始函数,但没有__self__绑定属性);而解包后的绑定方法会有__self__属性(指向绑定的类或实例),且不再是描述符类型。
问题2:如何判断获取到的cm是哪一个版本?
先梳理两种获取方式的本质差异:
- 从
K.__dict__中直接取出的cm是原始的classmethod描述符对象(未被解包,还没绑定到类); - 通过
getattr获取的cm是解包后的绑定类方法(已经通过__get__绑定到K类,类型为method)。
在无法直接访问K类的情况下,你可以用以下方法区分:
方法1:检查对象类型
最直接的方式是判断cm的类型:
import types if isinstance(cm, classmethod): print("这是从K.__dict__中取出的原始描述符") elif isinstance(cm, types.MethodType): print("这是通过getattr获取的解包后绑定方法")
方法2:检查属性特征
原始classmethod描述符有__get__方法,但没有__self__绑定属性;解包后的绑定方法则相反:
if hasattr(cm, '__get__') and not hasattr(cm, '__self__'): print("原始描述符") elif hasattr(cm, '__self__'): print("解包后的绑定方法")
方法3:尝试调用测试
原始classmethod实例调用时需要传入类作为第一个参数,而解包后的绑定方法可以直接传入业务参数:
try: # 尝试不带类参数调用 cm(1, 2, 3) print("解包后的绑定方法") except TypeError: # 报错说明需要传入类参数,是原始描述符 print("原始描述符")
内容的提问来源于stack exchange,提问作者Toothpick Anemone
相关产品推荐
相关产品推荐

