Python Docstring Guide

Python ist bekannt für seine Lesbarkeit und Eleganz – und ein wesentlicher Bestandteil davon sind Docstrings. Diese unscheinbaren Textblöcke sind viel mehr als einfache Kommentare: Sie sind das Rückgrat professioneller Python-Dokumentation und ein unverzichtbares Werkzeug für jeden Entwickler.

🎯 Was sind Docstrings wirklich?

Docstrings (kurz für „Documentation Strings“) sind spezielle Strings in Python, die direkt nach Funktions-, Klassen- oder Moduldefinitionen stehen. Im Gegensatz zu regulären Kommentaren (# Dies ist ein Kommentar) werden Docstrings zur Laufzeit als Teil des Objekts gespeichert und können programmatisch abgerufen werden.

Warum Docstrings wichtig sind

  • Selbstdokumentierender Code: Jede Funktion erklärt sich selbst
  • IDE-Integration: Intelligente Code-Vervollständigung und Tooltips
  • Automatische Dokumentation: Tools wie Sphinx nutzen Docstrings
  • Team-Kollaboration: Klare Kommunikation zwischen Entwicklern
  • Python-Standard: Teil der offiziellen PEP-Richtlinien

🚀 Die Basics: So funktionieren Docstrings

Einfache Docstrings

Der einfachste Docstring besteht aus einem einzigen String:

def begruesse(name):
    """Begrüßt eine Person mit ihrem Namen."""
    return f"Hallo, {name}!"

# Abruf des Docstrings
print(begruesse.__doc__)  # Ausgabe: Begrüßt eine Person mit ihrem Namen.
help(begruesse)          # Zeigt formatierte Hilfe an

Mehrzeilige Docstrings

Für komplexere Funktionen verwenden wir mehrzeilige Docstrings:

def berechne_zinsen(kapital, zinssatz, jahre):
    """
    Berechnet die Zinsen für ein Kapital über mehrere Jahre.

    Diese Funktion verwendet die Zinseszinsformel zur Berechnung
    des Endkapitals.

    Args:
        kapital (float): Anfangskapital in Euro
        zinssatz (float): Jahreszinssatz in Prozent (z.B. 3.5 für 3,5%)
        jahre (int): Anzahl der Jahre

    Returns:
        float: Endkapital nach Verzinsung

    Example:
        >>> berechne_zinsen(1000, 5, 2)
        1102.5
    """
    return kapital * (1 + zinssatz/100) ** jahre

📚 Wo Docstrings verwendet werden

1. Module

"""
finanzen.py - Finanzberechnungsmodul

Dieses Modul bietet Funktionen zur Berechnung von Zinsen,
Kreditraten und Investitionserträgen.

Author: Max Mustermann
Version: 1.2.0
"""

2. Klassen

class Bankkonto:
    """
    Repräsentiert ein Bankkonto mit grundlegenden Funktionen.

    Attributes:
        inhaber (str): Name des Kontoinhabers
        kontostand (float): Aktueller Kontostand in Euro
        kontonummer (str): Eindeutige Kontonummer
    """

    def __init__(self, inhaber, kontonummer, startguthaben=0):
        """
        Initialisiert ein neues Bankkonto.

        Args:
            inhaber (str): Name des Kontoinhabers
            kontonummer (str): Kontonummer
            startguthaben (float, optional): Startguthaben. Defaults to 0.
        """
        self.inhaber = inhaber
        self.kontonummer = kontonummer
        self.kontostand = startguthaben

3. Funktionen und Methoden

def ueberweise(self, ziel_konto, betrag):
    """
    Überweist Geld von diesem Konto auf ein anderes Konto.

    Args:
        ziel_konto (Bankkonto): Zielkonto der Überweisung
        betrag (float): Zu überweisender Betrag

    Returns:
        bool: True bei Erfolg, False bei Misserfolg

    Raises:
        ValueError: Wenn der Betrag negativ ist
        RuntimeError: Wenn das Konto nicht gedeckt ist
    """
    if betrag <= 0:
        raise ValueError("Betrag muss positiv sein")
    if self.kontostand < betrag:
        raise RuntimeError("Nicht genügend Deckung")

    self.kontostand -= betrag
    ziel_konto.kontostand += betrag
    return True

📝 Beliebte Docstring-Formate

Google Style (empfohlen für neue Projekte)

def komplexe_berechnung(daten, parameter_a=1.0, parameter_b=None):
    """Führt eine komplexe Berechnung mit den gegebenen Daten durch.

    Dies ist eine detaillierte Beschreibung der Funktion. Hier können
    zusätzliche Informationen über den Algorithmus oder besondere
    Anwendungsfälle stehen.

    Args:
        daten (list of dict): Eingabedaten für die Berechnung
        parameter_a (float, optional): Skalierungsfaktor. Defaults to 1.0.
        parameter_b (dict or None, optional): Optionale Parameter. 
            Defaults to None.

    Returns:
        dict: Ergebnisse der Berechnung mit folgenden Keys:
            - 'ergebnis' (float): Das Hauptergebnis
            - 'status' (str): Statusmeldung
            - 'details' (list): Detaillierte Zwischenergebnisse

    Raises:
        TypeError: Wenn daten kein list-Objekt ist
        KeyError: Wenn erforderliche Keys in den Daten fehlen
        ValueError: Bei ungültigen Parameterwerten

    Example:
        >>> daten = [{'wert': 10}, {'wert': 20}]
        >>> result = komplexe_berechnung(daten)
        >>> print(result['ergebnis'])
        30.0
    """
    pass

NumPy/SciPy Style (für wissenschaftliche Projekte)

def statistik_berechnen(werte):
    """
    Berechnet grundlegende Statistiken für eine Werteliste.

    Parameters
    ----------
    werte : array_like
        Liste oder Array von numerischen Werten

    Returns
    -------
    dict
        Dictionary mit statistischen Kennzahlen:
        - mean : float
            Arithmetisches Mittel
        - std : float  
            Standardabweichung
        - median : float
            Median der Werte

    Examples
    --------
    >>> statistik_berechnen([1, 2, 3, 4, 5])
    {'mean': 3.0, 'std': 1.58, 'median': 3.0}
    """
    pass

reStructuredText/Sphinx Style

def daten_verarbeiten(eingabe):
    """
    Verarbeitet Rohdaten und gibt strukturierte Ergebnisse zurück.

    :param eingabe: Rohe Eingabedaten im JSON-Format
    :type eingabe: str or dict
    :returns: Verarbeitete Daten als Dictionary
    :rtype: dict
    :raises ValueError: Bei ungültigem JSON-Format
    :raises TypeError: Bei falschem Datentyp

    .. note::
       Diese Funktion benötigt Internetzugang für externe APIs.

    .. warning::
       Sensible Daten sollten vor der Übergabe verschlüsselt werden.
    """
    pass

⚙️ Praktische Anwendung: Automatische Dokumentation

Einer der größten Vorteile von Docstrings ist ihre Integration mit Dokumentationswerkzeugen:

Beispiel mit Sphinx

# In conf.py
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.napoleon',  # Für Google/NumPy-Stil
]

# In index.rst
.. automodule:: mein_modul
   :members:
   :undoc-members:
   :show-inheritance:

Das erzeugt automatisch professionelle HTML-Dokumentation aus deinen Docstrings!

❌ Typische Fehler und wie man sie vermeidet

Fehler 1: Docstrings vergessen

❌ Schlecht:

def berechne_summe(a, b):
    return a + b

✅ Gut:

def berechne_summe(a, b):
    """Berechnet die Summe zweier Zahlen.

    Args:
        a (int or float): Erster Summand
        b (int or float): Zweiter Summand

    Returns:
        int or float: Summe von a und b
    """
    return a + b

Fehler 2: Uninformative Docstrings

❌ Schlecht:

def process_data(data):
    """Verarbeitet Daten."""
    # ...

✅ Gut:

def process_data(data):
    """Verarbeitet Rohdaten und bereinigt ungültige Einträge.

    Entfernt Duplikate, wandelt Datentypen um und validiert
    die Struktur der Eingabedaten.

    Args:
        data (list of dict): Liste von Datensätzen mit 'id' und 'value'

    Returns:
        list of dict: Bereinigte und validierte Datensätze

    Raises:
        ValueError: Bei fehlenden Pflichtfeldern
        TypeError: Bei falschem Datentyp
    """
    # ...

Fehler 3: Veraltete Docstrings

❌ Problematisch:

def update_user(user_id, name, email):
    """Aktualisiert Benutzerdaten.

    Args:
        user_id (int): Benutzer-ID
        name (str): Neuer Name
        email (str): Neue Email
        age (int): Neues Alter  # ← NICHT MEHR VERWENDET!
    """
    # age-Parameter wurde entfernt, aber Docstring nicht aktualisiert
    pass

✅ Korrigiert:

def update_user(user_id, name, email):
    """Aktualisiert Benutzerdaten.

    Args:
        user_id (int): Benutzer-ID
        name (str): Neuer Name
        email (str): Neue Email

    Returns:
        bool: True bei Erfolg, False bei Fehler
    """
    # ...

Fehler 4: Inkonsistente Typbeschreibungen

❌ Schlecht:

def calculate_average(numbers):
    """Berechnet den Durchschnitt.

    Args:
        numbers: Liste mit Zahlen

    Returns:
        float: Der Durchschnitt
    """
    # ...

✅ Gut:

def calculate_average(numbers):
    """Berechnet den arithmetischen Durchschnitt einer Zahlenliste.

    Args:
        numbers (list of int or float): Nicht-leere Liste von Zahlen

    Returns:
        float: Arithmetischer Durchschnitt der Zahlen

    Raises:
        ValueError: Wenn die Liste leer ist
        TypeError: Wenn Elemente keine Zahlen sind
    """
    # ...

Fehler 5: Docstrings an falscher Stelle

❌ Falsch:

def my_function():
    """
    Dies ist ein Docstring.
    Aber er steht an der falschen Stelle!
    """
    """Dies hier ist KEIN Docstring!"""
    pass

✅ Richtig:

def my_function():
    """Dies ist der korrekte Docstring."""

    # Jetzt kommen die Implementierungsdetails
    pass

🛡️ Best Practices für professionelle Docstrings

1. Sei prägnant aber vollständig

# ❌ Zu kurz
def add(a, b): """Addiert zwei Zahlen."""

# ❌ Zu lang
def add(a, b): """Diese Funktion nimmt zwei numerische Parameter entgegen und führt eine Addition durch, wobei das Ergebnis der Addition dieser beiden Parameter zurückgegeben wird."""

# ✅ Goldener Mittelweg
def add(a, b):
    """Addiert zwei Zahlen und gibt das Ergebnis zurück.

    Args:
        a (int or float): Erster Summand
        Bruce: Zweiter Summand

    Returns:
        int or float: Summe von a und b
    """

2. Verwende konsistentes Format

Wähle ein Format und bleibe dabei:

# Projektweit konsistent bleiben
def funktion_1():
    """Google-Style in ganzem Projekt."""
    pass

def funktion_2():
    """Auch Google-Style."""  # Nicht mischen!
    pass

3. Aktualisiere Docstrings regelmäßig

Behandle Docstrings wie produktiven Code:

# In deinem CI/CD-Prozess:
# flake8 --docstring-style=google .
# pydocstyle .

4. Nutze Tools zur Qualitätssicherung

pydocstyle

pip install pydocstyle
pydocstyle mein_projekt/

doctest

def quadrat(x):
    """Berechnet das Quadrat einer Zahl.

    Example:
        >>> quadrat(3)
        9
        >>> quadrat(-2)
        4
    """
    return x * x

# Testen:
# python -m doctest datei.py

🧪 Testing von Docstrings

Docstrings können sogar getestet werden:

def fibonacci(n):
    """Berechnet die n-te Fibonacci-Zahl.

    Args:
        n (int): Position in der Fibonacci-Folge (n >= 0)

    Returns:
        int: n-te Fibonacci-Zahl

    Examples:
        >>> fibonacci(0)
        0
        >>> fibonacci(1)
        1
        >>> fibonacci(10)
        55
        >>> fibonacci(-1)
        Traceback (most recent call last):
        ...
        ValueError: n muss nicht-negativ sein
    """
    if n < 0:
        raise ValueError("n muss nicht-negativ sein")
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# Test mit: python -m doctest fibonacci.py -v

🎯 Fortgeschrittene Tipps

1. Property-Docstrings

class Temperatur:
    def __init__(self, celsius):
        self._celsius = celsius

    @property
    def celsius(self):
        """float: Temperatur in Grad Celsius."""
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        """Setzt die Temperatur in Celsius.

        Args:
            value (float): Temperatur in Celsius

        Raises:
            ValueError: Bei unmöglichen Temperaturen
        """
        if value < -273.15:
            raise ValueError("Temperatur unter absolutem Nullpunkt")
        self._celsius = value

2. Abstract Base Classes

from abc import ABC, abstractmethod

class DatenProzessor(ABC):
    """Abstrakte Basisklasse für Datenprozessoren."""

    @abstractmethod
    def verarbeite(self, daten):
        """Verarbeitet die übergebenen Daten.

        Muss von Unterklassen implementiert werden.

        Args:
            daten (any): Zu verarbeitende Rohdaten

        Returns:
            any: Verarbeitete Daten
        """
        pass

📊 Zusammenfassung: Die Docstring-Checkliste

Bevor du deinen Code committest, prüfe diese Punkte:

  • [ ] Hat jede öffentliche Funktion/Klasse einen Docstring?
  • [ ] Ist der Docstring prägnant und informativ?
  • [ ] Sind alle Parameter dokumentiert?
  • [ ] Sind Rückgabewerte beschrieben?
  • [ ] Werden Exceptions erwähnt?
  • [ ] Enthält der Docstring Beispiele?
  • [ ] Ist das Format konsistent im ganzen Projekt?
  • [ ] Ist der Docstring aktuell und korrekt?

🚀 Fazit

Docstrings sind weit mehr als einfache Kommentare – sie sind das Fundament guter Softwareentwicklung. Sie verbessern nicht nur die Lesbarkeit deines Codes, sondern ermöglichen auch professionelle Dokumentation, bessere Teamarbeit und höhere Codequalität.

Investiere Zeit in gute Docstrings – dein zukünftiges Ich (und deine Kollegen) werden es dir danken!

💡 Pro-Tipp: Verwende Tools wie pydocstyle, doctest und Sphinx, um deine Python Dateien automatisch zu testen und zu dokumentieren. Eine gut dokumentierte Codebasis ist eine wartbare Codebasis!