在编写带有 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
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
...
...因为都是Iterable。
给函数调用者的隐含信息是:“按原样”传输数据,只是不要转换它。
如果调用者没有数据作为Sequence(例如tuple、list)或作为非SequenceCollection(例如set),并且因为迭代在@987654335 之前中断@,如果他提供一个“严格的”Iterator,它的表现也会更好。
但是,如果函数算法(例如my_func_2)需要多次迭代,那么如果调用者提供“严格”Iterator,Iterable 将失败,因为第一次迭代会耗尽它。因此使用Collection:
from typing import Collection
def my_func_2(items: Collection[int]) -> None:
for item in items:
...
for item in items:
...
return
如果函数算法 (my_func_3) 必须通过索引访问特定项目,那么如果调用者提供集合、Mapping 或“严格”Iterator,则Iterable 和Collection 都将失败。
因此使用Sequence:
from typing import Sequence
def my_func_3(items: Sequence[int]) -> None:
return items[5]
结论:策略是:“使用函数可以处理的最通用的类型”。不要忘记所有这些只是关于打字,以帮助静态类型检查器报告不正确的调用(例如,在需要 Sequence 时使用 set)。然后调用者有责任在必要时转换数据,例如:
my_func_3(tuple(x**2 for x in range(100)))
实际上,所有这些都与缩放项目长度时的性能有关。
如果可能,总是首选Iterator。性能应作为日常任务处理,而不是作为消防员任务组处理。
在那个方向上,您可能会遇到这样的情况:一个函数只处理空用例并委托其他用例,并且您不想将项目转换为 Collection 或 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