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.

Author Thomas701
Recipients Michael Robellard, Thomas701, UnHumbleBen, eric.smith, iivanyuk, juanpa.arrivillaga
Date 2021-10-21.16:49:32
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1634834972.21.0.317685045819.issue39247@roundup.psfhosted.org>
In-reply-to
Content
Hello everyone,

A quick look on SO and Google + this python issue + this blog post and its comments: https://florimond.dev/en/posts/2018/10/reconciling-dataclasses-and-properties-in-python/ show that this is still a problem where dataclass users keep hitting a wall.

The gist here seems to be that there's two ways to solve this:
- have descriptor be treated differently when found as default value in the __init__. I like this solution. The argument against is that users might want to have the descriptor object itself as an instance attribute and this solution would prevent them from doing it. I'd argue that, if the user intention was to have the descriptor object as a default value, the current dataclass implementation allows it in a weird way: as shown above, it actually sets and gets the descriptor using the descriptor as its own getter/setter (although it makes sense when one thinks of how dataclass are implemented, specifically "when" the dataclass modifies the class, it is nonetheless jarring at first glance).

- add an "alias/name/public_name/..." keyword to the field constructor so that we could write _bar: int = field(default=4, alias="bar"). The idea here keeps the usage of this alias to the __init__ method but I'd go further. The alias should be used everywhere we need to show the public API of the dataclass (repr, str, to_dict, ...). Basically, if a field has an alias, we only ever show / give access to the alias and essentially treat the original attribute name as a private name (i.e.: if the dataclass maintainer changes the attribute name, none of the user code should break).

I like both solutions for the given problem but I still have a preference for the first, as it covers more cases that are not shown by the example code: what if the descriptor doesn't delegate to a private field on the class? It is a bit less common, but one could want to have a field in the init that delegates to a resource that is not a field on the dataclass. The first solution allows that, the second doesn't.

So I'd like to propose a variation of the first solution that, hopefully, also solves the counter argument to that solution:

@dataclass
class FileObject:
    _uploaded_by: str = field(init=False)

    @property
    def uploaded_by(self):
        return self._uploaded_by

    @uploaded_by.setter
    def uploaded_by(self, uploaded_by):
        print('Setter Called with Value ', uploaded_by)
        self._uploaded_by = uploaded_by

    uploaded_by: str = field(default=None, descriptor=uploaded_by)


Basically, add an argument to the field constructor that allows developers to tell the dataclass constructor that this field requires special handling: in the __init__, it should use the default value as it would do for normal fields but at the class level, it should install the descriptor, instead of the default value.

What do you think ?
History
Date User Action Args
2021-10-21 16:49:32Thomas701setrecipients: + Thomas701, eric.smith, Michael Robellard, juanpa.arrivillaga, iivanyuk, UnHumbleBen
2021-10-21 16:49:32Thomas701setmessageid: <1634834972.21.0.317685045819.issue39247@roundup.psfhosted.org>
2021-10-21 16:49:32Thomas701linkissue39247 messages
2021-10-21 16:49:32Thomas701create