复制一个 itertools 循环对象

Copy an itertools cycle object

我想制作一个 itertools.cycle 对象的浅表副本,但我不知道怎么做,因为它没有内置的复制方法。我想实现如下所示,我创建循环的副本,迭代几次,然后再次复制原始副本,再迭代几次从循环的开头开始.

c = "ABCD"
cyc = itertools.cycle(c)

cyc_copy = cyc.copy()
for i in range(2):
    print(next(cyc_copy))
cyc_copy = cyc.copy()
for i in range(2):
    print(next(cyc_copy))

> A
  B
  A
  B

copy 应该可以解决问题:

>>> from copy import copy
>>> cyc_copy = copy(cyc)
>>> next(cyc_copy)
'A'
>>> next(cyc_copy)
'B'
>>> cyc_copy = copy(cyc)
>>> next(cyc_copy)
'A'
>>> next(cyc_copy)
'B'

复制循环本身会 运行 出问题。例如,copy.copying it doesn't produce independent copies.

与其尝试复制循环,我建议从您的原始对象重新创建它:

new_cyc = itertools.cycle(c)

如果您创建原始循环的对象是一个迭代器,您不能只是重复调用 cycle。相反,在第一次调用 cycle 之前列一个列表,并保留列表:

c_list = list(c)
cyc = itertools.cycle(c_list)

# later
new_cyc = itertools.cycle(c_list)

如果您创建原始循环的对象是一个可能无限也可能不是无限的迭代器,您不能安全地对其调用 list。相反,您可以 tee 创建循环之前 copy.copy 在需要创建新循环时使用不高级的发球台。 (tee支持复制。)

c_tee, c_tee2 = itertools.tee(c)
cyc = itertools.cycle(c_tee2)

# Copy c_tee, not the c_tee2 we already used.
new_cyc = itertools.cycle(copy.copy(c_tee))

所有这些都假定您控制循环的创建。如果您从其他地方接收到一个循环,您可能无法访问它循环过的对象。在这种情况下,您最好的选择是 tee 循环本身。如果您需要经历循环的许多循环,这可能会很昂贵:

cyc_master, cyc1 = itertools.tee(cyc)
# Use cyc1

# Later
cyc2 = copy.copy(cyc_master)

您可以创建自定义 class 来执行您想要的操作:

import itertools

class CopyCycle:
    def __init__(self, iterable):
        self.iterable = iterable
        self._cycle = itertools.cycle(self.iterable)

    def cycle(self):
        return self

    def __iter__(self):
        return self

    def next(self):
        return self._cycle.next()

    def __next__(self):  #Python 3+
        return self._cycle.next()

    def copy(self):
        return CopyCycle(self.iterable)


if __name__ == '__main__':
    cyc = CopyCycle("ABCD").cycle()
    for i in range(5):
        print(next(cyc))

    cyc_copy = cyc.copy()
    for i in range(2):
        print(next(cyc_copy))
    cyc_copy = cyc.copy()
    for i in range(2):
        print(next(cyc_copy))

输出:

A
B
C
D
A
A
B
A
B

它可能需要一些重构,但工厂在这里会工作得很好。

from itertools import cycle

cycle_factory = lambda: cycle('1234')

c1 = cycle_factory()
print next(c1) # 1

c2 = cycle_factory()
print next(c2) # 1

否则,我不确定你是否能够满足每次都在循环开始时开始的标准。基于 class 的方法也可以工作,但需要更多的开销。

itertools.tee 方法的一个问题是它会在 tee-d 迭代器停止的地方恢复迭代,而不是从头开始。因此,您必须在一开始就开球。如果您无法控制循环的生成方式,这可能是唯一的选择。

方法一:制作两个itertools.cycle对象

import itertools
c = 'ABCD'
cyc1 = itertools.cycle(c)
cyc2 = itertools.cycle(c)
for _ in range(2): print(next(cyc1))  # prints A\nB\n
for _ in range(2): print(next(cyc2))  # prints A\nB\n

<script src="//repl.it/embed/IRcx/0.js"></script>

[首选方案]方法二:使用itertools.tee

拆分为n个迭代器
import itertools
cyc = itertools.cycle('ABCD')
cyc1, cyc2 = itertools.tee(cyc, 2)
for _ in range(2): print(next(cyc1))  # prints A\nB\n
for _ in range(2): print(next(cyc2))  # prints A\nB\n

<script src="//repl.it/embed/IRcx/2.js"></script>

Warning using copy module using the copy.copy function 将不会按预期创建迭代器的副本。

import itertools, copy
cyc = itertools.cycle('ABCD')
cyc1 = copy.copy(cyc)
cyc2 = copy.copy(cyc)
for _ in range(2): print(next(cyc1))  # prints A\nB\n
for _ in range(2): print(next(cyc2))  # prints C\nD\n

<script src="//repl.it/embed/IRcx/3.js"></script>

使用 copy 模块的解决方法: 一个可能的解决方案是使用 copy.deepcopy 函数。

import itertools, copy
cyc = itertools.cycle('ABCD')
cyc1 = copy.deepcopy(cyc)
cyc2 = copy.deepcopy(cyc)
for _ in range(2): print(next(cyc1))  # prints A\nB\n
for _ in range(2): print(next(cyc2))  # prints A\nB\n

<script src="//repl.it/embed/IRcx/4.js"></script>