2012-12-03 19 views
6

Mam następujący fragment kodutworzenie nie tylko puste linie

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

Co oczywiście działa dobrze, ale zastanawiam się czasem, czy jest „lepszy” sposób napisać, że konstrukt, gdzie działają tylko na niepustych liniach.

Wyzwanie polega na tym, że należy używać iteracyjnego charakteru pliku "fd" do odczytu i obsługiwać pliki z zakresu ponad 100 MB.

AKTUALIZACJA - W swoim pośpiechu, aby zdobyć punkty za to pytanie, ignorujesz część importującą, czyli użycie pamięci. Na przykład wyrażenie:

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

będzie bufor cały plik do pamięci, nie wspominając wykonywania pasek() działania dwukrotnie. Który będzie działać dla małych plików, ale nie powiedzie się, gdy masz 100 + MB danych (lub raz na jakiś czas 100 GB).

Część wyzwaniem jest następujące prace, ale jest zupa czytać:

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

Look dla magicznych ludzi!

AKTUALNA AKTUALIZACJA: PEP-289 jest bardzo przydatna dla mojego lepszego zrozumienia różnicy między [] i() z włączonymi iteratorami.

+0

To nie jest odpowiedź na twoje pytanie, ale w przypadku dużych plików możesz chcieć spojrzeć na buforowaną IO (http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit -integers-in-2mb.html) – BorrajaX

+1

Nie mogę wymyślić lepszego sposobu. Inną rzeczą, którą możesz zrobić, jest napisanie swojej własnej funkcji '__iter__' dla twojego obiektu' fd' (o którym nie dałeś nam zbyt wielu informacji), aby tylko "dostarczyło ci linii, które nie są puste. – jdotjdot

+0

fd jest bardzo proste: '' with open (FILENAME) as fd: '' – koblas

Odpowiedz

4

Nie ma nic złego w tym kodzie, co napisano, jest czytelny i wydajny.

Alternatywnym rozwiązaniem byłoby napisać go jako pojmowania generatora:

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

Takie podejście może być korzystne (terser) jeśli ubiegasz funkcję, która może potrwać iterator: np python3 wydrukować

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

do rezygnacji z wielu połączeń do paska(), łańcuch razem generator listowe

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

pamiętać, że wyrażenia generatorów nie wpłynie negatywnie na pamięć jak wyszczególniono w niniejszym pep.

Aby uzyskać bardziej szczegółowe spojrzenie na to podejście i niektóre wskaźniki wydajności, spójrz na ten zestaw notes.

Na koniec należy zauważyć, że rstrip() przewyższy pasek(), jeśli nie jest wymagane zachowanie paska().

+0

Cytowany przez ciebie PEP jest bardzo cenny. – koblas

1

Po prostu nie ma "lepszej" drogi od twojej, działa tak, jak powinna, jest łatwa w czytaniu itd. Jednakże, jeśli klasyfikujesz prędkość jako "lepszą", na pewno można wprowadzić małe poprawki.

Nie zapoznałem się zbyt wiele z tym speed stuff na Pythonie, ale tutaj jest kilka sugestii, które działają tylko pod pewnymi warunkami. Mam nadzieję, że ktoś inny wymyśli coś lepszego, może ta odpowiedź pomoże im.

Jeśli plik nie będzie zawierać linie takie jak

 \n 

ale zamiast tylko \n, to ta droga będzie zauważalnie szybciej:

wartości
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 

Należy jednak zauważyć, że może to być nawet wolniejsze, jeśli plik nie ma pojedynczego l ine z \n, I timed it z fd jest ["string", "\n", "test string", "\n", "moreeee", "\n", "An other element"]

Prawdopodobnie nie wiem, czy linie są \n tylko jednak .strip() jest dość powolny, więc nie może być bardziej lepsze sposoby.

Powiązane problemy