假设我有一个范围:
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, ...
有没有办法结合range
和cycle
(或其他方式(来做到这一点?
只需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
如果start
和stop
参数很大,则此方法比islice
或dropwhile
具有优势,因为它不需要丢弃不需要的初始项。
也许不美观但实用?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)