2017-11-20 1 views
1

Ich habe folgende Funktionen schreiben: zu tunWie man eine guten Generator Verkettungs Funktion

def read_data(file_location): 
    for line in open(file_location): 
     # pre-process the line 
     yield line 

def transform_1(data): 
    for line in data: 
     # change line in some way 
     yield line 

def transform_2(data): 
    for line in data: 
     # change line in some other way 
     yield line 

def process_file(file_location): 
    # Some description 
    # 
    # returns: 
    #  generator 
    data = read_data(file_location) 
    data = transform_1(data) 
    data = transform_2(data) 
    return data 

Was ich versuche, Zeilen aus einer Datei zu lesen, jede Zeile mit einer Reihe von Funktionen umsetzen, und dann tun etwas mit den resultierenden Linien. Ich möchte nicht alle Zeilen auf einmal lesen, da die Datei ziemlich groß ist.

Meine Frage ist, ob ich das richtig mache. Der Code wird korrekt ausgeführt, aber die Programmausführung in meinem Kopf fühlt sich so kompliziert an, dass ich keine Ahnung habe, ob ich diesen Code in etwa einem Monat verarbeiten kann.

Also was ich wissen will ist: Gibt es eine Art Programmierungsmuster, das zeigt, wie Generatoren richtig miteinander verkettet werden?

+0

zuerst: vereinfachen dies: 'read_data (file_location)' => 'open (file_location)' wird genau das gleiche tun. –

+0

Dies ist eine vereinfachte Version meines tatsächlichen Codes. Es macht viel mehr im wirklichen Leben. – Arne

+0

@ArneRecknagel Das ist im Grunde die Python-Map (das gilt für Python 3, wo 'map' faul ist). Transformationsfunktionen würden dann für _elements_ des Datensatzes gelten, nicht für den vollständigen Satz, da dies der Generator ist. Für ein nicht eingebautes Tool können Sie sich ['RxPY'] (https://github.com/ReactiveX/RxPY) ansehen. –

Antwort

2

Angenommen, jede Zeile wird auf die gleiche Weise transformiert, können Sie Ihre Transformationsfunktionen auf jede Zeile anwenden und einen Generator verwenden, um über alle Zeilen zu iterieren, persönlich finde ich das besser lesbar.

def transform_1(line): 
    return line.replace(' ','') # example of transformation 

def transform_2(line): 
    return line.strip('#') 

def process_file(file_location): 
    with open(file_location) as in_f: 
     for line in in_f: 
      yield transform_2(transform_1(line)) 

Je nachdem, was die Transformationen zu tun, könnten sie möglicherweise in eine einzige Funktion kombiniert werden, aber es ist schwer, ohne mehr Kontext zu kennen.

+0

Die Transformatoren sind zur besseren Lesbarkeit getrennt. Sie würden also empfehlen, die äußerste Funktion "yield" zu haben, anstatt einen Generator zurückzugeben? – Arne

+0

Gut 'Ertrag' erzeugt einen Generator, also bin ich mir nicht sicher, ob ich den Unterschied verstehe, den Sie machen. Natürlich könnte man einen Generator-Ausdruck auch trivial verwenden, wie 'return (transform_2 (transform_1 (line)) für line in_f)' –

+0

Das Festcodieren der Transformationen innerhalb des Datengenerators ist keine gute Idee. Dies verhindert Flexibilität bei den Transformationen, die Sie anwenden möchten. Es ist jedoch eine gute Idee, Transformationen auf _items_ (nicht auf einen kompletten Satz) anzuwenden, und dies kann mit 'map' sauber realisiert werden:' map (transform_2, map (transform_1, process_file (...))) '. –

1

Eigentlich ist das gut gemacht. Ich bin mir nicht sicher, warum sich der Code für dich verdreht anfühlt. Der Schlüssel ist, dass jede Funktion nur eine Sache macht, sondern ein Plus. Offensichtlich sollten die Funktionsnamen die Art der durchgeführten Transformationen widerspiegeln. Code wie dieser ist sehr überprüfbar und wartbar. Wenn Sie in sechs Monaten eine Änderung an der Pipeline vornehmen müssen, werden Sie vielleicht überrascht sein, wie einfach es ist, das Teil zu finden, das angepasst werden muss und die Änderung vornehmen muss.

Ich würde vorschlagen, Ihr read_data Generator wie folgt zu ändern:

def read_data(file_location): 
    with open(file_location) as f: 
     for line in f: 
      yield line 
+0

Ich denke, weil das Objekt mehrmals neu zugewiesen wird, aber die Zuordnung an der Spitze beeinflusst noch sein Verhalten. Ich denke, ich fühle, dass nachdem ich 'data = x (data) gemacht habe 'ich nicht wissen muss, was' data' vor diesem Aufruf war, nur was 'x()' tut. – Arne

+0

Normalerweise ändere ich den Namen der Daten jedes Mal, um widerzuspiegeln, wie sie sich geändert hat, besonders da sie möglicherweise Änderungen am Typ durchlaufen, so dass es sich um eine Zeichenfolge handeln könnte. Der nächste Generator in der Pipeline könnte jedoch Tupel erzeugen. –