Python-Kurs / Klassen, Instanzen und self anhand von Ausgaben verstehen

Eine Klasse beschreibt zusammengehörige Daten und Verhalten; eine Instanz ist ein einzelnes nach dieser Definition erzeugtes Objekt. Verwenden Sie Klassen, wenn ein Zustand und die zugehörigen Regeln gemeinsam modelliert werden sollen, statt jede einfache Berechnung unnötig in ein Objekt zu verpacken.

Eine Definition für mehrere unabhängige Instanzen verwenden

Die Klasse Expense kann sowohl einen Datensatz für das Mittagessen als auch einen für die Busfahrt erzeugen. Beide verwenden dieselbe Definition, besitzen aber ihre eigenen Werte für Name und Betrag. Ein Attribut ist dabei ein unter einem Namen am Objekt erreichbarer Wert; eine Methode ist eine über das Objekt verwendete Funktion.

self ist der übliche Name des Parameters, der bei einem gebundenen Methodenaufruf die betreffende Instanz erhält. Bei first.describe() wird first automatisch als erster Wert an die Methode übergeben. Sie schreiben dort nicht noch einmal first in die Klammern. Das Verhalten hängt an der Bindung der Methode, nicht daran, dass das Wort self ein besonderes Schlüsselwort wäre.

__init__ initialisiert den Zustand der bereits angelegten Instanz. Bei Expense("lunch", 8000) werden Name und Betrag an die Initialisierung übergeben; die Instanz wird dabei bereitgestellt. Die Unterscheidung zwischen Erzeugung und Initialisierung hilft, __init__ nicht als gewöhnliche Funktion zur Rückgabe eines Objekts zu lesen. Die Einführung zu Klassenobjekten beschreibt diesen Aufruf.

Zwei Ausgaben und eine gemeinsame Summe berechnen

Speichern Sie dieses vollständige Beispiel als classes_demo.py in UTF-8 im Übungsordner. Starten Sie python classes_demo.py mit dem Projektinterpreter; außerhalb einer aktivierten Umgebung kann unter macOS oder Ubuntu python3 passen. Zusatzpakete oder Eingabedateien sind nicht nötig.

class Expense:
    def __init__(self, name, amount):
        if amount < 0:
            raise ValueError("negative amount")
        self.name = name
        self.amount = amount

    def describe(self):
        return f"{self.name}: {self.amount:,} KRW"

first = Expense("lunch", 8000)
second = Expense("bus", 2500)
print(first.describe())
print(second.describe())
print(first.amount + second.amount)

Erwartete Ausgabe:

lunch: 8,000 KRW
bus: 2,500 KRW
10500

self.name und self.amount legen die Werte an der jeweiligen Instanz ab. Der erste Aufruf erzeugt einen Datensatz mit 8000, der zweite einen anderen mit 2500. Die Methode describe gibt eine formatierte Zeichenkette zurück; das tatsächliche Anzeigen übernehmen die beiden äußeren print-Aufrufe. Danach werden die beiden numerischen Attribute zu 10500 addiert.

Die Kommas in den Beschreibungen kommen von :, und sind keine automatische deutsche Lokalisierung. KRW ist eine feste Beschriftung in diesem Beispiel; die Methode konvertiert keine Währungen. Die englischen Namen bleiben unveränderte Testwerte.

Objektzustand und Verantwortung begrenzen

Name im Beispiel Instanz Eigener Anfangszustand
first Datensatz A name="lunch", amount=8000
second Datensatz B name="bus", amount=2500

Die Tabelle entspricht genau den Werten des ausführbaren Beispiels. Eine Änderung von first.amount ändert nicht automatisch den Betrag von second. Wenn Sie dagegen alias = first schreiben, ist alias nur ein weiterer Name für dieselbe erste Instanz. Die Übung zu Referenzen und veränderlichen Objekten erklärt diese Unterscheidung zwischen zweitem Namen und zweitem Objekt.

Der Initialisierer lehnt negative Beträge ab, erzwingt aber keinen vollständigen Datentypvertrag. Ein Zeichenkettenbetrag scheitert beim numerischen Vergleich; ein positiver Dezimalwert oder ein boolescher Wert wird von dieser einzelnen Negativprüfung nicht grundsätzlich ausgeschlossen. Entscheiden Sie bei einer Erweiterung ausdrücklich, ob solche Eingaben erlaubt sind, und ergänzen Sie die notwendige Prüfung.

Eine spätere direkte Zuweisung wie first.amount = -1 umgeht außerdem die Initialisierungsprüfung. Damit zeigt diese Klasse eine einfache Anfangsregel, aber noch keine Kontrolle aller späteren Änderungen. Ebenso kann eine falsch geschriebene neue Attributzuweisung bei dieser normalen Klasse ein zusätzliches Attribut erzeugen. Geben Sie solche Grenzen im Vertrag an und prüfen Sie den tatsächlichen Zustand, statt allein aus dem erfolgreichen Erzeugen auf dauerhaft gültige Daten zu schließen.

Halten Sie die Aufgabe der Klasse klein. Eine Beschreibung und Betragsregel gehören zu diesem Datensatz; Dateispeicherung, Netzwerkübertragung und Eingabeaufforderungen müssen nicht ebenfalls Teil derselben Klasse werden. Explizite Zusammenarbeit mit anderen Funktionen oder Objekten macht diese Aufgaben leichter unabhängig testbar.

Unabhängigkeit und Methodenbindung prüfen

  1. Fügen Sie vor den Ausgaben nur first.amount = 9000 ein. Die erste Beschreibung soll 9000 verwenden, die zweite weiterhin 2500. Die gemeinsame Summe steigt auf 11500. Entfernen Sie die Zuweisung anschließend wieder.
  2. Erzeugen Sie in einem getrennten Test Expense("test", -1). Die Initialisierung muss ValueError: negative amount auslösen. Ein solches fehlgeschlagenes Erzeugen ist etwas anderes als eine spätere direkte Änderung am bereits vorhandenen Objekt.
  3. Vergleichen Sie Expense.describe(first) und first.describe(). Beide liefern dieselbe Beschreibung, weil die erste Schreibweise die Instanz ausdrücklich übergibt und die zweite sie als gebundener Methodenaufruf bereitstellt. Die Erklärung zu Methodenobjekten beschreibt diese Bindung.
  4. Prüfen Sie eine absichtlich falsch geschriebene Zuweisung first.amout = 1: Der ursprüngliche Wert first.amount bleibt unverändert. Entfernen Sie das zusätzliche Testattribut oder starten Sie das Skript frisch und prüfen Sie wieder die ursprünglichen drei Ausgabezeilen.

Klassen sind kein Selbstzweck zur Verkürzung von Code. Prüfen Sie, ob sie Daten und Regeln klarer zusammenführen und ob die gewünschten Zustandsänderungen tatsächlich durchgesetzt werden. Typangaben, Dataclasses und automatisierte Prüfungen können bei größeren Datenmodellen helfen, ersetzen aber nicht jeweils dieselbe Art von Laufzeitprüfung.