Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to use a dataclass, with fields with defaults, with __slots__

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?

like image 849
Gary van der Merwe Avatar asked Dec 14 '18 08:12

Gary van der Merwe


People also ask

Does Dataclasses use slots?

As noted already in the answers, data classes from dataclasses cannot generate slots for the simple reason that slots must be defined before a class is created.

What is __ slots __ in python?

__slots__ is a class variable. If you have more than one instance of your class, any change made to __slots__ will show up in every instance. You cannot access the memory allocated by the __slots__ declaration by using subscription. You will get only what is currently stored in the list.

Can Dataclass have methods?

A dataclass can very well have regular instance and class methods. Dataclasses were introduced from Python version 3.7. For Python versions below 3.7, it has to be installed as a library.

How does Dataclass work in python?

DataClass in Python DataClasses are like normal classes in Python, but they have some basic functions like instantiation, comparing, and printing the classes already implemented. Parameters: init: If true __init__() method will be generated. repr: If true __repr__() method will be generated.


1 Answers

There is, by using the @add_slots decorator by ericvsmith:

import dataclasses

def 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_names
    for 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__ = qualname
    return 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

like image 157
L3viathan Avatar answered Oct 19 '22 18:10

L3viathan