Python Dict gegen Asdict
Die dataclasses
-Bibliothek wurde in Python 3.7 eingeführt, was es uns ermöglichte, strukturierte Klassen speziell für die Datenspeicherung zu erstellen. Diese Klassen haben spezifische Eigenschaften und Methoden, um mit Daten und ihrer Darstellung umzugehen.
die Bibliothek dataclasses
in Python
Verwenden Sie den folgenden Befehl, um die Bibliothek dataclasses
zu installieren.
pip install dataclasses
Im Gegensatz zu einer normalen Klasse in Python werden die dataclasses
mithilfe der @dataclass
-Decorators mit Klassen implementiert. Außerdem erfolgt die Attributdeklaration mithilfe von Typhinweisen, die Datentypen für die Attribute in der Datenklasse
angeben.
Nachfolgend finden Sie ein Code-Snippet, das das Konzept in die Praxis umsetzt.
# A bare-bones Data Class
# Don't forget to import the dataclass module
from dataclasses import dataclass
@dataclass
class Student:
"""A class which holds a students data"""
# Declaring attributes
# Making use of type hints
name: str
id: int
section: str
classname: str
fatherName: str
motherName: str
# Below is a dataclass instance
student = Student("Muhammad", 1432, "Red", "0-1", "Ali", "Marie")
print(student)
Ausgang:
Student(name='Muhammad', id=1432, section='Red', classname='0-1', fatherName='Ali', motherName='Marie')
Im obigen Code sind zwei Punkte zu beachten. Erstens akzeptiert ein dataclass
-Objekt Argumente und weist sie relevanten Datenelementen ohne einen _init_()
-Konstruktor zu.
Das liegt daran, dass die dataclass
einen eingebauten _init_()
-Konstruktor bereitstellt.
Der zweite zu beachtende Punkt ist, dass die Anweisung print
die im Objekt vorhandenen Daten sauber druckt, ohne dass eine Funktion speziell dafür programmiert wurde. Das bedeutet, dass es eine geänderte _repr_()
-Funktion haben muss.
Warum dict
schneller ist als asdict
In den meisten Fällen, in denen Sie dict
ohne Datenklassen verwendet hätten, sollten Sie auf jeden Fall weiterhin dict
verwenden.
Der asdict
führt jedoch während eines Kopieranrufs zusätzliche Aufgaben aus, die für Ihren Fall möglicherweise nicht nützlich sind. Diese zusätzlichen Aufgaben haben einen Overhead, den Sie vermeiden möchten.
Hier ist, was es gemäß der offiziellen Dokumentation tut. Jedes dataclass
-Objekt wird zuerst in ein dict
seiner Felder als name: value
-Paare umgewandelt.
Dann werden die dataclasses
, dicts
, Listen und Tupel rekursiv ausgeführt.
Wenn Sie beispielsweise eine rekursive dataclass
-Diktifizierung benötigen, wählen Sie asdict
. Andernfalls ist all die zusätzliche Arbeit, die für die Bereitstellung erforderlich ist, verschwendet.
Wenn Sie insbesondere asdict
verwenden, ändert die Änderung der Implementierung enthaltener Objekte zur Verwendung von dataclass
das Ergebnis von asdict
für die äußeren Objekte.
from dataclasses import dataclass, asdict
from typing import List
@dataclass
class APoint:
x1: int
y1: int
@dataclass
class C:
aList: List[APoint]
point_instance = APoint(10, 20)
assert asdict(point_instance) == {"x1": 10, "y1": 20}
c = C([APoint(30, 40), APoint(50, 60)])
assert asdict(c) == {"aList": [{"x1": 30, "y1": 40}, {"x1": 50, "y1": 60}]}
Außerdem kann die rekursive Geschäftslogik keinesfalls mit Zirkelbezügen umgehen. Wenn Sie dataclasses
verwenden, um beispielsweise einen Graphen oder eine andere Datenstruktur mit Zirkelbezügen darzustellen, wird der asdict
mit Sicherheit abstürzen.
@dataclasses.dataclass
class GraphNode:
name: str
neighbors: list["GraphNode"]
x = GraphNode("x", [])
y = GraphNode("y", [])
x.neighbors.append(y)
y.neighbors.append(x)
dataclasses.asdict(x)
# The code will crash here as
# the max allowed recursion depth would have exceeded
# while calling the python object
# in case you're running this on jupyter notebook notice
# that the kernel will restart as the code crashed
Außerdem baut asdict
ein neues dict
auf, das __dict__
greift aber direkt auf das dict
-Attribut des Objekts zu.
Es ist wichtig zu beachten, dass der Rückgabewert von asdict
in keiner Weise von der Neuzuweisung der Attribute des ursprünglichen Objekts beeinflusst wird.
In Anbetracht dessen, dass asdict
Felder verwendet, wenn Sie Attribute zu einem dataclass
-Objekt hinzufügen, die nicht deklarierten Feldern zugeordnet sind, wird asdict
diese nicht enthalten.
Schließlich, obwohl die Dokumentation es nicht ausdrücklich erwähnt, ruft asdict
Deep-Copy für alles auf, was keine dataclass
-Instanz, dict
, Liste oder Tupel ist.
return copy.deepcopy(instance) # a very costly operation !
Dataclass
-Instanz, Diktate
, Listen und Tupel durchlaufen die rekursive Logik, die zusätzlich eine Kopie nur mit der angewendeten rekursiven Diktifikation erstellt.
Wenn Sie sich mit dem objektorientierten Paradigma einigermaßen auskennen, dann wissen Sie, dass Deep-Copy allein schon eine kostspielige Operation ist, da es jedes Objekt untersucht, um zu sehen, was kopiert werden muss. Der Mangel an Memo-Handhabung bedeutet im Wesentlichen, dass asdict
aller Wahrscheinlichkeit nach mehrere Kopien von gemeinsam genutzten Objekten in nichttrivialen Objektgraphen erstellen könnte.
Vorsicht vor einem solchen Szenario:
from dataclasses import dataclass, asdict
@dataclass
class PointClass:
x1: object
y1: object
obj_instance = object()
var1 = PointClass(obj_instance, obj_instance)
var2 = asdict(var1)
print(var1.x1 is var1.y1) # prints true
print(var2["x1"] is var2["y1"]) # prints false
print(var2["x1"] is var1.x1) # prints false
Ausgang:
True
False
False