Sie liest Mary Rose Cooks praktische Einführung in die funktionale Programmierung und gibt ein Beispiel für ein Anti-Pattern
def format_bands(bands):
for band in bands:
band['country'] = 'Canada'
band['name'] = band['name'].replace('.', '')
band['name'] = band['name'].title()
schon seit
- Die Funktion macht mehr als eine Sache
- Der Name ist nicht beschreibend
- es hat Nebenwirkungen
Als Lösungsvorschlag schlägt sie das Pipelining anonymer Funktionen vor
pipeline_each(bands, [call(lambda x: 'Canada', 'country'),
call(lambda x: x.replace('.', ''), 'name'),
call(str.title, 'name')])
Dies scheint mir jedoch den Nachteil zu haben, noch weniger überprüfbar zu sein; Zumindest format_bands könnte einen Komponententest durchführen, um zu überprüfen, ob es das tut, was es soll, aber wie wird die Pipeline getestet? Oder ist die Idee, dass die anonymen Funktionen so selbsterklärend sind, dass sie nicht getestet werden müssen?
Meine reale Anwendung hierfür besteht darin, meinen pandas
Code funktionsfähiger zu machen . Ich habe oft eine Art Pipeline in einer "Munging" -Funktion "
def munge_data(df)
df['name'] = df['name'].str.lower()
df = df.drop_duplicates()
return df
Oder im Pipeline-Stil umschreiben:
def munge_data(df)
munged = (df.assign(lambda x: x['name'].str.lower()
.drop_duplicates())
return munged
Irgendwelche Vorschläge für Best Practices in solchen Situationen?
quelle
Antworten:
Ich denke, Sie haben wahrscheinlich den wichtigeren Teil des korrigierten Beispiels des Buches verpasst. Die grundlegendere Änderung des Codes erfolgt von der Methode, die mit allen Werten in einer Liste ausgeführt wird, zur Methode, die mit einem Element ausgeführt wird.
Es gibt bereits Funktionen wie
iter
(in diesem Fall benanntpipeline_foreach
), die eine bestimmte Operation für alle Elemente in einer Liste ausführen. Es war nicht nötig, dies mit einerfor
Schleife zu duplizieren . Auch die Verwendung einer bekannten Listenoperation macht Ihre Absicht klar. Mitmap
transformieren Sie die Werte. Mit führeniter
Sie mit jedem Element einen Nebeneffekt durch. Mitfor
Schleife bist du ... nun, du weißt es nicht wirklich, bis du es durchschaust.Der beispielkorrigierte Code ist immer noch nicht sehr funktional, da er (soweit ich das beurteilen kann) die Werte in der Liste mutiert, ohne sie zurückzugeben, wodurch weitere Rohrleitungen oder Funktionszusammensetzungen verhindert werden. Die funktional bevorzugte Methode
map
würde eine neue Liste von Bändern mit dem aktualisiertencountry
und erstellenname
. Dann können Sie diese Ausgabe an die nächste Funktion weiterleiten odermap
mit einer anderen Funktion komponieren , die eine Bandliste erstellt hat. Mititer
, es ist wie eine Sackgasse.Ich denke, der Endergebniscode hat kleine Funktionen, die zu trivial sind, um sie hier zu testen. Schließlich sollten Sie keine Unit-Tests gegen
replace
oder schreiben müssentitle
. Vielleicht möchten Sie diese jetzt zu Ihrem eigenen Funktions- und Komponententest zusammensetzen, damit die gewünschte Kombination für einen einzelnen Artikel erreicht wird. Ich selbst hätte wahrscheinlich nurformat_bands
zuformat_band
Singular gewechselt , die for-Schleife gelöscht und angerufenpipeline_each(bands, format_band)
. Dann können Sie format_band testen, um sicherzustellen, dass Sie nichts vergessen haben.Wie auch immer, weiter zu deinem Code. Ihr zweites Beispiel für Code scheint eher Pipeline-y zu sein. Dies allein bietet jedoch nicht die Vorteile einer funktionalen Programmierung. In der Praxis bedeutet funktionale Programmierung, die Kompatibilität von Funktionen mit anderen Funktionen sicherzustellen, indem ihre Kompatibilität nur anhand ihrer Ein- und Ausgänge definiert wird. Wenn die Funktion versteckte Nebenwirkungen aufweist, können Sie trotz der Übereinstimmung der Eingabe / Ausgabe mit anderen Funktionen erst zur Laufzeit feststellen, ob sie kompatibel sind. Wenn jedoch zwei Funktionen nebenwirkungsfrei sind und Ausgabe zu Eingabe übereinstimmen, können Sie sie mit wenig Sorge um unerwartete Ergebnisse weiterleiten oder zusammenstellen .
quelle