Итерировать список как пару (текущий, следующий) в Python

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

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

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

Ответ 1

Здесь соответствующий пример из модуля itertools docs:

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:

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

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

А поскольку 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)

Ответ 2

Так как the_list[1:] фактически создает копию всего списка (исключая его первый элемент), а zip() создает список кортежей сразу после вызова, в общей сложности создаются три копии вашего списка. Если ваш список очень большой, вы можете предпочесть

from itertools import izip, islice
for current_item, next_item in izip(the_list, islice(the_list, 1, None)):
    print(current_item, next_item)

который не копирует список вообще.

Ответ 3

Сканируй свой собственный!

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

    for b in it:
        yield (a, b)
        a = b

Ответ 4

Итерация по индексу может сделать то же самое:

#!/usr/bin/python
the_list = [1, 2, 3, 4]
for i in xrange(len(the_list) - 1):
    current_item, next_item = the_list[i], the_list[i + 1]
    print(current_item, next_item)

Вывод:

(1, 2)
(2, 3)
(3, 4)

Ответ 5

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

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

Ответ 6

Пары из списка с использованием понимания списка

the_list = [1, 2, 3, 4]
pairs = [[the_list[i], the_list[i + 1]] for i in range(len(the_list) - 1)]
for [current_item, next_item] in pairs:
    print(current_item, next_item)

Вывод:

(1, 2)
(2, 3)
(3, 4)

Ответ 7

Базовое решение:

def neighbors( list ):
  i = 0
  while i + 1 < len( list ):
    yield ( list[ i ], list[ i + 1 ] )
    i += 1

for ( x, y ) in neighbors( list ):
  print( x, y )

Ответ 8

code = '0016364ee0942aa7cc04a8189ef3'
# Getting the current and next item
print  [code[idx]+code[idx+1] for idx in range(len(code)-1)]
# Getting the pair
print  [code[idx*2]+code[idx*2+1] for idx in range(len(code)/2)]