【问题标题】:super() class inheritance, Diamond Problemsuper() 类继承,钻石问题
【发布时间】:2020-10-03 04:19:21
【问题描述】:

为什么当我在WordCounter 类中调用super() 时,它调用Vocabulary 类,即使它不是继承自?它不应该调用Tokenizer 类吗?

class Tokenizer:
    """Tokenize text"""
    def __init__(self, text):
        print('Start Tokenizer.__init__()')
        print('End Tokenizer.__init__()')


class Vocabulary(Tokenizer):
    """Find unique words in text"""
    def __init__(self, text):
        print('Start init Vocabulary.__init__()')
        print('End init Vocabulary.__init__()')


class WordCounter(Tokenizer):
    """Count words in text"""
    def __init__(self, text):
        print('Start WordCounter.__init__()')
        super().__init__(text)
        print('End WordCounter.__init__()')


class TextDescriber(WordCounter, Vocabulary):
    """Describe text with multiple metrics"""
    def __init__(self, text):
        print('Start init TextDescriber.__init__()')
        super().__init__(text)
        print('End init TextDescriber.__init__()')


td = TextDescriber('row row row your boat')

输出:

Start init TextDescriber.__init__()
Start WordCounter.__init__()
Start init Vocabulary.__init__()
End init Vocabulary.__init__()
End WordCounter.__init__()
End init TextDescriber.__init__()

【问题讨论】:

    标签: python inheritance superclass


    【解决方案1】:

    钻石继承总是有点混乱。每种语言都有自己的怪癖,Python 也不例外。 (请注意,我解释的是 Python 中的“新样式”类。Python 2 中也有“经典”类,它们的行为不同)

    Python 对所有继承所做的事情是,如果当前类未实现请求的方法/属性,则呈现检查父类的顺序。您可以动态检查此方法解析顺序。您的示例产生

    >>> print(TextDescriber.__mro__)
    (<class '__main__.TextDescriber'>, <class '__main__.WordCounter'>, <class '__main__.Vocabulary'>, <class '__main__.Tokenizer'>, <class 'object'>)
    

    如您所见,Python 选择从左到右,然后才在层次结构中下降 (full details)。

    super() 所做的是调用此__mro__ 中的下一个方法。此链在Vocabulary.__init__() 处停止,因为此方法没有super().__init__() 调用以使链继续。

    如果您在Vocabulary.__init__() 中包含super().__init__() 调用,这将按预期工作:

    Start init TextDescriber.__init__()
    Start WordCounter.__init__()
    Start init Vocabulary.__init__()
    Start Tokenizer.__init__()
    End Tokenizer.__init__()
    End init Vocabulary.__init__()
    End WordCounter.__init__()
    End init TextDescriber.__init__()
    

    【讨论】:

    • 感谢@Niobos 对 MRO 的解释,但这并不能告诉我为什么它选择词汇表而不是 Tokenizer?比如它背后的逻辑是什么?
    • 这就是 Python 定义继承顺序的方式。如果您想了解有关所有边缘情况的更多信息,我已经添加了指向完整规范的链接。
    • @Kashan。请记住,在WordCounter.__init__ 中对super().__init__(text) 的调用等同于super(WordCounter, self).__init__(text)。这两个参数的意思是“使用self 的mro,以WordCounter 开头”。我们习惯于看到selfWordCounter 类型或从它单一继承。如果不是这种情况,WordCounter 的父级可能不是 mro 中的下一个对象,正如您在此处看到的那样。这就是答案中“super() 的作用是在这个__mro__ 中调用下一个方法”这句话的意思。
    猜你喜欢
    • 1970-01-01
    • 2019-10-09
    • 1970-01-01
    • 2017-12-09
    • 2022-01-12
    • 1970-01-01
    • 2012-11-05
    • 2020-01-01
    • 2020-09-23
    相关资源
    最近更新 更多