在Python中使用super(),我不理解最后的__init__调用

时间:2018-06-20 17:10:41

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

我有以下三个班级:

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. 为什么我必须将参数传递给super.__init__中的Page,而Page只是要调用__init__它继承自object

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

1 个答案:

答案 0 :(得分:6)

super()查看当前实例的MRO。在这里,当前的 class 仅从object继承即可。

ReportingPage的MRO将Report放在Pageobject之间:

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

因此,当您在super()中调用Page.__init__()时,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__()。如果将print(kwargs)包装器添加到每个__init__方法中,则可以看到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