Reputation: 341
I have three classes as follows:
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)
I instantiate a ReportingPage
object. To do this Python crawls up the MRO:
The Page
object is called first, as it's ordered first in the inheritance list for ReportingPage
, where it calls its own __init__
method.
Then it does the same for Report
.
Two things I don't understand:
Why I must pass in arguments to the super.__init__
in Page
, when Page
is just going to call __init__
on what it inherits from, object
.
Why I don't have to do the same thing for Report
.
Upvotes: 4
Views: 592
Reputation: 1122342
super()
looks at the MRO of the current instance. It doesn't matter here that the current class inherits only from object
.
The MRO of ReportingPage
puts Report
between Page
and object
:
>>> ReportingPage.__mro__
(<class '__main__.ReportingPage'>, <class '__main__.Page'>, <class '__main__.Report'>, <class 'object'>)
So when you call super()
in Page.__init__()
, the next class in the MRO is Report
, and you end up calling the Report.__init__
method.
You need to make your classes more cooperative; you could use keyword arguments and a catch-all **kwargs
argument to do so:
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)
Each method passes along the remaining keyword arguments here, to the next __init__
in the MRO, and in the end you'll have an empty dictionary to pass to object.__init__()
. If you add in a print(kwargs)
wrapper to each __init__
method, you can see that kwargs
becomes smaller as fewer values are passed on to the next call.
>>> 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>
Only title
remains, which Report.__init__()
consumes, so an empty kwargs
dictionary is passed to object.__init__()
You may be interested in Raymond Hettinger's super considered super, including his PyCon 2015 presentation.
Upvotes: 6