Python строит сложные типы MyPy - PullRequest
0 голосов
/ 23 октября 2019

В идеальном мире я мог бы просто сделать это:

ScoreBaseType = Union[bool, int, float]
ScoreComplexType = Union[ScoreBaseType, Dict[str, ScoreBaseType]]

Но это говорит о том, что ScoreComplexType - это или ScoreBaseType, или словарь, который допускает множественные типы значений ... не то, что я хочу.

Следующее выглядит так, как будто оно должно работать для меня, но это не так:

ScoreBaseTypeList = [bool, int, float]
ScoreBaseType = Union[*ScoreBaseTypeList]  # pycharm says "can't use starred expression here"
ScoreDictType = reduce(lambda lhs,rhs: Union[lhs, rhs], map(lambda x: Dict[str, x], ScoreBaseTypeList))
ScoreComplexType = Union[ScoreBaseType, ScoreDictType]

Есть ли способ, как я могу сделать что-то подобное без необходимости проходить через эту скуку?

ScoreComplexType = Union[bool, int, float,
                     Dict[str, bool],
                     Dict[str, int],
                     Dict[str, float]]

Редактировать : Более подробный пример желаемого использования:

# these strings are completely arbitrary and determined at runtime. Used as keys in nested dictionaries.
CatalogStr = NewType('CatalogStr', str)
DatasetStr = NewType('DatasetStr', str)
ScoreTypeStr = NewType('ScoreTypeStr', str)

ScoreBaseType = Union[bool, int, float]
ScoreDictType = Dict[ScoreTypeStr, 'ScoreBaseTypeVar']
ScoreComplexType = Union['ScoreBaseTypeVar', ScoreDictType]

ScoreBaseTypeVar = TypeVar('ScoreBaseTypeVar', bound=ScoreBaseType)
ScoreComplexTypeVar = TypeVar('ScoreComplexTypeVar', bound=ScoreComplexType) # errors: "constraints cannot be parameterized by type variables"

class EvalBase(ABC, Generic[ScoreComplexTypeVar]):
    def __init__(self) -> None:
        self.scores: Dict[CatalogStr,
                          Dict[DatasetStr,
                               ScoreComplexTypeVar]
                          ] = {}

class EvalExample(EvalBase[Dict[float]]): # can't do this either
    ...

Редактировать 2: Мне кажется, что я мог бы упроститьМНОГИЕ подсказки моего типа, если я использую кортежи вместо вложенных словарей. Это, кажется, может работать? Я только попробовал это в приведенном ниже примере с игрушкой и еще не пытался адаптировать весь мой код.

# These are used to make typing hints easier to understand
CatalogStr = NewType('CatalogStr', str)  # A str corresponding to the name of a catalog
DatasetStr = NewType('DatasetStr', str)  # A str corresponding to the name of a dataset
ScoreTypeStr = NewType('ScoreTypeStr', str)  # A str corresponding to the label for a ScoreType

ScoreBaseType = Union[bool, int, float]

SimpleScoreDictKey = Tuple[CatalogStr, DatasetStr]
ComplexScoreDictKey = Tuple[CatalogStr, DatasetStr, ScoreTypeStr]
ScoreKey = Union[SimpleScoreDictKey, ComplexScoreDictKey]
ScoreKeyTypeVar = TypeVar('ScoreKeyTypeVar', bound=ScoreKey)
ScoreDictType = Dict[ScoreKey, ScoreBaseType]

# These are used for Generics in classes
DatasetTypeVar = TypeVar('DatasetTypeVar', bound='Dataset')  # Must match a type inherited from Dataset
ScoreBaseTypeVar = TypeVar('ScoreBaseTypeVar', bound=ScoreBaseType)


class EvalBase(ABC, Generic[ScoreBaseTypeVar, ScoreKeyTypeVar]):
    def __init__(self):
        self.score: ScoreDictType = {}


class EvalExample(EvalBase[float, ComplexScoreDictKey]):
    ...

Хотя что тогда будет эквивалентом этого? Похоже, мне придется хранить пару списков ключей для итерации?

for catalog_name in self.catalog_list:
    for dataset_name in self.scores[catalog_name]:
        for score in self.scores[catalog_name][dataset_name]:

1 Ответ

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

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

Пример того, как это будет использоваться для ввода зависимого возвращаемого значенияна входе:

ScoreBaseType = Union[bool, int, float]
ScoreTypeVar = TypeVar('ScoreTypeVar', bound=ScoreBaseType)

ScoreDictType = Union[ScoreTypeVar, Dict[str, ScoreTypeVar]]


def scoring_func(Iterable[ScoreTypeVar]) -> ScoreDictType:
    ...

Если вы не делаете это на основе входных значений, возможно, вы захотите

ScoreBaseType = Union[bool, int, float]
ScoreDictTypes = Union[Dict[str, bool], Dict[str, int], Dict[str, float]]
ScoreComplexType = Union[ScoreBaseType, ScoreDictTypes]

В зависимости от того, как вы обрабатываете типы, вы также можетевозможность использовать типы SupportsInt или SupportsFloat вместо int и float

Редактировать: (дополнительная информация на основе отредактированного OP ниже)

Поскольку вы печатаетеABC с этим, может быть достаточно набрать базовый класс, используя Dict[str, Any] и ограничить подклассы далее.

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

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

...