简体   繁体   English

如何帮助Mypy理解“如果不是这样”的结构?

[英]How to help mypy understand “if isinstance else” structure?

I use a simple method for handling arguments that can either be strings or lists of strings. 我使用一种简单的方法来处理可以是字符串或字符串列表的参数。 It is intentionally not robust to errors, but is a quick one-liner. 它故意对错误没有鲁棒性,但却是一种快速的方法。

This method works fine in normal Python, but mypy does not handle its types well and raises an error: 该方法在普通的Python中可以正常工作,但是mypy不能很好地处理其类型并引发错误:

def dosomething(ext: Union[str, Iterable[str]] = ".txt"):
    exts: Tuple[str, ...] = (ext, ) if isinstance(ext, str) else ext

Error: Incompatible types in assignment (expression has type "object", variable has type "Tuple[str, ...]") 错误: Incompatible types in assignment (expression has type "object", variable has type "Tuple[str, ...]")

Investigating the issue, I see that mypy does not manage to reduce the available type options with the if isinstance else structure: 调查此问题,我发现mypy无法使用if isinstance else结构减少可用的类型选项:

def dosomething(ext: Union[str, Iterable[str]] = ".txt"):
    exts = (ext, ) if isinstance(ext, str) else ext
    reveal_type(exts)  # Revealed type is 'builtins.object'

Seeing mypy fall back to object is a surprise to me, but I suspect this may be because a str is also Iterable[str] . 看到mypy退回到object对我来说是一个惊喜,但是我怀疑这可能是因为str也是Iterable[str]

I would like to find a way to avoid this surprising situation by helping mypy while keeping the simplicity. 我想找到一种方法来避免这种意外情况,方法是在保持简单性的同时帮助mypy。

This comes down to the issue of how mypy infers the type for conditional expressions. 这归结为mypy如何为条件表达式推断类型的问题。 In simple contexts the expression a if predicate else b is inferred to be the supertype of a and b . 在简单的上下文中,表达式a if predicate else b推断为ab的超类型。 In the case of str and Iterable[str] this is builtins.object . strIterable[str]的情况下,这是builtins.object

x = reveal_type(1 if bool() else "str")
# Revealed type is 'builtins.object'

One exception is if mypy can resolve the predicate to True or False, then the type gets narrowed to the correct branch: 一个例外是,如果mypy可以将谓词解析为True或False,则将类型缩小为正确的分支:

x = reveal_type(1 if True else "str")
# Revealed type is 'builtins.int'

One would expect that isinstance checks could be resolved to True or False by mypy, but it seems that this has not been implemented yet. 有人希望mypy可以将isinstance检查解析为True或False,但是似乎尚未实现。 I think it would be a very reasonable feature request. 我认为这将是一个非常合理的功能要求。

However, we can make use of a nice feature of conditional expression typing, which is that it is context aware. 但是,我们可以利用条件表达式类型的一个不错的功能,那就是它可以识别上下文。 So if the conditional expression is used in a context where a different type is expected, and both sides of the conditional fulfill that type, then type checking passes. 因此,如果在期望使用其他类型的上下文中使用条件表达式,并且条件的两端都满足该类型,则类型检查将通过。 This has been discussed here in the context of Union types, but in your case both branches satisfy Iterable[str] : 这已经讨论过这里的联盟类型的范围内,但在你的情况下,两个分支满足Iterable[str]

exts: Iterable[str] = (ext, ) if isinstance(ext, str) else ext

So the key is just specifying the type of exts explicitely. 因此,关键只是明确指定exts的类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM