Dataclasses and slots causing ValueError: 'b' in __slots__ conflicts with class variable

Question:

I don’t understand the error message and also couldn’t find other SO questions and answers helping me to understand this. The MWE is tested with Python 3.9.2. I’m aware that there is a slots=True parameter in Pythons 3.10 dataclasses. But this isn’t an option here.

The error output:

Traceback (most recent call last):
  File "/home/user/dc.py", line 6, in <module>
    class Foo:
ValueError: 'b' in __slots__ conflicts with class variable

Why does that happen? I even don’t understand the background of that error.

#!/usr/bin/env pyhton3
from dataclasses import dataclass, field


@dataclass(init=False)
class Foo:
    __slots__ = ('a', 'b')
    a: int
    b: list[str] = field(init=False)

    def __init__(self, a, ccc):
        self.a = a

        # b = _some_fancy_modifications(ccc)

        self.b = b


if __name__ == '__main__':
    f = Foo(1, list('bar'))

The member b is not given as an argument of __init__() but computed based on the argument ccc. Because of that I think I need to write my own __init__() (@dataclass(init=False)) and the b member shouldn’t be initialized by dataclass (field(init=False)). Maybe I misunderstood something here?

Asked By: buhtz

||

Answers:

This is not related to dataclasses. A slot is realized as a class variable of an internal type "member_descriptor".

Try:

class Foo:
    __slots__ = ('a', 'b')

print(repr(Foo.a))
print(repr(Foo.a.__class__))

Output:

<member 'a' of 'Foo' objects>
<class 'member_descriptor'>
Answered By: Michael Butscher

For __slots__ to work, Python has to insert special descriptor objects into the class dict to manage attribute access. Otherwise, since the attributes aren’t stored in the instance dict, attribute lookup would be completely unable to find your attributes. Descriptors tell the attribute lookup machinery how to find special attributes.

For the b slot to work, Python has to insert a descriptor for that attribute corresponding to the 'b' key in your class dict. But you’ve already put something there: the field object you created. You can’t have a field object and a slot descriptor as values for the same key. Python recognizes that this is a problem, and raises an error to tell you that you need to do something about this.

This isn’t really something you can fix on your end. __slots__ support for dataclasses needs changes to the dataclass machinery, changes that happened in 3.10. In 3.9, you’re stuck not using slots with dataclasses.

Answered By: user2357112