当我copy.copy
和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"
答案 0 :(得分:2)
您只是因为使用嵌套的iterables和简单的iterables而感到困惑。
关于copy
和您的第一个示例,您只需使用deepcopy
来创建可迭代的正确副本:
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
正如您所看到的,第一个for
循环遍历迭代,在这种情况下是[1,2,3]
和[4,5,6]
所以如果你只是复制生成器对象并实际创建一个浅拷贝对它来说,首先对next
的每次调用都将使用其中一个迭代,然后迭代迭代项。因此,当您致电next(a)
时,它已经消耗了第一个可迭代,以及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
,因为您只是将一个迭代传递给该函数,而不需要deepcopy
。
毕竟,复制生成器的最好(最pythonic)方法是使用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]