2012-12-03 20 views
6

ho ottenuto il seguente frammento di codiceProcessing solo linee non vuote

def send(self, queue, fd): 
    for line in fd: 
     data = line.strip() 
     if data: 
      queue.write(json.loads(data)) 

Che naturalmente funziona bene, ma a volte mi chiedo se c'è un modo "migliore" per scrivere che costrutto in cui si agisci solo su righe non vuote.

La sfida è questa: si dovrebbe usare la natura iterativa di per la lettura di "fd" ed essere in grado di gestire i file nell'intervallo di oltre 100 MB.

UPDATE - Nella tua fretta di ottenere punti per questa domanda, stai ignorando una parte di importazione, che è l'utilizzo della memoria. Per esempio l'espressione:

non_blank_lines = (line.strip() for line in fd if line.strip()) 

sta per tamponare l'intero file nella memoria, per non parlare svolgere un'azione striscia() due volte. Che funzionerà con file di piccole dimensioni, ma fallisce quando hai 100 + MB di dati (o una volta ogni 100 GB).

Parte della sfida è le seguenti opere, ma è la zuppa di leggere:

for line in ifilter(lambda l: l, imap(lambda l: l.strip(), fd)): 
    queue.write(json.loads(line)) 

sguardo per la gente di magia!

AGGIORNAMENTO FINALE: PEP-289 è molto utile per la mia migliore comprensione della differenza tra [] e() con gli iteratori coinvolti.

+0

Questa non è esattamente una risposta alla tua domanda, ma per i file di grandi dimensioni, potresti voler dare un'occhiata a IO bufferizzato (http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit -integers-in-2mb.html) – BorrajaX

+1

Non riesco davvero a pensare a un modo migliore. Solo l'altra cosa che potresti fare è scrivere la tua funzione '__iter__' per il tuo oggetto' fd' (di cui non ci hai dato molte informazioni) in modo che esso restituisca solo linee che non sono vuote. – jdotjdot

+0

fd è molto semplice: '' con open (FILENAME) come fd: '' – koblas

risposta

4

Non c'è niente di sbagliato nel codice come scritto, leggibile ed efficiente.

Un approccio alternativo potrebbe essere quello di scrivere come un generatore di comprensione:

def send(self, queue, fd): 
    non_blank_lines = (line.strip() for line in fd if line.strip()) 
    for line in non_blank_lines: 
     queue.write(json.loads(data)) 

Questo approccio può essere utile (terser) se si sta applicando una funzione che può assumere un iteratore: per esempio python3 stampare

non_blank_lines = (line.strip() for line in fd if line.strip()) 
print(*non_blank_lines, file='foo') 

di farla finita con le chiamate multiple a nudo(), la catena di insieme generatore comprensioni

stripped_lines = (line.strip() for line in fd) 
non_blank_lines = (line for line in stripped_lines if line) 

notare che le espressioni del generatore non avranno ripercussioni negative di memoria come descritto in questo pep.

Per un approccio più approfondito a questo approccio e alcuni benchmark delle prestazioni, dare un'occhiata a questo set di notes.

Infine notare che rstrip() supererà strip() se non è necessario il completo comportamento di strip().

+0

Il PEP che citi è molto prezioso. – koblas

1

Semplicemente non esiste un modo "migliore" del tuo, funziona come dovrebbe, facile da leggere, ecc. Tuttavia, se si classifica la velocità come "migliore", è possibile effettuare piccole regolazioni.

Non ho familiarizzato troppo con questa roba di velocità su Python, ma qui ci sono alcuni suggerimenti, che funzionano solo in determinate condizioni. Spero che qualcun altro possa inventarsi qualcosa di meglio, forse questa risposta li aiuterà.

Se il file non contiene linee come

 \n 

ma invece solo \n, allora in questo modo sarà notevolmente più veloce:

valori
def send(self, queue, fd): 
    for line in fd: 
     if line != '\n': 
      queue.write(json.loads(line.strip())) 

timeit:

using: strip() :: 1.8722578811916337 
using: line != '\n' :: 1.0126976271093881 
using: line != '\n' and line != ' \n' :: 1.2862439244170275 

Si noti tuttavia che questo potrebbe effettivamente diventare ancora più lento, se il file non ha una sola l ine di \n, ho cronometrato con fd essendo ["string", "\n", "test string", "\n", "moreeee", "\n", "An other element"]

Probabilmente non so se le linee sono \n solo, però, .strip() è abbastanza lento, quindi ci potrebbe essere modi più migliori.

Problemi correlati