Как статически получить параметры TypeVar из Generic для использования при проверке статического типа?

У меня есть класс, который наследуется от typing.Generic и передает один TypeVar в качестве параметра.

Позже в коде я хотел бы:

  1. Статически (не во время выполнения) получить параметр TypeVar из класса
  2. Псевдоним переменной другого типа
  3. Используйте этот псевдоним, чтобы ввести подсказку return функции

Есть ли в Python способ сделать это?

Единственное, что мне не хватает, это шаг 1, как получить параметр типа из переменной типа.


Мой вариант использования

from abc import ABC, abstractmethod
from typing import TypeVar, Generic


TFloat = TypeVar("TFloat", bound=float)


class BaseDataClass(Generic[TFloat], ABC):

    @property
    @abstractmethod
    def data(self) -> TFloat:
        """Get data."""


class ChildDataClass(BaseDataClass[int]):

    @property
    def data(self) -> int:
        return 1

Затем я импортирую BaseDataClass и ChildDataClass в другой модуль.

Есть ли в этом втором модуле способ статически получить параметр TFloat из BaseDataClass или параметр int из ChildDataClass и использовать его в mypy?

К вашему сведению: я использую Python 3.8.2.


person Intrastellar Explorer    schedule 13.06.2020    source источник


Ответы (1)


Не существует способа «получить» переменную типа. Не следует думать о переменной типа как о фрагменте данных, который потенциально можно каким-либо образом извлечь. Вместо этого думайте об этом как о части определения.

Я думаю, что, основываясь на вашем вопросе, вам действительно нужен способ написать функцию, которая принимает некоторый BaseDataClass[T] (или подкласс этого типа) и возвращает то, что есть T.

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

В этом случае мы выбрали сопоставление с чем-либо типа BaseDataClass[T], где мы оставили T generic. И нашим возвращаемым типом будет то, с чем произошло совпадение T.

from typing import TypeVar
from other_module import BaseDataClass, ChildDataClass

T = TypeVar('T', bound=float)

def extract(wrapper: BaseDataClass[T]) -> T:
    return wrapper.data


# BaseDataClass[FloatSubclass] exactly matches against BaseDataClass[T],
# and so T will be FloatSubclass in 'extract(x)' call.

class FloatSubclass(float): pass
x: BaseDataClass[FloatSubclass]
reveal_type(extract(x))  # Mypy displays "FloatSubclass"


# ChildDataClass doesn't exactly match BaseDataClass[T], but the child
# class *is* a subtype of BaseDataClass[int], which does match.

x: ChildDataClass
reveal_type(extract(x))  # Mypy displays "int"

Дополнительные сведения и примеры см. В документации mypy по универсальным шаблонам.

person Michael0x2a    schedule 13.06.2020
comment
Спасибо @ Michael0x2a! У меня было непонимание предполагаемого использования Generic, и это меня прояснило. Я смог использовать вариант вышеупомянутой extract функции, чтобы выполнить то, что мне было нужно. Спасибо! - person Intrastellar Explorer; 15.06.2020