【问题标题】:Python building complex mypy typesPython 构建复杂的 mypy 类型
【发布时间】:2019-10-23 02:20:13
【问题描述】:

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

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]:

【问题讨论】:

    标签: python python-3.x mypy


    【解决方案1】:

    您可能需要使用 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]
    

    根据您处理类型的方式,您还可以使用SupportsIntSupportsFloat 类型,而不是同时使用intfloat

    编辑:(基于下面编辑的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),因此为了便于阅读,您需要为每个潜在的嵌套级别定义允许的类型,然后将它们收集到一个代表完整嵌套结构的类型中。

    【讨论】:

    • 感谢您的回复!修改了我的原始帖子,以提供更充实的所需用法示例。特别是,我有几个嵌套字典(ick)。它可以是Dict[str, Dict[str, Union[bool, int, float]]Dict[str, Dict[str, Dict[str, Union[bool, int, float]]],具体取决于子类。
    • 我已经继续并更新了我的答案以反映您的编辑,但这可能更像是一个确认缺少功能以及如何处理它的答案,而不是简单的修复。
    • 我又做了一个编辑……你觉得元组方法(相对于三重嵌套字典)怎么样?它似乎在一个玩具示例中通过了 mypy,但我不想花时间重构一堆代码,只是为了找到一个我没有想到的问题。
    • 我认为我对您的代码了解不足,无法围绕设计提出具体建议。字典和元组各有优缺点,因此切换数据类型可能会导致性能发生变化。
    猜你喜欢
    • 1970-01-01
    • 2022-06-14
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多