On Wed, Jul 01, 2015 at 10:40:20PM +0100, Alan Gauld wrote: > >Multiple inheitance is a fact in Python, and good practice is to not > >arbitrarily write classes that break it. > > That depends on what you mean by break it., MI should allow the > inheriting class to specify which, if any, of its direct superclasses > methods are invoked. That's critical to the use of MI in any language > The issues around that are addressed differently by different languages, > for example Eiffel allows you to rename inherited methods > or attributes to avoid them being used involuntarily.
That sounds like Eiffel multiple-inheritance is closer to traits than full M-I: http://www.artima.com/weblogs/viewpost.jsp?thread=246488 > Most just allow > the programmer to explicitly access the superclasses where default > behaviour is not required. That's what I thought Python was doing with > super. Making default easy to get but still allowing direct overriding > when required. In Python, super() is designed to ensure that when you call the parent(s)'s method, *all* the parents that define that method get run, not just the immediate parent. (Assuming that they all cooperate by using super.) To quote Michele Simionato from the above link: [quote] The point to notice is that the complication of the MRO is by design: languages with a non-trivial MRO where [sic] designed this way to make possible method cooperation via super calls. That means that if both parents P1 and P2 define a method m, a child class C can override it and still have access to the m methods of the parents via super: C.m will call first P1.m and then P2.m, if P1.m features a super call itself. [end quote] If your inheritance needs don't match that behaviour, then Python's M-I model is not a good fit for you, and you might be better off with Michele's strait module, some other library, or just managing the inheritance calls yourself. But if you do that, you're implicitly ruling your class out from participating in "regular" M-I hierarchies. > >>class A(B,C,D): > >> def m(self): > >> C.m(self) # call C's m but not B and D if they have them. > >> > >>Or am I missing something in the way Python evaluates > >>the C.m() call above? > > I still want to know how you would code the above scenario > using super? If B,C and D all have an m() method but I only > want to call the version in C how do I control that in > Python other than by a direct call to C.m()? That can't be done using super(). Super isn't designed for that use-case, it's designed for the case where you want to call B.m, C.m and D.m in that order. Hence my comments in the previous post about not *unnecessarily* ruling out the use of multiple inheritance. If it's necessary, you can do it. > >Any class one writes can > >become part of some other class's multiple-inheritance chain, and it is > >bad practice to assume one knows at time of writing what the superclass > >will be at run time. > > This is the bit I don't understand. The superclass of a given class > should never change. The sequence of default processing may change but > any other behavior would be a serious breach of the class definition. Even in the single inheritance world, there is no such concept as "the superclass". There is *one or more* superclasses (note plural). (Zero only in the case of the root of the object hierarchy.) In the most general case, your child class doesn't know whether it is inheriting the method from the parent, grandparent, great-grandparent, ... great-to-the-Nth-grandparent. Now, in S-I world, it doesn't matter whether you write: super().method(arg) or MyParent.method(self, arg) it will just work. But in a M-I world, only the first case will operate correctly when using multiple inheritance of the particular model for M-I supported by Python. If your needs are different, you're on your own, and good luck to you. > A rather contrived example is where I want to inherit a Pen and a > Curtain to produce a GraphicalCurtain class. Both Pen and Curtain define > a draw() method but they do very different things. So I define two > methods: draw() which invokes Pen.draw() and close() which invokes > Curtain.draw(). If I use super in either of those methods I'll get the > wrong behavior because both Pen.draw and Curtain.draw will be invoked, I think Ben's super()-friendly answer to that would be to use an adaptor class: # Untested class MyCurtain(object): def __init__(self, *args): self.__curtain = Curtain(*args) def __getattr__(self, name): if name == 'draw': raise AttributeError return getattr(self.__curtain, name) def close(self): return self.__curtain.draw() class GraphicalCurtain(Pen, MyCurtain): def draw(self): print("Drawing drawing drawing...") super().draw() > Unless I'm missing something clever in super? I never quite > understood the parameters in super() so it is possible that > there is a way to exclude some super classes, but I'm not > aware of any such. There may be tricks you can play with metaclasses, to re-define the __mro__. You cannot do that with a regular class, as the __mro__ attribute is read-only, but with metaclasses there is no limit to the deep cacky you can find yourself. Er, I mean, no limit to the wonderful things you can do. > And lest there is any confusion, I agree that super should be the > default option. I'm only pointing out that a) super does not change > the superclass, it simply moves it to a different place in the > chain of execution and b) several cases exist where the default MRO > sequence is not what you need. In those cases you have to work > it out for yourself (or find a way to avoid them if possible!) I think I can agree with that. -- Steve _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor