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
anitertools.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
如何处理迭代器对象。