Tengo la siguiente función de Python y quiero escribirla de manera que mypy entienda el tipo de devolución como un diccionario, con el tipo de elemento como tipo de valor y el tipo de identificación como tipo de clave:
def id_map(items): return {item.id: item for item in items}El enfoque actual se ve así:
from typing import Iterable, Protocol, TypeVar S = TypeVar("S", covariant=True, bound="ClassWithId") T = TypeVar("T", covariant=True) class ClassWithId(Protocol[S, T]): def __new__(cls: type[S], *args: Any, **kwargs: Any) -> "ClassWithId[S, T]": ... @property def id(self: S) -> T: ... def id_map(items: Iterable[ClassWithId[S, T]]) -> dict[T, S]: return {item.id: cast(S, item) for item in items} Actualmente, esto da como resultado el tipo builtins.dict[Any, Any] . No puedo pensar en una implementación diferente que haría que eso funcione. Me pregunto si esto es posible usando mypy.
TLDR: elimine la variable de tipo propio S . No es necesario, pero conduce a una relación de tipo que es poco probable que los verificadores de tipo entiendan.
class ClassWithId(Protocol[T]): @property def id(self) -> T: ...Las variables de tipo se pueden utilizar de dos formas: como variables inferidas en una llamada o como variables declaradas en un tipo genérico. Utilice el primero para los tipos que están restringidos por el contexto de uso y el último para los tipos que deben ser coherentes en toda una clase.
El tipo S var es un tipo Self ; esto siempre se puede inferir del uso y no es necesario repetirlo en el tipo mismo. De lo contrario, construye un tipo recursivo que probablemente confunda a los verificadores de tipos actuales.
Eliminar S del genérico/protocolo muestra que en realidad no se usa para ninguna relación de tipo: la T está totalmente restringida por la clase ClassWithId[T] . No es necesario referirse a S para definir ninguna relación entre entrada y salida.
class ClassWithId(Protocol[T]): @property def id(self: S) -> T: ... # ^ defined by generic class, not S Observe cómo id_map tampoco depende de S : conocer T o ClassWithId[T] es suficiente para conocer al otro. Como resultado, se puede eliminar S por completo.
T = TypeVar("T", covariant=True) class ClassWithId(Protocol[T]): @property def id(self) -> T: ... def id_map(items: Iterable[ClassWithId[T]]) -> dict[T, ClassWithId[T]]: return {item.id: item for item in items} Si se supone que id_map conserva el ClassWithId[T] exacto y no solo el protocolo, esto corresponde a un tipo más alto . En principio, esto se expresa mediante una variable Tipo con un límite del protocolo.
CWI = TypeVar("CWI", bound=ClassWithId) def id_map(items: Iterable[CWI[T]]) -> dict[T, CWI[T]]: return {item.id: item for item in items}Sin embargo, MyPy no admite tipos de tipos superiores en este momento .