On Wed, Apr 15, 2020 at 3:36 PM Eric V. Smith <eric@trueblade.com> wrote:
In general, it's not possible to know how to call super.__init__() if you don't a priori know the arguments it takes. That's why dataclasses doesn't guess. The only general-purpose way to do it is to use *args and **kwargs.

I was about to make this same point, thanks. super.__init__() absolutely should NOT be called by default in an auto-generated __init__.

However, dataclasses, could, optionally, take *args and **kwargs, and store them in a instance attribute. Then you could call super(), or anything else, in __post_init__. And there are other reasons to want them to take arbitrary other parameters (like to ignore them, see the PR).
Since a goal of dataclasses is to use good typing information that works with type checkers, that seems to defeat part of the purpose.

I have never used a type checker :-) -- but I still really dislike the passing around of *args, **kwargs -- it makes your API completely non-self documented. So I'll agree here.

One thing you could do in this scenario is to use a classmethod as an "alternate constructor". This basically gives you any post- and pre- init functionality you want, using any regular parameters or *args and **kwargs as fits your case.

That's a nice way to go -- I suggested a similar way by adding another layer of subclassing. Using a classmethod is cleaner, but using another layer of subclassing allows you to keep the regular __init__ syntax -- and if you want to make these classes for others to use, the familiar API is a good thing.

I do wonder if we could have a __pre_init__ -- as a way to add something like this classmethod, but as a regular __init__. Butnow that I think about it, here's another option: if the user passes in init=False, (or maybe some other value), then the __init__ could still be generated, and accesable to the user's __init__. It might look something like this (to follow your example):

from dataclasses import dataclass

class BaseClass:
    def __init__(self, x, y):
        print('initializing base class', x, y)
        self.x = x
        self.y = y

class MyClass(BaseClass):
    a: int
    b: int
    c: int
    def __init__(self, a, b, c, x, y):
        super.__init__(x, y)
        #  _ds__init__ would be the auto-generated __init__
        self._ds__init__(self, a, b, c)
        return self

c = MyClass.new(1, 2, 3, 10, 20)
print(c.x, c.y)

> To Brett's point: has anyone looked to see what attrs does here? When last I looked, they didn't call any super __init__, but maybe they've added something to address this.

"Please note that attrs does not call super() *ever*." (emphasis theirs)

So that's that. :-)

I also did see this: (from https://www.attrs.org/en/stable/init.html):
"Embrace classmethods as a filter between reality and what’s best for you to work with"
"Generally speaking, the moment you think that you need finer control over how your class is instantiated than what attrs offers, it’s usually best to use a classmethod factory..."

Which seems to support your classmethod idea :-)

But I still think that it would be good to have some kin dof way to customise the __init__ without re-writting the whole thing, and/or have a way to keep *args,**kwargs around to use in __post_init__

Maybe a gitHub repo just for dataclasses?

@Eric V. Smith: what do you think? Is there a way to keep them moving forward?

I think it's fine to make suggestions and have discussions here. Dataclasses aren't sufficiently large that they need their own repo (like tulip did, for example).

Sure -- and go to know you're monitoring this list (and no, I don't expect instant responses) But it's just that I've seen at least one idea (the **kwargs one) kind of wither and die, rather than be rejected (at least not obviously) -- which maybe would have happened anyway.

But I also don't think we're going to just add lots of features to dataclasses. They're meant to be lean.

As they should be.

I realize drawing the line is difficult. For example, I think asdict and astuple were mistakes, and should have been done outside of the stdlib.

I agree there -- I've found I needed to re-implement asdict anyway, as I needed something a little special. But that's only possible because dataclasses already have the infrastructure in place to do that.

So I think any enhancements should be to allow third-party extensions, rather than actual new functionality. In this case, yes, folks can use an alternate constructor, but there is no way to get other arguments passed through an auto-generated__init__ -- so there are various ways that one cannot extend dataclasses -- I'd like to see that additional feature, to enable various third-party extensions.


Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython