Переопределение атрибута, отличного от класса данных, подклассом класса данных вызывает ошибку атрибута

Учитывая следующую модель данных

from typing import List    

class A:
    a: List[int] = []

class B(A):
    def __init__(self, b: str, a: List[int] = []):
        self.b = b
        self.a = a

Факты

  • Это Евангелие, которое A не может быть dataclass (это сделало бы проблему тривиальной)
  • Мы хотим наследовать A через B
  • Мы не хотим иметь возможность устанавливать параметр a при создании экземпляра A
  • Мы хотим иметь возможность устанавливать параметр a при создании экземпляра B

Следующее, что я предположил, будет работать, было

from typing import List
from dataclasses import dataclass, field


class A:
    a: List[int] = []

@dataclass
class B(A):
    b: str
    a: List[int]

Исправление ошибки ValueError: mutable default <class 'list'> for field babies is not allowed: use default_factory Получаю

from typing import List
from dataclasses import dataclass, field


class A:
    a: List[int] = field(default_factory=list)

@dataclass
class B(A):
    b: str
    a: List[int]

но это приводит к следующей ошибке AttributeError: a

Если вместо этого я использую целочисленный тип для a, сработает следующее, показывая, что теоретически то, что я делаю, нормально, но я выражаю это неправильно:

from typing import List
from dataclasses import dataclass, field

class A:
    a: int = 1

@dataclass
class B(A):
    b: str
    a: int

Что я здесь делаю не так? Как мне заставить это работать с a как пустым списком в B


person Alexander McFarlane    schedule 16.12.2020    source источник


Ответы (1)


Я цитирую отрывок из модуля dataclasses, который вызывает ошибку (функция _process_class):

    # If the class attribute (which is the default value for this
    # field) exists and is of type 'Field', replace it with the
    # real default.  This is so that normal class introspection
    # sees a real default value, not a Field.
    if isinstance(getattr(cls, f.name, None), Field): 
        if f.default is MISSING:
            # If there's no default, delete the class attribute.
            # This happens if we specify field(repr=False), for
            # example (that is, we specified a field object, but
            # no default value).  Also if we're using a default 
            # factory.  The class attribute should not be set at
            # all in the post-processed class.
            delattr(cls, f.name) 
        else:   
            setattr(cls, f.name, f.default)

Я думаю, что комментарии показывают, что реализация не ожидает, что она должна обрабатывать унаследованный атрибут. Я думаю, это означает, что наследовать можно только обработанные атрибуты, т.е. они должны происходить из базовых классов данных.

person VPfB    schedule 16.12.2020
comment
это, кажется, означает, что целочисленный пример является непреднамеренным поведением? Я заметил, что замена значения поля по умолчанию также нарушает целочисленный пример, как следует из приведенного выше фрагмента. - person Alexander McFarlane; 16.12.2020
comment
@AlexanderMcFarlane Важное отличие состоит в том, что в целочисленном примере не используется Field. С моей личной точки зрения dataclass - это генератор кода (похожее видео: youtube.com / watch? v = T-TwcmT6Rcw), а способ, которым вы его используете, выходит за рамки его спецификации, поэтому результаты не определены (это нормально для обучения). Автор dataclasses является пользователем SO и может дать авторитетный ответ. - person VPfB; 16.12.2020