2010-11-16 13 views
12

Tengo un ciclo python for loop, en el cual necesito mirar hacia adelante un elemento para ver si es necesario realizar una acción antes del procesamiento.Python for-loop look-ahead

for line in file: 
    if the start of the next line == "0": 
     perform pre-processing 
     ... 
    continue with normal processing 
    ... 

¿Hay alguna manera fácil de hacerlo en python? Mi enfoque actual es almacenar el archivo en una matriz, sin embargo, esto no es ideal ya que el archivo es bastante grande.

Respuesta

16

usted puede conseguir cualquier iterable de captación previa orden del día, con esta receta: el uso

from itertools import tee, islice, izip_longest 
def get_next(some_iterable, window=1): 
    items, nexts = tee(some_iterable, 2) 
    nexts = islice(nexts, window, None) 
    return izip_longest(items, nexts) 

Ejemplo:

for line, next_line in get_next(myfile): 
    if next_line and next_line.startswith("0"): 
     ... do stuff 

El código le permite pasar el parámetro window como un valor mayor, si quiero ver 2 o más líneas adelante.

+0

¿Esto hace que se lea dos veces del archivo, o lo hace de alguna manera? – Mike

+3

Se lee solo una vez. Ver 'teedataobject_getitem' en [' itertoolsmodule.c'] (http://svn.python.org/projects/python/branches/release27-maint/Modules/itertoolsmodule.c) –

+4

Su 'get_next' está en las recetas itertools como ['pairwise'] (http://docs.python.org/library/itertools.html#recipes) –

9

Puede tener un prev_line donde almacene la línea anterior y procesarla siempre que lea una línea solo dada su condición.

Algo así como:

prev_line = None 
for line in file: 
    if prev_line is not None and the start of the next line == "0": 
     perform pre-processing on prev_line 
     ... 
    continue with normal processing 
    ... 
    prev_line = line 

Es posible que tenga que hacer un procesamiento adicional para la última línea si fuera necesario, en función de su lógica.

3

Simplemente necesita almacenar una línea.

for line in file: 
    if (prevLine is not None): 
    //test line as look ahead and then act on prevLine 
    prevLine = line 
-2

No soy un experto en Python, pero me imagino que necesitaría usar 2 bucles para esto. La primera ejecución del bucle for debe crear una lista de índices para los que deberá realizar una operación especial. Luego, en la segunda ejecución puede comparar el índice actual con su lista para determinar si necesita realizar esa operación especial.

+4

Uno tendría que pensar mucho para encontrar un enfoque menos eficiente, aunque probablemente exista :) Cheers – Morlock

2

Esto debería funcionar también. Siempre prefiero llamar al next sobre la configuración something = None para la primera ronda.

prev_line = next(the_file) 
for current_line in the_file: 
    if current_line.startswith('0'): 
     do_stuff(prev_line) 
    # continue with normal processing 
    # ... 
    prev_line = current_line 
8

A lo largo de las líneas de la respuesta de nosklo, que tienden a utilizar el siguiente patrón:

La función pairwise de la excelente itertools recipes es ideal para esto:

from itertools import tee 

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

Su uso en su código nos consigue:

for line, next_line in pairwise(file): 
    if next_line.startswith("0"): 
     pass #perform pre-processing 
     #... 
    pass #continue with normal processing 

Generalmente, para este tipo de p rocessing (lookahead en el iterable), tiendo a usar un window function. Pairwise es un caso especial de una ventana de tamaño 2.

+0

¿Qué te hace pensar que tu solución es mejor? ¿Qué pasa con mi enfoque (usando 'izip_longest' y' islice')? Mi solución permite una ventana más grande más fácilmente. – nosklo

+0

@nosklo: Pruébalo. Estoy seguro de que puedes hacerlo mejor que mi enlace porque solo estaba explicando el concepto. Yo puedo hacerlo mejor también. Tenga en cuenta que no es tan trivial como cree. No estoy seguro de qué hice para marcarlo, pero esperaba poder obtener la mejor respuesta para SO. Si editas la tuya para llegar allí, estaré más que feliz. –

+0

+1 por no intentar discutir. – Danosaure

0

more_itertools tiene varios lookahead tools. Aquí demostraremos algunas herramientas y una función abstracta para procesar líneas de un archivo.Teniendo en cuenta:

f = """\ 
A 
B 
C 
0 
D\ 
""" 
lines = f.splitlines() 

Código

import more_itertools as mit 


def iter_lookahead(iterable, pred): 
    # Option 1 
    p = mit.peekable(iterable) 
    try: 
     while True: 
      line = next(p) 
      next_line = p.peek() 
      if pred(next_line): 
       # Do something 
       pass 
      else: 
       print(line) 
    except StopIteration: 
     return 


pred = lambda x: x.startswith("0") 
iter_lookahead(lines, pred) 

salida

A 
B 
0 

Estas son otras opciones utilizando la misma biblioteca que incluyen pairwise y windowed herramientas mencionadas por @Muhammad Alkarouri.

# Option 2 
for line, next_line in mit.pairwise(lines): 
    if pred(next_line):    
     # Do something 
     pass 
    else: 
     print(line) 

# Option 3 
for line, next_line in mit.windowed(lines, 2): 
    if pred(next_line):    
     # Do something 
     pass 
    else: 
     print(line) 

Las últimas opciones se pueden ejecutar de forma independiente o sustituir la lógica en la función anterior.