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- classObjekt, 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  BookshelfLibraryWarehouse, 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, defaultwenn 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 Satz  weight zu  0.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 wir  chapters 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 wir  repr=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 nicht  condition als Teil des Vergleichs für zwei Bücher verwendet werden, also setzen wir  compare=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 InitVarkö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 @dataclassFeldtyp 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 conditionwä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)