On Tue, Jul 16, 2019 at 6:05 PM Antoon Pardon <antoon.par...@vub.be> wrote: > > On 16/07/19 09:18, Chris Angelico wrote: > > On Tue, Jul 16, 2019 at 3:32 PM Ian Kelly <ian.g.ke...@gmail.com> wrote: > >> Just using super() is not enough. You need to take steps if you want to > >> ensure that you class plays nicely with MI. For example, consider the > >> following: > >> > >> class C1: > >> def __init__(self, name): > >> self._name = name > >> > >> class C2(C1): > >> def __init__(self, name, value): > >> super().__init__(name) > >> self._value = value > >> > >> This usage of super is just fine for the single-inheritance shown here. But > >> there are two reasons why this cannot be neatly pulled into an MI > >> hierarchy. Can you spot both of them? > > Well, obviously it's violating LSP by changing the signature of > > __init__, which means that you have to be aware of its position in the > > hierarchy. If you want things to move around smoothly, you HAVE to > > maintain a constant signature (which might mean using *args and/or > > **kwargs cooperatively). > > I guess the second problem is that C1 doesn't call super. Meaning that if > someone else uses this in a multiple heritance scheme, and the MRO reaches > C1, the call doesn't get propagated to the rest. >
My understanding of this tiny hierarchy is that C1 is the root of the cooperative subtree. Example: class Pizza: # like C1 def __init__(self, *, name="Generic Pizza", **kw): self.name = name if kw: raise TypeError("Unexpected keyword args - did you miss a mixin?") class MeatloversPizza(Pizza): # like C2 def __init__(self, *, pepperoni=True, **kw): super().__init__(**kw) if pepperoni: self.ingredients = "hot" class CheesyCrustPizza(Pizza): # alternate mixin def __init__(self, *, cheesetype="mozzarella", **kw): super().__init__(**kw) self.crust = cheesetype + "-stuffed" class DecadentPizza(MeatloversPizza, CheesyCrustPizza): pass The only change I've made here is to ensure that everyone's cooperatively using **kwargs. You can instantiate a DecadentPizza(cheesetype="cheddar", name="Eat Me", pepperoni=False), and the different classes will pick up the parts they want. This sort of hierarchy works just fine, since MeatloversPizza is using super(). The signature of all __init__ functions is (self, *, **kw), with the variant that the arguments specifically wanted by this class are listed in a more discoverable way (rather than just popping them out of kw before calling super's init). It's therefore fine for MeatloversPizza to chain into CheesyCrustPizza. ChrisA -- https://mail.python.org/mailman/listinfo/python-list