Python Dict gegen Asdict

Abdullah Bukhari 21 Juni 2023
  1. die Bibliothek dataclasses in Python
  2. Warum dict schneller ist als asdict
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

Verwandter Artikel - Python Dataclass