Есть ли способ использовать класс данных с полями по умолчанию, с __slots__

Я хотел бы поместить __slots__ в класс данных с полями со значениями по умолчанию. Когда я пытаюсь это сделать, я получаю эту ошибку:

>>> @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

Есть ли способ добиться этого?


person Gary van der Merwe    schedule 14.12.2018    source источник


Ответы (1)


С помощью декоратора @add_slots от 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

Применение:

>>> @add_slots
... @dataclass
... class C:
...     __slots__ = ('x', 'y', )
...     x: int
...     y: int = 1

Добавление __slots__ вручную работает, если нет значений по умолчанию. Вы можете найти связанную с проблему Github здесь

person L3viathan    schedule 14.12.2018
comment
У меня не сработало, все равно выдает ту же ошибку. И если нет значений по умолчанию, вызовите ошибку «уже указаны слоты». - person antonavy; 08.12.2020
comment
Решение больше не работает в Python 3.9. Обычно вы можете просто использовать подкласс typing.NamedTuple, который использует слоты. - person L3viathan; 08.12.2020