Python - Предыдущие и следующие значения внутри цикла - PullRequest
64 голосов
/ 18 июня 2009

Как я могу сделать что-то подобное в Python?

foo = somevalue
previous = next = 0

for (i=1; i<objects.length(); i++) {
    if (objects[i]==foo){
        previous = objects[i-1]
        next = objects[i+1]
    }
}

Ответы [ 11 ]

121 голосов
/ 18 июня 2009

Решения до сих пор имеют дело только со списками, и большинство копирует список. По моему опыту, много раз это было невозможно.

Кроме того, они не связаны с тем фактом, что в списке могут быть повторяющиеся элементы.

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

Итак, я только что создал функцию. используя модуль itertools, разбивает и разбивает итерируемое и генерирует кортежи с предыдущими и последующими элементами вместе. Не совсем то, что делает ваш код, но на это стоит взглянуть, потому что это, вероятно, решит вашу проблему.

from itertools import tee, islice, chain, izip

def previous_and_next(some_iterable):
    prevs, items, nexts = tee(some_iterable, 3)
    prevs = chain([None], prevs)
    nexts = chain(islice(nexts, 1, None), [None])
    return izip(prevs, items, nexts)

Затем используйте его в цикле, и у вас будут предыдущие и следующие элементы:

mylist = ['banana', 'orange', 'apple', 'kiwi', 'tomato']

for previous, item, nxt in previous_and_next(mylist):
    print "Item is now", item, "next is", nxt, "previous is", previous

Результаты:

Item is now banana next is orange previous is None
Item is now orange next is apple previous is banana
Item is now apple next is kiwi previous is orange
Item is now kiwi next is tomato previous is apple
Item is now tomato next is None previous is kiwi

Он будет работать с любым списком размеров (потому что он не копирует список) и с любыми повторяемыми (файлами, наборами и т. Д.). Таким образом, вы можете просто перебирать последовательность и иметь в цикле предыдущий и следующий элементы. Нет необходимости снова искать элемент в последовательности.

Краткое объяснение кода:

  • tee используется для эффективного создания 3 независимых итераторов над входной последовательностью
  • chain связывает две последовательности в одну; здесь он используется для добавления одноэлементной последовательности [None] к prevs
  • islice используется для создания последовательности всех элементов, кроме первого, затем chain используется для добавления None к его концу
  • Теперь есть 3 независимых последовательности на основе some_iterable, которые выглядят следующим образом:
    • prevs: None, A, B, C, D, E
    • items: A, B, C, D, E
    • nexts: B, C, D, E, None
  • наконец izip используется для изменения 3 последовательностей в одну последовательность триплетов.

Обратите внимание, что izip останавливается, когда исчерпывается любая входная последовательность, поэтому последний элемент prevs будет игнорироваться, что правильно - нет такого элемента, что последним элементом был бы его prev. Мы могли бы попытаться удалить последние элементы из prevs, но поведение izip делает это избыточным

Также обратите внимание, что tee, izip, islice и chain происходят из модуля itertools; они работают на своих входных последовательностях «на лету» (лениво), что делает их эффективными и не вызывает необходимости иметь всю последовательность в памяти сразу в любое время.

В python 3 при импорте izip будет отображаться ошибка, вместо izip можно использовать zip. Нет необходимости импортировать zip, это предопределено в python 3 - source

76 голосов
/ 18 июня 2009

Это должно сработать.

foo = somevalue
previous = next_ = None
l = len(objects)
for index, obj in enumerate(objects):
    if obj == foo:
        if index > 0:
            previous = objects[index - 1]
        if index < (l - 1):
            next_ = objects[index + 1]

Вот документы по функции enumerate.

6 голосов
/ 22 сентября 2015

Используя понимание списка, верните 3 кортежа с текущими, предыдущими и следующими элементами:

three_tuple = [(current, 
                my_list[idx - 1] if idx >= 1 else None, 
                my_list[idx + 1] if idx < len(my_list) - 1 else None) for idx, current in enumerate(my_list)]
2 голосов
/ 18 июня 2009

Вот версия, использующая генераторы без граничных ошибок:

def trios(input):
    input = iter(input) # make sure input is an iterator
    try:
        prev, current = input.next(), input.next()
    except StopIteration:
        return
    for next in input:
        yield prev, current, next
        prev, current = current, next

def find_prev_next(objects, foo):
    prev, next = 0, 0
    for temp_prev, current, temp_next in trios(objects):
        if current == foo:
            prev, next = temp_prev, temp_next
    return prev, next

print find_prev_next(range(10), 1)
print find_prev_next(range(10), 0)
print find_prev_next(range(10), 10)
print find_prev_next(range(0), 10)
print find_prev_next(range(1), 10)
print find_prev_next(range(2), 10)

Обратите внимание, что граничным поведением является то, что мы никогда не ищем "foo" в первом или последнем элементе, в отличие от вашего кода. Опять же, семантика границ странная ... и ее трудно понять из вашего кода:)

1 голос
/ 08 мая 2019

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

from collections import deque  

foo = ['A', 'B', 'C', 'D']

def prev_and_next(input_list):
    CURRENT = input_list
    PREV = deque(input_list)
    PREV.rotate(-1)
    PREV = list(PREV)
    NEXT = deque(input_list)
    NEXT.rotate(1)
    NEXT = list(NEXT)
    return zip(PREV, CURRENT, NEXT)

for previous_, current_, next_ in prev_and_next(foo):
    print(previous_, current_, next)
1 голос
/ 18 июня 2009

использование условных выражений для краткости для python> = 2.5

def prenext(l,v) : 
   i=l.index(v)
   return l[i-1] if i>0 else None,l[i+1] if i<len(l)-1 else None


# example
x=range(10)
prenext(x,3)
>>> (2,4)
prenext(x,0)
>>> (None,2)
prenext(x,9)
>>> (8,None)
0 голосов
/ 18 июня 2019

Я не знаю, как это еще не произошло, поскольку он использует только встроенные функции и легко расширяется на другие смещения:

values = [1, 2, 3, 4]
offsets = [None] + values[:-1], values, values[1:] + [None]
for value in list(zip(*offsets)):
    print(value) # (previous, current, next)

(None, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, None)
0 голосов
/ 05 марта 2019

Используя генераторы, это довольно просто:

signal = ['→Signal value←']
def pniter( iter, signal=signal ):
    iA = iB = signal
    for iC in iter:
        if iB is signal:
            iB = iC
            continue
        else:
            yield iA, iB, iC
        iA = iB
        iB = iC
    iC = signal
    yield iA, iB, iC

if __name__ == '__main__':
    print('test 1:')
    for a, b, c in pniter( range( 10 )):
        print( a, b, c )
    print('\ntest 2:')
    for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 3:')
    cam = { 1: 30, 2: 40, 10: 9, -5: 36 }
    for a, b, c in pniter( cam ):
        print( a, b, c )
    for a, b, c in pniter( cam ):
        print( a, a if a is signal else cam[ a ], b, b if b is signal else cam[ b ], c, c if c is signal else cam[ c ])
    print('\ntest 4:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 5:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']):
        print( a, b, c )
    print('\ntest 6:')
    for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ):
        print( a, b, c )

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

0 голосов
/ 09 октября 2015

Pythonic и элегантный способ:

objects = [1, 2, 3, 4, 5]
value = 3
if value in objects:
   index = objects.index(value)
   previous_value = objects[index-1]
   next_value = objects[index+1] if index + 1 < len(objects) else None
0 голосов
/ 24 марта 2014

AFAIK это должно быть довольно быстро, но я не проверял:

def iterate_prv_nxt(my_list):
    prv, cur, nxt = None, iter(my_list), iter(my_list)
    next(nxt, None)

    while True:
        try:
            if prv:
                yield next(prv), next(cur), next(nxt, None)
            else:
                yield None, next(cur), next(nxt, None)
                prv = iter(my_list)
        except StopIteration:
            break

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

>>> my_list = ['a', 'b', 'c']
>>> for prv, cur, nxt in iterate_prv_nxt(my_list):
...    print prv, cur, nxt
... 
None a b
a b c
b c None
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...