Multiple inheritance, super() and changing signature

Nagy László Zsolt gandalf at shopzeus.com
Tue May 31 12:10:57 EDT 2016


Today I come across this problem for the N+1st time. Here are some
classes for the example:


class Observable:
    """Implements the observer-observable pattern."""

    def __init__(self):
        # initialization code here...
        super(Observable, self).__init__()

class AppServerSessionMixin:
    # There is no __init__ here, this is a mixin
    pass

class Widget(Observable, AppServerSessionMixin):
    def __init__(self, appserver, session):
        self.appserver = appserver
        self.session = session
        # general widget initialization code here...
        super(Widget, self).__init__()

class VisualWidget(Widget):
    def __init__(self, appserver, session):
        # some more code here for visually visible widgets...
        super(VisualWidget, self).__init__(appserver, session)

class BaseDesktop:
    def __init__(self, appserver, session):
        # general code for all desktops is here...
        super(BaseDesktop, self).__init__(appserver, session)

class BootstrapWidget(VisualWidget):
    def __init__(self, appserver, session):
        # bootstrap specific code for all widgets (not just desktops)
        super(BootstrapWidget, self).__init__(appserver, session)

class BootstrapDesktop(BootstrapWidget, BaseDesktop):
    def __init__(self, appserver, session):
        # there is NOTHING else here, it just connects bootstrap widget
implementation with desktop methods
        super(BootstrapDesktop, self).__init__(appserver, session)

class JqueryUiWidget(VisualWidget):
    def __init__(self, appserver, session):
        # jquery ui specific code for all widgets (not just
desktops)       
        super(JqueryUiWidget, self).__init__(appserver, session)

class JqueryUiDesktop(Widget, BaseDesktopMixIn):
    def __init__(self, appserver, session):
        # there is NOTHING else here, it just connects jquery ui widget
implementation with desktop methods
        super(JqueryUiDesktop, self).__init__(appserver, session)

Here is a part of the hierarchy:

Observable      AppServerSessionMixin
         \      / 
          \    /
          Widget (__init__ with two parameters is first introduced here)
             |
           VisualWidget
             |          
          BootstrapWidget BaseDesktop
                      \   /                     
                 BootstrapDesktop

The problem is obvious: because of the method resolution order, the
super call in Observable.__init__ will try to call BaseDesktop.__init__
without arguments, but that constructor has needs to arguments. The
arguments are lost when Widget calls super() without them.

Of course, I could explicitly call constructors of base classes. But
then I cannot use super() anywhere. Even not in places that are far down
in the class hierarchy. This results in typing all method parameters
many times, and also unnecesary refactoring. This is time consuming and
error prone.

There is a single class (Widget) that has changed the signature of the
constructor. I was thinking about cutting the MRO list somehow into two
parts. It would be very nice to tell Python to create a separate MRO
list for classes above and below the Widget class. Classes below Widget
would not see any method above Widget, and that would allow me to use
super() anywhere in the class hierarchy below Widget. I would only have
to do an explicit call when the method signature is changed. That would
save me a lot of parameter retyping and refactoring.

In fact, I could never safely use super() in any case when the method
signature has changed. When signatures are changed, method resolution
must be done manually anyway.

Here is a proposal:

* method signature changes can be detected syntactically, relatively easily
* raise an exception when super() is called in a method has changed the
signature (from any of base class(es))
* when a method changes signature, then split the MRO automatically into
two parts. The tail of the MRO "below" the signature-changing-class
should end in the method that has changed signature, and that method
would be responsible for calling (or not calling) the method(s) with the
same name inherited from base classes.

Of course, this would require Python to calculate an MRO that depends on
the name of the method, and not just the class hierarchy. And also the
signatures of the methods, but those can also be changed dynamically. So
this may be a bad idea after all.

Please let me know what you think.

   Laszlo






More information about the Python-list mailing list