{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7672f8a6",
   "metadata": {},
   "source": [
    "# Einstieg in die objektorientierte Programmierung\n",
    "\n",
    "Willkommen! In diesem Arbeitsblatt lernst du Schritt für Schritt, wie Python **Objekte** und **Klassen** verwendet.\n",
    "\n",
    "> **Ziel:** Du kannst am Ende eine einfache Klasse mit Attributen und Methoden erstellen und erklären, was `self` und `__init__` bedeuten.\n",
    "\n",
    "**Zeitbedarf:** ungefähr 75–90 Minuten  \n",
    "**Voraussetzungen:** Variablen, Funktionen, Bedingungen und einfache Datentypen\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "102f655b",
   "metadata": {},
   "source": [
    "## Ziel\n",
    "\n",
    "Nach diesem Arbeitsblatt kannst du:\n",
    "\n",
    "- den Unterschied zwischen einer **Klasse** und einem **Objekt** beschreiben,\n",
    "- mit `__init__` Anfangswerte setzen,\n",
    "- über `self` auf Attribute zugreifen,\n",
    "- Methoden definieren und aufrufen,\n",
    "- mehrere unabhängige Objekte derselben Klasse erzeugen,\n",
    "- mit Vererbung eine spezialisierte Klasse erstellen,\n",
    "- Methoden überschreiben und mit `super()` die Oberklasse aufrufen,\n",
    "- Polymorphie an einem einfachen Beispiel erklären.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b44efc04",
   "metadata": {},
   "source": [
    "## Vorbereitung\n",
    "\n",
    "1. Führe eine Codezelle mit **Shift + Enter** aus.\n",
    "2. Bearbeite die mit `TODO` markierten Stellen.\n",
    "3. Führe danach jeweils die Kontrollzelle aus.\n",
    "4. Fehlermeldungen sind erlaubt – sie gehören zum Programmieren.\n",
    "\n",
    "Dein Arbeitsstand wird in diesem Browser gespeichert. Lade das Notebook am Schluss zusätzlich über **File → Save and Export Notebook As → Notebook** herunter.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c17891e9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.484673Z",
     "iopub.status.busy": "2026-07-19T11:39:22.484548Z",
     "iopub.status.idle": "2026-07-19T11:39:22.488852Z",
     "shell.execute_reply": "2026-07-19T11:39:22.488443Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ Python ist bereit. Los geht's!\n"
     ]
    }
   ],
   "source": [
    "# Diese Zelle prüft, ob Python bereit ist.\n",
    "print(\"✅ Python ist bereit. Los geht's!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8af4611b",
   "metadata": {},
   "source": [
    "## Schritte\n",
    "\n",
    "### 1. Ein Gegenstand als Sammlung von Daten\n",
    "\n",
    "Stell dir eine Spielfigur vor. Ohne OOP könnten wir ihre Daten in einem Dictionary speichern:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f13b302f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.490912Z",
     "iopub.status.busy": "2026-07-19T11:39:22.490741Z",
     "iopub.status.idle": "2026-07-19T11:39:22.493698Z",
     "shell.execute_reply": "2026-07-19T11:39:22.493052Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nova\n",
      "Lebenspunkte: 100\n"
     ]
    }
   ],
   "source": [
    "spielfigur = {\n",
    "    \"name\": \"Nova\",\n",
    "    \"lebenspunkte\": 100,\n",
    "    \"level\": 1,\n",
    "}\n",
    "\n",
    "print(spielfigur[\"name\"])\n",
    "print(\"Lebenspunkte:\", spielfigur[\"lebenspunkte\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ad83d26",
   "metadata": {},
   "source": [
    "Das funktioniert. Bei vielen Figuren und Aktionen wie `heilen()` oder `schaden_nehmen()` wird es aber schnell unübersichtlich.\n",
    "\n",
    "Eine **Klasse** ist der Bauplan. Ein **Objekt** ist ein konkretes Exemplar dieses Bauplans.\n",
    "\n",
    "- Klasse: Bauplan „Spielfigur“\n",
    "- Objekt: die konkrete Figur „Nova“\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf6b7933",
   "metadata": {},
   "source": [
    "### 2. Die erste eigene Klasse\n",
    "\n",
    "Eine leere Klasse ist bereits gültiges Python. `pass` bedeutet: „Hier kommt vorerst noch nichts.“\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c88285b3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.495256Z",
     "iopub.status.busy": "2026-07-19T11:39:22.495072Z",
     "iopub.status.idle": "2026-07-19T11:39:22.498018Z",
     "shell.execute_reply": "2026-07-19T11:39:22.497433Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Spielfigur object at 0x7f5dd92636d0>\n",
      "Sind nova und orion dasselbe Objekt? False\n"
     ]
    }
   ],
   "source": [
    "class Spielfigur:\n",
    "    pass\n",
    "\n",
    "\n",
    "nova = Spielfigur()\n",
    "orion = Spielfigur()\n",
    "\n",
    "print(nova)\n",
    "print(\"Sind nova und orion dasselbe Objekt?\", nova is orion)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "169023a8",
   "metadata": {},
   "source": [
    "`Spielfigur()` erzeugt jeweils ein neues Objekt. Noch besitzen die Objekte aber keine sinnvollen Anfangswerte.\n",
    "\n",
    "### 3. Attribute und `__init__`\n",
    "\n",
    "`__init__` wird beim Erzeugen eines Objekts automatisch aufgerufen. Mit `self` meinen wir jeweils **dieses konkrete Objekt**.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "9005626d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.499300Z",
     "iopub.status.busy": "2026-07-19T11:39:22.499183Z",
     "iopub.status.idle": "2026-07-19T11:39:22.503056Z",
     "shell.execute_reply": "2026-07-19T11:39:22.502345Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nova 100 1\n",
      "Orion 80 1\n"
     ]
    }
   ],
   "source": [
    "class Spielfigur:\n",
    "    def __init__(self, name, lebenspunkte=100):\n",
    "        self.name = name\n",
    "        self.lebenspunkte = lebenspunkte\n",
    "        self.level = 1\n",
    "\n",
    "\n",
    "nova = Spielfigur(\"Nova\")\n",
    "orion = Spielfigur(\"Orion\", 80)\n",
    "\n",
    "print(nova.name, nova.lebenspunkte, nova.level)\n",
    "print(orion.name, orion.lebenspunkte, orion.level)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5963be57",
   "metadata": {},
   "source": [
    "Beachte:\n",
    "\n",
    "- `name` ist ein Parameter.\n",
    "- `self.name` ist ein Attribut, das im Objekt gespeichert bleibt.\n",
    "- `self` wird beim Methodenaufruf automatisch eingesetzt.\n",
    "\n",
    "#### Mini-Aufgabe\n",
    "\n",
    "Erzeuge eine Spielfigur namens `Lyra` mit 120 Lebenspunkten und gib ihre Werte aus.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8e7b4e99",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.504889Z",
     "iopub.status.busy": "2026-07-19T11:39:22.504728Z",
     "iopub.status.idle": "2026-07-19T11:39:22.507152Z",
     "shell.execute_reply": "2026-07-19T11:39:22.506443Z"
    }
   },
   "outputs": [],
   "source": [
    "# TODO: Ersetze None durch dein neues Spielfigur-Objekt.\n",
    "lyra = None\n",
    "\n",
    "# TODO: Gib Name und Lebenspunkte aus."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "aabfba19",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.508852Z",
     "iopub.status.busy": "2026-07-19T11:39:22.508690Z",
     "iopub.status.idle": "2026-07-19T11:39:22.511316Z",
     "shell.execute_reply": "2026-07-19T11:39:22.510860Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "💡 Erwartet wird eine Spielfigur namens Lyra mit 120 Lebenspunkten.\n"
     ]
    }
   ],
   "source": [
    "# Selbstkontrolle – ein unfertiger Versuch führt nicht zum Abbruch.\n",
    "if isinstance(lyra, Spielfigur) and lyra.name == \"Lyra\" and lyra.lebenspunkte == 120:\n",
    "    print(\"✅ Perfekt: Lyra wurde korrekt erzeugt.\")\n",
    "else:\n",
    "    print(\"💡 Erwartet wird eine Spielfigur namens Lyra mit 120 Lebenspunkten.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "867f878f",
   "metadata": {},
   "source": [
    "### 4. Methoden: Objekte können etwas tun\n",
    "\n",
    "Methoden sind Funktionen, die zu einer Klasse gehören. Über `self` lesen und verändern sie den Zustand des konkreten Objekts.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a25ca837",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.513022Z",
     "iopub.status.busy": "2026-07-19T11:39:22.512908Z",
     "iopub.status.idle": "2026-07-19T11:39:22.516559Z",
     "shell.execute_reply": "2026-07-19T11:39:22.515986Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ich bin Nova, Level 1.\n",
      "Nach dem Schaden: 65\n",
      "Nach dem Heilen: 75\n"
     ]
    }
   ],
   "source": [
    "class Spielfigur:\n",
    "    def __init__(self, name, lebenspunkte=100):\n",
    "        self.name = name\n",
    "        self.lebenspunkte = lebenspunkte\n",
    "        self.level = 1\n",
    "\n",
    "    def vorstellen(self):\n",
    "        return f\"Ich bin {self.name}, Level {self.level}.\"\n",
    "\n",
    "    def schaden_nehmen(self, schaden):\n",
    "        self.lebenspunkte = max(0, self.lebenspunkte - schaden)\n",
    "\n",
    "    def heilen(self, punkte):\n",
    "        self.lebenspunkte = min(100, self.lebenspunkte + punkte)\n",
    "\n",
    "\n",
    "nova = Spielfigur(\"Nova\")\n",
    "print(nova.vorstellen())\n",
    "nova.schaden_nehmen(35)\n",
    "print(\"Nach dem Schaden:\", nova.lebenspunkte)\n",
    "nova.heilen(10)\n",
    "print(\"Nach dem Heilen:\", nova.lebenspunkte)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c62b474",
   "metadata": {},
   "source": [
    "#### Denkfrage\n",
    "\n",
    "Warum verändert `nova.schaden_nehmen(35)` nur Nova?\n",
    "\n",
    "<details><summary>Antwort anzeigen</summary>\n",
    "\n",
    "Weil `self` beim Aufruf auf das konkrete Objekt `nova` verweist. Jedes Objekt besitzt eigene Attribute.\n",
    "\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8453c06a",
   "metadata": {},
   "source": [
    "### 5. Hauptaufgabe: Ein virtuelles Haustier\n",
    "\n",
    "Programmiere ein Haustier, dessen Zustand sich durch Spielen und Füttern verändert.\n",
    "\n",
    "Ergänze `Haustier`:\n",
    "\n",
    "1. Speichere `name`, `hunger` und `energie` als Attribute.\n",
    "2. `fuettern(menge)` verringert den Hunger, aber nie unter 0.\n",
    "3. `spielen()` kostet 20 Energie und erhöht den Hunger um 10. Bei zu wenig Energie gibt die Methode `False` zurück.\n",
    "4. `status()` gibt einen gut lesbaren Text mit allen drei Werten zurück.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "53acaa5c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.518464Z",
     "iopub.status.busy": "2026-07-19T11:39:22.518237Z",
     "iopub.status.idle": "2026-07-19T11:39:22.521785Z",
     "shell.execute_reply": "2026-07-19T11:39:22.521105Z"
    }
   },
   "outputs": [],
   "source": [
    "class Haustier:\n",
    "    def __init__(self, name, hunger=40, energie=70):\n",
    "        # TODO: Speichere die drei Anfangswerte im Objekt.\n",
    "        pass\n",
    "\n",
    "    def fuettern(self, menge):\n",
    "        # TODO: Nur positive Mengen verwenden und Hunger höchstens bis 0 senken.\n",
    "        pass\n",
    "\n",
    "    def spielen(self):\n",
    "        # TODO: Nur mit mindestens 20 Energie spielen.\n",
    "        # Bei Erfolg: Energie -20, Hunger +10 und True zurückgeben.\n",
    "        pass\n",
    "\n",
    "    def status(self):\n",
    "        # TODO: Name, Hunger und Energie als Text zurückgeben.\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7a161e2b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.523463Z",
     "iopub.status.busy": "2026-07-19T11:39:22.523275Z",
     "iopub.status.idle": "2026-07-19T11:39:22.525964Z",
     "shell.execute_reply": "2026-07-19T11:39:22.525206Z"
    }
   },
   "outputs": [],
   "source": [
    "# Eigene Versuche – entferne die Kommentarzeichen, wenn du bereit bist.\n",
    "# pixel = Haustier(\"Pixel\")\n",
    "# print(pixel.status())\n",
    "# pixel.spielen()\n",
    "# pixel.fuettern(15)\n",
    "# print(pixel.status())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2e90eb68",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.528025Z",
     "iopub.status.busy": "2026-07-19T11:39:22.527779Z",
     "iopub.status.idle": "2026-07-19T11:39:22.533145Z",
     "shell.execute_reply": "2026-07-19T11:39:22.532415Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "💡 Noch nicht fertig: AttributeError – 'Haustier' object has no attribute 'name'\n"
     ]
    }
   ],
   "source": [
    "def pruefe_haustier():\n",
    "    try:\n",
    "        tier = Haustier(\"Pixel\", hunger=40, energie=70)\n",
    "        start_ok = tier.name == \"Pixel\" and tier.hunger == 40 and tier.energie == 70\n",
    "        fuettern_ok = tier.fuettern(15) is True and tier.hunger == 25\n",
    "        grenze_ok = tier.fuettern(100) is True and tier.hunger == 0\n",
    "        spielen_ok = tier.spielen() is True and tier.energie == 50 and tier.hunger == 10\n",
    "        tier.energie = 10\n",
    "        schutz_ok = tier.spielen() is False and tier.energie == 10\n",
    "        text = tier.status()\n",
    "        text_ok = isinstance(text, str) and \"Pixel\" in text and \"10\" in text\n",
    "        checks = {\n",
    "            \"Startwerte\": start_ok,\n",
    "            \"Füttern\": fuettern_ok,\n",
    "            \"Wertebereich\": grenze_ok,\n",
    "            \"Spielen\": spielen_ok,\n",
    "            \"Energieschutz\": schutz_ok,\n",
    "            \"Status\": text_ok,\n",
    "        }\n",
    "        if all(checks.values()):\n",
    "            print(\"✅ Alle Haustier-Tests bestanden!\")\n",
    "        else:\n",
    "            print(\"💡 Noch nicht alle Anforderungen sind erfüllt:\", checks)\n",
    "    except Exception as fehler:\n",
    "        print(\"💡 Noch nicht fertig:\", type(fehler).__name__, \"–\", fehler)\n",
    "\n",
    "\n",
    "pruefe_haustier()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "71784add",
   "metadata": {},
   "source": [
    "<details><summary>Gestufte Hinweise anzeigen</summary>\n",
    "\n",
    "**Hinweis 1:** Im Konstruktor brauchst du für jeden Parameter eine Zuweisung wie `self.name = name`.\n",
    "\n",
    "**Hinweis 2:** Beim Füttern hilft `self.hunger = max(0, self.hunger - menge)`.\n",
    "\n",
    "**Hinweis 3:** Prüfe beim Spielen zuerst `if self.energie >= 20:`. Vergiss die Rückgabewerte `True` und `False` nicht.\n",
    "\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a96a9a11",
   "metadata": {},
   "source": [
    "### 6. Mehrere Objekte – ein gemeinsamer Bauplan\n",
    "\n",
    "Wenn deine Klasse funktioniert, kannst du mehrere unabhängige Haustiere erzeugen. Änderungen an einem Tier beeinflussen das andere nicht.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e64f92e2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.534950Z",
     "iopub.status.busy": "2026-07-19T11:39:22.534812Z",
     "iopub.status.idle": "2026-07-19T11:39:22.537968Z",
     "shell.execute_reply": "2026-07-19T11:39:22.537339Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    pixel = Haustier(\"Pixel\", hunger=30, energie=80)\n",
    "    luna = Haustier(\"Luna\", hunger=60, energie=40)\n",
    "    pixel.spielen()\n",
    "    luna.fuettern(25)\n",
    "    print(pixel.status())\n",
    "    print(luna.status())\n",
    "except Exception:\n",
    "    print(\"ℹ️ Ergänze zuerst die Haustier-Klasse; danach funktioniert dieses Beispiel.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98110d41",
   "metadata": {},
   "source": [
    "### 7. Vererbung: Gemeinsamkeiten nur einmal programmieren\n",
    "\n",
    "Viele Tierarten haben einen Namen und Energie. Statt diese Gemeinsamkeiten mehrfach zu programmieren, schreiben wir sie in die **Oberklasse** `Tier`.\n",
    "\n",
    "`Katze(Tier)` bedeutet: Eine Katze **ist ein** Tier. Sie erbt dadurch `__init__`, `ausruhen()` und `vorstellen()`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "207f3441",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.539500Z",
     "iopub.status.busy": "2026-07-19T11:39:22.539343Z",
     "iopub.status.idle": "2026-07-19T11:39:22.543498Z",
     "shell.execute_reply": "2026-07-19T11:39:22.542847Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ich heiße Minka und habe 60 Energie.\n",
      "Miau!\n",
      "Nach dem Ausruhen: 80\n",
      "Ist Minka ein Tier? True\n"
     ]
    }
   ],
   "source": [
    "class Tier:\n",
    "    def __init__(self, name, energie=50):\n",
    "        self.name = name\n",
    "        self.energie = energie\n",
    "\n",
    "    def ausruhen(self):\n",
    "        self.energie = min(100, self.energie + 20)\n",
    "\n",
    "    def vorstellen(self):\n",
    "        return f\"Ich heiße {self.name} und habe {self.energie} Energie.\"\n",
    "\n",
    "    def laut_geben(self):\n",
    "        return \"...\"\n",
    "\n",
    "\n",
    "class Katze(Tier):\n",
    "    def laut_geben(self):\n",
    "        return \"Miau!\"\n",
    "\n",
    "\n",
    "minka = Katze(\"Minka\", energie=60)\n",
    "print(minka.vorstellen())       # geerbte Methode\n",
    "print(minka.laut_geben())       # überschriebene Methode\n",
    "minka.ausruhen()                # geerbte Methode\n",
    "print(\"Nach dem Ausruhen:\", minka.energie)\n",
    "print(\"Ist Minka ein Tier?\", isinstance(minka, Tier))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39473924",
   "metadata": {},
   "source": [
    "#### Methoden überschreiben\n",
    "\n",
    "`Katze` besitzt keine eigene Methode `vorstellen()`. Python findet sie deshalb in `Tier`.\n",
    "\n",
    "`laut_geben()` ist dagegen in beiden Klassen vorhanden. Die speziellere Version aus `Katze` **überschreibt** die allgemeine Version aus `Tier`.\n",
    "\n",
    "Das ist nützlich, wenn verschiedene Unterklassen auf denselben Methodenaufruf unterschiedlich reagieren sollen.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afc71433",
   "metadata": {},
   "source": [
    "### 8. `super()`: Die Oberklasse gezielt aufrufen\n",
    "\n",
    "Ein Hund soll zusätzlich seine Lieblingsbeschäftigung speichern. Mit `super().__init__(...)` lassen wir zuerst die Oberklasse `Tier` Name und Energie einrichten. Danach ergänzen wir nur das neue Attribut.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "add2895a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.545231Z",
     "iopub.status.busy": "2026-07-19T11:39:22.545023Z",
     "iopub.status.idle": "2026-07-19T11:39:22.549096Z",
     "shell.execute_reply": "2026-07-19T11:39:22.548266Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ich heiße Rex und habe 70 Energie. Am liebsten spiele ich Frisbee.\n",
      "Wuff!\n"
     ]
    }
   ],
   "source": [
    "class Hund(Tier):\n",
    "    def __init__(self, name, energie=50, lieblingsspiel=\"Ball\"):\n",
    "        super().__init__(name, energie)\n",
    "        self.lieblingsspiel = lieblingsspiel\n",
    "\n",
    "    def laut_geben(self):\n",
    "        return \"Wuff!\"\n",
    "\n",
    "    def vorstellen(self):\n",
    "        grundtext = super().vorstellen()\n",
    "        return grundtext + f\" Am liebsten spiele ich {self.lieblingsspiel}.\"\n",
    "\n",
    "\n",
    "rex = Hund(\"Rex\", energie=70, lieblingsspiel=\"Frisbee\")\n",
    "print(rex.vorstellen())\n",
    "print(rex.laut_geben())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3347fbf",
   "metadata": {},
   "source": [
    "`super()` verhindert, dass wir den Code der Oberklasse kopieren müssen:\n",
    "\n",
    "- `super().__init__(...)` ruft den Konstruktor von `Tier` auf.\n",
    "- `super().vorstellen()` verwendet zuerst die allgemeine Vorstellung und erweitert sie anschließend.\n",
    "\n",
    "#### Aufgabe: Ein Papagei\n",
    "\n",
    "Ergänze `Papagei`:\n",
    "\n",
    "1. Die Klasse erbt von `Tier`.\n",
    "2. Der Konstruktor übernimmt `name` und `energie` mit `super()`.\n",
    "3. Zusätzlich wird das Attribut `wortschatz` gespeichert.\n",
    "4. `laut_geben()` gibt den ersten Eintrag aus `wortschatz` zurück. Bei einer leeren Liste gibt es `\"...\"` zurück.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ebed56f5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.550643Z",
     "iopub.status.busy": "2026-07-19T11:39:22.550432Z",
     "iopub.status.idle": "2026-07-19T11:39:22.552510Z",
     "shell.execute_reply": "2026-07-19T11:39:22.552002Z"
    }
   },
   "outputs": [],
   "source": [
    "# TODO: Vererbung, Konstruktor und laut_geben() ergänzen.\n",
    "class Papagei:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "dd1bc4b7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.553951Z",
     "iopub.status.busy": "2026-07-19T11:39:22.553803Z",
     "iopub.status.idle": "2026-07-19T11:39:22.557738Z",
     "shell.execute_reply": "2026-07-19T11:39:22.557240Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "💡 Noch nicht fertig: TypeError – Papagei() takes no arguments\n"
     ]
    }
   ],
   "source": [
    "def pruefe_papagei():\n",
    "    try:\n",
    "        coco = Papagei(\"Coco\", energie=65, wortschatz=[\"Hallo!\", \"Keks?\"])\n",
    "        erbt = isinstance(coco, Tier)\n",
    "        attribute_ok = coco.name == \"Coco\" and coco.energie == 65\n",
    "        wortschatz_ok = coco.wortschatz == [\"Hallo!\", \"Keks?\"]\n",
    "        laut_ok = coco.laut_geben() == \"Hallo!\"\n",
    "        leer_ok = Papagei(\"Pico\", wortschatz=[]).laut_geben() == \"...\"\n",
    "        geerbte_methode_ok = \"Coco\" in coco.vorstellen()\n",
    "        checks = {\n",
    "            \"Vererbung\": erbt,\n",
    "            \"super()\": attribute_ok,\n",
    "            \"Wortschatz\": wortschatz_ok,\n",
    "            \"Laut\": laut_ok,\n",
    "            \"Leerer Wortschatz\": leer_ok,\n",
    "            \"Geerbte Methode\": geerbte_methode_ok,\n",
    "        }\n",
    "        if all(checks.values()):\n",
    "            print(\"✅ Coco erbt korrekt von Tier!\")\n",
    "        else:\n",
    "            print(\"💡 Prüfe diese Punkte noch einmal:\", checks)\n",
    "    except Exception as fehler:\n",
    "        print(\"💡 Noch nicht fertig:\", type(fehler).__name__, \"–\", fehler)\n",
    "\n",
    "\n",
    "pruefe_papagei()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1689c71",
   "metadata": {},
   "source": [
    "<details><summary>Hinweise zur Papagei-Aufgabe anzeigen</summary>\n",
    "\n",
    "**Hinweis 1:** Der Klassenkopf beginnt mit `class Papagei(Tier):`.\n",
    "\n",
    "**Hinweis 2:** Im Konstruktor kannst du `super().__init__(name, energie)` verwenden.\n",
    "\n",
    "**Hinweis 3:** Mit `if self.wortschatz:` prüfst du, ob die Liste mindestens einen Eintrag enthält.\n",
    "\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2534914b",
   "metadata": {},
   "source": [
    "### 9. Polymorphie: Gleicher Aufruf, unterschiedliches Verhalten\n",
    "\n",
    "Katze, Hund und Papagei sind alles Tiere. Deshalb können wir sie gemeinsam in einer Liste speichern und immer `laut_geben()` aufrufen. Welche Methode tatsächlich läuft, entscheidet das konkrete Objekt.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "00f3b714",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-19T11:39:22.559070Z",
     "iopub.status.busy": "2026-07-19T11:39:22.558958Z",
     "iopub.status.idle": "2026-07-19T11:39:22.561831Z",
     "shell.execute_reply": "2026-07-19T11:39:22.561219Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ℹ️ Ergänze Papagei, damit Coco ebenfalls mitmacht.\n",
      "Minka sagt Miau!\n",
      "Rex sagt Wuff!\n"
     ]
    }
   ],
   "source": [
    "tiere = [\n",
    "    Katze(\"Minka\"),\n",
    "    Hund(\"Rex\"),\n",
    "]\n",
    "\n",
    "try:\n",
    "    tiere.append(Papagei(\"Coco\", wortschatz=[\"Hallo!\"]))\n",
    "except Exception:\n",
    "    print(\"ℹ️ Ergänze Papagei, damit Coco ebenfalls mitmacht.\")\n",
    "\n",
    "for tier in tiere:\n",
    "    print(tier.name, \"sagt\", tier.laut_geben())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5311a006",
   "metadata": {},
   "source": [
    "Bei Polymorphie kann derselbe Programmcode mit verschiedenen Objektarten arbeiten. Entscheidend ist, dass die Objekte die erwartete Methode anbieten.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bc782dc",
   "metadata": {},
   "source": [
    "## Checks\n",
    "\n",
    "Beantworte zunächst ohne nachzuschauen:\n",
    "\n",
    "1. Was ist der Unterschied zwischen Klasse und Objekt?\n",
    "2. Wann wird `__init__` ausgeführt?\n",
    "3. Worauf verweist `self`?\n",
    "4. Warum können zwei Objekte derselben Klasse unterschiedliche Werte besitzen?\n",
    "5. Was bedeutet Vererbung?\n",
    "6. Was ist der Unterschied zwischen einer geerbten und einer überschriebenen Methode?\n",
    "7. Wozu dient `super()`?\n",
    "8. Was bedeutet Polymorphie im Tier-Beispiel?\n",
    "\n",
    "<details><summary>Kurzlösungen anzeigen</summary>\n",
    "\n",
    "1. Klasse = Bauplan, Objekt = konkretes Exemplar.\n",
    "2. Automatisch beim Erzeugen eines Objekts.\n",
    "3. Auf das konkrete Objekt, für das die Methode läuft.\n",
    "4. Die Attribute werden in jedem Objekt separat gespeichert.\n",
    "5. Eine Klasse übernimmt und erweitert Eigenschaften einer anderen Klasse.\n",
    "6. Eine geerbte Methode stammt unverändert aus der Oberklasse; eine überschriebene Methode wird in der Unterklasse neu definiert.\n",
    "7. Mit `super()` rufen wir gezielt Methoden der Oberklasse auf und vermeiden kopierten Code.\n",
    "8. Derselbe Aufruf `laut_geben()` führt je nach konkretem Tier zu einem anderen Verhalten.\n",
    "\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f69351a1",
   "metadata": {},
   "source": [
    "## Nächste Schritte\n",
    "\n",
    "Passende Fortsetzungen sind Properties, `__str__`, die Zusammenarbeit mehrerer Klassen und ein kleines Projekt wie eine Haustier-Simulation oder ein Rollenspiel.\n",
    "\n",
    "### Abschlussreflexion\n",
    "\n",
    "Füge eine Markdown-Zelle hinzu und notiere:\n",
    "\n",
    "- Was ist dir heute klar geworden?\n",
    "- Wo bist du noch unsicher?\n",
    "- Wo erkennst du im Beispiel den Vorteil der Vererbung?\n",
    "- Welche weitere Tier-Unterklasse würdest du gerne programmieren?\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (Pyodide)",
   "language": "python",
   "name": "python"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
