我试图理解
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 中的列表是不变的。这意味着,即使
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
代替)
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']"