Класс данных Python: можете ли вы установить значения по умолчанию для полей? - PullRequest
3 голосов
/ 25 октября 2019

Я хочу создать базовый класс класса данных, в котором все поля в подклассах автоматически становятся необязательными и по умолчанию имеют значение «Нет» (если не задано значение по умолчанию).

Следующий код ... похоже, делает то, что яхочу, но не совсем. Он выдает ошибку так же, как если бы я никогда не писал __init_subclass__ (т.е. жалуется на незаполненные параметры) ... возможно, потому что мой код работает после происходит волшебство класса данных?

@dataclass(order=True, frozen=True)
class BaseDictKey:
    def __init_subclass__(cls, *args, **kwargs):
        super().__init_subclass__(*args, **kwargs)
        # noinspection PyUnresolvedReferences
        for field in cls.__dataclass_fields__.values():
            field.default = None if field.default is None else field.default
            field.type = typing.Union[field.type, NoneType]

@dataclass(order=True, frozen=True)
class ScoreDictKey(BaseDictKey):
    catalog: str  # magically becomes catalog: Optional[str] = None
    dataset: str = 'foo'  # magically becomes dataset: Optional[str] = 'foo'

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

Возможно ли это в Python 3.7 +?

1 Ответ

1 голос
/ 25 октября 2019

Я нашел способ изменить поле класса __annotations__, чтобы сделать поля необязательными, и установить атрибуты непосредственно в классе для предоставления значения по умолчанию None:

from dataclasses import dataclass

import typing


@dataclass(order=True, frozen=True)
class BaseDictKey:
    def __init_subclass__(cls, *args, **kwargs):
        for field, value in cls.__annotations__.items():
            cls.__annotations__[field] = typing.Union[value, None]
            if not hasattr(cls, field):
                setattr(cls, field, None)
        super().__init_subclass__(*args, **kwargs)


@dataclass(order=True, frozen=True)
class ScoreDictKey(BaseDictKey):
    catalog: str  # magically becomes catalog: Optional[str] = None
    dataset: str = 'foo'  # magically becomes dataset: Optional[str] = 'foo'

    def __init__(self, *args, **kwargs):  # to get rid of PyCharm warning
        pass


c = ScoreDictKey()
print(c.catalog, c.dataset)  # None foo
...