I would like to put __slots__
on a dataclass with fields with defaults. When I try do that, I get this error:
>>> @dataclass
... class C:
... __slots__ = ('x', 'y', )
... x: int
... y: int = 1
...
Traceback (most recent call last):File "<input>", line 1, in <module>
ValueError: 'y' in __slots__ conflicts with class variable
Is there a way to achieve this?
There is, by using the @add_slots decorator by ericvsmith:
import dataclassesdef add_slots(cls):# Need to create a new class, since we can't set __slots__# after a class has been created.# Make sure __slots__ isn't already set.if '__slots__' in cls.__dict__:raise TypeError(f'{cls.__name__} already specifies __slots__')# Create a new dict for our new class.cls_dict = dict(cls.__dict__)field_names = tuple(f.name for f in dataclasses.fields(cls))cls_dict['__slots__'] = field_namesfor field_name in field_names:# Remove our attributes, if present. They'll still be# available in _MARKER.cls_dict.pop(field_name, None)# Remove __dict__ itself.cls_dict.pop('__dict__', None)# And finally create the class.qualname = getattr(cls, '__qualname__', None)cls = type(cls)(cls.__name__, cls.__bases__, cls_dict)if qualname is not None:cls.__qualname__ = qualnamereturn cls
Usage:
>>> @add_slots
... @dataclass
... class C:
... __slots__ = ('x', 'y', )
... x: int
... y: int = 1
Adding __slots__
manually works as long as there are no defaults. You can find related the Github issue here