将变量类型提示为 Iterable 与 Sequence 之间有什么区别?

Fed*_*gon 71 type-hinting python-3.x

Iterable我不明白暗示和时的区别Sequence

这两者之间的主要区别是什么以及何时使用哪一个?

我认为setIterable但不是Sequence,是否有任何内置数据类型是Sequence但不是Iterable

def foo(baz: Sequence[float]):
  ...

# What is the difference?
def bar(baz: Iterable[float]):
  ...
Run Code Online (Sandbox Code Playgroud)

Zec*_* Hu 68

和抽象基类(也可以用作类型注释)大多数*遵循 Python 的Sequencesequenceiterable定义。再具体一点:Iterable

\n
    \n
  • Iterable是定义__iter__ 或 的 任何对象__getitem__
  • \n
  • 序列__getitem__是定义和的任何对象__len__。根据定义,任何序列都是可迭代的。该类Sequence还定义了其他方法,例如__contains____reversed__它调用两个必需的方法。
  • \n
\n

一些例子:

\n
    \n
  • list, tuple,str是最常见的序列。
  • \n
  • 一些内置的可迭代对象不是序列。例如,reversed返回无法添加下标的reversed对象(或list_reverseiterator列表)。
  • \n
\n
\n

*Iterable不完全符合 Python 的可迭代定义 \xe2\x80\x94 它只检查对象是否定义__iter__,并且不适用于只能通过迭代的对象__getitem__(有关详细信息,请参阅此表)。检查对象是否可迭代的黄金标准是使用iter内置函数。

\n


Pat*_*don 17

当编写带有参数的函数/方法时items,我通常更Iterable喜欢Sequence. 下面是原因,我希望它能帮助理解其中的差异。

说的my_func_1是:

from typing import Iterable
def my_func_1(items: Iterable[int]) -> None:
    for item in items:
        ...
        if condition:
            break
    return
Run Code Online (Sandbox Code Playgroud)

Iterable为呼叫者提供最大的可能性。正确的调用包括:

my_func_1((1, 2, 3)) # tuple is Sequence, Collection, Iterator
my_func_1([1, 2, 3]) # list is MutableSequence, Sequence, Collection, Iterator
my_func_1({1, 2, 3}) # set is Collection, Iterator
my_func_1(my_dict) # dict is Mapping, Collection, Iterator
my_func_1(my_dict.keys()) # dict.keys() is MappingKeys, Set, Collection, Iterator
my_func_1(range(10)) # range is Sequence, Collection, Iterator
my_func_1(x**2 for x in range(100)) # "strict' Iterator, i.e. neither a Collection nor a Sequence
... 
Run Code Online (Sandbox Code Playgroud)

...因为都是Iterable

给函数调用者的隐式消息是:“按原样”传输数据,只是不转换它。

如果调用者没有作为Sequence(例如tuple, list) 或作为非Sequence Collection(例如set) 的数据,并且因为迭代在 之前中断StopIteration,所以如果他提供 'strict' ,它的性能也会更高Iterator

但是,如果函数算法(例如my_func_2)需要多次迭代,那么Iterable如果调用者提供“严格”,则会失败,Iterator因为第一次迭代耗尽了它。因此使用Collection

from typing import Collection
def my_func_2(items: Collection[int]) -> None:
    for item in items:
        ...
    for item in items:
        ...
    return
Run Code Online (Sandbox Code Playgroud)

如果函数算法 (my_func_3)必须通过索引访问特定项,那么如果调用者提供 set、a或 'strict' ,则Iterable和都会失败。因此使用 a :CollectionMappingIteratorSequence

from typing import Sequence
def my_func_3(items: Sequence[int]) -> None:
    return items[5]
Run Code Online (Sandbox Code Playgroud)

结论:策略是:“使用函数可以处理的最通用的类​​型”。不要忘记,所有这些只是关于键入,以帮助静态类型检查器报告不正确的调用(例如,setSequence需要 a 时使用 a)。然后调用者有责任在必要时转换数据,例如:

my_func_3(tuple(x**2 for x in range(100)))
Run Code Online (Sandbox Code Playgroud)

实际上,所有这些实际上都与缩放项目长度时的性能有关。尽可能选择Iterator。表演应作为日常任务来处理,而不是作为消防员工作组来处理。

在这个方向上,您可能会遇到这样的情况:一个函数仅处理空用例并委托其他用例,并且您不想将项转换为 aCollection或 a Sequence。然后做这样的事情:

from more_itertools import spy
def my_func_4(items: Iterable[int]) -> None:
    (first, items) = spy(items)
    if not first: # i.e. items is empty
        ...
    else:
        my_func_1(items) # Here 'items' is always a 'strict' Iterator
    return
Run Code Online (Sandbox Code Playgroud)