Как стать автором
Обновить

Комментарии 6

mypy меня не особо интересует, потому что там в самом mypy ворох нерешённых проблем, взять хотя бы рекурсивные типы. Если следовать всем канонам mypy, код на Python визуально превращается в нечитабельную кашу из монстроузных аннотаций типов, каких-то бессмысленных Generic, TypeVar и т. д. при всём при этом он остаётся всё таким же динамически типизированным и таким же медленным, только писать и читать его становится дольше и скучнее. Я уж лучше на Rust тогда писать буду с нормальной системой типов, безопасностью памяти, высокой производительностью и т.д., чем буду тащить весь этот "типизированный" мусор в Python и мучиться с mypy. Более того, огромное кол-во популярных библиотек вообще плевать хотели на mypy и их приходится добавлять в игнорируемые в конфиге, что сводит на нет хоть какой-то профит от mypy.


Поэтому я использую аннотации типов в Python исключительно для документирования, помощи IDE для построения модели кода и в pydantic/FastAPI.


А pydantic (и ругаемый вами FastAPI) берет лучшее от аннотаций типов и позволяет решать с помощью них реальные практические задачи.


К pydantic у меня претензии не к необязательному Optional, а к тому, что там Strict типы не используются по умолчанию. Об этом у них кстати тоже висит issue.

каких-то бессмысленных Generic, TypeVar и т. д.

Ну, лично для меня они не бессмысленны, а очень сильно упрощают чтение кода, особенно чужого.


огромное кол-во популярных библиотек вообще плевать хотели на mypy

Это дело времени, со временем многие библиотеки подтянутся. А какие не подтянутся — там сообщество само запилит аннотации (для того же Django уже есть, например).


И в mypy баги когда-нибудь пофиксятся, и рекурсивные типы когда-нибудь завезут, и светлое будущее обязательно наступит.


А библиотеки вроде pydantic, объявляя несовместимость с системой типов mypy не багом, а фичей, отдаляют это светлое будущее и попадают в мой личный чёрный список.


Но в целом лучше конечно на Rust переходить. :)

Тот же изначальный пример, но с mashumaro работает. Нужно только добавить миксин (можно для удобства в какой-нибудь базовый класс BaseConfig):


from dataclasses import dataclass
from enum import Enum
from typing import Optional
from mashumaro import DataClassYAMLMixin

class Color(Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"

@dataclass
class BattleStationConfig(DataClassYAMLMixin):
    @dataclass
    class Processor(DataClassYAMLMixin):
        core_count: int
        manufacturer: str

    processor: Processor
    memory_gb: int
    led_color: Optional[Color] = None

yaml = """
processor:
  core_count: 8
  manufacturer: Intel
memory_gb: 8
led_color: red
"""

print(BattleStationConfig.from_yaml(yaml))
# BattleStationConfig(processor=BattleStationConfig.Processor(core_count=8, manufacturer='Intel'), memory_gb=8, led_color=<Color.RED: 'red'>)

Не нужно использовать ForwardRef в качестве аннотации. Просто указывайте тип в кавычках и все будет работать автоматом.


Так же я бы советовал не вкладывать классы внутрь классов без особой нужды, диктуемой некоторыми фреймворками.


Пользуясь случаем, так же хочу предложить свою библиотеку для парсинга датаклассов: dataclass-factory. При её использовании не требуется менять иерархию классов (как в случае c pydantic или mashumaro), но при необходимости все ещё можно гибко настраивать поведение парсинга с помощью опциональных "схем".


В таком случае я бы переписал указанный код в виде:


from dataclasses import dataclass
from enum import Enum
from typing import Optional

from dataclass_factory import Factory
from yaml import load, SafeLoader

class Color(Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"

@dataclass
class BattleStationConfig:
    processor: "Processor"  # вместо `ForwardRef`
    memory_gb: int
    led_color: Optional[Color] = None

@dataclass
class Processor:
    core_count: int
    manufacturer: str

yaml = """
processor:
  core_count: 8
  manufacturer: Intel
memory_gb: 8
led_color: red
"""

factory = Factory()  # здесь задаются дополнительные настройки обработки

loaded = load(yaml, Loader=SafeLoader)
print(factory.load(loaded, BattleStationConfig))  
# BattleStationConfig(processor=Processor(core_count=8, manufacturer='Intel'), memory_gb=8, led_color=<Color.RED: 'red'>)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации