Иногда мне нужно перебирать список в Python, глядя на «текущий» элемент и «следующий» элемент. До сих пор я делал это с помощью кода вроде:
for current, next in zip(the_list, the_list[1:]):
# Do something
Это работает и делает то, что я ожидаю, но есть ли более идиоматичный или эффективный способ сделать то же самое?
next
этого способа маскирует встроенный.next
также является встроенной функцией в Python 2.Ответы:
Вот соответствующий пример из документации модуля itertools :
Для Python 2
itertools.izip
вместоzip
:Как это работает:
Сначала создаются два параллельных итератора
a
иb
(tee()
вызов), указывающие на первый элемент исходного итератора. Второй итераторb
перемещается на 1 шаг вперед (next(b, None)
вызов). В этот моментa
указывает на s0 иb
указывает на s1. Обаa
иb
могут обходить исходный итератор независимо - функция izip берет два итератора и создает пары из возвращенных элементов, продвигая оба итератора с одинаковой скоростью.Одно предостережение:
tee()
функция создает два итератора, которые могут продвигаться независимо друг от друга, но за это приходится платить. Если один из итераторов продвигается дальше, чем другой, тоtee()
потребляемые элементы должны оставаться в памяти до тех пор, пока второй итератор тоже их не поглотит (он не может «перемотать» исходный итератор). Здесь это не имеет значения, потому что один итератор всего на 1 шаг впереди другого, но в целом таким образом легко использовать много памяти.И поскольку
tee()
может приниматьn
параметр, его также можно использовать для более чем двух параллельных итераторов:источник
zip(ł, ł[1:])
намного короче иfuncy
модуле:funcy.pairwise
: funcy.readthedocs.io/en/stable/seqs.html#pairwiseБрось свой!
источник
Поскольку
the_list[1:]
фактически создает копию всего списка (за исключением его первого элемента) иzip()
создает список кортежей сразу при вызове, в общей сложности создаются три копии вашего списка. Если ваш список очень большой, вы можете предпочестькоторый вообще не копирует список.
источник
the_list[1:]
просто создание объекта-среза, а не копия почти всего списка - так что техника OP не так расточительна, как вы говорите.[1:]
создает объект среза (или, возможно, "1:
"), который передается__slice__
в список, который затем возвращает копию, содержащую только выбранные элементы. Один идиоматический способ скопировать списокl_copy = l[:]
(который я считаю уродливым и нечитабельным - предпочитаюl_copy = list(l)
)__slice__
Специального метода нет.the_list[1:]
эквивалентноthe_list[slice(1, None)]
, что, в свою очередь, эквивалентноlist.__getitem__(the_list, slice(1, None))
.the_list[1:]
является лишь мелкой копией, поэтому она состоит только из одного указателя на элемент списка. Сама часть, интенсивно использующая памятьzip()
, потому что она создает список из одногоtuple
экземпляра для каждого элемента списка, каждый из которых будет содержать два указателя на эти два элемента и некоторую дополнительную информацию. Этот список будет потреблять в девять раз больше памяти, чем[1:]
потребляет копия .Я просто выкладываю это и очень удивлен, что никто не подумал о enumerate ().
источник
if
их также можно удалить, если использовать нарезку:for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]
Итерация по индексу может сделать то же самое:
Вывод:
источник
i
всегда это индекс текущего элемента.Теперь это простой импорт с 16 мая 2020 г.
Документы для more-itertools Под капотом этот код такой же, как и в других ответах, но я предпочитаю импорт, когда он доступен.
Если он еще не установлен, выполните следующие действия:
pip install more-itertools
пример
Например, если у вас есть последовательность Фиббонначчи, вы можете рассчитать отношения последующих пар как:
источник
Пары из списка с использованием понимания списка
Вывод:
источник
Я действительно удивлен, что никто не упомянул более короткое, простое и, самое главное, общее решение:
Python 3:
Python 2:
Он работает для попарной итерации путем передачи
n=2
, но может обрабатывать любое большее число:источник
Базовое решение:
источник
источник