代码之家  ›  专栏  ›  技术社区  ›  user3534080

Python构建复杂的mypy类型

  •  0
  • user3534080  · 技术社区  · 6 年前

    在一个完美的世界里,我可以这样做:

    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]:
    
    0 回复  |  直到 6 年前
        1
  •  1
  •   Michael H    6 年前

    你可能需要使用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目前所做的那样 some issues resolving some classes of programmatically generated types ,即使是在常数上操作。

    mypy目前也不支持递归类型别名( though there is a potential of support for them being added 是的 not currently planned ),因此为了可读性,您需要为每个潜在嵌套级别定义允许的类型,然后将它们收集到表示完整嵌套结构的类型中。