【问题标题】:Python mypy unable to infer type from union return typesPython mypy 无法从联合返回类型推断类型
【发布时间】:2018-12-20 13:28:12
【问题描述】:

这里是示例代码

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 中值的类型。
  • 您的函数接受一个字典,其中的值可以是字符串或整数,并返回两个这样的值。 然后你不能声称你总是返回整数
  • 我认为您在这里的误解是这些类型是静态的,但您正在考虑您在运行时返回的实际具体值。这不是静态类型系统的工作方式。

标签: python mypy static-typing


【解决方案1】:

即使字典值之一是 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 dataclass

我在这里使用名称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

【讨论】:

    【解决方案2】:

    代码一切都很好。你忘了给你函数参数:

    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]) 是工作的结果

    【讨论】:

    • 不,即使添加值也会抛出同样的错误。
    • 不,同样的错误仍然会出现,因为 mypy 是一个静态类型检查器。运行时实际发生的事情并不重要。此外,您现在将 lists 作为值传递。 Union[str, int] 并不意味着这些值是整数列表,值必须是整数或字符串。
    • 我认为您误解了这个问题的含义。你了解类型提示的工作原理吗?Dict[...]Union[...]Tuple[...] 语法的含义是什么?
    • 我认为问题在于您在代码中断言您在“开始”和“结束”之类的 dict 键中拥有但是当你没有它们时,你有一个错误......你需要检查您的 dict 是否有键。你可以这样做:如果 name_of_dict 中的“some_need_key”
    • 您的示例代码未能通过 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]",因为您使用的是列表)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-24
    相关资源
    最近更新 更多