2010-11-16 9 views
12

Ho un python per loop, nel quale ho bisogno di guardare avanti un elemento per vedere se è necessario eseguire un'azione prima dell'elaborazione.Python per-loop look-ahead

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

C'è un modo semplice per farlo in python? Il mio attuale approccio è quello di bufferizzare il file su un array, tuttavia questo non è l'ideale in quanto il file è piuttosto grande.

risposta

16

è possibile ottenere qualsiasi iterabile di prefetch voce successiva con questa ricetta: l'utilizzo

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) 

Esempio:

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

Il codice consente di passare il parametro window come un valore più grande, se si vuoi guardare 2 o più linee avanti.

+0

fa sì che legge due volte dal file, o bufferizza la linea in qualche modo? – Mike

+3

Si legge solo una volta. Vedi 'teedataobject_getitem' in [' itertoolsmodule.c'] (http://svn.python.org/projects/python/branches/release27-maint/Modules/itertoolsmodule.c) –

+4

Il tuo 'get_next' è nelle ricette itertools come ['pairwise'] (http://docs.python.org/library/itertools.html#recipes) –

9

È possibile avere un prev_line in cui si memorizza la riga precedente ed elaborarla ogni volta che si legge una riga solo in base alle proprie condizioni.

Qualcosa di simile:

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 

Potrebbe essere necessario fare ulteriori elaborazioni per l'ultima riga, se necessario, a seconda della logica.

3

È sufficiente memorizzare una riga nel buffer.

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

Non sono un esperto di Python ma immagino che sia necessario utilizzare 2 cicli per questo. La prima esecuzione del ciclo for dovrebbe creare un elenco di indici per i quali è necessario eseguire un'operazione speciale. Quindi, nel secondo passaggio, è possibile confrontare l'indice corrente con il proprio elenco per determinare se è necessario eseguire tale operazione speciale.

+4

Si dovrebbe pensare seriamente a trovare un approccio meno efficiente, anche se probabilmente esiste :) Cheers – Morlock

2

Anche questo dovrebbe funzionare. Preferisco sempre chiamare next per impostare il something = None per il primo round.

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

Lungo le linee di risposta di nosklo, tendo a usare il seguente schema:

La funzione pairwise dalla eccellente itertools recipes è ideale per questo:

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) 

Usandolo nel codice ci ottiene:

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

In genere, per questo tipo di p rocessing (lookahead nel iterable), tendo ad usare un window function. Pairwise è un caso speciale di una finestra di dimensioni 2.

+0

Cosa ti fa pensare che la tua soluzione sia migliore? Cosa c'è di sbagliato nel mio approccio (usando 'izip_longest' e' islice')? La mia soluzione consente una finestra più grande più facilmente. – nosklo

+0

@nosklo: Provalo. Sono sicuro che puoi fare meglio del mio collegamento perché stavo solo spiegando il concetto. Posso fare anche meglio. Nota che non è così banale come pensi. Non sono sicuro di quello che ho fatto per incolparli, ma speravo che potessimo ottenere la migliore risposta unica per SO. Se modifichi il tuo per arrivarci, sarò più che felice. –

+0

+1 per non tentare di discutere. – Danosaure

0

more_itertools ha diversi lookahead tools. Qui mostreremo alcuni strumenti e una funzione astratta per l'elaborazione di linee di un file.Dato:

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

Codice

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) 

uscita

A 
B 
0 

Qui ci sono altre opzioni che utilizzano la stessa libreria che includono pairwise e windowed strumenti menzionati da @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) 

Queste ultime opzioni possono essere eseguite indipendentemente o sostituire la logica nella funzione precedente.

Problemi correlati