git.net

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Property for dataclass field with default value


On Thu, 18 Jun 2020 at 11:26, Peter Otten <__peter__ at web.de> wrote:
>
> Ivan Ivanyuk wrote:
>
> > Hello All,
> >
> > I have some trouble using @dataclass together with @property decorator
> > or property() function.
> >
> > From the documentation and PEP is seems that the intended behaviour of
> > @dataclass is to be the same as normal __init__() that sets instance
> > variables.
> >
> > But it seems that when using @property decorator some parts work
> > differently when relying on default values. I'm using Pyhton 3.8.3 for
> > this.
> >
> > Using the code:
> >
> > from dataclasses import dataclass
> >
> > @dataclass
> > class Container:
> >     x: int = 30
> >
> >     @property
> >     def x(self) -> int:
> >         return self._x
> >
> >     @x.setter
> >     def x(self, z: int):
> >         if z > 1:
> >             self._x = z
> >         else:
> >             raise ValueError
> > c= Container(x=10)
> > print(c)
> > c= Container()
> > print(c)
> >
> > output is:
> >
> > Container(x=10)
> > Traceback (most recent call last):
> >   File
> >   "/Users/ivanivanyuk/Documents/Shared/repos/bitbucket/evbox/g5plus-
> automated-testing/dataclass_example.py",
> > line 18, in <module>
> >     c= Container()
> >   File "<string>", line 3, in __init__
> >   File
> >   "/Users/ivanivanyuk/Documents/Shared/repos/bitbucket/evbox/g5plus-
> automated-testing/dataclass_example.py",
> > line 13, in x
> >     if z > 1:
> > TypeError: '>' not supported between instances of 'property' and 'int'
> >
> > Code with __init__() being inserted that should be roughly the same as
> > generated by the @dataclass decorator:
> >
> > @dataclass
> > class Container:
> >     x: int = 30
> >
> >     def __init__(self, x:int = 30):
> >         self.x = x
> >
> >     @property
> >     def x(self) -> int:
> >         return self._x
> >
> >     @x.setter
> >     def x(self, z: int):
> >         if z > 1:
> >             self._x = z
> >         else:
> >             raise ValueError
> >
> > c= Container(x=10)
> > print(c)
> > c= Container()
> > print(c)
> >
> > output is:
> > Container(x=10)
> > Container(x=30)
> >
> > As far as I can see, this happens because actual __init__() generated
> > by the @dataclass decorator looks like:
> >
> >     def __init__(self, x:int = <property object at 0x106655db0>):
> >         self.x = x
> >
> > I came up with a really convoluted way to work around it (just for
> > fun, as this clearly defies the idea of dataclasses as a way to
> > decrease boilerplate code):
> >
> > from dataclasses import dataclass, field
> >
> > def set_property():
> >     Container.x = property(Container.get_x, Container.set_x)
> >     return 30
> >
> > @dataclass
> > class Container:
> >     x: int = field(default_factory=set_property)
> >
> >     def get_x(self) -> int:
> >         return self._x
> >
> >     def set_x(self, z: int):
> >         if z > 1:
> >             self._x = z
> >         else:
> >             raise ValueError
> >
> > c= Container(x=10)
> > print(c)
> > c= Container()
> > print(c)
> >
> > output is:
> > Container(x=10)
> > Container(x=30)
> >
> > So, what I'm missing here? Is there some way to use field() or
> > decorators to make property just work the same as in non-decorated
> > class?
>
> Your class definition is basically
>
> class Container:
>     x = default_value
>     x = property_x
>
> i. e. you use the same name twice. A possible workaround might be to define
> the property in a subclass. That way you get distinct namespaces for the
> default value and the property:
>
> @dataclass
> class ContainerBase:
>     x: int = 42
>
> class Container(ContainerBase):
>     @property
>     def x(self):
>         return self._x
>
>     @x.setter
>     def x(self, value):
>         if value <= 1:
>             raise ValueError
>         self._x = value
>
>
> --
> https://mail.python.org/mailman/listinfo/python-list

Didn't think about it in such a way! But now that it was pointed, I
see no obvious way to resolve this in dataclass decorator given the
way it's implemented now. And while adding another class looks easy it
somewhat detracts from the dataclasses' purpose of removing
boilerplate.

Does it seems like a good idea to ask for documenting that behaviour
in dataclasses documentation or it's not popular enough use case?

Regards,
Ivan