![[Formales]] ![[08_dokumentation__688767651.png]] > [!TLDR] > Dokumentation macht Euren Code lesbar und wartbar. Dazu zählen Kommentare (einzeilig, mehrzeilig) sowie Docstrings in Funktionen und Modulen. > > Außerdem kann man mit Funktionen samt Defaultwerten und klaren Parametern für mehr Transparenz sorgen. ![[00 Python 101 Syntax#08 Dokumentation]] # Kommentare > [!TLDR] > In Python kommentiert man üblicherweise so: > **Einzeilig** > \# Kommentartext > > **Mehrzeilig** > Entweder mehrere Zeilen mit # oder ein mehrzeiliger String """ ... """ (wenn es nur als Kommentar und nicht als Docstring fungieren soll). > [!QUOTE] Kommentare in einem kleinen CSV-Parsing > 1. Öffne apotheken_daten.csv. > 2. Lese den Header weg, lese 3 Zeilen. > 3. Kommentiere, warum Du bestimmte Checks machst (z. B. if not line: break). ```python # doc_comment_task1.py filename = "apotheken_daten.csv" with open(filename, "r", encoding="utf-8") as f: header = f.readline() # Headerzeile ignorieren for i in range(3): # Nur drei Zeilen verarbeiten line = f.readline() if not line: # Keine Zeile mehr vorhanden, Ende break # Daten parsen parts = line.strip().split(";") # Spalte[0] = Medikament, Spalte[1] = Kategorie, ... med_name = parts[0] cat = parts[1] print(f"Zeile {i}: Medikament={med_name}, Kategorie={cat}") # Fertig mit dieser Zeile ``` Hier sind die Kommentare hauptsächlich, um anderen zu erklären, was in welchem Schritt passiert. --- > [!SUMMARY] Kommentare begründen Logik > 1. Erstelle ein Skript, das 5 Zeilen aus apotheken_daten.csv einliest. > 2. Wenn sales_str (Spalte\[8]) leer ist, setze sales=0 und kommentiere, warum Du das tust (z. B. „fehlender Wert => 0“). > 3. Drucke am Ende, wie viele Zeilen Du verarbeitet hast, mit einem Kommentar, wieso du diese Output-Zeile am Ende hinzufügst. > [!SUMMARY]- Beispielhafte Lösung > ```python > # doc_comment_task2.py > > filename = "apotheken_daten.csv" > counter = 0 # Zähler für gelesene Zeilen > > with open(filename, "r", encoding="utf-8") as f: > header = f.readline() # Header ignorieren > > for _ in range(5): > line = f.readline() > if not line: > break > parts = line.strip().split(";") > > sales_str = parts[8] # Spalte Verkaufszahlen > # Wenn kein Wert, interpretieren wir 0 > if sales_str == "": > sales = 0 > else: > sales = int(sales_str) > > # Erhöhe den Zähler > counter += 1 > > # Ausgabe am Ende, um zu sehen, wie viele Zeilen tatsächlich gelesen wurden > print("Verarbeitete Zeilen:", counter) > ``` # Docstrings > [!TLDR] > Docstrings sind mehrzeilige Strings, die direkt unterhalb der def-Zeile (oder ganz am Anfang eines Moduls) stehen. Sie beschreiben den Zweck, die Parameter und evtl. den Rückgabewert einer Funktion. > > Manche Teams nutzen Zusätze wie Datentypen, Defaultwerte oder Beschreibungen im Stil von Sphinx (reST) oder Google-Style-Docstrings. > > Beispiel (Sphinx/reST-Style): ```python def example_func(param1, param2="Test"): """ Beispiel-Funktion, die zwei Parameter nimmt. :param param1: Beschreibung von param1 (z.B. string) :type param1: str :param param2: Optionaler zweiter Parameter, default="Test" :type param2: str :return: Gibt eine kombinierte Zeichenkette zurück. :rtype: str """ return param1 + " - " + param2 ``` Ihr könnt den Stil natürlich an Eure Anforderungen anpassen. > [!QUOTE] Funktion mit Docstring: parse_sales > 1. Schreibe eine Funktion parse_sales(value:str) -> int mit Docstring, die aus dem String value einen int macht (oder 0, wenn leer). > 2. Dokumentiere den Zweck im Docstring, den Parameter value und den Rückgabewert. > 3. Nutze diese Funktion in einem kleinen Skript, das 3 Zeilen aus der CSV liest. ```python # doc_docstring_task1.py def parse_sales(value: str) -> int: """ Konvertiert einen String in einen int-Wert für Verkaufszahlen. Falls der String leer oder nicht konvertierbar ist, wird 0 zurückgegeben. :param value: String, der die Verkaufszahlen repräsentiert (z.B. "50"). :type value: str :return: Integerwert (z.B. 50), 0 bei Leerstring. :rtype: int """ if value.isdigit(): return int(value) else: return 0 filename = "apotheken_daten.csv" with open(filename, "r", encoding="utf-8") as f: header = f.readline() # Header weg for i in range(3): line = f.readline() if not line: break parts = line.strip().split(";") sales_str = parts[8] # Spalte Verkaufszahlen sales = parse_sales(sales_str) print(f"Zeile {i}: Sales={sales}") ``` Hier siehst Du einen ausführlicheren Docstring inklusive Parametertyp und Rückgabetyp. --- > [!SUMMARY] Funktion get_price mit Defaultwert und Docstring > 1. Definiere eine Funktion get_price(price_str: str = "", default: float = 0.0) -> float. Falls price_str leer ist, gib default zurück, sonst konvertiere price_str in float. > 2. Schreibe in den Docstring: > - Zweck: „Konvertiert Preisstring in float, mit wählbarem Default.“ > - Parameter price_str und default. > - Rückgabewert. > 1. Verwende die Funktion, um 5 Zeilen einzulesen (Spalte Preis an Index ~13). Drucke die resultierenden Floats. > [!SUMMARY]- Beispielhafte Lösung > ```python > # doc_docstring_task2.py > > def get_price(price_str: str = "", default: float = 0.0) -> float: > """ > Konvertiert den Preis-String in einen float-Wert. > > Falls der String leer ist, wird 'default' zurückgegeben. > > :param price_str: Preis als String, z.B. "12.99". Kann leer sein. > :type price_str: str > :param default: Wert, der zurückgegeben wird, falls 'price_str' leer ist. > :type default: float > :return: Der konvertierte float-Wert oder der 'default'-Wert bei Leerstring. > :rtype: float > """ > if price_str == "": > return default > else: > return float(price_str) > > filename = "apotheken_daten.csv" > > with open(filename, "r", encoding="utf-8") as f: > header = f.readline() > for i in range(5): > line = f.readline() > if not line: > break > parts = line.strip().split(";") > p_str = parts[13] if len(parts) > 13 else "" > price_val = get_price(p_str, default=0.0) > print(f"Zeile {i}: Preis = {price_val}") > ``` Hier zeigen wir: 1) Docstring, 2) Defaultwert, 3) Datentypen in der Funktionssignatur. # Funktionsdefinitionen und Aufrufsignaturen > [!TLDR] > - def meine_funktion(param1, param2="Standard"): > - Positions-Argumente vs. Keyword-Argumente (meine_funktion("Hallo", param2="Welt")) > - Mehrere Rückgabewerte: return wert1, wert2 -> Kommt als Tuple zurück. > [!QUOTE] Zwei Parameter, optionaler Dritter > > [!NOTE] Ziel > > Eine Funktion > > ```python > > filter_line(parts, category="Schmerzmittel", min_sales=10) -> bool. > > ``` > > > [!NOTE] Die Funktion prüft > > - parts\[1] == category? > > - parts\[8] >= min_sales? > > - Gibt True oder False zurück. ```python # doc_func_task1.py def filter_line( parts: list, category: str = "Schmerzmittel", min_sales: int = 10 ) -> bool: """ Prüft, ob die gegebene Zeile (als Liste 'parts') die gewünschte 'category' hat und ob die Verkaufszahlen >= min_sales liegen. :param parts: Liste der Spalten einer CSV-Zeile. :type parts: list :param category: Gewünschte Kategorie (Default: "Schmerzmittel"). :type category: str :param min_sales: Minimale Verkaufszahlen (Default: 10). :type min_sales: int :return: True, wenn parts[1] == category und parts[8] >= min_sales. :rtype: bool """ if len(parts) < 9: return False # unvollständige Zeile cat = parts[1] sales_str = parts[8] sales = int(sales_str) if sales_str.isdigit() else 0 return (cat == category) and (sales >= min_sales) # Beispielnutzung filename = "apotheken_daten.csv" with open(filename, "r", encoding="utf-8") as f: header = f.readline() for i in range(5): line = f.readline() if not line: break parts = line.strip().split(";") if filter_line(parts, category="Allergiemittel", min_sales=20): print("Zeile passt zum Filter:", line.strip()) ``` --- > [!SUMMARY] Funktion, die (bool_flag, converted_value) zurückgibt > 1. Definiere eine Funktion check_discount(parts, discount_threshold=10.0) -> (bool, float). > 2. Sie soll: > - Spalte „Rabatt (% Rabatt)“ (Index ~14) parsen (float, bei Leerstring 0.0). > - Prüfen, ob der Rabatt >= discount_threshold ist. > - Zurückgeben: (True, float_value) wenn >= threshold, sonst (False, float_value). > 3. Dokumentiere im Docstring sowohl Parameter als auch Rückgabewert. > 4. Teste die Funktion an 5 Zeilen, drucke, wer True/False liefert + den Wert. > [!SUMMARY]- Beispielhafte Lösung > ```python > # doc_func_task2.py > > def check_discount(parts: list, discount_threshold: float = 10.0) -> (bool, float): > """ > Prüft, ob der Rabatt in parts[14] >= discount_threshold ist. > > :param parts: Liste der CSV-Spalten. > :type parts: list > :param discount_threshold: Mindest-Rabatt (Default = 10.0). > :type discount_threshold: float > :return: Tuple (bool_flag, discount_value) -> bool_flag=True, wenn discount_value >= threshold. > :rtype: tuple(bool, float) > """ > # Sicherheitscheck > if len(parts) < 15: > return (False, 0.0) > > discount_str = parts[14] > if discount_str == "": > discount_val = 0.0 > else: > discount_val = float(discount_str) > > # Prüfen > is_above = discount_val >= discount_threshold > return (is_above, discount_val) > > filename = "apotheken_daten.csv" > with open(filename, "r", encoding="utf-8") as f: > header = f.readline() > > for i in range(5): > line = f.readline() > if not line: > break > parts = line.strip().split(";") > flag, val = check_discount(parts, discount_threshold=15.0) > print(f"Zeile {i}: Rabatt={val}, >=15? {flag}") > ``` # Fazit > [!SUMMARY] > - Kommentare erklären Warum etwas passiert oder klären komplexe Stellen im Code. > - Docstrings ermöglichen ausführliche Beschreibung von Parametern, Datentypen und Rückgabewerten. Sie sind besonders nützlich für Tools wie help(), IDE-Hover, Sphinx-Dokumentation etc. > - Funktionsdefinition mit Default-Werten (param=default) und Typ-Hinweisen (param: type -> return: type) macht Code selbstbeschreibend. # Quiz ## 1 > [!QUESTION] Welche Zeichen (bzw. Symbol) verwendest Du in Python, um einzeilige Kommentare zu schreiben? > 1. [ ] // > 2. [ ] -- > 3. [ ] /\* ... \*/ > 4. [ ] \# > [!SUCCESS]- > **4 -> #** > In Python wird für einzeilige Kommentare das Rautezeichen # genutzt. ## 2 > [!QUESTION] Du hast ein Skript, das 5 Zeilen aus apotheken_daten.csv einliest. In welchen Fällen bieten sich Kommentare besonders an? > 1. [ ] Nur, wenn man eine Schleife verwendet > 2. [ ] Nur, wenn man Funktionen definiert > 3. [ ] Um den Codeablauf oder komplexe Checks zu erklären > 4. [ ] Kommentare sind in Python nicht nötig > [!SUCCESS]- > **3 -> Um den Codeablauf oder komplexe Checks zu erklären** > Kommentare erklären „warum“ wir etwas tun oder klären unklare Stellen. ## 3 > [!QUESTION] Docstrings in Python stehen typischerweise … > 1. [ ] direkt unter einer Funktionsdefinition (bzw. am Modulanfang) > 2. [ ] ganz am Ende der Datei > 3. [ ] in einer separaten .txt-Datei > 4. [ ] gar nicht, Python kennt keine Docstrings > [!SUCCESS]- > **1 -> direkt unter einer Funktionsdefinition (bzw. am Modulanfang)** > Der Docstring ist ein mehrzeiliger String, gleich nach def func_name(...):. ## 4 > [!QUESTION] Ein Docstring unterscheidet sich von einem normalen mehrzeiligen Kommentar dadurch, dass … > 1. [ ] er in HTML geschrieben sein muss > 2. [ ] er immer dreifache Anführungszeichen """ ... """ nutzt und vom Interpreter als Dokumentation erfasst wird > 3. [ ] man ihn anstelle von # mit ++ beginnt > 4. [ ] er nur in Python 2 gültig ist > [!SUCCESS]- > **2 -> er immer dreifache Anführungszeichen """ ... """ nutzt und vom Interpreter als Dokumentation erfasst wird** > Mehrzeilige Kommentare können ebenfalls """ ... """ sein, aber ein echter Docstring steht direkt nach def oder am Modulanfang, wird so vom Interpreter erfasst und ist z. B. per help(...) abrufbar. ## 5 > [!QUESTION] Du schreibst eine Funktion parse_sales(value) und fügst einen Docstring hinzu. Welche Info gehört typischerweise in diesen Docstring? > 1. [ ] Nur das Erscheinungsdatum des Skripts > 2. [ ] Beschreibung der Parameter, Rückgabewert und Zweck der Funktion > 3. [ ] Deine privaten Notizen zu Urlaubsplänen > 4. [ ] Keine Info, Docstrings sind leer > [!SUCCESS]- > **2 -> Beschreibung der Parameter, Rückgabewert und Zweck der Funktion** > Ein guter Docstring nennt kurz den Zweck, beschreibt Parameter und den Return-Wert. ## 6 > [!QUESTION] Was bedeutet folgender Funktionskopf in Python? > ```python > def get_price(price_str: str = "", default: float = 0.0) -> float: > ``` > > 1. [ ] price_str muss immer eine Zahl enthalten > 2. [ ] Die Funktion get_price akzeptiert zwei Parameter (mit Defaultwerten) und soll einen float zurückgeben > 3. [ ] price_str darf nur Strings mit Kommazahlen enthalten > 4. [ ] Man kann keine Kommentare in dieser Funktion verwenden > [!SUCCESS]- > **2 -> Die Funktion get_price akzeptiert zwei Parameter (mit Defaultwerten) und soll einen float zurückgeben** > price_str: str = "" heißt: Parameter ist ein String, Default ist "". default: float = 0.0 ebenso. -> float ist der angedachte Rückgabetyp. ## 7 > [!QUESTION] Du schreibst eine Funktion filter_line(parts, category="Schmerzmittel", min_sales=10) -> bool. Was kommt dabei zurück, wenn len(parts) < 9 ist? > 1. [ ] True, weil die Zeile automatisch passt > 2. [ ] Ein Docstring > 3. [ ] 0, weil man immer ein int zurückgibt > 4. [ ] False, weil die Zeile unvollständig ist > [!SUCCESS]- > **4 -> False, weil die Zeile unvollständig ist** > Im Beispielcode wird bei unvollständiger Zeile return False verwendet. ## 8 > [!QUESTION] Welche Python-Funktion kann Docstrings zur Laufzeit ausgeben, sodass Du Dir die Dokumentation einer Funktion anschauen kannst? > 1. [ ] print(docstring()) > 2. [ ] help(...) > 3. [ ] doc() > 4. [ ] comment_viewer(...) > [!SUCCESS]- > **2 -> help(...)** > help(meine_funktion) zeigt den Docstring (sofern vorhanden) im Terminal an. ## 9 > [!QUESTION] „Ein Kommentar in Python wird durch # eingeleitet und erstreckt sich bis zum Zeilenende.“ > - [ ] Richtig > - [ ] Falsch > [!SUCCESS]- > **Richtig** > Nach # wird alles in der gleichen Zeile als Kommentar behandelt. ## 10 > [!QUESTION] „Ein mehrzeiliger String mit """ ... """, der direkt unter einer def-Zeile steht, wird als sogenannter Docstring interpretiert.“ > - [ ] Richtig > - [ ] Falsch > [!SUCCESS]- > **Richtig** > Genau das ist der Standardort für Docstrings in Python. ## 11 > [!QUESTION] „In einer Funktion mit Docstring sollten weder Parameter noch Rückgabewert beschrieben werden, da das aus dem Code ersichtlich ist.“ > - [ ] Richtig > - [ ] Falsch > [!SUCCESS]- > **Falsch** > Ein Docstring sollte mindestens Zweck, Parameter und Rückgabewert erläutern – das erhöht die Verständlichkeit. ## 12 > [!QUESTION] „Funktionen wie def my_func(param: str) -> int: in Python verwenden Typannotationen, die nur informativ sind – der Interpreter erzwingt sie nicht.“ > - [ ] Richtig > - [ ] Falsch > [!SUCCESS]- > **Richtig** > Python selbst erzwingt diese Typen nicht. Sie dienen jedoch IDEs, Typecheckern und Menschen zur Orientierung. ## 13 > [!QUESTION] „Eine Funktion kann mehrere Werte zurückgeben, indem man einfach mehrere Werte in einem return angibt, z. B. return True, 10.5. In Python kommt dies als Tuple zurück.“ > - [ ] Richtig > - [ ] Falsch > [!SUCCESS]- > **Richtig** > In Python kann man return a, b angeben, was ein Tuple (a, b) liefert. ## 14 > [!QUESTION] „Kommentare sind nur für andere Programmierer da und sollten deshalb so ausführlich wie möglich alle Selbstverständlichkeiten des Codes erklären.“ > - [ ] Richtig > - [ ] Falsch > [!SUCCESS]- > **Falsch** > Kommentare erklären v. a. das Warum, nicht triviale Details. Zu viele offensichtliche Kommentare erschweren die Lesbarkeit. Ein gesundes Maß ist wichtig.