【问题标题】:Implicit inheritance from the collections.abc.Container class when implementing __contains__ method实现 __contains__ 方法时从 collections.abc.Container 类隐式继承
【发布时间】:2021-09-28 23:32:45
【问题描述】:

所以,我对 Python 有点陌生,对 OOP 范式也有点陌生,最近我遇到了一些似乎有点违反直觉的事情。例如,看看下面的代码(在 Python 3.9 上运行):

from collections.abc import Container
class OddContainer:
    
    def __contains__(self, x):
        if not isinstance(x, int) or not x%2:
            return False
        return True
            
if __name__ == '__main__':
    
    x = OddContainer()
    print(isinstance(x, Container))

这里的print会输出True,表示OddContainerContainer的子类,但是OddContainer类没有明确指定它的超类。 有人能告诉我这里到底发生了什么吗?说任何在内部重载了__contains__() 方法的类成为Container 类的子类是否正确,或者这是一种天真的解释?如果前者是正确的,那么还有其他情况会发生这种通过方法重载进行的继承吗?

提前致谢。

【问题讨论】:

    标签: python inheritance containers abstract-class


    【解决方案1】:

    来自docs of isinstance

    如果对象参数是 classinfo 参数或其(直接、间接或虚拟)子类的实例,则返回 True。

    强调我的。当点击“虚拟”的链接时,它会导致词汇表中对“ABC”的描述指出:

    ABCs引入了虚拟子类,这些类不继承自一个类但仍然被isinstance()和issubclass()识别;请参阅 abc 模块文档。

    因此,OddContainer 被视为“虚拟子类”,因为它满足 Container ABC。

    【讨论】:

      【解决方案2】:

      TL;DR isinstance 并非纯粹基于继承。被测试的类可以自己定义作为类的实例意味着什么。 (不要将“x 是 C 的实例”与“x 的类型继承自 C”混淆。)


      Container 定义__subclasshook__ 以使任何具有__contains__ 方法的类都被视为子类,无论Container 是否是该类通过继承的实际祖先。

      以下是 Python 3.9 中collections.abc.Container](https://github.com/python/cpython/blob/3.9/Lib/_collections_abc.py#L388) 的完整定义:

      class Container(metaclass=ABCMeta):
      
          __slots__ = ()
      
          @abstractmethod
          def __contains__(self, x):
              return False
      
          @classmethod
          def __subclasshook__(cls, C):
              if cls is Container:
                  return _check_methods(C, "__contains__")
              return NotImplemented
      
          __class_getitem__ = classmethod(GenericAlias)
      

      __subclasshook__ 优先于继承检查。

      当确定某个类C 是否是Container 的子类时,会调用Container.__subclasshook__(C)。如果C.__contains__存在,则返回True;如果不是,则返回 False。 (我不确定是否值得深入研究如何调用 Container.__subclasshook__cls 不是Container。)

      【讨论】:

      • 啊,好的,我明白了。通过这种方式,在 B 类中重载 __subclasshook__,我们可以定义一组条件,在这些条件下,A 类将被“视为”B 类的子类,对吧?
      • 对。也许另一种描述方式是继承只是一种成为另一个子类的方式。
      猜你喜欢
      • 2013-08-25
      • 1970-01-01
      • 2015-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-16
      • 1970-01-01
      • 2010-12-21
      相关资源
      最近更新 更多