Python mypy无法从union返回类型推断出类型

Kra*_*mar 4 python static-typing mypy

这是示例代码

from typing import Dict, Union, Tuple


def select_range(data: Dict[str, Union[str, int]]) -> Tuple[int, int]:
    if data['start'] and data['end']:
        return data['start'], data['end']
    return 1, 1

select_range({})
Run Code Online (Sandbox Code Playgroud)

Mypy输出:

mypy different_return.py
different_return.py:6: error: Incompatible return value type (got 
"Tuple[Union[str, int], Union[str, int]]", expected "Tuple[int, int]")
Run Code Online (Sandbox Code Playgroud)

即使其中一个字典值是int,mypy也无法推断出这一点.

Mar*_*ers 5

即使其中一个字典值是int,mypy也无法推断出来.

Mypy是对的.您的代码有错误,mypy正确标记它.您的代码无法保证data['start']并且data['end']始终是整数.

您的data签名是Dict[str, Union[str, int]],因此值具有类型Union[str, int].Mypy 必须假设传入总是正确的{'start': '2018-07-12', 'end': -42},所以返回值必须Tuple[Union[str, int], Union[str, int]].您声称该函数返回Tuple[int, int]与此冲突.

在运行时实际发生的事情并不重要.那不是重点; mypy是一个静态类型检查器,旨在帮助保持运行时行为无错误.这里的问题是,根据类型的提示,它可能在非整数值传startend,所以typechecker不能保护你的代码中的错误将来不小心设置一个字符串值或者这两个的键.

如果你在字典中传递结构化数据,你将永远不得不对此进行打击,因为词典实际上是错误的结构.你真的想在这里使用一个命名的元组或数据类.

我在FooBar这里使用这个名称,但是对于您的特定应用程序,我确信您传递的数据结构会有更好的名称:

from typing import NamedTuple

class FooBar(NamedTuple):
    start: int
    end: int
    # other fields, perhaps with defaults and Optionals


def select_range(data: FooBar) -> Tuple[int, int]:
    if data.start and data.end:
        return data.start, data.end
    return 1, 1
Run Code Online (Sandbox Code Playgroud)