【问题标题】:Using super() in Python, I do not understand this last __init__ call在 Python 中使用 super(),我不明白最后的 __init__ 调用
【发布时间】:2018-06-20 17:10:41
【问题描述】:

我有如下三个类:

class Page(object):
    def __init__(self, Obj_a, Obj_b):
        super().__init__(Obj_a, Obj_b)

class Report(object):
    def __init__(self, Obj_a, Obj_b):
        super().__init__()

class ReportingPage(Page,Report):
    def __init__(self, Obj_a, Obj_b):
        super().__init__(Obj_a, Obj_b)

我实例化了一个ReportingPage 对象。为此,Python 会爬取 MRO:

  1. 首先调用Page 对象,因为它在ReportingPage 的继承列表中排在第一位,它调用自己的__init__ 方法。

  2. 然后它对Report 执行相同的操作。

两件事我不明白:

  1. 为什么我必须将参数传递给Page 中的super.__init__,而Page 只是要调用__init__ 继承自object .

  2. 为什么我不必为 Report 做同样的事情。

【问题讨论】:

  • 当您尝试创建 Page 实例时,这应该会抛出 TypeError,因为 object.__init__ 不接受任何参数
  • 1.因为Page.__init__ 在通过ReportingPage.__init__ 调用时将调用Report.__init__;这是 MRO 中的下一个。 2. 因为它是在object.__init__ 之前被调用的最后一个,所以没有任何东西可以传递给它(实际上它不能传递任何东西,因为object.__init__ 不接受它们) .
  • @DrewAckerman 他们说“当你尝试创建Page 实例时”,即直接实例化Page(1, 2)。您的类不正确支持多重继承,并且仅在实例化 ReportingPageReport 时有效。
  • 相关:super-harmful。你已经在ReportingPage 中创建了一个菱形继承,所以你不得不在__init__ 中传递*args、**kwargs。
  • @wim:现在也阅读super considered super

标签: python python-3.x multiple-inheritance


【解决方案1】:

super() 查看当前实例的 MRO。当前的只继承自object,这无关紧要。

ReportingPage 的 MRO 将 Report 置于 Pageobject 之间:

>>> ReportingPage.__mro__
(<class '__main__.ReportingPage'>, <class '__main__.Page'>, <class '__main__.Report'>, <class 'object'>)

所以当你在Page.__init__() 中调用super() 时,MRO 中的下一个类是Report,你最终会调用Report.__init__ 方法。

你需要让你的班级更加合作;您可以使用关键字参数和包罗万象的**kwargs 参数来这样做:

class Page(object):
    def __init__(self, pagenum, **kwargs):
        self.pagenum = pagenum
        super().__init__(**kwargs)

class Report(object):
    def __init__(self, title, **kwargs):
        self.title = title
        super().__init__(**kwargs)

class ReportingPage(Page, Report):
    def __init__(self, footer=None, **kwargs):
        self.footer = footer
        super().__init__(**kwargs)

每个方法都将此处剩余的关键字参数传递给 MRO 中的下一个 __init__,最后您将有一个空字典传递给 object.__init__()。如果在每个__init__ 方法中添加print(kwargs) 包装器,您可以看到kwargs 变得更小,因为传递给下一个调用的值越少。

>>> def print_wrapper(name, f):
...     def wrapper(*args, **kwargs):
...         print(name, '->', kwargs)
...         return f(*args, **kwargs)
...     return wrapper
...
>>> for cls in ReportingPage.__mro__[:-1]:  # all except object
...     cls.__init__ = print_wrapper(cls.__name__, cls.__init__)
...
>>> ReportingPage(title='Watching Paint Dry II: The Second Coat', pagenum=42)
ReportingPage -> {'title': 'Watching Paint Dry II: The Second Coat', 'pagenum': 42}
Page -> {'title': 'Watching Paint Dry II: The Second Coat', 'pagenum': 42}
Report -> {'title': 'Watching Paint Dry II: The Second Coat'}
<__main__.ReportingPage object at 0x109e3c1d0>

只剩下titleReport.__init__() 消耗,所以一个空的kwargs 字典被传递给object.__init__()

您可能对Raymond Hettinger's super considered super 感兴趣,包括他的PyCon 2015 presentation

【讨论】:

  • 在这里提出问题并看到它基于 MRO 排序而不是子类/超类排序调用最终使这一切都得到了点击。这..很乱。
  • @Martjin Pieters。是在这个“子类”init中显式调用两个类的init的唯一解决方案吗?
  • @DrewAckerman:不,我在回答中添加了合作方法。
  • @DrewAckerman: super() 旨在使协作多重继承清晰一致。但是如果你不是在设计合作类,那就不要使用super(),直接在父类上调用方法。
  • @Martjin Pieters 什么将一个类指定为合作社?只是为了让我知道这是否是一个合适的用例
猜你喜欢
  • 1970-01-01
  • 2017-06-30
  • 2015-12-09
  • 2020-11-06
  • 2011-06-29
  • 2020-03-13
  • 2020-05-17
  • 1970-01-01
  • 2022-01-19
相关资源
最近更新 更多