Вопрос-Ответ

How can I iterate over overlapping (current, next) pairs of values from a list?

Как я могу выполнить итерацию по перекрывающимся (текущим, следующим) парам значений из списка?

Иногда мне нужно выполнить итерацию списка в Python, просматривая "текущий" элемент и "следующий" элемент. До сих пор я делал это с помощью кода типа:

for current, next in zip(the_list, the_list[1:]):
# Do something

Это работает и делает то, что я ожидаю, но есть ли более идиоматичный или эффективный способ сделать то же самое?


Некоторые ответы на эту проблему можно упростить, обратившись к конкретному случаю одновременного использования только двух элементов. Для общего случая одновременного использования N элементов см. Rolling or sliding window iterator?.

Переведено автоматически
Ответ 1

В документации для 3.8 представлен этот рецепт:

import itertools
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = itertools.tee(iterable)
next(b, None)
return zip(a, b)

Для Python 2 используйте itertools.izip вместо zip, чтобы получить такой же отложенный итератор (zip вместо этого создаст список):

import itertools
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = itertools.tee(iterable)
next(b, None)
return itertools.izip(a, b)

Как это работает:

Сначала создаются два параллельных итератора, a и b (tee() вызов), оба указывающие на первый элемент исходного итерируемого. Второй итератор, b перемещается на 1 шаг вперед (вызов next(b, None))). В этот момент a указывает на s0 и b указывает на s1. Оба a и b могут проходить исходный итератор независимо - функция izip принимает два итератора и создает пары возвращаемых элементов, продвигая оба итератора с одинаковой скоростью.

Поскольку tee() может принимать n параметр (количество итераторов для создания), тот же метод может быть адаптирован для создания большего "окна". Например:

def threes(iterator):
"s -> (s0, s1, s2), (s1, s2, s3), (s2, s3, 4), ..."
a, b, c = itertools.tee(iterator, 3)
next(b, None)
next(c, None)
next(c, None)
return zip(a, b, c)

Предостережение: если один из итераторов, созданных tee, продвигается дальше, чем другие, то реализации необходимо сохранять используемые элементы в памяти до тех пор, пока каждый итератор не израсходует их (он не может "перемотать" исходный итератор). Здесь это не имеет значения, потому что один итератор всего на 1 шаг впереди другого, но в целом таким образом легко использовать большой объем памяти.

Ответ 2

Создавайте свои собственные!

def pairwise(iterable):
it = iter(iterable)
a = next(it, None)

for b in it:
yield (a, b)
a = b
Ответ 3

Начиная с Python 3.10, это точная роль функции pairwise:

from itertools import pairwise

list(pairwise([1, 2, 3, 4, 5]))
# [(1, 2), (2, 3), (3, 4), (4, 5)]

или просто pairwise([1, 2, 3, 4, 5]) если вам не нужен результат в виде list.

Ответ 4

Я просто выкладываю это, я очень удивлен, что никто не додумался до enumerate().

for (index, thing) in enumerate(the_list):
if index < len(the_list):
current, next_ = thing, the_list[index + 1]
#do something
python