Verwendung von Python-Datenklassen
Alles in Python ist ein Objekt, so das Sprichwort. Wenn Sie Ihre eigenen benutzerdefinierten Objekte mit ihren eigenen Eigenschaften und Methoden erstellen möchten, verwenden Sie das Python- class
Objekt, um dies zu erreichen. Das Erstellen von Klassen in Python bedeutet jedoch manchmal das Schreiben einer Menge sich wiederholenden Boilerplate-Codes, um die Klasseninstanz anhand der an sie übergebenen Parameter einzurichten oder allgemeine Funktionen wie Vergleichsoperatoren zu erstellen.
Datenklassen, die in Python 3.7 eingeführt (und in Python 3.6 zurückportiert) wurden, bieten eine praktische Möglichkeit, Klassen weniger ausführlich zu gestalten. Viele der allgemeinen Dinge, die Sie in einer Klasse tun, wie das Instanziieren von Eigenschaften aus den an die Klasse übergebenen Argumenten, können auf einige grundlegende Anweisungen reduziert werden.
Beispiel für eine Python-Datenklasse
Hier ist ein einfaches Beispiel für eine herkömmliche Klasse in Python:
Klassenbuch:'' 'Objekt zum Verfolgen physischer Bücher in einer Sammlung.' ''
def __init __ (self, name: str, weight: float, shel_id: int = 0):
self.name = name
self.weight = weight # in Gramm zur Berechnung des Versands
self.shelf_id = Shelf_id
def __repr __ (self):
return (f "Buch (name = {self.name! r},
weight = {self.weight! r}, shel_id = {self.shelf_id! r}) ")
Das größte Problem hierbei ist die Art und Weise, wie jedes der übergebenen Argumente in __init__
die Eigenschaften des Objekts kopiert werden muss. Das ist nicht so schlimm , wenn man nur es zu tun hat Book
, aber was ist, wenn Sie mit zu tun haben Bookshelf
, Library
, Warehouse
, und so weiter? Je mehr Code Sie von Hand eingeben müssen, desto größer ist die Wahrscheinlichkeit, dass Sie einen Fehler machen.
Hier ist dieselbe Python-Klasse, die als Python-Datenklasse implementiert ist:
aus Datenklassen importieren Datenklasse @Dataclass-Klasse Buch: '' 'Objekt zum Verfolgen physischer Bücher in einer Sammlung.' '' Name: str Gewicht: float Regal_ID: int = 0
Wenn Sie in einer Datenklasse Eigenschaften angeben, die als Felder bezeichnet werden, wird @dataclass
automatisch der gesamte Code generiert, der zum Initialisieren erforderlich ist. Außerdem werden die Typinformationen für jede Eigenschaft beibehalten. Wenn Sie also einen Code-Linter wie verwenden mypy
, wird sichergestellt, dass Sie dem Klassenkonstruktor die richtigen Arten von Variablen bereitstellen.
Eine andere Sache, @dataclass
die hinter den Kulissen geschieht, ist das automatische Erstellen von Code für eine Reihe gängiger Dunder-Methoden in der Klasse. In der oben genannten konventionellen Klasse mussten wir unsere eigenen erstellen __repr__
. In der Datenklasse ist dies nicht erforderlich. @dataclass
generiert das __repr__
für dich.
Sobald eine Datenklasse erstellt wurde, ist sie funktional identisch mit einer regulären Klasse. Es gibt keine Leistungseinbußen bei der Verwendung einer Datenklasse, abgesehen vom minimalen Overhead des Dekorateurs beim Deklarieren der Klassendefinition.
Passen Sie Python-Datenklassenfelder mit der field
Funktion an
Die Standardarbeitsweise von Datenklassen sollte für die meisten Anwendungsfälle in Ordnung sein. Manchmal müssen Sie jedoch genau festlegen, wie die Felder in Ihrer Datenklasse initialisiert werden. Dazu können Sie die field
Funktion verwenden.
aus Datenklassen importieren Datenklasse, Feld aus der Eingabe import List @dataclass class Buch: '' 'Objekt zum Verfolgen physischer Bücher in einer Sammlung.' '' Name: str Bedingung: str = Feld (Vergleich = Falsch) Gewicht: float = Feld (Standard) = 0.0, repr = False) Shelf_id: int = 0 Kapitel: Liste [str] = Feld (default_factory = Liste)
Wenn Sie einen Standardwert für eine Instanz von festlegen field
, ändert sich die Einrichtung des Felds in Abhängigkeit von den von Ihnen angegebenen Parametern field
. Dies sind die am häufigsten verwendeten Optionen für field
(es gibt andere):
default
: Legt den Standardwert für das Feld fest. Sie müssen verwenden,default
wenn Sie a)field
andere Parameter für das Feld ändern und b) darüber hinaus einen Standardwert für das Feld festlegen möchten. In diesem Fall verwenden wir ,default
um Satzweight
zu0.0
.default_factory
: Gibt den Namen einer Funktion an, die keine Parameter akzeptiert und ein Objekt zurückgibt, das als Standardwert für das Feld dient. In diesem Fall möchten wirchapters
eine leere Liste sein.repr
: Standardmäßig (True
) steuert, ob das betreffende Feld in der automatisch generierten__repr__
Datenklasse angezeigt wird. In diesem Fall möchten wir nicht, dass das Gewicht des Buches in der angezeigt wird__repr__
, also lassen wirrepr=False
es weg.compare
: Standardmäßig (True
), enthält das Feld in den Vergleichsmethoden, die automatisch für die Datenklasse generiert werden. Hier wollen wir nichtcondition
als Teil des Vergleichs für zwei Bücher verwendet werden, also setzen wircompare=
False
.
Beachten Sie, dass wir die Reihenfolge der Felder anpassen mussten, damit die nicht standardmäßigen Felder an erster Stelle stehen.
Verwenden Sie __post_init__
diese Option, um die Initialisierung der Python-Datenklasse zu steuern
An dieser Stelle fragen Sie sich wahrscheinlich: Wenn die __init__
Methode einer Datenklasse automatisch generiert wird, wie erhalte ich die Kontrolle über den Init-Prozess, um feinkörnigere Änderungen vorzunehmen?
Geben Sie die __post_init__
Methode ein. Wenn Sie die __post_init__
Methode in Ihre Datenklassendefinition aufnehmen, können Sie Anweisungen zum Ändern von Feldern oder anderen Instanzdaten bereitstellen.
aus Datenklassen importieren Datenklasse, Feld aus der Eingabe import List @dataclass class Buch: '' 'Objekt zum Verfolgen physischer Bücher in einer Sammlung.' '' Name: str Gewicht: float = Feld (Standard = 0.0, repr = False) Shelf_id: int = Feld (init = False) Kapitel: Liste [str] = Feld (default_factory = Liste) Bedingung: str = Feld (Standard = "Gut", Vergleich = Falsch) def __post_init __ (self): wenn self.condition == "verworfen ": self.shelf_id = Keine andere: self.shelf_id = 0
In diesem Beispiel haben wir eine __post_init__
Methode erstellt, auf die festgelegt werden soll shelf_id
, None
ob der Zustand des Buches als initialisiert wird "Discarded"
. Beachten Sie, wie wir verwenden field
zu initialisieren shelf_id
, und übergeben , init
wie False
zu field
. Dies bedeutet, shelf_id
dass nicht in initialisiert wird __init__
.
Verwenden Sie InitVar
diese Option, um die Initialisierung der Python-Datenklasse zu steuern
Eine andere Möglichkeit, das Setup der Python-Datenklasse anzupassen, besteht darin, den InitVar
Typ zu verwenden. Auf diese Weise können Sie ein Feld angeben, das an __init__
und dann an übergeben wird __post_init__
, aber nicht in der Klasseninstanz gespeichert wird.
Mithilfe von InitVar
können Sie beim Einrichten der Datenklasse Parameter berücksichtigen, die nur während der Initialisierung verwendet werden. Ein Beispiel:
aus Datenklassen importieren Datenklasse, Feld, InitVar aus Eingabe von import List @dataclass class Buch: '' 'Objekt zum Verfolgen physischer Bücher in einer Sammlung.' '' Name: str Bedingung: InitVar [str] = Keine weight: float = field (Standard = 0.0, repr = False) Shelf_id: int = Feld (init = False) Kapitel: Liste [str] = Feld (default_factory = Liste) def __post_init __ (Selbst, Bedingung): wenn Bedingung == "Verworfen": self.shelf_id = Keine andere: self.shelf_id = 0
Wenn Sie den Feldtyp auf InitVar
(wobei der Subtyp der tatsächliche @dataclass
Feldtyp ist ) setzen, wird signalisiert , dass dieses Feld nicht in ein Datenklassenfeld umgewandelt, sondern als Daten an die Daten weitergegeben werden soll __post_init__
.
In dieser Version unserer Book
Klasse werden wir nicht condition
als Feld in der Klasseninstanz gespeichert . Wir verwenden nur condition
während der Initialisierungsphase. Wenn wir feststellen, dass dies condition
auf gesetzt wurde "Discarded"
, setzen wir shelf_id
auf None
- aber wir speichern nicht condition
in der Klasseninstanz.
Wann Python-Datenklassen verwendet werden sollen - und wann nicht
Ein häufiges Szenario für die Verwendung von Datenklassen ist der Ersatz des benannten Tupels. Datenklassen bieten das gleiche Verhalten und mehr und können durch einfache Verwendung @dataclass(frozen=True)
als Dekorateur unveränderlich gemacht werden (wie die genannten Tupel) .
Another possible use case is replacing nested dictionaries, which can be clumsy to work with, with nested instances of dataclasses. If you have a dataclass Library
, with a list property shelves
, you could use a dataclass ReadingRoom
to populate that list, and then add methods to make it easy to access nested items (e.g., a book on a shelf in a particular room).
But not every Python class needs to be a dataclass. If you’re creating a class mainly as a way to group together a bunch of static methods, rather than as a container for data, you don’t need to make it a dataclass. For instance, a common pattern with parsers is to have a class that takes in an abstract syntax tree, walks the tree, and dispatches calls to different methods in the class based on the node type. Because the parser class has very little data of its own, a dataclass isn’t useful here.
How to do more with Python
- Get started with async in Python
- How to use asyncio in Python
- How to use PyInstaller to create Python executables
- Cython tutorial: How to speed up Python
- How to install Python the smart way
- How to manage Python projects with Poetry
- How to manage Python projects with Pipenv
- Virtualenv und venv: Erklärte virtuelle Python-Umgebungen
- Python virtualenv und venv tun und nicht tun
- Python-Threading und Unterprozesse erklärt
- Verwendung des Python-Debuggers
- Verwendung von timeit zum Profilieren von Python-Code
- Verwendung von cProfile zum Profilieren von Python-Code
- So konvertieren Sie Python in JavaScript (und wieder zurück)