2013-04-19 3 views
12

Ich möchte viele Dateien behandeln, als ob sie alle eine Datei wären. Was ist der richtige phytonische Weg [Dateinamen] => [Dateiobjekte] => [Zeilen] mit Generatoren/nicht eine ganze Datei in den Speicher zu lesen?Was ist der pythischste Weg, um alle Zeilen mehrerer Dateien zu durchlaufen?

Wir alle wissen, der richtige Weg, um eine Datei zu öffnen:

with open("auth.log", "rb") as f: 
    print sum(f.readlines()) 

Und wir wissen, die richtige Art und Weise mehrere Iteratoren/Generatoren in einer lang zu verknüpfen:

>>> list(itertools.chain(range(3), range(3))) 
[0, 1, 2, 0, 1, 2] 

aber wie kann ich mehrere Dateien miteinander verknüpfen und die Kontextmanager beibehalten?

with open("auth.log", "rb") as f0: 
    with open("auth.log.1", "rb") as f1: 
     for line in itertools.chain(f0, f1): 
      do_stuff_with(line) 

    # f1 is now closed 
# f0 is now closed 
# gross 

Ich konnte die Kontext-Manager ignorieren und so etwas tun, aber es fühlt sich nicht richtig:

files = itertools.chain(*(open(f, "rb") for f in file_names)) 
for line in files: 
    do_stuff_with(line) 

Oder ist diese Art von dem, was für Async IO - PEP 3156 ist und ich muss nur an warte später auf die elegante Syntax?

+3

Beachten Sie auch, dass 'files = itertools.chain (* (open (f," rb ") für f in file_names)) in diesem Zusammenhang definitiv nicht gut ist. Wenn Sie das Tupel entpacken, werden alle Ihre Dateien geöffnet, bevor Sie tatsächlich den Konstruktor "chain" eingeben. Sie sind besser dran mit 'itertools.chain.from_iterable (offen (fname, 'r') für fname in Dateinamen))' - In der Tat ist dies ein klassischer Grund, warum die 'from_iterable' Klassenmethode in der ersten existieren muss Ort :). – mgilson

+0

@mgilson hatte keine Ahnung 'from_iterable' war eine Sache! Ich bin froh, dass mein Anwendungsfall ein Beispiel dafür ist, warum es nützlich ist. Ich versuchte herauszufinden, wie man die Lazy-Evaluierung ohne geschachtelte For-Schleifen richtig durchführen kann. Vielen Dank! –

+0

Beachten Sie, dass selbst das 'from_iterable' nicht garantiert, dass alle Ihre Dateien geschlossen sind, wenn Sie mit der Iteration fertig sind, weil Sie nie wissen, wann' __del__' tatsächlich ausgeführt wird (obwohl ich ziemlich sicher bin, dass sie da sind) Cpython) ... – mgilson

Antwort

20

Es gibt immer fileinput.

for line in fileinput.input(filenames): 
    ... 

die source jedoch Lesen, scheint es, dass fileinput.FileInput nicht als Kontext-Manager verwendet werden. Um dies zu beheben, könnte man contextlib.closing seit FileInput Instanzen haben verwenden, um eine sanely implementiert close Methode:

from contextlib import closing 
with closing(fileinput.input(filenames)) as line_iter: 
    for line in line_iter: 
     ... 

Eine Alternative mit dem Kontext-Manager ist eine einfache Funktion Looping über die Dateien zu schreiben und wodurch man Linien, wie Sie gehen:

def fileinput(files): 
    for f in files: 
     with open(f,'r') as fin: 
      for line in fin: 
       yield line 

keine wirkliche Notwendigkeit für itertools.chain hier IMHO ... die Magie hier ist in der yield Anweisung, die verwendet wird, um eine normale Funktion int umwandeln o ein fantastisch fauler Generator.


Als Nebenwirkung, beginnend mit python3.2, fileinput.FileInput als Kontext-Manager implementiert ist, die genau das tut, was wir mit contextlib zuvor. Jetzt wird unser Beispiel:

# Python 3.2+ version 
with fileinput.input(filenames) as line_iter: 
    for line in line_iter: 
     ... 

obwohl das andere Beispiel auch auf python3.2 + funktioniert.

+2

+1, ich wusste nie über 'fileinput'. – Blender

+0

@Blender - Es ist ein anständiges Modul, das sich nicht zu sehr gewöhnt, da seine Funktionalität durch 'chain.from_iterable' ersetzt werden kann. 'itertools' und' collections' sind die bekannteren Werkzeuge, die Menschen in 90% der Fälle erreichen. Ich bin ein wenig enttäuscht, dass es nicht als ein Kontextmanager implementiert ist (es ist nicht einmal eine neue Stilklasse). Es scheint, als wäre es eine ziemlich einfache Ergänzung, aber glücklicherweise ist es einfach genug, mit ContextLib zu arbeiten. – mgilson

+3

Seit Python 3.2 kann 'fileinput' als ein Kontextmanager verwendet werden (http://docs.python.org/3/library/fileinput.html. –

Verwandte Themen