0

I have been here:

and could not find a direct answer to why this simple code works fine...

class Vehicle:
    
    def __init__(self, wheels: int = 10):
        self.wheels = wheels # -> calls the setter method
    
    @property
    def wheels(self) -> int:
        print("getting wheels")
        return self._wheels
    
    @wheels.setter
    def wheels(self, wheels: int):
        print("setting wheels to", wheels)
        self._wheels = wheels

v = Vehicle() 
number = v.wheels # -> calls the getter method
print(number)

# last output: 10

...but this one does not (using dataclass):

from dataclasses import dataclass

@dataclass
class Vehicle:
    
    wheels: int = 10
    
    @property
    def wheels(self) -> int:
        print("getting wheels")
        return self._wheels
    
    @wheels.setter
    def wheels(self, wheels: int):
        print("setting wheels to", wheels)
        self._wheels = wheels

v = Vehicle() 
number = v.wheels
print(number)

# output: <property object at 0x000002042D70DDB0>

even when the official documentation of dataclass tells explicitly in the beginning that the decorator @dataclass adds exactly the __init__ method from the first code, i.e., this code:

@dataclass
class Vehicle:
    wheels: int = 10

should add this __init__:

def __init__(self, wheels: int = 10):
    self.wheels = wheels

Is that really a bug?


Short Note:

The private attribute _wheels is accessed only inside the property's methods, as it should be (to isolate it).

I found in others threads (listed above) that this attribute is manipulated outside the methods, exposed as 'public', which is not desired in some cases.

Diogo
  • 590
  • 6
  • 23

1 Answers1

1

That's a bug in your code.

This code:

wheels: int = 10

sets wheels to 10, then this code immediately after:

@property
def wheels(self) -> int:
    print("getting wheels")
    return self._wheels

sets wheels to a property instance.

@dataclass can't see the 10. Your 10 is gone. The annotation remains, so @dataclass creates a wheels field, but your field's default value is a property instance, not 10.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Ok, I understood. So, just to make it clear, is this affirmation correct?: `@dataclass` does not adds the `__init__` method described above (which signature's is using the default value with annotation), but actually it adds a simillar to this one: `def __init__(self, wheels: int = Vehicle.wheels):`. – Diogo Mar 30 '22 at 16:49
  • 1
    @Diogo: Pretty much. `@dataclass` has no opportunity to see the `10`. (Note that the default is evaluated during execution of the dataclass decorator, so it won't pick up any further changes to `Vehicle.wheels` after decorator execution.) – user2357112 Mar 30 '22 at 16:52
  • Ok, thankyou for clarify. I think that should be more clear in the documentation ... – Diogo Mar 30 '22 at 17:18
  • I came back here to say that, actually, this "issue" is in a recente discussion: https://bugs.python.org/issue39247 . There are some workarounds, but I think this remains as an issue in the relation `dataclass` vs `properties` – Diogo Apr 01 '22 at 04:10
  • This [other thread](https://mail.python.org/pipermail/python-list/2020-June/897506.html) also expose that. – Diogo Apr 01 '22 at 12:46