Zum Inhalt springen
aviral gupta

// I2.2 · ca. 30 Min. · Aufbau

Klassen und Instanzen

Nach dieser Lektion definieren Sie eine Klasse mit __init__ und Methoden, erzeugen Instanzen mit jeweils eigenen Daten und wählen zwischen Instanz- und Klassenvariablen.

Lektion 2 von 6 in I2 Klassen

Danach können Sie

  • Eine Klasse mit __init__ und Methoden definieren, Instanzen erzeugen und über self auf ihre Daten zugreifen
  • Instanzvariablen von Klassenvariablen unterscheiden und vorhersagen, welche ein Attributzugriff findet
  • Erklären, wie ein Methodenaufruf die Instanz als self übergibt, und Methoden richtig aufrufen
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen aus Modul B4: Methoden auf Objekten rufen Sie seit den ersten Modulen auf. Was gibt das aus?

    names = ["bob", "ada"]
    names.sort()
    print(names, "hi".upper())
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: zwei Objekte derselben Klasse. Was gibt das aus?

    class Counter:
        def __init__(self):
            self.count = 0
    
        def increment(self):
            self.count += 1
    
    
    a = Counter()
    b = Counter()
    a.increment()
    a.increment()
    b.increment()
    print(a.count, b.count)
  3. Üben · Aufgabe 3 von 7

    Setzen Sie den Namen ein, damit jeder Point sein eigenes x speichert.

    class Point:
        def __init__(self, x, y):
            ____.x = x
            self.y = y
    .x = x
  4. Üben · Aufgabe 4 von 7

    kind ist eine Klassenvariable. Was gibt das aus?

    class Dog:
        kind = "canine"
    
        def __init__(self, name):
            self.name = name
    
    
    d = Dog("Fido")
    e = Dog("Buddy")
    e.kind = "robot"
    print(d.kind, e.kind, Dog.kind)
  5. Üben · Aufgabe 5 von 7

    Ordnen Sie jedem Ausdruck zu, was er ist oder tut.

  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Zwei Hunde lernen je einen Trick. Was gibt das aus?

    class Dog:
        tricks = []
    
        def __init__(self, name):
            self.name = name
    
        def add_trick(self, trick):
            self.tricks.append(trick)
    
    
    d = Dog("Fido")
    e = Dog("Buddy")
    d.add_trick("roll over")
    e.add_trick("play dead")
    print(d.tricks)
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe. Schreiben Sie eine Klasse Rectangle, deren __init__ width und height nimmt. Ergänzen Sie eine Methode area(), die Breite mal Höhe liefert, und eine Methode scale(factor), die beide Seiten mit factor multipliziert. Rectangle(3, 4) hat die Fläche 12; nach scale(2) ist es 6 mal 8 groß, mit Fläche 48.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Ein Warenkorb

Jeder Cart speichert seinen Besitzer und eine eigene Liste von Positionen, angelegt in __init__. Die Methoden rufen einander über self auf: summary nutzt total, total nutzt net. vat_rate ist eine Klassenvariable; eine Änderung an der Klasse gilt also für jeden Warenkorb. Die dritte Zeile zeigt, dass ein Methodenaufruf die Funktion der Klasse ist, mit der Instanz als self.

main.py

class Cart:
    """A shopping cart that holds (name, price, quantity) lines."""

    vat_rate = 0.19  # class variable: the same for every cart

    def __init__(self, owner: str) -> None:
        self.owner = owner
        self.lines: list[tuple[str, float, int]] = []  # a new list for each cart

    def add(self, name: str, price: float, quantity: int = 1) -> None:
        self.lines.append((name, price, quantity))

    def net(self) -> float:
        return sum(price * quantity for _, price, quantity in self.lines)

    def total(self) -> float:
        return round(self.net() * (1 + self.vat_rate), 2)

    def summary(self) -> str:
        count = sum(quantity for _, _, quantity in self.lines)
        return f"{self.owner}: {count} item(s), {self.total():.2f} EUR"


ada = Cart("ada")
ada.add("coffee", 8.50, 2)
ada.add("mug", 12.00)
bob = Cart("bob")
bob.add("tea", 4.20)

print(ada.summary())
print(bob.summary())
print(Cart.summary(bob) == bob.summary())
Cart.vat_rate = 0.07  # changes it for every cart
print(ada.summary())

Ausführen mit

python main.py

Ausgabe

ada: 3 item(s), 34.51 EUR
bob: 1 item(s), 5.00 EUR
True
ada: 3 item(s), 31.03 EUR
  • ada und bob haben getrennte lines-Listen, weil jeder Aufruf von __init__ eine neue anlegt.
  • self.vat_rate findet die Klassenvariable, da kein Warenkorb ein eigenes vat_rate hat.
  • Cart.summary(bob) ist derselbe Aufruf wie bob.summary().
  • Nach Cart.vat_rate = 0.07 verwendet jeder Warenkorb den neuen Satz.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 2

Ein Buch, das Sie gerade lesen

Vervollständigen Sie die Klasse Book. Book(title, pages) speichert beides und beginnt mit current_page bei 0. read(n) rückt current_page um n Seiten vor, aber nie über die letzte Seite hinaus, und liefert das neue current_page. is_finished() liefert True, sobald current_page gleich pages ist. Jedes Buch merkt sich seinen eigenen Fortschritt.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Setzen Sie self.current_page = 0 in __init__, neben title und pages.

  2. Hinweis 2

    Addieren Sie in read n zu self.current_page und begrenzen Sie mit min(..., self.pages), damit es nicht über die letzte Seite geht.

  3. Hinweis 3

    is_finished liefert den Vergleich selbst: return self.current_page == self.pages.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

class Book:
    def __init__(self, title: str, pages: int) -> None:
        self.title = title
        self.pages = pages
        self.current_page = 0

    def read(self, n: int) -> int:
        """Read n pages, stopping at the last page; return the current page."""
        self.current_page = min(self.current_page + n, self.pages)
        return self.current_page

    def is_finished(self) -> bool:
        return self.current_page == self.pages
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

class Book:
    def __init__(self, title: str, pages: int) -> None:
        self.title = title
        self.pages = pages

    def read(self, n: int) -> int:
        """Read n pages, stopping at the last page; return the current page."""
        return 0

    def is_finished(self) -> bool:
        return False

test_main.py

from main import Book


def test_starts_at_zero():
    """Ein neues Buch beginnt bei Seite 0"""
    book = Book("Dune", 600)
    got = getattr(book, "current_page", None)
    assert got == 0, f"current_page eines neuen Buchs ist {got!r}, erwartet: 0"


def test_read_moves_on():
    """read(n) addiert n Seiten und liefert die neue Seite"""
    book = Book("Dune", 600)
    got = book.read(40), book.read(10)
    assert got == (40, 50), f"read(40), read(10) lieferten {got!r}, erwartet: (40, 50)"


def test_stops_at_last_page():
    """read geht nie über die letzte Seite hinaus"""
    book = Book("Emma", 100)
    got = book.read(250)
    assert got == 100, f"read(250) bei einem Buch mit 100 Seiten lieferte {got!r}, erwartet: 100"


def test_finished():
    """is_finished ist erst auf der letzten Seite True"""
    book = Book("Emma", 100)
    book.read(99)
    assert book.is_finished() is False, "is_finished() ist auf Seite 99 von 100 schon True"
    book.read(1)
    assert book.is_finished() is True, "is_finished() ist auf Seite 100 von 100 noch False"


def test_separate_books():
    """Zwei Bücher merken sich ihren Fortschritt getrennt"""
    a = Book("A", 50)
    b = Book("B", 50)
    a.read(20)
    assert b.read(5) == 5, "das Lesen in Buch a hat den Fortschritt von Buch b verändert"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 2 von 2

Nummerierte Tickets

Jedes Ticket soll die nächste Nummer bekommen: 1, 2, 3 und so weiter, gezählt in der Klassenvariable issued. Jedes Ticket hat außerdem eine eigene Liste von Sitzplätzen. Der Starter hat zwei Fehler: Jedes Ticket bekommt die Nummer 1, und alle Tickets teilen sich eine seats-Liste. Beheben Sie beide und lassen Sie issued auf der Klasse.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    self.issued += 1 liest die Klassenvariable, legt dann aber ein Instanzattribut an. Ändern Sie die Klassenvariable über die Klasse: Ticket.issued += 1.

  2. Hinweis 2

    Eine Liste im Klassenrumpf teilen sich alle Tickets. Legen Sie self.seats = [] stattdessen in __init__ an.

  3. Hinweis 3

    Entfernen Sie die seats-Zeile aus dem Klassenrumpf und ergänzen Sie self.seats: list[str] = [] in __init__.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

class Ticket:
    issued = 0

    def __init__(self, event: str) -> None:
        self.event = event
        Ticket.issued += 1
        self.number = Ticket.issued
        self.seats: list[str] = []

    def add_seat(self, seat: str) -> None:
        self.seats.append(seat)
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

class Ticket:
    issued = 0
    seats: list[str] = []

    def __init__(self, event: str) -> None:
        self.event = event
        self.issued += 1
        self.number = self.issued

    def add_seat(self, seat: str) -> None:
        self.seats.append(seat)

test_main.py

from main import Ticket


def test_numbers_count_up():
    """Tickets bekommen die Nummern 1, 2, 3"""
    Ticket.issued = 0
    got = [Ticket("concert").number for _ in range(3)]
    assert got == [1, 2, 3], f"drei Tickets bekamen die Nummern {got!r}, erwartet: [1, 2, 3]"


def test_class_counter():
    """issued auf der Klasse zählt jedes Ticket"""
    Ticket.issued = 0
    Ticket("play")
    Ticket("play")
    assert Ticket.issued == 2, f"Ticket.issued ist nach zwei Tickets {Ticket.issued!r}, erwartet: 2"


def test_own_seats():
    """Jedes Ticket hat eine eigene Liste von Sitzplätzen"""
    a = Ticket("film")
    b = Ticket("film")
    a.add_seat("A1")
    b.add_seat("B7")
    assert a.seats == ["A1"], f"a.seats ist {a.seats!r}, erwartet: ['A1']"
    assert b.seats == ["B7"], f"b.seats ist {b.seats!r}, erwartet: ['B7']"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Häufige Fehler

self in einer Methode vergessen

class Greeter:
    def hello():
        return "hello"


Greeter().hello()

Was Python ausgibt

TypeError: Greeter.hello() takes 0 positional arguments but 1 was given

Warum, und die Lösung

Ein Methodenaufruf übergibt immer die Instanz als erstes Argument, also braucht jede Methode einen Parameter, der sie aufnimmt. Schreiben Sie def hello(self):. Die Fehlermeldung zählt dieses versteckte Argument mit: Das „1 was given“ ist die Instanz selbst.

Ein Attribut ohne self. verwenden

class Circle:
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * radius ** 2


print(Circle(2).area())

Was Python ausgibt

NameError: name 'radius' is not defined

Warum, und die Lösung

radius war ein Parameter von __init__ und verschwand, als __init__ zurückkehrte. Übrig bleibt das Attribut self.radius. Greifen Sie in einer Methode immer über self auf die Daten des Objekts zu: 3.14159 * self.radius ** 2.

Ein Argument von __init__ weglassen

class Circle:
    def __init__(self, radius):
        self.radius = radius


c = Circle()

Was Python ausgibt

TypeError: Circle.__init__() missing 1 required positional argument: 'radius'

Warum, und die Lösung

Die Argumente von Circle(...) gehen an __init__, nach self. __init__ braucht einen radius, also übergeben Sie einen: Circle(2). Gibt es einen sinnvollen Standardwert, geben Sie dem Parameter stattdessen einen: def __init__(self, radius=1.0):.

Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Eine Klasse erzeugt Objekte; __init__ richtet sie ein

class Account: definiert einen neuen Typ. Ein Aufruf wie Account("ada", 50) erzeugt eine neue Instanz, ruft __init__ mit diesen Argumenten auf ihr auf und gibt die Instanz zurück. __init__ gibt jedem Objekt seinen Anfangszustand: self.owner = owner legt ein Attribut auf genau diesem Objekt an. Attribute brauchen keine Deklaration; wie Variablen existieren sie, sobald sie zugewiesen sind. Jede Instanz hat eigene Attribute, eine Änderung an a.balance lässt b.balance also in Ruhe. __init__ gibt nichts zurück: Es richtet nur das Objekt ein, das der Aufruf der Klasse liefert.

Methoden und self

Eine im Klassenrumpf definierte Funktion ist eine Methode. Beim Aufruf a.deposit(10) findet Python deposit in der Klasse und übergibt die Instanz als erstes Argument; der Aufruf ist also dasselbe wie Account.deposit(a, 10). Dieser erste Parameter heißt per Konvention self; für Python ist der Name nichts Besonderes, aber alle Python-Entwickler erwarten ihn. In einer Methode erreichen Sie Daten und andere Methoden des Objekts über self: self.balance, self.deposit(5). Ein bloßes balance wäre eine lokale Variable, nicht das Attribut.

Klassenvariablen sind geteilt

Ein Name, der im Klassenrumpf außerhalb jeder Methode zugewiesen wird, ist eine Klassenvariable: ein Wert für alle Instanzen, etwa vat_rate = 0.19. Beim Lesen von obj.name sucht Python zuerst auf der Instanz, dann in der Klasse. Die Zuweisung obj.name = value setzt immer ein Instanzattribut, das die Klassenvariable für dieses eine Objekt verdeckt; um den Wert für alle zu ändern, weisen Sie der Klasse zu: Cart.vat_rate = 0.07. Eine veränderbare Klassenvariable wie eine Liste ist eine einzige Liste für alle Instanzen; legen Sie Listen pro Objekt in __init__ an.

Quellen

Zuletzt geprüft am 29. September 2026