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