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 ответов:
Это должно сделать трюк.
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
используется для добавления aNone
до конца- теперь есть 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