Python 3.7 Datenklassen

Ernesto Rico SChmidt am 4. Juli 2018

Die Version 3.7 der Programmiersprache Python wurde vergangene Woche, genau nach Plan, am 27. Juni 2018 freigegeben.

Die Liste der Highlights dieser Version ist relativ lang aber ich finde eins der neuen Module besonders interessant: Datenklassen.

@dataclass

PEP 557, beschreibt die Idee und die Implementierung von Datenklassen, und fasst Funktionalität so zusammen:

A class decorator is provided which inspects a class definition for variables with type annotations as defined in PEP 526, "Syntax for Variable Annotations". In this document, such variables are called fields. Using these fields, the decorator adds generated method definitions to the class to support instance initialization, a repr, comparison methods, and optionally other methods as described in the Specification section. Such a class is called a Data Class, but there's really nothing special about the class: the decorator adds generated methods to the class and returns the same class it was given.

Es ist also ein Code-Generator, der die speziellen dunder-Methoden (für double underscore) für die Initialisierung, für die String-Repräsentation, für den Vergleich, und für die Sortierung von Objekten der Klasse, generiert. Das erspart nicht nur Arbeit beim Entwicklen, sondern vereinfacht die Wartung von diesen Methoden.

Dazu wird der Dekorator dataclass definiert, der aus einer normalen Klasse eine mit eine Reihe spezielle Methoden generiert. Die Variablen werden dabei mit ihren Datentyp gemäß dem PEP 526 annotiert.

@dataclass
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

Per Default werden die __init__-, __repr__- und die __eq__-Methode generiert.

  • mit order=True wird bewirkt, dass auch die Methoden für die Sortierung generiert werden (__lt__, __le__, __gt__, __ge__), die dann alle Felder des Objekts vergleichen.
  • per Default wird die __hash__-Methode nicht generiert, erst wenn eq=True und frozen=True sind, dann wird die Methode generiert. Wenn aber eq=True und frozen=False sind, dann wird __hash__=None gesetzt, wodurch das Objekt nicht hasheable wird.
  • mit unsafe_hash=True wird bewirkt, dass eine __hash__-Methode generiert, das wird in der PEP aber ausdrücklich nicht empfohlen.
  • mit frozen=True wird bewirkt, dass die Felder des Objekts nur bei der Initialisierung gesetzt werden dürfen. Beim Versuch sie direkt zu setzten wird eine Ausnahme ValueError geworfen.

Felder

Für den Fall, dass Felder in der Klasse enthalten sein sollen, aber bei der Initialisierung eines neuen Objekts nicht immer angegeben werden müssen, gibt es die Möglichkeit bei der Deklaration dieser Felder einen Default-Wert anzugeben:

@dataclass
class C:
    a: int      # 'a' hat kein default Wert
    b: int = 0  # 'b' hat den default Wert 0

Für den Fall, dass Felder in der Klassen enthalten sein sollen, aber in der Initialisierung eines neuen Objekt nicht angegeben werden, kann das Feld mit Hilfe der field-Funktion angepasst werden:

@dataclass
class C:
    x: int                      # __init__ enthält 'x'
    y: int = field(init=False)  # __init__ enthält nicht 'y'

Per Default wird das Feld in den generierten Methoden für die String-Repräsentation, die Initialisierung und der Vergleich berücksichtigt.

  • die default und default_factory können None sein, daher wird MISSING als Default-Wert definiert. Es erzeugt einen Fehler, wenn beide angegeben werden.
  • mit repr=False wird bewirkt, dass das Feld nicht in der generierten String-Repräsentationsmethoden berücksichtigt wird.
  • mit hash=False wird bewirkt, dass das Feld nicht in der generierten Hash-Methode berücksichtigt wird.
  • mit init=False wird bewirkt, dass das Feld nicht in der generierten Initialisierungsmethode berücksichtig wird.
  • mit compare=False wird bewirkt, dass das Feld nicht in den Vergleichsmethoden berücksichtigt wird.

attrs

Eine bereits vorhanden Lösung für Python 2.7 bis (inklusive) 3.7 ist attrs, die zwar mehr Funktionalität anbietet als das Modul dataclasses, aber wegen der Schnelligkeit ihrer Entwicklung nicht in die Standardbibliothek von Python integriert werden kann.

attrs enthält validators, conversion, metadata und vieles mehr.