Mypy не распознает типы классов с наследованием

#python #mypy #python-dataclasses

#python #mypy #python-классы данных

Вопрос:

Я не могу заставить Mypy распознавать правильные типы здесь. Все, что я пытаюсь сделать, это создать a dict имен для классов, чтобы я мог получить класс, указав a type_name , который является его атрибутом. Может быть, потому dataclass , что и dataclasses-json прочее сбивает с толку?

 # mypy_test.py

from dataclasses import dataclass
from typing import Any, Dict, Type, TypeVar

from dataclasses_json import DataClassJsonMixin


@dataclass
class _BaseDataItem(DataClassJsonMixin):
    name: str  # functions as an ID. Subclasses should NOT modify its value after creation.
    type_name: str
    body: Any = None
    # etc ...


@dataclass
class DatanetItem(_BaseDataItem):
    type_name: str = "datanet"


@dataclass
class RedshiftItem(_BaseDataItem):
    type_name: str = "redshift"


# https://mypy.readthedocs.io/en/stable/kinds_of_types.html#the-type-of-class-objects
IT = TypeVar("IT", bound=_BaseDataItem)

items2classes: Dict[str, Type[IT]] = {
    c.type_name: c for c in (DatanetItem, RedshiftItem)
}


def create_obj(name: str, item_type: str) -> _BaseDataItem:
    klass = items2classes[item_type]
    obj = klass(name)
    return obj
  

Пример использования:

 In [1]: import mypy_test as m

In [2]: m.create_obj('123','datanet')
Out[2]: DatanetItem(name='123', type_name='datanet', body=None)

In [3]: m.create_obj('mytable','redshift')
Out[3]: RedshiftItem(name='mytable', type_name='redshift', body=None)
  

Хотя я думал, что следовал инструкциям в документах, по какой-то причине MyPy продолжает выдавать мне эту ошибку:

 mypy_test.py:28: error: Type variable "mypy_test.IT" is unbound
mypy_test.py:28: note: (Hint: Use "Generic[IT]" or "Protocol[IT]" base class to bind "IT" inside a class)
mypy_test.py:28: note: (Hint: Use "IT" in function signature to bind "IT" inside a function)
mypy_test.py:35: error: Cannot instantiate type "Type[IT?]"
Found 2 errors in 1 file (checked 1 source file)
  

Ответ №1:

Причина сбоя mypy не имеет ничего общего с классами данных или классами данных-json. Если вы попытаетесь упростить свой пример, чтобы все было обычным объектом, вы все равно получите ту же ошибку.

Причина этого в том, что, к сожалению, вы пытаетесь использовать TypeVars для чего-то, для чего они никогда не предназначались.

TypeVars предназначены для использования в определениях классов или определениях функций: они позволяют вам «захватывать» значение типа, предоставленного пользователем, и повторно использовать его в других частях определения вашего класса / определения функции. Этот «захват» или «сопоставление» выполняется каждый раз, когда пользователь фактически пытается использовать ваш класс или функцию.

Но поскольку items2class он не является частью универсального класса или функции, mypy в конечном итоге будет справедливо жаловаться, что вы пытаетесь использовать TypeVar вне контекста, в котором вы собираетесь его использовать.

К счастью, в этом случае решение относительно простое: прекратите использовать дженерики и вместо этого переключитесь на то, чтобы ваш dict был типа Dict[Str, Type[_BaseDataItem]] . Все ваши подклассы _BaseDataItem изначально имеют тип, поэтому они являются допустимыми элементами для вставки в этот dict.

Если вам нужны более точные подсказки по типу — например, пусть mypy поймет, что ключ «redshift» точно соответствует RedshiftItem при использовании вашего dict — возможно, вы можете попробовать поэкспериментировать с использованием TypedDicts . Но я не уверен, что это действительно будет полезно в контексте вашего кода.

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

1. спасибо за исчерпывающий ответ. Но тоже не работает — "Type[object]" has no attribute "type_name" [attr-defined] - at items2classes: Dict[str, Type[_BaseDataItem]] = {c.type_name: c for c ...