Python的mypy无法从联合返回类型中推断类型

6

这里是示例代码

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({})

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]")

尽管字典的一个值是 int 类型,但是 mypy 无法推断出它的类型。

mypy是正确的。你把字典值定义为Union[str, int],但是在函数签名中返回类型却是int。请修改返回类型或者不要指定data中的值类型。 - Jared Smith
2
你的函数接受一个字典,其中的值可以是字符串或整数,并返回两个这样的值。然后你不能声称你总是返回整数 - Martijn Pieters
我认为你在这里的误解是这些类型是静态的,但你正在考虑运行时返回的实际具体值。这不是静态类型系统的工作方式。 - Jared Smith
2个回答

6
尽管字典值之一是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,因此类型检查器不能防止您的代码中未意识到将字符串值设置为这两个键而引起的未来的漏洞问题。
如果您要在字典中传递结构化数据,则始终需要与 mypy 打交道,因为字典实际上并不是用于此目的的正确结构。您真正想要使用命名元组或a数据类
我在这里使用名称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

-1

代码方面一切都很好。你忘记获取函数参数了:

from typing import Dict, Union, Tuple


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

print (select_range({"start":[1,5], 'end':[2,6]}))

用于查找某些参数:

from typing import Dict, Union, Tuple

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

print (select_range({"start":[5], 'end':[6]}))
#select_range({})

([5], [6]) 是工作的结果


不,即使添加了该值,也会出现相同的错误。 - Kracekumar
不会,仍然会出现相同的错误,因为mypy是一个静态类型检查器。在运行时发生了什么并不重要。此外,您现在正在将列表作为值传递。Union[str, int]并不意味着这些值是整数列表,这些值必须是整数或字符串。 - Martijn Pieters
我认为你误解了这个问题的意思。你是否理解类型提示是如何工作的,Dict[...]Union[...]Tuple[...]语法表示的含义是什么? - Martijn Pieters
我认为问题在于你在代码中断言你有字典键(如“start”和“end”),但当你没有它们时,就会出现错误......你需要检查你的字典是否具有该键。你可以这样做:if "some_need_key" in name_of_dict - Konstantin Kozlenko
你的示例代码未能通过mypy检查。函数实现和调用没有问题,但是类型提示声明有问题。缺少键并不重要(这不是问题所在)。(你的代码仍然会产生Incompatible return value type (got "Tuple[Union[str, int], Union[str, int]]", expected "Tuple[int, int]")而且它还会添加Dict entry 0 has incompatible type "str": "List[int]"; expected "str": "Union[str, int]"Dict entry 1 has incompatible type "str": "List[int]"; expected "str": "Union[str, int]",因为你使用了列表)。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接