git.net

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

Property for dataclass field with default value


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?

Best regards,
Ivan Ivanyuk