Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #109286 > unrolled thread
| Started by | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| First post | 2016-05-31 18:10 +0200 |
| Last post | 2016-06-04 10:44 +1000 |
| Articles | 20 on this page of 23 — 6 participants |
Back to article view | Back to comp.lang.python
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Multiple inheritance, super() and changing signature Nagy László Zsolt <gandalf@shopzeus.com> - 2016-05-31 18:10 +0200
Re: Multiple inheritance, super() and changing signature Steven D'Aprano <steve@pearwood.info> - 2016-06-01 12:18 +1000
Re: Multiple inheritance, super() and changing signature Nagy László Zsolt <gandalf@shopzeus.com> - 2016-06-03 14:10 +0200
Re: Multiple inheritance, super() and changing signature Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2016-06-04 13:06 +1200
Re: Multiple inheritance, super() and changing signature Steven D'Aprano <steve@pearwood.info> - 2016-06-04 12:51 +1000
Re: Multiple inheritance, super() and changing signature Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2016-06-04 23:50 +1200
Re: Multiple inheritance, super() and changing signature Ian Kelly <ian.g.kelly@gmail.com> - 2016-06-03 23:05 -0600
Re: Multiple inheritance, super() and changing signature Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2016-06-04 23:52 +1200
Re: Multiple inheritance, super() and changing signature Steven D'Aprano <steve@pearwood.info> - 2016-06-04 23:46 +1000
Re: Multiple inheritance, super() and changing signature Nagy László Zsolt <gandalf@shopzeus.com> - 2016-06-04 16:42 +0200
Re: Multiple inheritance, super() and changing signature Ben Finney <ben+python@benfinney.id.au> - 2016-06-03 22:21 +1000
Re: Multiple inheritance, super() and changing signature Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2016-06-04 13:10 +1200
Re: Multiple inheritance, super() and changing signature Nagy László Zsolt <gandalf@shopzeus.com> - 2016-06-03 14:14 +0200
Re: Multiple inheritance, super() and changing signature Nagy László Zsolt <gandalf@shopzeus.com> - 2016-06-03 14:57 +0200
Re: Multiple inheritance, super() and changing signature Nagy László Zsolt <gandalf@shopzeus.com> - 2016-06-03 16:06 +0200
Re: Multiple inheritance, super() and changing signature Ian Kelly <ian.g.kelly@gmail.com> - 2016-06-03 08:39 -0600
Re: Multiple inheritance, super() and changing signature Michael Selik <michael.selik@gmail.com> - 2016-06-03 15:33 +0000
Re: Multiple inheritance, super() and changing signature Nagy László Zsolt <gandalf@shopzeus.com> - 2016-06-03 17:59 +0200
Re: Multiple inheritance, super() and changing signature Michael Selik <michael.selik@gmail.com> - 2016-06-03 16:28 +0000
Re: Multiple inheritance, super() and changing signature Ben Finney <ben+python@benfinney.id.au> - 2016-06-04 06:16 +1000
Re: Multiple inheritance, super() and changing signature Ben Finney <ben+python@benfinney.id.au> - 2016-06-04 06:19 +1000
Re: Multiple inheritance, super() and changing signature Ian Kelly <ian.g.kelly@gmail.com> - 2016-06-03 15:53 -0600
Re: Multiple inheritance, super() and changing signature Ben Finney <ben+python@benfinney.id.au> - 2016-06-04 10:44 +1000
Page 1 of 2 [1] 2 Next page →
| From | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| Date | 2016-05-31 18:10 +0200 |
| Subject | Multiple inheritance, super() and changing signature |
| Message-ID | <mailman.54.1464711627.1839.python-list@python.org> |
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
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2016-06-01 12:18 +1000 |
| Message-ID | <574e45f4$0$1611$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #109286 |
On Wed, 1 Jun 2016 02:10 am, Nagy Lc3a1szlc3b3 Zsolt wrote:
> Today I come across this problem for the N+1st time. Here are some
> classes for the example:
A couple of comments... if you're using Python 2, then you may be having
trouble because none of the classes shown below inherit from object. They
are all "old-style" classes. super needs at least one newstyle class to
work correctly.
In Python 3, that will be automatic and you don't need to worry about it.
[...]
> 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)
The correct way to do that is to simply not define an __init__ method at
all.
> Here is a part of the hierarchy:
>
> Observable AppServerSessionMixin
> \ /
> \ /
> Widget (__init__ with two parameters is first introduced here)
> |
> VisualWidget
> |
> BootstrapWidget BaseDesktop
> \ /
> BootstrapDesktop
For this to actually work, it needs to start:
object
/ \
/ \
Observable AppServerSessionMixin
\ /
... continues as above...
or something similar.
> The problem is obvious:
Heh :-) There's nothing obvious about multiple inheritance problems.
> 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.
Raymond Hettinger gives an excellent presentation where he describes various
problems with MI and gives solutions for them. I think this might be it:
http://pyvideo.org/video/1094/the-art-of-subclassing-0
He also discusses super here:
https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
You should also read the provocatively named "Super Considered Harmful"
here:
https://fuhm.net/super-harmful/
Despite the title, after a long discussion on the Python-Dev mailing list,
the author eventually admitted that in fact you do need to use super, and
that it works better than the alternatives. (Unfortunately, apart from a
partial change to the title, he didn't update the essay.) Nevertheless, MI
is always difficult, and he makes some good points in the essay.
[...]
> There is a single class (Widget) that has changed the signature of the
> constructor.
Is this your code? Then simply fix Widget. MI in Python is cooperative: all
the classes have to be designed for MI. It seems that Widget is not.
(Possibly I have misdiagnosed the problem, and the fault lies elsewhere.)
> 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.
Even if you got your wish, you couldn't use it until you're using Python 3.6
or higher.
> 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.
No, that it incorrect. See Raymond Hettinger's talk and essay.
> Here is a proposal:
>
> * method signature changes can be detected syntactically, relatively
> easily
I don't think they can, at least not reliably.
> * raise an exception when super() is called in a method has changed
> the signature (from any of base class(es))
That's overly strict. As Raymond shows, it is easy to deal with changing
method signatures in *cooperative* classes.
> * 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.
This sounds confusing and harder to get right than the current. Consider
that means that, in the worst case, you could split the MRO into two, then
two again, then two again, then two again... trying to debug this, or
understand it, would be a nightmare.
The MRO used by Python is a well-known linearisation studied in great
detail. It is proven to be effective and *correct*. Your proposal would
break that.
Perhaps you are unaware that manually calling the superclass method does not
work correctly in cases of multiple inheritance? You end up either missing
classes, and not calling their method, or calling them twice. That's why
you need to use a proper linearisation algorithm, as used by super.
See this explanation of C3 linearisation here:
https://www.python.org/download/releases/2.3/mro/
> Of course, this would require Python to calculate an MRO that depends on
> the name of the method, and not just the class hierarchy.
I don't even know what that means.
> And also the
> signatures of the methods, but those can also be changed dynamically. So
> this may be a bad idea after all.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| Date | 2016-06-03 14:10 +0200 |
| Message-ID | <mailman.119.1464955829.1839.python-list@python.org> |
| In reply to | #109298 |
> > In Python 3, that will be automatic and you don't need to worry about it. I'm using Python 3. I'm aware of old style and new style classes in Python 2. > > > [...] >> 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) > The correct way to do that is to simply not define an __init__ method at > all. But I have to initialize some default attributes. I could put them into a method called something else, but that would have the same problem. The question I put up is not specific to constructors, initializers or whatever. It was a general question about super() being used in a hierarchy with different signatures of the same method. I'm not sure why everybody started to discuss __init__ and __new__ :-) > Raymond Hettinger gives an excellent presentation where he describes various > problems with MI and gives solutions for them. I think this might be it: > > http://pyvideo.org/video/1094/the-art-of-subclassing-0 I'm going to get back when I'm done with this video. It will take some hours. :-) >> There is a single class (Widget) that has changed the signature of the >> constructor. > Is this your code? Then simply fix Widget. MI in Python is cooperative: all > the classes have to be designed for MI. It seems that Widget is not. > (Possibly I have misdiagnosed the problem, and the fault lies elsewhere.) If I knew what would be a good fix, I would do it. Somebody else suggested to always use *args and **kwargs. That would be cooperative for sure. But then It would be much harder to document the code (with Sphinx for example), or use the syntax analyzer of an IDE (say PyCharm) and have code completion. (Although the later is not a good argument against the language itself.) > Even if you got your wish, you couldn't use it until you're using Python 3.6 > or higher. Yes, I know. I do this for fun, not for money. > That's overly strict. As Raymond shows, it is easy to deal with > changing method signatures in *cooperative* classes. I must watch that for sure. > Perhaps you are unaware that manually calling the superclass method does not > work correctly in cases of multiple inheritance? You end up either missing > classes, and not calling their method, or calling them twice. That's why > you need to use a proper linearisation algorithm, as used by super. > > See this explanation of C3 linearisation here: > > https://www.python.org/download/releases/2.3/mro/ I do not use diamond shapes in my hierarchy, I guess that does not affect me. I may be wrong. Thank you! Laszlo
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2016-06-04 13:06 +1200 |
| Message-ID | <drensfF24tfU1@mid.individual.net> |
| In reply to | #109406 |
Nagy László Zsolt wrote: > I do not use diamond shapes in my hierarchy, I guess that does not > affect me. I may be wrong. If there are no diamonds, there is no need to use super. Explicit inherited method calls, done correctly, will work fine. The only downside is that if your inheritance hierarchy changes, you need to review all your inherited calls to make sure they're still correct. The use of super to address that issue seems attractive. However, super is only applicable if some rather stringent requirements are met: 1. All the methods must have compatible signatures. 2. Except for 3 below, all classes participating in the hierarchy must use super for a given method if any of them do, including any future subclasses. 3. There must be a class at the top of the hierarchy that does *not* make super calls, to terminate the chain. 4. It must not matter what order the methods in a super chain are called. This is because you cannot predict which method a given super call will invoke. It could belong to a subclass of the class making the call. If you can't satisfy *all* of these reqirements, then you can't use super. Sorry, but that's just the way super is. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2016-06-04 12:51 +1000 |
| Message-ID | <5752423f$0$1587$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #109449 |
On Sat, 4 Jun 2016 11:06 am, Gregory Ewing wrote: > Nagy László Zsolt wrote: >> I do not use diamond shapes in my hierarchy, I guess that does not >> affect me. I may be wrong. > > If there are no diamonds, In Python 3, or Python 2 with new-style classes, there are ALWAYS diamonds when you use multiple inheritance. > there is no need to use super. Except then you are precluding others from integrating your classes into their class hierarchies. > Explicit inherited method calls, done correctly, will > work fine. > > The only downside is that if your inheritance hierarchy > changes, you need to review all your inherited calls > to make sure they're still correct. > > The use of super to address that issue seems attractive. > However, super is only applicable if some rather stringent > requirements are met: > > 1. All the methods must have compatible signatures. > > 2. Except for 3 below, all classes participating in the > hierarchy must use super for a given method if any of > them do, including any future subclasses. > > 3. There must be a class at the top of the hierarchy > that does *not* make super calls, to terminate the chain. Normally that will be object. Sometimes you need to prevent messages reaching object. But to my mind, that's a code-smell, and indicates you're doing something wrong. "Something wrong" may be multiple inheritance itself. There is a reason why most languages don't allow MI at all, or only allow a much restricted subset of it, in the form of mixins or traits. And in fact, even using inheritance alone is harder than it seems. Composition is just as powerful and usually easier to work with. [...] > If you can't satisfy *all* of these reqirements, then > you can't use super. Sorry, but that's just the way > super is. If you can't use super, then chances are you can't use *anything*, including manual calls to superclass methods. There's nothing magical about the use of super itself. If you have problems with super, then you have two choices: (1) Manually walk the MRO making the same calls that super would have made, in which case you'll have the same problems super did. (2) DON'T walk the MRO making the same calls that super would have made, in which case you will have all the problems that super solves. Namely, you will either miss calling superclass methods, or you will call them two many times. In a straight linear single inheritance class hierarchy, walking the MRO is trivial whether you use super or not, but not using super restricts you to never using super. But in MI, not using super almost certainly means you're doing it wrong. (If you're lucky, you can get away with it if the methods you fail to call aren't needed, or do nothing, and the methods you call twice are harmless.) -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2016-06-04 23:50 +1200 |
| Message-ID | <drftk7F92k3U1@mid.individual.net> |
| In reply to | #109460 |
Steven D'Aprano wrote: > On Sat, 4 Jun 2016 11:06 am, Gregory Ewing wrote: > >>there is no need to use super. > > Except then you are precluding others from integrating your classes into > their class hierarchies. And if you *do* use super, you're precluding integrating them into other hierarchies that *don't* use super. You can't win. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2016-06-03 23:05 -0600 |
| Message-ID | <mailman.146.1465016744.1839.python-list@python.org> |
| In reply to | #109449 |
On Jun 3, 2016 7:12 PM, "Gregory Ewing" <greg.ewing@canterbury.ac.nz> wrote: > > 4. It must not matter what order the methods in a super > chain are called. This is because you cannot predict > which method a given super call will invoke. It could > belong to a subclass of the class making the call. It can't belong to a subclass; the MRI guarantees that. But it's not necessarily a superclass either.
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2016-06-04 23:52 +1200 |
| Message-ID | <drftp0F92k3U2@mid.individual.net> |
| In reply to | #109465 |
Ian Kelly wrote: > > It can't belong to a subclass; the MRI guarantees that. But it's not > necessarily a superclass either. Er, yes, what I really meant to say was that it could be a class that got introduced into the MRO as a result of someone else subclassing your class. So when you make a super call, you really have *no idea* what it's going to call. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2016-06-04 23:46 +1000 |
| Message-ID | <5752dbc4$0$1596$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #109476 |
On Sat, 4 Jun 2016 09:52 pm, Gregory Ewing wrote: > Ian Kelly wrote: >> >> It can't belong to a subclass; the MRI guarantees that. But it's not >> necessarily a superclass either. > > Er, yes, what I really meant to say was that it could > be a class that got introduced into the MRO as a result > of someone else subclassing your class. > > So when you make a super call, you really have *no idea* > what it's going to call. That's the nature of inheritance in a MI world. If you try to specify a particular superclass manually, you're in the position of the archetypal drunk looking for his lost car keys under a street light "because the light is better here". Sure, you always know which class you're calling, but sometimes its the wrong class! That's the mind-twisting part of MI. Just because your class inherits from K, doesn't mean that you should be calling K's methods. Until you (generic you) come to terms with that, you're going to struggle with MI regardless of what you do. It might help to read Michele Simionato's articles about super, multiple inheritance and related topics. Michele has been working with issues related to MI for many years and has a lot to say about them. Things to know about super: Part 1 http://www.artima.com/weblogs/viewpost.jsp?thread=236275 Part 2 http://www.artima.com/weblogs/viewpost.jsp?thread=236278 Part 3 http://www.artima.com/weblogs/viewpost.jsp?thread=237121 The wonders of super: http://www.artima.com/weblogs/viewpost.jsp?thread=281127 Mixins considered harmful: Part 1 http://www.artima.com/weblogs/viewpost.jsp?thread=246341 Part 2 http://www.artima.com/weblogs/viewpost.jsp?thread=246483 Part 3 http://www.artima.com/weblogs/viewpost.jsp?thread=254367 Part 4 http://www.artima.com/weblogs/viewpost.jsp?thread=254507 Traits as an alternative to MI and mixins: http://www.artima.com/weblogs/viewpost.jsp?thread=246488 Generic functions as an alternative to mixins: http://www.artima.com/weblogs/viewpost.jsp?thread=237764 The Method Resolution Order: https://www.python.org/download/releases/2.3/mro/ Michele wrote: "... the problem is multiple inheritance itself. Inheritance makes your code heavily coupled and difficult to follow (spaghetti inheritance). I have not found a real life problem yet that I could not solve with single inheritance + composition/delegation in a better and more maintainable way than using multiple inheritance." and I think that is the most valuable lesson here. MI itself is, if not an anti-pattern, at least a dangerous one. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| Date | 2016-06-04 16:42 +0200 |
| Message-ID | <mailman.151.1465051368.1839.python-list@python.org> |
| In reply to | #109481 |
> > Things to know about super: > Part 1 http://www.artima.com/weblogs/viewpost.jsp?thread=236275 > Part 2 http://www.artima.com/weblogs/viewpost.jsp?thread=236278 > Part 3 http://www.artima.com/weblogs/viewpost.jsp?thread=237121 > > The wonders of super: > http://www.artima.com/weblogs/viewpost.jsp?thread=281127 > > Mixins considered harmful: > Part 1 http://www.artima.com/weblogs/viewpost.jsp?thread=246341 > Part 2 http://www.artima.com/weblogs/viewpost.jsp?thread=246483 > Part 3 http://www.artima.com/weblogs/viewpost.jsp?thread=254367 > Part 4 http://www.artima.com/weblogs/viewpost.jsp?thread=254507 > > Traits as an alternative to MI and mixins: > http://www.artima.com/weblogs/viewpost.jsp?thread=246488 > > Generic functions as an alternative to mixins: > http://www.artima.com/weblogs/viewpost.jsp?thread=237764 > > The Method Resolution Order: > https://www.python.org/download/releases/2.3/mro/ > Very good links! This will be a good recreational reading for the next two days.
[toc] | [prev] | [next] | [standalone]
| From | Ben Finney <ben+python@benfinney.id.au> |
|---|---|
| Date | 2016-06-03 22:21 +1000 |
| Message-ID | <mailman.120.1464956494.1839.python-list@python.org> |
| In reply to | #109298 |
Nagy László Zsolt <gandalf@shopzeus.com> writes: > > [...] > >> 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) > > The correct way to do that is to simply not define an __init__ method at > > all. > But I have to initialize some default attributes. Then the statement “there is NOTHING else here” must be false. Either the custom ‘__init__’ does something useful, or it doesn't. > > See this explanation of C3 linearisation here: > > > > https://www.python.org/download/releases/2.3/mro/ > I do not use diamond shapes in my hierarchy, I guess that does not > affect me. I may be wrong. With classes all inheriting ultimately from ‘object’ (as all Python 3 classes do, and as all current Python 2 classes should), mutliple inheritance inevitably places your classes in a diamond inheritance pattern. And, what's more, you can't know when writing a class whether it participates in a multiple inheritance hierarchy! So in practice you must write every class so that it will behave well in a diamond inheritance pattern. All this is covered in Raymond Hettinger's material, so it's best that I just leave you to read that. -- \ “If the arguments in favor of atheism upset you, explain why | `\ they’re wrong. If you can’t do that, that’s your problem.” | _o__) —Amanda Marcotte, 2015-02-13 | Ben Finney
[toc] | [prev] | [next] | [standalone]
| From | Gregory Ewing <greg.ewing@canterbury.ac.nz> |
|---|---|
| Date | 2016-06-04 13:10 +1200 |
| Message-ID | <dreo4sF268eU1@mid.individual.net> |
| In reply to | #109407 |
Ben Finney wrote: > With classes all inheriting ultimately from ‘object’ (as all Python 3 > classes do, and as all current Python 2 classes should), mutliple > inheritance inevitably places your classes in a diamond inheritance > pattern. That's usually harmless, though, because object provides very little functionality of its own. -- Greg
[toc] | [prev] | [next] | [standalone]
| From | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| Date | 2016-06-03 14:14 +0200 |
| Message-ID | <mailman.122.1464957114.1839.python-list@python.org> |
| In reply to | #109298 |
> Raymond Hettinger gives an excellent presentation where he describes various > problems with MI and gives solutions for them. I think this might be it: > > http://pyvideo.org/video/1094/the-art-of-subclassing-0 This is a much better version from one year later: https://www.youtube.com/watch?v=miGolgp9xq8
[toc] | [prev] | [next] | [standalone]
| From | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| Date | 2016-06-03 14:57 +0200 |
| Message-ID | <mailman.123.1464958674.1839.python-list@python.org> |
| In reply to | #109298 |
>> But I have to initialize some default attributes. > Then the statement “there is NOTHING else here” must be false. Either > the custom ‘__init__’ does something useful, or it doesn't. Well... the custom __init__ method with nothing else just a super() call was expressed there to show the super() call explicitly, and to emphasize that in that particular class, super() is used instead of an explicit base method call. It is not a pattern to be followed, just syntactic sugar for the sake of the example. So you are right: the custom __init__ in the BootstrapDesktop class is not really needed, and does not do anything useful in that particular class. My original statement was this: "I have to initialize some default attributes", and for that I need to pass arguments. The truthness of this statement is not affected by adding a useless override of a method (with the very same parameters). Even though I see that you are right in what you wrote, I think I don't understand the point because it seem unrelated. > All this is covered in Raymond Hettinger's material, so it's best that I > just leave you to read that. > Is it available in written form? I have tried to watch the video, but the sound quality is so poor that I cannot understand. I have tried to search for a better one, but that is a different one. :-(
[toc] | [prev] | [next] | [standalone]
| From | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| Date | 2016-06-03 16:06 +0200 |
| Message-ID | <mailman.125.1464962774.1839.python-list@python.org> |
| In reply to | #109298 |
>> That's overly strict. As Raymond shows, it is easy to deal with
>> changing method signatures in *cooperative* classes.
> I must watch that for sure.
All right, I have read this:
https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
There is still something I don't get: how to create cooperative classes
when some base classes share some of the parameters?
Here is an example modified from Raymond's post:
class A:
def __init__(self, param1, param2, **kwds):
self.param1 = param1
self.param2 = param2
super().__init__(**kwds)
class B:
def __init__(self, param1, param3, **kwds):
self.param1 = param1
self.param3 = param3
super().__init__(**kwds)
class X(A,B):
def __init__(self, param1, param2, param3, **kwds):
print("param1=",param1,"param2=",param2,"param3=",param3)
super().__init__(param1=param1,param2=param2,param3=param3,**kwds)
print(X.__mro__)
x = X(1,2,3)
Result:
(<class '__main__.X'>, <class '__main__.A'>, <class '__main__.B'>,
<class 'object'>)
param1= 1 param2= 2 param3= 3
Traceback (most recent call last):
File "test.py", line 20, in <module>
x = X(1,2,3)
File "test.py", line 17, in __init__
super().__init__(param1=param1,param2=param2,param3=param3,**kwds)
File "test.py", line 5, in __init__
super().__init__(**kwds)
TypeError: __init__() missing 1 required positional argument: 'param1'
I could only find this as a solution:
class Root:
def __init__(self, **kwds):
pass
class A(Root):
def __init__(self, **kwds):
self.param1 = kwds['param1']
self.param2 = kwds['param2']
super().__init__(**kwds)
class B(Root):
def __init__(self, **kwds):
self.param1 = kwds['param1']
self.param3 = kwds['param3']
super().__init__(**kwds)
class X(A,B):
def __init__(self, param1, param2, param3, **kwds):
print("param1=",param1,"param2=",param2,"param3=",param3)
super().__init__(param1=param1,param2=param2,param3=param3,**kwds)
X(1,2,3)
But then self documentation and code completion becomes very problematic:
http://i.imgur.com/wzlh8uy.png
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2016-06-03 08:39 -0600 |
| Message-ID | <mailman.126.1464964789.1839.python-list@python.org> |
| In reply to | #109298 |
On Fri, Jun 3, 2016 at 8:06 AM, Nagy László Zsolt <gandalf@shopzeus.com> wrote:
>
>>> That's overly strict. As Raymond shows, it is easy to deal with
>>> changing method signatures in *cooperative* classes.
>> I must watch that for sure.
>
> All right, I have read this:
>
> https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
>
> There is still something I don't get: how to create cooperative classes
> when some base classes share some of the parameters?
Why do they need to share the same parameter? Part of cooperative
design is that you can't really design each class in a vacuum; you
need to take the other classes they might get combined with into
account. Perhaps you can extract that parameter into another base
class that is shared by both:
class Root:
def __init__(self, *, param1, **kwds):
self.param1 = param1
super().__init__(**kwds)
class A(Root):
def __init__(self, *, param2, **kwds):
self.param2 = param2
super().__init__(**kwds)
class B(Root):
def __init__(self, *, param3, **kwds):
self.param3 = param3
super().__init__(**kwds)
class X(A, B):
pass
A and B can both depend on having param1 without stomping on each
other because they both inherit Root which requires it.
[toc] | [prev] | [next] | [standalone]
| From | Michael Selik <michael.selik@gmail.com> |
|---|---|
| Date | 2016-06-03 15:33 +0000 |
| Message-ID | <mailman.127.1464968009.1839.python-list@python.org> |
| In reply to | #109298 |
On Fri, Jun 3, 2016 at 10:41 AM Ian Kelly <ian.g.kelly@gmail.com> wrote: > On Fri, Jun 3, 2016 at 8:06 AM, Nagy László Zsolt <gandalf@shopzeus.com> > wrote: > > There is still something I don't get: how to create cooperative classes > > when some base classes share some of the parameters? > > Why do they need to share the same parameter? > Is the problem that the attribute or parameter has the same name in both base classes, but has different meanings in each? If so, and you're in control of every class in the inheritance hierarchy, perhaps you can come up with more specific names so that the bases no longer share the same attribute. If you can't change the base classes, I've got some other solutions, but they're more involved, so I'll wait to hear back.
[toc] | [prev] | [next] | [standalone]
| From | Nagy László Zsolt <gandalf@shopzeus.com> |
|---|---|
| Date | 2016-06-03 17:59 +0200 |
| Message-ID | <mailman.130.1464969564.1839.python-list@python.org> |
| In reply to | #109298 |
> Is the problem that the attribute or parameter has the same name in both > base classes, but has different meanings in each? If they had different meanings, a simple rename would solve the problem. They have the same meaning. > If you can't change the base classes, I've got some other solutions, but > they're more involved, so I'll wait to hear back. One possible solution being encapsulating an object instead of inheriting from it? Fortunately, I can change all of the classes, and extracting the common parameter into a common base class worked. Problem solved. Thank you for all your help! Cooperative classes are fantastic! :-)
[toc] | [prev] | [next] | [standalone]
| From | Michael Selik <michael.selik@gmail.com> |
|---|---|
| Date | 2016-06-03 16:28 +0000 |
| Message-ID | <mailman.133.1464971617.1839.python-list@python.org> |
| In reply to | #109298 |
On Fri, Jun 3, 2016 at 12:01 PM Nagy László Zsolt <gandalf@shopzeus.com> wrote: > > Is the problem that the attribute or parameter has the same name in > both base classes, but has different meanings in each? > If they had different meanings, a simple rename would solve the problem. > Sometimes finding a good name ain't so simple. > If you can't change the base classes, I've got some other solutions, but > > they're more involved, so I'll wait to hear back. > One possible solution being encapsulating an object instead of > inheriting from it? > That's one option, creating a wrapper class that dispatches almost everything to the contained class, except with one renamed attribute/method.
[toc] | [prev] | [next] | [standalone]
| From | Ben Finney <ben+python@benfinney.id.au> |
|---|---|
| Date | 2016-06-04 06:16 +1000 |
| Message-ID | <mailman.135.1464985027.1839.python-list@python.org> |
| In reply to | #109298 |
Nagy László Zsolt <gandalf@shopzeus.com> writes:
> So you are right: the custom __init__ in the BootstrapDesktop class is
> not really needed, and does not do anything useful in that particular
> class.
I disagree: setting initial attributes is a normal and useful case for
defining a custom initialiser.
> My original statement was this: "I have to initialize some default
> attributes", and for that I need to pass arguments.
Yes. If you need to initialise the instance, that's what a custom
‘__init__’ is for.
> The truthness of this statement is not affected by adding a useless
> override of a method (with the very same parameters). Even though I
> see that you are right in what you wrote, I think I don't understand
> the point because it seem unrelated.
My point was rather that if the custom initialiser does *nothing* except
call the superclass's initialiser, then there's no purpose to writing
the custom initialiser.
If you're writing a custom initialiser that handles two additional
parameters, then those parameters should not be present when you call
the super() method's initialiser::
# You specified Python 3, which allows simpler syntax.
class LoremIpsum:
def __init__(self, spam, *args, **kwargs):
do_something_important_with(spam)
super().__init__(*args, **kwargs)
class DolorSitAmet(LoremIpsum):
def __init__(self, spam, eggs=4, beans=None, *args, **kwargs):
self.eggs = eggs
self.beans = beans
super().__init__(spam, *args, **kwargs)
So the sub-class follows the Liskov Substitution Principle: every
DolorSitAmet instance should be substitutable for LoremIpsum instances,
and users that only expect a LoremIpsum instance should not need to know
any difference.
This entails that its methods (in this case the initialiser) will accept
the same parameters as ‘LoremIpsum.__init__’, and do the same things
with those parameters. We make that explicit by omitting the
*additional* parameters that we already handled, ‘eggs’ and ‘beans’,
from the next call in the chain.
--
\ “The difference between religions and cults is determined by |
`\ how much real estate is owned.” —Frank Zappa |
_o__) |
Ben Finney
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.python
csiph-web