为何未被@abstractmethod装饰的方法的__isabstractmethod__属性为True?
这是个挺有意思的问题,我先帮你理清正常情况下的行为,再分析你看到异常结果的可能原因。
首先,我们先明确几个关键知识点:
@abstractmethod装饰器的核心作用是给被装饰的方法对象添加__isabstractmethod__ = True属性,以此标记它是抽象方法。staticmethod和classmethod都是描述符类,它们的__isabstractmethod__属性是通过代理到包装的原始函数来实现的:如果包装的函数有__isabstractmethod__属性就返回它的值,没有就返回False。- 普通实例方法(比如你的
im)如果没被@abstractmethod装饰,本身是没有__isabstractmethod__属性的,直接访问会抛出AttributeError。
正常运行结果
用你给出的代码在 Python 3.3+ 的版本中运行,正确的输出应该是:
from abc import abstractmethod class K: @staticmethod def sm(*args): pass @classmethod def cm(*args): pass @abstractmethod def am(*args): pass def im(*args): pass K_dct = object.__getattribute__(K, '__dict__') sm = K_dct['sm'] cm = K_dct['cm'] am = K_dct['am'] im = K_dct['im'] print(sm.__isabstractmethod__) # False print(cm.__isabstractmethod__) # False print(am.__isabstractmethod__) # True # print(im.__isabstractmethod__) # 会抛出 AttributeError,因为普通函数没有这个属性
你看到非抽象方法 __isabstractmethod__ = True 的可能原因
Python 版本的历史 bug
在 Python 3.0 到 3.2 的早期版本中,staticmethod和classmethod的__isabstractmethod__属性实现存在问题,可能会错误地返回True。如果你使用的是这些旧版本,升级到 Python 3.3+ 就能解决这个问题。混淆了类和方法的
__isabstractmethod__属性
你的类K因为包含抽象方法am,所以类本身的__isabstractmethod__属性会是True(这是由ABCMeta元类自动设置的,用来标识这是一个抽象类)。如果你误把类的这个属性当成了某个方法的属性,就会产生误解。间接触发了描述符的
__get__方法
虽然你说“不要调用描述符的__get__方法”,但如果不小心通过类或实例访问了方法(比如K.sm而不是直接从__dict__取),会得到一个绑定后的函数对象,此时如果错误地认为这个函数的某个属性是__isabstractmethod__,可能会因为其他逻辑产生混淆(不过实际上绑定后的函数没有这个属性)。
总结一下:正常情况下,只有被 @abstractmethod 装饰的 am 方法的 __isabstractmethod__ 是 True,其他方法要么返回 False,要么没有这个属性。如果你的结果不符合这个预期,优先检查 Python 版本是否过旧。
内容的提问来源于stack exchange,提问作者Toothpick Anemone

