[issue39247] dataclass defaults and property don't work together

Thomas report at bugs.python.org
Thu Oct 21 12:49:32 EDT 2021


Thomas <thomas.d.mckay at gmail.com> added the comment:

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 ?

----------
nosy: +Thomas701

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue39247>
_______________________________________


More information about the Python-bugs-list mailing list