[Python-ideas] Keyword for direct pass through of kwargs to super

Michael Lohmann mial.lohmann at gmail.com
Sun May 27 04:42:08 EDT 2018


> 	If I understand correctly, the essence of your argument seems to be that you want be able to write a class A, and you want to be able to use that class EITHER as the top of an inheritance chain (i.e., have it inherit directly from object) OR in the middle of an inheritance chain (i.e., inheriting from some other class, but not object).
Well, it does not necessarily inherit from object, but from any class, that does not accept any more kwargs. E.g.  if you have a diamond structure as your class-hierachy then one branch could forward information to the second (if you understand what I mean?).

> But I don't really see how your solution of magically making kwargs appear and disappear is a good solution to that problem.

I intended the following text for the python-list mailing-list, but it I think I might have structured my ideas a bit better than in my previous messages and the summary in __Reason__ might tackle why this could be a nice idea (spoiler: make super even more super by doing things if it is NOT there in a clever manner).


Let us start with a simple class:
    class Aardvark:
        def __init__(self, quantity):
            print("There is some quantity:", quantity)


Well, that works nicely and we can go and send our Aardvark to survey some quantities. But if we now want to get the multi-inheritance to work properly we need to change it to:

    class Aardvark:
        def __init__(self, quantity, **kwargs):
            print("There is some quantity:", quantity)
            # I actually don’t care about **kwargs and just hand them on
            super().__init__(**kwargs)

    class Clever:
        def __init__(self, cleverness=1):
            print("You are %d clever“ % cleverness)

    class Ethel(Aardvark, Clever):
        """Ethel is a very clever Aardvark"""
        def __init__(self):
            super().__init__(quantity="some spam", cleverness=1000)

But if you now look at the declaration of the Aardvark .__init__, it seems like you could instantiate it with **kwargs. This in fact is not true. As soon as you create a direct instance of Aardvark, `object` as the super() doesn’t accept any kwargs. So basically I think that the parameters for the init should just say `quantity ` while still preserving the functionality.

Now that obviously doesn’t work until now. But could you add something that lets this class tell the interpreter instead: "Hey, could you just forward anything that this init doesn’t need to super().__init__" ? I have something like this in mind:

    class Aardvark:
        @bypass_kwargs_to_super
        def __init__(self, *, quantity):
            print("There is some quantity:", quantity)
            super().__init__()

This would collect everything "behind the scenes" that usually **kwargs would have collected and "append" it to the super call. Question: If Aardvark knows that he really does not need the kwargs himself: why give them to him in the first place? I mean, I trust him - he seems like a very nice guy, but he might have accidentally forgotten to forward them unintentionally.

You would obviously still be able to use **kwargs the usual way but then you couldn’t use this technique and would need to take care of passing down all the information to super() yourself as usual.


__Reason__: With something like this it is immediately obvious that Aardvark ONLY takes `quantity` as an input if you instantiate it directly but if subclassed it is able to hand information down the MRO to something from a different "Branch". And in addition to the better human readability: any form of automated docstring-genaration now can be certain that you don’t do anything with (or to) the **kwargs (since the init doesn’t get them in the first place). In addition it could make super even more super by doing something if it is NOT there as proposed in the second of the the following problems.

Of course you need to be quite clever to do this properly, for example
1) What to do if there are collisions between the bypassed kwargs and the ones from the init call? - Probably keep the ones you bypassed since they come from the top of the MRO
2) What do you do if super().__init__ was not called? The most clever thing would be to go „up and to the next branch“ of the inheritance diagram. As in: if Aardvark is a Subclass of Animal, don’t call its init but directly Clevers - (you would have to look up the MRO of the super of Aardvark and skip them in the Ethel MRO before calling the next init automatically).

In theory you could also add something that forwards *args as well but the usage of that is probably much more limited...

On thing that might look a bit strange is that you actually could actually pass in additional kwargs despite the init saying otherwise:
`Aardvark(quantity='some spam', something="Object will throw an error now unexpected kwarg")`
and this would then throw an error that "object (instead of Aardvark) does not expect something". But I guess that would be okay since the init now is much better readable of what it actually expects.

Michael


More information about the Python-ideas mailing list