Ich überprüfte den Code des Datenwissenschaftlers meines Kollegen und sagte: "Es ist gefährlich, destruktive Methoden für Funktionsargumente anzuwenden", aber ich konnte ihn aufgrund fehlenden Wortschatzes nicht richtig erklären, also schrieb ich einen Artikel. Ich habe es gemacht.
Sicherlich gibt es keine destruktive Verarbeitung in der R-Sprache (nicht wahr?), Also habe ich das Gefühl, dass ich nicht einfach lernen kann, indem ich "numpy" oder "pandas" mit diesem Gefühl benutze. Ich suchte nach guten Dokumentationen und Artikeln, fand aber nur Rubys, die schwer zu erklären schienen, also schrieb ich sie.
Hier ist ein Beispiel für den destruktiven / zerstörungsfreien Prozess "Hinzufügen einer Liste am Ende einer Liste".
#Destruktives Beispiel
x = [1, 2, 3]
x.extend([4, 5, 6])
print(x)
# => [1, 2, 3, 4, 5, 6]
#Zerstörungsfreies Beispiel
x = [1, 2, 3]
y = x + [4, 5, 6]
print(y)
# => [1, 2, 3, 4, 5, 6]
print(x)
# => [1, 2, 3]
In Python werden "list.sort ()" und "list.reverse ()" als Listenmethoden bereitgestellt, und "sortiert" und "umgekehrt" werden als zerstörungsfreie Verarbeitung als "Listensortier" -Operationen bereitgestellt. Es ist gewesen. Weitere Informationen finden Sie in diesem offiziellen Dokument.
Wir verwenden der Einfachheit halber auch den Begriff "destruktive Methode", aber es kann sich um eine Funktion handeln, obwohl dies in der Standard-Python-Funktion selten vorkommt. Zum Beispiel random.shuffle
, das die Liste zufällig sortiert.
Der Nachteil ist, dass, wenn Sie eine destruktive Operation für das Argument einer Funktion ausführen, das Argument selbst geändert wird und der Code schwer zu befolgen ist.
Betrachten Sie die folgende "Funktion, die Daten verarbeitet, um json zusammenzusetzen".
import json
from typing import List
def convert_to_json(values: List[int]) -> str:
"""Funktion zum Speichern des Ergebnisses im JSON-Format
"""
#Bei der Verarbeitung der Daten`append`Führt eine destruktive Verarbeitung durch
new_value = 3
values.append(new_value)
result = {"values": values, "size": len(values)}
return json.dumps(result)
x = [1, 2, 3]
log = convert_to_json(x)
#Das Berechnungsergebnis wurde erhalten!
print(log)
# => {"values": [1, 2, 3, 3], "size": 4}
# convert_to_x selbst wird in json geändert!
print(x)
# => [1, 2, 3, 3]
Haben Sie gesehen, dass die Variable "x" aktualisiert wurde? Wenn Sie dies ungewollt tun, werden Sie am Ende weinen und das Debuggen endlos drucken, nachdem Sie bemerkt haben, dass "Was? Der Liste wurde ** irgendwo ** im Programm ein Wert hinzugefügt?"
Um dies nicht ungewollt zu tun, ist es beispielsweise in Ordnung, eine zerstörungsfreie Verarbeitung wie folgt durchzuführen.
def convert_to_json(values: List[int]) -> str:
# Note:Gegen die Liste`values += [3]`Wenn Sie dies tun, wird es eine zerstörerische Operation sein, seien Sie also vorsichtig!
# https://stackoverflow.com/questions/2347265/why-does-behave-unexpectedly-on-lists
#Es ist ein erstmaliges Töten, aber es scheint, dass es mit einem Schwerpunkt auf Ausführungseffizienz implementiert wird
values = values + [3]
result = {"values": values, "size": len(values)}
return json.dumps(result)
Alternativ können Sie es zu Beginn der Funktion wie unten gezeigt kopieren. Es gibt hier und da einige störende Kommentare, aber ich werde es Ihnen sagen, wenn ich nicht weiterkomme, weil es keine Schärfe gibt
def convert_to_json(values: List[int]) -> str:
# Note: `copy`Beachten Sie, dass die Methode nicht in die verschachtelte Hierarchie von Listen und Wörterbüchern kopiert wird!
# https://qiita.com/ninomiyt/items/2923aa3ac9bc06e6a9db
#Für Details, tiefe Kopie/Überprüfen Sie mit flacher Kopie
copied = valus.copy()
copied.append(3)
result = {"values": values, "size": len(values)}
return json.dumps(result)
Wenn Sie danach kein "x" verwenden, müssen Sie sich möglicherweise nicht zu viele Sorgen machen. Beachten Sie jedoch, dass dies das gleiche Problem verursacht, wenn Sie die Funktion an anderer Stelle wiederverwenden. Gleiches gilt für die destruktive Verarbeitung globaler Variablen (oder Variablen außerhalb der Funktion).
Sie fragen sich vielleicht: "Wenn ein solches Risiko besteht, ist es nicht nur eine zerstörungsfreie Verarbeitung?", Aber die destruktive Methode schafft keine neuen Werte, sodass die Effizienz des Programms gut ist. In Pythons Offizielle Dokumentation zum Vergleich von list.sort
und sorted
heißt es außerdem:
Etwas effizienter, wenn Sie die ursprüngliche Liste nicht benötigen.
Beispielsweise kann "das Bearbeiten einer Liste mit einer enormen Anzahl von Werten" bei jedem Kopieren Speicherplatz beanspruchen. Wenn Sie ein Datenwissenschaftler sind, werden Sie wahrscheinlich auf diese Probleme mit der Speichereffizienz stoßen, wenn Sie die Verarbeitung mit einer großen Anzahl von Zeilen mit "pandas.DataFrame" durchführen.
In diesem Fall verwenden wir die destruktive Methode ordnungsgemäß. Bei Pandas sind destruktive / zerstörungsfreie Operationen jedoch aus dem Methodennamen schwer zu erraten, und ich habe Fälle gesehen, in denen große Datenmengen innerhalb der Methode kopiert werden. / items / f8c562e0938271695576), sodass Sie sich möglicherweise daran gewöhnen müssen.
Abgesehen davon ist es nicht möglich, Maßnahmen zur Verbesserung der Effizienz solcher Programme zu ergreifen, da es in der R-Sprache keine destruktive Verarbeitung gibt (sie erstellt jedes Mal eine Kopie intern), und es handelt sich um eine universelle Programmiersprache, die je nach Fall ordnungsgemäß verwendet werden kann. Ich denke, es ist einer der Vorzüge der Verwendung eines bestimmten Python.
Um es ein wenig esoterisch auszudrücken, ist es notwendig, den Kompromiss mit der Effizienz des Programms zu berücksichtigen und dabei "** Lokalisierung der Nebenwirkung **" zu berücksichtigen. Ich habe gerade eine gute Zahl im Artikel "Unit Testing in R" zitiert.
Andere Effekte als die Eingabe (Argument) und Ausgabe (Rückgabewert) der Funktion werden als Nebenwirkungen bezeichnet. Andere Daten als Argumente (globale Variablen, externe Datenbanken) usw.
Die Verwendung einer Funktion ohne Nebenwirkungen (reine Funktion) bietet Vorteile wie "einfach zu testen" und "einfach wiederzuverwenden". Wenn Sie beispielsweise Code in pytest schreiben, wird der Test leicht beendet.
def test_f():
x = [1, 2, 3]
y = f(x)
assert y == [1, 2, 3, 4, 5, 6]
Wenn Sie SQL in der Funktion f
in die Datenbank werfen oder Eingaben vom Terminal akzeptieren, können Sie es nicht so testen, wie es ist (Sie müssen Code schreiben, um ein Scheinobjekt vorzubereiten), und das Argument x
ist beabsichtigt Wenn es ohne es zerstört wird, wird der Scheck oft übersehen.
Mit anderen Worten, wenn die Auswirkungen destruktiver Operationen nur in der Funktion enthalten sind, halte ich es nicht für erforderlich, den Code in zerstörungsfreien Code zu zwingen. Der folgende Code führt beispielsweise eine destruktive Operation mit dem Namen "Anhängen" aus. Da die Funktion "f" jedoch keine Auswirkung auf die Außenseite hat (keine Nebenwirkungen), gibt es kein Problem beim Testen und Wiederverwenden.
def f(x):
matrix = []
for i in range(x):
row = []
for j in range(x):
if i == j:
row.append(1)
else:
row.append(0)
matrix.append(row)
return matrix
Sie müssen sich auch anderer Nebenwirkungen als destruktiver Operationen bewusst sein. Ich werde es Ihnen jedes Mal sagen, weil es lang zu sein scheint, aber wenn Sie mir einen Rat geben
Ich denke, es ist gut, es in Form von zu implementieren, weil es für Datenwissenschaftler einfacher ist, den numerischen Berechnungsteil zu testen und wiederzuverwenden, der wirklich einen Mehrwert bieten sollte.
(Obwohl es eine völlige Seite ist, sind einige Sprachen gezwungen, reine Funktionen und Funktionen mit Nebenwirkungen und Land of Lisp's Cartoon der Nebenwirkungen streng zu trennen.) /archives/51854832.html) lacht so sehr, bitte lies es)
Ich habe es zusammengefasst. Bitte kommentieren Sie, wenn Sie einen Fehler machen.