如何从 Python 中的特定点开始 Itertools 循环?

How to start Itertools cycle from a particular point in Python?

我使用下面的代码为英文字母表创建了一个 itertools 循环,

lowercase_letters_cycle = itertools.cycle(string.ascii_lowercase)

如果我 运行 在此迭代器对象上进行 for 循环,第一次迭代将给我“a”作为输出,因为循环从“a”开始。我怎样才能使循环从我选择的任何字母开始?

一种可行的方法是,

def start_cycle(letter):
  lowercase_letters_cycle = itertools.cycle(lowercase_letters)
  letter_index = lowercase_letters.index(letter)
  index = 0

  while True:
    if index == letter_index:
      break

    letter = next(lowercase_letters_cycle)
    index += 1

  return lowercase_letters_cycle

但是有没有更短的方法呢?

您可以只对输入列表进行切片以从您选择的索引开始。

offset = 10
lowercase_letters = list(string.ascii_lowercase)

#                 offset_index to end       + start to offset_index-1
offset_letters = lowercase_letters[offset:] + lowercase_letters[:offset]
offset_letters_cycle = itertools.cycle(offset_letters)

然后,

for i in range(10):
    print(next(offset_letters_cycle), end=" ")

打印:

k l m n o p q r s t 

itertools 文档提供了一个 recipe 用于使用迭代器中的多个项目。

from itertools import islice
import collections


def consume(iterator, n=None):
    "Advance the iterator n-steps ahead. If n is None, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

所以你创建了循环,然后在继续之前消耗了一点。

lowercase_letters_cycle = itertools.cycle(string.ascii_lowercase)

consume(lowercase_letters_cycle, ord('n') - ord('a'))

assert next(lowercase_letters_cycle) == 'n')

同样的 consume 也可以从 third-party more-itertools package 获得。

您可以组合 itertools 模块中的 islicecycle,如下所示:

import string 
import itertools

my_it = itertools.islice(itertools.cycle(string.ascii_lowercase), 3, None)

它将产生d(前3个字符之后的字符),然后是e,....,然后是z,然后是a,然后 b,依此类推。您可以将第二个参数中的数字更改为 islice 以从不同的字母开始。


import string
from itertools import cycle, dropwhile

def start_cycle(letter):
    return dropwhile(lambda x: x!= letter, cycle(string.ascii_lowercase))

itertools.dropwhile 会将可迭代结果提供给它的哨兵函数 - 在本例中为 lambda x: x != letter,并将“吞噬”结果直到函数 returns False for第一次。在那时候 该函数不再被调用,可迭代继续进行,产生任何 进一步的价值。

此外,请注意无需将 ascii_lowercase 转换为列表, 因为字符串已经是可迭代的了。

请记住,如果传递的字符不在 ascii_lowercase 中,这将 运行 进入 100% CPU 的无限循环。最好用检查来保护它:


def start_cycle(letter):
    if letter not in string.ascii_lowercase:
         raise ValueError()
    return dropwhile(lambda x: x!= letter, cycle(string.ascii_lowercase))