Python-предыдущие и следующие значения внутри цикла


Как я могу сделать что-то подобное в 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]
    }
}
8 60

8 ответов:

Это должно сделать трюк.

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

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

кроме того, они не имеют дело с тем, что вы можете иметь повторяющихся элементов в списке.

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

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

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 используется для добавления a 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,вы можете использовать zip вместо izip. Нет необходимости импортировать zip, это предопределено в python 3 -источник

используя понимание списка, верните 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)]

вот версия с использованием генераторов без границ ошибки:

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" в первом или последнем элементе, в отличие от вашего кода. Опять же, граничная семантика странна...и трудно понять из вашего кода :)

использование условных выражений для краткости для 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)

вы могли бы просто использовать index в списке найти, где somevalue и затем получить предыдущий и следующий по мере необходимости:


def find_prev_next(elem, elements):
    previous, next = None, None
    index = elements.index(elem)
    if index > 0:
        previous = elements[index -1]
    if index < (len(elements)-1):
        next = elements[index +1]
    return previous, next


foo = 'three'
list = ['one','two','three', 'four', 'five']

previous, next = find_prev_next(foo, list)

print previous # should print 'two'
print next # should print 'four'


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

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

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