简体   繁体   English

如何在 itertools.cycle 函数中指定从哪里开始

[英]How to specify where to start in an itertools.cycle function

I need to cycle through a list for starting position between 1-4 using itertools I am able to cycle through the list我需要使用itertools循环浏览1-4之间起始位置的列表我能够循环浏览列表

positions = itertools.cycle([1,2,3,4])
next(positions)

This does return the next position, but what if the next time I need to start at 3?这确实返回了下一个位置,但是如果下次我需要从 3 开始呢? How can I set the start position?如何设置起始位置?

I need the start position to change often, I cant just change the list to start at 3.我需要经常更改开始位置,我不能只是将列表更改为从 3 开始。

You can't set a starting position;你不能设置起始位置; it'll always start where the given sequence starts.它总是从给定序列开始的地方开始。

You can move the cycle along a few steps before you use it for whatever you need it for.可以将循环向前移动几个步骤,然后再将其用于任何您需要的用途。 Use itertools.islice() to skip some items:使用itertools.islice()跳过一些项目:

from itertools import islice

starting_at_three = islice(positions, 2, None)

You pass in the iterable, then a start and stop value;您传入可迭代对象,然后是开始和停止值; None here means that the islice() iterator continues forever or until the underlying positions iterator is exhausted.这里的None意味着islice()迭代器永远持续下去,或者直到底层positions迭代器耗尽。

Demo:演示:

>>> from itertools import islice, cycle
>>> positions = cycle([1, 2, 3, 4])
>>> starting_at_three = islice(positions, 2, None)
>>> next(starting_at_three)
3
>>> next(starting_at_three)
4
>>> next(starting_at_three)
1

The other option is to pass in a different sequence;另一种选择是传入不同的序列; you could pass in [3, 4, 1, 2] for example.例如,您可以传入[3, 4, 1, 2]

You can use itertools.islice for that:您可以使用itertools.islice

from itertools import cycle
from itertools import islice

positions3 = islice(cycle([1,2,3,4]),2,None)

this will result in a generator that emits 3,4,1,2,3,4,1,2,3,4,...这将导致生成器发出 3,4,1,2,3,4,1,2,3,4,...

In case the start position k is large (compared to the length of the original list), it can pay off to perform a modulo first:如果起始位置k很大(与原始列表的长度相比),则可以先执行取模:

from itertools import cycle
from itertools import islice

source_list = [1,2,3,4]
k = 10000000 # offset index
positions_k = islice(cycle(source_list),k%len(source_list),None)

This will generate an equivalent result, but islice will not drop the first 10M elements.这将生成等效的结果,但islice不会删除前 10M 元素。

Use slices of the original list:使用原始列表的切片:

In [15]: def custom_slice(lst, start):
   ....:     return cycle(lst[start:] + lst[:start + 1])

Demo:演示:

In [16]: positions = custom_slice(lst, 2)

In [17]: next(positions)
Out[17]: 3

In [18]: next(positions)
Out[18]: 4

In [19]: next(positions)
Out[19]: 1

In [20]: next(positions)
Out[20]: 2

You can do this with itertools.dropwhile .你可以用itertools.dropwhile做到这一点。 It will ignore values until the predicate function is satisfied, then continue iterating over the entire cycle.它将忽略值,直到满足谓词函数为止,然后在整个循环中继续迭代。

>>> c = itertools.cycle([1, 2, 3, 4, 5])
>>> dw = itertools.dropwhile(lambda x: x < 3, c)
>>> [next(dw) for _ in range(10)]
[3, 4, 5, 1, 2, 3, 4, 5, 1, 2]

However, as noted in the docs但是,如文档中所述

the iterator does not produce any output until the predicate first becomes false, so it may have a lengthy start-up time迭代器在谓词第一次变为假之前不会产生任何输出,因此它可能有很长的启动时间

So it's better to use islice for this use case.所以最好在这个用例中使用islice

>>> timeit.timeit(setup='from itertools import cycle, islice', 
      stmt='c = cycle(range(1000)); sl = islice(c, 900, None); assert next(sl) == 900')
35.37439359538257

>>> timeit.timeit(setup='from itertools import cycle, dropwhile', 
      stmt='c = cycle(range(1000)); dw = dropwhile(lambda x: x < 900, c); assert next(dw) == 900')
131.17783254291862

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM