I have a dataclass with (kind of) a getter method.
This code works as expected:
from dataclasses import dataclass
@dataclass()
class A:
def get_data(self):
# get some values from object's fields
# do some calculations
return "a calculated value"
@dataclass()
class B(A):
def get_data(self):
data = super().get_data()
return data + " (modified)"
b = B()
print(b.get_data()) # a calculated value (modified)
However, if I add slots=True
, I get a TypeError
:
from dataclasses import dataclass
@dataclass(slots=True)
class A:
def get_data(self):
return "a calculated value"
@dataclass(slots=True)
class B(A):
def get_data(self):
data = super().get_data()
return data + " (modified)"
b = B()
print(b.get_data()) # TypeError: super(type, obj): obj must be an instance or subtype of type
The error vanishes if I use the old-style super(), contrary to pep-3135:
from dataclasses import dataclass
@dataclass(slots=True)
class A:
def get_data(self):
return "a calculated value"
@dataclass(slots=True)
class B(A):
def get_data(self):
data = super(B, self).get_data()
return data + " (modified)"
b = B()
print(b.get_data()) # a calculated value (modified)
Why does this happen and how to fix it the right way?
slots
: If true (the default isFalse
),__slots__
attribute will be generated and new class will be returned instead of the original one. If__slots__
is already defined in the class, thenTypeError
is raised.https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass
Taking this as an example:
@dataclass(slots=True)
class Foo:
pass
This means this works something like this:
class Foo:
pass
Foo = dataclass(slots=True)(Foo)
You define a class Foo
, and then it gets replaced with a different, altered class.
Now, your method:
def get_data(self):
data = super().get_data()
...
This super()
was written in the original Foo
class and will assume it's supposed to look up the parent of this original Foo
class; but the instance it currently has is not actually an instance of that class, it's an instance of the other, altered class.
When you do this instead:
data = super(Foo, self).get_data()
This looks up what currently refers to the name Foo
, which again matches what self
also refers to at this moment.