

Bild vom Autor
# Einführung
Commonplace-Python-Objekte speichern Attribute in Instanzwörterbüchern. Sie sind nicht hashbar, es sei denn, Sie implementieren das Hashing manuell, und sie vergleichen standardmäßig alle Attribute. Dieses Standardverhalten ist sinnvoll, aber nicht für Anwendungen optimiert, die viele Instanzen erstellen oder Objekte als Cache-Schlüssel benötigen.
Datenklassen Beheben Sie diese Einschränkungen durch Konfiguration und nicht durch benutzerdefinierten Code. Sie können Parameter verwenden, um das Verhalten von Instanzen und die Speichernutzung zu ändern. Mit Einstellungen auf Feldebene können Sie außerdem Attribute von Vergleichen ausschließen, sichere Standardwerte für veränderbare Werte definieren oder steuern, wie die Initialisierung funktioniert.
Dieser Artikel konzentriert sich auf die wichtigsten Datenklassenfunktionen, die die Effizienz und Wartbarkeit verbessern, ohne die Komplexität zu erhöhen.
Den Code finden Sie auf GitHub.
# 1. Eingefrorene Datenklassen für Hashbarkeit und Sicherheit
Die Unveränderlichkeit Ihrer Datenklassen sorgt für Hashfähigkeit. Dadurch können Sie Instanzen als Wörterbuchschlüssel verwenden oder sie in Sätzen speichern, wie unten gezeigt:
from dataclasses import dataclass
@dataclass(frozen=True)
class CacheKey:
user_id: int
resource_type: str
timestamp: int
cache = {}
key = CacheKey(user_id=42, resource_type="profile", timestamp=1698345600)
cache(key) = {"information": "expensive_computation_result"}
Der frozen=True Der Parameter macht alle Felder nach der Initialisierung unveränderlich und wird automatisch implementiert __hash__(). Ohne es würden Sie auf a stoßen TypeError beim Versuch, Instanzen als Wörterbuchschlüssel zu verwenden.
Dieses Muster ist für den Aufbau von Caching-Ebenen, Deduplizierungslogik oder anderen Datenstrukturen, die hashbare Typen erfordern, von wesentlicher Bedeutung. Die Unveränderlichkeit verhindert auch ganze Kategorien von Fehlern, bei denen der Standing unerwartet geändert wird.
# 2. Steckplätze für Speichereffizienz
Wenn Sie Tausende von Objekten instanziieren, erhöht sich der Speicheraufwand schnell. Hier ist ein Beispiel:
from dataclasses import dataclass
@dataclass(slots=True)
class Measurement:
sensor_id: int
temperature: float
humidity: float
Der slots=True Der Parameter eliminiert die Instanz professional Instanz __dict__ das Python normalerweise erstellt. Anstatt Attribute in einem Wörterbuch zu speichern, verwenden Slots ein kompakteres Array fester Größe.
Für eine einfache Datenklasse wie diese, Sie Sparen Sie mehrere Bytes professional Instanz und erhalten Sie einen schnelleren Attributzugriff. Der Nachteil besteht darin, dass Sie keine neuen Attribute dynamisch hinzufügen können.
# 3. Benutzerdefinierte Gleichheit mit Feldparametern
Oftmals benötigen Sie nicht jedes Feld, um an Gleichheitsprüfungen teilzunehmen. Dies gilt insbesondere beim Umgang mit Metadaten oder Zeitstempeln, wie im folgenden Beispiel:
from dataclasses import dataclass, discipline
from datetime import datetime
@dataclass
class Person:
user_id: int
e-mail: str
last_login: datetime = discipline(evaluate=False)
login_count: int = discipline(evaluate=False, default=0)
user1 = Person(1, "alice@instance.com", datetime.now(), 5)
user2 = Person(1, "alice@instance.com", datetime.now(), 10)
print(user1 == user2)
Ausgabe:
Der evaluate=False Der Parameter für ein Feld schließt es von der automatischen Generierung aus __eq__() Verfahren.
Hier gelten zwei Benutzer als gleich, wenn sie dieselbe ID und E-Mail-Adresse haben, unabhängig davon, wann und wie oft sie sich angemeldet haben. Dies verhindert falsche Ungleichheit beim Vergleich von Objekten, die dieselbe logische Entität darstellen, aber über unterschiedliche Monitoring-Metadaten verfügen.
# 4. Manufacturing unit-Funktionen mit Default Manufacturing unit
Die Verwendung veränderlicher Standardwerte in Funktionssignaturen ist eine Python, verstanden. Datenklassen bieten eine saubere Lösung:
from dataclasses import dataclass, discipline
@dataclass
class ShoppingCart:
user_id: int
gadgets: record(str) = discipline(default_factory=record)
metadata: dict = discipline(default_factory=dict)
cart1 = ShoppingCart(user_id=1)
cart2 = ShoppingCart(user_id=2)
cart1.gadgets.append("laptop computer")
print(cart2.gadgets)
Der default_factory Der Parameter akzeptiert einen Callable, der für jede Instanz einen neuen Standardwert generiert. Ohne es, mit gadgets: record = () würde eine einzige gemeinsame Liste für alle Instanzen erstellen – das klassische veränderbare Standardproblem!
Dieses Muster funktioniert für Pay attention, Diktate, Mengen oder jeden veränderlichen Typ. Sie können auch benutzerdefinierte Manufacturing unit-Funktionen für eine komplexere Initialisierungslogik übergeben.
# 5. Verarbeitung nach der Initialisierung
Manchmal müssen Sie nach der automatischen Generierung Felder ableiten oder Daten validieren __init__ läuft. Hier erfahren Sie, wie Sie dies mit erreichen können post_init Haken:
from dataclasses import dataclass, discipline
@dataclass
class Rectangle:
width: float
peak: float
space: float = discipline(init=False)
def __post_init__(self):
self.space = self.width * self.peak
if self.width <= 0 or self.peak <= 0:
elevate ValueError("Dimensions should be optimistic")
rect = Rectangle(5.0, 3.0)
print(rect.space)
Der __post_init__ Die Methode wird unmittelbar nach der generierten ausgeführt __init__ vervollständigt. Der init=False Der Parameter für die Fläche verhindert, dass daraus ein wird __init__ Parameter.
Dieses Muster eignet sich perfekt für berechnete Felder, Validierungslogik oder die Normalisierung von Eingabedaten. Sie können es auch verwenden, um Felder zu transformieren oder Invarianten zu erstellen, die von mehreren Feldern abhängen.
# 6. Bestellung mit Bestellparameter
Manchmal müssen Ihre Datenklasseninstanzen sortierbar sein. Hier ist ein Beispiel:
from dataclasses import dataclass
@dataclass(order=True)
class Process:
precedence: int
identify: str
duties = (
Process(precedence=3, identify="Low precedence activity"),
Process(precedence=1, identify="Vital bug repair"),
Process(precedence=2, identify="Function request")
)
sorted_tasks = sorted(duties)
for activity in sorted_tasks:
print(f"{activity.precedence}: {activity.identify}")
Ausgabe:
1: Vital bug repair
2: Function request
3: Low precedence activity
Der order=True Parameter generiert Vergleichsmethoden (__lt__, __le__, __gt__, __ge__) basierend auf der Feldreihenfolge. Da die Felder von hyperlinks nach rechts verglichen werden, hat in diesem Beispiel die Priorität Vorrang vor dem Namen.
Mit dieser Funktion können Sie Sammlungen auf natürliche Weise sortieren, ohne eine benutzerdefinierte Vergleichslogik oder Schlüsselfunktionen schreiben zu müssen.
# 7. Feldreihenfolge und InitVar
Wenn die Initialisierungslogik Werte erfordert, die nicht zu Instanzattributen werden sollen, können Sie diese verwenden InitVarwie unten gezeigt:
from dataclasses import dataclass, discipline, InitVar
@dataclass
class DatabaseConnection:
host: str
port: int
ssl: InitVar(bool) = True
connection_string: str = discipline(init=False)
def __post_init__(self, ssl: bool):
protocol = "https" if ssl else "http"
self.connection_string = f"{protocol}://{self.host}:{self.port}"
conn = DatabaseConnection("localhost", 5432, ssl=True)
print(conn.connection_string)
print(hasattr(conn, 'ssl'))
Ausgabe:
https://localhost:5432
False
Der InitVar Der Typhinweis markiert einen Parameter, an den übergeben wird __init__ Und __post_init__ wird aber kein Feld. Dadurch bleibt Ihre Instanz sauber und ermöglicht gleichzeitig eine komplexe Initialisierungslogik. Der ssl Das Flag beeinflusst, wie wir die Verbindungszeichenfolge erstellen, muss danach aber nicht bestehen bleiben.
# Wann Datenklassen nicht verwendet werden sollten
Datenklassen sind nicht immer das richtige Werkzeug. Verwenden Sie keine Datenklassen, wenn:
- Sie benötigen komplexe Vererbungshierarchien mit benutzerdefinierten
__init__Logik über mehrere Ebenen hinweg - Sie erstellen Klassen mit signifikantem Verhalten und Methoden (verwenden Sie reguläre Klassen für Domänenobjekte).
- Sie benötigen Validierungs-, Serialisierungs- oder Analysefunktionen, die Bibliotheken mögen Pydantisch oder attrs bieten
- Sie arbeiten mit Klassen, die komplizierte Statusverwaltungs- oder Lebenszyklusanforderungen haben
Datenklassen eignen sich am besten als einfache Datencontainer und nicht als Domänenobjekte mit vollem Funktionsumfang.
# Abschluss
Beim Schreiben effizienter Datenklassen geht es darum, zu verstehen, wie ihre Optionen interagieren, und nicht darum, sie alle auswendig zu lernen. Wissen Wann Und Warum Es ist wichtiger, jede Funktion zu nutzen, als sich jeden Parameter zu merken.
Wie im Artikel erläutert, ermöglicht Ihnen die Verwendung von Funktionen wie Unveränderlichkeit, Slots, Feldanpassung und Publish-Init-Hooks das Schreiben von Python-Objekten, die schlank, vorhersehbar und sicher sind. Diese Muster tragen dazu bei, Fehler zu vermeiden und den Speicheraufwand zu reduzieren, ohne die Komplexität zu erhöhen.
Mit diesen Ansätzen können Sie mit Datenklassen sauberen, effizienten und wartbaren Code schreiben. Viel Spaß beim Codieren!
Bala Priya C ist ein Entwickler und technischer Redakteur aus Indien. Sie arbeitet gerne an der Schnittstelle von Mathematik, Programmierung, Datenwissenschaft und Inhaltserstellung. Zu ihren Interessen- und Fachgebieten gehören DevOps, Datenwissenschaft und Verarbeitung natürlicher Sprache. Sie liebt es zu lesen, zu schreiben, zu programmieren und Kaffee zu trinken! Derzeit arbeitet sie daran, zu lernen und ihr Wissen mit der Entwickler-Group zu teilen, indem sie Tutorials, Anleitungen, Meinungsbeiträge und mehr verfasst. Bala erstellt außerdem ansprechende Ressourcenübersichten und Programmier-Tutorials.
