在 Python 中使用 super(),我不明白最后一个 __init__ 调用

Using super() in Python, I do not understand this last __init__ call

我有三个类如下:

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 做同样的事情。

super()查看当前实例的MRO。这里并不重要,当前的 class 仅继承自 object.

ReportingPage 的 MRO 将 Report 置于 Pageobject 之间:

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

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

你需要让你的 class 更加合作;你可以使用关键字参数和一个包罗万象的 **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, including his PyCon 2015 presentation 感兴趣。