`zip()` 中生成器的额外 next()?
Extra next() for generator in `zip()`?
给定,
import itertools as it
def foo():
idx = 0
while True:
yield idx
idx += 1
k = foo()
当我如下使用zip()
时,
>>> list(zip(k,[11,12,13]))
[(0, 11), (1, 12), (2, 13)]
紧接着,
>>> list(zip(k,[11,12,13]))
[(4, 11), (5, 12), (6, 13)]
请注意,第二个 zip 应该以 (3,11)
开头,但它跳到了 (4,11)
反而。就好像某处隐藏着另一个next(k)
。当我使用 it.islice
时不会发生这种情况
>>> k = foo()
>>> list(it.islice(k,6))
[0, 1, 2, 3, 4, 5]
注意 it.islice
没有遗漏 3
项。
我正在使用 Python 3.8.
zip
基本上(并且必然,考虑到迭代器协议的设计)是这样工作的:
# zip is actually a class, but we'll pretend it's a generator
# function for simplicity.
def zip(xs, ys):
# zip doesn't require its arguments to be iterators, just iterable
xs = iter(xs)
ys = iter(ys)
while True:
x = next(xs)
y = next(ys)
yield x, y
在 xs
的元素被消耗之前无法判断 ys
是否耗尽,并且迭代器协议没有为 zip
提供放置 x
如果 next(ys)
引发 StopIteration
异常,xs
中的“返回”。
对于其中一个输入迭代器是 sized 的特殊情况,您可以比 zip
:
做得更好一点
import itertools as it
from collections.abc import Sized
def smarter_zip(*iterables):
sized = [i for i in iterables if isinstance(i, Sized)]
try:
min_length = min(len(s) for s in sized)
except ValueError:
# can't determine a min length.. fall back to regular zip
return zip(*iterables)
return zip(*[it.islice(i, min_length) for i in iterables])
它使用 islice
to prevent zip
从每个迭代器中消耗比我们知道的更多是绝对必要的。这smarter_zip
将解决原始问题中提出的案例的问题。
然而,在一般情况下,没有办法事先判断迭代器是否耗尽(考虑一个生成器产生到达套接字的字节)。如果最短的 iterables 没有大小,原来的问题仍然存在。为了解决一般情况,您可能希望将迭代器包装在一个 class 中,它会记住最后生成的项目,以便在必要时可以从内存中调用它。
给定,
import itertools as it
def foo():
idx = 0
while True:
yield idx
idx += 1
k = foo()
当我如下使用zip()
时,
>>> list(zip(k,[11,12,13]))
[(0, 11), (1, 12), (2, 13)]
紧接着,
>>> list(zip(k,[11,12,13]))
[(4, 11), (5, 12), (6, 13)]
请注意,第二个 zip 应该以 (3,11)
开头,但它跳到了 (4,11)
反而。就好像某处隐藏着另一个next(k)
。当我使用 it.islice
>>> k = foo()
>>> list(it.islice(k,6))
[0, 1, 2, 3, 4, 5]
注意 it.islice
没有遗漏 3
项。
我正在使用 Python 3.8.
zip
基本上(并且必然,考虑到迭代器协议的设计)是这样工作的:
# zip is actually a class, but we'll pretend it's a generator
# function for simplicity.
def zip(xs, ys):
# zip doesn't require its arguments to be iterators, just iterable
xs = iter(xs)
ys = iter(ys)
while True:
x = next(xs)
y = next(ys)
yield x, y
在 xs
的元素被消耗之前无法判断 ys
是否耗尽,并且迭代器协议没有为 zip
提供放置 x
如果 next(ys)
引发 StopIteration
异常,xs
中的“返回”。
对于其中一个输入迭代器是 sized 的特殊情况,您可以比 zip
:
import itertools as it
from collections.abc import Sized
def smarter_zip(*iterables):
sized = [i for i in iterables if isinstance(i, Sized)]
try:
min_length = min(len(s) for s in sized)
except ValueError:
# can't determine a min length.. fall back to regular zip
return zip(*iterables)
return zip(*[it.islice(i, min_length) for i in iterables])
它使用 islice
to prevent zip
从每个迭代器中消耗比我们知道的更多是绝对必要的。这smarter_zip
将解决原始问题中提出的案例的问题。
然而,在一般情况下,没有办法事先判断迭代器是否耗尽(考虑一个生成器产生到达套接字的字节)。如果最短的 iterables 没有大小,原来的问题仍然存在。为了解决一般情况,您可能希望将迭代器包装在一个 class 中,它会记住最后生成的项目,以便在必要时可以从内存中调用它。