从特定数字开始的范围内的无限循环



假设我有一个范围:

r = range(1, 6)

使用此范围,我想无限循环并产生数字:

for i in cycle(r):
yield(i)

这将正确生成以下值:

1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, ...

但是,我有时想从特定值开始收益,但继续定义范围。也就是说,如果我想从3开始,顺序将是:

3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, ...

有没有办法结合rangecycle(或其他方式(来做到这一点?

只需dropwhile,直到达到要发出的第一个值:

>>> from itertools import cycle, dropwhile
>>> iterable = dropwhile(lambda x: x < 3, cycle(range(1, 6)))
>>> for _ in range(10):
...     print(next(iterable))
...
3
4
5
1
2
3
4
5
1
2

根据文档(强调我的(:

创建一个迭代器,从可迭代对象中删除元素,只要 谓词为真;之后,返回每个元素

predicate仅在计算 false-y 的第一个值之前生效。

由于cycle从提供给它的迭代器的开头开始,因此请为其提供一个从您想要的位置开始的迭代器(在本例中为序列(:

r = tuple(range(3,6)) + tuple(range(1,3))

上面的1是要重复的最低值,6比最大值多一个,使用两次的3是起始值。应该清楚如何将其推广到其他情况。

另一种方法是将两个范围链接在一起并将链传递给cycle

from itertools import cycle, chain
def shift_cycle(lo, start, stop):
return cycle(chain(range(start, stop), range(lo, start)))
for t in zip(range(12), shift_cycle(1, 3, 6)):
print('{}: {}'.format(*t))

输出

0: 3
1: 4
2: 5
3: 1
4: 2
5: 3
6: 4
7: 5
8: 1
9: 2
10: 3
11: 4

如果startstop参数很大,则此方法比islicedropwhile具有优势,因为它不需要丢弃不需要的初始项。

也许不美观但实用?sys.maxsize出于多种目的"实际上是无限的">

import sys
r, n = 5, 3
cyc = (i%r + 1 for i in range(n, sys.maxsize))
next(cyc)
Out[106]: 4
next(cyc)
Out[107]: 5
next(cyc)
Out[108]: 1
next(cyc)
Out[109]: 2
next(cyc)
Out[110]: 3
next(cyc)
Out[111]: 4
next(cyc)
Out[112]: 5
next(cyc)
Out[113]: 1

sys.maxsize*1e-9/3600/24/365 Out[117]: 292.471208677536

每个请求 1 ns 的年数 - 在 64 位系统上

但当然它运行得慢一点

timeit.timeit('next(cyc)','r, n = 5, 3; cyc = (i%r + 1 for i in range(n, sys.maxsize))')
Out[126]: 0.2556792100261305

随着请求不断上升,模数需要更多时间i

但这似乎不是最大的时间槽

timeit.timeit('max%5', 'max=sys.maxsize')
Out[120]: 0.07545763840474251
timeit.timeit('1111%5')
Out[122]: 0.01156394737682831
timeit.timeit('111%5')
Out[123]: 0.011720469965638358

您正在寻找islice函数

from itertools import islice, cycle
offset = 2
r = range(1, 6)
generator = islice(cycle(r), offset, None)

最新更新