Python抽象方法显式抛NotImplementedError的差异与最佳实践探讨
关于Python抽象方法两种实现形式的疑问与解答
我在研究Python内置库numbers.py的源码时,发现被@abstractmethod装饰的方法存在两种实现形式:一种没有显式抛出NotImplementedError,另一种则显式抛出。
父类示例代码
from abc import ABC, abstractmethod class Complex(ABC): @abstractmethod def __complex__(self): """Return a builtin complex instance. Called for complex(self).""" @abstractmethod def __neg__(self): """-self""" raise NotImplementedError
子类实现代码
class MyComplex(Complex): def __complex__(self): return complex(1, 1) def __neg__(self): return complex(-1, -1)
测试结果
>>> c = MyComplex() >>> Complex.__complex__(c) >>> Complex.__neg__(c) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/maskalev/dev/foo/bar.py", line 15, in __neg__ raise NotImplementedError NotImplementedError
测试后发现:直接调用父类的Complex.__complex__(c)会返回None,而调用Complex.__neg__(c)则会抛出NotImplementedError。
问题1:这是唯一差异吗?
是的,这是最直观的差异,但核心逻辑上两种写法都是为了标记方法为抽象方法,强制子类必须实现。唯一的行为区别仅出现在绕开子类实现直接调用父类抽象方法时:
- 无显式抛出的抽象方法:执行后默认返回
None(Python函数无return语句时的默认行为) - 显式抛出的抽象方法:执行后直接抛出
NotImplementedError
当子类正常实现方法后,子类实例的方法调用行为完全一致,不会有任何区别。
问题2:为何两种写法并存,不统一实现?
这是Python标准库历史演进和代码风格遗留的结果:
- 早期抽象类实践:在
abc模块完善前,开发者习惯通过显式抛出NotImplementedError标记抽象方法,这种写法被保留到了部分老代码中。 - 特殊方法的约定差异:像
__complex__这类特殊方法,Python内置逻辑对未实现的情况有明确的 fallback 处理;而__neg__这类运算符重载方法没有默认 fallback,显式抛出异常能更清晰地提示“该方法必须被子类实现”。 - 兼容性要求:标准库需要保持向后兼容,不能随意修改已有代码的写法,因此两种形式会长期并存。
问题3:最佳实践是什么?
推荐显式抛出NotImplementedError,理由如下:
- 行为明确:直接调用父类抽象方法时,抛出异常能立刻提醒调用者“此方法不应直接调用,必须由子类实现”,比返回
None更直观。 - 调试友好:调试时,异常能快速定位到未正确实现的抽象方法,而返回
None可能导致后续代码出现难以排查的逻辑错误。 - 风格统一:统一的写法能提升代码可读性,避免团队成员对抽象方法的行为产生困惑。
少数例外场景:如果是定义__str__、__complex__这类特殊方法,且Python内置逻辑已对未实现情况有明确处理,也可以选择不写显式抛出,但这种情况占比极低。
内容的提问来源于stack exchange,提问作者maskalev
相关产品推荐
相关产品推荐

