mypy return 各种长度的元组类型
mypy return type of tuples of various length
这是一些模拟代码:
def my_func() -> set[tuple[str, ...]]:
my_set: set[tuple[str, str]] = set()
# do some stuff
return my_set
运行 上面代码中的 mypy 给出了错误:
Incompatible return value type (got "Set[Tuple[str, str]]", expected "Set[Tuple[str, ...]]")
为什么 set[tuple[str, str]]
不被识别为 set[tuple[str, ...]]
?
在生产中,我有几个上述形式的函数,每个函数 return 是一组同质元组(my_func2
将 return 一组 5 元组,并且my_func3
会 return 一组 11-tuples
)。我希望 return 类型与我的函数系列 my_func, my_func2, my_func3
.
相同
set
是不变的,这意味着类型参数必须完全匹配,而不是一个简单地符合另一个。这里最简单的修复是只用所需的 return 类型注释 my_set
:
def my_func() -> set[tuple[str, ...]]:
my_set: set[tuple[str, ...]] = set()
my_set.add(("foo", "bar"))
return my_set
另一个选项(如果你希望 my_set
被限制在函数体内的 2 元素元组)将是 cast
它当你 return:
from typing import cast
def my_func() -> set[tuple[str, ...]]:
my_set: set[tuple[str, str]] = set()
my_set.add(("foo", "bar"))
return cast(set[tuple[str, ...]], my_set)
但是,请注意集合不变的原因是因为它们是可变的。假设您将 my_set
(作为一组 2 元组)传递给另一个(更挑剔的)函数,该函数期望它只包含 2 元组,然后 return 对 my_set
的引用已转换为可以包含 n-tuples。现在任何拥有该转换引用的人都可以将 n-tuples 插入其中,这违反了更挑剔的函数的类型假设!这就是为什么 cast
可能是危险的——在这种情况下,只有在 return
.
之后没有对 my_set
及其 2 元组类型的引用仍然存在时,它才是安全的
所以如果你想my_set
被打成一个二元组,但是你想return一组n-tuples,另一种选择是return frozenset
:
def my_func() -> frozenset[tuple[str, ...]]:
my_set: set[tuple[str, str]] = set()
my_set.add(("foo", "bar"))
return frozenset(my_set)
返回 frozenset
可确保调用者无法改变 my_set
并违反其二元组类型。
请注意,return 匿名(可变)副本也非常安全(我认为),但 mypy 不会推断出这一点,因此您仍然必须 cast
明确地:
return cast(set[tuple[str, ...]], my_set.copy())
这是一些模拟代码:
def my_func() -> set[tuple[str, ...]]:
my_set: set[tuple[str, str]] = set()
# do some stuff
return my_set
运行 上面代码中的 mypy 给出了错误:
Incompatible return value type (got "Set[Tuple[str, str]]", expected "Set[Tuple[str, ...]]")
为什么 set[tuple[str, str]]
不被识别为 set[tuple[str, ...]]
?
在生产中,我有几个上述形式的函数,每个函数 return 是一组同质元组(my_func2
将 return 一组 5 元组,并且my_func3
会 return 一组 11-tuples
)。我希望 return 类型与我的函数系列 my_func, my_func2, my_func3
.
set
是不变的,这意味着类型参数必须完全匹配,而不是一个简单地符合另一个。这里最简单的修复是只用所需的 return 类型注释 my_set
:
def my_func() -> set[tuple[str, ...]]:
my_set: set[tuple[str, ...]] = set()
my_set.add(("foo", "bar"))
return my_set
另一个选项(如果你希望 my_set
被限制在函数体内的 2 元素元组)将是 cast
它当你 return:
from typing import cast
def my_func() -> set[tuple[str, ...]]:
my_set: set[tuple[str, str]] = set()
my_set.add(("foo", "bar"))
return cast(set[tuple[str, ...]], my_set)
但是,请注意集合不变的原因是因为它们是可变的。假设您将 my_set
(作为一组 2 元组)传递给另一个(更挑剔的)函数,该函数期望它只包含 2 元组,然后 return 对 my_set
的引用已转换为可以包含 n-tuples。现在任何拥有该转换引用的人都可以将 n-tuples 插入其中,这违反了更挑剔的函数的类型假设!这就是为什么 cast
可能是危险的——在这种情况下,只有在 return
.
my_set
及其 2 元组类型的引用仍然存在时,它才是安全的
所以如果你想my_set
被打成一个二元组,但是你想return一组n-tuples,另一种选择是return frozenset
:
def my_func() -> frozenset[tuple[str, ...]]:
my_set: set[tuple[str, str]] = set()
my_set.add(("foo", "bar"))
return frozenset(my_set)
返回 frozenset
可确保调用者无法改变 my_set
并违反其二元组类型。
请注意,return 匿名(可变)副本也非常安全(我认为),但 mypy 不会推断出这一点,因此您仍然必须 cast
明确地:
return cast(set[tuple[str, ...]], my_set.copy())