Как получить доступ к предыдущему / следующему элементу в цикле for?

Есть ли способ получить доступ к следующему или предыдущему элементу list (или tuple, или другому повторяемому элементу) при прохождении через него с помощью цикла for?

l = [1, 2, 3]
for item in l:
    if item == 2:
        get_previous(l, item)
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
48
0
75 405
13

Ответы 13

Итераторы имеют только метод next (), поэтому вы не можете смотреть вперед или назад, вы можете получить только следующий элемент.

enumerate (iterable) может быть полезен, если вы выполняете итерацию списка или кортежа.

Самый простой способ - найти элемент в списке:

def get_previous(l, item):
    idx = l.find(item)
    return None if idx == 0 else l[idx-1]

Конечно, это работает, только если список содержит только уникальные элементы. Другое решение:

for idx in range(len(l)):
    item = l[idx]
    if item == 2:
        l[idx-1]

Я не думаю, что есть простой способ, особенно потому, что итерация может быть генератор (без возврата). Вы можете сделать это с помощью последовательности, передав индекс элемента в тело цикла:

for index, item in enumerate(l):
    if index > 0:
        previous_item = l[index - 1]
    else:
        previous_item = None 

Функция enumerate() является встроенной.

Выражается как функция генератора:

def neighborhood(iterable):
    iterator = iter(iterable)
    prev_item = None
    current_item = next(iterator)  # throws StopIteration if empty.
    for next_item in iterator:
        yield (prev_item, current_item, next_item)
        prev_item = current_item
        current_item = next_item
    yield (prev_item, current_item, None)

Использование:

for prev,item,next in neighborhood(l):
    print prev, item, next

В этом случае я мог бы сделать «prev, item = item, next».

Paul Fisher 27.11.2008 20:31

Чтобы сделать этот цикл бесконечным (без StopIteration), выполните from itertools import cycle и измените вторую строку на: iterator = cycle(iterable)

Dennis Williamson 17.12.2009 04:50

Неужели в этом контексте менее Pythonic использовать enumerate?

batbrat 27.02.2014 10:40

Ответ, который вы ищете, - это ответ Вики Ляу, тот, что ниже этого. Или, если вы хотите, чтобы он работал с любыми итерациями, а не только со списками / кортежами / строками и т. д., один с использованием itertools.

Boris 28.12.2019 13:04

Просто сделайте for prev, cur, nxt in zip(l, l[1:], l[2:]): вместо всего этого кода. Если вам нужно установить prev и nxt на None для первой и последней итерации, просто выполните l = [None, *l, None] перед циклом for.

Boris 28.12.2019 20:12

Попытка использовать это в пустом списке вызывает RuntimeError (потому что это вызывает ошибку StopIteration).

Boris 28.12.2019 20:26

Выбрасывание StopIteration является нормальным и ожидаемым для функции генератора. При вызове в цикле for он просто завершает цикл.

Markus Jarderot 29.12.2019 04:25

@Boris «ниже этого» не очень точен, потому что с изменением оценки каждого ответа они могут повышаться или понижаться. Вы говорили об ответе Вики Лау?

Jean-Francois T. 09.04.2020 18:45

@ Жан-Франсуа. "Ответ, который вы ищете, - это ответ Вики Ляу"

Boris 09.04.2020 18:50

@Boris, стоит отметить, что ваше решение не хранит генераторы в качестве генераторов - l = [None, *l, None] распаковывает l в список. Исходное решение сохраняет генераторы как генераторы, что может быть тем, что вам нужно, например на память или побочные эффекты.

Sean 16.05.2020 12:20

При работе с генераторами, где вам нужен какой-то контекст, я часто использую приведенную ниже служебную функцию, чтобы отобразить скользящее окно на итераторе:

import collections, itertools

def window(it, winsize, step=1):
    """Sliding window iterator."""
    it=iter(it)  # Ensure we have an iterator
    l=collections.deque(itertools.islice(it, winsize))
    while 1:  # Continue till StopIteration gets raised.
        yield tuple(l)
        for i in range(step):
            l.append(it.next())
            l.popleft()

Он будет генерировать представление последовательности N элементов за раз, сдвигая шаг вперед. например.

>>> list(window([1,2,3,4,5],3))
[(1, 2, 3), (2, 3, 4), (3, 4, 5)]

При использовании в ситуациях, когда вам также нужно иметь дело с числами без следующего или предыдущего значения, вы можете дополнить последовательность соответствующим значением, например None.

l= range(10)
# Print adjacent numbers
for cur, next in window(l + [None] ,2):
    if next is None: print "%d is the last number." % cur
    else: print "%d is followed by %d" % (cur,next)

Если вы используете itertools, используйте вместо этого ответ Франсиско Кузо с itertools.tee: stackoverflow.com/a/41047005

Boris 09.04.2020 23:59

Сразу предыдущий?

Вы имеете в виду следующее, правда?

previous = None
for item in someList:
    if item == target: break
    previous = item
# previous is the item before the target

Если вам нужны предыдущие элементы п, вы можете сделать это с помощью своего рода циклической очереди размером п.

queue = []
for item in someList:
    if item == target: break
    queue .append( item )
    if len(queue ) > n: queue .pop(0)
if len(queue ) < n: previous = None
previous = previous[0]
# previous is *n* before the target
l = [1, 2, 3]
for i, item in enumerate(l):
    if item == 2:
        previous = l[i - 1]
        print(previous)

Выход:

1

Это завершится и вернет последний элемент в списке, если элемент, который вы ищете, является первым элементом в списке. Другими словами, изменение третьей строки на if item == 1: в приведенном выше коде приведет к печати 3.

Если ваш список состоит только из одного элемента l = [2], это вернет тот же элемент, что и предыдущий элемент.

Boris 09.04.2020 23:58

Проверьте утилиту петлителя из Проект Темпита. Он дает вам объект-оболочку вокруг элемента цикла, который предоставляет такие свойства, как предыдущий, следующий, первый, последний и т. д.

Взгляните на исходный код для класса лупера, это довольно просто. Существуют и другие такие помощники цикла, но я не могу вспомнить других прямо сейчас.

Пример:

> easy_install Tempita
> python
>>> from tempita import looper
>>> for loop, i in looper([1, 2, 3]):
...     print loop.previous, loop.item, loop.index, loop.next, loop.first, loop.last, loop.length, loop.odd, loop.even
... 
None 1 0 2 True False 3 True 0
1 2 1 3 False False 3 False 1
2 3 2 None False True 3 True 0

Ссылки все битые.

Jean-Francois T. 09.04.2020 18:49

Не очень питонический, но справляется и прост:

l=[1,2,3]
for index in range(len(l)):
    if l[index]==2:
        l[index-1]

ЧТО ДЕЛАТЬ: защитить края

Это точно так же, как ответ @Rudiger Wolf stackoverflow.com/a/324273

Boris 10.04.2020 00:07

Я знаю, что это старый, но почему бы просто не использовать enumerate?

l = ['adam', 'rick', 'morty', 'adam', 'billy', 'bob', 'wally', 'bob', 'jerry']

for i, item in enumerate(l):
    if i == 0:
        previous_item = None
    else:
        previous_item = l[i - 1]

    if i == len(l) - 1:
        next_item = None
    else:
        next_item = l[i + 1]

    print('Previous Item:', previous_item)
    print('Item:', item)
    print('Next Item:', next_item)
    print('')

    pass

Если вы запустите это, вы увидите, что он захватывает предыдущие и следующие элементы и не заботится о повторяющихся элементах в списке.

Почему отрицательные голоса? Это нормально и не требует внешних библиотек или специальных функций.

ronan_mac 29.04.2014 13:05

Вопрос также задан по предыдущему пункту. И не будет ли это работать неправильно, если элемент повторяется? [1,2,1,3]?

Teepeemm 31.05.2016 17:47

@Teepeemm, ага, обновлю до версии, отвечающей требованиям.

DuckPuncher 05.04.2017 21:19
l = [1, 2, 3]

for i, j in zip(l, l[1:]):
    print(i, j)

Я использовал это, но расширил, чтобы не отбрасывать начальные / конечные элементы: for prev,cur,next in zip([None]+l[:-1], l, l[1:]+[None]):

Maximus 24.09.2014 06:35

Вы можете не отбрасывать начальные / конечные элементы, например: l = [None, *l, None], а затем for prev, cur, nxt in zip(l, l[1:], l[2:]):

Boris 28.12.2019 20:14

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

import itertools

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

Я знаю, что это старый вопрос, но я считаю важным показать простое решение, которое также работает с генераторами и другими типами итераций, в отличие от большинства ответов, работающих только со списками, такими как объекты. Это несколько похоже на ответ Брайана и решение здесь: https://www.programcreek.com/python/example/1754/itertools.tee

import itertools

iter0, iter1 = itertools.tee(iterable)

for item, next_item in itertools.zip_longest(
    iter0,
    itertools.islice(iter1, 1, None)
):

    do_something(item, next_item)

В качестве альтернативы вызов next на второй итерации (если вы уверены, что у нее есть хотя бы один элемент):

import itertools

iter0, iter1 = itertools.tee(iterable)
_ = next(iter1)

for item, next_item in itertools.zip_longest(iter0, iter1):

    do_something(item, next_item)

Другие вопросы по теме