I'm just curious:  what is the downside of calling super with kwargs?
Usually when I define a class, the first I write is

def __init__(self, **kwargs):
    super().__init__(**kwargs)

just in case I want to use the class in cooperative inheritance.  I always
thought it couldn't hurt?

I might be alone in this interpretation, but I imagine that there are three
fundamental kinds of inheritance patterns for methods, which I defined in
my ipromise package (https://pypi.org/project/ipromise/): implementing an
abstract method, overriding, and augmenting.  If I had to choose, I would
say that __init__ should be an "augmenting" pattern.

Therefore, it seems weird for __init__ not to call super.  Even if you want
Y t to override some behavior in X, what happens if Z inherits from Y and
W?  Now, Y.__init__'s decision not to call super would mean that W.__init__
would not be called.  That seems like a bug.  Instead, I would rather put
the behavior that Y wants to override in a separate method, say X.f, which
is called in X.__init__.  Now, if Y.f overrides X.f, everything is okay.
Even if Z inherits from Y and W, the override still works, and W.__init__
still gets called, angels sing, etc.

Long story short, am I wrong to interpret __init__ as a "must augment"
method and always call super().__init__(**kwargs)?

On Wed, Apr 15, 2020 at 1:32 PM Andrew Barnert <abarn...@yahoo.com> wrote:

> On Apr 15, 2020, at 04:26, Ricky Teachey <ri...@teachey.org> wrote:
>
> 
>
>> For simple situations you can call super in the __post_init__ method and
>>> things will work fine:
>>>
>>
>> But not for the OP's case: he wanted to pass extra parameters in -- and
>> the dataclass' __init__ won't accept extra arguments.
>>
>>
>> Can’t you just create InitVar attributes for the extra args you want to
>> pass through in that case?
>>
>
> InitVar fields for all the desired parent class init parameters can often
> solve the problem.
>
> But it can be painful to have to manually provide every parameter
> explicitly when normally (when not using a dataclass) you'd just add *args
> and **kwargs to the init signature and call super().__init__(*args,
> **kwargs).
>
>
> To handle that case, couldn’t we just add InitVarStar and InitVarStarStar
> fields? If present, any extra positional and/or keyword args get captured
> for the __postinit__ to pass along just like normal InitVars.
>
> I think that could definitely be useful, but not as useful as you seem to
> think it would be.
>
> It becomes more painful the more parameters the parent has- parameters
> which the dataclass may not even care about. It not only makes the class
> definition long, it adds so these additional parameters to the init
> signature, which is icky for introspection and discoverability. Lots of
> "What the heck is this parameter doing here?" head scratching for future me
> (because I forget everything).
>
>
> I think that’s backward. The signature is there for the user of the
> dataclass, not the implementer. And the user had better care about that x
> argument, because it’s a mandatory parameter of the X class, so if they
> don’t pass one, they’re going to get an exception from inside some class
> they never heard of. So having x show up in the signature would be helpful
> for introspection and discovery, not harmful. It makes your users ask “What
> the heck is the x parameter doing here?” but that’s a question that they’d
> better have an answer to or they can’t construct a Y instance. (And notice
> that the X doesn’t take or pass along *args, so if the Y claims to take
> *args as well as **kwargs, that’s even more misleading, because passing any
> extra positional args to the constructor will also raise.) And that’s as
> true for tools as for human readers—an IDE auto-completing the parameters
> of Y(…) should be prompting you for an x; a static analyzer should be
> catching that you forgot to pass as x; etc.
>
> There are cases where you need *args and/or **kwargs in a constructor. But
> I don’t think they make sense for a dataclsss. For example, you’re not
> going to write a functools.partial replacement or a generic RPC proxy
> object as a dataclass.
>
> But there are cases where you _want_ them, just out of… call it
> enlightened laziness. And those cases do seem to apply to dataclass at
> least as much as normal classes. And that’s why I think it could be worth
> having these new fields. The OP’s toy example looks like part if a design
> where the X is one of a bag of mixins from some library that you compose up
> however you want in your app.
>

You got it.


> It would be more discoverable and readable if the final composed Y class
> knew which parameters its mixins demanded—but it may not be worth the
> effort to put that together (either manually, or programmatically at class
> def time). If your Y class is being exposed as part of a library (or RPC or
> bridge or whatever), or it forms part of the connection between two key
> components in an air traffic control system, then you probably do want to
> put in that effort. If it’s an internal class that only gets constructed in
> one place, in a tool for trolling each other with bad music in the office
> stereo that only you and three colleagues will ever run, why do the extra
> work to get earlier checking that you don’t need? The fact that Python
> leaves that kind of choice up to you to decide (because you’re the only one
> who knows), and so do most pythonic libraries like the one you got that X
> mixin out of… that’s a big part of why you wrote that script in Python in
> the first place. And if dataclasses get in the way of that, it’s a problem,
> and probably worth fixing.
>
>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/BTHZWXO3PYIMT7JLIY4Z7VFP7UXPX2JD/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to