[Python-Dev] Second post: PEP 557, Data Classes (original) (raw)

Eric V. Smith eric at trueblade.com
Mon Nov 27 07:23:48 EST 2017


On 11/27/2017 6:01 AM, Sebastian Rittau wrote:

On 25.11.2017 22:06, Eric V. Smith wrote:

The major changes from the previous version are:

- Add InitVar to specify initialize-only fields. This is the only feature that does not sit right with me. It looks very obscure and "hacky". From what I understand, we are supposed to use the field syntax to define constructor arguments. I'd argue that the name "initialize-only fields" is a misnomer, which only hides the fact that this has nothing to do with fields at all. Couldn't dataclassses just pass *args and **kwargs to postinit()? Type checkers need to be special-cases for InitVar anyway, couldn't they instead be special cased to look at postinit argument types?

First off, I expect this feature to be used extremely rarely. I'm tempted to remove it, since it's infrequently needed and it could be added later. And as the PEP points out, you can get most of the way with an alternate classmethod constructor.

I had something like your suggestion half coded up, except I inspected the args to post_init() and added them to init, avoiding the API-unfriendly *args and **kwargs.

So in: @dataclass class C: x: int y: int

 def __post_init__(self, database: DatabaseType): pass

Then the init signature became:

def init(self, x:int, y:int, database:DatabaseType):

In the end, that seems like a lot of magic (but what about this isn't?), it required the inspect module to be imported, and I thought it made more sense for all of the init params to be near each other:

@dataclass class C: x: int y: int database: InitVar[DatabaseType]

 def __post_init__(self, database): pass

No matter what we do here, static type checkers are going to have to be aware of either the InitVars or the hoisting of params from post_init to init.

One other thing about InitVar: it lets you control where the init-only parameter goes in the init call. This is especially important with default values:

@dataclass class C: x: int database: InitVar[DatabaseType] y: int = 0

 def __post_init__(self, database): pass

In this case, if I were hoisting params from post_init to init, the init call would be:

def init(self, x, y=0, database)

Which is an error. I guess you could say the init-only parameters would go first in the init definition, but then you have the same problem if any of them have default values.

Eric.



More information about the Python-Dev mailing list