Dataclass in Python¶
Fortgeschrittene Techniken mit Python dataclass¶
In diesem Kapitel unseres Python-Tutorials tauchen wir in die mächtige Welt der Python-Datenklassen ein. Datenklassen sind ein wesentliches Feature, was in Python 3.7 eingeführt wurde, um die Erstellung und Verwaltung von Klassen zu vereinfachen, die hauptsächlich zum Speichern von Daten verwendet werden. Zu Beginn des Kapitels werden die Unterschiede zwischen traditionellen Klassenstrukturen zur Darstellung von Daten aufgezeigt und Dataclasses als attraktive Alternative vorgestellt. Wir werden die Syntax und die Funktionalitäten von Datenklassen untersuchen und zeigen, wie die Lesbarkeit des Codes durch sie verbessert werden kann.
Erste Beispiele¶
In unserem ersten Beispiel bleiben wir unserem geliebten Roboter Marvin treu. Wir beginnen also mit einer "traditionellen" Python-Klasse Robot_traditional
, die einen Roboter darstellt:
class Robot_traditional:
def __init__(self, model, serial_number, manufacturer):
self.model = model
self.serial_number = serial_number
self.manufacturer = manufacturer
Der Standardcode innerhalb der __init__
-Methode folgt einem ähnlichen Musterin fast allen Klassendefinition. So auch indiesem Beispiel, wobei die einzige Variation die Namen der den Attributen zugewiesenen Bezeichnungen sind. Dies wird besonders mühsam, wenn die Anzahl der Attribute zunimmt.
Wenn wir uns das gleiche Beispiel in der Notation mittels dataclass
anschauen, sehen wir, dass der Code erheblich schlanker wird. Bevor wir jedoch dataclass als Dekorateur für unsere Klasse verwenden können, müssen wir es aus dem Modul dataclasses
importieren.
from dataclasses import dataclass
@dataclass
class Robot:
model: str
serial_number: str
manufacturer: str
Hier automatisiert der Datenklassen-Dekorator die Generierung spezieller Methoden wie __init__
, wodurch der Bedarf an Boilerplate-Code reduziert wird. Die Klassendefinition ist prägnant, was sie übersichtlicher und wartungsfreundlicher macht, insbesondere wenn die Anzahl der Attribute steigt.
Dieses Beispiel zeigt, wie die Verwendung von Datenklassen für eine Klasse, die hauptsächlich zum Speichern von Daten verwendet wird, wie z. B. eine Roboterdarstellung, eine schlankere und lesbarere Alternative zu traditionellen Klassenstrukturen bietet.
Die Initialisierung von Robotern beider Klassen ist identisch;
x = Robot_traditional("NanoGuardian XR-2000", "234-76", "Cyber Robotics Co.")
y = Robot("MachinaMaster MM-42", "986-42", "Quantum Automations Inc.")
Doch es gibt noch weitere Unterschiede in diesen Klassen. Der Klassendekorator dataclass
hat nicht nur die spezielle Methode __init__
erstellt, sondern auch __repr__
, __eq__
, __ne__
und __hash__
. Methoden, die Sie ansonsten manuell zum Aufruf Robot_traditional
hinzufügen müssten.
Werfen wir einen Blick auf __repr__
und vergleichen wir es mit der traditionellen Klassendefinition:
class Robot_traditional:
def __init__(self, model, serial_number, manufacturer):
self.model = model
self.serial_number = serial_number
self.manufacturer = manufacturer
def __repr__(self):
return f"Robot_traditional(model='{self.model}', serial_number='{self.serial_number}', manufacturer='{self.manufacturer}')"
x = Robot_traditional("NanoGuardian XR-2000", "234-76", "Cyber Robotics Co.")
print(repr(x))
Unveränderliche Klassen¶
In unserem Kapitel zu Unveränderliche Klassen in Python (Immutable Klassen unseres Tutorials diskutieren wir die Gründe für die Notwendigkeit von Unveränderlichkeit und erkunden verschiedene Methoden zu ihrer Erstellung.
Mit Dataclasses ist es sehr einfach. Alles, was man tun muss, ist, den Dekorator mit frozen=True
aufzurufen:
from dataclasses import dataclass
@dataclass(frozen=True)
class ImmutableRobot:
name: str
brandname: str
Mit dieser Klasse können wir zwei Roboter auf Gleichheit prüfen. Sie sind gleich, wenn alle Attribute gleich sind. Denken Sie daran, dass __eq__
automatisch erstellt wurde.
x1 = ImmutableRobot("Marvin", "NanoGuardian XR-2000")
x2 = ImmutableRobot("Marvin", "NanoGuardian XR-2000")
print(x1 == x2)
Schauen wir uns die Hahs-Werte an:
print(x1.__hash__(), x2.__hash__())
Zunächst können wir sehen, dass __hash__
automatisch erstellt und korrekt implementiert wurde. Dadurch wird sichergestellt, dass Roboter, die gleich sind, identische Hash-Werte erzeugen.
Nun wollen wir eine ähnliche Klasse implementieren, ohne eine Datenklasse zu verwenden. In unserer zuvor definierten Klasse Robot_traditional
sind die Instanzen veränderbar, da wir die Möglichkeit haben, die Attribute zu ändern. Das folgende Beispiel ist eine unveränderliche Version. Wir können sofort sehen, dass es eine Menge mehr Kodierungsaufwand gibt. Wir müssen __init__', die Getter-Eigenschaften,
eqund
hash` implementieren:
class ImmutableRobot_traditional:
def __init__(self, name: str, brandname: str):
self._name = name
self._brandname = brandname
@property
def name(self) -> str:
return self._name
@property
def brandname(self) -> str:
return self._brandname
def __eq__(self, other):
if not isinstance(other, ImmutableRobot_traditional):
return False
return self.name == other.name and self.brandname == other.brandname
def __hash__(self):
return hash((self.name, self.brandname))
x1 = ImmutableRobot_traditional("Marvin", "NanoGuardian XR-2000")
x2 = ImmutableRobot_traditional("Marvin", "NanoGuardian XR-2000")
print(x1 == x2)
print(x1.__hash__(), x2.__hash__())
print(x1.__hash__(), x2.__hash__())
Eine unveränderliche Klasse mit einer `hash-Methode zu haben, bedeutet, dass wir unsere Klasse in Mengen und Wörterbüchern verwenden können. Wir veranschaulichen dies im folgenden Beispiel:
from dataclasses import dataclass
@dataclass(frozen=True)
class ImmutableRobot:
name: str
brandname: str
robot1 = ImmutableRobot("Marvin", "NanoGuardian XR-2000")
robot2 = ImmutableRobot("R2D2", "QuantumTech Sentinel-7")
robot3 = ImmutableRobot("Marva", "MachinaMaster MM-42")
# we create a set of Robots:
robots = {robot1, robot2, robot3}
print("The robots in the set robots:")
for robo in robots:
print(robo)
# now a dictionary with robots as keys:
activity = {robot1: 'activated', robot2: 'activated', robot3: 'deactivated'}
print("\nAll the activated robots:")
for robo, mode in activity.items():
if mode == 'activated':
print(f"{robo} is activated")
Zusammenfassung¶
Die Verwendung einer Datenklasse bietet mehrere Vorteile gegenüber traditionellen Klassendefinitionen in Python:
Automatische Generierung von speziellen Methoden: Mit Datenklassen müssen Sie spezielle Methoden wie
__init__
,__repr__
,__eq__
,__hash__
usw. nicht manuell schreiben. Derdataclass
-Dekorator generiert diese Methoden automatisch basierend auf den Klassenattributen, die Sie definieren.Prägnante Syntax:
dataclass
verwendet eine prägnante Syntax zur Definition von Klassen, wodurch Boilerplate-Code reduziert wird. Sie müssen nur die Attribute der Klasse angeben, und der Dekorator kümmert sich um den Rest.Integrierte Vergleichsmethoden:
dataclass
bietet automatisch Implementierungen für Vergleichsmethoden wie__eq__
,__ne__
,__lt__
,__le__
,__gt__
und__ge__
, basierend auf den Attributen der Klasse.Unveränderliche Instanzen: Durch die Angabe von
frozen=True
imdataclass
-Dekorator können Instanzen der Klasse unveränderlich gemacht werden, was dazu beitragen kann, versehentliche Änderungen an Daten zu verhindern.Integration mit Typ-Hinweisen:
dataclass
integriert sich nahtlos in das Typ-Hinweis-System von Python, sodass Sie die Typen von Attributen für eine bessere Code-Lesbarkeit und statische Analyse angeben können.Standardwerte und Standardwerte-Fabrik: Sie können Standardwerte für Attribute direkt in der Klassendefinition oder unter Verwendung von Fabrikfunktionen angeben, was die Notwendigkeit für Boilerplate-Code in der
__init__
-Methode reduziert.Unterstützung für Vererbung:
dataclass
unterstützt Vererbung, sodass Sie Unterklassen mit zusätzlichen Attributen oder Methoden erstellen können, während Sie das Verhalten der Elternklasse erben.Anpassung: Obwohl
dataclass
die automatische Generierung spezieller Methoden bietet, können Sie diese Methoden bei Bedarf weiterhin anpassen oder überschreiben, was Ihnen Flexibilität bei der Definition des Klassenverhaltens gibt.
Insgesamt vereinfacht dataclass
den Prozess der Klassenerstellung in Python und macht den Code prägnanter, lesbarer und wartbarer, insbesondere für Klassen, die hauptsächlich als Container für Daten dienen.
This Markdown text provides the translated version of the original text into German.
Exercises¶
Exercise 1: Buchinformationen¶
Erstellen Sie eine Book
-Klasse mithilfe von dataclass
, um Informationen über Bücher darzustellen. Jedes Buch sollte die folgenden Attribute haben:
- Titel
- Autor
- ISBN (Internationale Standardbuchnummer)
- Veröffentlichungsjahr
- Genre
Schreiben Sie ein Programm, das Folgendes tut:
- Definieren Sie die
Book
-Klasse mitdataclass
. - Erstellen Sie Instanzen mehrerer Bücher.
- Geben Sie die Details jedes Buches aus, einschließlich Titel, Autor, ISBN, Veröffentlichungsjahr und Genre.
Sie können diese Übung verwenden, um das Definieren von dataclass
, das Erstellen von Instanzen und den Zugriff auf Attribute von dataclass
-Objekten zu üben. Darüber hinaus können Sie erkunden, wie Sie der Book
-Klasse Methoden oder Anpassungen hinzufügen können, z. B. eine Methode zur Berechnung des Alters des Buches basierend auf dem Veröffentlichungsjahr oder die Validierung von ISBN-Nummern hinzufügen.
from dataclasses import dataclass
@dataclass
class Book:
title: str
author: str
isbn: str
publication_year: int
genre: str
# Create instances of several books
buch1 = Book("Die Verwandlung", "Franz Kafka", "9783150091563", 1915, "Roman")
buch2 = Book("Faust. Der Tragödie erster Teil", "Johann Wolfgang von Goethe", "9783150000008", 1808, "Drama")
buch3 = Book("Der Steppenwolf", "Hermann Hesse", "9783518366868", 1927, "Roman")
# Print out the details of each book
print("Buch 1:")
print("Titel:", buch1.title)
print("Autor:", buch1.author)
print("ISBN:", buch1.isbn)
print("Veröffentlichungsjahr:", buch1.publication_year)
print("Genre:", buch1.genre)
print("\nBuch 2:")
print("Titel:", buch2.title)
print("Autor:", buch2.author)
print("ISBN:", buch2.isbn)
print("Veröffentlichungsjahr:", buch2.publication_year)
print("Genre:", buch2.genre)
print("\nBuch 3:")
print("Titel:", buch3.title)
print("Autor:", buch3.author)
print("ISBN:", buch3.isbn)
print("Veröffentlichungsjahr:", buch3.publication_year)
print("Genre:", buch3.genre)