Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #109291
| From | Ben Finney <ben+python@benfinney.id.au> |
|---|---|
| Newsgroups | comp.lang.python |
| Subject | Re: Multiple inheritance, super() and changing signature |
| Date | 2016-06-01 06:01 +1000 |
| Message-ID | <mailman.58.1464724917.1839.python-list@python.org> (permalink) |
| References | <80ea0ea1-5468-2dee-2c38-c2b09801d0f1@shopzeus.com> <85h9de6lp4.fsf@benfinney.id.au> |
Nagy László Zsolt <gandalf@shopzeus.com> writes:
> Today I come across this problem for the N+1st time. Here are some
> classes for the example:
Thank you for the example.
(Note that ‘__init__’ is not a constructor, because it operates on the
*already constructed* instance, and does not return anything. Python's
classes implement the constructor as ‘__new__’, and you very rarely need
to bother with that.)
For the reasons you describe (among others), participating in multiple
inheritance is tricky.
As described in numerous articles, for example
<URL:https://rhettinger.wordpress.com/2011/05/26/super-considered-super/>,
the right way to do this is:
* Obey the Liskov Substitution Principle (sub-classes should override a
method only when they allow all the parent's behaviour as well as
their own).
This also means that the sub-class should pass any unknown arguments
along to the superclass's method to handle.
* Once a parameter is handled in a method, remove it from the set and
pass only the remaining arguments along to the superclass's method.
> class Observable:
> """Implements the observer-observable pattern."""
>
> def __init__(self):
> # initialization code here...
> super(Observable, self).__init__()
This should be:
def __init__(self, **kwargs):
# …
super(Observable, self).__init__(**kwargs)
> class Widget(Observable, AppServerSessionMixin):
> def __init__(self, appserver, session):
> self.appserver = appserver
> self.session = session
> # general widget initialization code here...
> super(Widget, self).__init__()
This should be:
def __init__(self, appserver, session, **kwargs):
self.appserver = appserver
self.session = session
# …
super(Widget, self).__init__(**kwargs)
> class VisualWidget(Widget):
> def __init__(self, appserver, session):
> # some more code here for visually visible widgets...
> super(VisualWidget, self).__init__(appserver, session)
Since this function doesn't do anything with the parameters, it doesn't
need to have its own names for them:
def __init__(self, **kwargs):
# …
super(VisualWidget, self).__init__(**kwargs)
> 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)
And again:
def __init__(self, **kwargs):
# …
super(BaseDesktop, self).__init__(**kwargs)
def __init__(self, **kwargs):
# …
super(BootstrapWidget, self).__init__(**kwargs)
> 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)
If the ‘__init__’ method literally does nothing except call the
superclass's method, don't define it in BootstrapDesktop at all.
And likewise for the rest: pass along all remaining arguments, and don't
write a do-nothing ‘__init__’.
> 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.
You avoid this by the ‘**kwargs’ idiom (no need to name arguments you
don't explicitly handle), and by not writing any ‘__init__’ if you want
the default behaviour (call the superclass's method).
> This is time consuming and error prone.
As pointed out in Raymond Hettinger's article
<URL:https://rhettinger.wordpress.com/2011/05/26/super-considered-super/>,
multiple inheritance is inherently tricky and there is an irreducible
complexity that has to appear somewhere in your code.
> 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.
Use ‘**kwargs’, and always pass along the remaining ‘**kwargs’
minus-any-parameters-you-already-handled, to protect your existing
classes from unnecessary signature changes.
--
\ “Creativity can be a social contribution, but only in so far as |
`\ society is free to use the results.” —Richard M. Stallman |
_o__) |
Ben Finney
Back to comp.lang.python | Previous | Next — Next in thread | Find similar | Unroll thread
Re: Multiple inheritance, super() and changing signature Ben Finney <ben+python@benfinney.id.au> - 2016-06-01 06:01 +1000
Re: Multiple inheritance, super() and changing signature Lawrence D’Oliveiro <lawrencedo99@gmail.com> - 2016-06-02 01:22 -0700
Re: Multiple inheritance, super() and changing signature Michael Selik <michael.selik@gmail.com> - 2016-06-02 14:44 +0000
Re: Multiple inheritance, super() and changing signature Steven D'Aprano <steve@pearwood.info> - 2016-06-03 03:36 +1000
Re: Multiple inheritance, super() and changing signature Ian Kelly <ian.g.kelly@gmail.com> - 2016-06-02 11:55 -0600
Re: Multiple inheritance, super() and changing signature Random832 <random832@fastmail.com> - 2016-06-02 17:18 -0400
Re: Multiple inheritance, super() and changing signature Marko Rauhamaa <marko@pacujo.net> - 2016-06-03 01:20 +0300
Re: Multiple inheritance, super() and changing signature Steven D'Aprano <steve@pearwood.info> - 2016-06-03 22:47 +1000
csiph-web