Python 复制链

Python 复制链,python,copy,itertools,Python,Copy,Itertools,当我copy.copyanitertools.chain时,我注意到一些奇怪的行为: from copy import copy from itertools import chain 当我耗尽其中一个时,结果与预期一致: >>> a = chain([1,2,3], [4,5,6]) >>> b = copy(a) >>> list(a), list(b) ([1, 2, 3, 4, 5, 6], []) >>> a,

当我
copy.copy
an
itertools.chain
时,我注意到一些奇怪的行为:

from copy import copy
from itertools import chain
当我耗尽其中一个时,结果与预期一致:

>>> a = chain([1,2,3], [4,5,6])
>>> b = copy(a)
>>> list(a), list(b)
([1, 2, 3, 4, 5, 6], [])

>>> a, b = chain_and_copy()
>>> list(b), list(a)
([1, 2, 3, 4, 5, 6], [])
但是,当我使用
next
时,结果似乎很奇怪:

>>> a = chain([1,2,3], [4,5,6])
>>> b = copy(a)
>>> next(a), list(b), list(a)
(1, [4, 5, 6], [2, 3])   # b "jumps" to the second iterable...

>>> a = chain([1,2,3], [4,5,6])
>>> next(a)
1
>>> b = copy(a)
>>> next(a), next(b), next(a)
(2, 3, 4)
>>> next(b)   # b is empty
StopIteration:
>>> next(a)   # a is not empty
5
这是一个Bug还是简单地复制迭代器通常是个坏主意?我注意到一份
iter
和一份
zip
的行为也不同:

>>> a = zip([1,2,3], [4,5,6])
>>> b = copy(a)
>>> next(a), next(b)
((1, 4), (2, 5))  # copies share the same "position"

>>> a = iter([1,2,3])
>>> b = copy(a)
>>> next(a), next(b)
(1, 1)   # copies don't share the same "position"

您只是被没有使用嵌套的iterables和简单的iterables弄糊涂了

关于
copy
和您的第一个示例,您只需使用
deepcopy
即可创建您的iterable的正确副本:

In [87]: a = chain([1,2,3], [4,5,6])

In [88]: b = deepcopy(a)

In [89]: list(a)
Out[89]: [1, 2, 3, 4, 5, 6]

In [90]: list(b)
Out[90]: [1, 2, 3, 4, 5, 6]
而且
next
也没有什么特别之处。下面是python文档中的
chain
函数的等价物:

def chain(*iterables):
    # chain('ABC', 'DEF') --> A B C D E F
    for it in iterables:
        for element in it:
            yield element
如您所见,的第一个
在iterables上循环,在本例中,iterables是
[1,2,3]
[4,5,6]
,因此,如果您只是复制generator对象并实际创建它的浅层副本,那么对
下一个
的每次调用首先都将消耗一个iterables,然后,它迭代iterable项。因此,当您调用
next(a)
时,它已经使用了第一个iterable,这就是
list(b)
返回
[4,5,6]
的原因

同样,如果您使用
deepcopy
,您将不会再看到这种行为

In [94]: a = chain([1,2,3], [4,5,6])

In [95]: b = deepcopy(a)

In [96]: next(a), list(b), list(a)
Out[96]: (1, [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6])
这同样适用于
zip
,因为您要向函数传递多个iterable。如果您使用
deepcopy
,您将得到不同的对象:

In [100]: a = zip([1,2,3], [4,5,6])

In [101]: b = deepcopy(a)

In [102]: next(a), next(b)
Out[102]: ((1, 4), (1, 4))
但是
copy
对于
iter
来说效果很好,因为您只是将一个iterable传递给函数,而不需要
deepcopy

毕竟,复制生成器的最佳(最具Python风格)方法是使用
itertools.tee

In [103]: from itertools import tee

In [104]: a = zip([1,2,3], [4,5,6])

In [105]: a, b = tee(a)

In [106]: list(a)
Out[106]: [(1, 4), (2, 5), (3, 6)]

In [107]: list(b)
Out[107]: [(1, 4), (2, 5), (3, 6)]

In [108]: 

In [108]: a = chain([1,2,3], [4,5,6])

In [109]: a, b = tee(a)

In [110]: list(a)
Out[110]: [1, 2, 3, 4, 5, 6]

In [111]: list(b)
Out[111]: [1, 2, 3, 4, 5, 6]

使用
tee
来“复制”你的生成器:另外@Jean-FrançoisFabre实际上我希望它能像
zip
的浅拷贝一样工作(在迭代器之间共享位置)。使用
itertools.tee
copy.deepcopy
pickle
无法真正实现这一点。如果我想要一个深拷贝,我就不会使用浅拷贝操作
copy.copy
,首先:)我没有结束这个问题,因为我有一个疑问,而且你已经对python了解很多了。但我不会复制迭代器。我读了一些问答(现在找不到),解释说它肯定会失败。迭代器可以是许多不同的东西。有些具有
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。。。我不是专家,但我觉得这是XY问题:)好吧,问题是关于我试图调试和修复的bug的根本原因。是的,可能是XY问题,因为答案似乎是“[…]我不会复制迭代器。我读了一些问答(现在找不到),其中解释了它肯定会失败。”。谢谢你的答案。这是有道理的,但是我不明白传递多个iterable的意义。这可能令人毛骨悚然,但我在iterables中传递的是
chain(a,b)
zip(a,b)
,那么为什么我希望一个浅拷贝不会复制迭代器本身呢?如果我将它们作为数据结构传入,比如
chain([a,b])
zip([a,b])
(这些显然不起作用,只是为了说明这一点)。然而,python生成器是一个完全不同的主题,因为它们无论如何都不能被复制。但是我的意思是“不要浅复制迭代器!”对吗?事实上,
a,b
是一个元组。但是is与复制迭代器的方式无关,这是因为
copy
的工作方式。我也不是说不要复制迭代器。像
chain
zip
这样先在iterables上迭代的函数不能像嵌套列表一样使用
copy
函数正确复制。@mseifer但是,如果您还不满意,您应该查看
copy
deepcopy
的源代码,看看它们是如何实际工作的。我不太清楚
copy
如何处理迭代器对象。