This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: dataclass: always generate default __init__ on __default_init__
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: eric.smith Nosy List: Shmuel H., eric.smith
Priority: normal Keywords:

Created on 2019-10-11 10:39 by Shmuel H., last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (7)
msg354437 - (view) Author: Shmuel H. (Shmuel H.) Date: 2019-10-11 10:39
Currently, `dataclasses.dataclass` will generate `__init__` only where the user has not defined one. 

However, sometimes, with frozen classes or dataclasses with a lot of members, redefinition of this function is not trivial,
especially if the only purpose is to change the default behaviour for only one member:
```python
from dataclasses import dataclass

@dataclass(frozen=True)
class Dataclass:
    #...big list of members
    member20: int
    
    def __init__(self, member20: str, **kwargs):
        # self.member20 = int(member20)
        object.__setattr__(self, "member20", int(member20))
        # Now we have to trivially initialize 
        # 20 other members like that :[
```
My idea is to generate the default `__init__` into `__default_init__` even, if the user has defined their own version.
That will allow them to use it like that:
 ```python
from dataclasses import dataclass

@dataclass(frozen=True)
class Dataclass:
    #...big list of members
    member20: int
    
    def __init__(self, member20: str, **kwargs):
        # Oh, that's better :)
        self.__default_init__(member20=int(member20), **kwargs)
```

Implementing that is pretty trivial (I can do that if this change will be approved). 
Please let me know what you think about that.
msg354438 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-10-11 10:42
This would be a 3.9 feature only, so changing the versions.
msg354441 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-10-11 11:18
Doesn't __post_init__ address this use case?
msg354458 - (view) Author: Shmuel H. (Shmuel H.) Date: 2019-10-11 14:19
I think it was designed to. However, it is not very usable in production for a number of reasons:
1. It won't work with frozen instances (you'll have to call `object.__setattr__` directly).
2. It gets very messy with more than one or two `InitVar`s which makes it very hard to differentiate between "real"
 values, `InitVar`s and the init logic:
```python
from dataclasses import dataclass, InitVar
@dataclass
class DataClass:
    member0_init: InitVar[str] = None
    member1_init: InitVar[list] = None

    member0: int = None
    member1: dict = None

    def __post_init__(self, member0_init: str, member1_init: list):
        if member0_init is not None and self.member0 is None:
            self.member0 = int(member0_init)
        if member1_init is not None and self.member1 is None:
            self.member1 = dict(member1_init)
```
That code should be equivalent to:
```python
from dataclasses import dataclass
from typing import Union
@dataclass
class DataClass:
    member0: int
    member1: dict

    def __init__(self, member0: Union[int, str], member1: Union[dict, list]):
        if isinstance(member0, str):
            member0 = int(member0)
        if isinstance(member1, list):
            member1 = dict(member1)

        self.__default_init__(member0=member0, member1=member1)
```
Which is much closer to regular python code to someone new for dataclasses.

I would be happy to hear if you have a better solution; I just think it is pretty simple and straight-forward.
msg354459 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-10-11 14:33
I'll try and think of something better.

I know I'm guilty of sending to python-ideas all the time, but discussing proposed design decisions like this are what that list is all about.

If you want to bring it up there, I'd focus on making the problem description crystal clear, and not so much on any proposed solution. At least for starters.

And, someone will ask what attrs or other projects do to solve this problem, so you should research that.
msg354472 - (view) Author: Shmuel H. (Shmuel H.) Date: 2019-10-11 17:19
The only other solution I could think about was to change setattr's behaviour dynamically so that it would be valid to call it from frozen instance's `__init__`, but I think it is somehow even worst.

However, thanks for your help, I think we can close this one for now and I'll hopefully write that mail in the next day or two.                

As for other projects, I doubt I'll find any big projects that use frozen dataclasses internally, but I'll try my best to come with one.

(Only now I realize that your the one behind python's dataclasses, keep up the good work!).
msg354474 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-10-11 17:26
Thanks. I'm always looking for ways to make dataclasses easier to use, while keeping within the original goals. I'll close this for now.
History
Date User Action Args
2022-04-11 14:59:21adminsetgithub: 82625
2019-10-11 17:26:13eric.smithsetstatus: open -> closed
resolution: wont fix
messages: + msg354474

stage: resolved
2019-10-11 17:19:23Shmuel H.setmessages: + msg354472
2019-10-11 14:33:34eric.smithsetmessages: + msg354459
2019-10-11 14:19:47Shmuel H.setmessages: + msg354458
2019-10-11 11:18:11eric.smithsetmessages: + msg354441
2019-10-11 10:42:07eric.smithsetmessages: + msg354438
versions: - Python 3.7, Python 3.8
2019-10-11 10:41:31eric.smithsetassignee: eric.smith

nosy: + eric.smith
2019-10-11 10:39:38Shmuel H.create