2009-06-18 9 views
51

¿Cómo puedo hacer algo así en Python?Python - Valores anteriores y siguientes dentro de un ciclo

foo = somevalue 
previous = next = 0 

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

¿Qué debería pasar si foo está al principio o al final de la lista? Actualmente, esto saldrá de los límites de su matriz. – Brian

+0

si necesita la primera aparición de "foo", luego haga "break" desde el bloque "for" cuando coincida. – van

Respuesta

61

Esto debería hacer el truco.

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] 

Aquí está la documentación sobre la función enumerate.

+9

Pero probablemente sea una buena práctica no usar 'siguiente' como nombre de variable, ya que es una función incorporada. – mkosmala

+0

La versión editada de esto todavía no es lógica: al final del ciclo, 'obj' y' next_' serán el mismo objeto para la última iteración, lo que puede tener efectos secundarios no deseados. – TemporalWolf

0

Se podía utilizar index en la lista para encontrar dónde somevalue es y luego conseguir el anterior y el siguiente, según sea necesario:

 

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' 

 
1

utilizando expresiones condicionales para la concisión para 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) 
105

Las soluciones hasta ahora solo se ocupan de las listas, y la mayoría están copiando la lista. En mi experiencia, muchas veces eso no es posible.

Además, no tienen en cuenta el hecho de que puede tener elementos repetidos en la lista.

El título de su pregunta dice "valores anterior y siguiente dentro de un bucle", pero si se ejecuta la mayoría de respuestas aquí dentro de un bucle, que va a terminar la iteración en la lista entera de nuevo en cada elemento para encontrarlo .

Así que acabo de crear una función que. usando el módulo itertools, divide y corta el iterable, y genera tuplas con los elementos anterior y siguiente juntos. No es exactamente lo que hace su código, pero vale la pena echarle un vistazo, porque probablemente pueda resolver su problema.

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) 

A continuación, utilizarlo en un bucle, y tendrás artículos anteriores y siguientes en él:

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 

Los resultados:

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 

que va a trabajar con cualquier tamaño lista (porque no copia la lista), y con cualquier iterable (archivos, conjuntos, etc.). De esta forma, puede iterar sobre la secuencia y tener los elementos anteriores y siguientes disponibles dentro del ciclo. No es necesario volver a buscar el elemento en la secuencia.

Una breve explicación del código:

  • tee se utiliza para crear eficientemente 3 iteradores independientes más de la secuencia de entrada
  • chain enlaces dos secuencias en uno; se utiliza aquí para añadir una secuencia de un solo elemento [None] a prevs
  • islice se utiliza para hacer una secuencia de todos los elementos excepto el primero, entonces chain se utiliza para añadir una None a su extremo
  • Hay ahora 3 secuencias independientes basado en some_iterable que se parecen:
    • prevs: None, A, B, C, D, E
    • items: A, B, C, D, E
    • nexts: B, C, D, E, None
  • finalmente izip se utiliza para cambiar 3 secuencias en una secuencia de tripletes.

Tenga en cuenta que izip se detiene cuando cualquier secuencia de entrada se agota, por lo que se tendrá en cuenta el último elemento de prevs, lo que es correcto - no hay tal elemento que el último elemento sería su prev. Podríamos tratar de quitarse los últimos elementos de prevs pero el comportamiento izip 's que hace redundante

También tenga en cuenta que tee, izip, islice y chain provienen del módulo de itertools; operan en sus secuencias de entrada sobre la marcha (perezosamente), lo que los hace eficientes y no introduce la necesidad de tener toda la secuencia en la memoria a la vez y en cualquier momento.

En python 3, se mostrará un error al importar izip, puede usar zip en lugar de izip. No hay necesidad de importar zip, que está predefinido en python 3-source

+3

Buena solución. Como desempacar – Skurmedel

+2

La solución requiere una explicación de tee, islice, chain e izip. –

+3

@becomingGuru: no es necesario convertir SO en un espejo de los documentos de referencia de Python. Todas estas funciones están muy bien explicadas (con ejemplos) en la documentación oficial –

2

Aquí hay una versión utilizando generadores sin errores de contorno:

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) 

Tenga en cuenta que el comportamiento límite es que nunca buscamos "foo" en el primer o el último elemento, a diferencia de tu código. Nuevamente, la semántica del límite es extraña ...y son difíciles de entender desde el código :)

0

Aparentemente, este debe ser bastante rápido, pero no he probado que: el uso

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 

Ejemplo:

>>> 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 
3

Usando una lista por comprensión , devolver un 3-tupla con elementos actuales, anterior y siguiente:

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)] 
+1

¡Increíble! No entiendo por qué esto no fue votado. :) – buhtz

-1

Pythonic y manera elegante:

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 
+4

'next' es una [función incorporada] (https://docs.python.org/2/library/functions.html?highlight=next#next) en Python y usted lo reemplaza. – Psytho

+0

Buen punto. Editado ¡Gracias! – ImportError

+1

Fallará si 'value' está al final. Además, devuelve el último elemento como 'valor_previo' si' valor' es el primero. –

Cuestiones relacionadas