【发布时间】:2021-12-15 14:50:01
【问题描述】:
通常在 Python 中,可以使用以下技术检测方法是否已在子类中被覆盖:
>>> class Foo:
... def mymethod(self): pass
...
>>> class Bar(Foo): pass
...
>>> Bar.mymethod is Foo.mymethod
True
如果Foo 中的方法没有在Bar 中被覆盖,则表达式Bar.mymethod is Foo.mymethod 将评估为True,但如果方法 将评估为False已在Bar 中覆盖。此技术适用于从object 继承的 dunder 方法以及非 dunder 方法:
>>> Bar.__new__ is Foo.__new__
True
>>> Bar.__eq__ is Foo.__eq__
True
我们可以在这样的函数中形式化这个逻辑:
def method_has_been_overridden(superclass, subclass, method_name):
"""
Return `True` if the method with the name `method_name`
has been overridden in the subclass
or an intermediate class in the method resolution order
"""
if not issubclass(subclass, superclass):
raise ValueError(
"This function only makes sense if `subclass` is a subclass of `superclass`"
)
subclass_method = getattr(subclass, method_name)
if not callable(method):
raise ValueError(f"'{subclass.__name__}.{method_name}' is not a method")
return subclass_method is not getattr(superclass, method_name, object())
但是,当涉及到两种方法时,这种技术会失败:__init_subclass__ 和 __subclasshook__:
>>> class Foo: pass
...
>>> class Bar(Foo): pass
...
>>> Bar.__init_subclass__ is Foo.__init_subclass__
False
>>> Bar.__subclasshook__ is Foo.__subclasshook__
False
还有一个更令人困惑的例子:
>>> type.__init_subclass__ is type.__init_subclass__
False
我有两个问题:
- 为什么这种技术在使用这些方法时会失败,而且只有这些方法? (我还没有找到任何其他这种技术失败的例子——但如果有的话,我很想知道它们!)
- 是否有替代技术可用于检测
__init_subclass__或__subclasshook__在超类中未定义后是否已在子类中定义?
【问题讨论】:
-
一种检查覆盖的方法可能不能保证适用于所有实现是
Bar.__init_subclass__.__func__ is Foo.__init_subclass__.__func__。 -
@MichaelButscher:不幸的是,这甚至不适用于纯 CPython 和问题中简单的
Foo和Bar示例类。用 C 编写的类方法(如object.__init_subclass__)使用与用 Python 编写的类方法不同的类型,并且 C 方法的类型不支持__func__。
标签: python oop inheritance