Mypy 因重载和文字而失败

问题描述 投票:0回答:2

我试图理解

typing.overload
并将其应用在一个简单的情况下,我想要一个接受输入
x: Literal["foo", "bar"]
并返回列表
[x]
的函数。

我希望 mypy 将结果列表键入为

list[Literal["foo"]]
list[Literal["bar"]]
,具体取决于
x
的值。

我知道我可以用

TypeVar
来实现这一点,但我仍然想了解为什么下面的代码失败并出现以下错误:

test.py:14: error: Overloaded function implementation cannot produce return type of signature 1
test.py:14: error: Overloaded function implementation cannot produce return type of signature 2
from typing import Literal, overload


@overload
def f(x: Literal["foo"]) -> list[Literal["foo"]]:
    ...


@overload
def f(x: Literal["bar"]) -> list[Literal["bar"]]:
    ...


def f(x: Literal["foo", "bar"]) -> list[Literal["foo", "bar"]]:
    return [x]
python overloading python-typing mypy
2个回答
3
投票

Python 中的列表是不变的。这意味着,即使

B
A
的子类型,list[A]
list[B]
类型之间也
没有关系

如果允许

list[B]
成为
list[A]
的子类型,那么有人可以来做这件事。

my_b_list: list[B] = []
my_a_list: list[A] = my_b_list
my_a_list.append(A())
print(my_b_list) # Oh no, a list[B] contains an A value!

如果您打算修改返回的列表,那么您所做的事情就不安全。故事结束。如果您打算将列表视为不可变,那么请考虑您实际需要哪些操作,并且您可能能够在

list
中找到
typing
的协变超类型。

例如,

Sequence
是一个受欢迎的选择。它支持迭代、随机访问和长度访问,同时明确允许突变。

from typing import Literal, overload, Sequence


@overload
def f(x: Literal["foo"]) -> Sequence[Literal["foo"]]:
    ...


@overload
def f(x: Literal["bar"]) -> Sequence[Literal["bar"]]:
    ...


def f(x: Literal["foo", "bar"]) -> Sequence[Literal["foo", "bar"]]:
    return [x]

(注意:

typing.Sequence
在Python 3.9中已弃用;如果您只打算支持3.9+,则可以使用
collections.abc.Sequence
代替)


2
投票

AFAIU你的问题,你的实际实现需要提供单一类型(

str
),而不是多个
Literal

以下内容根据

pyright
正确工作,并且似乎提供了您正在寻找的功能(仅允许
"foo"
"bar"
的列表,拒绝其他所有内容)。

from typing import Literal, overload


@overload
def f(x: Literal["foo"]) -> list[Literal["foo"]]:
    ...


@overload
def f(x: Literal["bar"]) -> list[Literal["bar"]]:
    ...


def f(x: str) -> list[str]:
    return [x]


f("foo")  # valid
f("bar")  # valid
f("baz")  # error

这会导致以下错误:

a.py:20:3 - error: Argument of type "Literal['baz']" cannot be assigned to parameter "x" of type "Literal['bar']" in function "f"
    "Literal['baz']" cannot be assigned to type "Literal['bar']"
© www.soinside.com 2019 - 2024. All rights reserved.