如果我创建两个列表并压缩它们
a=[1,2,3]
b=[7,8,9]
z=zip(a,b)
Run Code Online (Sandbox Code Playgroud)
然后我将z分为两个列表
l1=list(z)
l2=list(z)
Run Code Online (Sandbox Code Playgroud)
然后l1的内容变得很好[(1,7),(2,8),(3,9)],但l2的内容只是[].
我想这是python关于iterables的一般行为.但作为从C家族迁移的新手程序员,这对我来说没有意义.为什么它会以这种方式表现?有没有办法解决这个问题?
我的意思是,是的,在这个特定的例子中,我可以将l1复制到l2中,但一般来说有一种方法可以'重置'Python用来迭代'z'后迭代它一次吗?
sen*_*rle 12
没有办法"重置"发电机.但是,您可以使用itertools.tee"复制"迭代器.
>>> z = zip(a, b)
>>> zip1, zip2 = itertools.tee(z)
>>> list(zip1)
[(1, 7), (2, 8), (3, 9)]
>>> list(zip2)
[(1, 7), (2, 8), (3, 9)]
Run Code Online (Sandbox Code Playgroud)
这涉及缓存值,因此只有以大约相同的速率迭代两个迭代时才有意义.(换句话说,不要像我在这里那样使用它!)
另一种方法是传递生成器函数,并在需要迭代时调用它.
def gen(x):
for i in range(x):
yield i ** 2
def make_two_lists(gen):
return list(gen()), list(gen())
Run Code Online (Sandbox Code Playgroud)
但是现在你必须在传递它时将参数绑定到生成器函数.你可以使用lambda它,但很多人觉得lambda很难看.(不是我!YMMV.)
>>> make_two_lists(lambda: gen(10))
([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
Run Code Online (Sandbox Code Playgroud)
我希望不言而喻,在大多数情况下,最好只制作一份清单并复制它.
此外,作为解释此行为的更一般方式,请考虑这一点.生成器的关键是产生一系列值,同时在迭代之间保持一些状态.现在,有时候,您可能希望执行以下操作,而不是简单地迭代生成器:
z = zip(a, b)
while some_condition():
fst = next(z, None)
snd = next(z, None)
do_some_things(fst, snd)
if fst is None and snd is None:
do_some_other_things()
Run Code Online (Sandbox Code Playgroud)
让我们说这个循环可能会或可能不会耗尽z.现在我们有一个处于不确定状态的发电机!因此,在这一点上,重要的是,发电机的行为以明确定义的方式受到限制.虽然我们不知道生成器在输出中的位置,但是我们知道a)所有后续访问都会在序列中产生更晚的值,并且b)一旦它"空",我们就完全得到了该系列中的所有项目一旦.我们操纵状态的能力越强z,推理它的难度就越大,所以我们最好避免破坏这两个承诺的情况.
当然,作为乔尔科尼特下面所指出的,它是可以编写接受经由消息发生器send方法; 并且可以编写一个可以使用重置的生成器send.但请注意,在这种情况下,我们所能做的就是发送消息.我们不能直接操纵生成器的状态,因此对生成器状态的所有更改都是明确定义的(由生成器本身 - 假设它被正确写入!).send真的是用于实现协同程序,所以我不会将它用于此目的.日常生成器几乎从不对发送给他们的价值做任何事情 - 我认为由于我给出的原因.