如何键入可以获取列表或元组的列表或元组的函数?
Posted
技术标签:
【中文标题】如何键入可以获取列表或元组的列表或元组的函数?【英文标题】:How to type a function that can take a list or tuple of a list or tuple? 【发布时间】:2019-11-18 14:43:27 【问题描述】:我正在尝试定义一个函数,该函数可以接收对象容器的容器,并且我不在乎容器是元组还是列表。
(我知道我可以经历实现this 的麻烦,但我不想这样做,我仍然不确定这是否能解决我的问题。)
所以,我有以下代码:
from typing import Any, Union, List, Tuple
# (list or tuple) of Any
AnyBucket = Union[List[Any], Tuple[Any, ...]]
# should be (list or tuple) of (lists or tuples) of Any
AnyBuckets = Union[List[AnyBucket], Tuple[AnyBucket, ...]]
def takes_any_buckets(inpt: AnyBuckets) -> None:
print(inpt[0][0])
input_value: List[List[Any]] = [[1], [2]]
# Argument 1 to "takes_any_buckets" has incompatible type "List[List[Any]]";
# expected "Union[List[Union[List[Any], Tuple[Any, ...]]], Tuple[Union[List[Any], Tuple[Any, ...]], ...]]"
takes_any_buckets(input_value)
我有两个问题:
-
为什么 Mypy 会为此抛出错误?
我可以做些什么来获得我想要的功能(没有出现 Mypy 错误)?
(我不想只用# type: ignore
禁用错误,我想定义一个可以工作的类型。)
我对 (1) 的猜测是它与 List
s 是 invariant 有关,但是对于这个复杂的示例,我不知道如何。
除了可能实施前面提到的麻烦之外,我对 (2) 没有任何想法,这将使我摆脱 Union
s,我怀疑这可能是问题的一部分。
【问题讨论】:
如果您对tuple
或list
都满意,是否有理由不接受typing.Sequence
(涵盖两者)或typing.Container
(更广泛一点,接受非-像set
)这样的可索引类型?
谢谢;在过去的几分钟里,我自己发现了 Sequence(适用于我的情况)。如果您写它作为问题(2)的答案,我会暂时接受。
【参考方案1】:
-
确实与列表不变有关。当前版本的 Mypy 会告诉你这一点;如果您在当前版本中运行代码,则会收到以下注释以及错误:
main.py:14: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
main.py:14: note: Consider using "Sequence" instead, which is covariant
列表不变意味着A
类型的事物列表是B
类型的事物列表的子类型当且仅当A
等于B
.这意味着即使 A 是 B
的子类型,List[A]
也不是 List[B]
的子类型。 (有关不变性的解释以及 mypy 将列表视为不变的原因,请参见此处:https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types)。
所以List[List[Any]]
不是List[AnyBucket]
的子类型,即使List[Any]
是AnyBucket
的子类型,所以input_value
没有与AnyBuckets
兼容的类型。因此出现错误。
-
正如上面的 Mypy 和 ShadowRanger 都建议的那样,您可能应该使用
Sequence
,这是一种协变类型,它涵盖了可以被索引到的可迭代对象(第一个近似值 - 对于集合,还有一些其他的事情需要是真实的因为它是一个序列)。您需要从 collections.abc 导入它;看这里:https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence
我说“可能”是因为这完全取决于您要键入的函数对集合的作用。例如,如果您所做的只是遍历集合(一次),那么您想使用Iterable
,这是一种更通用的类型。
使用Sequence
之类的一个优点是,如果有任何其他类型的对象漂浮在那里——也许是用户定义的——可以完成你想要的工作,它们将是你函数的有效输入(只要他们已经注册为Sequences
)。一般来说,如果你可以使用structural typing,那就很好了,它是duck-typing 的静态类型版本,相当于 Python 方式。
【讨论】:
以上是关于如何键入可以获取列表或元组的列表或元组的函数?的主要内容,如果未能解决你的问题,请参考以下文章