首页 > 解决方案 > 如何检测 `__init_subclass__` 是否已在子类中被覆盖?

问题描述

通常在 Python 中,可以使用以下技术检测方法是否已在子类中被覆盖:

>>> class Foo:
...     def mymethod(self): pass
...
>>> class Bar(Foo): pass
...
>>> Bar.mymethod is Foo.mymethod 
True

如果方法 from在 中被覆盖,则表达式Bar.mymethod is Foo.mymethod将计算为,但如果方法在 中覆盖,则计算结果为。此技术适用于从 继承的 dunder 方法以及非 dunder 方法:TrueFooBarFalseBarobject

>>> 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

我有两个问题:

  1. 为什么这种技术在这些方法上失败了,而且只有这些方法?(我还没有找到任何其他这种技术失败的例子——但如果有的话,我很想知道它们!)
  2. 在超类中未定义后,是否有替代技术可用于检测是否__init_subclass__或已在子类中定义?__subclasshook__

标签: pythonoopinheritance

解决方案


__init_subclass__特殊情况下是一个类方法,无论你是否装饰它classmethod。就像每次通过类实例访问属性时Foo().mymethod返回一个新method实例一样,每次通过类本身访问属性时都会Foo.__init_subclass__生成一个新方法。instance

__subclasshook__另一方面,必须将其声明为类方法才能正常工作。如果将其定义为简单的函数/实例方法,则不假定它是类方法。


推荐阅读