如果我有一个迭代器 it
并且想要耗尽它,我可以这样写:
for x in it:
pass
有没有内置或标准库函数可以让我用一行代码来实现?当然我也可以这样做:
list(it)
该代码将使用迭代器构建列表,然后丢弃它。但是,由于列表构建过程,我认为这是低效的。当然,编写一个辅助函数来执行空for循环是微不足道的,但我想知道是否还有其他方法。
如果我有一个迭代器 it
并且想要耗尽它,我可以这样写:
for x in it:
pass
有没有内置或标准库函数可以让我用一行代码来实现?当然我也可以这样做:
list(it)
该代码将使用迭代器构建列表,然后丢弃它。但是,由于列表构建过程,我认为这是低效的。当然,编写一个辅助函数来执行空for循环是微不足道的,但我想知道是否还有其他方法。
来自itertools
的示例:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
collections.exhaust_iterator
或itertools.exhaust_iterator
会更好,更明显。 - hpk422022年更新(悬赏问答):标准库中没有“专用功能”,deque(it, 0)
仍然是最有效的。这就是为什么在itertools的consume
配方和more-itertools的consume
函数中使用它(点击[源代码]).
各种提案的基准测试,每个元素的迭代时间,遍历itertools.repeat(None, 10 ** 5)
(使用CPython 3.10):
2.7 ns ± 0.1 ns consume_deque
6.5 ns ± 0.0 ns consume_loop
6.5 ns ± 0.0 ns consume_all_if_False
13.9 ns ± 0.3 ns consume_object_in
27.0 ns ± 0.1 ns consume_all_True
29.4 ns ± 0.3 ns consume_sum_0
44.8 ns ± 0.1 ns consume_reduce
deque
由于其是C语言编写,且具有最大长度为0时的快速路径,因此获胜。该快速路径不与元素进行任何操作。consume_all_if_False
,以显示如何有效地执行all
/sum
:增加一个if False
子句,以便你的生成器不产生任何内容。
基准测试代码 (在线测试!):
def consume_loop(it):
for _ in it:
pass
def consume_deque(it):
deque(it, 0)
def consume_object_in(it):
object() in it
def consume_all_True(it):
all(True for _ in it)
def consume_all_if_False(it):
all(_ for _ in it if False)
def consume_sum_0(it):
sum(0 for _ in it)
def consume_reduce(it):
reduce(lambda x, y: y, it)
funcs = [
consume_loop,
consume_deque,
consume_object_in,
consume_all_True,
consume_all_if_False,
consume_sum_0,
consume_reduce,
]
from timeit import default_timer as timer
from itertools import repeat
from collections import deque
from functools import reduce
from random import shuffle
from statistics import mean, stdev
times = {f: [] for f in funcs}
def stats(f):
ts = [t * 1e9 for t in sorted(times[f])[:5]]
return f'{mean(ts):5.1f} ns ± {stdev(ts):3.1f} ns'
for _ in range(25):
shuffle(funcs)
for f in funcs:
n = 10**5
it = repeat(None, n)
t0 = timer()
f(it)
t1 = timer()
times[f].append((t1 - t0) / n)
for f in sorted(funcs, key=stats):
print(stats(f), f.__name__)
deque.extend
(和deque.extendleft
)中调用该函数,当maxlen
为0时。 - Mechanic Pig,如果他们决定删除
deque`的快速路径,这对我来说似乎是公平的。 - Simply Beautiful Artfor _ in it: pass
我刚刚制作了:
def exhaust(it):
for _ in it:
pass
与使用 deque
的解决方案相比(在我的笔记本上比较慢,慢了10%),但我认为这种方法更简洁。
object() in it
如果您知道迭代器永远不会产生某种类型的对象,您也可以使用该对象,例如None in it
或() in it
。新创建的object()
几乎在任何情况下都有效,因为它永远不会等于其他任何东西(除非有什么诡计)。
我并不是在“倡导”这个惯用法;问题中的for
循环在许多方面都是最佳解决方案。但是,如果您正在寻找一种令人毛骨悚然的“优雅”答案,即在尽可能少的副作用计算的情况下仍然是一个非常整洁的单行代码(而不是例如any(False for _ in it)
),那么这可能就是它。
all(True for _ in it)
可以实现。 - hemflitsum
:sum(0 for _ in it)
或者类似地,使用reduce
:
reduce(lambda x, y: y, it)
[None for _ in it]
这个代码行可以完成任务(或者类似的变体)吗? - Mathias711