Property for dataclass field with default value
Peter Otten
__peter__ at web.de
Thu Jun 18 05:22:22 EDT 2020
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
More information about the Python-list
mailing list