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!