From a3a6362c777103590cf99fe4cef1acf51efdcde0 Mon Sep 17 00:00:00 2001 From: Alex Mueller Date: Sat, 29 Aug 2026 14:42:12 +0200 Subject: [PATCH] Implementiere Lehrpfad 1 Grundlagen (G01 bis G10) und erstelle 3-Lehrpfad-System im Lernportal --- LEHRPLAN.md | 120 ++++--- README.md | 82 +++-- g01_erste_schritte_taschenrechner/README.md | 92 +++++ g01_erste_schritte_taschenrechner/aufgabe.py | 144 ++++++++ g01_erste_schritte_taschenrechner/index.html | 305 ++++++++++++++++ .../musterloesung.py | 53 +++ .../test_aufgabe.py | 127 +++++++ g02_variablen_und_datentypen/README.md | 124 +++++++ g02_variablen_und_datentypen/aufgabe.py | 121 +++++++ g02_variablen_und_datentypen/index.html | 256 ++++++++++++++ g02_variablen_und_datentypen/musterloesung.py | 49 +++ g02_variablen_und_datentypen/test_aufgabe.py | 113 ++++++ g03_ein_und_ausgabe/README.md | 122 +++++++ g03_ein_und_ausgabe/aufgabe.py | 151 ++++++++ g03_ein_und_ausgabe/index.html | 259 ++++++++++++++ g03_ein_und_ausgabe/musterloesung.py | 58 +++ g03_ein_und_ausgabe/test_aufgabe.py | 145 ++++++++ g04_verzweigungen_und_bedingungen/README.md | 129 +++++++ g04_verzweigungen_und_bedingungen/aufgabe.py | 167 +++++++++ g04_verzweigungen_und_bedingungen/index.html | 291 +++++++++++++++ .../musterloesung.py | 97 +++++ .../test_aufgabe.py | 160 +++++++++ g05_schleifen_und_wiederholungen/README.md | 196 +++++++++++ g05_schleifen_und_wiederholungen/aufgabe.py | 167 +++++++++ g05_schleifen_und_wiederholungen/index.html | 332 ++++++++++++++++++ .../musterloesung.py | 77 ++++ .../test_aufgabe.py | 178 ++++++++++ g06_funktionen_und_module/README.md | 169 +++++++++ g06_funktionen_und_module/aufgabe.py | 145 ++++++++ g06_funktionen_und_module/index.html | 285 +++++++++++++++ g06_funktionen_und_module/musterloesung.py | 51 +++ g06_funktionen_und_module/test_aufgabe.py | 167 +++++++++ g07_listen_und_sequenzen/README.md | 136 +++++++ g07_listen_und_sequenzen/aufgabe.py | 131 +++++++ g07_listen_und_sequenzen/index.html | 276 +++++++++++++++ g07_listen_und_sequenzen/musterloesung.py | 57 +++ g07_listen_und_sequenzen/test_aufgabe.py | 123 +++++++ g08_textverarbeitung_und_strings/README.md | 127 +++++++ g08_textverarbeitung_und_strings/aufgabe.py | 140 ++++++++ g08_textverarbeitung_und_strings/index.html | 231 ++++++++++++ .../musterloesung.py | 42 +++ .../test_aufgabe.py | 97 +++++ g09_dictionaries_und_sets/README.md | 169 +++++++++ g09_dictionaries_und_sets/aufgabe.py | 137 ++++++++ g09_dictionaries_und_sets/index.html | 305 ++++++++++++++++ g09_dictionaries_und_sets/musterloesung.py | 75 ++++ g09_dictionaries_und_sets/test_aufgabe.py | 142 ++++++++ .../README.md | 142 ++++++++ .../aufgabe.py | 148 ++++++++ .../index.html | 255 ++++++++++++++ .../musterloesung.py | 72 ++++ .../test_aufgabe.py | 143 ++++++++ index.html | 279 ++++++++++++--- test_all.py | 10 + 54 files changed, 8022 insertions(+), 147 deletions(-) create mode 100644 g01_erste_schritte_taschenrechner/README.md create mode 100644 g01_erste_schritte_taschenrechner/aufgabe.py create mode 100644 g01_erste_schritte_taschenrechner/index.html create mode 100644 g01_erste_schritte_taschenrechner/musterloesung.py create mode 100644 g01_erste_schritte_taschenrechner/test_aufgabe.py create mode 100644 g02_variablen_und_datentypen/README.md create mode 100644 g02_variablen_und_datentypen/aufgabe.py create mode 100644 g02_variablen_und_datentypen/index.html create mode 100644 g02_variablen_und_datentypen/musterloesung.py create mode 100644 g02_variablen_und_datentypen/test_aufgabe.py create mode 100644 g03_ein_und_ausgabe/README.md create mode 100644 g03_ein_und_ausgabe/aufgabe.py create mode 100644 g03_ein_und_ausgabe/index.html create mode 100644 g03_ein_und_ausgabe/musterloesung.py create mode 100644 g03_ein_und_ausgabe/test_aufgabe.py create mode 100644 g04_verzweigungen_und_bedingungen/README.md create mode 100644 g04_verzweigungen_und_bedingungen/aufgabe.py create mode 100644 g04_verzweigungen_und_bedingungen/index.html create mode 100644 g04_verzweigungen_und_bedingungen/musterloesung.py create mode 100644 g04_verzweigungen_und_bedingungen/test_aufgabe.py create mode 100644 g05_schleifen_und_wiederholungen/README.md create mode 100644 g05_schleifen_und_wiederholungen/aufgabe.py create mode 100644 g05_schleifen_und_wiederholungen/index.html create mode 100644 g05_schleifen_und_wiederholungen/musterloesung.py create mode 100644 g05_schleifen_und_wiederholungen/test_aufgabe.py create mode 100644 g06_funktionen_und_module/README.md create mode 100644 g06_funktionen_und_module/aufgabe.py create mode 100644 g06_funktionen_und_module/index.html create mode 100644 g06_funktionen_und_module/musterloesung.py create mode 100644 g06_funktionen_und_module/test_aufgabe.py create mode 100644 g07_listen_und_sequenzen/README.md create mode 100644 g07_listen_und_sequenzen/aufgabe.py create mode 100644 g07_listen_und_sequenzen/index.html create mode 100644 g07_listen_und_sequenzen/musterloesung.py create mode 100644 g07_listen_und_sequenzen/test_aufgabe.py create mode 100644 g08_textverarbeitung_und_strings/README.md create mode 100644 g08_textverarbeitung_und_strings/aufgabe.py create mode 100644 g08_textverarbeitung_und_strings/index.html create mode 100644 g08_textverarbeitung_und_strings/musterloesung.py create mode 100644 g08_textverarbeitung_und_strings/test_aufgabe.py create mode 100644 g09_dictionaries_und_sets/README.md create mode 100644 g09_dictionaries_und_sets/aufgabe.py create mode 100644 g09_dictionaries_und_sets/index.html create mode 100644 g09_dictionaries_und_sets/musterloesung.py create mode 100644 g09_dictionaries_und_sets/test_aufgabe.py create mode 100644 g10_comprehensions_datum_algorithmen/README.md create mode 100644 g10_comprehensions_datum_algorithmen/aufgabe.py create mode 100644 g10_comprehensions_datum_algorithmen/index.html create mode 100644 g10_comprehensions_datum_algorithmen/musterloesung.py create mode 100644 g10_comprehensions_datum_algorithmen/test_aufgabe.py diff --git a/LEHRPLAN.md b/LEHRPLAN.md index 09d71a9..21f7f7a 100644 --- a/LEHRPLAN.md +++ b/LEHRPLAN.md @@ -1,73 +1,77 @@ -# 📚 Didaktischer Lehrplan & Abgleich mit dem Schulskriptum +# 📚 Didaktischer Lehrplan: Das 3-Säulen-System Dieses Dokument dient als didaktischer Leitfaden für die Python-Nachhilfe. -Es gleicht die Übungsreihe mit dem offiziellen Lehrplan und Skriptum des Lehrers (**`rradlbauer/python_skriptum`**) ab. +Es gleicht die gesamte Übungsreihe (Grundlagen & Fortgeschrittene Objektorientierung) mit dem offiziellen Lehrplan und Skriptum des Lehrers (**`rradlbauer/python_skriptum`**) ab. --- -## 🎯 Didaktische Leitlinien für die Nachhilfe - -1. **Type Hints als Standard:** Da der Lehrer in Teil 2 strikt Typ-Annotationen verwendet (`name: str`, `def alter(self) -> int:`), führen wir diese ab den Klassen sauber ein. -2. **UML-Klassendiagramme:** Das Skriptum fordert UML-Diagramme (`22.1_UML.md`). Jedes Kapitel enthält visuelle UML-Karten. -3. **Echte Entwickler-Werkzeuge früh erlernen:** Nach den ersten OOP-Kapiteln folgen **VS Code (04b)** und **Git Versionskontrolle (04c)**, damit die Schülerin ab Kapitel 05 jedes gelöste Kapitel selbstständig versioniert und committet. -4. **Eigene Unit Tests schreiben:** Ab Kapitel 09 lernt die Schülerin, wie man mit `unittest` eigene Testsuiten und Test-Driven Development (TDD) praktiziert. -5. **Praxisnahe Analogien:** Statt trockener Theorie nutzen wir greifbare Alltagsbeispiele (Fahrrad-Verleih, Spotify-Playlists, Stammbäume, Geldautomaten). -6. **Automatische Tests & Sofort-Feedback:** Jedes Kapitel hat ein Testskript (`test_aufgabe.py`), das bei `python3 test_aufgabe.py` oder `python3 test_all.py` sofort anzeigt, was schon klappt. -7. **Autonomes Lernen:** Die Schülerin kann alle Kapitel über die modernen, interaktiven HTML-Seiten im Browser selbstständig durcharbeiten. - ---- - -## 🗺️ Der Gesamtlehrplan im Überblick (Kapitel 00 bis 16) +## 🧭 Die 3 Lehrpfade im Überblick ``` -PHASE 1: GRUNDLAGEN DER OBJEKTORIENTIERUNG (OOP) & TOOLING - ├── Kapitel 00: Fehlersuche & Python-Debugging (Warm-up) - ├── Kapitel 01: Erste Klasse & Baupläne (Lehrer 22.0 / 21.0 Type Hints) - ├── Kapitel 02: Konstruktor __init__, self & UML (Lehrer 22.0 / 22.1) - ├── Kapitel 03: Methoden & Objekt-Interaktion (Lehrer 22.0) - ├── Kapitel 04: Schöne Textausgabe mit __str__ (Lehrer 28.0) - ├── Kapitel 04b: Umstieg & Installation von VS Code (Lehrer 20.1) - ├── Kapitel 04c: Git & Versionskontrolle (VS Code & Terminal) 🌿 - ├── Kapitel 05: Assoziation & Aggregation: Spotify Playlists (Lehrer 23.0) - └── Kapitel 06: Mini-Abschlussprojekt: Tamagotchi Terminal-Game +[ 🛣️ LEHRPFAD 1: GRUNDLAGEN DER PROGRAMMIERUNG (Prozedural & Datenstrukturen) ] + ├── G01: Python als Taschenrechner (Zahlen, Operatoren, Rangfolge) (Lehrer 02.1) + ├── G02: Variablen & Datentypen (int, float, str, bool, Type Casting) (Lehrer 03.0 & 03.1) + ├── G03: Interaktive Ein- & Ausgabe (input, print, formatierte f-Strings) (Lehrer 07.0) + ├── G04: Bedingungen & Verzweigungen (if, elif, else, and/or/not) (Lehrer 08.0) + ├── G05: Schleifen & Wiederholungen (for range, while, break, continue) (Lehrer 09.1 & 09.2) + ├── G06: Eigene Funktionen & Module (def, return, scope, math, random) (Lehrer 05.0 & 05.1) + ├── G07: Listen & Sequenzen (Indexing, Slicing, append/pop, sum/len/min) (Lehrer 09.0) + ├── G08: Textverarbeitung & Strings (.strip, .replace, .split, .join) (Lehrer 11.0) + ├── G09: Dictionaries & Sets ({key: value}, .get(), Mengen ohne Duplikate) (Lehrer 15.0) + └── G10: Comprehensions, Datum & Algorithmen ([x for x in ...], datetime) (Lehrer 13.0 & 15.1) -PHASE 2: FORTGESCHRITTENES OOP, TESTING & SPEICHER - ├── Kapitel 07: Referenzen, Speicher & Stammbäume (Lehrer 24.0) 🧠 - ├── Kapitel 08: Operator Overloading & Dunder-Methoden (Lehrer 28.0) ➕ - ├── Kapitel 09: Eigene Unit Tests schreiben & TDD (unittest) 🧪 - ├── Kapitel 10: Vererbung (Inheritance) & super() (Lehrer 25.0) 🧬 - ├── Kapitel 11: Polymorphie & Duck Typing (Lehrer 25.1) 🎭 - ├── Kapitel 12: Exceptions & Robuste Fehlerbehandlung (Lehrer 26.0) 🛡️ - └── Kapitel 13: Speichern & Laden: JSON & CSV Persistenz (Lehrer 14.0) 💾 +[ 🛣️ LEHRPFAD 2: EINSTIEG IN DIE OBJEKTORIENTIERUNG (OOP) & WERKZEUGE ] + ├── 00: Fehlersuche & Python-Debugging (Warm-up & Tracebacks) (Lehrer 04.1 & 07.1) + ├── 01: Erste Klasse & Baupläne (Instanziierung, Punktnotation) (Lehrer 22.0 & 21.0) + ├── 02: Konstruktor __init__, self & UML-Klassendiagramme (Lehrer 22.0 & 22.1) + ├── 03: Methoden & Objekt-Interaktionen (Rennauto & Duell) (Lehrer 22.0) + ├── 04: Schöne Textausgabe mit __str__ (Warenkorb & Bon) (Lehrer 28.0) + ├── 04b: Exkurs: Umstieg & Installation von VS Code (Lehrer 20.1) + ├── 04c: Exkurs: Git & Versionskontrolle (VS Code & Terminal) (Tooling) + ├── 05: Assoziation & Komposition (Spotify Playlist-Manager) (Lehrer 23.0) + └── 06: Mini-Abschlussprojekt: Tamagotchi Terminal-Game (Konsolidierung) -PHASE 3: GRAFISCHE OBERFLÄCHEN & PROFESSIONS-MODULE - ├── Kapitel 14: Grafische Oberflächen mit Tkinter / CustomTkinter (Lehrer 27.0) 🖥️ - ├── Kapitel 15: Erweiterte Parameter (*args, **kwargs) & Container (Lehrer 29/30) 📦 - └── Kapitel 16: Master-Abschlussprojekt: Grafische Desktop-App mit Savegame 🏆 +[ 🛣️ LEHRPFAD 3: FORTGESCHRITTENES OOP, SOFTWAREQUALITÄT & GUIS ] + ├── 07: Referenzen, RAM-Speicher & Familienstammbäume (Lehrer 24.0) + ├── 08: Operator Overloading & Dunder-Methoden (__add__, __eq__, __lt__) (Lehrer 28.0) + ├── 09: Eigene Unit Tests schreiben & TDD (unittest.TestCase) (Testing & TDD) + ├── 10: Vererbung (Inheritance) & super() (Fahrzeugflotte) (Lehrer 25.0) + ├── 11: Polymorphie & Interfaces (ABC, Geometrie & Zeichenfläche) (Lehrer 25.1) + ├── 12: Exceptions & Robuste Fehlerbehandlung (try-except, Bankkonto) (Lehrer 26.0) + ├── 13: Datei-Persistenz: JSON & CSV Speichern (Savegames) (Lehrer 14.0) + └── 14: Desktop-GUIs mit Tkinter & MVC-Architektur (Zähler & Rechner) (Lehrer 27.0) ``` --- -## 📑 Detaillierte Kapitel-Übersicht & Abgleich +## 📑 Detaillierte Themen-Abgleichstabelle -| Kapitel | Titel / Thema | Lehrerskript-Referenz | Kerninhalte & Praxisaufgabe | +| Modul | Titel | Thema | Praxisbeispiel | | :--- | :--- | :--- | :--- | -| **00** | **Fehlersuche & Debugging** | *04.1 & 07.1* | Tracebacks von unten lesen, `IndentationError`, `SyntaxError`, `TypeError`, Detektiv-Aufgabe | -| **01** | **Einstieg in Klassen** | *22.0 & 21.0* | Bauplan vs. Objekt, Instanziierung, Punktnotation, erste Type Hints (`Fahrrad`) | -| **02** | **Konstruktor & self** | *22.0 & 22.1* | `__init__`, `self`, Startwerte, erstes UML-Klassendiagramm (`Bankkonto` & `Smartphone`) | -| **03** | **Methoden & Verhalten** | *22.0* | Methoden definieren, Rückgabewerte, Interaktion zweier Objekte (`Rennauto` & RPG-Duell) | -| **04** | **`__str__` & Darstellung** | *28.0* | Dunder-Methode `__str__`, lesbare Ausgabe von Objekten, Kassenzettel (`Warenkorb`) | -| **04b** | **VS Code Umstieg** | *20.1* | Installation (Windows/Mac/Linux), Plugins (Error Lens, Python), Shortcuts, Projektordner | -| **04c** | **Git Versionskontrolle** | *Tooling* | Was ist Git? Spielstand-Analogie, `git add`, `git commit`, `git push`, VS Code Git-Tab | -| **05** | **Objekte kombinieren** | *23.0* | Assoziation & Aggregation (1:1 und 1:n), Listen von Objekten filtern (`Spotify Playlist`) | -| **06** | **Abschlussprojekt OOP 1** | *Konsolidierung* | Tamagotchi-Konsolenspiel (Lebenszyklus, Hunger, Müdigkeit, Zufallsevents) | -| **07** | **Referenzen & Speicher** | *24.0 Referenzen* | RAM-Visualisierung, Mutable vs. Immutable, `is` vs. `==`, Aliasing, `Stammbaum`-Projekt | -| **08** | **Operator Overloading** | *28.0 Spezielle Methoden*| `__add__`, `__sub__`, `__eq__`, `__lt__`, `__len__` (2D-Vektoren & Kartenspiel) | -| **09** | **Eigene Unit Tests & TDD**| *Softwarequalität* | Testsuiten schreiben mit `unittest`, `assertEqual`, `assertTrue`, `assertRaises` | -| **10** | **Vererbung & `super()`** | *25.0 Vererbung* | Basisklassen, Kindklassen, Vermeidung von Code-Duplikaten, `super().__init__()` (`Fahrzeuge`) | -| **11** | **Polymorphie & Interfaces**| *25.1 Polymorphie* | Methoden überschreiben, gemeinsames Interface, Geometrie- & Grafik-Engine (`Formen`) | -| **12** | **Exceptions & Fehler** | *26.0 Exceptions* | `try/except/else/finally`, `raise`, eigene Exception-Klassen (`Geldautomat`) | -| **13** | **JSON & CSV Speichern** | *14.0 CSV / Persistenz* | `with open()`, `json.dump`/`json.load`, Spielstände dauerhaft sichern (`Savegame`) | -| **14** | **Tkinter Desktop-GUI** | *27.0 Tkinter* | Fenster, Buttons, Labels, Eingabefelder, Event-Handling in OOP-Klassen | -| **15** | **Parameter & Container** | *29.0 & 30.0 Parameter*| `*args`, `**kwargs`, Dunder `__getitem__`, `__iter__` für eigene Listen-Container | -| **16** | **Master-Abschlussprojekt**| *Meisterstück* | Vollständige Desktop-App mit grafischer Oberfläche, Speichern & Laden | +| **G01** | **Taschenrechner** | Grundrechenarten, `//`, `%`, `**`, Rangfolge | Rechner-Funktionen & Kreisflächen | +| **G02** | **Variablen & Typen** | Basisdatentypen, Type Casting, Type Hints | Typen-Erkenner & Preis-Formatierer | +| **G03** | **Ein- & Ausgabe** | `input()`, f-Strings mit Formatierungs-Codes | Steckbrief & Rechnungsposten | +| **G04** | **Verzweigungen** | `if`, `elif`, `else`, `and`, `or`, `not` | Kinokarten, Notenspiegel & Schaltjahr | +| **G05** | **Schleifen** | `for in range()`, `while`, `break`, `continue` | Fakultät, Primzahlen & Quersummen | +| **G06** | **Funktionen** | `def`, `return`, Scope, `math`, `random` | Hypotenuse, Zylindervolumen & Würfel | +| **G07** | **Listen & Slicing**| Indexing, Slicing `[1:4]`, Methoden wie `append`/`pop` | Notenschnitt ohne Ausreißer | +| **G08** | **Textverarbeitung**| String-Methoden (`.strip`, `.split`, `.replace`) | Palindrom-Prüfer & Kreditkarten-Maske | +| **G09** | **Dicts & Sets** | Key-Value Paare, `.get()`, Mengen ohne Duplikate | Häufigkeitszähler & Hobbys-Abgleich | +| **G10** | **Comprehensions** | `[x for x in ...]`, `datetime`, Sortier-Algorithmen | Countdown, Alters-Suche | +| **00** | **Fehlersuche** | Tracebacks von unten lesen, `IndentationError` | Code-Detektivin | +| **01** | **Erste Klasse** | Klassen als Baupläne, Punktnotation, Type Hints | Fahrrad-Verleih | +| **02** | **Konstruktor** | `__init__`, `self`, Startwerte, UML-Diagramme | Bankkonto & Smartphone | +| **03** | **Methoden** | Verhalten & Interaktion zweier Objekte | Rennauto-Duell | +| **04** | **`__str__`** | Lesbare Darstellung mit `print()`, Rechnungsdruck | Pizzeria-Warenkorb | +| **04b** | **VS Code** | Installation, Extensions (Error Lens, Python), Shortcuts | IDE-Praxis | +| **04c** | **Git** | Spielstand-Analogie, Commits, VS Code Git-Tab, Push | Versionskontrolle | +| **05** | **Komposition** | 1:1 und 1:n Beziehungen zwischen Objekten | Spotify Playlist-Manager | +| **06** | **Mini-Projekt**| Konsolidierung von OOP Teil 1 | Tamagotchi Konsolenspiel | +| **07** | **Referenzen** | RAM-Zeiger, Aliasing (`b = a`), `is` vs. `==` | Familienstammbaum (`Person`) | +| **08** | **Dunder** | Operator Overloading `__add__`, `__eq__`, `__len__` | 2D-Vektor & Wegstrecken | +| **09** | **Unit Testing**| Eigene Testsuiten schreiben, `assertEqual`, TDD | Taschenrechner & Bankkonto Tests | +| **10** | **Vererbung** | Basis- und Kindklassen, `super().__init__()`, DRY | Fahrzeugflotte (`ElektroAuto`, `Lkw`) | +| **11** | **Polymorphie** | Duck Typing, abstrakte Klassen (`ABC`), Overriding | Geometrie-Engine & Zeichenfläche | +| **12** | **Exceptions** | `try-except-else-finally`, `raise`, eigene Fehler | Bankkonto- & Geldautomat-Simulator | +| **13** | **Persistenz** | `with open()`, `json.dump`/`load`, CSV-Export | Savegames & Notenbuch | +| **14** | **Tkinter GUI** | Echte Desktop-Apps, Widgets, `grid()`, MVC-Muster | Interaktive Zähler- & Rechner-App | diff --git a/README.md b/README.md index 260942d..69855b4 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,55 @@ -# 🐍 Python OOP Übungsreihe: Der Einstieg in Klassen & Objekte +# 🐍 Python Lernportal: Vom Einsteiger zum Software-Entwickler -Willkommen zu deiner Übungsreihe für **Objektorientierte Programmierung (OOP)** in Python! 🎉 +Willkommen zum interaktiven **Python Lernportal**! 🎉 -Diese Übungsreihe ist didaktisch aufbereitet und **1:1 mit dem offiziellen Lehrplan des Informatik-Unterrichts** ([`LEHRPLAN.md`](./LEHRPLAN.md)) abgeglichen. +Dieses Portal ist didaktisch aufbereitet, voll **abgeglichen mit dem Informatik-Schulunterricht** ([`LEHRPLAN.md`](./LEHRPLAN.md)) und gliedert sich in **3 strukturierte Lehrpfade**: --- -## 🗺️ Der Lernpfad +## 🧭 Die 3 Lehrpfade -### 🌱 Phase 1: Grundlagen OOP & Werkzeuge -| Kapitel | Thema | Was du lernst | +### 🛣️ Lehrpfad 1: Grundlagen der Programmierung (Prozedural & Datenstrukturen) +| Modul | Titel | Themenschwerpunkte | | :--- | :--- | :--- | -| **[00. Fehlersuche & Grundlagen](./00_fehlersuche_und_grundlagen/)** | Warm-up & Debugging | Tracebacks lesen, Einrückungen (`IndentationError`), Typfehler & Detektiv-Aufgabe | -| **[01. Einstieg in Klassen](./01_einstieg_klassen/)** | Warum Klassen? | Bauplan vs. Objekt, Instanziierung, Punktnotation & Type Hints | -| **[02. Konstruktor & self](./02_init_und_self/)** | `__init__`, `self` & UML | Objekte sauber initialisieren und erste UML-Klassendiagramme | -| **[03. Methoden & Verhalten](./03_methoden_und_verhalten/)** | Methoden & Interaktion | Objekte können handeln & miteinander im Duell interagieren | -| **[04. Schöne Textausgabe](./04_str_und_darstellung/)** | `__str__` Dunder-Methode | Wie Objekte lesbar mit `print()` ausgegeben werden (Warenkorb & Bon) | -| **[04b. Umstieg & Installation VS Code](./04b_umstieg_vscode/)** | **Exkurs: IDE-Wechsel** | **Installation (Win/Mac/Linux), Plugins (Error Lens, Python), Shortcuts & Terminal** | -| **[04c. Git & Versionskontrolle](./04c_git_und_versionskontrolle/)** | **Exkurs: Git-Tooling** | **Spielstände (Commits) sichern, VS Code Git-Tab, Terminal-Befehle & Remote Push** | -| **[05. Objekte kombinieren](./05_objekte_kombinieren/)** | Assoziation & Komposition | Listen von Objekten & Spotify-Playlists filtern und berechnen | -| **[06. Abschlussprojekt OOP 1](./06_abschlussprojekt_tamagotchi/)** | Mini-Projekt: Tamagotchi | Ein interaktives Konsolenspiel mit allem Gelernten | +| **[G01. Taschenrechner](./g01_erste_schritte_taschenrechner/)** | **Python als Rechner** | Zahlenarten (`int`, `float`), Grundrechenarten, `//`, `%`, `**`, Rangfolge & `print()` | +| **[G02. Variablen & Typen](./g02_variablen_und_datentypen/)** | **Datentypen** | Speicherboxen, `int`, `float`, `str`, `bool`, Type Casting & Type Hints | +| **[G03. Ein- & Ausgabe](./g03_ein_und_ausgabe/)** | **Interaktion** | Tastatureingaben mit `input()`, f-Strings mit Formatierungs-Codes (`{preis:.2f} €`) | +| **[G04. Bedingungen](./g04_verzweigungen_und_bedingungen/)** | **Entscheidungen** | `if`, `elif`, `else`, Vergleichsoperatoren & Logik (`and`, `or`, `not`) | +| **[G05. Schleifen](./g05_schleifen_und_wiederholungen/)** | **Wiederholungen** | Zählschleifen mit `for in range()`, Bedingungsschleifen mit `while`, `break`/`continue` | +| **[G06. Funktionen](./g06_funktionen_und_module/)** | **Modularität** | `def`, `return`, Parameter, Scope & Module (`math`, `random`) | +| **[G07. Listen & Sequenzen](./g07_listen_und_sequenzen/)** | **Datenstrukturen 1** | Listen erstellen, Indexing, Slicing `[1:4]`, Methoden (`append`, `pop`) & `sum/min/max` | +| **[G08. Textverarbeitung](./g08_textverarbeitung_und_strings/)** | **String-Methoden** | Unveränderlichkeit, `.strip()`, `.replace()`, `.split()`, `", ".join()` & Textanalyse | +| **[G09. Dictionaries & Sets](./g09_dictionaries_und_sets/)** | **Datenstrukturen 2** | Key-Value Paare (`{key: val}`), sicheres `.get()`, Mengen (`set`) ohne Duplikate | +| **[G10. Comprehensions & Datum](./g10_comprehensions_datum_algorithmen/)** | **Vertiefung** | List Comprehensions `[x for x in ...]`, `datetime` & Such-/Sortier-Algorithmen | -### 🧠 Phase 2: Fortgeschrittenes OOP, Testing & Persistenz -| Kapitel | Thema | Was du lernst | +--- + +### 🛣️ Lehrpfad 2: Einstieg in die Objektorientierung (OOP) & Entwickler-Tools +| Modul | Titel | Themenschwerpunkte | | :--- | :--- | :--- | -| **[07. Referenzen, Speicher & Stammbäume](./07_referenzen_und_speicher/)** | **Speicher & RAM** | **Referenzen, Aliasing (`b = a`), Mutable vs. Immutable, `is` vs. `==` & Familienstammbaum** | -| **[08. Operator Overloading](./08_operator_overloading_dunder/)** | **Dunder-Methoden** | **`__add__`, `__mul__`, `__eq__`, `__lt__` (2D-Vektoren & Wegstrecken)** | -| **[09. Eigene Unit Tests & TDD](./09_eigene_unit_tests_schreiben/)** | **Softwarequalität** | **Testsuiten schreiben mit `unittest`, `assertEqual`, `assertTrue`, `assertRaises`** | -| **[10. Vererbung & `super()`](./10_vererbung_und_super/)** | **Inheritance** | **Basisklassen, Kindklassen, DRY-Prinzip & `super().__init__()` (Fahrzeugflotte)** | -| **[11. Polymorphie & Interfaces](./11_polymorphie_und_interfaces/)** | **Polymorphism** | **Vielgestaltigkeit, Duck Typing, abstrakte Basisklassen (`ABC`) & Geometrie-Engine** | -| **[12. Exceptions & Fehler](./12_exceptions_und_fehlerbehandlung/)** | **Robustes Coding** | **`try/except/else/finally`, `raise`, eigene Exception-Klassen (Bankkonto)** | -| **[13. JSON & CSV Speichern](./13_persistenz_json_und_csv/)** | **Persistenz** | **Spielstände dauerhaft mit JSON/CSV auf der Festplatte sichern** | -| **[14. Desktop-GUIs mit Tkinter](./14_gui_mit_tkinter/)** | **Grafische Oberflächen** | **Fenster, Buttons, Labels, Grid-Layout & MVC-Architektur** | +| **[00. Fehlersuche](./00_fehlersuche_und_grundlagen/)** | **Warm-up & Debugging** | Tracebacks von unten lesen, `IndentationError`, `SyntaxError`, `TypeError` | +| **[01. Einstieg in Klassen](./01_einstieg_klassen/)** | **Bauplan vs. Objekt** | Klassen definieren, Instanziierung, Punktnotation & Type Hints (`Fahrrad`) | +| **[02. Konstruktor & self](./02_init_und_self/)** | **Initialisierung & UML** | `__init__`, `self`, Startwerte & UML-Klassendiagramme (`Bankkonto`, `Smartphone`) | +| **[03. Methoden & Verhalten](./03_methoden_und_verhalten/)** | **Objekt-Interaktion** | Methoden definieren, Rückgabewerte & Duell zweier Objekte (`Rennauto`) | +| **[04. Schöne Textausgabe](./04_str_und_darstellung/)** | **`__str__` Dunder** | Lesbare `print()`-Ausgabe von Objekten, Kassenzettel (`Warenkorb`) | +| **[04b. VS Code Installation](./04b_umstieg_vscode/)** | **IDE-Setup** | Installation (Win/Mac/Linux), Plugins (Error Lens, Python), Shortcuts | +| **[04c. Git & Versionskontrolle](./04c_git_und_versionskontrolle/)** | **Git-Tooling** | Spielstände (Commits) sichern, VS Code Git-Tab, Terminal & Remote Push | +| **[05. Objekte kombinieren](./05_objekte_kombinieren/)** | **Komposition** | 1:1 und 1:n Beziehungen zwischen Objekten (`Spotify Playlist-Manager`) | +| **[06. Mini-Projekt OOP 1](./06_abschlussprojekt_tamagotchi/)** | **Tamagotchi Game** | Voll spielbares, interaktives Haustierspiel im Terminal | -*(Den vollständigen didaktischen Masterplan für alle Kapitel bis 16 findest du in [`LEHRPLAN.md`](./LEHRPLAN.md).)* +--- + +### 🛣️ Lehrpfad 3: Fortgeschrittenes OOP, Softwarequalität & Desktop-GUIs +| Modul | Titel | Themenschwerpunkte | +| :--- | :--- | :--- | +| **[07. Referenzen & Speicher](./07_referenzen_und_speicher/)** | **RAM & Zeiger** | Referenzen im RAM, Aliasing (`b = a`), `is` vs. `==` & Familienstammbaum (`Person`) | +| **[08. Operator Overloading](./08_operator_overloading_dunder/)** | **Dunder-Methoden** | Eigene Klassen rechenbar machen mit `__add__`, `__mul__`, `__eq__` (2D-Vektoren) | +| **[09. Eigene Unit Tests & TDD](./09_eigene_unit_tests_schreiben/)** | **Softwarequalität** | Testsuiten schreiben mit `unittest`, `assertEqual`, `assertRaises` & TDD | +| **[10. Vererbung & `super()`](./10_vererbung_und_super/)** | **Inheritance** | Basis- & Kindklassen, DRY-Prinzip, `super().__init__()` & Fahrzeugflotte | +| **[11. Polymorphie & Interfaces](./11_polymorphie_und_interfaces/)** | **Polymorphism** | Duck Typing, abstrakte Basisklassen (`ABC`) & Geometrie-Engine | +| **[12. Exceptions & Fehler](./12_exceptions_und_fehlerbehandlung/)** | **Robustes Coding** | `try-except-else-finally`, `raise`, eigene Exception-Klassen im Banking | +| **[13. Datei-Persistenz](./13_persistenz_json_und_csv/)** | **JSON & CSV** | Dauerhaftes Speichern & Laden: Savegames & Tabellen-Export | +| **[14. Desktop-GUIs mit Tkinter](./14_gui_mit_tkinter/)** | **Tkinter Desktop-Apps** | Echte Fenster, Buttons, Labels, Grid-Layout & MVC-Architektur | --- @@ -44,17 +60,17 @@ In jedem Kapitelordner liegt eine Datei namens `test_aufgabe.py`. Sie enthält a ### 1. Einzelnes Kapitel testen Öffne das Terminal in VS Code (`Strg + \``) und führe den Test im jeweiligen Ordner aus: ```bash -# Beispiel für Kapitel 01: -cd 01_einstieg_klassen +# Beispiel für Grundlagen G01: +cd g01_erste_schritte_taschenrechner python3 test_aufgabe.py ``` ### 2. Testausgabe richtig lesen: - ✅ **`OK`**: Perfekt! Alle Tests sind grün und bestanden. Du kannst mit dem nächsten Kapitel weitermachen. - ❌ **`FAIL` (AssertionError)**: Dein Code läuft, liefert aber einen falschen Wert zurück (z.B. Rechenfehler oder falsche Bedingung). Lies die letzte Zeile der Fehlermeldung! -- 💥 **`ERROR` (Syntax / AttributeError)**: Dein Code hat einen Absturz (z.B. Tippfehler im Methodennamen oder `self` im Methodenkopf vergessen). +- 💥 **`ERROR` (Syntax / AttributeError)**: Dein Code hat einen Absturz (z.B. Tippfehler im Funktionsnamen). -### 3. Gesamten Fortschritt aller Kapitel prüfen: +### 3. Gesamten Fortschritt aller 25 Kapitel prüfen: Führe im Hauptordner folgenden Befehl aus: ```bash python3 test_all.py @@ -65,6 +81,6 @@ Dieser Befehl zeigt dir eine Übersicht über alle Kapitel mit `✅` (bestanden) ## 💡 3 Wichtige Regeln für die Aufgaben -1. **`return` statt `print()`:** Tests prüfen das, was eine Methode mit `return` zurückliefert. Ein reines `print()` genügt dem Test nicht! -2. **Exakte Namen:** Die Namen der Methoden und Attribute müssen exakt der Aufgabenstellung entsprechen (`mutter_setzen`, nicht `MutterSetzen`). -3. **Immer `self` als erster Methoden-Parameter:** Jede Methode in einer Klasse muss `def meine_methode(self, ...):` geschrieben werden. +1. **`return` statt `print()`:** Tests prüfen das, was eine Funktion/Methode mit `return` zurückliefert. Ein reines `print()` genügt dem Test nicht! +2. **Exakte Namen:** Die Namen der Funktionen, Methoden und Attribute müssen exakt der Aufgabenstellung entsprechen (`addieren`, nicht `Addieren`). +3. **Fehlermeldungen von unten nach oben lesen:** Ganz unten im Traceback steht immer die genaue Ursache. diff --git a/g01_erste_schritte_taschenrechner/README.md b/g01_erste_schritte_taschenrechner/README.md new file mode 100644 index 0000000..eafe2e8 --- /dev/null +++ b/g01_erste_schritte_taschenrechner/README.md @@ -0,0 +1,92 @@ +# Grundlagen 01: Python als Taschenrechner 🧮 (Schulabgleich 02.1) + +Willkommen zum ersten Kapitel des Python-Grundlagen-Lehrpfads! + +In diesem Kapitel lernst du, wie Python als extrem präziser und vielseitiger Taschenrechner verwendet wird, welche Zahlenarten es gibt und wie die mathematischen Operatoren funktionieren. + +--- + +## 💡 Das Wichtigste in Kürze + +### 1. Zahlenarten in Python +Python unterscheidet grundlegend zwischen zwei Zahlenarten: +- **`int` (Integer):** Ganze Zahlen ohne Nachkommastellen (z.B. `42`, `-5`, `0`, `1000000`). Python kann mit beliebig großen ganzen Zahlen rechnen! +- **`float` (Floating Point Number):** Kommazahlen mit Nachkommastellen (z.B. `3.14`, `-0.5`, `2.0`). Beachte: In Python wird als Dezimaltrennzeichen immer ein **Punkt `.`** verwendet, kein Komma! + +--- + +## 🧮 Die 7 Rechenoperatoren in Python + +| Operator | Name | Beispiel | Ergebnis | Besonderheit / Erklärung | +| :---: | :--- | :--- | :---: | :--- | +| `+` | Addition | `12 + 8` | `20` | Addiert zwei Zahlen | +| `-` | Subtraktion | `20 - 7` | `13` | Subtrahiert den zweiten Wert vom ersten | +| `*` | Multiplikation | `6 * 7` | `42` | Multipliziert zwei Zahlen | +| `/` | Echte Division | `7 / 2` | `3.5` | **Wichtig:** Liefert in Python 3 *immer* ein `float` (Kommazahl)! `4 / 2` ergibt `2.0`. | +| `//` | Ganzzahldivision | `17 // 5` | `3` | Teilt und rundet immer zur nächsten ganzen Zahl nach unten ab (Floor Division). | +| `%` | Modulo | `17 % 5` | `2` | Berechnet den verbleibenden **Rest** einer Ganzzahldivision. | +| `**` | Potenz | `2 ** 3` | `8` | Berechnet $2^3 = 2 \times 2 \times 2 = 8$. | + +--- + +## 🍕 Pizza-Analogie für `//` und `%` + +Stell dir vor, du bestellst eine Pizza mit **17 Stücken** für **5 Personen**: +- Wie viele ganze Stücke bekommt jede Person fair aufgeteilt? + $$\text{Stücke pro Person} = 17 // 5 = 3$$ +- Wie viele Stücke bleiben in der Schachtel übrig? + $$\text{Rest} = 17 \% 5 = 2$$ +- Probe: $3 \times 5 + 2 = 17$! + +--- + +## 📐 Vorrangregeln (Punkt vor Strich & Klammern) + +Genau wie in der Schulmathematik beachtet Python feste Vorrangregeln: +1. **Klammern `(...)`** haben immer die höchste Priorität. +2. **Potenzen `**`** werden vor Multiplikation/Division berechnet. +3. **Punktrechnung `*`, `/`, `//`, `%`** kommt vor **Strichrechnung `+`, `-`**. + +```python +# Ohne Klammer: 3 * 4 = 12, dann 2 + 12 = 14 +ergebnis = 2 + 3 * 4 # 14 + +# Mit Klammer: (2 + 3) = 5, dann 5 * 4 = 20 +ergebnis = (2 + 3) * 4 # 20 +``` + +--- + +## 📢 Textausgabe mit `print()` + +Mit der eingebauten Funktion `print()` gibst du Texte und Rechenergebnisse im Terminal aus: + +```python +print("Hallo Welt!") +print("Ergebnis:", 40 + 2) +print(f"Die Kreisfläche beträgt: {3.14159 * 5**2}") +``` + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne die Datei `aufgabe.py` und bearbeite die folgenden 7 TODOs: +1. **TODO 1:** `addieren(a, b)` $\rightarrow$ Summe $a + b$ +2. **TODO 2:** `subtrahieren(a, b)` $\rightarrow$ Differenz $a - b$ +3. **TODO 3:** `multiplizieren(a, b)` $\rightarrow$ Produkt $a \times b$ +4. **TODO 4:** `dividieren(a, b)` $\rightarrow$ Quotient $a / b$ (als float) +5. **TODO 5:** `ganzzahl_rest(a, b)` $\rightarrow$ Tupel `(a // b, a % b)` +6. **TODO 6:** `potenz(basis, exponent)` $\rightarrow$ $basis^{exponent}$ (`basis ** exponent`) +7. **TODO 7:** `kreis_flaeche(radius)` $\rightarrow$ $3.14159 \times radius^2$ + +--- + +## 🧪 Lösung überprüfen + +Führe im Terminal den automatischen Unittest aus: +```bash +python3 test_aufgabe.py +``` + +Wenn alle 7 Tests mit `OK` durchlaufen, bist du bereit für das nächste Kapitel: **G02: Variablen & Datentypen**! diff --git a/g01_erste_schritte_taschenrechner/aufgabe.py b/g01_erste_schritte_taschenrechner/aufgabe.py new file mode 100644 index 0000000..9a2cc3a --- /dev/null +++ b/g01_erste_schritte_taschenrechner/aufgabe.py @@ -0,0 +1,144 @@ +""" +Grundlagen 01: Python als Taschenrechner – Aufgabenblatt +========================================================= + +In diesem Kapitel lernst du, wie Python als leistungsfähiger Taschenrechner +funktioniert und wie du mathematische Operationen in Funktionen kapselst. + +Bearbeite die Aufgaben Schritt für Schritt von TODO 1 bis TODO 7. +Wenn du fertig bist, überprüfe deine Lösung im Terminal mit: + python3 test_aufgabe.py +""" + +# ============================================================================== +# TODO 1: Schreibe die Funktion 'addieren(a, b)' +# +# Die Funktion soll zwei Zahlen (int oder float) entgegennehmen und deren +# mathematische Summe (a + b) zurückgeben. +# +# Beispiel: +# addieren(5, 3) -> 8 +# addieren(2.5, 1.5) -> 4.0 +# ============================================================================== + +def addieren(a: float, b: float) -> float: + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Schreibe die Funktion 'subtrahieren(a, b)' +# +# Die Funktion soll die Differenz von a und b (a - b) berechnen und zurückgeben. +# +# Beispiel: +# subtrahieren(10, 4) -> 6 +# subtrahieren(3, 5) -> -2 +# ============================================================================== + +def subtrahieren(a: float, b: float) -> float: + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Schreibe die Funktion 'multiplizieren(a, b)' +# +# Die Funktion soll das Produkt von a und b (a * b) berechnen und zurückgeben. +# +# Beispiel: +# multiplizieren(6, 7) -> 42 +# multiplizieren(4, 0.5) -> 2.0 +# ============================================================================== + +def multiplizieren(a: float, b: float) -> float: + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Schreibe die Funktion 'dividieren(a, b)' +# +# Die Funktion soll den Quotienten von a und b (a / b) berechnen und zurückgeben. +# Hinweis: Die Division '/' in Python liefert immer eine Kommazahl (float). +# +# Beispiel: +# dividieren(10, 2) -> 5.0 +# dividieren(7, 2) -> 3.5 +# ============================================================================== + +def dividieren(a: float, b: float) -> float: + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# TODO 5: Schreibe die Funktion 'ganzzahl_rest(a, b)' +# +# Die Funktion soll zwei ganze Zahlen a und b annehmen und ein Tupel mit zwei +# Werten zurückgeben: (quotient, rest) +# 1. Der ganzzahlige Quotient (mit Ganzzahldivision '//') +# 2. Der verbleibende Rest (mit Modulo-Operator '%') +# +# Beispiel: +# ganzzahl_rest(17, 5) -> (3, 2) # 17 // 5 ist 3, Rest 17 % 5 ist 2 +# ganzzahl_rest(20, 4) -> (5, 0) # 20 // 4 ist 5, Rest 20 % 4 ist 0 +# ============================================================================== + +def ganzzahl_rest(a: int, b: int) -> tuple: + # Schreibe hier deinen Code für TODO 5: + pass + + +# ============================================================================== +# TODO 6: Schreibe die Funktion 'potenz(basis, exponent)' +# +# Die Funktion soll 'basis' hoch 'exponent' berechnen (mit dem Operator '**'). +# +# Beispiel: +# potenz(2, 3) -> 8 # 2 * 2 * 2 = 8 +# potenz(5, 2) -> 25 # 5 * 5 = 25 +# potenz(10, 0) -> 1 +# ============================================================================== + +def potenz(basis: float, exponent: float) -> float: + # Schreibe hier deinen Code für TODO 6: + pass + + +# ============================================================================== +# TODO 7: Schreibe die Funktion 'kreis_flaeche(radius)' +# +# Berechne die Fläche eines Kreises mit der Formel: +# flaeche = pi * r^2 +# Verwende für Pi den Näherungswert 3.14159. +# Rechne: 3.14159 * (radius ** 2) +# +# Beispiel: +# kreis_flaeche(1) -> 3.14159 +# kreis_flaeche(2) -> 12.56636 # (3.14159 * 4) +# ============================================================================== + +def kreis_flaeche(radius: float) -> float: + # Schreibe hier deinen Code für TODO 7: + pass + + +# ============================================================================== +# Hauptprogramm zum Ausprobieren: +# (Führe diese Datei direkt aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("=" * 50) + print("🧮 PYTHON ALS TASCHENRECHNER – TEST-AUSGABE") + print("=" * 50) + + print("Addition (12 + 8):", addieren(12, 8)) + print("Subtraktion (20 - 7):", subtrahieren(20, 7)) + print("Multiplikation (6 * 7):", multiplizieren(6, 7)) + print("Division (15 / 2):", dividieren(15, 2)) + print("Ganzzahl & Rest (17 durch 5):", ganzzahl_rest(17, 5)) + print("Potenz (2 hoch 8):", potenz(2, 8)) + print("Kreisfläche (Radius 5):", kreis_flaeche(5)) + + print("\n💡 Führe 'python3 test_aufgabe.py' aus, um deine Lösungen zu prüfen!") diff --git a/g01_erste_schritte_taschenrechner/index.html b/g01_erste_schritte_taschenrechner/index.html new file mode 100644 index 0000000..7fd39be --- /dev/null +++ b/g01_erste_schritte_taschenrechner/index.html @@ -0,0 +1,305 @@ + + + + + + Grundlagen 01: Python als Taschenrechner + + + + +
+
+ +
Grundlagen 01 • Schulabgleich 02.1
+

Python als Taschenrechner 🧮

+

Lerne die Grundrechenarten, besondere Operatoren wie Modulo und Ganzzahldivision sowie Punkt-vor-Strich-Regeln in Python kennen.

+
+
+ +
+ + +
+

1. Die Taschenrechner-Analogie 💡

+

+ Bevor wir riesige Programme und Klassen bauen, starten wir mit der einfachsten Eigenschaft von Python: Python ist der schnellste und präziseste Taschenrechner der Welt! +

+ +
+
🧠 Warum ist Python besser als ein gewöhnlicher Taschenrechner?
+ Ein normaler Taschenrechner hat oft ein Limit bei 10 oder 12 Stellen. Python hingegen rechnet mit beliebig großen Zahlen (sogar Zahlen mit hunderten Ziffern!) und verarbeitet komplexe mathematische Formeln fehlerfrei nach den universellen Rechengesetzen. +
+ +
+
+ Python Interaktiv (REPL / Terminal) +
+ + + +
+
+
# Einfach eintippen – Python rechnet sofort:
+>>> 12 + 8
+20
+
+>>> 100 * 5
+500
+
+>>> 2 ** 10  # 2 hoch 10
+1024
+
+
+ + +
+

2. Ganze Zahlen (int) vs. Kommazahlen (float)

+

+ In Python gibt es zwei fundamentale Typen von Zahlen: +

+ +
+
🔢 Die zwei Zahlenwelten:
+
    +
  • int (Integer): Ganze Zahlen ohne Nachkommastellen (z.B. 42, -7, 0, 1000000).
  • +
  • float (Floating Point): Kommazahlen mit Nachkommastellen (z.B. 3.14, -0.5, 2.0).
  • +
+
+ +
+
⚠️ Wichtig: Punkt statt Komma!
+ Im Deutschen schreiben wir 3,14 mit Komma. Im Programmieren gilt immer der angelsächsische Punkt: 3.14. Ein Komma in Python trennt Parameter oder Listenelemente! +
+
+ + +
+

3. Die 7 Rechenoperatoren im Überblick

+

+ Hier sind alle wichtigen mathematischen Operatoren, die du in Python beherrschen musst: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorNameBeispielErgebnisErklärung & Besonderheit
+Addition12 + 820Addiert zwei Werte
-Subtraktion20 - 713Zieht den zweiten Wert ab
*Multiplikation6 * 742Multipliziert zwei Werte
/Echte Division7 / 23.5Wichtig: Liefert in Python 3 immer ein float (Kommazahl)! Sogar 4 / 2 liefert 2.0.
//Ganzzahldivision17 // 53Rundet das Ergebnis immer zur nächsten ganzen Zahl nach unten ab (Floor Division).
%Modulo (Rest)17 % 52Gibt den Rest bei der Ganzzahldivision zurück.
**Potenzierung2 ** 38Berechnet Basis hoch Exponent (2 * 2 * 2 = 8).
+ +

🍕 Die Pizza-Analogie für // und %

+
+
17 Pizzastücke auf 5 Freunde aufteilen:
+

+ - 17 // 5 = 3 → Jeder Freund bekommt genau 3 ganze Stücke.
+ - 17 % 5 = 2 → In der Schachtel bleiben 2 Stücke übrig.
+ - Kontrollrechnung: \(3 \times 5 + 2 = 17\) Stücke! +

+
+
+ + +
+

4. Vorrangregeln: Punkt vor Strich & Klammern 📐

+

+ Python hält sich streng an die mathematischen Rechengesetze (PEMDAS): +

+ +
    +
  1. Klammern (...): Höchste Priorität – wird immer zuerst ausgewertet!
  2. +
  3. Potenzen **: Werden vor Punkt- und Strichrechnung ausgeführt.
  4. +
  5. Punktrechnung *, /, //, %: Kommt vor Strichrechnung.
  6. +
  7. Strichrechnung +, -: Wird zuletzt ausgeführt.
  8. +
+ +
+
+ Vergleich: Vorrangregeln +
+
# Ohne Klammer: 3 * 4 = 12, dann 2 + 12 = 14
+a = 2 + 3 * 4        # 14
+
+# Mit Klammer: (2 + 3) = 5, dann 5 * 4 = 20
+b = (2 + 3) * 4      # 20
+
+# Kreisflächen-Formel: pi * r²
+flaeche = 3.14159 * (radius ** 2)
+
+
+ + +
+

5. Erste Textausgaben mit print() 📢

+

+ Mit der eingebauten Python-Funktion print() gibst du Texte und Rechenergebnisse auf der Konsole aus: +

+ +
+
Beispiele für print()
+
print("Hallo Welt!")
+print("Das Ergebnis von 5 + 3 ist:", 5 + 3)
+
+# Modern mit f-String:
+radius = 5
+flaeche = 3.14159 * (radius ** 2)
+print(f"Kreis mit Radius {radius} hat die Fläche: {flaeche}")
+
+
+ + +
+

6. 📝 Deine Aufgaben in aufgabe.py

+

+ Öffne die Datei g01_erste_schritte_taschenrechner/aufgabe.py in deinem Editor und bearbeite die 7 TODOs: +

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 5 (Tupel als Rückgabewert) +
+

In Python kannst du zwei Werte auf einmal als Tupel zurückgeben, indem du sie mit einem Komma in runden Klammern schreibst:

+
+
return (a // b, a % b)
+
+
+
+ +
+ 🔍 Tipp zu TODO 7 (Kreisfläche) +
+

Verwende für Pi den vorgegebenen Wert 3.14159 und den Potenzoperator ** 2:

+
+
return 3.14159 * (radius ** 2)
+
+
+
+
+ + +
+

7. 🧪 Testen im Terminal

+

Führe das Testskript aus, um deine Funktionen automatisch zu überprüfen:

+ +
+
Terminal
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g01_erste_schritte_taschenrechner/musterloesung.py b/g01_erste_schritte_taschenrechner/musterloesung.py new file mode 100644 index 0000000..9bee502 --- /dev/null +++ b/g01_erste_schritte_taschenrechner/musterloesung.py @@ -0,0 +1,53 @@ +""" +Grundlagen 01: Python als Taschenrechner – Musterlösung +======================================================== + +Vollständige Referenzlösung für alle Rechenoperationen. +""" + +def addieren(a: float, b: float) -> float: + """Berechnet die Summe von a und b.""" + return a + b + + +def subtrahieren(a: float, b: float) -> float: + """Berechnet die Differenz a - b.""" + return a - b + + +def multiplizieren(a: float, b: float) -> float: + """Berechnet das Produkt von a und b.""" + return a * b + + +def dividieren(a: float, b: float) -> float: + """Berechnet den Quotienten a / b als float.""" + return a / b + + +def ganzzahl_rest(a: int, b: int) -> tuple: + """Gibt ein Tupel (ganzzahliger_quotient, rest) zurück.""" + return (a // b, a % b) + + +def potenz(basis: float, exponent: float) -> float: + """Berechnet basis hoch exponent.""" + return basis ** exponent + + +def kreis_flaeche(radius: float) -> float: + """Berechnet die Kreisfläche für den gegebenen Radius (pi = 3.14159).""" + return 3.14159 * (radius ** 2) + + +if __name__ == "__main__": + print("=" * 50) + print("🧮 MUSTERLÖSUNG: G01 TASCHENRECHNER") + print("=" * 50) + print("addieren(12, 8) =", addieren(12, 8)) + print("subtrahieren(20, 7) =", subtrahieren(20, 7)) + print("multiplizieren(6, 7) =", multiplizieren(6, 7)) + print("dividieren(15, 2) =", dividieren(15, 2)) + print("ganzzahl_rest(17, 5) =", ganzzahl_rest(17, 5)) + print("potenz(2, 8) =", potenz(2, 8)) + print("kreis_flaeche(5) =", kreis_flaeche(5)) diff --git a/g01_erste_schritte_taschenrechner/test_aufgabe.py b/g01_erste_schritte_taschenrechner/test_aufgabe.py new file mode 100644 index 0000000..fba2f9b --- /dev/null +++ b/g01_erste_schritte_taschenrechner/test_aufgabe.py @@ -0,0 +1,127 @@ +import unittest +import sys +import importlib +from pathlib import Path + +# Sicherstellen, dass das Verzeichnis im Python-Suchpfad liegt +sys.path.insert(0, str(Path(__file__).parent)) + + +class TestKapitelG01(unittest.TestCase): + + @classmethod + def setUpClass(cls): + # Ermöglicht das Testen alternativer Module (z.B. musterloesung) + if hasattr(cls, "modul_name"): + cls.mod = importlib.import_module(cls.modul_name) + else: + try: + cls.mod = importlib.import_module("aufgabe") + except SyntaxError as e: + cls.mod = None + cls.import_error = f"SyntaxError in 'aufgabe.py': {e}. Überprüfe Doppelpunkte und Klammern!" + except Exception as e: + cls.mod = None + cls.import_error = f"Fehler beim Import von 'aufgabe.py': {e}" + + def _get_modul(self): + if getattr(self, "mod", None) is None: + err = getattr(self, "import_error", "aufgabe.py konnte nicht importiert werden") + self.fail(f"❌ {err}") + return self.mod + + def test_01_addieren(self): + """Prüft die Funktion addieren(a, b).""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "addieren"), "Fehler: Die Funktion 'addieren' fehlt noch!") + self.assertEqual(mod.addieren(5, 3), 8, "addieren(5, 3) muss 8 ergeben!") + self.assertEqual(mod.addieren(-4, 9), 5, "addieren(-4, 9) muss 5 ergeben!") + self.assertEqual(mod.addieren(0, 0), 0, "addieren(0, 0) muss 0 ergeben!") + self.assertAlmostEqual(mod.addieren(2.5, 1.5), 4.0, places=4, msg="addieren(2.5, 1.5) muss 4.0 ergeben!") + + def test_02_subtrahieren(self): + """Prüft die Funktion subtrahieren(a, b).""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "subtrahieren"), "Fehler: Die Funktion 'subtrahieren' fehlt noch!") + self.assertEqual(mod.subtrahieren(10, 4), 6, "subtrahieren(10, 4) muss 6 ergeben!") + self.assertEqual(mod.subtrahieren(3, 5), -2, "subtrahieren(3, 5) muss -2 ergeben!") + self.assertEqual(mod.subtrahieren(7, 7), 0, "subtrahieren(7, 7) muss 0 ergeben!") + self.assertAlmostEqual(mod.subtrahieren(5.5, 2.3), 3.2, places=4, msg="subtrahieren(5.5, 2.3) muss 3.2 ergeben!") + + def test_03_multiplizieren(self): + """Prüft die Funktion multiplizieren(a, b).""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "multiplizieren"), "Fehler: Die Funktion 'multiplizieren' fehlt noch!") + self.assertEqual(mod.multiplizieren(6, 7), 42, "multiplizieren(6, 7) muss 42 ergeben!") + self.assertEqual(mod.multiplizieren(-3, 4), -12, "multiplizieren(-3, 4) muss -12 ergeben!") + self.assertEqual(mod.multiplizieren(99, 0), 0, "multiplizieren(99, 0) muss 0 ergeben!") + self.assertAlmostEqual(mod.multiplizieren(4, 0.5), 2.0, places=4, msg="multiplizieren(4, 0.5) muss 2.0 ergeben!") + + def test_04_dividieren(self): + """Prüft die Funktion dividieren(a, b).""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "dividieren"), "Fehler: Die Funktion 'dividieren' fehlt noch!") + self.assertEqual(mod.dividieren(10, 2), 5.0, "dividieren(10, 2) muss 5.0 ergeben!") + self.assertEqual(mod.dividieren(7, 2), 3.5, "dividieren(7, 2) muss 3.5 ergeben!") + self.assertEqual(mod.dividieren(-9, 3), -3.0, "dividieren(-9, 3) muss -3.0 ergeben!") + self.assertAlmostEqual(mod.dividieren(1, 3), 0.333333, places=4, msg="dividieren(1, 3) sollte ca. 0.3333 ergeben!") + + def test_05_ganzzahl_rest(self): + """Prüft die Funktion ganzzahl_rest(a, b) für Ganzzahldivision // und Modulo %.""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "ganzzahl_rest"), "Fehler: Die Funktion 'ganzzahl_rest' fehlt noch!") + + ergebnis1 = mod.ganzzahl_rest(17, 5) + self.assertIsInstance(ergebnis1, tuple, "ganzzahl_rest muss ein Tupel (quotient, rest) zurückgeben!") + self.assertEqual(len(ergebnis1), 2, "Das zurückgegebene Tupel muss genau 2 Elemente haben: (quotient, rest)") + self.assertEqual(ergebnis1[0], 3, "17 // 5 ergibt den Quotienten 3!") + self.assertEqual(ergebnis1[1], 2, "17 % 5 ergibt den Rest 2!") + + ergebnis2 = mod.ganzzahl_rest(20, 4) + self.assertEqual(ergebnis2, (5, 0), "ganzzahl_rest(20, 4) muss (5, 0) ergeben!") + + ergebnis3 = mod.ganzzahl_rest(7, 3) + self.assertEqual(ergebnis3, (2, 1), "ganzzahl_rest(7, 3) muss (2, 1) ergeben!") + + ergebnis4 = mod.ganzzahl_rest(4, 9) + self.assertEqual(ergebnis4, (0, 4), "ganzzahl_rest(4, 9) muss (0, 4) ergeben!") + + def test_06_potenz(self): + """Prüft die Funktion potenz(basis, exponent).""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "potenz"), "Fehler: Die Funktion 'potenz' fehlt noch!") + self.assertEqual(mod.potenz(2, 3), 8, "potenz(2, 3) muss 8 ergeben (2 ** 3 = 8)!") + self.assertEqual(mod.potenz(5, 2), 25, "potenz(5, 2) muss 25 ergeben (5 ** 2 = 25)!") + self.assertEqual(mod.potenz(10, 0), 1, "potenz(10, 0) muss 1 ergeben (jede Zahl hoch 0 ist 1)!") + self.assertEqual(mod.potenz(3, 4), 81, "potenz(3, 4) muss 81 ergeben!") + self.assertAlmostEqual(mod.potenz(4, 0.5), 2.0, places=4, msg="potenz(4, 0.5) muss 2.0 (Quadratwurzel) ergeben!") + + def test_07_kreis_flaeche(self): + """Prüft die Funktion kreis_flaeche(radius) mit pi = 3.14159.""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "kreis_flaeche"), "Fehler: Die Funktion 'kreis_flaeche' fehlt noch!") + + erg1 = mod.kreis_flaeche(1) + self.assertIsNotNone(erg1, "kreis_flaeche(1) gibt None zurück. Vergiss nicht das 'return'!") + self.assertAlmostEqual( + erg1, 3.14159, places=4, + msg="kreis_flaeche(1) muss 3.14159 ergeben!" + ) + + erg2 = mod.kreis_flaeche(2) + self.assertIsNotNone(erg2, "kreis_flaeche(2) gibt None zurück. Vergiss nicht das 'return'!") + self.assertAlmostEqual( + erg2, 12.56636, places=4, + msg="kreis_flaeche(2) muss 12.56636 ergeben!" + ) + + erg0 = mod.kreis_flaeche(0) + self.assertIsNotNone(erg0, "kreis_flaeche(0) gibt None zurück. Vergiss nicht das 'return'!") + self.assertAlmostEqual( + erg0, 0.0, places=4, + msg="kreis_flaeche(0) muss 0.0 ergeben!" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g02_variablen_und_datentypen/README.md b/g02_variablen_und_datentypen/README.md new file mode 100644 index 0000000..2929c50 --- /dev/null +++ b/g02_variablen_und_datentypen/README.md @@ -0,0 +1,124 @@ +# Grundlagen 02: Variablen & Datentypen 📦🏷️ (Schulabgleich 03.0 & 03.1) + +In diesem Kapitel lernst du, wie Python Daten im Arbeitsspeicher speichert, welche 4 Basisdatentypen es gibt, wie Type Hints funktionieren und wie du Typen sicher umwandelst (Type Casting). + +--- + +## 📦 1. Was ist eine Variable? (Die Speicherbox-Analogie) + +Stell dir den Arbeitsspeicher deines Computers wie ein großes Lagerregal voller **Aufbewahrungsboxen** vor: +- Der **Wert** (z.B. `18` oder `"Anna"`) liegt in der Box. +- Die **Variable** ist das **Namensschild / Etikett**, das du außen an die Box klebst. +- Über das Etikett kannst du jederzeit auf den Inhalt zugreifen oder einen neuen Wert hineinlegen! + +```python +# Zuweisungs-Operator '=' klebt das Etikett 'alter' an den Wert 16: +alter = 16 + +# Später Geburtstag feiern – Wert in der Box aktualisieren: +alter = alter + 1 # Jetzt ist alter 17! +``` + +--- + +## 🏷️ 2. Die 4 Basisdatentypen in Python + +Python besitzt 4 grundlegende Datentypen, die du blind beherrschen musst: + +| Datentyp | Deutscher Begriff | Python-Typ | Beispiele | Erklärung | +| :--- | :--- | :---: | :--- | :--- | +| **Ganzzahl** | Ganze Zahl | `int` | `42`, `-7`, `0`, `1000` | Zahlen ohne Kommastelle | +| **Kommazahl** | Gleitkommazahl | `float` | `3.14`, `-0.5`, `2.0` | Zahlen mit Dezimalpunkt `.` | +| **Text** | Zeichenkette / String | `str` | `"Hallo"`, `'Python'`, `"123"` | Zeichen in einfachen `'` oder doppelten `"` Anführungszeichen | +| **Wahrheitswert** | Boolescher Wert | `bool` | `True`, `False` | Genau zwei Zustände: Wahr oder Falsch | + +--- + +## 🔍 3. Typprüfung mit `type()` & Moderne Type Hints + +### Typ abfragen: +Mit der Funktion `type(wert)` kannst du herausfinden, welcher Datentyp vorliegt: +```python +print(type(42)) # +print(type(3.14)) # +print(type("Hallo")) # +print(type(True)) # +``` + +### Type Hints (Typ-Annotationen): +In modernem Python (und in deinem Schulskriptum!) dokumentieren wir Variablen und Funktionsparameter mit Typ-Hinweisen: +```python +name: str = "Alice" +alter: int = 16 +preis: float = 19.99 +ist_aktiv: bool = True + +def begruessen(benutzer: str, wiederholungen: int) -> str: + return (benutzer + "! ") * wiederholungen +``` + +--- + +## 🔄 4. Typumwandlung (Type Casting) + +Manchmal liegt ein Wert im falschen Format vor (z.B. `"42"` als Text statt Zahl). Mit den Typ-Funktionen kannst du Daten umwandeln: + +```python +# Text zu Ganzzahl: +zahl = int("42") # 42 (int) + +# Text zu Kommazahl: +pi = float("3.14") # 3.14 (float) + +# Zahl zu Text: +text = str(100) # "100" (str) + +# Zu Wahrheitswert (bool): +b1 = bool(1) # True (alles außer 0 ist True) +b2 = bool(0) # False +b3 = bool("") # False (leerer String ist False) +b4 = bool("Python") # True +``` + +--- + +## ⚠️ 5. Die klassische Anfängerfalle: `"10" + "20"` vs. `10 + 20` + +Der Plus-Operator `+` verhält sich bei Texten ganz anders als bei Zahlen! + +```python +# ❌ Text-Verkettung (String-Konkatenation): +ergebnis_text = "10" + "20" +print(ergebnis_text) # Ausgabe: "1020" + +# ✅ Mathematische Addition: +ergebnis_zahl = 10 + 20 +print(ergebnis_zahl) # Ausgabe: 30 + +# ✅ Aus Texten echte Zahlen machen vor der Addition: +text_a = "10" +text_b = "20" +richtig = int(text_a) + int(text_b) +print(richtig) # Ausgabe: 30 +``` + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne die Datei `aufgabe.py` und bearbeite die folgenden 4 TODOs: +1. **TODO 1: `bestimme_typ_name(wert)`** $\rightarrow$ Gibt `"Wahrheitswert"`, `"Ganzzahl"`, `"Kommazahl"`, `"Text"` oder `"Unbekannt"` zurück. +2. **TODO 2: `summe_aus_texten(text_a, text_b)`** $\rightarrow$ Wandelt zwei Zahlen-Strings in `int` um und gibt die mathematische Summe zurück. +3. **TODO 3: `formatiere_preis(preis_float)`** $\rightarrow$ Formatiert einen Preis auf 2 Nachkommastellen mit Euro-Zeichen (z.B. `19.99 €`). +4. **TODO 4: `ist_volljaehrig(alter) -> bool`** $\rightarrow$ Gibt `True` ab 18 Jahren zurück, sonst `False`. + +--- + +## 🧪 Lösung überprüfen + +Führe im Terminal den automatischen Unittest aus: +```bash +python3 test_aufgabe.py +``` + +Wenn alle 4 Tests mit `OK` durchlaufen, hast du die Grundlagen von Variablen und Datentypen gemeistert! 🎉 diff --git a/g02_variablen_und_datentypen/aufgabe.py b/g02_variablen_und_datentypen/aufgabe.py new file mode 100644 index 0000000..1eee5a2 --- /dev/null +++ b/g02_variablen_und_datentypen/aufgabe.py @@ -0,0 +1,121 @@ +""" +Grundlagen 02: Variablen & Datentypen – Aufgabenblatt +====================================================== + +In diesem Kapitel lernst du, wie Variablen als benannte Speicherboxen +funktionieren, welche 4 Basisdatentypen Python besitzt und wie du Typen +umwandelst (Type Casting). + +Bearbeite die Aufgaben Schritt für Schritt von TODO 1 bis TODO 4. +Wenn du fertig bist, überprüfe deine Lösung im Terminal mit: + python3 test_aufgabe.py +""" + +# ============================================================================== +# TODO 1: Schreibe die Funktion 'bestimme_typ_name(wert)' +# +# Die Funktion bekommt einen beliebigen Wert übergeben und soll als String +# den deutschen Namen des Datentyps zurückgeben: +# - Wenn 'wert' ein bool ist (True/False): -> "Wahrheitswert" +# - Wenn 'wert' ein int ist (z.B. 42): -> "Ganzzahl" +# - Wenn 'wert' ein float ist (z.B. 3.14): -> "Kommazahl" +# - Wenn 'wert' ein str ist (z.B. "Hallo"): -> "Text" +# - Bei allen anderen Typen: -> "Unbekannt" +# +# WICHTIGER HINWEIS: +# In Python ist 'bool' eine Unterklasse von 'int' (isinstance(True, int) ist True!). +# Prüfe daher 'bool' ZUERST vor 'int' oder verwende 'type(wert) is bool'! +# +# Beispiele: +# bestimme_typ_name(42) -> "Ganzzahl" +# bestimme_typ_name(3.14) -> "Kommazahl" +# bestimme_typ_name("Python") -> "Text" +# bestimme_typ_name(True) -> "Wahrheitswert" +# ============================================================================== + +def bestimme_typ_name(wert) -> str: + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Schreibe die Funktion 'summe_aus_texten(text_a, text_b)' +# +# Oft kommen Benutzereingaben (z.B. von input()) als Text (str) an. +# Wenn man "10" + "20" rechnet, entsteht fälschlicherweise "1020" (Textverkettung). +# +# Deine Aufgabe: +# Wandle beide Parameter 'text_a' und 'text_b' mit 'int()' in echte Ganzzahlen um, +# addiere sie mathematisch und gib das Ergebnis (als int) zurück! +# +# Beispiele: +# summe_aus_texten("10", "20") -> 30 (nicht "1020"!) +# summe_aus_texten("5", "7") -> 12 +# ============================================================================== + +def summe_aus_texten(text_a: str, text_b: str) -> int: + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Schreibe die Funktion 'formatiere_preis(preis_float)' +# +# Die Funktion bekommt einen Preis als float (Kommazahl) übergeben und soll +# einen formatierten String mit genau 2 Nachkommastellen und dem Euro-Zeichen '€' +# zurückgeben. +# +# Format: "{preis:.2f} €" +# +# Beispiele: +# formatiere_preis(19.99) -> "19.99 €" +# formatiere_preis(5.0) -> "5.00 €" +# formatiere_preis(0.0) -> "0.00 €" +# ============================================================================== + +def formatiere_preis(preis_float: float) -> str: + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Schreibe die Funktion 'ist_volljaehrig(alter)' +# +# Die Funktion bekommt ein Alter als Ganzzahl übergeben. +# Sie soll 'True' zurückgeben, wenn 'alter' größer oder gleich 18 ist, +# andernfalls 'False'. +# +# Tipp: +# Ein Vergleichsoperator wie 'alter >= 18' liefert bereits direkt einen bool! +# +# Beispiele: +# ist_volljaehrig(18) -> True +# ist_volljaehrig(21) -> True +# ist_volljaehrig(17) -> False +# ============================================================================== + +def ist_volljaehrig(alter: int) -> bool: + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# Hauptprogramm zum Ausprobieren: +# (Führe diese Datei direkt aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("=" * 55) + print("📦 VARIABLEN & DATENTYPEN – TEST-AUSGABE") + print("=" * 55) + + print("Typ von 42: ", bestimme_typ_name(42)) + print("Typ von 3.14: ", bestimme_typ_name(3.14)) + print("Typ von 'Hallo': ", bestimme_typ_name("Hallo")) + print("Typ von True: ", bestimme_typ_name(True)) + + print("\nSumme aus '10' + '20':", summe_aus_texten("10", "20")) + print("Preis formatiert (19.9):", formatiere_preis(19.9)) + print("Ist 17 volljährig? ", ist_volljaehrig(17)) + print("Ist 18 volljährig? ", ist_volljaehrig(18)) + + print("\n💡 Führe 'python3 test_aufgabe.py' aus, um deine Lösungen zu prüfen!") diff --git a/g02_variablen_und_datentypen/index.html b/g02_variablen_und_datentypen/index.html new file mode 100644 index 0000000..11c6133 --- /dev/null +++ b/g02_variablen_und_datentypen/index.html @@ -0,0 +1,256 @@ + + + + + + Grundlagen 02: Variablen & Datentypen + + + + +
+
+ +
Grundlagen 02 • Schulabgleich 03.0 & 03.1
+

Variablen & Datentypen 📦🏷️

+

Lerne, wie Daten im Arbeitsspeicher abgelegt werden, was int, float, str und bool unterscheidet und wie Type Casting funktioniert.

+
+
+ +
+ + +
+

1. Was ist eine Variable? (Die Speicherbox-Analogie) 📦

+

+ Programme müssen Zwischenergebnisse, Spielstände oder Benutzernamen speichern. Dafür nutzt man Variablen. +

+ +
+
🏷️ Das Namensschild am Karton:
+ Stell dir den Arbeitsspeicher (RAM) deines Computers wie ein gigantisches Regal voller Kartons vor: +
    +
  • Der Wert (z.B. 16 oder "Mia") liegt im Karton.
  • +
  • Der Variablenname (z.B. alter) ist das Etikett, das du außen auf den Karton klebst.
  • +
  • Mit dem Zuweisungsoperator = legst du einen Wert in den Karton hinein!
  • +
+
+ +
+
Variablen zuweisen und verändern
+
# Wert 16 in die Box 'alter' legen:
+alter = 16
+
+# Später Geburtstag feiern – Wert aktualisieren:
+alter = alter + 1  # Jetzt ist alter 17!
+
+print("Neues Alter:", alter)
+
+
+ + +
+

2. Die 4 Basisdatentypen in Python 🏷️

+

+ Jeder Wert in Python gehört zu einer bestimmten Datenkategorie (einem sogenannten Datentyp): +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DatentypPython-TypBeispieleErklärung & Verwendung
Ganzzahlint42, -7, 0, 1000Zähler, Alter, Punkte im Spiel (ohne Nachkommastellen).
Kommazahlfloat3.14, -0.5, 2.0Preise, Messwerte, Notendurchschnitt (mit Dezimalpunkt .).
Text (String)str"Hallo", 'Python', "42"Namen, Nachrichten, Adressen (immer in Anführungszeichen).
WahrheitswertboolTrue, FalseLogische Schalter, Bedingungen, Berechtigungen (z.B. Volljährigkeit).
+
+ + +
+

3. Typprüfung mit type() & Moderne Type Hints 🔍

+ +

Mit der eingebauten Funktion type() kannst du Python fragen, welchen Typ ein Wert hat:

+ +
+
type() Abfragen
+
print(type(42))       # <class 'int'>
+print(type(3.14))     # <class 'float'>
+print(type("Mia"))    # <class 'str'>
+print(type(True))     # <class 'bool'>
+
+ +
+
📝 Type Hints (Typ-Hinweise nach Schulstandard):
+ Im Unterricht und in professionellen Codebases kennzeichnen wir Variablen und Funktions-Signaturen mit Typen. Das hilft VS Code und Lesern enorm beim Verstehen des Codes! +
+ +
+
Type Hints in Aktion
+
name: str = "Alice"
+alter: int = 17
+kontostand: float = 149.50
+ist_mitglied: bool = True
+
+def ist_volljaehrig(alter: int) -> bool:
+    return alter >= 18
+
+
+ + +
+

4. Typumwandlung (Type Casting) 🔄

+

+ Oft müssen wir Daten von einem Typ in einen anderen umwandeln (z.B. einen Text "42" in eine echte Zahl 42, um damit rechnen zu können). +

+ +
+
Casting-Funktionen
+
# 1. In Ganzzahl umwandeln:
+z1 = int("42")     # 42
+z2 = int(9.99)     # 9 (schneidet Nachkommastellen einfach ab!)
+
+# 2. In Kommazahl umwandeln:
+f1 = float("3.14") # 3.14
+f2 = float(10)     # 10.0
+
+# 3. In Text umwandeln:
+s1 = str(100)      # "100"
+s2 = str(True)     # "True"
+
+# 4. In Wahrheitswert (bool) umwandeln:
+b1 = bool(1)       # True (Zahlen != 0 sind True)
+b2 = bool(0)       # False
+b3 = bool("")      # False (leerer Text ist False)
+b4 = bool("Hallo") # True (nicht-leerer Text ist True)
+
+ + +
+
💥 Der klassische Anfängerfehler: "10" + "20" vs. 10 + 20
+

+ Wenn du zwei Strings mit + verbindest, werden sie aneinandergehängt (Konkatenation). Wenn du zwei Zahlen addierst, wird gerechnet: +

+

+ - "10" + "20" → "1020" ❌ (Textverkettung!)
+ - 10 + 20 → 30 ✅ (Zahlenaddition!)
+ - int("10") + int("20") → 30 ✅ (Mit Casting!) +

+
+
+ + +
+

5. 📝 Deine Aufgaben in aufgabe.py

+

+ Öffne die Datei g02_variablen_und_datentypen/aufgabe.py und bearbeite die 4 TODOs: +

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 1 (Die bool vs. int Falle) +
+

In Python erbt bool historisch von int. Wenn du isinstance(True, int) ausführst, liefert Python True! Prüfe daher immer zuerst:

+
+
if isinstance(wert, bool):
+    return "Wahrheitswert"
+elif isinstance(wert, int):
+    return "Ganzzahl"
+
+
+
+ +
+ 🔍 Tipp zu TODO 3 (Kommazahlen mit f-String runden) +
+

Mit {preis:.2f} wird eine Kommazahl auf exakt 2 Nachkommastellen formatiert:

+
+
return f"{preis_float:.2f} €"
+
+
+
+
+ + +
+

6. 🧪 Testen im Terminal

+

Sobald du alle 4 Funktionen implementiert hast, führe den Unittest aus:

+ +
+
Terminal
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g02_variablen_und_datentypen/musterloesung.py b/g02_variablen_und_datentypen/musterloesung.py new file mode 100644 index 0000000..de9993a --- /dev/null +++ b/g02_variablen_und_datentypen/musterloesung.py @@ -0,0 +1,49 @@ +""" +Grundlagen 02: Variablen & Datentypen – Musterlösung +===================================================== + +Vollständige Referenzlösung für alle Aufgaben. +""" + +def bestimme_typ_name(wert) -> str: + """Gibt den deutschen Namen des Datentyps als String zurück.""" + # Wichtig: bool zuerst prüfen, da bool von int erbt! + if isinstance(wert, bool): + return "Wahrheitswert" + elif isinstance(wert, int): + return "Ganzzahl" + elif isinstance(wert, float): + return "Kommazahl" + elif isinstance(wert, str): + return "Text" + else: + return "Unbekannt" + + +def summe_aus_texten(text_a: str, text_b: str) -> int: + """Wandelt zwei Zahlen-Strings in echte Integers um und berechnet deren Summe.""" + return int(text_a) + int(text_b) + + +def formatiere_preis(preis_float: float) -> str: + """Formatiert einen Preis mit genau 2 Nachkommastellen und Euro-Zeichen.""" + return f"{preis_float:.2f} €" + + +def ist_volljaehrig(alter: int) -> bool: + """Prüft, ob ein Alter >= 18 ist und gibt True/False zurück.""" + return alter >= 18 + + +if __name__ == "__main__": + print("=" * 55) + print("📦 MUSTERLÖSUNG: G02 VARIABLEN & DATENTYPEN") + print("=" * 55) + print("bestimme_typ_name(42) =", bestimme_typ_name(42)) + print("bestimme_typ_name(3.14) =", bestimme_typ_name(3.14)) + print("bestimme_typ_name('Python') =", bestimme_typ_name("Python")) + print("bestimme_typ_name(True) =", bestimme_typ_name(True)) + print("summe_aus_texten('10', '20')=", summe_aus_texten("10", "20")) + print("formatiere_preis(19.99) =", formatiere_preis(19.99)) + print("ist_volljaehrig(17) =", ist_volljaehrig(17)) + print("ist_volljaehrig(18) =", ist_volljaehrig(18)) diff --git a/g02_variablen_und_datentypen/test_aufgabe.py b/g02_variablen_und_datentypen/test_aufgabe.py new file mode 100644 index 0000000..d15cff7 --- /dev/null +++ b/g02_variablen_und_datentypen/test_aufgabe.py @@ -0,0 +1,113 @@ +import unittest +import sys +import importlib +from pathlib import Path + +# Sicherstellen, dass das Verzeichnis im Python-Suchpfad liegt +sys.path.insert(0, str(Path(__file__).parent)) + + +class TestKapitelG02(unittest.TestCase): + + @classmethod + def setUpClass(cls): + # Ermöglicht das Testen alternativer Module (z.B. musterloesung) + if hasattr(cls, "modul_name"): + cls.mod = importlib.import_module(cls.modul_name) + else: + try: + cls.mod = importlib.import_module("aufgabe") + except SyntaxError as e: + cls.mod = None + cls.import_error = f"SyntaxError in 'aufgabe.py': {e}. Überprüfe Doppelpunkte und Einrückungen!" + except Exception as e: + cls.mod = None + cls.import_error = f"Fehler beim Import von 'aufgabe.py': {e}" + + def _get_modul(self): + if getattr(self, "mod", None) is None: + err = getattr(self, "import_error", "aufgabe.py konnte nicht importiert werden") + self.fail(f"❌ {err}") + return self.mod + + def test_01_bestimme_typ_name(self): + """Prüft die Funktion bestimme_typ_name(wert) für alle 4 Basisdatentypen und Unbekannt.""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "bestimme_typ_name"), "Fehler: Die Funktion 'bestimme_typ_name' fehlt noch!") + + # Ganze Zahlen (int) + self.assertEqual(mod.bestimme_typ_name(42), "Ganzzahl", "42 muss als 'Ganzzahl' erkannt werden!") + self.assertEqual(mod.bestimme_typ_name(0), "Ganzzahl", "0 muss als 'Ganzzahl' erkannt werden!") + self.assertEqual(mod.bestimme_typ_name(-15), "Ganzzahl", "-15 muss als 'Ganzzahl' erkannt werden!") + + # Kommazahlen (float) + self.assertEqual(mod.bestimme_typ_name(3.14), "Kommazahl", "3.14 muss als 'Kommazahl' erkannt werden!") + self.assertEqual(mod.bestimme_typ_name(0.0), "Kommazahl", "0.0 muss als 'Kommazahl' erkannt werden!") + self.assertEqual(mod.bestimme_typ_name(-2.5), "Kommazahl", "-2.5 muss als 'Kommazahl' erkannt werden!") + + # Text (str) + self.assertEqual(mod.bestimme_typ_name("Hallo"), "Text", "'Hallo' muss als 'Text' erkannt werden!") + self.assertEqual(mod.bestimme_typ_name(""), "Text", "Leerer String '' muss als 'Text' erkannt werden!") + self.assertEqual(mod.bestimme_typ_name("123"), "Text", "'123' in Anführungszeichen ist 'Text'!") + + # Wahrheitswerte (bool) - Besonderer Test: Darf nicht als Ganzzahl erkannt werden! + self.assertEqual( + mod.bestimme_typ_name(True), "Wahrheitswert", + "True muss als 'Wahrheitswert' erkannt werden (nicht als 'Ganzzahl'!)" + ) + self.assertEqual( + mod.bestimme_typ_name(False), "Wahrheitswert", + "False muss als 'Wahrheitswert' erkannt werden (nicht als 'Ganzzahl'!)" + ) + + # Andere / Unbekannte Typen + self.assertEqual(mod.bestimme_typ_name([1, 2, 3]), "Unbekannt", "Eine Liste sollte 'Unbekannt' liefern.") + self.assertEqual(mod.bestimme_typ_name(None), "Unbekannt", "None sollte 'Unbekannt' liefern.") + + def test_02_summe_aus_texten(self): + """Prüft die Typkonvertierung von Texten zu Integers und deren Addition.""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "summe_aus_texten"), "Fehler: Die Funktion 'summe_aus_texten' fehlt noch!") + + ergebnis = mod.summe_aus_texten("10", "20") + self.assertIsNotNone(ergebnis, "summe_aus_texten('10', '20') liefert None. Vergiss nicht das 'return'!") + self.assertNotEqual( + ergebnis, "1020", + "Achtung: Du hast '1020' als Text erhalten! Wandle die Strings mit int() um, bevor du addierst!" + ) + self.assertIsInstance(ergebnis, int, "Das Ergebnis muss vom Typ 'int' sein!") + self.assertEqual(ergebnis, 30, "summe_aus_texten('10', '20') muss 30 ergeben!") + + self.assertEqual(mod.summe_aus_texten("5", "7"), 12, "summe_aus_texten('5', '7') muss 12 ergeben!") + self.assertEqual(mod.summe_aus_texten("-3", "8"), 5, "summe_aus_texten('-3', '8') muss 5 ergeben!") + self.assertEqual(mod.summe_aus_texten("0", "0"), 0, "summe_aus_texten('0', '0') muss 0 ergeben!") + + def test_03_formatiere_preis(self): + """Prüft die Preisformatierung mit 2 Nachkommastellen und Euro-Symbol.""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "formatiere_preis"), "Fehler: Die Funktion 'formatiere_preis' fehlt noch!") + + self.assertEqual(mod.formatiere_preis(19.99), "19.99 €", "19.99 muss zu '19.99 €' formatiert werden!") + self.assertEqual(mod.formatiere_preis(5.0), "5.00 €", "5.0 muss zu '5.00 €' (mit 2 Nullen) formatiert werden!") + self.assertEqual(mod.formatiere_preis(0.0), "0.00 €", "0.0 muss zu '0.00 €' formatiert werden!") + self.assertEqual(mod.formatiere_preis(149.5), "149.50 €", "149.5 muss zu '149.50 €' formatiert werden!") + self.assertEqual(mod.formatiere_preis(3.14159), "3.14 €", "3.14159 muss auf 2 Nachkommastellen '3.14 €' gerundet werden!") + + def test_04_ist_volljaehrig(self): + """Prüft die Funktion ist_volljaehrig(alter) für verschiedene Altersgrenzen.""" + mod = self._get_modul() + self.assertTrue(hasattr(mod, "ist_volljaehrig"), "Fehler: Die Funktion 'ist_volljaehrig' fehlt noch!") + + # Volljährig + self.assertIs(mod.ist_volljaehrig(18), True, "18 Jahre muss True (volljährig) sein!") + self.assertIs(mod.ist_volljaehrig(21), True, "21 Jahre muss True sein!") + self.assertIs(mod.ist_volljaehrig(50), True, "50 Jahre muss True sein!") + + # Minderjährig + self.assertIs(mod.ist_volljaehrig(17), False, "17 Jahre muss False (minderjährig) sein!") + self.assertIs(mod.ist_volljaehrig(16), False, "16 Jahre muss False sein!") + self.assertIs(mod.ist_volljaehrig(0), False, "0 Jahre muss False sein!") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g03_ein_und_ausgabe/README.md b/g03_ein_und_ausgabe/README.md new file mode 100644 index 0000000..efdf4a0 --- /dev/null +++ b/g03_ein_und_ausgabe/README.md @@ -0,0 +1,122 @@ +# Kapitel G03: Interaktive Ein- & Ausgabe (Schulabgleich 07.0) 💬⌨️ + +Herzlich willkommen zum Kapitel **Interaktive Ein- und Ausgabe**! + +Programme werden erst dann richtig spannend, wenn sie mit Menschen interagieren können: Sie stellen Fragen, nehmen Antworten über die Tastatur entgegen, verarbeiten diese und geben schön formatierte Ergebnisse auf dem Bildschirm aus. + +--- + +## 🎯 Lernziele + +1. **Benutzereingaben erfassen:** `input("Fragetext: ")` nutzen. +2. **Die goldene `input()`-Regel verstehen:** `input()` liefert **immer** einen `str` (Text), selbst wenn die Benutzerin `42` eingibt. +3. **Datentypen umwandeln (Typkonvertierung / Casting):** Mit `int()` und `float()` Texte in Zahlen umwandeln. +4. **Moderne f-Strings einsetzen:** Variablen und Ausdrücke sauber in Texte einbetten: `f"Hallo {name}!"`. +5. **Format-Spezifizierer beherrschen:** Zahlen präzise runden und formatieren (`{preis:.2f} €`, `{bmi:.1f}`, `{zahl:04d}`). +6. **`print()` Parameter steuern:** `sep="-"` (Trennzeichen) und `end=""` (Zeilenende) anwenden. + +--- + +## 💡 Theorie & Wichtige Konzepte + +### 1. Tastatureingabe mit `input()` + +Die Funktion `input()` pausiert dein Programm und wartet darauf, dass die Benutzerin etwas im Terminal eintippt und mit Enter bestätigt. + +```python +name = input("Wie heißt du? ") +print(f"Schön, dich kennenzulernen, {name}!") +``` + +> ⚠️ **Achtung, Falle!** `input()` liefert IMMER einen `str`: +> ```python +> alter_text = input("Dein Alter: ") # Wenn du 15 eingibst, ist der Wert "15" (Text)! +> # alter_text + 1 💥 TypeError: can only concatenate str (not "int") to str +> ``` + +### 2. Typumwandlung (Type Casting) + +Um mit Eingabezahlen rechnen zu können, musst du sie in `int` (Ganzzahl) oder `float` (Kommazahl) umwandeln: + +```python +# Ganze Zahl einlesen: +alter = int(input("Dein Alter in Jahren: ")) +tage = alter * 365 + +# Kommazahl einlesen: +preis = float(input("Preis in Euro (z.B. 4.99): ")) +rabatt = preis * 0.9 +``` + +--- + +### 3. Moderne f-Strings (Formatierte Strings) + +Seit Python 3.6 nutzt man **f-Strings** (ein vorangestelltes `f` vor den Anführungszeichen). Alles innerhalb geschweifter Klammern `{...}` wird direkt als Python-Code ausgewertet: + +```python +name = "Mia" +punkte = 42 +print(f"Spielerin {name} hat {punkte} Punkte (doppelt so viel: {punkte * 2})!") +``` + +--- + +### 4. Formatierungs-Cheat-Sheet in f-Strings + +Hinter einem Doppelpunkt `:` in den geschweiften Klammern kannst du das Format exakt bestimmen: + +| Format-Code | Bedeutung | Beispiel | Ergebnis | +| :--- | :--- | :--- | :--- | +| `{x:.2f}` | Kommazahl mit genau 2 Dezimalstellen | `{12.5:.2f} €` | `"12.50 €"` | +| `{x:.1f}` | Kommazahl mit genau 1 Dezimalstelle | `{23.148:.1f}` | `"23.1"` | +| `{x:.1%}` | Prozentangabe (multipliziert mit 100) | `{0.19:.1%}` | `"19.0%"` | +| `{n:04d}` | Ganzzahl mit führenden Nullen (4 Stellen) | `{7:04d}` | `"0007"` | +| `{s:>10}` | Text rechtsbündig (Breite 10) | `{"Python":>10}` | `" Python"` | + +--- + +### 5. `print()` Parameter: `sep` und `end` + +Die `print()`-Funktion hat zwei sehr praktische optionale Parameter: +- `sep`: Trennzeichen zwischen mehreren Werten (Standard: ein Leerzeichen `" "`). +- `end`: Zeichen am Ende der Ausgabe (Standard: Zeilenumbruch `"\n"`). + +```python +# sep: Eigenes Trennzeichen +print("2026", "08", "29", sep="-") +# Ausgabe: 2026-08-29 + +# end: Kein Zeilenumbruch, nächste Ausgabe bleibt in derselben Zeile +print("Lade Fortschritt", end="... ") +print("100% Fertig!") +# Ausgabe: Lade Fortschritt... 100% Fertig! +``` + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne `g03_ein_und_ausgabe/aufgabe.py` und implementiere die 4 Funktionen: + +1. **TODO 1:** `begruessungs_text(name: str, stadt: str) -> str` + - Rückgabe: `f"Hallo {name}, herzlich willkommen in {stadt}!"` +2. **TODO 2:** `berechne_alter_in_tagen(jahre: int) -> int` + - Multipliziere `jahre * 365`. +3. **TODO 3:** `formatiere_rechnungsposten(artikel: str, anzahl: int, einzelpreis: float) -> str` + - Berechne Gesamtpreis und formatiere: `f"{anzahl}x {artikel} à {einzelpreis:.2f} € = {gesamt:.2f} €"` +4. **TODO 4:** `steckbrief(name: str, groesse_m: float, gewicht_kg: float) -> str` + - Berechne BMI: `gewicht_kg / (groesse_m ** 2)` + - Formatiere: `f"Steckbrief: {name} | Größe: {groesse_m:.2f} m | Gewicht: {gewicht_kg:.1f} kg | BMI: {bmi:.1f}"` + +--- + +## 🧪 Tests ausführen + +Überprüfe deine Lösung im Terminal mit: + +```bash +python3 test_aufgabe.py +``` + +Wenn alle 4 Tests mit **`OK`** durchlaufen, hast du das Kapitel gemeistert! 🎉 diff --git a/g03_ein_und_ausgabe/aufgabe.py b/g03_ein_und_ausgabe/aufgabe.py new file mode 100644 index 0000000..af229af --- /dev/null +++ b/g03_ein_und_ausgabe/aufgabe.py @@ -0,0 +1,151 @@ +""" +Kapitel G03: Interaktive Ein- & Ausgabe (Schulabgleich 07.0) +============================================================ + +In diesem Kapitel lernst du: +1. Wie du Benutzereingaben über die Tastatur mit input() entgegennimmst. +2. Wie du Datentypen umwandelst (int(input(...)), float(input(...))). +3. Wie du moderne f-Strings für formatierte Textausgaben einsetzt. +4. Wie du Zahlenwerte formatiert darstellst ({preis:.2f} €, {prozent:.1%}, {zahl:04d}). +5. Wie du print() mit den Parametern sep= und end= steuerst. + +Bearbeite die Aufgaben TODO 1 bis TODO 4. +Führe danach die Tests aus mit: + python3 test_aufgabe.py +""" + +# ============================================================================== +# TODO 1: Begrüßungstext mit f-String +# +# Schreibe eine Funktion 'begruessungs_text(name: str, stadt: str) -> str'. +# Sie soll folgenden Text zurückgeben: +# "Hallo {name}, herzlich willkommen in {stadt}!" +# +# Beispiel: +# begruessungs_text("Anna", "Wien") -> "Hallo Anna, herzlich willkommen in Wien!" +# ============================================================================== + +def begruessungs_text(name: str, stadt: str) -> str: + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Alter in Tagen berechnen +# +# Schreibe eine Funktion 'berechne_alter_in_tagen(jahre: int) -> int'. +# Sie erhält das Alter in ganzen Jahren (z.B. 18) und soll das Alter in Tagen +# berechnen (wir rechnen vereinfacht mit genau 365 Tagen pro Jahr). +# +# Beispiel: +# berechne_alter_in_tagen(18) -> 6570 +# berechne_alter_in_tagen(1) -> 365 +# ============================================================================== + +def berechne_alter_in_tagen(jahre: int) -> int: + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Rechnungsposten formatieren +# +# Schreibe eine Funktion 'formatiere_rechnungsposten(artikel: str, anzahl: int, einzelpreis: float) -> str'. +# Sie berechnet den Gesamtpreis (anzahl * einzelpreis) und gibt einen formatierten +# Rechnungs-String zurück. +# +# Format: +# "{anzahl}x {artikel} à {einzelpreis:.2f} € = {gesamtpreis:.2f} €" +# +# WICHTIG: Die Preise müssen immer auf genau 2 Nachkommastellen formatiert sein! +# +# Beispiele: +# formatiere_rechnungsposten("Kaffee", 3, 2.5) -> "3x Kaffee à 2.50 € = 7.50 €" +# formatiere_rechnungsposten("Buch", 1, 19.99) -> "1x Buch à 19.99 € = 19.99 €" +# formatiere_rechnungsposten("Semmel", 5, 0.40) -> "5x Semmel à 0.40 € = 2.00 €" +# ============================================================================== + +def formatiere_rechnungsposten(artikel: str, anzahl: int, einzelpreis: float) -> str: + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Steckbrief mit BMI-Berechnung +# +# Schreibe eine Funktion 'steckbrief(name: str, groesse_m: float, gewicht_kg: float) -> str'. +# 1. Berechne den BMI (Body-Mass-Index) nach der Formel: +# bmi = gewicht_kg / (groesse_m ** 2) +# 2. Gib folgenden formatierten Steckbrief-String zurück: +# "Steckbrief: {name} | Größe: {groesse_m:.2f} m | Gewicht: {gewicht_kg:.1f} kg | BMI: {bmi:.1f}" +# +# WICHTIG: +# - Größe auf 2 Nachkommastellen formatiert ({groesse_m:.2f} m) +# - Gewicht auf 1 Nachkommastelle formatiert ({gewicht_kg:.1f} kg) +# - BMI auf 1 Nachkommastelle formatiert ({bmi:.1f}) +# +# Beispiel: +# steckbrief("Alex", 1.80, 75.0) -> "Steckbrief: Alex | Größe: 1.80 m | Gewicht: 75.0 kg | BMI: 23.1" +# steckbrief("Mia", 1.65, 58.5) -> "Steckbrief: Mia | Größe: 1.65 m | Gewicht: 58.5 kg | BMI: 21.5" +# ============================================================================== + +def steckbrief(name: str, groesse_m: float, gewicht_kg: float) -> str: + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# Interaktives Hauptprogramm zum Ausprobieren: +# (Führe dieses Skript aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("=" * 60) + print("💬 INTERAKTIVE EIN- & AUSGABE DEMO") + print("=" * 60) + + # 1. Demonstration von print() mit sep und end + print("Python", "ist", "klasse!", sep=" - ") + print("Lade Daten", end="... ") + print("Fertig!\n") + + # 2. Interaktiver Dialog mit Tastatureingabe + try: + benutzer_name = input("Wie heißt du? ") + benutzer_stadt = input("Aus welcher Stadt kommst du? ") + begruessung = begruessungs_text(benutzer_name, benutzer_stadt) + if begruessung: + print(begruessung) + else: + print(f"Hallo {benutzer_name} aus {benutzer_stadt} (Implementiere TODO 1)!") + + alter_str = input("\nWie alt bist du (in Jahren)? ") + # Achtung: input() liefert immer einen String -> Umwandlung mit int() nötig! + alter_int = int(alter_str) + tage = berechne_alter_in_tagen(alter_int) + if tage is not None: + print(f"Wahnsinn, du lebst schon seit ca. {tage} Tagen!") + else: + print("Implementiere TODO 2 für die Tage-Berechnung!") + + print("\n--- Kassenbon-Generator ---") + art = input("Welcher Artikel? ") + anz = int(input("Wie viele Stück? ")) + preis = float(input("Einzelpreis in € (z.B. 2.99): ")) + posten = formatiere_rechnungsposten(art, anz, preis) + if posten: + print(posten) + else: + print("Implementiere TODO 3 für den formatierten Rechnungsposten!") + + print("\n--- Steckbrief & BMI-Rechner ---") + gr = float(input("Deine Körpergröße in Metern (z.B. 1.75): ")) + gw = float(input("Dein Körpergewicht in kg (z.B. 68.0): ")) + sb = steckbrief(benutzer_name, gr, gw) + if sb: + print(sb) + else: + print("Implementiere TODO 4 für den Steckbrief!") + + except (ValueError, TypeError) as fehler: + print(f"\n💡 Hinweis / Fehleingabe: {fehler}") + print("Achte darauf, bei Zahlen nur Ziffern und bei Kommazahlen einen Punkt '.' einzugeben!") diff --git a/g03_ein_und_ausgabe/index.html b/g03_ein_und_ausgabe/index.html new file mode 100644 index 0000000..224f3b4 --- /dev/null +++ b/g03_ein_und_ausgabe/index.html @@ -0,0 +1,259 @@ + + + + + + Kapitel G03: Interaktive Ein- & Ausgabe + + + + +
+
+ +
Grundlagen G03 • Schulabgleich 07.0
+

Interaktive Ein- & Ausgabe 💬⌨️

+

Lerne, wie du Benutzereingaben mit input() verarbeitest, Typen umwandelst und Daten mit modernen f-Strings elegant formatierst.

+
+
+ +
+ + +
+

1. Die Dialog-Analogie: Wenn Programme sprechen lernen 🗣️

+ +

+ Bisher haben unsere Programme Werte starr im Code festgelegt. In der echten Welt müssen Programme jedoch auf Eingaben von Nutzerinnen und Nutzern reagieren: Ein Chatbot, ein Online-Shop-Kassenbon oder ein Videospiel-Menü. +

+ +
+
🎭 Die Dialog-Analogie (Das Interview):
+ Stell dir dein Programm wie eine Interviewerin vor: +
1. Frage stellen: Das Programm fragt höflich nach Informationen (input("Name: ")). +
2. Zuhören & Notieren: Das Programm nimmt die Antwort entgegen (immer als Text!). +
3. Verarbeiten: Das Programm wandelt Zahlen um und rechnet damit. +
4. Antworten: Das Programm gibt das schön formatierte Ergebnis zurück (print(...) mit f-String). +
+
+ + +
+

2. Tastatureingabe mit input() & Typumwandlung ⚠️

+ +

+ Mit der eingebauten Funktion input() pausiert Python die Programmausführung und wartet auf Tastatureingaben im Terminal. +

+ +
+
+
+ + + +
+ Python Eingabe +
+
name = input("Wie lautet dein Name? ")
+print(f"Hallo {name}!")
+
+ +
+
🚨 Die goldene Regel: input() liefert IMMER einen String (str)!
+ Selbst wenn die Benutzerin 42 eintippt, speichert Python den Text "42". +
Wenn du versuchst, "42" + 5 zu rechnen, stürzt Python mit einem TypeError ab! +
+ +

Typkonvertierung (Type Casting):

+
+
Ganze Zahlen und Kommazahlen umwandeln
+
# 1. Ganze Zahl (int):
+alter_text = input("Dein Alter: ")      # z.B. "18"
+alter = int(alter_text)                 # Jetzt als Ganzzahl 18
+tage = alter * 365                      # Rechnen klappt problemlos!
+
+# Direkt in einer Zeile kombiniert:
+alter = int(input("Dein Alter: "))
+
+# 2. Kommazahl (float):
+preis = float(input("Preis in Euro (z.B. 4.99): "))
+brutto = preis * 1.20
+
+
+ + +
+

3. Moderne f-Strings & Formatierungs-Cheat-Sheet 📄

+ +

+ In modernem Python (ab Version 3.6) sind f-Strings (Formatted String Literals) der absolute Standard. Setze ein f direkt vor die Anführungszeichen, und du kannst Variablen und Ausdrücke direkt in {...} einbetten. +

+ +
+
Beispiel: Ausdrücke direkt im f-String berechnen
+
name = "Mia"
+alter = 16
+print(f"Hallo {name}, nächstes Jahr bist du schon {alter + 1} Jahre alt!")
+
+ +

Format-Spezifizierer (Format Codes):

+

Hinter einem Doppelpunkt : kannst du das Aussehen von Zahlen exakt steuern:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeBedeutungBeispielErgebnis
{x:.2f}Genau 2 Nachkommastellen (z.B. für Geldbeträge)f"{2.5:.2f} €""2.50 €"
{x:.1f}Genau 1 Nachkommastelle (z.B. für BMI / Notenschnitt)f"{23.148:.1f}""23.1"
{x:.1%}Prozentwert (multipliziert automatisch mit 100)f"{0.19:.1%}""19.0%"
{n:04d}Ganzzahl mit führenden Nullen (mind. 4 Ziffern)f"{7:04d}""0007"
{s:>10}Rechtsbündig in einer 10-Zeichen-Spaltef'{"Kaffee":>10}'" Kaffee"
+
+ +

4. print() mit Parametern steuern (sep & end)

+
+
sep und end Parameter
+
# sep ändert das Trennzeichen zwischen Argumenten:
+print("2026", "08", "29", sep="-")
+# Ausgabe: 2026-08-29
+
+# end verhindert den Zeilenumbruch:
+print("Lade Daten", end="... ")
+print("Fertig!")
+# Ausgabe: Lade Daten... Fertig!
+
+
+ + +
+

4. 📝 Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei g03_ein_und_ausgabe/aufgabe.py und setze folgende 4 Funktionen um:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 3 (Rechnungsposten) +
+

Multipliziere zuerst gesamtpreis = anzahl * einzelpreis. Setze dann beide Werte in den f-String ein und formatiere sie mit :.2f:

+
+
gesamtpreis = anzahl * einzelpreis
+return f"{anzahl}x {artikel} à {einzelpreis:.2f} € = {gesamtpreis:.2f} €"
+
+
+
+ +
+ 🔍 Tipp zu TODO 4 (BMI & Potenzen in Python) +
+

In Python rechnet man Potenzen mit dem Doppelstern-Operator **:

+
+
bmi = gewicht_kg / (groesse_m ** 2)
+return f"Steckbrief: {name} | Größe: {groesse_m:.2f} m | Gewicht: {gewicht_kg:.1f} kg | BMI: {bmi:.1f}"
+
+
+
+
+ + +
+

5. 🧪 Automatische Tests & Überprüfung

+

Führe das Testskript im Terminal aus, um deine Funktionen zu prüfen:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ +

Um das interaktive Programm mit echten Tastatureingaben auszuprobieren, führe aus:

+
+
Interaktiver Test
+
python3 aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g03_ein_und_ausgabe/musterloesung.py b/g03_ein_und_ausgabe/musterloesung.py new file mode 100644 index 0000000..deb5f9b --- /dev/null +++ b/g03_ein_und_ausgabe/musterloesung.py @@ -0,0 +1,58 @@ +""" +Kapitel G03: Interaktive Ein- & Ausgabe (Schulabgleich 07.0) +============================================================ +Musterlösung mit sauberen f-Strings, Typkonvertierungen und Formatangaben. +""" + +# ============================================================================== +# TODO 1: Begrüßungstext mit f-String +# ============================================================================== +def begruessungs_text(name: str, stadt: str) -> str: + """Gibt einen freundlichen Begrüßungstext mit f-String zurück.""" + return f"Hallo {name}, herzlich willkommen in {stadt}!" + + +# ============================================================================== +# TODO 2: Alter in Tagen berechnen +# ============================================================================== +def berechne_alter_in_tagen(jahre: int) -> int: + """Berechnet das ungefähre Alter in Tagen (365 Tage pro Jahr).""" + return jahre * 365 + + +# ============================================================================== +# TODO 3: Rechnungsposten formatieren +# ============================================================================== +def formatiere_rechnungsposten(artikel: str, anzahl: int, einzelpreis: float) -> str: + """Formatiert einen Rechnungsposten mit 2 Nachkommastellen für Preise.""" + gesamtpreis = anzahl * einzelpreis + return f"{anzahl}x {artikel} à {einzelpreis:.2f} € = {gesamtpreis:.2f} €" + + +# ============================================================================== +# TODO 4: Steckbrief mit BMI-Berechnung +# ============================================================================== +def steckbrief(name: str, groesse_m: float, gewicht_kg: float) -> str: + """Berechnet den BMI und gibt einen formatierten Steckbrief zurück.""" + bmi = gewicht_kg / (groesse_m ** 2) + return f"Steckbrief: {name} | Größe: {groesse_m:.2f} m | Gewicht: {gewicht_kg:.1f} kg | BMI: {bmi:.1f}" + + +# ============================================================================== +# Interaktives Hauptprogramm +# ============================================================================== +if __name__ == "__main__": + print("=" * 60) + print("💬 INTERAKTIVE EIN- & AUSGABE (MUSTERLÖSUNG)") + print("=" * 60) + + # 1. Demonstration von print() Parametern: + print("Python", "ist", "klasse!", sep=" - ") + print("Lade Daten", end="... ") + print("Fertig!\n") + + # 2. Demonstration der Funktionen: + print(begruessungs_text("Anna", "Wien")) + print(f"18 Jahre entsprechen ca. {berechne_alter_in_tagen(18)} Tagen.") + print(formatiere_rechnungsposten("Kaffee", 3, 2.5)) + print(steckbrief("Alex", 1.80, 75.0)) diff --git a/g03_ein_und_ausgabe/test_aufgabe.py b/g03_ein_und_ausgabe/test_aufgabe.py new file mode 100644 index 0000000..3d431eb --- /dev/null +++ b/g03_ein_und_ausgabe/test_aufgabe.py @@ -0,0 +1,145 @@ +import unittest +import sys +from pathlib import Path +import importlib.util + +# Pfad zum aktuellen Kapitelverzeichnis +CHAPTER_DIR = Path(__file__).parent + + +def lade_modul(modul_name="aufgabe"): + """Lädt das Modul (aufgabe oder musterloesung) isoliert aus dem Kapitelverzeichnis.""" + dateipfad = CHAPTER_DIR / f"{modul_name}.py" + if not dateipfad.exists(): + raise FileNotFoundError(f"Datei '{dateipfad}' wurde nicht gefunden.") + spec = importlib.util.spec_from_file_location(f"g03_{modul_name}", dateipfad) + modul = importlib.util.module_from_spec(spec) + spec.loader.exec_module(modul) + return modul + + +class TestKapitelG03(unittest.TestCase): + """ + Testsuite für Kapitel G03: Interaktive Ein- & Ausgabe + Überprüft f-String-Formatierungen, Berechnungen und Typkonvertierungen. + """ + + @classmethod + def setUpClass(cls): + cls.target_modul = getattr(cls, "modul_name", "aufgabe") + try: + cls.mod = lade_modul(cls.target_modul) + except SyntaxError as e: + cls.mod = None + cls.import_error = f"SyntaxError in '{cls.target_modul}.py': {e}" + except Exception as e: + cls.mod = None + cls.import_error = f"Fehler beim Laden von '{cls.target_modul}.py': {e}" + + def _get_modul(self): + if getattr(self, "mod", None) is None: + err = getattr(self, "import_error", f"{self.target_modul}.py konnte nicht geladen werden") + self.fail(f"❌ {err}") + return self.mod + + def test_01_begruessungs_text(self): + """Prüft TODO 1: begruessungs_text(name, stadt).""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "begruessungs_text"), + "Fehler: Funktion 'begruessungs_text' fehlt in aufgabe.py!" + ) + + ergebnis1 = mod.begruessungs_text("Anna", "Wien") + erwartet1 = "Hallo Anna, herzlich willkommen in Wien!" + self.assertEqual( + ergebnis1, + erwartet1, + f"\n❌ Falsche Begrüßung!\nErwartet: '{erwartet1}'\nErhalten: '{ergebnis1}'" + ) + + ergebnis2 = mod.begruessungs_text("Maximilian", "München") + erwartet2 = "Hallo Maximilian, herzlich willkommen in München!" + self.assertEqual( + ergebnis2, + erwartet2, + f"\n❌ Falsche Begrüßung!\nErwartet: '{erwartet2}'\nErhalten: '{ergebnis2}'" + ) + + def test_02_berechne_alter_in_tagen(self): + """Prüft TODO 2: berechne_alter_in_tagen(jahre).""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "berechne_alter_in_tagen"), + "Fehler: Funktion 'berechne_alter_in_tagen' fehlt in aufgabe.py!" + ) + + self.assertEqual(mod.berechne_alter_in_tagen(18), 6570, "18 Jahre * 365 sollte 6570 Tage ergeben!") + self.assertEqual(mod.berechne_alter_in_tagen(1), 365, "1 Jahr * 365 sollte 365 Tage ergeben!") + self.assertEqual(mod.berechne_alter_in_tagen(0), 0, "0 Jahre * 365 sollte 0 Tage ergeben!") + self.assertEqual(mod.berechne_alter_in_tagen(25), 9125, "25 Jahre * 365 sollte 9125 Tage ergeben!") + + def test_03_formatiere_rechnungsposten(self): + """Prüft TODO 3: formatiere_rechnungsposten(artikel, anzahl, einzelpreis).""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "formatiere_rechnungsposten"), + "Fehler: Funktion 'formatiere_rechnungsposten' fehlt in aufgabe.py!" + ) + + # Test 1: Kaffee + erg1 = mod.formatiere_rechnungsposten("Kaffee", 3, 2.5) + erw1 = "3x Kaffee à 2.50 € = 7.50 €" + self.assertEqual( + erg1, + erw1, + f"\n❌ Formatierungsfehler bei Rechnungsposten!\nErwartet: '{erw1}'\nErhalten: '{erg1}'\n💡 Tipp: Achte auf ':.2f' für zwei Nachkommastellen bei beiden Preisen!" + ) + + # Test 2: Buch + erg2 = mod.formatiere_rechnungsposten("Buch", 1, 19.99) + erw2 = "1x Buch à 19.99 € = 19.99 €" + self.assertEqual( + erg2, + erw2, + f"\n❌ Formatierungsfehler bei Rechnungsposten!\nErwartet: '{erw2}'\nErhalten: '{erg2}'" + ) + + # Test 3: Semmel (Rundung / Nullen am Ende z.B. 0.40 € und 2.00 €) + erg3 = mod.formatiere_rechnungsposten("Semmel", 5, 0.4) + erw3 = "5x Semmel à 0.40 € = 2.00 €" + self.assertEqual( + erg3, + erw3, + f"\n❌ Formatierungsfehler bei Rechnungsposten!\nErwartet: '{erw3}'\nErhalten: '{erg3}'" + ) + + def test_04_steckbrief_und_bmi(self): + """Prüft TODO 4: steckbrief(name, groesse_m, gewicht_kg).""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "steckbrief"), + "Fehler: Funktion 'steckbrief' fehlt in aufgabe.py!" + ) + + # Test 1: Alex (75 kg / (1.80^2) = 23.1481... -> 23.1) + erg1 = mod.steckbrief("Alex", 1.80, 75.0) + erw1 = "Steckbrief: Alex | Größe: 1.80 m | Gewicht: 75.0 kg | BMI: 23.1" + self.assertEqual( + erg1, + erw1, + f"\n❌ Formatierungsfehler im Steckbrief!\nErwartet: '{erw1}'\nErhalten: '{erg1}'\n💡 Formel: BMI = gewicht / (groesse ** 2) mit ':.1f'" + ) + + # Test 2: Mia (58.5 kg / (1.65^2) = 21.4876... -> 21.5) + erg2 = mod.steckbrief("Mia", 1.65, 58.5) + erw2 = "Steckbrief: Mia | Größe: 1.65 m | Gewicht: 58.5 kg | BMI: 21.5" + self.assertEqual( + erg2, + erw2, + f"\n❌ Formatierungsfehler im Steckbrief!\nErwartet: '{erw2}'\nErhalten: '{erg2}'" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g04_verzweigungen_und_bedingungen/README.md b/g04_verzweigungen_und_bedingungen/README.md new file mode 100644 index 0000000..1670276 --- /dev/null +++ b/g04_verzweigungen_und_bedingungen/README.md @@ -0,0 +1,129 @@ +# Kapitel G04: Verzweigungen & Bedingungen (Schulabgleich 08.0) 🛤️🔀 + +Herzlich willkommen zum Kapitel **Bedingungen und Verzweigungen**! + +Programme müssen oft Entscheidungen treffen: *Darf die Person schon Auto fahren?*, *Bekommt sie einen Rabatt?*, *Ist heute ein Schaltjahr?*. In Python steuern wir solche Entscheidungen mit `if`, `elif` und `else`. + +--- + +## 🎯 Lernziele + +1. **Entscheidungsstrukturen verstehen:** `if`, `elif` und `else` für logische Programmabläufe einsetzen. +2. **Die Eisenbahn-Weichen-Analogie begreifen:** Sobald die erste Bedingung zutrifft, werden alle nachfolgenden `elif`/`else`-Zweige übersprungen. +3. **Vergleichsoperatoren beherrschen:** `==`, `!=`, `<`, `>`, `<=`, `>=`. +4. **Logische Operatoren kombinieren:** `and` (und), `or` (oder), `not` (nicht / Verneinung). +5. **Klammern & Rangfolge anwenden:** Komplexe Bedingungen mit `()` sicher strukturieren. +6. **Flache vs. tief verschachtelte Bedingungen:** Sauberen, lesbaren Code nach dem KISS-Prinzip schreiben. + +--- + +## 💡 Theorie & Wichtige Konzepte + +### 1. Die Weichen-Analogie (Eisenbahn-Gleis) 🛤️ + +Stell dir dein Programm wie einen Zug auf den Schienen vor. An einer Weiche entscheidet das Signal (`Bedingung`), auf welches Gleis der Zug abbiegt: + +```python +if alter < 12: + preis = 6.0 # Gleis 1 (Kind) +elif alter >= 65: + preis = 8.5 # Gleis 2 (Senior) +elif ist_student: + preis = 9.5 # Gleis 3 (Student) +else: + preis = 12.0 # Ausweichgleis (Standard-Erwachsener) +``` + +> 🔑 **Wichtig:** Python prüft die Bedingungen von oben nach unten. Der **erste** Block, dessen Bedingung `True` ergibt, wird ausgeführt. Danach verlässt Python die gesamte Weiche! + +--- + +### 2. Die Vergleichsoperatoren + +| Operator | Bedeutung | Beispiel | Ergebnis | +| :---: | :--- | :--- | :--- | +| `==` | Ist gleich? *(Vorsicht: Nicht mit Zuweisung `=` verwechseln!)* | `5 == 5` | `True` | +| `!=` | Ist ungleich? | `5 != 3` | `True` | +| `<` | Kleiner als | `10 < 20` | `True` | +| `>` | Größer als | `10 > 20` | `False` | +| `<=` | Kleiner oder gleich (Grenzwert inkludiert) | `10 <= 10` | `True` | +| `>=` | Größer oder gleich (Grenzwert inkludiert) | `18 >= 18` | `True` | + +--- + +### 3. Logische Operatoren & Wahrheitstabellen + +Oft reicht eine einzelne Bedingung nicht aus. Mit logischen Operatoren verknüpfst du mehrere Bedingungen: + +#### `and` (Logisches UND – Beide müssen wahr sein): +| Bedingung A | Bedingung B | `A and B` | +| :---: | :---: | :---: | +| `True` | `True` | **`True`** | +| `True` | `False` | `False` | +| `False` | `True` | `False` | +| `False` | `False` | `False` | + +#### `or` (Logisches ODER – Mindestens eins muss wahr sein): +| Bedingung A | Bedingung B | `A or B` | +| :---: | :---: | :---: | +| `True` | `True` | **`True`** | +| `True` | `False` | **`True`** | +| `False` | `True` | **`True`** | +| `False` | `False` | `False` | + +#### `not` (Logisches NICHT – Dreht den Wahrheitswert um): +- `not True` ergibt `False` +- `not False` ergibt `True` + +--- + +### 4. Das Schaltjahr-Rätsel 📅 + +Ein schönes Praxisbeispiel für logische Verknüpfungen ist die gregorianische Schaltjahr-Regel: +1. Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist (`jahr % 4 == 0`). +2. Säkularjahre (Jahrhunderte wie 1900, 2100) sind **keine** Schaltjahre (`jahr % 100 != 0`), +3. **außer** sie sind durch 400 teilbar (wie 2000, 2400: `jahr % 400 == 0`). + +In Python formuliert: +```python +ist_schalt = (jahr % 4 == 0 and jahr % 100 != 0) or (jahr % 400 == 0) +``` + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne `g04_verzweigungen_und_bedingungen/aufgabe.py` und implementiere die 4 Funktionen: + +1. **TODO 1: Kinokarten-Preisrechner** + - `ticket_preis(alter: int, ist_student: bool) -> float` + - Alter < 12: `6.0` € + - Alter >= 65: `8.5` € + - 12 bis 64 Jahre: Wenn `ist_student` -> `9.5` €, sonst -> `12.0` € + +2. **TODO 2: Schulnoten-Ermittlung** + - `schulnote_text(punkte: int) -> str` + - `< 0` oder `> 100`: `"Ungültige Punktezahl"` + - `>= 90`: `"Sehr gut"`, `>= 75`: `"Gut"`, `>= 60`: `"Befriedigend"`, `>= 50`: `"Genügend"`, `< 50`: `"Nicht genügend"` + +3. **TODO 3: Schaltjahr-Erkennung** + - `ist_schaltjahr(jahr: int) -> bool` + - `(jahr % 4 == 0 and jahr % 100 != 0) or (jahr % 400 == 0)` + +4. **TODO 4: Achterbahn-Zulassung** + - `kann_achterbahn_fahren(groesse_cm: int, begleitung_erwachsen: bool) -> bool` + - Ab 140 cm: immer `True` + - Ab 120 cm bis 139 cm: `True`, wenn `begleitung_erwachsen == True`, sonst `False` + - Unter 120 cm: immer `False` + +--- + +## 🧪 Tests ausführen + +Überprüfe deine Lösung im Terminal mit: + +```bash +python3 test_aufgabe.py +``` + +Wenn alle 4 Tests mit **`OK`** durchlaufen, hast du das Kapitel gemeistert! 🎉 diff --git a/g04_verzweigungen_und_bedingungen/aufgabe.py b/g04_verzweigungen_und_bedingungen/aufgabe.py new file mode 100644 index 0000000..013d79d --- /dev/null +++ b/g04_verzweigungen_und_bedingungen/aufgabe.py @@ -0,0 +1,167 @@ +""" +Kapitel G04: Verzweigungen & Bedingungen (Schulabgleich 08.0) +============================================================ + +In diesem Kapitel lernst du: +1. Wie du Entscheidungen mit if, elif und else programmierst. +2. Vergleichsoperatoren: ==, !=, <, >, <=, >=. +3. Logische Operatoren: and, or, not. +4. Flache elif-Ketten vs. tief verschachtelte Bedingungen. +5. Wie du komplexe Geschäftslogik und Grenzwerte sauber implementierst. + +Bearbeite die Aufgaben TODO 1 bis TODO 4. +Führe danach die Tests aus mit: + python3 test_aufgabe.py +""" + +# ============================================================================== +# TODO 1: Kinokarten-Preisrechner +# +# Schreibe eine Funktion 'ticket_preis(alter: int, ist_student: bool) -> float'. +# Sie berechnet den Ticketpreis nach folgenden Tarif-Regeln: +# - Kinder unter 12 Jahren (alter < 12): 6.00 € (Kindertarif) +# - Senioren ab 65 Jahren (alter >= 65): 8.50 € (Seniorentarif) +# - Alle anderen Personen (12 bis 64 Jahre): +# - Wenn ist_student True ist: 9.50 € (Studenten-/Ermäßigungstarif) +# - Wenn ist_student False ist: 12.00 € (Regulärer Erwachsenentarif) +# +# Beispiele: +# ticket_preis(8, False) -> 6.0 +# ticket_preis(70, False) -> 8.5 +# ticket_preis(22, True) -> 9.5 +# ticket_preis(30, False) -> 12.0 +# ============================================================================== + +def ticket_preis(alter: int, ist_student: bool) -> float: + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Schulnoten-Ermittlung +# +# Schreibe eine Funktion 'schulnote_text(punkte: int) -> str'. +# Sie ordnet einer erreichten Punktezahl (0 bis 100) die passende Textnote zu: +# - 90 bis 100 Punkte (punkte >= 90): "Sehr gut" +# - 75 bis 89 Punkte (punkte >= 75): "Gut" +# - 60 bis 74 Punkte (punkte >= 60): "Befriedigend" +# - 50 bis 59 Punkte (punkte >= 50): "Genügend" +# - 0 bis 49 Punkte (punkte < 50): "Nicht genügend" +# - Werte unter 0 oder über 100: "Ungültige Punktezahl" +# +# Beispiele: +# schulnote_text(95) -> "Sehr gut" +# schulnote_text(75) -> "Gut" +# schulnote_text(49) -> "Nicht genügend" +# schulnote_text(110) -> "Ungültige Punktezahl" +# schulnote_text(-5) -> "Ungültige Punktezahl" +# ============================================================================== + +def schulnote_text(punkte: int) -> str: + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Schaltjahr-Erkennung +# +# Schreibe eine Funktion 'ist_schaltjahr(jahr: int) -> bool'. +# Die Funktion soll prüfen, ob ein übergebenes Kalenderjahr ein Schaltjahr ist. +# +# Die Gregorianische Schaltjahr-Regel lautet: +# 1. Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist (jahr % 4 == 0). +# 2. AUSNAHME: Ist es durch 100 teilbar (jahr % 100 == 0), ist es KEIN Schaltjahr... +# 3. AUSNAHME DER AUSNAHME: ...es sei denn, es ist auch durch 400 teilbar (jahr % 400 == 0)! +# +# Beispiele: +# ist_schaltjahr(2024) -> True (durch 4 teilbar, nicht durch 100) +# ist_schaltjahr(2023) -> False (nicht durch 4 teilbar) +# ist_schaltjahr(1900) -> False (durch 100 teilbar, aber nicht durch 400) +# ist_schaltjahr(2000) -> True (durch 400 teilbar) +# ============================================================================== + +def ist_schaltjahr(jahr: int) -> bool: + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Achterbahn-Zulassung +# +# Schreibe eine Funktion 'kann_achterbahn_fahren(groesse_cm: int, begleitung_erwachsen: bool) -> bool'. +# Ein Freizeitpark hat folgende Sicherheitsbestimmungen: +# - Ab 140 cm Körpergröße darf jede Person alleine mitfahren (True). +# - Zwischen 120 cm und 139 cm darf eine Person NUR mitfahren, wenn +# eine erwachsene Begleitperson dabei ist (begleitung_erwachsen == True). +# - Unter 120 cm Körpergröße darf niemand mitfahren (immer False). +# +# Beispiele: +# kann_achterbahn_fahren(145, False) -> True (Groß genug für Alleinfahrt) +# kann_achterbahn_fahren(130, True) -> True (Mit Begleitung erlaubt) +# kann_achterbahn_fahren(130, False) -> False (Ohne Begleitung verboten) +# kann_achterbahn_fahren(115, True) -> False (Zu klein, auch mit Begleitung) +# ============================================================================== + +def kann_achterbahn_fahren(groesse_cm: int, begleitung_erwachsen: bool) -> bool: + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# Interaktives Hauptprogramm zum Ausprobieren: +# (Führe dieses Skript aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("=" * 60) + print("🎢 BEDINGUNGEN & VERZWEIGUNGEN DEMO") + print("=" * 60) + + # 1. Kino-Test + print("\n🎟️ --- Kino-Preisrechner ---") + try: + alter_eingabe = int(input("Alter des Besuchers: ")) + student_eingabe = input("Student/Schüler? (j/n): ").strip().lower() == "j" + preis = ticket_preis(alter_eingabe, student_eingabe) + if preis is not None: + print(f"Ticketpreis: {preis:.2f} €") + else: + print("Implementiere TODO 1 für die Preisberechnung!") + except ValueError: + print("Ungültige Alterseingabe!") + + # 2. Noten-Test + print("\n📝 --- Notenspiegel-Rechner ---") + try: + punkte_eingabe = int(input("Erreichte Punkte (0-100): ")) + note = schulnote_text(punkte_eingabe) + if note is not None: + print(f"Ergebnis: {note}") + else: + print("Implementiere TODO 2 für die Notenberechnung!") + except ValueError: + print("Ungültige Punktezahl!") + + # 3. Schaltjahr-Test + print("\n📅 --- Schaltjahr-Prüfer ---") + try: + jahr_eingabe = int(input("Jahr eingeben (z.B. 2024): ")) + schalt = ist_schaltjahr(jahr_eingabe) + if schalt is not None: + print(f"Ist {jahr_eingabe} ein Schaltjahr? -> {'Ja! 🎉' if schalt else 'Nein.'}") + else: + print("Implementiere TODO 3 für die Schaltjahrprüfung!") + except ValueError: + print("Ungültiges Jahr!") + + # 4. Achterbahn-Test + print("\n🎢 --- Achterbahn-Einlasskontrolle ---") + try: + groesse = int(input("Körpergröße in cm (z.B. 135): ")) + begl = input("Erwachsene Begleitung dabei? (j/n): ").strip().lower() == "j" + darf_fahren = kann_achterbahn_fahren(groesse, begl) + if darf_fahren is not None: + print(f"Darf Achterbahn fahren? -> {'Ja, viel Spaß! 🎢' if darf_fahren else 'Leider nein 🛑'}") + else: + print("Implementiere TODO 4 für die Achterbahn-Zulassung!") + except ValueError: + print("Ungültige Größenangabe!") diff --git a/g04_verzweigungen_und_bedingungen/index.html b/g04_verzweigungen_und_bedingungen/index.html new file mode 100644 index 0000000..7a99c28 --- /dev/null +++ b/g04_verzweigungen_und_bedingungen/index.html @@ -0,0 +1,291 @@ + + + + + + Kapitel G04: Bedingungen & Verzweigungen + + + + +
+
+ +
Grundlagen G04 • Schulabgleich 08.0
+

Bedingungen & Verzweigungen 🛤️🔀

+

Lerne, wie dein Code mit if, elif und else intelligente Entscheidungen trifft und wie du logische Operatoren meisterst.

+
+
+ +
+ + +
+

1. Die Eisenbahn-Weichen-Analogie 🚂

+ +

+ Code wird standardmäßig von oben nach unten Zeile für Zeile ausgeführt. Doch oft soll ein Programm je nach Situation einen anderen Pfad einschlagen: Ein Kinokassen-System berechnet für Kinder einen anderen Preis als für Erwachsene. +

+ +
+
🛤️ Das Eisenbahngleis & die Weichen:
+ Stell dir den Programmlauf wie einen Zug vor, der auf ein Weichensystem zufährt: +
1. Das Signal (Bedingung): Die Weiche prüft: "Ist das Alter unter 12?" +
2. Grünes Signal (True): Der Zug biegt auf das Kindertarif-Gleis ab und führt dessen Code aus. +
3. Rotes Signal (False): Der Zug fährt weiter zur nächsten Weiche (elif). +
4. Das Ausweichgleis (else): Wenn keines der Signale grün war, nimmt der Zug das Standardgleis. +
5. Weiterfahrt: Hinter der Weichenanlage laufen alle Gleise wieder auf der Hauptstrecke zusammen! +
+
+ + +
+

2. Syntax von if / elif / else & Vergleichsoperatoren

+ +

+ In Python werden Verzweigungen mit Doppelpunkt : eingeleitet und der zugehörige Codeblock muss genau 4 Leerzeichen eingerückt sein. +

+ +
+
+
+ + + +
+ Kino-Tarif-Weiche +
+
if alter < 12:
+    preis = 6.00   # 4 Leerzeichen Einrückung
+elif alter >= 65:
+    preis = 8.50
+elif ist_student:
+    preis = 9.50
+else:
+    preis = 12.00
+
+ +
+
⚠️ Häufige Fehlerquelle: = vs. ==
+ - = (einfaches Istgleich) ist eine Zuweisung: punkte = 10.
+ - == (doppeltes Istgleich) ist ein Vergleich: if punkte == 10:. +
+ +

Die 6 Vergleichsoperatoren:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorBedeutungBeispielErgebnis
==Gleich5 == 5True
!=Ungleich5 != 3True
< / >Kleiner als / Größer als10 < 20True
<= / >=Kleiner gleich / Größer gleich18 >= 18True
+
+
+ + +
+

3. Logische Operatoren & Wahrheitstabellen 🧪

+ +

+ Bedingungen lassen sich mit and, or und not kombinieren: +

+ +
+ + +
+

and (Beide wahr)

+ + + + + + + + + + + + + + +
ABA and B
TrueTrueTrue
TrueFalseFalse
FalseTrueFalse
FalseFalseFalse
+
+ + +
+

or (Mind. eins wahr)

+ + + + + + + + + + + + + + +
ABA or B
TrueTrueTrue
TrueFalseTrue
FalseTrueTrue
FalseFalseFalse
+
+ + +
+

not (Verneinung)

+ + + + + + + + + + + +
Anot A
TrueFalse
FalseTrue
+
+ +
+ +
+
💡 Tipp: Klammern setzen für klare Lesbarkeit
+ Genau wie in der Mathematik gilt: Klammern (...) binden stärker und machen deinen Code absolut unmissverständlich!
+ darf_fahren = (groesse >= 140) or (groesse >= 120 and begleitung) +
+
+ + +
+

4. 📝 Deine Aufgaben in aufgabe.py

+ +

Öffne g04_verzweigungen_und_bedingungen/aufgabe.py und setze folgende 4 Funktionen um:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 2 (Reihenfolge der Notenstufen) +
+

Prüfe zuerst die ungültigen Werte (punkte < 0 or punkte > 100). Danach kannst du bequem von oben nach unten mit >= 90, >= 75 usw. prüfen!

+
+
if punkte < 0 or punkte > 100:
+    return "Ungültige Punktezahl"
+elif punkte >= 90:
+    return "Sehr gut"
+# ...
+
+
+
+ +
+ 🔍 Tipp zu TODO 3 (Modulo % & Schaltjahre) +
+

Mit dem Modulo-Operator % prüfst du die Teilbarkeit (Restwert der Division ist 0):

+
+
if (jahr % 4 == 0 and jahr % 100 != 0) or (jahr % 400 == 0):
+    return True
+return False
+
+
+
+
+ + +
+

5. 🧪 Automatische Tests & Überprüfung

+

Führe das Testskript im Terminal aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ +

Oder teste das interaktive Programm mit eigenen Eingaben:

+
+
Interaktiver Test
+
python3 aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g04_verzweigungen_und_bedingungen/musterloesung.py b/g04_verzweigungen_und_bedingungen/musterloesung.py new file mode 100644 index 0000000..d3a88a9 --- /dev/null +++ b/g04_verzweigungen_und_bedingungen/musterloesung.py @@ -0,0 +1,97 @@ +""" +Kapitel G04: Verzweigungen & Bedingungen (Schulabgleich 08.0) +============================================================ +Musterlösung mit sauberen if/elif/else-Verzweigungen und logischen Operatoren. +""" + +# ============================================================================== +# TODO 1: Kinokarten-Preisrechner +# ============================================================================== +def ticket_preis(alter: int, ist_student: bool) -> float: + """Berechnet den Kinokartenpreis basierend auf Alter und Schüler/Studenten-Status.""" + if alter < 12: + return 6.0 + elif alter >= 65: + return 8.5 + elif ist_student: + return 9.5 + else: + return 12.0 + + +# ============================================================================== +# TODO 2: Schulnoten-Ermittlung +# ============================================================================== +def schulnote_text(punkte: int) -> str: + """Gibt die passende Textnote für eine Punktezahl zwischen 0 und 100 zurück.""" + if punkte < 0 or punkte > 100: + return "Ungültige Punktezahl" + elif punkte >= 90: + return "Sehr gut" + elif punkte >= 75: + return "Gut" + elif punkte >= 60: + return "Befriedigend" + elif punkte >= 50: + return "Genügend" + else: + return "Nicht genügend" + + +# ============================================================================== +# TODO 3: Schaltjahr-Erkennung +# ============================================================================== +def ist_schaltjahr(jahr: int) -> bool: + """ + Ermittelt nach den Gregorianischen Kalenderregeln, ob ein Jahr ein Schaltjahr ist. + Teilbar durch 4, aber nicht durch 100 (außer wenn durch 400 teilbar). + """ + if (jahr % 4 == 0 and jahr % 100 != 0) or (jahr % 400 == 0): + return True + return False + + +# ============================================================================== +# TODO 4: Achterbahn-Zulassung +# ============================================================================== +def kann_achterbahn_fahren(groesse_cm: int, begleitung_erwachsen: bool) -> bool: + """Prüft, ob eine Person (mit oder ohne Begleitung) groß genug für die Fahrt ist.""" + if groesse_cm >= 140: + return True + elif groesse_cm >= 120 and begleitung_erwachsen: + return True + else: + return False + + +# ============================================================================== +# Interaktives Hauptprogramm +# ============================================================================== +if __name__ == "__main__": + print("=" * 60) + print("🎢 BEDINGUNGEN & VERZWEIGUNGEN (MUSTERLÖSUNG)") + print("=" * 60) + + print("Kino:", ticket_preis(8, False), "€ (Kind)") + print("Kino:", ticket_preis(20, True), "€ (Student)") + print("Kino:", ticket_preis(30, False), "€ (Erwachsen)") + print("Kino:", ticket_preis(70, False), "€ (Senior)") + + print("\nNoten:") + print("95 Punkte:", schulnote_text(95)) + print("75 Punkte:", schulnote_text(75)) + print("50 Punkte:", schulnote_text(50)) + print("30 Punkte:", schulnote_text(30)) + print("110 Punkte:", schulnote_text(110)) + + print("\nSchaltjahre:") + print("2024:", ist_schaltjahr(2024)) + print("2023:", ist_schaltjahr(2023)) + print("1900:", ist_schaltjahr(1900)) + print("2000:", ist_schaltjahr(2000)) + + print("\nAchterbahn:") + print("145cm alleine:", kann_achterbahn_fahren(145, False)) + print("130cm mit Begleitung:", kann_achterbahn_fahren(130, True)) + print("130cm alleine:", kann_achterbahn_fahren(130, False)) + print("110cm mit Begleitung:", kann_achterbahn_fahren(110, True)) diff --git a/g04_verzweigungen_und_bedingungen/test_aufgabe.py b/g04_verzweigungen_und_bedingungen/test_aufgabe.py new file mode 100644 index 0000000..da3e0fb --- /dev/null +++ b/g04_verzweigungen_und_bedingungen/test_aufgabe.py @@ -0,0 +1,160 @@ +import unittest +import sys +from pathlib import Path +import importlib.util + +# Pfad zum aktuellen Kapitelverzeichnis +CHAPTER_DIR = Path(__file__).parent + + +def lade_modul(modul_name="aufgabe"): + """Lädt das Modul (aufgabe oder musterloesung) isoliert aus dem Kapitelverzeichnis.""" + dateipfad = CHAPTER_DIR / f"{modul_name}.py" + if not dateipfad.exists(): + raise FileNotFoundError(f"Datei '{dateipfad}' wurde nicht gefunden.") + spec = importlib.util.spec_from_file_location(f"g04_{modul_name}", dateipfad) + modul = importlib.util.module_from_spec(spec) + spec.loader.exec_module(modul) + return modul + + +class TestKapitelG04(unittest.TestCase): + """ + Testsuite für Kapitel G04: Verzweigungen & Bedingungen + Überprüft if/elif/else-Verzweigungen, Grenzwerte und logische Operatoren. + """ + + @classmethod + def setUpClass(cls): + cls.target_modul = getattr(cls, "modul_name", "aufgabe") + try: + cls.mod = lade_modul(cls.target_modul) + except SyntaxError as e: + cls.mod = None + cls.import_error = f"SyntaxError in '{cls.target_modul}.py': {e}" + except Exception as e: + cls.mod = None + cls.import_error = f"Fehler beim Laden von '{cls.target_modul}.py': {e}" + + def _get_modul(self): + if getattr(self, "mod", None) is None: + err = getattr(self, "import_error", f"{self.target_modul}.py konnte nicht geladen werden") + self.fail(f"❌ {err}") + return self.mod + + def test_01_ticket_preis_alle_tarife(self): + """Prüft TODO 1: ticket_preis(alter, ist_student) für alle Altersgruppen.""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "ticket_preis"), + "Fehler: Funktion 'ticket_preis' fehlt in aufgabe.py!" + ) + + # Kindertarif (< 12 Jahre): 6.00 € + self.assertEqual(mod.ticket_preis(5, False), 6.0, "Alter 5 sollte 6.00 € kosten (Kindertarif)!") + self.assertEqual(mod.ticket_preis(11, False), 6.0, "Alter 11 sollte 6.00 € kosten (Kindertarif)!") + self.assertEqual(mod.ticket_preis(11, True), 6.0, "Kinder zahlen immer 6.00 €!") + + # Seniorentarif (>= 65 Jahre): 8.50 € + self.assertEqual(mod.ticket_preis(65, False), 8.5, "Alter 65 sollte 8.50 € kosten (Seniorentarif)!") + self.assertEqual(mod.ticket_preis(80, False), 8.5, "Alter 80 sollte 8.50 € kosten (Seniorentarif)!") + self.assertEqual(mod.ticket_preis(70, True), 8.5, "Senioren ab 65 zahlen 8.50 €!") + + # Studenten- / Ermäßigungstarif (12 <= alter < 65 und ist_student == True): 9.50 € + self.assertEqual(mod.ticket_preis(12, True), 9.5, "Schüler/Student mit 12 Jahren sollte 9.50 € kosten!") + self.assertEqual(mod.ticket_preis(22, True), 9.5, "Student mit 22 Jahren sollte 9.50 € kosten!") + self.assertEqual(mod.ticket_preis(64, True), 9.5, "Student mit 64 Jahren sollte 9.50 € kosten!") + + # Regulärer Erwachsenentarif (12 <= alter < 65 und ist_student == False): 12.00 € + self.assertEqual(mod.ticket_preis(12, False), 12.0, "Regulärer Preis ab 12 Jahren ohne Ermäßigung ist 12.00 €!") + self.assertEqual(mod.ticket_preis(35, False), 12.0, "Erwachsener (35) ohne Ermäßigung sollte 12.00 € zahlen!") + self.assertEqual(mod.ticket_preis(64, False), 12.0, "Erwachsener (64) ohne Ermäßigung sollte 12.00 € zahlen!") + + def test_02_schulnote_text_grenzwerte(self): + """Prüft TODO 2: schulnote_text(punkte) inklusive aller Grenzwerte.""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "schulnote_text"), + "Fehler: Funktion 'schulnote_text' fehlt in aufgabe.py!" + ) + + # 90..100: "Sehr gut" + self.assertEqual(mod.schulnote_text(100), "Sehr gut", "100 Punkte müssen 'Sehr gut' ergeben!") + self.assertEqual(mod.schulnote_text(90), "Sehr gut", "Genau 90 Punkte müssen 'Sehr gut' ergeben (Grenzwert)!") + + # 75..89: "Gut" + self.assertEqual(mod.schulnote_text(89), "Gut", "89 Punkte müssen 'Gut' ergeben!") + self.assertEqual(mod.schulnote_text(75), "Gut", "Genau 75 Punkte müssen 'Gut' ergeben (Grenzwert)!") + + # 60..74: "Befriedigend" + self.assertEqual(mod.schulnote_text(74), "Befriedigend", "74 Punkte müssen 'Befriedigend' ergeben!") + self.assertEqual(mod.schulnote_text(60), "Befriedigend", "Genau 60 Punkte müssen 'Befriedigend' ergeben (Grenzwert)!") + + # 50..59: "Genügend" + self.assertEqual(mod.schulnote_text(59), "Genügend", "59 Punkte müssen 'Genügend' ergeben!") + self.assertEqual(mod.schulnote_text(50), "Genügend", "Genau 50 Punkte müssen 'Genügend' ergeben (Grenzwert)!") + + # 0..49: "Nicht genügend" + self.assertEqual(mod.schulnote_text(49), "Nicht genügend", "49 Punkte müssen 'Nicht genügend' ergeben!") + self.assertEqual(mod.schulnote_text(0), "Nicht genügend", "0 Punkte müssen 'Nicht genügend' ergeben!") + + # Ungültig (< 0 oder > 100) + self.assertEqual(mod.schulnote_text(-1), "Ungültige Punktezahl", "-1 Punkt muss 'Ungültige Punktezahl' ergeben!") + self.assertEqual(mod.schulnote_text(101), "Ungültige Punktezahl", "101 Punkte müssen 'Ungültige Punktezahl' ergeben!") + + def test_03_ist_schaltjahr_regeln(self): + """Prüft TODO 3: ist_schaltjahr(jahr) mit allen Gregorianischen Ausnahmen.""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "ist_schaltjahr"), + "Fehler: Funktion 'ist_schaltjahr' fehlt in aufgabe.py!" + ) + + # Normale Schaltjahre (durch 4 teilbar, nicht durch 100): + self.assertTrue(mod.ist_schaltjahr(2024), "2024 ist ein Schaltjahr (durch 4 teilbar)!") + self.assertTrue(mod.ist_schaltjahr(2020), "2020 ist ein Schaltjahr!") + self.assertTrue(mod.ist_schaltjahr(2016), "2016 ist ein Schaltjahr!") + + # Normale Nicht-Schaltjahre (nicht durch 4 teilbar): + self.assertFalse(mod.ist_schaltjahr(2023), "2023 ist kein Schaltjahr!") + self.assertFalse(mod.ist_schaltjahr(2025), "2025 ist kein Schaltjahr!") + + # Säkularjahre / Jahrhundert-Regel: + # Durch 100 teilbar, aber NICHT durch 400 -> KEIN Schaltjahr! + self.assertFalse(mod.ist_schaltjahr(1900), "1900 ist KEIN Schaltjahr (durch 100 teilbar, aber nicht durch 400)!") + self.assertFalse(mod.ist_schaltjahr(2100), "2100 ist KEIN Schaltjahr (durch 100 teilbar, aber nicht durch 400)!") + self.assertFalse(mod.ist_schaltjahr(1800), "1800 ist KEIN Schaltjahr!") + + # 400er-Regel: + # Durch 400 teilbar -> IST ein Schaltjahr! + self.assertTrue(mod.ist_schaltjahr(2000), "2000 IST ein Schaltjahr (durch 400 teilbar)!") + self.assertTrue(mod.ist_schaltjahr(2400), "2400 IST ein Schaltjahr (durch 400 teilbar)!") + self.assertTrue(mod.ist_schaltjahr(1600), "1600 IST ein Schaltjahr (durch 400 teilbar)!") + + def test_04_kann_achterbahn_fahren(self): + """Prüft TODO 4: kann_achterbahn_fahren(groesse_cm, begleitung_erwachsen).""" + mod = self._get_modul() + self.assertTrue( + hasattr(mod, "kann_achterbahn_fahren"), + "Fehler: Funktion 'kann_achterbahn_fahren' fehlt in aufgabe.py!" + ) + + # Ab 140 cm: Immer erlaubt (auch ohne Begleitung) + self.assertTrue(mod.kann_achterbahn_fahren(140, False), "Genau 140 cm darf alleine fahren!") + self.assertTrue(mod.kann_achterbahn_fahren(155, False), "155 cm darf alleine fahren!") + self.assertTrue(mod.kann_achterbahn_fahren(140, True), "140 cm mit Begleitung darf natürlich auch fahren!") + + # 120 bis 139 cm: NUR mit erwachsener Begleitung + self.assertTrue(mod.kann_achterbahn_fahren(120, True), "120 cm mit Begleitung ist erlaubt!") + self.assertTrue(mod.kann_achterbahn_fahren(135, True), "135 cm mit Begleitung ist erlaubt!") + self.assertFalse(mod.kann_achterbahn_fahren(120, False), "120 cm OHNE Begleitung ist verboten!") + self.assertFalse(mod.kann_achterbahn_fahren(139, False), "139 cm OHNE Begleitung ist verboten!") + + # Unter 120 cm: Grundsätzlich verboten (auch mit Begleitung) + self.assertFalse(mod.kann_achterbahn_fahren(119, True), "119 cm ist zu klein, auch mit Begleitung!") + self.assertFalse(mod.kann_achterbahn_fahren(119, False), "119 cm ohne Begleitung ist verboten!") + self.assertFalse(mod.kann_achterbahn_fahren(95, True), "95 cm ist zu klein!") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g05_schleifen_und_wiederholungen/README.md b/g05_schleifen_und_wiederholungen/README.md new file mode 100644 index 0000000..69121b0 --- /dev/null +++ b/g05_schleifen_und_wiederholungen/README.md @@ -0,0 +1,196 @@ +# Kapitel G05: Schleifen & Wiederholungen 🎡🔄 + +Willkommen zu **Kapitel G05** unseres Python-Grundlagen-Lehrpfads! +Dieses Kapitel entspricht den Modulen **09.1 & 09.2** deines Informatik-Skriptums. + +Computer sind genial darin, dieselbe Aufgabe tausende oder millionen Male in Millisekunden auszuführen – ohne müde zu werden oder Fehler zu machen. Das Werkzeug dafür sind **Schleifen (Loops)**! + +--- + +## 🎡 Die Karussell- & Riesenrad-Analogie + +Stell dir einen Jahrmarkt vor: +- **`for`-Schleife (Zählschleife):** Wie ein Karussell mit Wertmarken. Du kaufst dir 5 Runden. Der Automat zählt mit: Runde 1, 2, 3, 4, 5. Nach 5 Runden stoppt das Karussell automatisch. +- **`while`-Schleife (Bedingungsschleife):** Wie ein Riesenrad, das sich dreht, **solange** die Musik spielt. Sobald die Musik stoppt (Bedingung wird `False`), hält das Rad an. + +``` +[ FOR-SCHLEIFE ] [ WHILE-SCHLEIFE ] +"Fahre genau 5 Runden!" "Fahre, SOLANGE Musik läuft!" + ┌───────────┐ ┌───────────┐ + │ Runde 1/5 │ │ Musik da? │ ── Nein ──> Stopp + │ Runde 2/5 │ └─────┬─────┘ + │ Runde 3/5 │ │ Ja + │ Runde 4/5 │ ┌─────▼─────┐ + │ Runde 5/5 │ │ 1 Runde │ + └─────┬─────┘ └─────┬─────┘ + ▼ │ + FERTIG └───────── Wiederholen! +``` + +--- + +## 1. Die Zählschleife: `for` und die `range()`-Funktion + +Wenn du im Voraus weißt, **wie oft** etwas wiederholt werden soll, ist die `for`-Schleife die perfekte Wahl. + +### Wie funktioniert `range()`? + +Die `range()`-Funktion erzeugt eine Zahlenfolge. **Wichtigste Regel:** Das `stop`-Element ist **immer exklusiv** (wird nicht mehr erreicht)! + +``` +range(stop) -> 0 bis stop - 1 +range(start, stop) -> start bis stop - 1 +range(start, stop, step) -> start bis stop - 1 mit Schrittweite step +``` + +#### Visualisierung: +``` +range(1, 6) ──> [ 1, 2, 3, 4, 5 ] (6 ist NICHT dabei!) +range(0, 10, 2) ─> [ 0, 2, 4, 6, 8 ] (nur gerade Zahlen!) +range(5, 0, -1) ─> [ 5, 4, 3, 2, 1 ] (rückwärts zählen!) +``` + +```python +# Einfaches Beispiel: +for i in range(1, 6): + print(f"Runde {i}") +``` + +--- + +## 2. Die Bedingungsschleife: `while` + +Wenn du vorher **nicht genau weißt**, wie viele Durchläufe nötig sind, sondern die Wiederholung an eine **Bedingung** geknüpft ist: + +```python +energie = 3 +while energie > 0: + print(f"Roboter läuft... Energie: {energie}") + energie -= 1 # ⚠️ WICHTIG: Schleifenzähler verändern! + +print("Roboter hat keine Energie mehr.") +``` + +### 🛑 Die Gefahr der Endlosschleife (Infinite Loop) + +Wenn die Bedingung einer `while`-Schleife niemals `False` wird, läuft das Programm für immer und friert ein: + +```python +# 💥 GEFÄHRLICH: Endlosschleife! +x = 1 +while x > 0: + print(x) + # Vergessen, x zu verändern -> x bleibt immer 1 -> Bedingung immer True! +``` + +> **Tipp zum Abbrechen:** Falls dein Programm in einer Endlosschleife festhängt, drücke im Terminal Strg + C (Ctrl+C). + +--- + +## 3. Schleifen steuern: `break` & `continue` + +Manchmal möchtest du eine Schleife vorzeitig beenden oder eine Runde überspringen: + +- **`break` (Die Notbremse):** Verlässt die Schleife **sofort**, egal wie viele Runden noch übrig wären. +- **`continue` (Nächste Runde):** Bricht nur den **aktuellen Durchlauf** ab und springt direkt zur nächsten Runde. + +```python +# Beispiel break: +for zahl in range(1, 10): + if zahl == 5: + print("Treffer! Schleife wird abgebrochen.") + break + print(zahl) # Gibt 1, 2, 3, 4 aus + +# Beispiel continue: +for zahl in range(1, 6): + if zahl == 3: + print("Zahl 3 wird übersprungen!") + continue + print(zahl) # Gibt 1, 2, 4, 5 aus +``` + +--- + +## 4. Das Schleifen-Akkumulator-Muster 🧺 + +Ein **Akkumulator** ist eine Hilfsvariable, die **vor** der Schleife erstellt wird und in jedem Schleifendurchlauf einen neuen Wert aufsammelt. + +```python +# 1. Summen-Akkumulator (Startwert: 0) +summe = 0 +for x in [10, 20, 30]: + summe += x # summe = summe + x +print("Summe:", summe) # 60 + +# 2. Zähler-Akkumulator (Startwert: 0) +anzahl_gerade = 0 +for x in range(1, 11): + if x % 2 == 0: + anzahl_gerade += 1 +print("Gerade Zahlen:", anzahl_gerade) # 5 + +# 3. Produkt-Akkumulator (Startwert: 1, NIEMALS 0!) +produkt = 1 +for x in range(1, 5): + produkt *= x # 1 * 2 * 3 * 4 +print("Produkt:", produkt) # 24 +``` + +--- + +## 5. 🌟 Praxisbeispiele + +### Beispiel A: Kleines Einmaleins (Verschachtelte Schleifen) +```python +for zeile in range(1, 4): + for spalte in range(1, 4): + ergebnis = zeile * spalte + print(f"{zeile}x{spalte}={ergebnis:2d}", end=" ") + print() # Zeilenumbruch +``` + +### Beispiel B: Zahlen-Ratespiel mit `while` und `break` +```python +geheime_zahl = 7 +versuche = 0 + +while True: + versuche += 1 + tipp = int(input("Rate eine Zahl zwischen 1 und 10: ")) + if tipp == geheime_zahl: + print(f"🎉 Richtig erraten nach {versuche} Versuchen!") + break + elif tipp < geheime_zahl: + print("Zu klein!") + else: + print("Zu groß!") +``` + +--- + +## ⚠️ Die 3 häufigsten Anfängerfehler + +1. **Off-by-One Fehler bei `range()`:** + - `range(1, 5)` läuft nur bis 4! Wenn du die 5 inklusive haben willst, schreibe `range(1, 6)`. +2. **Akkumulator in der Schleife zurücksetzen:** + - Wenn du `summe = 0` **in** der Schleife schreibst, wird die Summe in jeder Runde gelöscht! Der Startwert muss **vor** der Schleife stehen. +3. **Endlosschleife bei `while`:** + - Vergiss nicht, die Variable in der `while`-Bedingung innerhalb der Schleife anzupassen (`i += 1` oder `n //= 10`). + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne `aufgabe.py` und bearbeite die Aufgaben: +1. **TODO 1:** `summe_bis(n: int) -> int` – Berechnet $1 + 2 + \dots + n$. +2. **TODO 2:** `fakultaet(n: int) -> int` – Berechnet $n! = 1 \cdot 2 \cdots n$. +3. **TODO 3:** `zaehle_gerade_zahlen(start: int, ende: int) -> int` – Zählt gerade Zahlen. +4. **TODO 4:** `ist_primzahl(n: int) -> bool` – Prüft Primzahlen. +5. **TODO 5:** `quorsumme(n: int) -> int` – Berechnet die Quersumme mit einer `while`-Schleife. + +### Testen: +```bash +python3 test_aufgabe.py +``` +Sobald alle 5 Tests grün sind (`OK`), kannst du zu Kapitel G06 weitergehen! diff --git a/g05_schleifen_und_wiederholungen/aufgabe.py b/g05_schleifen_und_wiederholungen/aufgabe.py new file mode 100644 index 0000000..c3a1a8d --- /dev/null +++ b/g05_schleifen_und_wiederholungen/aufgabe.py @@ -0,0 +1,167 @@ +""" +Kapitel G05: Schleifen & Wiederholungen – Aufgabenblatt +====================================================== +Schulabgleich: 09.1 & 09.2 + +In diesem Kapitel lernst du, wie Wiederholungen in Python funktionieren: +- for-Schleifen mit range() +- while-Schleifen mit Bedingungen +- Schleifen-Akkumulatoren (Summen, Zähler, Produkte) +- Vorzeitiger Abbruch mit break und Weiterspringen mit continue + +Bearbeite die Aufgaben von TODO 1 bis TODO 5. +Teste deine Lösungen jederzeit im Terminal mit: + python3 test_aufgabe.py +""" + + +# ============================================================================== +# TODO 1: Schreibe die Funktion "summe_bis(n: int) -> int". +# +# Beschreibung: +# Berechnet die Summe aller ganzen Zahlen von 1 bis n (inklusive). +# Beispiel: summe_bis(5) = 1 + 2 + 3 + 4 + 5 = 15. +# +# Regeln: +# - Wenn n <= 0 ist, soll 0 zurückgegeben werden. +# - Nutze eine for-Schleife mit range(1, n + 1) und einen Akkumulator (summe = 0). +# ============================================================================== + +def summe_bis(n: int) -> int: + """Berechnet 1 + 2 + ... + n.""" + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Schreibe die Funktion "fakultaet(n: int) -> int". +# +# Beschreibung: +# Die Fakultät n! ist das Produkt aller ganzen Zahlen von 1 bis n. +# Beispiel: fakultaet(5) = 1 * 2 * 3 * 4 * 5 = 120. +# +# Sonderregeln: +# - Für n = 0 ist die Fakultät mathematisch definiert als 1 (fakultaet(0) == 1). +# - Für negative Zahlen (n < 0) ist die Fakultät nicht definiert. +# Löse in diesem Fall einen ValueError aus: +# raise ValueError("Fakultät ist für negative Zahlen nicht definiert") +# - Nutze eine for-Schleife mit range(1, n + 1) und einen Akkumulator (produkt = 1). +# ============================================================================== + +def fakultaet(n: int) -> int: + """Berechnet n! (Fakultät von n).""" + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Schreibe die Funktion "zaehle_gerade_zahlen(start: int, ende: int) -> int". +# +# Beschreibung: +# Zählt, wie viele gerade Zahlen im Intervall von start bis ende (beide inklusive!) +# liegen. +# +# Beispiele: +# - zaehle_gerade_zahlen(1, 6) -> 3 (nämlich 2, 4, 6) +# - zaehle_gerade_zahlen(2, 4) -> 2 (nämlich 2, 4) +# - zaehle_gerade_zahlen(5, 5) -> 0 +# - zaehle_gerade_zahlen(10, 2) -> 0 (wenn start > ende) +# +# Tipp: +# Eine Zahl x ist gerade, wenn x % 2 == 0 gilt. +# Nutze eine for-Schleife über range(start, ende + 1) und zähle mit zaehler += 1. +# ============================================================================== + +def zaehle_gerade_zahlen(start: int, ende: int) -> int: + """Zählt alle geraden Zahlen im Bereich [start, ende].""" + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Schreibe die Funktion "ist_primzahl(n: int) -> bool". +# +# Beschreibung: +# Eine Primzahl ist eine ganze Zahl größer als 1, die nur durch 1 und sich selbst +# ohne Rest teilbar ist (z.B. 2, 3, 5, 7, 11, 13, ...). +# +# Regeln: +# - Zahlen kleiner oder gleich 1 (n <= 1) sind KEINE Primzahlen -> False. +# - Die Zahl 2 ist die kleinste Primzahl -> True. +# - Prüfe mit einer for-Schleife alle möglichen Teiler i von 2 bis n - 1 +# (oder bis int(n**0.5) + 1): +# Wenn n durch i ohne Rest teilbar ist (n % i == 0), dann ist n KEINE Primzahl +# -> brich sofort ab und gib False zurück! +# - Wird kein Teiler gefunden, gib am Ende True zurück. +# ============================================================================== + +def ist_primzahl(n: int) -> bool: + """Prüft, ob n eine Primzahl ist.""" + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# TODO 5: Schreibe die Funktion "quorsumme(n: int) -> int". +# +# Beschreibung: +# Berechnet die Quersumme einer Zahl n mittels einer WHILE-Schleife. +# Die Quersumme ist die Summe aller einzelnen Ziffern der Zahl. +# +# Beispiele: +# - quorsumme(482) = 4 + 8 + 2 = 14 +# - quorsumme(1234) = 1 + 2 + 3 + 4 = 10 +# - quorsumme(0) = 0 +# - quorsumme(-482) = 14 (behandle negative Zahlen mit abs(n)) +# +# Wie holt man Ziffern mit while heraus? (Klassischer Algorithmus): +# 1. Zahl positiv machen: rest = abs(n), summe = 0 +# 2. while rest > 0: +# letzte_ziffer = rest % 10 # Modulo 10 liefert die letzte Ziffer +# summe += letzte_ziffer +# rest = rest // 10 # Ganzzahldivision schneidet letzte Ziffer ab +# 3. summe zurückgeben! +# ============================================================================== + +def quorsumme(n: int) -> int: + """Berechnet die Quersumme von n mittels einer while-Schleife.""" + # Schreibe hier deinen Code für TODO 5: + pass + + +# Alias für deutsche Schreibweise +quersumme = quorsumme + + +# ============================================================================== +# Hauptprogramm zum manuellen Ausprobieren: +# (Führe diese Datei aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("=" * 50) + print("🎡 KAPITEL G05: SCHLEIFEN & WIEDERHOLUNGEN") + print("=" * 50) + + # 1. Summe + s5 = summe_bis(5) + print(f"Summe von 1 bis 5: {s5} (Erwartet: 15)") + + # 2. Fakultät + f5 = fakultaet(5) + print(f"Fakultät von 5 (5!): {f5} (Erwartet: 120)") + + # 3. Gerade Zahlen zählen + g = zaehle_gerade_zahlen(1, 10) + print(f"Gerade Zahlen von 1 bis 10: {g} (Erwartet: 5)") + + # 4. Primzahlprüfung + p7 = ist_primzahl(7) + p9 = ist_primzahl(9) + print(f"Ist 7 eine Primzahl? {p7} (Erwartet: True)") + print(f"Ist 9 eine Primzahl? {p9} (Erwartet: False)") + + # 5. Quersumme + q = quorsumme(482) + print(f"Quersumme von 482: {q} (Erwartet: 14)") + print("=" * 50) + print("💡 Führe 'python3 test_aufgabe.py' aus, um deine Lösungen zu prüfen!") diff --git a/g05_schleifen_und_wiederholungen/index.html b/g05_schleifen_und_wiederholungen/index.html new file mode 100644 index 0000000..204134c --- /dev/null +++ b/g05_schleifen_und_wiederholungen/index.html @@ -0,0 +1,332 @@ + + + + + + Kapitel G05: Schleifen & Wiederholungen + + + + +
+
+ +
Grundlagen G05 • Schulabgleich 09.1 & 09.2
+

Schleifen & Wiederholungen 🎡🔄

+

Lerne Zählschleifen (for), Bedingungsschleifen (while), Schleifen-Akkumulatoren und die Steuerbefehle break & continue kennen.

+
+
+ +
+ + +
+

1. Die Grundidee: Warum brauchen wir Schleifen?

+ +

+ Computer sind unschlagbar schnell darin, monotone Aufgaben millionenfach zu wiederholen. Anstatt 100-mal denselben Befehl untereinander zu schreiben, sagen wir Python einfach: "Wiederhole diesen Code-Block X-mal!" +

+ +
+
🎡 Die Karussell- & Riesenrad-Analogie:
+

Es gibt in Python zwei Hauptarten von Schleifen:

+
    +
  • Zählschleife (for): Wie ein Karussell mit Wertmarken. Du kaufst 5 Runden. Der Automat zählt: 1, 2, 3, 4, 5 – und stoppt genau nach 5 Runden.
  • +
  • Bedingungsschleife (while): Wie ein Riesenrad, das sich dreht, SOLANGE die Musik spielt. Sobald die Musik stoppt (Bedingung wird False), hält das Rad an.
  • +
+
+
+ + +
+

2. Zählschleifen: for und range()

+ +

+ Verwende for, wenn du im Voraus weißt, wie oft etwas wiederholt werden soll. Die Funktion range() erzeugt dafür die Zahlenfolge. +

+ +
+
⚠️ Die goldene range()-Regel: Der Stop-Wert ist immer EXKLUSIV!
+ range(1, 5) läuft von 1 bis 4 (die Zahl 5 ist nicht dabei)!
+ Wenn du bis 5 zählen willst, musst du range(1, 6) schreiben. +
+ +

Die 3 Varianten von range() im Überblick:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AufrufBedeutungErzeugte Zahlen
range(5)Start bei 0 bis Stop-10, 1, 2, 3, 4
range(1, 6)Start bei 1 bis 6-11, 2, 3, 4, 5
range(0, 10, 2)Start bei 0, 2er-Schritte0, 2, 4, 6, 8
range(5, 0, -1)Rückwärts (Countdown!)5, 4, 3, 2, 1
+
+ +
+
+
+ + + +
+ Python (for-Schleife mit range) +
+
# Countdown von 5 bis 1
+for sekunde in range(5, 0, -1):
+    print(f"Raketenstart in {sekunde}...")
+print("🚀 Liftoff!")
+
+
+ + +
+

3. Bedingungsschleifen: while

+ +

+ Verwende while, wenn du nicht weißt, wie viele Durchläufe nötig sind, sondern die Schleife laufen soll, solange eine Bedingung wahr (True) ist. +

+ +
+
+
+ Python (while-Schleife) +
+
energie = 3
+while energie > 0:
+    print(f"Roboter läuft... Energie: {energie}")
+    energie -= 1  # ⚠️ Zähler anpassen, sonst Endlosschleife!
+
+print("Akku leer! Bitte aufladen.")
+
+ +
+
🛑 Achtung vor der Endlosschleife (Infinite Loop)!
+

Wenn die Bedingung niemals False wird, stürzt dein Programm zwar nicht ab, aber es friert für immer ein! Verändere immer die Prüfvariable im Schleifenkörper.

+

Notausstieg im Terminal: Drücke Strg + C, um eine Endlosschleife sofort abzubrechen!

+
+
+ + +
+

4. Schleifen-Steuerung: break & continue

+ +

Mit zwei speziellen Schlüsselwörtern hast du volle Kontrolle über den Schleifenablauf:

+ +
+
+
🛑
+
+

break (Notbremse)

+

Beendet die Schleife sofort und vollständig. Die Programmausführung springt direkt hinter die Schleife.

+
+
+ +
+
⏭️
+
+

continue (Nächste Runde)

+

Bricht nur die aktuelle Runde ab und springt sofort zum nächsten Schleifendurchlauf.

+
+
+
+ +
+
+
+ Python (break und continue im Einsatz) +
+
# Beispiel: Nur ungerade Zahlen drucken, bei 7 abbrechen
+for zahl in range(1, 11):
+    if zahl % 2 == 0:
+        continue  # Überspringe gerade Zahlen
+    if zahl == 7:
+        print("Glückszahl 7 gefunden -> Abbruch!")
+        break     # Schleife komplett beenden
+    print("Ungerade Zahl:", zahl)
+
+# Ausgabe: 1, 3, 5, danach Abbruch!
+
+
+ + +
+

5. Das Akkumulator-Muster 🧺

+ +

+ Ein Akkumulator (Sammelbehälter) ist eines der wichtigsten Entwurfsmuster der Informatik. Er sammelt während der Schleife Werte an: +

+ +
+
+
+ Python (Die 3 klassischen Akkumulatoren) +
+
# 1. Summen-Akkumulator (Startwert: 0)
+summe = 0
+for x in [10, 20, 30]:
+    summe += x     # summe = summe + x
+print("Gesamtsumme:", summe)  # 60
+
+# 2. Zähler-Akkumulator (Startwert: 0)
+anzahl = 0
+for x in range(1, 11):
+    if x % 2 == 0:
+        anzahl += 1
+print("Anzahl gerade Zahlen:", anzahl)  # 5
+
+# 3. Produkt-Akkumulator (Startwert: 1, NIEMALS 0!)
+produkt = 1
+for x in range(1, 5):
+    produkt *= x   # 1 * 2 * 3 * 4
+print("Produkt (4!):", produkt)  # 24
+
+ +
+
💡 Goldene Akkumulator-Regel:
+ Definiere die Akkumulator-Variable (summe = 0 oder produkt = 1) immer VOR der Schleife! Wenn du sie in der Schleife definierst, wird der Wert bei jedem Durchlauf wieder auf 0 bzw. 1 zurückgesetzt. +
+
+ + +
+

6. 🎯 Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei g05_schleifen_und_wiederholungen/aufgabe.py und bearbeite die folgenden 5 Funktionen:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 1 & 2 (Summe & Fakultät) +
+
+
def summe_bis(n: int) -> int:
+    if n <= 0:
+        return 0
+    summe = 0
+    for i in range(1, n + 1):
+        summe += i
+    return summe
+
+
+
+ +
+ 🔍 Tipp zu TODO 4 (Primzahl-Test mit Schleife) +
+
+
def ist_primzahl(n: int) -> bool:
+    if n <= 1:
+        return False
+    for i in range(2, n):
+        if n % i == 0:
+            return False  # Teiler gefunden -> keine Primzahl!
+    return True           # Kein Teiler gefunden -> Primzahl!
+
+
+
+ +
+ 🔍 Tipp zu TODO 5 (Quersumme mit while-Schleife) +
+
+
def quorsumme(n: int) -> int:
+    rest = abs(n)
+    summe = 0
+    while rest > 0:
+        summe += rest % 10    # Letzte Ziffer addieren
+        rest = rest // 10     # Letzte Ziffer abschneiden
+    return summe
+
+
+
+
+ + +
+

7. 🧪 Testen & Weitergehen

+

Führe im Terminal den Unittest aus:

+
+
Terminal
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g05_schleifen_und_wiederholungen/musterloesung.py b/g05_schleifen_und_wiederholungen/musterloesung.py new file mode 100644 index 0000000..f1ae4dc --- /dev/null +++ b/g05_schleifen_und_wiederholungen/musterloesung.py @@ -0,0 +1,77 @@ +""" +Kapitel G05: Schleifen & Wiederholungen – Musterlösung +===================================================== +Schulabgleich: 09.1 & 09.2 +""" + + +def summe_bis(n: int) -> int: + """Berechnet 1 + 2 + ... + n.""" + if n <= 0: + return 0 + summe = 0 + for i in range(1, n + 1): + summe += i + return summe + + +def fakultaet(n: int) -> int: + """Berechnet n! (Fakultät von n).""" + if n < 0: + raise ValueError("Fakultät ist für negative Zahlen nicht definiert") + if n == 0: + return 1 + produkt = 1 + for i in range(1, n + 1): + produkt *= i + return produkt + + +def zaehle_gerade_zahlen(start: int, ende: int) -> int: + """Zählt alle geraden Zahlen im Bereich [start, ende].""" + if start > ende: + return 0 + zaehler = 0 + for zahl in range(start, ende + 1): + if zahl % 2 == 0: + zaehler += 1 + return zaehler + + +def ist_primzahl(n: int) -> bool: + """Prüft, ob n eine Primzahl ist.""" + if n <= 1: + return False + if n == 2: + return True + if n % 2 == 0: + return False + # Prüfe ungerade Teiler bis zur Wurzel von n + for teiler in range(3, int(n**0.5) + 1, 2): + if n % teiler == 0: + return False + return True + + +def quorsumme(n: int) -> int: + """Berechnet die Quersumme von n mittels einer while-Schleife.""" + rest = abs(n) + summe = 0 + while rest > 0: + letzte_ziffer = rest % 10 + summe += letzte_ziffer + rest = rest // 10 + return summe + + +# Alias für alternative Schreibweise +quersumme = quorsumme + + +if __name__ == "__main__": + print("Musterlösung G05 Demonstrationen:") + print("summe_bis(5) =", summe_bis(5)) + print("fakultaet(5) =", fakultaet(5)) + print("zaehle_gerade_zahlen(1, 10) =", zaehle_gerade_zahlen(1, 10)) + print("ist_primzahl(17) =", ist_primzahl(17)) + print("quorsumme(482) =", quorsumme(482)) diff --git a/g05_schleifen_und_wiederholungen/test_aufgabe.py b/g05_schleifen_und_wiederholungen/test_aufgabe.py new file mode 100644 index 0000000..196be77 --- /dev/null +++ b/g05_schleifen_und_wiederholungen/test_aufgabe.py @@ -0,0 +1,178 @@ +""" +Testsuite für Kapitel G05: Schleifen & Wiederholungen +===================================================== +Schulabgleich: 09.1 & 09.2 +""" + +import sys +import unittest +from pathlib import Path + +# Sicherstellen, dass das aktuelle Verzeichnis im Modulpfad ist +sys.path.insert(0, str(Path(__file__).parent)) + +try: + import aufgabe +except ImportError as e: + raise ImportError(f"Konnte 'aufgabe.py' nicht importieren: {e}") + + +class TestKapitelG05(unittest.TestCase): + + def test_01_summe_bis(self): + """Prüft die Funktion summe_bis(n).""" + self.assertTrue( + hasattr(aufgabe, "summe_bis"), + "Fehler: Die Funktion 'summe_bis' fehlt in aufgabe.py!" + ) + self.assertEqual( + aufgabe.summe_bis(1), 1, + "Fehler: summe_bis(1) sollte 1 ergeben!" + ) + self.assertEqual( + aufgabe.summe_bis(5), 15, + "Fehler: summe_bis(5) sollte 15 (1+2+3+4+5) ergeben!" + ) + self.assertEqual( + aufgabe.summe_bis(10), 55, + "Fehler: summe_bis(10) sollte 55 ergeben!" + ) + self.assertEqual( + aufgabe.summe_bis(100), 5050, + "Fehler: summe_bis(100) sollte 5050 (Gaußsche Summenformel) ergeben!" + ) + self.assertEqual( + aufgabe.summe_bis(0), 0, + "Fehler: summe_bis(0) sollte 0 zurückgeben!" + ) + self.assertEqual( + aufgabe.summe_bis(-10), 0, + "Fehler: summe_bis für negative Zahlen sollte 0 zurückgeben!" + ) + + def test_02_fakultaet(self): + """Prüft die Funktion fakultaet(n).""" + self.assertTrue( + hasattr(aufgabe, "fakultaet"), + "Fehler: Die Funktion 'fakultaet' fehlt in aufgabe.py!" + ) + self.assertEqual( + aufgabe.fakultaet(0), 1, + "Fehler: fakultaet(0) ist mathematisch 1 (0! = 1)!" + ) + self.assertEqual( + aufgabe.fakultaet(1), 1, + "Fehler: fakultaet(1) sollte 1 ergeben!" + ) + self.assertEqual( + aufgabe.fakultaet(4), 24, + "Fehler: fakultaet(4) sollte 24 (1*2*3*4) ergeben!" + ) + self.assertEqual( + aufgabe.fakultaet(5), 120, + "Fehler: fakultaet(5) sollte 120 ergeben!" + ) + self.assertEqual( + aufgabe.fakultaet(7), 5040, + "Fehler: fakultaet(7) sollte 5040 ergeben!" + ) + # Fehlerbehandlung bei negativen Zahlen + with self.assertRaises(ValueError, msg="Fehler: fakultaet(-1) muss einen ValueError auslösen!"): + aufgabe.fakultaet(-1) + with self.assertRaises(ValueError, msg="Fehler: fakultaet(-5) muss einen ValueError auslösen!"): + aufgabe.fakultaet(-5) + + def test_03_zaehle_gerade_zahlen(self): + """Prüft die Funktion zaehle_gerade_zahlen(start, ende).""" + self.assertTrue( + hasattr(aufgabe, "zaehle_gerade_zahlen"), + "Fehler: Die Funktion 'zaehle_gerade_zahlen' fehlt in aufgabe.py!" + ) + self.assertEqual( + aufgabe.zaehle_gerade_zahlen(1, 6), 3, + "Fehler: Im Bereich von 1 bis 6 gibt es 3 gerade Zahlen (2, 4, 6)!" + ) + self.assertEqual( + aufgabe.zaehle_gerade_zahlen(1, 10), 5, + "Fehler: Im Bereich von 1 bis 10 gibt es 5 gerade Zahlen (2, 4, 6, 8, 10)!" + ) + self.assertEqual( + aufgabe.zaehle_gerade_zahlen(2, 4), 2, + "Fehler: Im Bereich von 2 bis 4 gibt es 2 gerade Zahlen (2, 4)!" + ) + self.assertEqual( + aufgabe.zaehle_gerade_zahlen(3, 3), 0, + "Fehler: 3 ist ungerade, also sollte 0 zurückgegeben werden!" + ) + self.assertEqual( + aufgabe.zaehle_gerade_zahlen(4, 4), 1, + "Fehler: Im Bereich [4, 4] gibt es genau eine gerade Zahl (4)!" + ) + self.assertEqual( + aufgabe.zaehle_gerade_zahlen(10, 5), 0, + "Fehler: Wenn start > ende ist, sollte 0 zurückgegeben werden!" + ) + self.assertEqual( + aufgabe.zaehle_gerade_zahlen(-4, 2), 4, + "Fehler: Im Bereich [-4, 2] liegen -4, -2, 0, 2 (4 gerade Zahlen)!" + ) + + def test_04_ist_primzahl(self): + """Prüft die Funktion ist_primzahl(n).""" + self.assertTrue( + hasattr(aufgabe, "ist_primzahl"), + "Fehler: Die Funktion 'ist_primzahl' fehlt in aufgabe.py!" + ) + # Echte Primzahlen + for p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 97]: + self.assertTrue( + aufgabe.ist_primzahl(p), + f"Fehler: {p} ist eine Primzahl, wurde aber als False bewertet!" + ) + + # Keine Primzahlen + for non_p in [0, 1, -1, -7, 4, 6, 8, 9, 15, 21, 25, 100]: + self.assertFalse( + aufgabe.ist_primzahl(non_p), + f"Fehler: {non_p} ist KEINE Primzahl, wurde aber als True bewertet!" + ) + + def test_05_quorsumme(self): + """Prüft die Funktion quorsumme(n).""" + fn = getattr(aufgabe, "quorsumme", getattr(aufgabe, "quersumme", None)) + self.assertIsNotNone( + fn, + "Fehler: Die Funktion 'quorsumme' (oder 'quersumme') fehlt in aufgabe.py!" + ) + self.assertEqual( + fn(0), 0, + "Fehler: Quersumme von 0 sollte 0 sein!" + ) + self.assertEqual( + fn(5), 5, + "Fehler: Quersumme von 5 sollte 5 sein!" + ) + self.assertEqual( + fn(123), 6, + "Fehler: Quersumme von 123 (1+2+3) sollte 6 sein!" + ) + self.assertEqual( + fn(482), 14, + "Fehler: Quersumme von 482 (4+8+2) sollte 14 sein!" + ) + self.assertEqual( + fn(9999), 36, + "Fehler: Quersumme von 9999 (9+9+9+9) sollte 36 sein!" + ) + self.assertEqual( + fn(1000), 1, + "Fehler: Quersumme von 1000 sollte 1 sein!" + ) + self.assertEqual( + fn(-482), 14, + "Fehler: Quersumme von -482 sollte 14 sein (Vorzeichen wird ignoriert)!" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g06_funktionen_und_module/README.md b/g06_funktionen_und_module/README.md new file mode 100644 index 0000000..638f425 --- /dev/null +++ b/g06_funktionen_und_module/README.md @@ -0,0 +1,169 @@ +# Kapitel G06: Eigene Funktionen & Module 🪄📦 + +Willkommen zu **Kapitel G06** unseres Python-Grundlagen-Lehrpfads! +Dieses Kapitel entspricht den Modulen **05.0 & 05.1** deines Informatik-Skriptums. + +Bis jetzt hast du vorgefertigte Funktionen wie `print()`, `len()` oder `range()` genutzt. Jetzt lernst du, wie du **deine eigenen Funktionen schreibst** und mächtige **Module der Standardbibliothek** (wie `math` und `random`) einbindest. + +--- + +## 🪄 Die Fabrik- / Zauberkisten-Analogie + +Stell dir eine Funktion wie eine kleine Fabrikmaschine (oder einen Zauberkasten) vor: + +``` + [ INPUT / ZUTATEN ] + (Parameter: z.B. a=3, b=4) + │ + ▼ +┌─────────────────────────┐ +│ EIGENE FUNKTION │ +│ def hypotenuse(a, b): │ <─── VERARBEITUNG (Black Box) +│ c = math.sqrt(...) │ +└────────────┬────────────┘ + │ + ▼ + [ OUTPUT / PRODUKT ] + (return 5.0) +``` + +1. **Input (Parameter):** Was die Funktion braucht, um zu arbeiten (z.B. zwei Zahlen). +2. **Verarbeitung:** Der Code innerhalb der Funktion, der die Berechnung durchführt. +3. **Output (`return`):** Das fertige Ergebnis, das die Funktion an den Aufrufer zurückgibt. + +--- + +## 1. Funktionen definieren: `def` & Standardwerte + +```python +# Definition mit Parametern und Standardwert (Default Argument): +def begruesse(name: str, titel: str = "Frau/Herr") -> str: + return f"Guten Tag, {titel} {name}!" + +# Aufruf mit beiden Argumenten: +print(begruesse("Müller", "Dr.")) # "Guten Tag, Dr. Müller!" + +# Aufruf ohne Standardwert (nimmt Default "Frau/Herr"): +print(begruesse("Schmidt")) # "Guten Tag, Frau/Herr Schmidt!" +``` + +--- + +## 2. Der fundamentale Unterschied: `return` vs. `print()` + +Das ist der mit Abstand häufigste Anfängerfehler: + +| Eigenschaft | `print()` 🖨️ | `return` 📦 | +| :--- | :--- | :--- | +| **Was passiert?** | Schreibt nur Text auf den Bildschirm. | Übergibt das echte Ergebnis zurück an das Programm. | +| **Weiterverwendung?** | **NEIN!** Das Ergebnis ist für das Programm verloren (`None`). | **JA!** Du kannst das Ergebnis in Variablen speichern, weiterrechnen oder vergleichen. | +| **Analogie:** | Du liest den Kassenzettel im Supermarkt. | Du nimmst die gekaufte Ware mit nach Hause! | + +```python +# ❌ Falsch: Mit print kann man nicht weiterrechnen! +def verdopple_falsch(x): + print(x * 2) + +ergebnis = verdopple_falsch(5) # Gibt 10 auf dem Bildschirm aus +# print(ergebnis + 5) # 💥 TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'! + +# ✅ Richtig: Mit return kann man das Ergebnis weiterverwenden! +def verdopple_richtig(x): + return x * 2 + +ergebnis = verdopple_richtig(5) # ergebnis ist jetzt 10 +print(ergebnis + 5) # Funktioniert perfekt -> 15! +``` + +--- + +## 3. Mehrere Rückgabewerte (Tupel-Return) + +In Python kann eine Funktion problemlos **mehrere Werte gleichzeitig** zurückgeben. Python packt sie automatisch in ein **Tupel**: + +```python +def min_und_max(zahlen: list[float]) -> tuple[float, float]: + kleinste = min(zahlen) + groesste = max(zahlen) + return kleinste, groesste # Gibt (kleinste, groesste) zurück + +# Tuple-Unpacking (Entpacken in zwei separate Variablen): +tiefst, hoechst = min_und_max([12.5, 3.2, 19.8, -1.0]) +print(f"Tiefstwert: {tiefst}, Höchstwert: {hoechst}") +``` + +--- + +## 4. Lokaler vs. Globaler Gültigkeitsbereich (Scope) 🏠 + +Variablen, die du **innerhalb** einer Funktion erstellst, sind **lokal**. Sie existieren nur so lange, wie die Funktion ausgeführt wird: + +```python +def meine_funktion(): + geheime_zahl = 42 # Lokale Variable! + print("In der Funktion:", geheime_zahl) + +meine_funktion() +# print(geheime_zahl) # 💥 NameError: name 'geheime_zahl' is not defined! +``` + +> **Die Hotelzimmer-Regel:** Was im Hotelzimmer (der Funktion) liegt, sieht man von außen auf der Straße nicht. + +--- + +## 5. Module nutzen: `math` & `random` 🧰 + +Python hat eine riesige "Werkzeugkiste" an eingebauten Modulen (Standard Library), die du einfach mit `import` laden kannst: + +### Das `math`-Modul (Mathematische Werkzeuge): +```python +import math + +print(math.sqrt(25)) # Quadratwurzel -> 5.0 +print(math.pi) # Kreiszahl Pi -> 3.141592653589793 +print(math.pow(2, 3)) # Potenz 2^3 -> 8.0 +print(math.floor(4.9)) # Abrunden -> 4 +print(math.ceil(4.1)) # Aufrunden -> 5 +``` + +### Das `random`-Modul (Zufallszahlen & Würfel): +```python +import random + +# Zufällige ganze Zahl zwischen 1 und 6 (beide inklusive): +wuerfelwurf = random.randint(1, 6) + +# Zufällige Kommazahl zwischen 0.0 und 1.0: +zufall = random.random() + +# Zufälliges Element aus einer Liste wählen: +farben = ["rot", "grün", "blau", "gelb"] +gluecksfarbe = random.choice(farben) +``` + +--- + +## ⚠️ Die 3 häufigsten Anfängerfehler + +1. **`return` vergessen oder durch `print()` ersetzt:** + - Deine Funktion berechnet etwas, gibt es aber nicht an den Aufrufer zurück. Ergebnis ist `None`. +2. **Auf lokale Variablen von außen zugreifen:** + - Variablen innerhalb von Funktionen existieren außerhalb nicht. +3. **Falsche Modul-Namen oder Funktionen:** + - Achte auf die Schreibweise: `math.sqrt()` (nicht `math.square_root()`) und `random.randint(1, 6)` (nicht `random.random_int()`). + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne `aufgabe.py` und bearbeite die nummerierten TODOs: +1. **TODO 1:** `hypotenuse(a: float, b: float) -> float` – Satz des Pythagoras mit `math.sqrt`. +2. **TODO 2:** `zylinder_volumen(radius: float, hoehe: float) -> float` – Zylindervolumen mit `math.pi`. +3. **TODO 3:** `wuerfle_wuerfel(anzahl: int, seiten: int = 6) -> list[int]` – Würfelsimulator mit `random.randint` und Standardwert `seiten=6`. +4. **TODO 4:** `statistik(zahlen: list[float]) -> tuple[float, float, float]` – Berechnet `(min, max, mittelwert)` als Tupel. + +### Testen: +```bash +python3 test_aufgabe.py +``` +Sobald alle 7 Tests erfolgreich durchlaufen (`OK`), hast du Funktionen und Module gemeistert! diff --git a/g06_funktionen_und_module/aufgabe.py b/g06_funktionen_und_module/aufgabe.py new file mode 100644 index 0000000..46adefd --- /dev/null +++ b/g06_funktionen_und_module/aufgabe.py @@ -0,0 +1,145 @@ +""" +Kapitel G06: Eigene Funktionen & Module – Aufgabenblatt +====================================================== +Schulabgleich: 05.0 & 05.1 + +In diesem Kapitel lernst du: +- Eigene Funktionen mit Parametern und Standardwerten definieren (def) +- Rückgabewerte mit 'return' (und warum 'return' nicht 'print' ist!) +- Mehrere Rückgabewerte als Tupel zurückgeben (Tuple-Return) +- Lokale vs. Globale Gültigkeit von Variablen (Scope) +- Standardbibliotheks-Module importieren und nutzen (math, random) + +Bearbeite die Aufgaben von TODO 1 bis TODO 4. +Teste deine Lösungen jederzeit im Terminal mit: + python3 test_aufgabe.py +""" + +# ============================================================================== +# Modul-Importe: +# Importiere hier die benötigten Standard-Module (math und random): +# ============================================================================== +import math +import random + + +# ============================================================================== +# TODO 1: Schreibe die Funktion "hypotenuse(a: float, b: float) -> float". +# +# Beschreibung: +# Berechnet die Länge der Hypotenuse c eines rechtwinkligen Dreiecks nach dem +# Satz des Pythagoras: c = sqrt(a² + b²). +# +# Regeln: +# - Verwende math.sqrt(...) für die Quadratwurzel und a**2 bzw. b**2 zum Quadrieren. +# - Wenn a < 0 oder b < 0 ist, soll ein ValueError ausgelöst werden: +# raise ValueError("Seitenlängen dürfen nicht negativ sein") +# - Gib das Ergebnis mit 'return' als float zurück. +# ============================================================================== + +def hypotenuse(a: float, b: float) -> float: + """Berechnet die Hypotenuse c = sqrt(a^2 + b^2).""" + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Schreibe die Funktion "zylinder_volumen(radius: float, hoehe: float) -> float". +# +# Beschreibung: +# Berechnet das Volumen eines Zylinders: V = pi * r² * h. +# +# Regeln: +# - Verwende die Kreiszahl Pi aus dem math-Modul: math.pi. +# - Wenn radius < 0 oder hoehe < 0 ist, soll ein ValueError ausgelöst werden: +# raise ValueError("Radius und Höhe dürfen nicht negativ sein") +# - Gib das berechnete Volumen mit 'return' zurück. +# ============================================================================== + +def zylinder_volumen(radius: float, hoehe: float) -> float: + """Berechnet das Volumen eines Zylinders: V = pi * r^2 * h.""" + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Schreibe die Funktion "wuerfle_wuerfel(anzahl: int, seiten: int = 6) -> list[int]". +# +# Beschreibung: +# Simuliert das Würfeln von 'anzahl' Würfeln mit jeweils 'seiten' Seiten. +# +# Regeln: +# - Der Parameter 'seiten' hat den Standardwert 6 (Standard-Spielwürfel). +# - Verwende random.randint(1, seiten), um eine Zufallszahl zwischen 1 und 'seiten' +# (inklusive) zu erzeugen. +# - Bei anzahl < 0 oder seiten < 1 soll ein ValueError ausgelöst werden: +# raise ValueError("Ungültige Würfel-Parameter") +# - Bei anzahl == 0 soll eine leere Liste [] zurückgegeben werden. +# - Gib eine Liste mit allen gewürfelten Augenzahlen zurück. +# ============================================================================== + +def wuerfle_wuerfel(anzahl: int, seiten: int = 6) -> list[int]: + """Würfelt 'anzahl' Würfel mit je 'seiten' Seiten (Standard: 6-seitig).""" + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Schreibe die Funktion "statistik(zahlen: list[float]) -> tuple[float, float, float]". +# +# Beschreibung: +# Berechnet das Minimum, das Maximum und den arithmetischen Mittelwert (Durchschnitt) +# einer Liste von Zahlen. +# +# Regeln: +# - Wenn die Liste leer ist (len(zahlen) == 0), soll ein ValueError ausgelöst werden: +# raise ValueError("Die Liste darf nicht leer sein") +# - Berechne: +# * min_wert = min(zahlen) +# * max_wert = max(zahlen) +# * mittelwert = sum(zahlen) / len(zahlen) +# - Gib alle drei Werte als Tupel zurück: +# return (min_wert, max_wert, mittelwert) +# ============================================================================== + +def statistik(zahlen: list[float]) -> tuple[float, float, float]: + """Gibt (min_wert, max_wert, mittelwert) einer Zahlenliste zurück.""" + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# Hauptprogramm zum manuellen Ausprobieren: +# (Führe diese Datei aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("=" * 55) + print("🪄 KAPITEL G06: EIGENE FUNKTIONEN & MODULE") + print("=" * 55) + + # 1. Pythagoras + c = hypotenuse(3.0, 4.0) + print(f"Hypotenuse für a=3, b=4: {c} (Erwartet: 5.0)") + + # 2. Zylindervolumen + v = zylinder_volumen(3.0, 5.0) + v_str = f"{v:.2f}" if v is not None else "None" + print(f"Zylindervolumen (r=3, h=5): {v_str} (Erwartet: ~141.37)") + + # 3. Würfel werfen + wuerfe_w6 = wuerfle_wuerfel(5) + print(f"5x W6 gewürfelt: {wuerfe_w6}") + wuerfe_w20 = wuerfle_wuerfel(3, seiten=20) + print(f"3x W20 (D20) gewürfelt: {wuerfe_w20}") + + # 4. Statistik mit Tuple-Unpacking + noten = [1.0, 2.3, 1.7, 3.0, 1.3] + stats = statistik(noten) + if stats is not None: + bester, schlechtester, schnitt = stats + print(f"Notenstatistik: Beste={bester}, Schlechteste={schlechtester}, Schnitt={schnitt:.2f}") + else: + print(f"Notenstatistik: None (Erwartet: (1.0, 3.0, 1.86))") + + print("=" * 55) + print("💡 Führe 'python3 test_aufgabe.py' aus, um deine Lösungen zu prüfen!") diff --git a/g06_funktionen_und_module/index.html b/g06_funktionen_und_module/index.html new file mode 100644 index 0000000..5079e0e --- /dev/null +++ b/g06_funktionen_und_module/index.html @@ -0,0 +1,285 @@ + + + + + + Kapitel G06: Eigene Funktionen & Module + + + + +
+
+ +
Grundlagen G06 • Schulabgleich 05.0 & 05.1
+

Eigene Funktionen & Module 🪄📦

+

Lerne, wie du saubere Funktionen baust, Ergebnisse mit return zurückgibst, Variablen-Scopes verstehst und Standard-Module wie math und random nutzt.

+
+
+ +
+ + +
+

1. Die Fabrik-Analogie: Was ist eine Funktion?

+ +

+ Eine Funktion ist wie eine kleine, eigenständige Fabrikmaschine (oder ein Zauberkasten). Du gibst ihr Zutaten hinein, die Maschine verarbeitet sie, und am Ende liefert sie ein fertiges Produkt heraus! +

+ +
+
🏭 Das 3-Schritte-Prinzip jeder Funktion:
+
    +
  1. Input (Parameter): Die Zutaten, die du beim Aufruf übergibst (z.B. zwei Zahlen: a = 3, b = 4).
  2. +
  3. Verarbeitung: Die Rechenschritte im Inneren der Funktion (z.B. Satz des Pythagoras c = math.sqrt(a**2 + b**2)).
  4. +
  5. Output (return): Das fertige Produkt, das an dein Hauptprogramm zurückgegeben wird (z.B. 5.0).
  6. +
+
+
+ + +
+

2. Der entscheidende Unterschied: return vs. print()

+ +

Das ist der häufigste Stolperstein für alle Programmieranfänger:

+ +
+
+
🖨️
+
+

print(...)

+

Schreibt nur Text auf den Bildschirm. Das Programm kann mit der Bildschirmausgabe nicht weiterrechnen!

+
+
+ +
+
📦
+
+

return ...

+

Übergibt den echten Wert zurück an den Aufrufer. Du kannst das Ergebnis in Variablen speichern und weiterverwenden!

+
+
+
+ +
+
+
+ Vergleich: print() vs. return +
+
# ❌ Falsch: Mit print() kann man nicht weiterrechnen
+def quadrat_falsch(x):
+    print(x * x)
+
+ergebnis = quadrat_falsch(5)  # Gibt 25 auf der Konsole aus, liefert aber None!
+# print(ergebnis + 10)         # 💥 TypeError: unsupported operand type for +: 'NoneType' and 'int'
+
+# ✅ Richtig: Mit return kann der Wert weiterverwendet werden
+def quadrat_richtig(x):
+    return x * x
+
+ergebnis = quadrat_richtig(5)  # ergebnis ist jetzt der echte Wert 25!
+print(ergebnis + 10)          # Funktioniert perfekt -> Ausgabe: 35
+
+
+ + +
+

3. Standardwerte (Defaults) & Mehrere Rückgabewerte

+ +

Du kannst Parametern Standardwerte geben, die verwendet werden, wenn beim Aufruf kein Wert übergeben wird:

+ +
+
+
+ Python (Standardwerte und Tupel-Return) +
+
# Standardwert seiten=6 (W6 Spielwürfel)
+def wuerfeln(anzahl: int, seiten: int = 6) -> list[int]:
+    ergebnisse = []
+    for _ in range(anzahl):
+        ergebnisse.append(random.randint(1, seiten))
+    return ergebnisse
+
+print(wuerfeln(3))            # 3 normale 6-seitige Würfel
+print(wuerfeln(2, seiten=20))  # 2 20-seitige D20-Würfel
+
+ +

Mehrere Werte zurückgeben (Tuple-Unpacking):

+

Trenne Rückgabewerte einfach mit Kommas. Beim Aufruf kannst du sie direkt in separate Variablen entpacken:

+ +
+
+ Python (Mehrere Rückgabewerte) +
+
def min_max_spanne(werte: list[float]) -> tuple[float, float]:
+    return min(werte), max(werte)
+
+# Beide Werte auf einmal entpacken:
+tiefster, hoechster = min_max_spanne([12.5, 3.1, 18.9])
+print(f"Minimum: {tiefster}, Maximum: {hoechster}")
+
+
+ + +
+

4. Gültigkeitsbereich: Lokaler vs. Globaler Scope 🏠

+ +
+
🏨 Die Hotelzimmer-Regel:
+ Variablen, die innerhalb einer Funktion erstellt werden (z.B. Zwischenergebnisse oder Zähler), sind lokal. Sie existieren nur für die Dauer des Funktionsaufrufs und sind von außen unsichtbar und unzugänglich. +
+ +
+
Scope-Veranschaulichung
+
globaler_name = "Alice"  # Überall im gesamten Programm sichtbar
+
+def geheimnis():
+    lokales_passwort = "Geheim123"  # Nur HIER DRINNEN sichtbar!
+    print("In Funktion:", lokales_passwort)
+
+geheimnis()
+# print(lokales_passwort)  # 💥 NameError: name 'lokales_passwort' is not defined!
+
+
+ + +
+

5. Die Python-Werkzeugkiste: Module math & random

+ +

Python bringt hunderte nützliche Module mit. Du bindest sie mit import modulname am Anfang deiner Datei ein:

+ +
+
+
📐
+
+

Modul math

+

math.sqrt(x): Quadratwurzel von x
+ math.pi: Kreiszahl π ≈ 3.14159...
+ math.pow(b, e): Potenzieren $b^e$
+ math.floor(x) / math.ceil(x): Ab-/Aufrunden

+
+
+ +
+
🎲
+
+

Modul random

+

random.randint(a, b): Zufalls-Ganzzahl von a bis b
+ random.random(): Zufalls-Float zwischen 0.0 und 1.0
+ random.choice(liste): Zufälliges Element wählen
+ random.shuffle(liste): Liste mischen

+
+
+
+
+ + +
+

6. 🎯 Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei g06_funktionen_und_module/aufgabe.py und bearbeite die folgenden 4 Aufgaben:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 1 & 2 (Geometrie mit math) +
+
+
import math
+
+def hypotenuse(a: float, b: float) -> float:
+    if a < 0 or b < 0:
+        raise ValueError("Seitenlängen dürfen nicht negativ sein")
+    return math.sqrt(a**2 + b**2)
+
+
+
+ +
+ 🔍 Tipp zu TODO 3 (Würfelsimulator mit random) +
+
+
import random
+
+def wuerfle_wuerfel(anzahl: int, seiten: int = 6) -> list[int]:
+    if anzahl < 0 or seiten < 1:
+        raise ValueError("Ungültige Würfel-Parameter")
+    ergebnisse = []
+    for _ in range(anzahl):
+        ergebnisse.append(random.randint(1, seiten))
+    return ergebnisse
+
+
+
+ +
+ 🔍 Tipp zu TODO 4 (Statistik & Tupel-Return) +
+
+
def statistik(zahlen: list[float]) -> tuple[float, float, float]:
+    if not zahlen:
+        raise ValueError("Die Liste darf nicht leer sein")
+    return min(zahlen), max(zahlen), sum(zahlen) / len(zahlen)
+
+
+
+
+ + +
+

7. 🧪 Testen & Weitergehen

+

Führe im Terminal den Unittest aus:

+
+
Terminal
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g06_funktionen_und_module/musterloesung.py b/g06_funktionen_und_module/musterloesung.py new file mode 100644 index 0000000..2b868df --- /dev/null +++ b/g06_funktionen_und_module/musterloesung.py @@ -0,0 +1,51 @@ +""" +Kapitel G06: Eigene Funktionen & Module – Musterlösung +===================================================== +Schulabgleich: 05.0 & 05.1 +""" + +import math +import random + + +def hypotenuse(a: float, b: float) -> float: + """Berechnet die Hypotenuse c = sqrt(a^2 + b^2).""" + if a < 0 or b < 0: + raise ValueError("Seitenlängen dürfen nicht negativ sein") + return math.sqrt(a**2 + b**2) + + +def zylinder_volumen(radius: float, hoehe: float) -> float: + """Berechnet das Volumen eines Zylinders: V = pi * r^2 * h.""" + if radius < 0 or hoehe < 0: + raise ValueError("Radius und Höhe dürfen nicht negativ sein") + return math.pi * (radius**2) * hoehe + + +def wuerfle_wuerfel(anzahl: int, seiten: int = 6) -> list[int]: + """Würfelt 'anzahl' Würfel mit je 'seiten' Seiten (Standard: 6-seitig).""" + if anzahl < 0 or seiten < 1: + raise ValueError("Ungültige Würfel-Parameter") + ergebnisse = [] + for _ in range(anzahl): + augen = random.randint(1, seiten) + ergebnisse.append(augen) + return ergebnisse + + +def statistik(zahlen: list[float]) -> tuple[float, float, float]: + """Gibt (min_wert, max_wert, mittelwert) einer Zahlenliste zurück.""" + if not zahlen: + raise ValueError("Die Liste darf nicht leer sein") + min_wert = min(zahlen) + max_wert = max(zahlen) + mittelwert = sum(zahlen) / len(zahlen) + return min_wert, max_wert, mittelwert + + +if __name__ == "__main__": + print("Musterlösung G06 Demonstrationen:") + print("hypotenuse(3, 4) =", hypotenuse(3.0, 4.0)) + print("zylinder_volumen(3, 5) =", zylinder_volumen(3.0, 5.0)) + print("wuerfle_wuerfel(5) =", wuerfle_wuerfel(5)) + print("statistik([10, 20, 30]) =", statistik([10.0, 20.0, 30.0])) diff --git a/g06_funktionen_und_module/test_aufgabe.py b/g06_funktionen_und_module/test_aufgabe.py new file mode 100644 index 0000000..265b93a --- /dev/null +++ b/g06_funktionen_und_module/test_aufgabe.py @@ -0,0 +1,167 @@ +""" +Testsuite für Kapitel G06: Eigene Funktionen & Module +===================================================== +Schulabgleich: 05.0 & 05.1 +""" + +import math +import sys +import unittest +from pathlib import Path + +# Sicherstellen, dass das aktuelle Verzeichnis im Modulpfad ist +sys.path.insert(0, str(Path(__file__).parent)) + +try: + import aufgabe +except ImportError as e: + raise ImportError(f"Konnte 'aufgabe.py' nicht importieren: {e}") + + +class TestKapitelG06(unittest.TestCase): + + def test_01_hypotenuse(self): + """Prüft die Funktion hypotenuse(a, b).""" + self.assertTrue( + hasattr(aufgabe, "hypotenuse"), + "Fehler: Die Funktion 'hypotenuse' fehlt in aufgabe.py!" + ) + res = aufgabe.hypotenuse(3.0, 4.0) + self.assertIsNotNone( + res, + "Fehler: hypotenuse(3.0, 4.0) gibt None zurück. Vergiss nicht das 'return'!" + ) + # Standard rechtwinklige Dreiecke + self.assertAlmostEqual( + res, 5.0, places=4, + msg="Fehler: hypotenuse(3.0, 4.0) sollte 5.0 sein (3-4-5 Dreieck)!" + ) + self.assertAlmostEqual( + aufgabe.hypotenuse(5.0, 12.0), 13.0, places=4, + msg="Fehler: hypotenuse(5.0, 12.0) sollte 13.0 sein (5-12-13 Dreieck)!" + ) + self.assertAlmostEqual( + aufgabe.hypotenuse(6.0, 8.0), 10.0, places=4, + msg="Fehler: hypotenuse(6.0, 8.0) sollte 10.0 sein!" + ) + # Fehlerbehandlung für negative Längen + with self.assertRaises(ValueError, msg="Fehler: hypotenuse(-3.0, 4.0) muss einen ValueError auslösen!"): + aufgabe.hypotenuse(-3.0, 4.0) + with self.assertRaises(ValueError, msg="Fehler: hypotenuse(3.0, -4.0) muss einen ValueError auslösen!"): + aufgabe.hypotenuse(3.0, -4.0) + + def test_02_zylinder_volumen(self): + """Prüft die Funktion zylinder_volumen(radius, hoehe).""" + self.assertTrue( + hasattr(aufgabe, "zylinder_volumen"), + "Fehler: Die Funktion 'zylinder_volumen' fehlt in aufgabe.py!" + ) + res = aufgabe.zylinder_volumen(3.0, 5.0) + self.assertIsNotNone( + res, + "Fehler: zylinder_volumen(3.0, 5.0) gibt None zurück. Vergiss nicht das 'return'!" + ) + # V = pi * r^2 * h + # Für r=3, h=5 -> pi * 9 * 5 = 45 * pi ≈ 141.3716694 + erwartet = math.pi * 9 * 5 + self.assertAlmostEqual( + res, erwartet, places=4, + msg=f"Fehler: zylinder_volumen(3.0, 5.0) sollte {erwartet:.4f} sein!" + ) + # Für r=1, h=1 -> pi + self.assertAlmostEqual( + aufgabe.zylinder_volumen(1.0, 1.0), math.pi, places=4, + msg="Fehler: zylinder_volumen(1.0, 1.0) sollte math.pi entsprechen!" + ) + # Für r=0, h=10 -> 0.0 + self.assertAlmostEqual( + aufgabe.zylinder_volumen(0.0, 10.0), 0.0, places=4, + msg="Fehler: zylinder_volumen(0.0, 10.0) sollte 0.0 sein!" + ) + # Fehlerbehandlung für negative Werte + with self.assertRaises(ValueError, msg="Fehler: zylinder_volumen(-1.0, 5.0) muss einen ValueError auslösen!"): + aufgabe.zylinder_volumen(-1.0, 5.0) + with self.assertRaises(ValueError, msg="Fehler: zylinder_volumen(2.0, -3.0) muss einen ValueError auslösen!"): + aufgabe.zylinder_volumen(2.0, -3.0) + + def test_03_wuerfle_wuerfel_standard(self): + """Prüft wuerfle_wuerfel mit Standardwert seiten=6.""" + self.assertTrue( + hasattr(aufgabe, "wuerfle_wuerfel"), + "Fehler: Die Funktion 'wuerfle_wuerfel' fehlt in aufgabe.py!" + ) + wuerfe = aufgabe.wuerfle_wuerfel(20) + self.assertIsInstance( + wuerfe, list, + "Fehler: wuerfle_wuerfel muss eine Liste zurückgeben!" + ) + self.assertEqual(len(wuerfe), 20, "Fehler: Bei anzahl=20 müssen 20 Würfelergebnisse geliefert werden!") + for w in wuerfe: + self.assertIsInstance(w, int, "Fehler: Jedes Würfelergebnis muss eine ganze Zahl (int) sein!") + self.assertTrue(1 <= w <= 6, f"Fehler: Würfelergebnis {w} liegt nicht zwischen 1 und 6!") + + # 0 Würfel + leer = aufgabe.wuerfle_wuerfel(0) + self.assertEqual(leer, [], "Fehler: wuerfle_wuerfel(0) sollte eine leere Liste [] zurückgeben!") + + def test_04_wuerfle_wuerfel_custom_seiten(self): + """Prüft wuerfle_wuerfel mit benutzerdefinierter Seitenzahl (z.B. W20).""" + wuerfe_w20 = aufgabe.wuerfle_wuerfel(25, seiten=20) + self.assertIsInstance( + wuerfe_w20, list, + "Fehler: wuerfle_wuerfel(25, seiten=20) muss eine Liste zurückgeben!" + ) + self.assertEqual(len(wuerfe_w20), 25) + for w in wuerfe_w20: + self.assertTrue(1 <= w <= 20, f"Fehler: W20-Ergebnis {w} liegt nicht im Bereich [1, 20]!") + + # W2 (Münzwurf) + wuerfe_w2 = aufgabe.wuerfle_wuerfel(10, seiten=2) + self.assertIsInstance(wuerfe_w2, list) + for w in wuerfe_w2: + self.assertIn(w, [1, 2], f"Fehler: W2-Ergebnis {w} darf nur 1 oder 2 sein!") + + def test_05_wuerfle_wuerfel_ungueltig(self): + """Prüft ungültige Argumente bei wuerfle_wuerfel.""" + with self.assertRaises(ValueError, msg="Fehler: wuerfle_wuerfel(-1) muss ValueError auslösen!"): + aufgabe.wuerfle_wuerfel(-1) + with self.assertRaises(ValueError, msg="Fehler: wuerfle_wuerfel(5, seiten=0) muss ValueError auslösen!"): + aufgabe.wuerfle_wuerfel(5, seiten=0) + with self.assertRaises(ValueError, msg="Fehler: wuerfle_wuerfel(5, seiten=-4) muss ValueError auslösen!"): + aufgabe.wuerfle_wuerfel(5, seiten=-4) + + def test_06_statistik(self): + """Prüft die Funktion statistik(zahlen).""" + self.assertTrue( + hasattr(aufgabe, "statistik"), + "Fehler: Die Funktion 'statistik' fehlt in aufgabe.py!" + ) + zahlen = [10.0, 20.0, 30.0, 40.0, 50.0] + ergebnis = aufgabe.statistik(zahlen) + self.assertIsInstance( + ergebnis, tuple, + "Fehler: statistik(...) muss ein Tupel (min_wert, max_wert, mittelwert) zurückgeben!" + ) + self.assertEqual(len(ergebnis), 3, "Fehler: Das Ergebnis-Tupel muss genau 3 Elemente haben!") + + min_w, max_w, mittel = ergebnis + self.assertEqual(min_w, 10.0, "Fehler: Minimum von [10, 20, 30, 40, 50] sollte 10.0 sein!") + self.assertEqual(max_w, 50.0, "Fehler: Maximum von [10, 20, 30, 40, 50] sollte 50.0 sein!") + self.assertEqual(mittel, 30.0, "Fehler: Mittelwert von [10, 20, 30, 40, 50] sollte 30.0 sein!") + + # Einzelelement + einz = aufgabe.statistik([7.5]) + self.assertIsInstance(einz, tuple) + einz_min, einz_max, einz_mit = einz + self.assertEqual(einz_min, 7.5) + self.assertEqual(einz_max, 7.5) + self.assertEqual(einz_mit, 7.5) + + def test_07_statistik_leere_liste(self): + """Prüft Fehlerbehandlung bei leerer Liste in statistik().""" + with self.assertRaises(ValueError, msg="Fehler: statistik([]) für eine leere Liste muss einen ValueError auslösen!"): + aufgabe.statistik([]) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g07_listen_und_sequenzen/README.md b/g07_listen_und_sequenzen/README.md new file mode 100644 index 0000000..cc69005 --- /dev/null +++ b/g07_listen_und_sequenzen/README.md @@ -0,0 +1,136 @@ +# Kapitel G07: Listen & Sequenzen in Python 🚂📋 + +Willkommen im Kapitel **Listen und Sequenzen**! Listen gehören zu den wichtigsten und am häufigsten verwendeten Datenstrukturen in Python. Mit ihnen kannst du mehrere Daten geordnet in einer einzigen Variable speichern. + +--- + +## 🚂 1. Die Zugwaggon-Analogie: Was ist eine Liste? + +Stell dir eine Liste wie einen **Zug mit durchnummerierten Waggons** vor: + +```text + ┌───────────┬───────────┬───────────┬───────────┬───────────┐ +Werte: │ "Apfel" │ "Banane" │ "Kirsche"│ "Dattel" │ "Erdbeere" + └───────────┴───────────┴───────────┴───────────┴───────────┘ +Positiver Index: 0 1 2 3 4 +Negativer Index: -5 -4 -3 -2 -1 +``` + +- **Eckige Klammern `[]`:** Definieren eine Liste (`fruechte = ["Apfel", "Banane", "Kirsche"]`). +- **Indexierung ab 0:** Der erste Waggon ist immer Index `0`! +- **Negativer Index:** `-1` ist immer das letzte Element, `-2` das vorletzte. +- **Veränderbarkeit (mutable):** Du kannst Waggons austauschen, neue anhängen oder abkoppeln! + +--- + +## ✂️ 2. Slicing: Teile einer Liste herausschneiden + +Mit dem **Slice-Operator `[start:stop:step]`** kannst du beliebige Abschnitte aus einer Sequenz herausschneiden: + +```python +zahlen = [10, 20, 30, 40, 50, 60] + +# 1. Bereich von Index 1 bis vor Index 4: +print(zahlen[1:4]) # -> [20, 30, 40] + +# 2. Vom Anfang bis vor Index 3: +print(zahlen[:3]) # -> [10, 20, 30] + +# 3. Ab Index 3 bis zum Ende: +print(zahlen[3:]) # -> [40, 50, 60] + +# 4. Jedes 2. Element (Schrittweite 2): +print(zahlen[::2]) # -> [10, 30, 50] + +# 5. Ganze Liste umkehren (Rückwärts-Schritt): +print(zahlen[::-1]) # -> [60, 50, 40, 30, 20, 10] +``` + +> 💡 **Merke:** Der `stop`-Index ist immer **exklusiv** (wird nicht mehr mitgenommen). `zahlen[1:4]` nimmt also die Indizes 1, 2 und 3. + +--- + +## 🛠️ 3. Die wichtigsten Listen-Methoden + +Listen bringen viele eingebaute Methoden mit, um Elemente hinzuzufügen, zu löschen oder zu sortieren: + +| Methode | Beschreibung | Beispiel | +| :--- | :--- | :--- | +| `.append(x)` | Fügt `x` ans Ende der Liste an | `tiere.append("Hund")` | +| `.insert(i, x)` | Fügt `x` an Position `i` ein | `tiere.insert(0, "Katze")` | +| `.remove(x)` | Entfernt das erste Vorkommen von `x` | `tiere.remove("Hund")` *(wirft Fehler, wenn `x` fehlt!)* | +| `.pop()` | Entfernt und liefert das letzte Element | `letztes = tiere.pop()` | +| `.pop(i)` | Entfernt und liefert Element an Index `i` | `erstes = tiere.pop(0)` | +| `.sort()` | Sortiert die Liste aufsteigend in-place | `zahlen.sort()` | +| `.reverse()` | Dreht die Liste an Ort und Stelle um | `zahlen.reverse()` | + +--- + +## 🧮 4. Nützliche eingebaute Funktionen & `in`-Operator + +Python bietet praktische globale Funktionen für Sequenzen: + +```python +noten = [1.5, 2.0, 3.7, 1.0, 2.3] + +# Länge (Anzahl der Elemente): +print(len(noten)) # -> 5 + +# Kleinster und größter Wert: +print(min(noten)) # -> 1.0 (beste Note) +print(max(noten)) # -> 3.7 (schlechteste Note) + +# Summe aller Zahlen: +print(sum(noten)) # -> 10.5 + +# Notendurchschnitt: +schnitt = sum(noten) / len(noten) +print(f"Durchschnitt: {schnitt:.2f}") # -> 2.10 + +# Prüfen, ob ein Element enthalten ist: +if 1.0 in noten: + print("Glückwunsch zur Bestnote!") +``` + +--- + +## 🛒 5. Praxisbeispiel: Einkaufsliste verwalten + +```python +# ============================================================================== +# Einkaufslisten-Manager +# ============================================================================== +einkauf = ["Milch", "Brot", "Käse"] + +# 1. Etwas hinzufügen: +einkauf.append("Äpfel") + +# 2. Prüfen und sicher entfernen: +artikel = "Brot" +if artikel in einkauf: + einkauf.remove(artikel) + print(f"✅ '{artikel}' wurde eingekauft und abgehakt.") +else: + print(f"❌ '{artikel}' stand nicht auf der Liste.") + +# 3. Sortiert ausgeben: +einkauf.sort() +print("Verbleibende Einkäufe:", einkauf) +``` + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne `aufgabe.py` und implementiere die 5 Funktionen: + +1. **`liste_umdrehen(liste)`**: Gibt eine neue, umgedrehte Liste zurück, ohne das Original zu verändern. +2. **`filtere_positive_zahlen(zahlen)`**: Filtert nur Zahlen `> 0` heraus. +3. **`entferne_element(liste, element)`**: Entfernt das Element sicher (ohne `ValueError` Absturz) und gibt `True` zurück, wenn es gefunden wurde, sonst `False`. +4. **`mittlere_elemente(liste)`**: Schneidet mit Slicing das erste und letzte Element ab. Bei `<= 2` Elementen wird `[]` zurückgegeben. +5. **`noten_durchschnitt_ohne_ausreisser(noten)`**: Entfernt Minimum und Maximum aus einer Kopie der Liste und berechnet den bereinigten Schnitt. + +### Testen deiner Lösung: +```bash +python3 test_aufgabe.py +``` diff --git a/g07_listen_und_sequenzen/aufgabe.py b/g07_listen_und_sequenzen/aufgabe.py new file mode 100644 index 0000000..a2fe04b --- /dev/null +++ b/g07_listen_und_sequenzen/aufgabe.py @@ -0,0 +1,131 @@ +""" +Kapitel G07: Listen & Sequenzen in Python (Schulabgleich 09.0 & 11.0) +==================================================================== + +In dieser Aufgabe vertiefst du dein Wissen über Python-Listen, Indexierung, +Slicing, Listen-Methoden und nützliche eingebaute Funktionen. +""" + + +def liste_umdrehen(liste: list) -> list: + """ + TODO 1: Gibt eine NEUE Liste zurück, deren Elemente in umgekehrter + Reihenfolge angeordnet sind. Die Original-Liste darf nicht verändert werden! + + Beispiel: + liste_umdrehen([1, 2, 3, 4]) -> [4, 3, 2, 1] + liste_umdrehen(["A", "B", "C"]) -> ["C", "B", "A"] + + Tipp: + Verwende Slicing mit Schrittweite -1: `liste[::-1]` + oder erstelle eine Kopie und wende `.reverse()` an. + """ + # Schreibe hier deinen Code für TODO 1: + pass + + +def filtere_positive_zahlen(zahlen: list[float]) -> list[float]: + """ + TODO 2: Gibt eine neue Liste zurück, die nur die echten positiven Zahlen + (Zahl > 0) aus der übergebenen Liste enthält. 0 und negative Zahlen werden ignoriert. + + Beispiel: + filtere_positive_zahlen([-3, 5, 0, 12, -1, 8.5]) -> [5, 12, 8.5] + + Tipp: + Erstelle eine leere Ergebnisliste `positiv = []` und iteriere mit einer + for-Schleife über alle Zahlen. Wenn `z > 0`, füge sie mit `.append(z)` hinzu. + """ + # Schreibe hier deinen Code für TODO 2: + pass + + +def entferne_element(liste: list, element) -> bool: + """ + TODO 3: Entfernt das erste Vorkommen von 'element' aus der übergebenen 'liste'. + - Wenn das Element in der Liste vorkommt: Entferne es und gib True zurück. + - Wenn das Element NICHT in der Liste ist: Ändere die Liste nicht und gib False zurück. + + WICHTIG: Die Funktion darf keinen Fehler (ValueError) werfen! + + Beispiel: + namen = ["Anna", "Ben", "Clara"] + entferne_element(namen, "Ben") -> True (namen ist jetzt ["Anna", "Clara"]) + entferne_element(namen, "David") -> False (namen bleibt ["Anna", "Clara"]) + + Tipp: + Prüfe zuerst mit `if element in liste:`, bevor du `liste.remove(element)` aufrufst. + """ + # Schreibe hier deinen Code für TODO 3: + pass + + +def mittlere_elemente(liste: list) -> list: + """ + TODO 4: Gibt einen Teil der Liste zurück, der weder das erste (Index 0) + noch das letzte (Index -1) Element enthält. + - Wenn die Liste 2 oder weniger Elemente hat, gib eine leere Liste [] zurück. + + Beispiel: + mittlere_elemente([10, 20, 30, 40, 50]) -> [20, 30, 40] + mittlere_elemente(["Start", "Mitte", "Ende"]) -> ["Mitte"] + mittlere_elemente([1, 2]) -> [] + mittlere_elemente([]) -> [] + + Tipp: + Nutze Slicing: `liste[1:-1]`. Wenn `len(liste) <= 2`, gib `[]` zurück. + """ + # Schreibe hier deinen Code für TODO 4: + pass + + +def noten_durchschnitt_ohne_ausreisser(noten: list[float]) -> float: + """ + TODO 5: Berechnet den Notendurchschnitt, nachdem die beste (niedrigste Zahl) + und die schlechteste Note (höchste Zahl) entfernt wurden. + - Bei weniger als 3 Noten kann kein sinnvoller bereinigter Schnitt berechnet werden; + gib in diesem Fall 0.0 zurück. + - Die übergebene Liste 'noten' darf nicht dauerhaft verändert werden! + + Beispiel: + noten_durchschnitt_ohne_ausreisser([1.0, 2.0, 3.0, 4.0, 5.0]) + -> Beste Note: 1.0, Schlechteste Note: 5.0 werden entfernt. + -> Verbleibende Noten: [2.0, 3.0, 4.0] + -> Durchschnitt: (2.0 + 3.0 + 4.0) / 3 = 3.0 + + Tipp: + 1. Wenn `len(noten) < 3`: `return 0.0` + 2. Erstelle eine Kopie der Liste: `kopie = list(noten)` + 3. Finde Minimum mit `min(kopie)` und Maximum mit `max(kopie)`. + 4. Entferne je ein Exemplar mit `kopie.remove(...)`. + 5. Berechne den Schnitt mit `sum(kopie) / len(kopie)` und gib ihn zurück. + """ + # Schreibe hier deinen Code für TODO 5: + pass + + +# ============================================================================== +# Hauptprogramm zum Ausprobieren: +# (Führe die Datei aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("--- Teste deine Listen-Funktionen ---") + + test_liste = [10, 20, 30, 40, 50] + print("Original:", test_liste) + print("Umgedreht (TODO 1):", liste_umdrehen(test_liste)) + + zahlen = [-5, 12, 0, -3, 42, 7] + print("Positive Zahlen (TODO 2):", filtere_positive_zahlen(zahlen)) + + tiere = ["Hund", "Katze", "Maus"] + erfolg = entferne_element(tiere, "Katze") + print(f"Katze entfernt (TODO 3)? {erfolg} -> Rest: {tiere}") + erfolg_fehlt = entferne_element(tiere, "Elefant") + print(f"Elefant entfernt (TODO 3)? {erfolg_fehlt} -> Rest: {tiere}") + + print("Mittlere Elemente von [1, 2, 3, 4, 5] (TODO 4):", mittlere_elemente([1, 2, 3, 4, 5])) + + noten = [1.0, 2.0, 3.0, 4.0, 5.0] + schnitt = noten_durchschnitt_ohne_ausreisser(noten) + print(f"Bereinigter Notenschnitt von {noten} (TODO 5): {schnitt}") diff --git a/g07_listen_und_sequenzen/index.html b/g07_listen_und_sequenzen/index.html new file mode 100644 index 0000000..292051c --- /dev/null +++ b/g07_listen_und_sequenzen/index.html @@ -0,0 +1,276 @@ + + + + + + Kapitel G07: Listen & Sequenzen in Python + + + + +
+
+ +
Grundlagen G07 (Schulabgleich 09.0 & 11.0)
+

Listen & Sequenzen in Python 🚂📋

+

Lerne, wie du Daten in Listen sammelst, mit Index und Slicing zuschneidest und mit Methoden verwaltest.

+
+
+ +
+ + +
+

1. 🚂 Die Zugwaggon-Analogie: Was ist eine Liste?

+ +

+ Eine Liste (list) ist ein geordneter Behälter für mehrere Werte. + Du kannst dir eine Liste wie einen Zug mit durchnummerierten Waggons vorstellen: +

+ +
+
Zugwaggons & Indexe:
+

Jedes Element hat einen festen Platzhalter – den Index. Python zählt immer ab 0!

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Positiver Index01234
Werte (Waggons)"Apfel""Banane""Kirsche""Dattel""Erdbeere"
Negativer Index-5-4-3-2-1
+
+
+ +
+
+
+ + + +
+ Python: Index-Zugriff +
+
fruechte = ["Apfel", "Banane", "Kirsche", "Dattel", "Erdbeere"]
+
+print(fruechte[0])   # -> "Apfel" (Erstes Element)
+print(fruechte[2])   # -> "Kirsche" (Drittes Element)
+print(fruechte[-1])  # -> "Erdbeere" (Letztes Element)
+print(fruechte[-2])  # -> "Dattel" (Vorletztes Element)
+
+
+ + +
+

2. ✂️ Slicing: Teile einer Liste herausschneiden

+ +

+ Mit dem Slice-Muster liste[start:stop:step] schneidest du beliebige Teilbereiche heraus. + Wichtig: Der stop-Index ist immer exklusiv (wird nicht mitgenommen)! +

+ +
+
+
+ + + +
+ Python: Slicing Beispiele +
+
zahlen = [10, 20, 30, 40, 50, 60]
+
+# 1. Bereich von Index 1 bis vor Index 4:
+print(zahlen[1:4])   # -> [20, 30, 40]
+
+# 2. Vom Anfang bis vor Index 3:
+print(zahlen[:3])    # -> [10, 20, 30]
+
+# 3. Ab Index 3 bis ganz zum Schluss:
+print(zahlen[3:])    # -> [40, 50, 60]
+
+# 4. Alle Elemente ohne erstes und letztes:
+print(zahlen[1:-1])  # -> [20, 30, 40, 50]
+
+# 5. Jedes 2. Element (Schrittweite 2):
+print(zahlen[::2])   # -> [10, 30, 50]
+
+# 6. Liste komplett rückwärts:
+print(zahlen[::-1])  # -> [60, 50, 40, 30, 20, 10]
+
+ +
+
💡 Slicing erzeugt immer eine NEUE Liste:
+ Wenn du kopie = zahlen[:] oder rueckwaerts = zahlen[::-1] schreibst, bleibt die Original-Liste völlig unverändert! +
+
+ + +
+

3. 🛠️ Methoden & Eingebaute Funktionen

+ +

Listen bieten praktische Werkzeuge, um Elemente zu verändern, zu durchsuchen und auszuwerten:

+ +
+
+
+ + + +
+ Python: Listen manipulieren +
+
tiere = ["Hund", "Katze"]
+
+# Am Ende anfügen:
+tiere.append("Maus")         # -> ["Hund", "Katze", "Maus"]
+
+# An bestimmter Stelle einfügen:
+tiere.insert(0, "Papagei")   # -> ["Papagei", "Hund", "Katze", "Maus"]
+
+# Letztes Element entfernen und zurückgeben:
+raus = tiere.pop()           # raus = "Maus", Rest: ["Papagei", "Hund", "Katze"]
+
+# Bestimmten Wert sicher mit 'in' prüfen und entfernen:
+if "Katze" in tiere:
+    tiere.remove("Katze")    # Entfernt "Katze"
+
+# Sortieren & Umdrehen (in-place am Original):
+tiere.sort()
+tiere.reverse()
+
+ +
+
⚠️ Vorsicht bei .remove():
+ Wenn du liste.remove("Etwas") aufrufst und "Etwas" gar nicht in der Liste ist, stürzt dein Programm mit einem ValueError ab! Prüfe vorher immer mit if "Etwas" in liste:. +
+ +

Statistik & Rechnen mit Listen

+
+
noten = [1.0, 2.3, 1.7, 3.0]
+
+print(len(noten))   # -> 4 (Anzahl der Elemente)
+print(min(noten))   # -> 1.0 (kleinste Zahl / beste Note)
+print(max(noten))   # -> 3.0 (größte Zahl / schlechteste Note)
+print(sum(noten))   # -> 8.0 (Summe)
+schnitt = sum(noten) / len(noten)  # -> 2.0 (Durchschnitt)
+
+
+ + +
+

4. 🎯 Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei g07_listen_und_sequenzen/aufgabe.py und löse die 5 TODOs:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 3 (Element sicher entfernen) +
+
+
def entferne_element(liste, element):
+    if element in liste:
+        liste.remove(element)
+        return True
+    return False
+
+
+
+ +
+ 🔍 Tipp zu TODO 5 (Notenschnitt ohne Ausreißer) +
+
+
def noten_durchschnitt_ohne_ausreisser(noten):
+    if len(noten) < 3:
+        return 0.0
+    kopie = list(noten)
+    kopie.remove(min(kopie))
+    kopie.remove(max(kopie))
+    return sum(kopie) / len(kopie)
+
+
+
+
+ + +
+

5. 🧪 Lösung überprüfen

+

Führe im Terminal folgenden Befehl aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g07_listen_und_sequenzen/musterloesung.py b/g07_listen_und_sequenzen/musterloesung.py new file mode 100644 index 0000000..5e81268 --- /dev/null +++ b/g07_listen_und_sequenzen/musterloesung.py @@ -0,0 +1,57 @@ +""" +Kapitel G07: Listen & Sequenzen – Musterlösung +=============================================== +""" + + +def liste_umdrehen(liste: list) -> list: + """Gibt eine neue umgedrehte Liste zurück.""" + return liste[::-1] + + +def filtere_positive_zahlen(zahlen: list[float]) -> list[float]: + """Filtert alle Zahlen > 0 heraus.""" + return [z for z in zahlen if z > 0] + + +def entferne_element(liste: list, element) -> bool: + """Entfernt ein Element sicher, falls es existiert.""" + if element in liste: + liste.remove(element) + return True + return False + + +def mittlere_elemente(liste: list) -> list: + """Gibt alle Elemente ohne erstes und letztes Element zurück.""" + if len(liste) <= 2: + return [] + return liste[1:-1] + + +def noten_durchschnitt_ohne_ausreisser(noten: list[float]) -> float: + """Entfernt Minimum und Maximum und berechnet den arithmetischen Mittelwert.""" + if len(noten) < 3: + return 0.0 + kopie = list(noten) + kopie.remove(min(kopie)) + kopie.remove(max(kopie)) + return sum(kopie) / len(kopie) + + +if __name__ == "__main__": + test_liste = [10, 20, 30, 40, 50] + print("Original:", test_liste) + print("Umgedreht:", liste_umdrehen(test_liste)) + + zahlen = [-5, 12, 0, -3, 42, 7] + print("Positive Zahlen:", filtere_positive_zahlen(zahlen)) + + tiere = ["Hund", "Katze", "Maus"] + erfolg = entferne_element(tiere, "Katze") + print(f"Katze entfernt? {erfolg} -> Rest: {tiere}") + + print("Mittlere Elemente:", mittlere_elemente([1, 2, 3, 4, 5])) + + noten = [1.0, 2.0, 3.0, 4.0, 5.0] + print(f"Notenschnitt ohne Ausreißer von {noten}: {noten_durchschnitt_ohne_ausreisser(noten)}") diff --git a/g07_listen_und_sequenzen/test_aufgabe.py b/g07_listen_und_sequenzen/test_aufgabe.py new file mode 100644 index 0000000..9b242d0 --- /dev/null +++ b/g07_listen_und_sequenzen/test_aufgabe.py @@ -0,0 +1,123 @@ +import unittest +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent)) + +try: + import aufgabe +except ImportError as e: + raise ImportError(f"Konnte 'aufgabe.py' nicht importieren: {e}") + + +class TestKapitelG07(unittest.TestCase): + + def test_01_liste_umdrehen(self): + """Prüft, ob eine Liste umgedreht wird, ohne das Original zu verändern.""" + original = [1, 2, 3, 4, 5] + kopie_check = list(original) + ergebnis = aufgabe.liste_umdrehen(original) + + self.assertEqual( + ergebnis, + [5, 4, 3, 2, 1], + "Fehler: liste_umdrehen([1, 2, 3, 4, 5]) sollte [5, 4, 3, 2, 1] liefern!" + ) + self.assertEqual( + original, + kopie_check, + "Fehler: Die Original-Liste darf durch liste_umdrehen nicht verändert werden!" + ) + + # Leere Liste & Einzelelement + self.assertEqual(aufgabe.liste_umdrehen([]), [], "Fehler bei leerer Liste!") + self.assertEqual(aufgabe.liste_umdrehen(["A"]), ["A"], "Fehler bei einelementiger Liste!") + + def test_02_filtere_positive_zahlen(self): + """Prüft das Filtern positiver Zahlen (Zahlen > 0).""" + eingabe = [-10, 5.5, 0, -1, 42, 0.0, 7] + ergebnis = aufgabe.filtere_positive_zahlen(eingabe) + erwartet = [5.5, 42, 7] + self.assertEqual( + ergebnis, + erwartet, + f"Fehler: filtere_positive_zahlen({eingabe}) sollte {erwartet} liefern, war aber {ergebnis}!" + ) + + # Nur negative / Nullen + self.assertEqual( + aufgabe.filtere_positive_zahlen([-5, -1, 0]), + [], + "Fehler: Bei nur negativen Zahlen und 0 muss das Ergebnis [] sein!" + ) + + # Leere Liste + self.assertEqual(aufgabe.filtere_positive_zahlen([]), []) + + def test_03_entferne_element(self): + """Prüft das sichere Entfernen eines Elements aus einer Liste.""" + farben = ["Rot", "Grün", "Blau", "Gelb"] + + # Vorhandenes Element entfernen + erfolg = aufgabe.entferne_element(farben, "Grün") + self.assertTrue(erfolg, "Fehler: entferne_element sollte True zurückgeben, wenn Element existiert!") + self.assertEqual(farben, ["Rot", "Blau", "Gelb"], "Fehler: 'Grün' wurde nicht aus der Liste entfernt!") + + # Nicht vorhandenes Element + erfolg_fehlt = aufgabe.entferne_element(farben, "Schwarz") + self.assertFalse(erfolg_fehlt, "Fehler: entferne_element sollte False zurückgeben, wenn Element nicht existiert!") + self.assertEqual(farben, ["Rot", "Blau", "Gelb"], "Fehler: Liste darf sich bei fehlendem Element nicht ändern!") + + # Mehrfache Vorkommen: nur das erste soll entfernt werden + zahlen = [1, 2, 2, 3] + aufgabe.entferne_element(zahlen, 2) + self.assertEqual(zahlen, [1, 2, 3], "Fehler: Bei Duplikaten sollte nur das erste Vorkommen entfernt werden!") + + def test_04_mittlere_elemente(self): + """Prüft das Extrahieren der mittleren Elemente (ohne erstes und letztes).""" + self.assertEqual( + aufgabe.mittlere_elemente([10, 20, 30, 40, 50]), + [20, 30, 40], + "Fehler bei [10, 20, 30, 40, 50]!" + ) + self.assertEqual( + aufgabe.mittlere_elemente(["A", "B", "C"]), + ["B"], + "Fehler bei 3 Elementen!" + ) + + # Randfälle: <= 2 Elemente + self.assertEqual(aufgabe.mittlere_elemente([1, 2]), [], "Fehler: Liste mit 2 Elementen muss [] liefern!") + self.assertEqual(aufgabe.mittlere_elemente([1]), [], "Fehler: Liste mit 1 Element muss [] liefern!") + self.assertEqual(aufgabe.mittlere_elemente([]), [], "Fehler: Leere Liste muss [] liefern!") + + def test_05_noten_durchschnitt_ohne_ausreisser(self): + """Prüft die Durchschnittsberechnung ohne Best- und Schlechtnote.""" + noten = [1.0, 2.0, 3.0, 4.0, 5.0] + original_noten = list(noten) + schnitt = aufgabe.noten_durchschnitt_ohne_ausreisser(noten) + + self.assertAlmostEqual( + schnitt, + 3.0, + places=4, + msg="Fehler: Notenschnitt für [1.0, 2.0, 3.0, 4.0, 5.0] ohne 1.0 und 5.0 sollte 3.0 sein!" + ) + self.assertEqual( + noten, + original_noten, + "Fehler: Die übergebene Liste 'noten' darf nicht mutiert werden!" + ) + + # Weiteres Notenbeispiel + noten2 = [5.0, 1.0, 2.0, 3.0] # min=1.0, max=5.0 -> rest=[2.0, 3.0] -> avg=2.5 + self.assertAlmostEqual(aufgabe.noten_durchschnitt_ohne_ausreisser(noten2), 2.5, places=4) + + # Randfälle: weniger als 3 Noten + self.assertEqual(aufgabe.noten_durchschnitt_ohne_ausreisser([1.0, 2.0]), 0.0) + self.assertEqual(aufgabe.noten_durchschnitt_ohne_ausreisser([3.0]), 0.0) + self.assertEqual(aufgabe.noten_durchschnitt_ohne_ausreisser([]), 0.0) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g08_textverarbeitung_und_strings/README.md b/g08_textverarbeitung_und_strings/README.md new file mode 100644 index 0000000..a88bae8 --- /dev/null +++ b/g08_textverarbeitung_und_strings/README.md @@ -0,0 +1,127 @@ +# Kapitel G08: Textverarbeitung & Strings in Python 🔤✨ + +Willkommen im Kapitel **Textverarbeitung und Strings**! Strings (Zeichenketten) gehören zu den vielseitigsten Datentypen in Python. In diesem Kapitel lernst du alle wichtigen String-Methoden zur Bereinigung, Suche, Zerlegung und Formatierung kennen. + +--- + +## 🏛️ 1. Die Stempel-Analogie: Strings sind unveränderlich (immutable)! + +Ein grundlegendes Konzept in Python: **Strings können nach ihrer Erstellung nicht mehr verändert werden!** + +```python +name = "python" +# ❌ Das funktioniert NICHT und wirft einen TypeError: +# name[0] = "P" + +# ✅ Stattdessen erzeugt jede String-Methode einen NEUEN String: +name = name.capitalize() # Erzeugt "Python" und weist es neu zu +``` + +> 🖨️ **Die Stempel-Analogie:** Stell dir einen String wie gedruckten Text auf Papier vor. Du kannst die Buchstaben auf dem Papier nicht wegradieren. Jede String-Methode druckt stattdessen ein **frisches neues Blatt Papier** mit dem geänderten Text aus! + +--- + +## 🧰 2. Das große String-Methoden Cheat-Sheet + +Python stellt eine Fülle praktischer Methoden für Strings bereit: + +### A. Groß- & Kleinschreibung +```python +text = "hallo welt" +print(text.upper()) # -> "HALLO WELT" (alles GROSS) +print(text.lower()) # -> "hallo welt" (alles klein) +print(text.capitalize()) # -> "Hallo welt" (nur erstes Zeichen groß) +print(text.title()) # -> "Hallo Welt" (jedes Wort groß) +``` + +### B. Leerzeichen & Whitespace entfernen +```python +eingabe = " max_mustermann \n" +print(eingabe.strip()) # -> "max_mustermann" (vorne & hinten bereinigt) +print(eingabe.lstrip()) # -> "max_mustermann \n" (nur links) +print(eingabe.rstrip()) # -> " max_mustermann" (nur rechts) +``` + +### C. Suchen, Zählen & Ersetzen +```python +satz = "Python ist super und Python macht Spaß" + +# Ersetzen: +print(satz.replace("Python", "Coding")) +# -> "Coding ist super und Coding macht Spaß" + +# Vorkommen zählen: +print(satz.count("Python")) # -> 2 + +# Position finden (-1 wenn nicht gefunden): +print(satz.find("super")) # -> 11 + +# Anfang & Ende prüfen (liefert True / False): +print(satz.startswith("Python")) # -> True +print(satz.endswith("Spaß")) # -> True +``` + +### D. Zerlegen (`.split`) & Zusammenfügen (`.join`) +```python +# 1. An Trennzeichen in eine Liste zerlegen: +daten = "Max;Mustermann;18;Informatik" +teile = daten.split(";") +print(teile) # -> ["Max", "Mustermann", "18", "Informatik"] + +# 2. Ohne Parameter zerlegt .split() an beliebigen Leerzeichen: +woerter = " Python macht Spaß! ".split() +print(woerter) # -> ["Python", "macht", "Spaß!"] + +# 3. Eine Liste mit einem Trennzeichen zu einem String zusammenfügen: +zutaten = ["Mehl", "Zucker", "Eier", "Milch"] +kuchen = ", ".join(zutaten) +print(kuchen) # -> "Mehl, Zucker, Eier, Milch" +``` + +--- + +## ✂️ 3. Strings sind Sequenzen (Index & Slicing) + +Genau wie Listen kannst du auch auf einzelne Zeichen in Strings über deren Index und Slicing zugreifen: + +```python +wort = "Lagerregal" + +print(wort[0]) # -> "L" (Erster Buchstabe) +print(wort[-1]) # -> "l" (Letzter Buchstabe) +print(wort[-4:]) # -> "egal" (Die letzten 4 Buchstaben) +print(wort[::-1]) # -> "lagerregaL" (String umkehren!) +``` + +--- + +## 🧪 4. Praxisbeispiel: CSV-Daten bereinigen & parsen + +```python +csv_zeile = " anna.meier@schule.at ; 1050 ; Wien ; 4A " + +# 1. Zeile säubern und in Spalten zerlegen: +spalten = [feld.strip() for feld in csv_zeile.split(";")] +email, plz, stadt, klasse = spalten + +print(f"E-Mail: {email.lower()}") +print(f"Standort: {plz} {stadt.title()}") +print(f"Klasse: {klasse}") +``` + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne `aufgabe.py` und implementiere die 5 Funktionen: + +1. **`ist_palindrom(text)`**: Prüft, ob ein Text vorwärts wie rückwärts gleich ist (ignoriert Groß/Klein und Leerzeichen). +2. **`zaehle_vokale(text)`**: Zählt alle Vokale (a, e, i, o, u, ä, ö, ü) im Text. +3. **`bereinige_benutzernamen(name)`**: Entfernt Rand-Leerzeichen, konvertiert zu Kleinbuchstaben und ersetzt innere Leerzeichen durch `_`. +4. **`woerter_zaehlen(text)`**: Nutzt `.split()`, um die Anzahl der Wörter zu ermitteln. +5. **`maskiere_kreditkarte(nummer)`**: Ersetzt alle Zeichen außer den letzten 4 durch `*`. + +### Testen deiner Lösung: +```bash +python3 test_aufgabe.py +``` diff --git a/g08_textverarbeitung_und_strings/aufgabe.py b/g08_textverarbeitung_und_strings/aufgabe.py new file mode 100644 index 0000000..3b808ee --- /dev/null +++ b/g08_textverarbeitung_und_strings/aufgabe.py @@ -0,0 +1,140 @@ +""" +Kapitel G08: Textverarbeitung & Strings in Python (Schulabgleich 11.0) +===================================================================== + +In dieser Aufgabe lernst du die wichtigsten String-Methoden zur Textbereinigung, +Suche, Formatierung und Analyse kennen. +""" + + +def ist_palindrom(text: str) -> bool: + """ + TODO 1: Prüft, ob ein Text ein Palindrom ist (von vorne und hinten + gelesen exakt gleich lautet). + + Regeln: + - Groß- und Kleinschreibung wird ignoriert ('A' == 'a'). + - Leerzeichen werden ignoriert (z.B. "Dreh mal am Herd"). + + Beispiele: + ist_palindrom("Anna") -> True + ist_palindrom("Lagerregal") -> True + ist_palindrom("Dreh mal am Herd") -> True + ist_palindrom("Python") -> False + + Tipp: + 1. Wandle den Text mit `.lower()` in Kleinbuchstaben um. + 2. Entferne Leerzeichen mit `.replace(" ", "")`. + 3. Vergleiche den bereinigten Text mit seiner Umkehrung: `bereinigt == bereinigt[::-1]`. + """ + # Schreibe hier deinen Code für TODO 1: + pass + + +def zaehle_vokale(text: str) -> int: + """ + TODO 2: Zählt die Anzahl aller Vokale im übergebenen Text. + Als Vokale gelten: a, e, i, o, u sowie die Umlaute ä, ö, ü (Groß- und Kleinschreibung!). + + Beispiele: + zaehle_vokale("Python") -> 1 (nur 'o') + zaehle_vokale("Käsebrot") -> 3 ('ä', 'e', 'o') + zaehle_vokale("SCHÖNES Wetter") -> 4 ('Ö', 'E', 'e', 'e') + zaehle_vokale("HTML") -> 0 + zaehle_vokale("Fly") -> 0 + + Tipp: + Definiere eine Vokal-Menge oder Liste: `vokale = "aeiouäöü"` + Wandle den Text in Kleinbuchstaben um (`text.lower()`) und zähle, + wie viele Zeichen in `vokale` vorkommen. + """ + # Schreibe hier deinen Code für TODO 2: + pass + + +def bereinige_benutzernamen(name: str) -> str: + """ + TODO 3: Bereinigt einen eingegebenen Benutzernamen für ein Login-System. + + Regeln: + 1. Führende und nachfolgende Leerzeichen werden entfernt (.strip()). + 2. Alle Buchstaben werden in Kleinbuchstaben umgewandelt (.lower()). + 3. Alle verbleibenden Leerzeichen zwischen Wörtern werden durch Unterstriche '_' ersetzt (.replace()). + + Beispiele: + bereinige_benutzernamen(" Max Mustermann ") -> "max_mustermann" + bereinige_benutzernamen(" Super Coder 99 ") -> "super_coder_99" + bereinige_benutzernamen("Lukas") -> "lukas" + + Tipp: + Du kannst String-Methoden verketten: `name.strip().lower().replace(" ", "_")` + """ + # Schreibe hier deinen Code für TODO 3: + pass + + +def woerter_zaehlen(text: str) -> int: + """ + TODO 4: Zählt, wie viele Wörter im übergebenen Text vorkommen. + - Mehrere Leerzeichen hintereinander zählen nicht als leere Wörter. + - Ein leerer String "" enthält 0 Wörter. + + Beispiele: + woerter_zaehlen("Hallo Welt") -> 2 + woerter_zaehlen(" Python macht Spaß! ") -> 3 + woerter_zaehlen("") -> 0 + + Tipp: + Verwende `.split()`. Ohne Parameter teilt es an beliebigen Leerzeichen + und ignoriert doppelte Leerzeichen automatisch! `len(text.split())` + """ + # Schreibe hier deinen Code für TODO 4: + pass + + +def maskiere_kreditkarte(nummer: str) -> str: + """ + TODO 5: Maskiert eine sensible Kartennummer oder ID, sodass nur die + letzten 4 Zeichen sichtbar bleiben und alle vorherigen durch '*' ersetzt werden. + + Regeln: + - Hat die Nummer 4 oder weniger Zeichen, wird sie unverändert zurückgegeben. + - Hat sie mehr als 4 Zeichen, werden alle bis auf die letzten 4 durch '*' ersetzt. + + Beispiele: + maskiere_kreditkarte("1234567812345678") -> "************5678" + maskiere_kreditkarte("987654321") -> "*****4321" + maskiere_kreditkarte("1234") -> "1234" + maskiere_kreditkarte("99") -> "99" + + Tipp: + 1. Wenn `len(nummer) <= 4`: `return nummer` + 2. Anzahl der Sterne: `anzahl_sterne = len(nummer) - 4` + 3. Die letzten 4 Zeichen: `letzte_vier = nummer[-4:]` + 4. Gib `("*" * anzahl_sterne) + letzte_vier` zurück. + """ + # Schreibe hier deinen Code für TODO 5: + pass + + +# ============================================================================== +# Hauptprogramm zum Ausprobieren: +# (Führe die Datei aus mit: python3 aufgabe.py) +# ============================================================================== +if __name__ == "__main__": + print("--- Teste deine String-Funktionen ---") + + test_palindrom = "Dreh mal am Herd" + print(f"Ist '{test_palindrom}' ein Palindrom (TODO 1)?", ist_palindrom(test_palindrom)) + + test_vokale = "Käsebrot mit Äpfeln" + print(f"Vokale in '{test_vokale}' (TODO 2):", zaehle_vokale(test_vokale)) + + roher_name = " Max Mustermann 2026 " + print(f"Bereinigter Name von '{roher_name}' (TODO 3):", bereinige_benutzernamen(roher_name)) + + text = " Python macht wirklich großen Spaß! " + print(f"Wortanzahl (TODO 4):", woerter_zaehlen(text)) + + karte = "1234567812345678" + print(f"Maskierte Karte '{karte}' (TODO 5):", maskiere_kreditkarte(karte)) diff --git a/g08_textverarbeitung_und_strings/index.html b/g08_textverarbeitung_und_strings/index.html new file mode 100644 index 0000000..e6780e7 --- /dev/null +++ b/g08_textverarbeitung_und_strings/index.html @@ -0,0 +1,231 @@ + + + + + + Kapitel G08: Textverarbeitung & Strings in Python + + + + +
+
+ +
Grundlagen G08 (Schulabgleich 11.0)
+

Textverarbeitung & Strings 🔤✨

+

Lerne, wie du Texte zerlegst, bereinigst, durchsuchst und formatierst – mit den mächtigen String-Methoden von Python.

+
+
+ +
+ + +
+

1. 🏛️ Die Stempel-Analogie: Strings sind unveränderlich!

+ +

+ In Python sind Strings (str) unveränderlich (immutable). Das bedeutet: + Sobald ein String erzeugt wurde, kann kein einzelner Buchstabe mehr ausgetauscht werden! +

+ +
+
🖨️ Die Stempel- & Druck-Analogie:
+

+ Stell dir einen String wie ein gedrucktes Dokument vor. Wenn du text.upper() oder text.replace("a", "b") aufrufst, + wird nicht der Buchstabe auf dem Papier radiert. + Stattdessen nimmt Python ein frisches, neues Blatt Papier und stempelt den modifizierten Text darauf! +

+
+ +
+
+
+ + + +
+ Python: Immutable Strings +
+
name = "python"
+
+# ❌ Falsch: Einzelnes Zeichen überschreiben geht nicht!
+# name[0] = "P"  -> TypeError: 'str' object does not support item assignment
+
+# ✅ Richtig: Die Methode liefert einen NEUEN String zurück, den wir neu zuweisen:
+name = name.capitalize()  # name ist jetzt "Python"
+
+
+ + +
+

2. 🧰 Das große String-Methoden Cheat-Sheet

+ +

Hier siehst du die wichtigsten Methoden für deine tägliche Textverarbeitung im Überblick:

+ +

A. Groß- & Kleinschreibung

+
+
t = "hallo welt"
+print(t.upper())       # -> "HALLO WELT" (alles GROSS)
+print(t.lower())       # -> "hallo welt" (alles klein)
+print(t.capitalize())  # -> "Hallo welt" (nur erstes Zeichen groß)
+print(t.title())       # -> "Hallo Welt" (jedes Wort groß)
+
+ +

B. Leerzeichen bereinigen (.strip)

+
+
roh = "   max_mustermann \n  "
+print(roh.strip())     # -> "max_mustermann" (entfernt Leerzeichen & Umbrüche an beiden Enden)
+print(roh.lstrip())    # -> entfernt nur links
+print(roh.rstrip())    # -> entfernt nur rechts
+
+ +

C. Suchen, Zählen & Ersetzen

+
+
satz = "Python ist super und Python macht Spaß"
+
+# Ersetzen:
+neu = satz.replace("Python", "Coding")  # Ersetzt alle Vorkommen
+
+# Zählen & Suchen:
+print(satz.count("Python"))      # -> 2
+print(satz.find("super"))        # -> Index 11 (-1 falls nicht vorhanden)
+print(satz.startswith("Python")) # -> True
+print(satz.endswith("Spaß"))    # -> True
+
+ +

D. Zerlegen (.split) & Zusammenfügen (.join)

+
+
# 1. An Trennzeichen zerlegen:
+csv = "Anna;17;Wien"
+spalten = csv.split(";")         # -> ["Anna", "17", "Wien"]
+
+# 2. Ohne Parameter zerlegt .split() an beliebigen Whitespaces:
+woerter = "  Python   macht   Spaß  ".split()
+print(woerter)                 # -> ["Python", "macht", "Spaß"]
+
+# 3. Liste wieder zu einem formatierten String verknüpfen:
+liste = ["Rot", "Grün", "Blau"]
+farben_text = ", ".join(liste)  # -> "Rot, Grün, Blau"
+
+
+ + +
+

3. 🌟 Praxisbeispiel: Palindrom & Text-Bereinigung

+ +

Ein Palindrom ist ein Text, der vorwärts wie rückwärts identisch ist (z.B. "Dreh mal am Herd"). Mit String-Methoden und Slicing ist die Prüfung ein Kinderspiel:

+ +
+
+
+ + + +
+ Python: Palindrom-Prüfer +
+
def pruefe_palindrom(text):
+    # 1. Alles in Kleinbuchstaben umwandeln:
+    sauber = text.lower()
+    # 2. Leerzeichen entfernen:
+    sauber = sauber.replace(" ", "")
+    # 3. Mit der Umkehrung (Slicing [::-1]) vergleichen:
+    return sauber == sauber[::-1]
+
+print(pruefe_palindrom("Anna"))             # -> True
+print(pruefe_palindrom("Dreh mal am Herd")) # -> True
+print(pruefe_palindrom("Python"))           # -> False
+
+
+ + +
+

4. 🎯 Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei g08_textverarbeitung_und_strings/aufgabe.py und löse die 5 Aufgaben:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 3 (Benutzernamen bereinigen) +
+
+
def bereinige_benutzernamen(name):
+    return name.strip().lower().replace(" ", "_")
+
+
+
+ +
+ 🔍 Tipp zu TODO 5 (Kreditkarte maskieren) +
+
+
def maskiere_kreditkarte(nummer):
+    if len(nummer) <= 4:
+        return nummer
+    return "*" * (len(nummer) - 4) + nummer[-4:]
+
+
+
+
+ + +
+

5. 🧪 Lösung überprüfen

+

Führe im Terminal folgenden Befehl aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g08_textverarbeitung_und_strings/musterloesung.py b/g08_textverarbeitung_und_strings/musterloesung.py new file mode 100644 index 0000000..c82af7a --- /dev/null +++ b/g08_textverarbeitung_und_strings/musterloesung.py @@ -0,0 +1,42 @@ +""" +Kapitel G08: Textverarbeitung & Strings – Musterlösung +====================================================== +""" + + +def ist_palindrom(text: str) -> bool: + """Prüft, ob text ein Palindrom ist (ignoriert Groß/Klein und Leerzeichen).""" + bereinigt = text.lower().replace(" ", "") + return bereinigt == bereinigt[::-1] + + +def zaehle_vokale(text: str) -> int: + """Zählt Vokale inklusive deutscher Umlaute.""" + vokale = set("aeiouäöü") + return sum(1 for c in text.lower() if c in vokale) + + +def bereinige_benutzernamen(name: str) -> str: + """Strippt, wandelt in Kleinbuchstaben um und ersetzt Leerzeichen durch Unterstriche.""" + return name.strip().lower().replace(" ", "_") + + +def woerter_zaehlen(text: str) -> int: + """Zählt Wörter anhand von Whitespace-Trennung.""" + return len(text.split()) + + +def maskiere_kreditkarte(nummer: str) -> str: + """Maskiert alle Zeichen bis auf die letzten 4 mit Sternchen.""" + if len(nummer) <= 4: + return nummer + return "*" * (len(nummer) - 4) + nummer[-4:] + + +if __name__ == "__main__": + print("Palindrom 'Anna':", ist_palindrom("Anna")) + print("Palindrom 'Dreh mal am Herd':", ist_palindrom("Dreh mal am Herd")) + print("Vokale 'Käsebrot':", zaehle_vokale("Käsebrot")) + print("Benutzername ' Max Mustermann ':", bereinige_benutzernamen(" Max Mustermann ")) + print("Wörter 'Python macht Spaß':", woerter_zaehlen("Python macht Spaß")) + print("Maskiert '1234567812345678':", maskiere_kreditkarte("1234567812345678")) diff --git a/g08_textverarbeitung_und_strings/test_aufgabe.py b/g08_textverarbeitung_und_strings/test_aufgabe.py new file mode 100644 index 0000000..f9d48ad --- /dev/null +++ b/g08_textverarbeitung_und_strings/test_aufgabe.py @@ -0,0 +1,97 @@ +import unittest +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent)) + +try: + import aufgabe +except ImportError as e: + raise ImportError(f"Konnte 'aufgabe.py' nicht importieren: {e}") + + +class TestKapitelG08(unittest.TestCase): + + def test_01_ist_palindrom(self): + """Prüft die Palindrom-Erkennung unter Berücksichtigung von Leerzeichen und Groß/Klein.""" + # Echte Palindrome + self.assertTrue(aufgabe.ist_palindrom("Anna"), "'Anna' ist ein Palindrom!") + self.assertTrue(aufgabe.ist_palindrom("Lagerregal"), "'Lagerregal' ist ein Palindrom!") + self.assertTrue(aufgabe.ist_palindrom("Dreh mal am Herd"), "'Dreh mal am Herd' sollte True liefern!") + self.assertTrue(aufgabe.ist_palindrom("Rentner"), "'Rentner' ist ein Palindrom!") + self.assertTrue(aufgabe.ist_palindrom("A"), "Ein Buchstabe ist ein Palindrom!") + self.assertTrue(aufgabe.ist_palindrom(""), "Leerer Text gilt als Palindrom!") + + # Keine Palindrome + self.assertFalse(aufgabe.ist_palindrom("Python"), "'Python' ist kein Palindrom!") + self.assertFalse(aufgabe.ist_palindrom("Informatik"), "'Informatik' ist kein Palindrom!") + self.assertFalse(aufgabe.ist_palindrom("Hallo Welt"), "'Hallo Welt' ist kein Palindrom!") + + def test_02_zaehle_vokale(self): + """Prüft das Zählen von Vokalen inklusive Umlauten und Groß/Kleinschreibung.""" + self.assertEqual(aufgabe.zaehle_vokale("Python"), 1, "In 'Python' gibt es 1 Vokal ('o')!") + self.assertEqual(aufgabe.zaehle_vokale("Käsebrot"), 3, "In 'Käsebrot' gibt es 3 Vokale ('ä', 'e', 'o')!") + self.assertEqual( + aufgabe.zaehle_vokale("SCHÖNES Wetter"), + 4, + "In 'SCHÖNES Wetter' gibt es 4 Vokale ('Ö', 'E', 'e', 'e')!" + ) + self.assertEqual(aufgabe.zaehle_vokale("Überflieger"), 5, "In 'Überflieger' gibt es 5 Vokale ('Ü', 'e', 'i', 'e', 'e')!") + self.assertEqual(aufgabe.zaehle_vokale("HTML"), 0, "In 'HTML' gibt es 0 Vokale!") + self.assertEqual(aufgabe.zaehle_vokale("Fly"), 0, "In 'Fly' gibt es 0 Vokale!") + self.assertEqual(aufgabe.zaehle_vokale(""), 0, "Leerer String hat 0 Vokale!") + + def test_03_bereinige_benutzernamen(self): + """Prüft die Bereinigung von Benutzernamen (strip, lowercase, Leerzeichen -> '_').""" + self.assertEqual( + aufgabe.bereinige_benutzernamen(" Max Mustermann "), + "max_mustermann", + "Fehler bei ' Max Mustermann '!" + ) + self.assertEqual( + aufgabe.bereinige_benutzernamen(" Super Coder 99 "), + "super_coder_99", + "Fehler bei ' Super Coder 99 '!" + ) + self.assertEqual( + aufgabe.bereinige_benutzernamen("LUKAS"), + "lukas", + "Fehler bei 'LUKAS'!" + ) + self.assertEqual( + aufgabe.bereinige_benutzernamen("anna"), + "anna" + ) + + def test_04_woerter_zaehlen(self): + """Prüft das Zählen von Wörtern bei verschiedenen Leerzeichen-Kombinationen.""" + self.assertEqual(aufgabe.woerter_zaehlen("Hallo Welt"), 2) + self.assertEqual( + aufgabe.woerter_zaehlen(" Python macht Spaß! "), + 3, + "Mehrere Leerzeichen sollten ignoriert werden!" + ) + self.assertEqual(aufgabe.woerter_zaehlen("EinWort"), 1) + self.assertEqual(aufgabe.woerter_zaehlen(""), 0, "Leerer Text muss 0 Wörter liefern!") + self.assertEqual(aufgabe.woerter_zaehlen(" "), 0, "Nur Leerzeichen müssen 0 Wörter liefern!") + + def test_05_maskiere_kreditkarte(self): + """Prüft die Maskierung von Kartennummern / sensiblen Strings.""" + self.assertEqual( + aufgabe.maskiere_kreditkarte("1234567812345678"), + "************5678", + "Fehler: 16-stellige Nummer muss 12 Sterne + letzte 4 Ziffern haben!" + ) + self.assertEqual( + aufgabe.maskiere_kreditkarte("987654321"), + "*****4321", + "Fehler: 9-stellige Nummer muss 5 Sterne + letzte 4 Ziffern haben!" + ) + # Randfälle: <= 4 Zeichen + self.assertEqual(aufgabe.maskiere_kreditkarte("1234"), "1234") + self.assertEqual(aufgabe.maskiere_kreditkarte("99"), "99") + self.assertEqual(aufgabe.maskiere_kreditkarte(""), "") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g09_dictionaries_und_sets/README.md b/g09_dictionaries_und_sets/README.md new file mode 100644 index 0000000..354c107 --- /dev/null +++ b/g09_dictionaries_und_sets/README.md @@ -0,0 +1,169 @@ +# Kapitel G09: Dictionaries & Sets (Mengen) 📖🎯 + +Schulabgleich: **Thema 15.0 (Dictionaries und Mengen)** + +Herzlich willkommen zu Kapitel G09! In diesem Kapitel lernst du zwei unverzichtbare Datenstrukturen kennen, die in modernen Python-Programmen tagtäglich eingesetzt werden: **Dictionaries** (Schlüssel-Wert-Zuordnungen) und **Sets** (Mengen ohne Duplikate). + +--- + +## 🗃️ 1. Dictionaries: Die Karteikasten-Analogie + +Ein **Dictionary** (abgekürzt `dict`) speichert Daten als **Schlüssel-Wert-Paare** (`key: value`). + +Stell dir einen klassischen **Karteikasten** oder ein **Wörterbuch** vor: +- Wenn du nach dem Wort *"Schmetterling"* suchst (der **Key / Schlüssel**), findest du die englische Übersetzung *"Butterfly"* (der **Value / Wert**). +- Während eine Python-Liste Elemente über einen numerischen Index (`liste[0]`, `liste[1]`) abruft, greift ein Dictionary über beliebige eindeutige Schlüssel (meist Texte oder Zahlen) zu! + +``` + [ DICTIONARY: telefonbuch ] +┌────────────────────────┬────────────────────────┐ +│ SCHLÜSSEL (Key) │ WERT (Value) │ +├────────────────────────┼────────────────────────┤ +│ "Anna" │ "0171-123456" │ +│ "Ben" │ "0160-987654" │ +│ "Clara" │ "0151-555666" │ +└────────────────────────┴────────────────────────┘ +``` + +### Grundlegende Syntax: +```python +# Dictionary erstellen: +kontakt = { + "name": "Anna", + "alter": 23, + "ort": "Berlin" +} + +# Zugriff über Schlüssel: +print(kontakt["name"]) # "Anna" + +# Neuen Eintrag hinzufügen oder bestehenden ändern: +kontakt["email"] = "anna@example.com" +kontakt["alter"] = 24 + +# Eintrag entfernen: +del kontakt["ort"] +# oder mit Rückgabewert: +entfernter_wert = kontakt.pop("email") +``` + +--- + +## 🛡️ 2. Sicherer Datenzugriff: `dict[key]` vs. `dict.get(key, default)` + +Ein häufiger Absturzgrund in Python ist der direkte Zugriff auf einen nicht existierenden Schlüssel: + +```python +benutzer = {"name": "Max"} + +# 💥 ABSTURZ: +print(benutzer["telefon"]) # KeyError: 'telefon' + +# ✅ SICHER: +telefon = benutzer.get("telefon", "Keine Nummer hinterlegt") +print(telefon) # Gibt "Keine Nummer hinterlegt" aus, OHNE Absturz! +``` + +> **Goldene Regel:** Wenn du nicht zu 100% sicher bist, ob ein Schlüssel im Dictionary vorkommt, nutze immer `.get()` mit einem sinnvollen Standardwert! + +--- + +## 🔄 3. Über Dictionaries iterieren: `.keys()`, `.values()`, `.items()` + +Python bietet drei praktische Methoden für Schleifen: + +```python +preise = {"Apfel": 0.80, "Banane": 1.20, "Mango": 2.50} + +# 1. Nur Schlüssel durchlaufen: +for frucht in preise.keys(): # oder einfach: for frucht in preise: + print(frucht) + +# 2. Nur Werte durchlaufen: +for preis in preise.values(): + print(f"{preis:.2f} €") + +# 3. Schlüssel UND Werte gleichzeitig (sehr nützlich!): +for frucht, preis in preise.items(): + print(f"{frucht}: {preis:.2f} €") +``` + +--- + +## 🎟️ 4. Sets (Mengen): Die Club-Türsteher-Analogie + +Ein **Set** (`set`) ist eine ungeordnete Sammlung von **eindeutigen Elementen**. + +Stell dir einen strengen **Club-Türsteher** vor: +- Jeder Gast darf nur **ein einziges Mal** auf die Gästeliste. +- Wenn jemand versucht, sich ein zweites Mal anzustellen, winkt der Türsteher ab: *"Du bist schon drin!"* +- Im Club gibt es **keine feste Reihenfolge** (Sets haben keinen Index `set[0]`). + +```python +# Set mit geschweiften Klammern erstellen: +farben = {"rot", "grün", "blau", "rot", "grün"} +print(farben) # Ausgabe: {'rot', 'blau', 'grün'} (Duplikate automatisch entfernt!) + +# Schnelle Zugehörigkeitsprüfung mit 'in' (extrem schnell in O(1)): +if "rot" in farben: + print("Rot ist dabei!") +``` + +--- + +## 🧮 5. Mathematische Mengenoperationen mit Sets + +Sets glänzen bei mathematischen Mengen-Vergleichen: + +| Operation | Operator | Methode | Beschreibung | +| :--- | :--- | :--- | :--- | +| **Schnittmenge** | `A & B` | `A.intersection(B)` | Elemente, die in **beiden** Mengen vorkommen | +| **Vereinigung** | `A \| B` | `A.union(B)` | Alle Elemente aus **beiden** Mengen (ohne Duplikate) | +| **Differenz** | `A - B` | `A.difference(B)` | Elemente, die in A, aber **nicht** in B sind | +| **Symmetrische Differenz** | `A ^ B` | `A.symmetric_difference(B)` | Elemente in A oder B, aber **nicht in beiden** | + +```python +schueler_kurs_a = {"Anna", "Ben", "Clara"} +schueler_kurs_b = {"Ben", "Daniel", "Clara", "Emma"} + +# Wer ist in beiden Kursen? (Schnittmenge) +beide = schueler_kurs_a & schueler_kurs_b +print("In beiden Kursen:", beide) # {'Ben', 'Clara'} + +# Alle Schüler insgesamt? (Vereinigung) +alle = schueler_kurs_a | schueler_kurs_b +print("Alle Schüler:", alle) # {'Anna', 'Ben', 'Clara', 'Daniel', 'Emma'} + +# Wer ist nur in Kurs A? (Differenz) +nur_a = schueler_kurs_a - schueler_kurs_b +print("Nur in Kurs A:", nur_a) # {'Anna'} +``` + +--- + +## ⚠️ Häufige Stolperfallen + +1. **Leeres Set vs. Leeres Dictionary:** + - `{}` erzeugt ein **leeres Dictionary**, KEIN Set! + - Um ein leeres Set zu erstellen, schreibe immer: `mein_set = set()`. +2. **Keine veränderlichen Typen (Lists) als Keys oder Set-Elemente:** + - Dictionaries-Schlüssel und Set-Elemente müssen **hashbar** (unveränderlich) sein (Strings, Zahlen, Tuples). Listen `[1, 2]` sind verboten (`TypeError: unhashable type: 'list'`). +3. **Reihenfolge bei Sets:** + - Sets garantieren **keine** feste Reihenfolge. Wenn du Duplikate aus einer Liste entfernen und dabei die Reihenfolge beibehalten willst, musst du dir mit einer Hilfsmenge (`seen = set()`) und einer Ergebnisliste behelfen. + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne `g09_dictionaries_und_sets/aufgabe.py` und implementiere: + +1. **TODO 1 (`woerter_haeufigkeit`):** Zähle die Häufigkeit jedes Wortes in einem Text (in Kleinbuchstaben, ohne Satzzeichen). +2. **TODO 2 (`telefonbuch_suche`):** Suche sicher mit `.get()` eine Nummer im Telefonbuch (Rückgabe: Nummer oder `"Nicht gefunden"`). +3. **TODO 3 (`gemeinsame_interessen`):** Finde die Hobbys, die zwei Personen gemeinsam haben (Schnittmenge `&`). +4. **TODO 4 (`entferne_duplikate_behalte_reihenfolge`):** Filtere Duplikate aus einer Liste, behalte die Reihenfolge des ersten Auftretens bei. + +### Testen: +```bash +python3 test_aufgabe.py +``` +Sobald alle 9 Tests mit `OK` durchlaufen, bist du bereit für Kapitel G10! diff --git a/g09_dictionaries_und_sets/aufgabe.py b/g09_dictionaries_und_sets/aufgabe.py new file mode 100644 index 0000000..247ae74 --- /dev/null +++ b/g09_dictionaries_und_sets/aufgabe.py @@ -0,0 +1,137 @@ +""" +Kapitel G09: Dictionaries & Sets (Mengen) – Aufgabenblatt +========================================================= +Schulabgleich: Thema 15.0 (Dictionaries und Mengen) + +In diesem Kapitel lernst du zwei der wichtigsten Datenstrukturen in Python kennen: +1. Dictionaries (Schlüssel-Wert-Paare wie im Wörterbuch oder Telefonbuch) +2. Sets (Mengen von eindeutigen Elementen ohne Duplikate) + +Bearbeite die Aufgaben von TODO 1 bis TODO 4. +Überprüfe deine Lösung im Terminal mit: + python3 test_aufgabe.py +""" + +from typing import List, Dict, Set + + +# ============================================================================== +# TODO 1: Wörter-Häufigkeitszähler +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'woerter_haeufigkeit(text: str) -> dict[str, int]'. +# +# Anforderungen: +# 1. Wandle den gesamten Text in Kleinbuchstaben um (.lower()). +# 2. Entferne gängige Satzzeichen (.,!?;:) aus dem Text oder bereinige die Wörter. +# 3. Zerlege den Text in einzelne Wörter (.split()). +# 4. Zähle mit einem Dictionary, wie oft jedes Wort vorkommt. +# 5. Gib das fertige Dictionary {wort: anzahl} zurück. +# +# Beispiel: +# text = "Apfel Banane Apfel Birne Banane Apfel" +# Ergebnis: {"apfel": 3, "banane": 2, "birne": 1} +# ============================================================================== + +def woerter_haeufigkeit(text: str) -> Dict[str, int]: + """Zählt die Häufigkeit aller Wörter in einem gegebenen Text (case-insensitive).""" + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Telefonbuch-Suche mit .get() +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'telefonbuch_suche(telefonbuch: dict[str, str], name: str) -> str'. +# +# Anforderungen: +# 1. Suche im Dictionary 'telefonbuch' nach dem Schlüssel 'name'. +# 2. Verwende dafür die sichere Methode .get(key, standardwert), um einen KeyError +# zu vermeiden! +# 3. Wenn der Name existiert, gib die zugehörige Telefonnummer zurück. +# 4. Wenn der Name NICHT existiert, gib den Text "Nicht gefunden" zurück. +# +# Beispiel: +# buch = {"Anna": "0171-123456", "Ben": "0160-987654"} +# telefonbuch_suche(buch, "Anna") -> "0171-123456" +# telefonbuch_suche(buch, "Clara") -> "Nicht gefunden" +# ============================================================================== + +def telefonbuch_suche(telefonbuch: Dict[str, str], name: str) -> str: + """Sucht eine Telefonnummer im Telefonbuch mit sicherer .get() Abfrage.""" + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Gemeinsame Interessen (Mengenoperation Schnittmenge) +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'gemeinsame_interessen(person_a_hobbys: set, person_b_hobbys: set) -> set'. +# +# Anforderungen: +# 1. Die Funktion erhält zwei Mengen (Sets) mit Hobbys von zwei Personen. +# 2. Ermittle alle Hobbys, die BEIDE Personen gemeinsam haben (Schnittmenge / Intersection). +# 3. Nutze entweder den Mengen-Operator '&' oder die Methode '.intersection()'. +# 4. Gib das resultierende Set zurück. +# +# Beispiel: +# a = {"Gaming", "Klettern", "Musik"} +# b = {"Kochen", "Gaming", "Musik", "Lesen"} +# gemeinsame_interessen(a, b) -> {"Gaming", "Musik"} +# ============================================================================== + +def gemeinsame_interessen(person_a_hobbys: Set[str], person_b_hobbys: Set[str]) -> Set[str]: + """Ermittelt die Schnittmenge der Hobbys zweier Personen.""" + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Duplikate entfernen & Reihenfolge beibehalten +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'entferne_duplikate_behalte_reihenfolge(liste: list) -> list'. +# +# Problem: Ein normales 'set(liste)' entfernt Duplikate, verliert aber die Reihenfolge! +# Anforderungen: +# 1. Entferne alle doppelten Elemente aus der übergebenen Liste. +# 2. Die ursprüngliche Reihenfolge des ERSTEN Auftretens der Elemente muss erhalten bleiben. +# 3. Nutze z.B. ein 'seen = set()', um bereits gesehene Elemente blitzschnell nachzuschlagen, +# und füge neue Elemente einer Ergebnisliste hinzu. +# 4. Gib die bereinigte Liste zurück. +# +# Beispiel: +# eingabe = [1, 3, 2, 3, 1, 4, 2, 5] +# Ergebnis: [1, 3, 2, 4, 5] +# ============================================================================== + +def entferne_duplikate_behalte_reihenfolge(liste: list) -> list: + """Entfernt Duplikate aus einer Liste unter Beibehaltung der Reihenfolge.""" + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# Hauptprogramm zum Ausprobieren: +# (Du kannst diese Datei direkt mit 'python3 aufgabe.py' ausführen) +# ============================================================================== +if __name__ == "__main__": + print("--- 📚 Testlauf: Dictionaries & Sets ---") + + # Test TODO 1 + beispiel_text = "Python ist super. Python macht Spaß! Ist Python leicht? Ja!" + haeufigkeiten = woerter_haeufigkeit(beispiel_text) + print("Wort-Häufigkeiten:", haeufigkeiten) + + # Test TODO 2 + tbuch = {"Alice": "0151-111111", "Bob": "0172-222222"} + print("Suche Alice:", telefonbuch_suche(tbuch, "Alice")) + print("Suche Charlie:", telefonbuch_suche(tbuch, "Charlie")) + + # Test TODO 3 + hobbys_1 = {"Fotografie", "Reisen", "Schwimmen", "Python"} + hobbys_2 = {"Kochen", "Python", "Reisen", "Yoga"} + print("Gemeinsame Hobbys:", gemeinsame_interessen(hobbys_1, hobbys_2)) + + # Test TODO 4 + tiere_mit_duplikaten = ["Katze", "Hund", "Katze", "Maus", "Hund", "Hamster"] + eindeutige_tiere = entferne_duplikate_behalte_reihenfolge(tiere_mit_duplikaten) + print("Ohne Duplikate (Reihenfolge fix):", eindeutige_tiere) diff --git a/g09_dictionaries_und_sets/index.html b/g09_dictionaries_und_sets/index.html new file mode 100644 index 0000000..d4f4b0a --- /dev/null +++ b/g09_dictionaries_und_sets/index.html @@ -0,0 +1,305 @@ + + + + + + Kapitel G09: Dictionaries & Sets + + + + +
+
+ +
Kapitel G09 • Schulabgleich 15.0
+

Dictionaries & Sets: Schlüssel, Werte & Mengen 🗃️✨

+

Lerne, wie du Daten in Schlüssel-Wert-Paaren organisierst, blitzschnell nachschlägst und mit mathematischen Mengen Duplikate eliminierst.

+
+
+ +
+ + +
+

1. Was ist ein Dictionary? (Die Karteikasten-Analogie)

+ +

+ Bisher hast du Daten oft in Listen gespeichert. Bei einer Liste greifst du über eine Zahl (den Index 0, 1, 2, ...) auf Einträge zu. + Aber was machst du, wenn du eine Telefonnummer zu einem Namen suchst oder eine deutsche Übersetzung zu einem englischen Wort? +

+ +
+
🗃️ Die Karteikasten- & Wörterbuch-Analogie:
+

Ein Dictionary (Wörterbuch) funktioniert wie ein echter Karteikasten:

+
    +
  • Vorne auf der Karteikarte steht das Stichwort (der Key / Schlüssel), z.B. "Anna".
  • +
  • Auf der Rückseite steht die eigentliche Information (der Value / Wert), z.B. "0171-123456".
  • +
  • Du musst nicht alle Karten von vorne bis hinten durchblättern – Python springt sofort in Lichtgeschwindigkeit zum gesuchten Schlüssel!
  • +
+
+ +
+
+
+ + + +
+ Python: Dictionary Grundlagen +
+
# 1. Dictionary erstellen mit geschweiften Klammern {key: value}:
+kontakt = {
+    "name": "Anna",
+    "alter": 23,
+    "beruf": "Entwicklerin"
+}
+
+# 2. Wert über Schlüssel abrufen:
+print(kontakt["name"])   # Gibt "Anna" aus
+
+# 3. Eintrag hinzufügen oder verändern:
+kontakt["stadt"] = "Berlin"  # Neu hinzugefügt
+kontakt["alter"] = 24        # Wert aktualisiert
+
+# 4. Eintrag löschen:
+del kontakt["beruf"]         # Entfernt den Schlüssel "beruf"
+
+
+ + +
+

2. Sicherer Datenzugriff mit .get() & Iteration

+ +

+ Greifst du auf einen Schlüssel zu, den es im Dictionary nicht gibt, stürzt dein Programm mit einem gefürchteten KeyError ab! +

+ +
+
🛡️ Die Lebensversicherung: dict.get(key, standardwert)
+ Verwende .get(), wenn du nicht sicher weißt, ob der Schlüssel existiert. Statt abzustürzen, liefert Python einen sicheren Standardwert zurück! +
+ +
+
+ Vergleich: dict[key] vs dict.get() +
+
user = {"username": "coder99"}
+
+# ❌ Riskant: Programm stürzt bei fehlendem Key ab!
+# print(user["email"])  # 💥 KeyError: 'email'
+
+# ✅ Sicher: Liefert Fallback-Wert ohne Absturz!
+email = user.get("email", "Keine E-Mail angegeben")
+print(email)  # Ausgabe: Keine E-Mail angegeben
+
+ +

Praktische Methoden: .keys(), .values() und .items()

+ +
+
+ Schleifen über Dictionaries +
+
preise = {"Kaffee": 3.50, "Croissant": 2.20, "Tee": 2.80}
+
+# Schlüssel und Werte gleichzeitig durchlaufen mit .items():
+for produkt, preis in preise.items():
+    print(f"{produkt} kostet {preis:.2f} €")
+
+
+ + +
+

3. Was ist ein Set (Menge)? (Die Club-Türsteher-Analogie)

+ +

+ Ein Set (Menge) ist eine Sammlung von Elementen mit zwei besonderen Superkräften: + 1. Keine Duplikate erlaubt und 2. Extrem schnelle Suche (in O(1)). +

+ +
+
🚪 Die Club-Türsteher-Analogie:
+

+ Stell dir einen Club vor, in dem jeder Gast genau einmal auf die Gästeliste darf: +

+
    +
  • Versucht jemand doppelt hineinzugehen, ignoriert der Türsteher den zweiten Versuch einfach.
  • +
  • Es gibt im Club keine feste Sitzordnung (Sets sind ungeordnet und haben keinen Index wie set[0]).
  • +
+
+ +
+
+ Sets erstellen und Duplikate filtern +
+
# Set erstellen (Duplikate fallen automatisch weg):
+zahlen = {1, 2, 2, 3, 3, 3, 4}
+print(zahlen)  # Ausgabe: {1, 2, 3, 4}
+
+# Blitzschnelle Überprüfung mit 'in':
+if 3 in zahlen:
+    print("3 ist in der Menge enthalten!")
+
+# ⚠️ Achtung beim leeren Set:
+leeres_dict = {}       # Erzeugt ein leeres Dictionary!
+leeres_set = set()     # ✅ So erstellst du ein leeres Set!
+
+ +

Mengenoperationen im Überblick

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationOperatorMethodeBedeutung
Schnittmenge (Intersection)A & BA.intersection(B)Gemeinsame Elemente beider Mengen
Vereinigung (Union)A | BA.union(B)Alle Elemente aus A und B zusammen
DifferenzA - BA.difference(B)Elemente in A, die NICHT in B vorkommen
+
+ + +
+

4. 🌟 Vollständiges Praxisbeispiel: Wörter-Zähler

+ +

Hier siehst du, wie man mit Dictionaries Wort-Häufigkeiten in Texten ermittelt:

+ +
+
+
+ + + +
+ Python: Wort-Häufigkeiten zählen +
+
def zaehle_woerter(text):
+    haeufigkeit = {}
+    woerter = text.lower().split()
+    
+    for wort in woerter:
+        # Wenn das Wort noch nicht im Dict ist, startet der Zähler bei 0:
+        haeufigkeit[wort] = haeufigkeit.get(wort, 0) + 1
+        
+    return haeufigkeit
+
+beispiel = "Python macht Spaß und Python ist mächtig"
+print(zaehle_woerter(beispiel))
+# Ausgabe: {'python': 2, 'macht': 1, 'spaß': 1, 'und': 1, 'ist': 1, 'mächtig': 1}
+
+
+ + +
+

5. 🎯 Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei g09_dictionaries_und_sets/aufgabe.py und bearbeite die folgenden 4 Aufgaben:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 1 (Satzzeichen entfernen & Zählen) +
+

Du kannst Satzzeichen mit replace() durch Leerzeichen ersetzen und dann mit .get(wort, 0) + 1 zählen:

+
+
bereinigt = text.lower()
+for zeichen in ".,!?;:()\"'":
+    bereinigt = bereinigt.replace(zeichen, " ")
+woerter = bereinigt.split()
+
+
+
+ +
+ 🔍 Tipp zu TODO 4 (Reihenfolge bei Duplikaten behalten) +
+

Nutze eine Liste für das Ergebnis und ein Set zum Merken bereits gesehener Elemente:

+
+
seen = set()
+ergebnis = []
+for element in liste:
+    if element not in seen:
+        seen.add(element)
+        ergebnis.append(element)
+return ergebnis
+
+
+
+
+ + +
+

6. 🧪 Lösung überprüfen

+

Führe im Terminal die automatische Test-Suite aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g09_dictionaries_und_sets/musterloesung.py b/g09_dictionaries_und_sets/musterloesung.py new file mode 100644 index 0000000..aa58f94 --- /dev/null +++ b/g09_dictionaries_und_sets/musterloesung.py @@ -0,0 +1,75 @@ +""" +Kapitel G09: Dictionaries & Sets (Mengen) – Musterlösung +========================================================= +Schulabgleich: Thema 15.0 (Dictionaries und Mengen) + +Hier findest du die vollständige Musterlösung zu allen Aufgaben. +""" + +from typing import List, Dict, Set + + +# TODO 1: Wörter-Häufigkeitszähler +def woerter_haeufigkeit(text: str) -> Dict[str, int]: + """Zählt die Häufigkeit aller Wörter in einem gegebenen Text (case-insensitive).""" + # 1. Satzzeichen entfernen und Text in Kleinbuchstaben umwandeln + satzzeichen = ".,!?;:()\"'[]{}<>" + bereinigter_text = text.lower() + for z in satzzeichen: + bereinigter_text = bereinigter_text.replace(z, " ") + + # 2. In Wörter zerlegen + woerter = bereinigter_text.split() + + # 3. Häufigkeiten zählen + haeufigkeit: Dict[str, int] = {} + for wort in woerter: + haeufigkeit[wort] = haeufigkeit.get(wort, 0) + 1 + + return haeufigkeit + + +# TODO 2: Telefonbuch-Suche mit .get() +def telefonbuch_suche(telefonbuch: Dict[str, str], name: str) -> str: + """Sucht eine Telefonnummer im Telefonbuch mit sicherer .get() Abfrage.""" + return telefonbuch.get(name, "Nicht gefunden") + + +# TODO 3: Gemeinsame Interessen (Mengenoperation Schnittmenge) +def gemeinsame_interessen(person_a_hobbys: Set[str], person_b_hobbys: Set[str]) -> Set[str]: + """Ermittelt die Schnittmenge der Hobbys zweier Personen.""" + return person_a_hobbys & person_b_hobbys + + +# TODO 4: Duplikate entfernen & Reihenfolge beibehalten +def entferne_duplikate_behalte_reihenfolge(liste: list) -> list: + """Entfernt Duplikate aus einer Liste unter Beibehaltung der Reihenfolge.""" + seen = set() + ergebnis = [] + for element in liste: + if element not in seen: + seen.add(element) + ergebnis.append(element) + return ergebnis + + +if __name__ == "__main__": + print("--- 📚 Musterlösung G09: Dictionaries & Sets ---") + + # 1. Wörter-Häufigkeit + text = "Python ist super. Python macht Spaß! Ist Python leicht? Ja!" + print("Häufigkeiten:", woerter_haeufigkeit(text)) + + # 2. Telefonbuch + buch = {"Anna": "0171-123456", "Ben": "0160-987654"} + print("Suche Anna:", telefonbuch_suche(buch, "Anna")) + print("Suche Clara:", telefonbuch_suche(buch, "Clara")) + + # 3. Gemeinsame Interessen + a = {"Gaming", "Klettern", "Musik"} + b = {"Kochen", "Gaming", "Musik", "Lesen"} + print("Gemeinsame Hobbys:", gemeinsame_interessen(a, b)) + + # 4. Duplikate entfernen + zahlen = [1, 3, 2, 3, 1, 4, 2, 5] + print("Bereinigte Zahlenliste:", entferne_duplikate_behalte_reihenfolge(zahlen)) diff --git a/g09_dictionaries_und_sets/test_aufgabe.py b/g09_dictionaries_und_sets/test_aufgabe.py new file mode 100644 index 0000000..50c7135 --- /dev/null +++ b/g09_dictionaries_und_sets/test_aufgabe.py @@ -0,0 +1,142 @@ +import unittest +import sys +from pathlib import Path + +# Sicherstellen, dass das aktuelle Verzeichnis im Modulpfad ist +sys.path.insert(0, str(Path(__file__).parent)) + +try: + import aufgabe +except ImportError as e: + raise ImportError(f"Konnte 'aufgabe.py' nicht importieren: {e}") + + +class TestKapitelG09(unittest.TestCase): + + def test_01_woerter_haeufigkeit_grundlagen(self): + """Prüft woerter_haeufigkeit mit einfachen Wörtern.""" + self.assertTrue( + hasattr(aufgabe, "woerter_haeufigkeit"), + "Fehler: Funktion 'woerter_haeufigkeit' fehlt in aufgabe.py!" + ) + text = "Apfel Banane Apfel Birne Banane Apfel" + ergebnis = aufgabe.woerter_haeufigkeit(text) + erwartet = {"apfel": 3, "banane": 2, "birne": 1} + self.assertIsInstance(ergebnis, dict, "Fehler: Rückgabewert muss ein Dictionary (dict) sein!") + self.assertEqual( + ergebnis, + erwartet, + f"Fehler bei woerter_haeufigkeit: Erwartet {erwartet}, aber erhalten: {ergebnis}" + ) + + def test_02_woerter_haeufigkeit_satzzeichen_und_grossklein(self): + """Prüft woerter_haeufigkeit bei Groß-/Kleinschreibung und Satzzeichen.""" + text = "Hallo, hallo! HALLO... ist da wer? Ja, wer da ist." + ergebnis = aufgabe.woerter_haeufigkeit(text) + erwartet = {"hallo": 3, "ist": 2, "da": 2, "wer": 2, "ja": 1} + self.assertEqual( + ergebnis, + erwartet, + f"Fehler: Satzzeichen oder Groß-/Kleinschreibung nicht korrekt behandelt!\nErwartet: {erwartet}\nErhalten: {ergebnis}" + ) + + def test_03_woerter_haeufigkeit_leerer_text(self): + """Prüft woerter_haeufigkeit bei leerem String.""" + ergebnis = aufgabe.woerter_haeufigkeit("") + self.assertEqual(ergebnis, {}, "Fehler: Ein leerer Text sollte ein leeres Dictionary {} liefern.") + + def test_04_telefonbuch_suche_gefunden(self): + """Prüft telefonbuch_suche, wenn der Name existiert.""" + self.assertTrue( + hasattr(aufgabe, "telefonbuch_suche"), + "Fehler: Funktion 'telefonbuch_suche' fehlt in aufgabe.py!" + ) + buch = { + "Anna": "0171-123456", + "Ben": "0160-987654", + "Clara": "0151-555666" + } + self.assertEqual( + aufgabe.telefonbuch_suche(buch, "Anna"), + "0171-123456", + "Fehler: Telefonnummer für 'Anna' wurde nicht korrekt zurückgegeben." + ) + self.assertEqual( + aufgabe.telefonbuch_suche(buch, "Clara"), + "0151-555666", + "Fehler: Telefonnummer für 'Clara' wurde nicht korrekt zurückgegeben." + ) + + def test_05_telefonbuch_suche_nicht_gefunden(self): + """Prüft telefonbuch_suche mit .get() Fallback bei unbekanntem Namen.""" + buch = {"Anna": "0171-123456"} + ergebnis = aufgabe.telefonbuch_suche(buch, "Daniel") + self.assertEqual( + ergebnis, + "Nicht gefunden", + f"Fehler: Unbekannter Name muss 'Nicht gefunden' liefern, ergab aber: {ergebnis}" + ) + # Auch bei leerem Buch + self.assertEqual( + aufgabe.telefonbuch_suche({}, "Niemand"), + "Nicht gefunden", + "Fehler: Suche in leerem Telefonbuch muss 'Nicht gefunden' liefern." + ) + + def test_06_gemeinsame_interessen(self): + """Prüft die Mengenoperation Schnittmenge (intersection / &).""" + self.assertTrue( + hasattr(aufgabe, "gemeinsame_interessen"), + "Fehler: Funktion 'gemeinsame_interessen' fehlt in aufgabe.py!" + ) + a = {"Gaming", "Klettern", "Musik"} + b = {"Kochen", "Gaming", "Musik", "Lesen"} + ergebnis = aufgabe.gemeinsame_interessen(a, b) + erwartet = {"Gaming", "Musik"} + self.assertIsInstance(ergebnis, set, "Fehler: Rückgabewert muss ein Set (Menge) sein!") + self.assertEqual( + ergebnis, + erwartet, + f"Fehler bei gemeinsame_interessen: Erwartet {erwartet}, erhalten: {ergebnis}" + ) + + def test_07_gemeinsame_interessen_disjunkt(self): + """Prüft gemeinsame_interessen ohne Überschneidung.""" + a = {"Tennis", "Schwimmen"} + b = {"Schach", "Malen"} + ergebnis = aufgabe.gemeinsame_interessen(a, b) + self.assertEqual( + ergebnis, + set(), + "Fehler: Bei disjunkten Mengen muss ein leeres Set() zurückgegeben werden." + ) + + def test_08_entferne_duplikate_behalte_reihenfolge(self): + """Prüft, ob Duplikate entfernt und die ursprüngliche Reihenfolge erhalten wird.""" + self.assertTrue( + hasattr(aufgabe, "entferne_duplikate_behalte_reihenfolge"), + "Fehler: Funktion 'entferne_duplikate_behalte_reihenfolge' fehlt in aufgabe.py!" + ) + eingabe = [1, 3, 2, 3, 1, 4, 2, 5] + ergebnis = aufgabe.entferne_duplikate_behalte_reihenfolge(eingabe) + erwartet = [1, 3, 2, 4, 5] + self.assertIsInstance(ergebnis, list, "Fehler: Rückgabewert muss eine Liste sein!") + self.assertEqual( + ergebnis, + erwartet, + f"Fehler: Reihenfolge oder Duplikat-Filterung weicht ab!\nErwartet: {erwartet}\nErhalten: {ergebnis}" + ) + + def test_09_entferne_duplikate_strings_und_randfaelle(self): + """Prüft Duplikatentfernung mit Strings und leerer Liste.""" + eingabe = ["Apfel", "Banane", "Apfel", "Birne", "Banane", "Orange"] + ergebnis = aufgabe.entferne_duplikate_behalte_reihenfolge(eingabe) + erwartet = ["Apfel", "Banane", "Birne", "Orange"] + self.assertEqual(ergebnis, erwartet) + + # Leere Liste + self.assertEqual(aufgabe.entferne_duplikate_behalte_reihenfolge([]), []) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/g10_comprehensions_datum_algorithmen/README.md b/g10_comprehensions_datum_algorithmen/README.md new file mode 100644 index 0000000..1a2f43c --- /dev/null +++ b/g10_comprehensions_datum_algorithmen/README.md @@ -0,0 +1,142 @@ +# Kapitel G10: Comprehensions, Datum & Algorithmen 🚀📅 + +Schulabgleich: **Thema 13.0 (Algorithmen & Datumsverarbeitung) & 15.1 (Comprehensions)** + +Herzlich willkommen zum krönenden Abschluss des Grundlagen-Lehrpfads! In diesem Kapitel lernst du drei essenzielle Werkzeuge kennen, die dich von einer Programmieranfängerin zu einer echten Python-Entwicklerin machen: +1. **List & Dict Comprehensions** (Eleganter, kompakter Code in einer Zeile) +2. **Datum & Uhrzeit** mit dem `datetime`-Modul (Rechnen mit Zeitspannen & Kalendern) +3. **Praktische Algorithmen** (Suchen, Filtern, Sortieren mit `key=` und Min/Max-Findung) + +--- + +## ⚡ 1. List Comprehensions: Das Schnellschienen-Fließband + +Bisher hast du neue Listen oft mit einer leeren Liste und einer `for`-Schleife befüllt: + +```python +# 🐢 Der traditionelle Weg (4 Zeilen): +quadrate = [] +for x in range(1, 6): + if x % 2 == 0: + quadrate.append(x ** 2) +``` + +Mit einer **List Comprehension** schreibst du das in **einer einzigen, glasklaren Zeile**: + +```python +# 🚀 Der Pythonic Weg mit List Comprehension: +quadrate = [x ** 2 for x in range(1, 6) if x % 2 == 0] +# Ergebnis: [4, 16] +``` + +### Die Anatomie einer List Comprehension: +``` +[ WAS_REIN_SOLL for VARIABLE in QUELLE if BEDINGUNG ] + (Ausdruck / Map) (x) (Liste) (Filter) +``` + +### Auch für Dictionaries: Dict Comprehensions +Genauso kannst du blitzschnell Dictionaries erzeugen: +```python +woerter = ["Apfel", "Banane", "Erdbeere"] +wort_laengen = {w: len(w) for w in woerter} +# Ergebnis: {'Apfel': 5, 'Banane': 6, 'Erdbeere': 8} +``` + +--- + +## 📅 2. Datum & Uhrzeit mit dem `datetime`-Modul + +In der echten Softwareentwicklung (z.B. bei Buchungs-Systemen, Online-Shops oder Kalendern) musst du ständig mit Zeitangaben rechnen. Python bringt dafür das mächtige Modul `datetime` mit. + +```python +from datetime import datetime, date, timedelta + +# 1. Aktuelles Datum & Uhrzeit: +heute = date.today() # z.B. 2026-08-29 +jetzt = datetime.now() # z.B. 2026-08-29 14:30:00 + +# 2. Rechnen mit Zeitspannen (timedelta): +in_einer_woche = heute + timedelta(days=7) +gestern = heute - timedelta(days=1) + +# 3. Differenz zwischen zwei Daten berechnen: +silvester = date(2026, 12, 31) +tage_rest = (silvester - heute).days +print(f"Noch {tage_rest} Tage bis Silvester!") +``` + +### Formatieren (`strftime`) & Einlesen (`strptime`) + +- **`strftime` (String Format Time):** Wandelt ein Datumsobjekt in einen formatierten Text um. +- **`strptime` (String Parse Time):** Liest einen Text ein und baut daraus ein Datumsobjekt. + +```python +# Objekt -> Formatierter String: +datum = date(2026, 8, 29) +deutsch = datum.strftime("%d.%m.%Y") # "29.08.2026" + +# String -> Datumsobjekt: +text = "2026-12-24" +weihnachten = datetime.strptime(text, "%Y-%m-%d").date() +``` + +| Platzhalter | Bedeutung | Beispiel | +| :--- | :--- | :--- | +| `%d` | Tag des Monats (01 - 31) | `29` | +| `%m` | Monat als Zahl (01 - 12) | `08` | +| `%Y` | Jahr vierstellig | `2026` | +| `%H` | Stunde (00 - 23) | `14` | +| `%M` | Minute (00 - 59) | `35` | +| `%S` | Sekunde (00 - 59) | `09` | + +--- + +## 🧠 3. Grundlegende Algorithmen: Suchen, Finden & Sortieren + +### A. Lineare Suche +Wenn du ein Element in einer Liste suchst, durchläufst du die Liste von vorne nach hinten: + +```python +def finde_person(personen, gesuchter_name): + for person in personen: + if person["name"] == gesuchter_name: + return person + return None +``` + +### B. Maximum & Minimum mit Schlüssel (`key=`) +Wie findet man die älteste Person oder das teuerste Produkt? + +```python +gruppe = [ + {"name": "Anna", "alter": 25}, + {"name": "Ben", "alter": 32}, + {"name": "Clara", "alter": 19} +] + +# Die Person mit dem höchsten Alter: +aelteste = max(gruppe, key=lambda person: person["alter"]) +print("Älteste Person:", aelteste["name"]) # "Ben" + +# Sortieren nach Alter aufsteigend: +sortiert = sorted(gruppe, key=lambda p: p["alter"]) +``` + +--- + +## 🎯 Deine Aufgaben in `aufgabe.py` + +Öffne die Datei `g10_comprehensions_datum_algorithmen/aufgabe.py` und löse die 5 TODOs: + +1. **TODO 1 (`quadratzahlen_gerade`):** Berechne die Quadrate aller geraden Zahlen mit einer List Comprehension. +2. **TODO 2 (`filtriere_lange_woerter`):** Filtere Wörter heraus, deren Länge `>= min_laenge` ist. +3. **TODO 3 (`tage_bis_datum`):** Berechne die Anzahl der Tage von heute bis zu einem Zieldatum (`YYYY-MM-DD`). +4. **TODO 4 (`formatiere_deutsches_datum`):** Formatiere ein Datumsobjekt in das Format `DD.MM.YYYY`. +5. **TODO 5 (`finde_aelteste_person`):** Finde in einer Liste von Personen-Dictionaries den Namen der ältesten Person. + +### Testen: +```bash +python3 test_aufgabe.py +``` +Sobald alle Tests `OK` sind, hast du den gesamten Grundlagen-Pfad erfolgreich abgeschlossen! 🏆🎉 diff --git a/g10_comprehensions_datum_algorithmen/aufgabe.py b/g10_comprehensions_datum_algorithmen/aufgabe.py new file mode 100644 index 0000000..7e47a16 --- /dev/null +++ b/g10_comprehensions_datum_algorithmen/aufgabe.py @@ -0,0 +1,148 @@ +""" +Kapitel G10: Comprehensions, Datum & Algorithmen – Aufgabenblatt +================================================================ +Schulabgleich: Thema 13.0 & 15.1 + +In diesem Kapitel meisterst du drei fortgeschrittene Kernkompetenzen: +1. List & Dict Comprehensions (kompakter, eleganter Python-Code) +2. Datum & Uhrzeit mit dem 'datetime'-Modul (Rechnen mit Tagen & Zeitspannen) +3. Grundlegende Algorithmen (Filtern, Suchen und Min/Max-Ermittlung) + +Bearbeite die Aufgaben von TODO 1 bis TODO 5. +Überprüfe deine Lösung im Terminal mit: + python3 test_aufgabe.py +""" + +from datetime import datetime, date, timedelta +from typing import List, Dict, Union + + +# ============================================================================== +# TODO 1: Quadratzahlen gerader Zahlen (List Comprehension) +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'quadratzahlen_gerade(zahlen: list[int]) -> list[int]'. +# +# Anforderungen: +# 1. Verwende eine elegante List Comprehension: [ausdruck for x in ... if ...] +# 2. Filtere alle geraden Zahlen (x % 2 == 0) heraus. +# 3. Berechne für jede gefilterte Zahl das Quadrat (x ** 2). +# 4. Gib die neue Liste zurück. +# +# Beispiel: +# eingabe = [1, 2, 3, 4, 5, 6] +# Ergebnis: [4, 16, 36] (denn 2²=4, 4²=16, 6²=36) +# ============================================================================== + +def quadratzahlen_gerade(zahlen: List[int]) -> List[int]: + """Gibt die Quadrate aller geraden Zahlen als List Comprehension zurück.""" + # Schreibe hier deinen Code für TODO 1: + pass + + +# ============================================================================== +# TODO 2: Wörter nach Mindestlänge filtern +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'filtriere_lange_woerter(woerter: list[str], min_laenge: int) -> list[str]'. +# +# Anforderungen: +# 1. Verwende vorzugsweise eine List Comprehension. +# 2. Behalte nur Wörter, deren Zeichenlänge größer oder gleich 'min_laenge' ist (len(w) >= min_laenge). +# 3. Gib die gefilterte Liste zurück. +# +# Beispiel: +# liste = ["Python", "ist", "eine", "tolle", "Sprache"] +# filtriere_lange_woerter(liste, 5) -> ["Python", "tolle", "Sprache"] +# ============================================================================== + +def filtriere_lange_woerter(woerter: List[str], min_laenge: int) -> List[str]: + """Filtert alle Wörter heraus, die mindestens 'min_laenge' Zeichen lang sind.""" + # Schreibe hier deinen Code für TODO 2: + pass + + +# ============================================================================== +# TODO 3: Tage bis Zieldatum berechnen (datetime & timedelta) +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'tage_bis_datum(ziel_datum_str: str) -> int'. +# +# Anforderungen: +# 1. Der Parameter 'ziel_datum_str' ist ein Datum im Format "YYYY-MM-DD" (z.B. "2026-12-24"). +# 2. Lies den Text mit 'datetime.strptime(ziel_datum_str, "%Y-%m-%d").date()' ein. +# 3. Ermittle das heutige Datum mit 'date.today()'. +# 4. Berechne die Differenz: (ziel_datum - heute).days. +# 5. Gib die Anzahl der verbleibenden Tage als Ganzzahl (int) zurück. +# (Positiv = in der Zukunft, Negativ = in der Vergangenheit, 0 = heute). +# ============================================================================== + +def tage_bis_datum(ziel_datum_str: str) -> int: + """Berechnet die Anzahl der Tage von heute bis zum angegebenen Zieldatum (YYYY-MM-DD).""" + # Schreibe hier deinen Code für TODO 3: + pass + + +# ============================================================================== +# TODO 4: Datum im deutschen Format darstellen (.strftime) +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'formatiere_deutsches_datum(datum_obj: Union[date, datetime]) -> str'. +# +# Anforderungen: +# 1. Die Funktion erhält ein 'date'- oder 'datetime'-Objekt. +# 2. Formatiere das Datum mit '.strftime()' im deutschen Standard-Format "DD.MM.YYYY" (z.B. "24.12.2026"). +# Tipp: %d = Tag (zweistellig), %m = Monat (zweistellig), %Y = Jahr (vierstellig). +# 3. Gib den formatierten String zurück. +# ============================================================================== + +def formatiere_deutsches_datum(datum_obj: Union[date, datetime]) -> str: + """Formatiert ein date- oder datetime-Objekt in das deutsche Format DD.MM.YYYY.""" + # Schreibe hier deinen Code für TODO 4: + pass + + +# ============================================================================== +# TODO 5: Älteste Person finden (Algorithmus: Maximum / Key-Suche) +# ------------------------------------------------------------------------------ +# Schreibe eine Funktion 'finde_aelteste_person(personen: list[dict]) -> str'. +# +# Anforderungen: +# 1. 'personen' ist eine Liste von Dictionaries, z.B.: +# [{"name": "Anna", "alter": 25}, {"name": "Ben", "alter": 32}, {"name": "Clara", "alter": 19}] +# 2. Wenn die Liste leer ist, gib einen leeren String "" zurück. +# 3. Finde die Person mit dem höchsten 'alter' (z.B. mit Schleife oder max(..., key=...)). +# 4. Gib den 'name' dieser ältesten Person zurück (im Beispiel: "Ben"). +# ============================================================================== + +def finde_aelteste_person(personen: List[Dict[str, Union[str, int]]]) -> str: + """Findet in einer Liste von Personen-Dicts die älteste Person und gibt deren Namen zurück.""" + # Schreibe hier deinen Code für TODO 5: + pass + + +# ============================================================================== +# Hauptprogramm zum Ausprobieren: +# (Du kannst diese Datei direkt mit 'python3 aufgabe.py' ausführen) +# ============================================================================== +if __name__ == "__main__": + print("--- 🚀 Testlauf: Comprehensions, Datum & Algorithmen ---") + + # Test TODO 1 + zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + print("Quadrate gerader Zahlen:", quadratzahlen_gerade(zahlen)) + + # Test TODO 2 + woerter = ["Code", "Python", "KI", "Entwicklung", "Funktion", "Loop"] + print("Wörter >= 6 Zeichen:", filtriere_lange_woerter(woerter, 6)) + + # Test TODO 3 & 4 + heute = date.today() + print("Heutiges deutsches Datum:", formatiere_deutsches_datum(heute)) + + silvester = f"{heute.year}-12-31" + print(f"Tage bis zum Jahresende ({silvester}):", tage_bis_datum(silvester)) + + # Test TODO 5 + gruppe = [ + {"name": "Lisa", "alter": 21}, + {"name": "Markus", "alter": 29}, + {"name": "Sarah", "alter": 24} + ] + print("Älteste Person in der Gruppe:", finde_aelteste_person(gruppe)) diff --git a/g10_comprehensions_datum_algorithmen/index.html b/g10_comprehensions_datum_algorithmen/index.html new file mode 100644 index 0000000..b60ca2d --- /dev/null +++ b/g10_comprehensions_datum_algorithmen/index.html @@ -0,0 +1,255 @@ + + + + + + Kapitel G10: Comprehensions, Datum & Algorithmen + + + + +
+
+ +
Kapitel G10 • Schulabgleich 13.0 & 15.1
+

Comprehensions, Datum & Algorithmen 🚀📅

+

Lerne, wie du elegante Einzeiler schreibst, mit Zeitspannen rechnest und grundlegende Such- und Filter-Algorithmen meisterst.

+
+
+ +
+ + +
+

1. List Comprehensions: Das Schnellschienen-Fließband

+ +

+ In Python musst du nicht jedes Mal eine leere Liste erstellen und eine 4-zeilige for-Schleife mit .append() schreiben. + Mit List Comprehensions erstellst und filterst du Listen in einer einzigen, eleganten Zeile! +

+ +
+
🏭 Die Fließband- & Filtersieb-Analogie:
+

Stell dir ein automatisiertes Fließband in einer Manufaktur vor:

+
    +
  • Die Rohstoffe (Elemente aus der Ausgangsliste) laufen über das Band (for x in liste).
  • +
  • Ein Sensor sortiert unpassende Teile sofort aus (if x % 2 == 0).
  • +
  • Am Ende wird das Teil direkt veredelt oder berechnet (x ** 2) und fällt fertig verpackt in die neue Liste!
  • +
+
+ +
+
+
+ + + +
+ Vergleich: Traditionelle Schleife vs. List Comprehension +
+
# 🐢 Vorher: Traditionelle for-Schleife (4 Zeilen)
+gerade_quadrate = []
+for x in range(1, 7):
+    if x % 2 == 0:
+        gerade_quadrate.append(x ** 2)
+
+# 🚀 Nachher: Elegante List Comprehension (1 Zeile!)
+gerade_quadrate = [x ** 2 for x in range(1, 7) if x % 2 == 0]
+print(gerade_quadrate)  # [4, 16, 36]
+
+ +

Dict Comprehensions: Dictionaries on the fly

+
+
Dict Comprehension Syntax
+
woerter = ["Python", "Code", "Entwicklung"]
+laengen = {w: len(w) for w in woerter}
+print(laengen)  # {'Python': 6, 'Code': 4, 'Entwicklung': 11}
+
+
+ + +
+

2. Datum, Uhrzeit & Zeitspannen (Das datetime-Modul)

+ +

+ Egal ob Ticket-Buchung, Geburtstags-Erinnerung oder Spielstand-Speicherung: Zeitberechnungen gehören zum Alltag jeder Programmiererin. +

+ +
+
🗓️ Die Kalender- & Stoppuhr-Analogie:
+
    +
  • date & datetime sind wie dein Kalender: Ein festes Datum wie der 24.12.2026.
  • +
  • timedelta ist wie eine Zeitspanne (z.B. "+ 14 Tage" oder "- 3 Stunden").
  • +
+
+ +
+
+ Python: Rechnen mit datetime & timedelta +
+
from datetime import datetime, date, timedelta
+
+# 1. Heute ermitteln:
+heute = date.today()
+
+# 2. Text in Datum parsen (.strptime):
+urlaub = datetime.strptime("2026-07-15", "%Y-%m-%d").date()
+
+# 3. Differenz in Tagen berechnen:
+tage_bis_urlaub = (urlaub - heute).days
+print(f"Noch {tage_bis_urlaub} Tage bis zum Urlaub!")
+
+# 4. Deutsches Datumsformat ausgeben (.strftime):
+print(urlaub.strftime("%d.%m.%Y"))  # Ausgabe: "15.07.2026"
+
+ +

Wichtige Formatierungs-Kürzel

+ + + + + + + + + + + + + + + + + + + + + + + + + +
KürzelBedeutungBeispiel
%dTag im Monat mit führender Null (01..31)05 oder 29
%mMonat mit führender Null (01..12)08
%YVierstelliges Jahr2026
+
+ + +
+

3. Grundlegende Algorithmen: Min/Max, Suchen & Sortieren

+ +

+ Ein Algorithmus ist eine präzise Handlungsanweisung zur Lösung eines Problems. + Hier lernst du die wichtigsten Muster kennen: +

+ +
+
+ Maximum suchen mit key= +
+
personen = [
+    {"name": "Anna", "alter": 25},
+    {"name": "Ben", "alter": 32},
+    {"name": "Clara", "alter": 19}
+]
+
+# Die älteste Person mit max() und Lambda-Schlüssel finden:
+aelteste = max(personen, key=lambda p: p["alter"])
+print(f"Älteste Person: {aelteste['name']} ({aelteste['alter']} Jahre)")
+# Ausgabe: Älteste Person: Ben (32 Jahre)
+
+
+ + +
+

4. 🎯 Deine Aufgaben in aufgabe.py

+ +

Öffne die Datei g10_comprehensions_datum_algorithmen/aufgabe.py und bearbeite die folgenden 5 Aufgaben:

+ +
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+ +

💡 Lösungshinweise (Aufklappen bei Bedarf)

+ +
+ 🔍 Tipp zu TODO 1 & 2 (List Comprehensions) +
+
+
# TODO 1:
+return [x ** 2 for x in zahlen if x % 2 == 0]
+
+# TODO 2:
+return [w for w in woerter if len(w) >= min_laenge]
+
+
+
+ +
+ 🔍 Tipp zu TODO 3 (Datum einlesen & subtrahieren) +
+
+
ziel = datetime.strptime(ziel_datum_str, "%Y-%m-%d").date()
+heute = date.today()
+return (ziel - heute).days
+
+
+
+
+ + +
+

5. 🧪 Lösung überprüfen & Kursabschluss

+

Führe den Test im Terminal aus:

+
+
+ Terminal +
+
python3 test_aufgabe.py
+
+ + +
+ +
+ + + diff --git a/g10_comprehensions_datum_algorithmen/musterloesung.py b/g10_comprehensions_datum_algorithmen/musterloesung.py new file mode 100644 index 0000000..44789e2 --- /dev/null +++ b/g10_comprehensions_datum_algorithmen/musterloesung.py @@ -0,0 +1,72 @@ +""" +Kapitel G10: Comprehensions, Datum & Algorithmen – Musterlösung +================================================================ +Schulabgleich: Thema 13.0 & 15.1 + +Hier findest du die vollständige Musterlösung zu allen Aufgaben. +""" + +from datetime import datetime, date, timedelta +from typing import List, Dict, Union + + +# TODO 1: Quadratzahlen gerader Zahlen (List Comprehension) +def quadratzahlen_gerade(zahlen: List[int]) -> List[int]: + """Gibt die Quadrate aller geraden Zahlen als List Comprehension zurück.""" + return [x ** 2 for x in zahlen if x % 2 == 0] + + +# TODO 2: Wörter nach Mindestlänge filtern +def filtriere_lange_woerter(woerter: List[str], min_laenge: int) -> List[str]: + """Filtert alle Wörter heraus, die mindestens 'min_laenge' Zeichen lang sind.""" + return [w for w in woerter if len(w) >= min_laenge] + + +# TODO 3: Tage bis Zieldatum berechnen (datetime & timedelta) +def tage_bis_datum(ziel_datum_str: str) -> int: + """Berechnet die Anzahl der Tage von heute bis zum angegebenen Zieldatum (YYYY-MM-DD).""" + ziel_datum = datetime.strptime(ziel_datum_str, "%Y-%m-%d").date() + heute = date.today() + return (ziel_datum - heute).days + + +# TODO 4: Datum im deutschen Format darstellen (.strftime) +def formatiere_deutsches_datum(datum_obj: Union[date, datetime]) -> str: + """Formatiert ein date- oder datetime-Objekt in das deutsche Format DD.MM.YYYY.""" + return datum_obj.strftime("%d.%m.%Y") + + +# TODO 5: Älteste Person finden (Algorithmus: Maximum / Key-Suche) +def finde_aelteste_person(personen: List[Dict[str, Union[str, int]]]) -> str: + """Findet in einer Liste von Personen-Dicts die älteste Person und gibt deren Namen zurück.""" + if not personen: + return "" + aelteste = max(personen, key=lambda p: p["alter"]) + return str(aelteste["name"]) + + +if __name__ == "__main__": + print("--- 🚀 Musterlösung G10: Comprehensions, Datum & Algorithmen ---") + + # 1. Quadratzahlen + test_zahlen = [1, 2, 3, 4, 5, 6] + print("Gerade Quadrate:", quadratzahlen_gerade(test_zahlen)) + + # 2. Wörter filtern + woerter_liste = ["Python", "ist", "klasse", "und", "schnell"] + print("Wörter >= 6:", filtriere_lange_woerter(woerter_liste, 6)) + + # 3. Tage bis Zieldatum + morgen = (date.today() + timedelta(days=1)).strftime("%Y-%m-%d") + print(f"Tage bis morgen ({morgen}):", tage_bis_datum(morgen)) + + # 4. Deutsches Datum + print("Heute im deutschen Format:", formatiere_deutsches_datum(date.today())) + + # 5. Älteste Person + leute = [ + {"name": "Anna", "alter": 25}, + {"name": "Ben", "alter": 35}, + {"name": "Clara", "alter": 28} + ] + print("Älteste Person:", finde_aelteste_person(leute)) diff --git a/g10_comprehensions_datum_algorithmen/test_aufgabe.py b/g10_comprehensions_datum_algorithmen/test_aufgabe.py new file mode 100644 index 0000000..90e1208 --- /dev/null +++ b/g10_comprehensions_datum_algorithmen/test_aufgabe.py @@ -0,0 +1,143 @@ +import unittest +import sys +from datetime import datetime, date, timedelta +from pathlib import Path + +# Sicherstellen, dass das aktuelle Verzeichnis im Modulpfad ist +sys.path.insert(0, str(Path(__file__).parent)) + +try: + import aufgabe +except ImportError as e: + raise ImportError(f"Konnte 'aufgabe.py' nicht importieren: {e}") + + +class TestKapitelG10(unittest.TestCase): + + def test_01_quadratzahlen_gerade(self): + """Prüft, ob quadratzahlen_gerade Quadrate der geraden Zahlen liefert.""" + self.assertTrue( + hasattr(aufgabe, "quadratzahlen_gerade"), + "Fehler: Funktion 'quadratzahlen_gerade' fehlt in aufgabe.py!" + ) + eingabe = [1, 2, 3, 4, 5, 6] + ergebnis = aufgabe.quadratzahlen_gerade(eingabe) + erwartet = [4, 16, 36] + self.assertIsInstance(ergebnis, list, "Fehler: Rückgabewert muss eine Liste sein!") + self.assertEqual( + ergebnis, + erwartet, + f"Fehler: Erwartet {erwartet}, aber erhalten: {ergebnis}" + ) + + # Nur ungerade Zahlen + self.assertEqual(aufgabe.quadratzahlen_gerade([1, 3, 5, 7]), []) + # Leere Liste + self.assertEqual(aufgabe.quadratzahlen_gerade([]), []) + + def test_02_filtriere_lange_woerter(self): + """Prüft, ob filtriere_lange_woerter Wörter nach Mindestlänge filtert.""" + self.assertTrue( + hasattr(aufgabe, "filtriere_lange_woerter"), + "Fehler: Funktion 'filtriere_lange_woerter' fehlt in aufgabe.py!" + ) + woerter = ["Python", "ist", "eine", "tolle", "Sprache"] + ergebnis = aufgabe.filtriere_lange_woerter(woerter, 5) + erwartet = ["Python", "tolle", "Sprache"] + self.assertIsInstance(ergebnis, list, "Fehler: Rückgabewert muss eine Liste sein!") + self.assertEqual( + ergebnis, + erwartet, + f"Fehler: Erwartet {erwartet}, aber erhalten: {ergebnis}" + ) + + # Hohe Mindestlänge (kein Treffer) + self.assertEqual(aufgabe.filtriere_lange_woerter(woerter, 20), []) + # Mindestlänge 0 (alle Wörter) + self.assertEqual(aufgabe.filtriere_lange_woerter(woerter, 0), woerter) + + def test_03_tage_bis_datum(self): + """Prüft die Datumsberechnung mit strptime und timedelta.""" + self.assertTrue( + hasattr(aufgabe, "tage_bis_datum"), + "Fehler: Funktion 'tage_bis_datum' fehlt in aufgabe.py!" + ) + heute = date.today() + + # 1. Heute + heute_str = heute.strftime("%Y-%m-%d") + self.assertEqual(aufgabe.tage_bis_datum(heute_str), 0, "Fehler: Tage bis heute muss 0 sein!") + + # 2. In 14 Tagen + zukunft = heute + timedelta(days=14) + zukunft_str = zukunft.strftime("%Y-%m-%d") + self.assertEqual( + aufgabe.tage_bis_datum(zukunft_str), + 14, + f"Fehler: Tage bis {zukunft_str} sollte 14 sein!" + ) + + # 3. Vor 5 Tagen + vergangenheit = heute - timedelta(days=5) + vergangenheit_str = vergangenheit.strftime("%Y-%m-%d") + self.assertEqual( + aufgabe.tage_bis_datum(vergangenheit_str), + -5, + f"Fehler: Tage bis {vergangenheit_str} sollte -5 sein!" + ) + + def test_04_formatiere_deutsches_datum(self): + """Prüft die Formatierung mit .strftime in DD.MM.YYYY.""" + self.assertTrue( + hasattr(aufgabe, "formatiere_deutsches_datum"), + "Fehler: Funktion 'formatiere_deutsches_datum' fehlt in aufgabe.py!" + ) + # Test mit date Objekt + d1 = date(2026, 12, 24) + self.assertEqual( + aufgabe.formatiere_deutsches_datum(d1), + "24.12.2026", + "Fehler: date(2026, 12, 24) sollte als '24.12.2026' formatiert werden." + ) + + # Test mit datetime Objekt und einstelligen Ziffern (Prüfung führender Nullen) + dt = datetime(2025, 3, 4, 15, 30) + self.assertEqual( + aufgabe.formatiere_deutsches_datum(dt), + "04.03.2025", + "Fehler: Führende Nullen beachten! datetime(2025, 3, 4) -> '04.03.2025'." + ) + + def test_05_finde_aelteste_person(self): + """Prüft die Algorithmus-Suche nach der ältesten Person.""" + self.assertTrue( + hasattr(aufgabe, "finde_aelteste_person"), + "Fehler: Funktion 'finde_aelteste_person' fehlt in aufgabe.py!" + ) + personen = [ + {"name": "Anna", "alter": 25}, + {"name": "Ben", "alter": 32}, + {"name": "Clara", "alter": 19} + ] + self.assertEqual( + aufgabe.finde_aelteste_person(personen), + "Ben", + "Fehler: 'Ben' (32) ist die älteste Person." + ) + + # Einzelne Person + self.assertEqual( + aufgabe.finde_aelteste_person([{"name": "Opa Wilhelm", "alter": 88}]), + "Opa Wilhelm" + ) + + # Leere Liste + self.assertEqual( + aufgabe.finde_aelteste_person([]), + "", + "Fehler: Bei leerer Personenliste soll ein leerer String '' zurückgegeben werden." + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/index.html b/index.html index 18cd813..7ecc761 100644 --- a/index.html +++ b/index.html @@ -3,16 +3,51 @@ - Python OOP Lernportal: Klassen & Objekte + Python Lernportal: Grundlagen, OOP & Software-Entwicklung +
-
🐍 Python Nachhilfe & Selbstlern-Kurs
-

Objektorientierte Programmierung (OOP)

-

Lerne Schritt für Schritt, wie du eigene Klassen baust, Methoden nutzt und professionell mit Git & Unit Tests arbeitest.

+
🐍 Python Nachhilfe- & Selbstlern-Portal
+

Vom Einsteiger zum Software-Entwickler

+

3 strukturierte Lehrpfade: Prozedurale Grundlagen • Objektorientierung • Professionelle Werkzeuge (Git, TDD, Tkinter)

@@ -20,25 +55,160 @@
-

👋 Willkommen zu deiner Übungsreihe!

+

👋 Willkommen zu deiner interaktiven Python-Lernreise!

- Dieser Kurs begleitet dich durch die gesamte Objektorientierte Programmierung in Python – exakt abgestimmt auf dein Schulskriptum, aber mit viel mehr Erklärungen, Analogien, Visualisierungen und automatischen Tests! + Dieses Portal begleitet dich Schritt für Schritt von den ersten Schritten in Python bis hin zu vollständigen Desktop-Apps mit grafischer Oberfläche. Alle Aufgaben sind mit verständlichen Erklärungen, Alltags-Analogien, visuellen Beispielen und automatischen Tests für sofortiges Feedback ausgestattet.

-
-
🏗️ Die Grundidee von OOP in einem Satz:
- Eine Klasse ist wie ein Bauplan (oder eine Plätzchen-Ausstechform). Ein Objekt ist das fertige Haus (oder das gebackene Plätzchen), das nach diesem Bauplan entstanden ist! +
+
+ Lehrpfad 1 +

🌱 Grundlagen der Programmierung

+

Für Einsteiger oder zur Wiederholung: Rechnen, Variablen, Bedingungen, Schleifen, Funktionen, Listen, Dictionaries & Strings (G01–G10).

+
+
+ Lehrpfad 2 +

🏗️ Einstieg in OOP & Tools

+

Klassen, Baupläne, __init__, Methoden, UML-Diagramme, VS Code Umstieg, Git-Versionskontrolle & Tamagotchi-Projekt (00–06).

+
+
+ Lehrpfad 3 +

🚀 Fortgeschrittenes OOP & GUIs

+

Speicher-Referenzen, Dunder-Methoden, eigene Unit Tests (TDD), Vererbung, Polymorphie, Exceptions, JSON-Persistenz & Tkinter (07–14).

+
📚 Schulabgleich & Lehrplan:
- Alle Kapitel sind 1:1 mit den Themen deines Informatik-Unterrichts (Type Hints, UML, Assoziationen, Referenzen, Vererbung, Tkinter) abgestimmt. Ausführlicher Plan in LEHRPLAN.md. + Alle Kapitel sind 1:1 mit dem offiziellen Skriptum des Informatik-Unterrichts abgestimmt. Detaillierter Leitfaden in LEHRPLAN.md.
- +
-

🌱 Phase 1: Grundlagen OOP & Entwickler-Werkzeuge

+
+
+

🛣️ Lehrpfad 1: Grundlagen der Programmierung (Prozedural)

+
Die unverzichtbare Basis: Datentypen, Kontrollstrukturen, Funktionen und Datenstrukturen.
+
+ 10 Kapitel • G01 bis G10 +
+ +
+ + + +
+ Grundlagen G01 +
🧮 Python als Taschenrechner
+
Zahlen (int, float), Grundrechenarten, Ganzzahldivision //, Modulo %, Potenzen ** & print().
+
+ +
+ + + +
+ Grundlagen G02 +
📦 Variablen & Datentypen
+
Speicherboxen, die 4 Basisdatentypen (int, float, str, bool), Type Casting & Type Hints.
+
+ +
+ + + +
+ Grundlagen G03 +
💬 Interaktive Ein- & Ausgabe
+
Benutzereingaben mit input(), Textumwandlung und moderne formatierte f-Strings (z.B. {preis:.2f} €).
+
+ +
+ + + +
+ Grundlagen G04 +
🚦 Bedingungen & Verzweigungen
+
Entscheidungen treffen mit if, elif, else, Vergleichs- und Logikoperatoren (and, or, not).
+
+ +
+ + + +
+ Grundlagen G05 +
🎡 Schleifen & Wiederholungen
+
Zählschleifen mit for in range(...), Bedingungsschleifen mit while, break & continue.
+
+ +
+ + + +
+ Grundlagen G06 +
🪄 Eigene Funktionen & Module
+
Wiederverwendbarer Code mit def, return, Parametern, Scope und Modulen (math, random).
+
+ +
+ + + +
+ Grundlagen G07 +
📋 Listen & Sequenzen
+
Elemente speichern, Indexzugriff, Slicing ([1:4]), Listen-Methoden (append, pop) & sum/len/min/max.
+
+ +
+ + + +
+ Grundlagen G08 +
🔤 Textverarbeitung & Strings
+
String-Methoden wie .strip(), .replace(), .split(), .join() und Palindrom-Analyse.
+
+ +
+ + + +
+ Grundlagen G09 +
🗃️ Dictionaries & Sets
+
Schlüssel-Wert-Paare ({key: val}), sicheres .get(), Telefonbuch-Suche & Mengen (set) ohne Duplikate.
+
+ +
+ + + +
+ Grundlagen G10 +
🚀 Comprehensions & Algorithmen
+
List Comprehensions ([x for x in ...]), datetime für Kalenderberechnungen & Min/Max-Suche.
+
+ +
+ +
+
+ + +
+
+
+

🛣️ Lehrpfad 2: Einstieg in OOP & Entwickler-Werkzeuge

+
Der Übergang zur Objektorientierung: Baupläne, Methoden, UML, VS Code & Git.
+
+ Kapitel 00 bis 06 +
+
@@ -46,9 +216,9 @@
Warm-up 00
🔍 Fehlersuche & Bugs
-
Tracebacks richtig lesen, Einrückungsfehler (IndentationError), Typfehler und Detektiv-Aufgabe.
+
Tracebacks von unten lesen, Einrückungen (IndentationError), Typfehler und Detektiv-Aufgabe.
- + @@ -95,10 +265,10 @@
Exkurs 04b -
🚀 Umstieg & Installation VS Code
+
🚀 Installation & Setup VS Code
Installationsanleitung (Win/Mac/Linux), Plugins (Error Lens, Python), Shortcuts & Terminal.
- +
@@ -106,9 +276,9 @@
Exkurs 04c
🌿 Git & Versionskontrolle
-
Die Programmier-Zeitmaschine: Spielstände (Commits) sichern, Diffs in VS Code, Terminal-Befehle & Push.
+
Die Programmier-Zeitmaschine: Spielstände (Commits) sichern, Diffs in VS Code, Terminal & Push.
- + @@ -134,9 +304,16 @@
- +
-

🧠 Phase 2: Fortgeschrittenes OOP, Testing & Persistenz

+
+
+

🛣️ Lehrpfad 3: Fortgeschrittenes OOP, Testing & Desktop-GUIs

+
Professionelle Softwaretechnik: Speicherverwaltung, Vererbung, TDD-Tests, Persistenz und GUIs.
+
+ Kapitel 07 bis 14 +
+
@@ -144,91 +321,81 @@
Kapitel 07
🌳 Referenzen & Stammbäume
-
Speicherbereiche im RAM, Aliasing (b = a), is vs. == und Familienstammbaum-Klassen.
+
Speicherbereiche im RAM, Aliasing (b = a), is vs. == und Familienstammbäume.
- +
- Kapitel 08 • Neu + Kapitel 08
➕ Operator Overloading & Dunder
-
Eigene Klassen rechen- und vergleichbar machen mit __add__, __mul__, __eq__, __lt__ für 2D-Vektoren & Wegstrecken.
+
Eigene Klassen rechenbar machen mit __add__, __mul__, __eq__ für 2D-Vektoren & Wegstrecken.
- +
- Kapitel 09 • Neu + Kapitel 09
🧪 Eigene Unit Tests & TDD
-
Schreibe eigene Testsuiten mit unittest, self.assertEqual(), assertRaises() und lerne Test-Driven Development.
+
Schreibe eigene Testsuiten mit unittest, self.assertEqual(), assertRaises() und lerne TDD.
- +
- Kapitel 10 • Neu + Kapitel 10
🧬 Vererbung & super()
-
DRY-Prinzip, Basis- & Kindklassen, super().__init__(), Methoden erweitern & Fahrzeugflotten-Hierarchie.
+
DRY-Prinzip, Basis- & Kindklassen, super().__init__(), Methoden erweitern & Fahrzeugflotten.
- +
- Kapitel 11 • Neu + Kapitel 11
🎭 Polymorphie & Interfaces
-
Vielgestaltigkeit, Duck Typing, abstrakte Basisklassen (ABC), Geometrie-Engine & Zeichenflächen-Manager.
+
Vielgestaltigkeit, Duck Typing, abstrakte Basisklassen (ABC), Geometrie-Engine & Zeichenflächen.
- +
- Kapitel 12 • Neu + Kapitel 12
🛡️ Exceptions & Fehlerbehandlung
-
Robuste Fehlerkontrolle mit try-except-else-finally, raise und eigene Exception-Klassen im Bankkonto-Simulator.
+
Robuste Fehlerkontrolle mit try-except-else-finally, raise und eigene Fehlerklassen im Banking.
- +
- Kapitel 13 • Neu + Kapitel 13
💾 Datei-Persistenz: JSON & CSV
-
Dauerhaftes Speichern & Laden: Savegames mit to_dict()/from_dict(), json.dump und CSV-Tabellen-Export.
+
Dauerhaftes Speichern & Laden: Savegames mit to_dict()/from_dict(), json.dump und CSV-Export.
- +
- Kapitel 14 • Neu + Kapitel 14
🖥️ Desktop-GUIs mit Tkinter
Fenster, Buttons, Labels, Grid-Layout, Event-Loop und MVC-Architektur im interaktiven Zähler & Rechner.
- +
- -
-
- Weitere Kapitel • Geplant -
🧬 Vererbung, Polymorphie & Master-Projekt
-
Operator Overloading (08), Unit Testing (09), Vererbung (10), Polymorphie (11), Parameter (15) und Master-Projekt (16).
-
- -
-
@@ -246,9 +413,9 @@
- Terminal (Beispiel für Kapitel 01) + Terminal (Beispiel für Grundlagen G01)
-
cd 01_einstieg_klassen
+        
cd g01_erste_schritte_taschenrechner
 python3 test_aufgabe.py
@@ -293,7 +460,7 @@ object has no attribute 'gang'
💡 Die 3 goldenen Regeln beim Arbeiten mit Tests:
  1. Immer return statt nur print(): Tests prüfen das, was eine Funktion mit return zurückliefert. Ein print() zeigt es zwar auf dem Bildschirm an, reicht dem Test aber nicht!
  2. -
  3. Exakte Namen beachten: Wenn die Aufgabe def mutter_setzen(self): verlangt, darf die Methode nicht def MutterSetzen heißen (Python unterscheidet Groß- und Kleinschreibung).
  4. +
  5. Exakte Namen beachten: Wenn die Aufgabe def addieren(a, b): verlangt, darf die Funktion nicht def Addieren heißen (Python unterscheidet Groß- und Kleinschreibung).
  6. Fehlermeldungen von unten nach oben lesen: Ganz unten steht immer genau, was nicht gepasst hat!
@@ -322,7 +489,7 @@ object has no attribute 'gang'
❌ Wie wechsle ich im Terminal zwischen den Ordnern hin und her?
-

In einen Unterordner hinein: cd 01_einstieg_klassen

+

In einen Unterordner hinein: cd g01_erste_schritte_taschenrechner

Einen Ordner nach oben zurück: cd ..

Inhalt des aktuellen Ordners auflisten: ls (oder dir auf Windows)

diff --git a/test_all.py b/test_all.py index d78b05f..18110db 100644 --- a/test_all.py +++ b/test_all.py @@ -11,6 +11,16 @@ import unittest from pathlib import Path KAPITEL = [ + ("Grundlagen 01: Python als Taschenrechner", "g01_erste_schritte_taschenrechner"), + ("Grundlagen 02: Variablen & Datentypen", "g02_variablen_und_datentypen"), + ("Grundlagen 03: Interaktive Ein- & Ausgabe", "g03_ein_und_ausgabe"), + ("Grundlagen 04: Verzweigungen & Bedingungen", "g04_verzweigungen_und_bedingungen"), + ("Grundlagen 05: Schleifen & Wiederholungen", "g05_schleifen_und_wiederholungen"), + ("Grundlagen 06: Eigene Funktionen & Module", "g06_funktionen_und_module"), + ("Grundlagen 07: Listen & Sequenzen", "g07_listen_und_sequenzen"), + ("Grundlagen 08: Textverarbeitung & Strings", "g08_textverarbeitung_und_strings"), + ("Grundlagen 09: Dictionaries & Sets", "g09_dictionaries_und_sets"), + ("Grundlagen 10: Comprehensions & Algorithmen", "g10_comprehensions_datum_algorithmen"), ("Kapitel 00: Python-Fehlersuche (Warm-up)", "00_fehlersuche_und_grundlagen"), ("Kapitel 01: Einstieg in Klassen", "01_einstieg_klassen"), ("Kapitel 02: Konstruktor & self", "02_init_und_self"),