14,99 €
Schreiben Sie Python-Code, der nicht nur funktioniert, sondern begeistert!
Funktionierender Code ist nicht automatisch guter Code. Dieses Buch zeigt Ihnen, wie Sie Python-Code schreiben, der lesbar, wartbar und nachhaltig ist – Code, der auch nach Monaten noch verständlich bleibt und den Ihre Kollegen gerne weiterentwickeln.
Was macht dieses Buch besonders?
"Lesbarer Python-Code" ist Ihr praxisorientierter Leitfaden für sauberes Programmieren in Python. Autor Lukas Neumann vereint jahrelange Erfahrung als Python-Entwickler und Trainer in einem systematischen Ansatz, der weit über Syntax-Regeln hinausgeht.
Das werden Sie lernen:
Pythonic Thinking: Verstehen Sie die Philosophie hinter Python und nutzen Sie die Sprache so, wie sie gedacht ist
Aussagekräftige Namen: Wählen Sie Namen, die Ihre Absichten klar kommunizieren und Code selbsterklärend machen
Funktionsdesign: Schreiben Sie kleine, fokussierte Funktionen nach dem Single-Responsibility-Prinzip
Python-Features meistern: Setzen Sie Decorators, Context Manager und Comprehensions elegant ein
Robuste Fehlerbehandlung: Implementieren Sie Fehlerbehandlung ohne die Lesbarkeit zu opfern
Datenstrukturen: Wählen Sie die richtige Datenstruktur für sauberen, effizienten Code
Klassen & OOP: Gestalten Sie objektorientierte Architekturen, die wartbar und erweiterbar sind
Refactoring: Verbessern Sie bestehenden Code systematisch und sicher
Tests als Dokumentation: Schreiben Sie Tests, die gleichzeitig als lebende Dokumentation dienen
Für wen ist dieses Buch?
Ob Python-Einsteiger, der von Anfang an gute Gewohnheiten entwickeln möchte, oder erfahrener Entwickler, der seine Code-Qualität auf das nächste Level heben will – dieses Buch bietet praxiserprobte Techniken für jeden Erfahrungsgrad.
Praxisnah und sofort anwendbar:
Alle Beispiele stammen aus der realen Python-Entwicklung. Sie sehen konkrete Vorher-Nachher-Vergleiche und können die Verbesserungen sofort in Ihren eigenen Projekten umsetzen. Die umfangreichen Anhänge mit Checklisten, Cheat Sheets und Codebeispielen dienen als praktische Nachschlagewerke für Ihren Entwickleralltag.
Ihr Mehrwert:
✓ Weniger technische Schulden und Wartungsaufwand ✓ Bessere Zusammenarbeit im Team durch lesbaren Code ✓ Schnelleres Onboarding neuer Teammitglieder ✓ Reduktion von Bugs durch klare Strukturen ✓ Mehr Freude beim Programmieren und Code-Reviews
Umfassende Themenabdeckung:
Von den Grundlagen sauberen Codes über Namensgebung, Funktionsdesign und Kontrollstrukturen bis hin zu fortgeschrittenen Themen wie Modularität, Abhängigkeitsmanagement und systematischem Refactoring – dieses Buch begleitet Sie auf dem Weg von "es läuft" zu "es ist exzellent".
Extras, die den Unterschied machen:
Clean-Code-Checkliste für Python zum schnellen Nachschlagen
Umfangreiche Vorher/Nachher-Codebeispiele
Strategien für dauerhafte Code-Qualität
Empfehlungen für Tools und Weiterbildung
Mini-Cheat-Sheet für den täglichen Gebrauch
Sauberer Code ist keine Destination, sondern eine Reise der kontinuierlichen Verbesserung. Jede Zeile, die Sie bewusster schreiben, macht Ihren Code besser – für Sie selbst und für alle, die damit arbeiten werden.
Beginnen Sie heute Ihre Reise zu besserem Python-Code!
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Veröffentlichungsjahr: 2025
Python hat sich in den letzten Jahren zu einer der beliebtesten Programmiersprachen der Welt entwickelt. Seine elegante Syntax und Lesbarkeit machen es zu einem idealen Werkzeug für Entwickler aller Erfahrungsstufen. Doch gerade diese scheinbare Einfachheit kann trügerisch sein: Funktionierender Code ist nicht automatisch guter Code.
Dieses Buch entstand aus der Überzeugung heraus, dass Python-Entwickler nicht nur lernen sollten, wie sie Code zum Laufen bringen, sondern wie sie Code schreiben, der lesbar, wartbar und nachhaltig ist. In meiner langjährigen Erfahrung als Python-Entwickler und -Trainer habe ich immer wieder gesehen, wie brillante Ideen in unlesbarem Code verschwinden und wie technische Schulden Projekte zum Stillstand bringen können.
Lesbarer Python-Code ist Ihr praktischer Leitfaden für die Kunst des sauberen Programmierens in Python. Dieses Buch geht weit über Syntax-Regeln hinaus und zeigt Ihnen, wie Sie pythonischen Code schreiben, der nicht nur funktioniert, sondern auch ein Vergnügen zu lesen und zu warten ist.
Sie werden lernen, wie Sie:
Ob Sie Python-Einsteiger sind, der von Anfang an gute Gewohnheiten entwickeln möchte, oder erfahrener Entwickler, der seine Code-Qualität auf das nächste Level heben will – dieses Buch bietet Ihnen praxiserprobte Techniken und Strategien. Die Beispiele sind bewusst aus der realen Python-Entwicklung gewählt und zeigen konkrete Verbesserungen, die Sie sofort in Ihren Projekten umsetzen können.
Das Buch folgt einem systematischen Ansatz: Wir beginnen mit den Grundlagen sauberen Codes und dem pythonischen Denkansatz, vertiefen uns dann in spezifische Aspekte wie Namensgebung, Funktionsdesign und Datenstrukturen. Die mittleren Kapitel behandeln fortgeschrittene Themen wie Modularität und Refactoring, während die abschließenden Kapitel häufige Fallstricke aufzeigen und den Weg von funktionierendem zu exzellentem Python-Code ebnen.
Die umfangreichen Anhänge bieten Ihnen praktische Werkzeuge: Checklisten für den Alltag, Vorher-Nachher-Beispiele zum Nachvollziehen und Empfehlungen für die kontinuierliche Weiterentwicklung Ihrer Python-Fertigkeiten.
Mein besonderer Dank gilt der Python-Community, deren Prinzipien und Best Practices die Grundlage dieses Buches bilden. Die Arbeiten von Guido van Rossum, Robert C. Martin und vielen anderen haben mein Verständnis von sauberem Code geprägt. Ebenso danke ich den unzähligen Python-Entwicklern, deren Fragen, Diskussionen und Code-Reviews zu den Erkenntnissen in diesem Buch beigetragen haben.
Sauberer Code ist keine Destination, sondern eine Reise der kontinuierlichen Verbesserung. Jede Zeile, die Sie bewusster schreiben, jede Funktion, die Sie durchdachter strukturieren, und jeder Test, den Sie sorgfältiger gestalten, bringt Sie diesem Ziel näher.
Lassen Sie uns gemeinsam diese Reise antreten und Python-Code schreiben, der nicht nur heute funktioniert, sondern auch morgen noch Freude bereitet – Ihnen und allen, die nach Ihnen damit arbeiten werden.
Viel Erfolg und Freude beim sauberen Programmieren!
Lukas Neumann
Kapitel
Titel
Seite
Einl
Einleitung
7
1
Was ist sauberer Code?
18
2
Pythonic Thinking
34
3
Namen, die Code erklären
55
4
Kleine Funktionen, klare Aufgaben
73
5
Vermeidung von Duplikaten
95
6
Kontrollstrukturen sauber schreiben
121
7
Richtige Datenstruktur, sauberer Code
141
8
Fehlerbehandlung ohne Chaos
178
9
Kommentare, Docstrings & Dokumentation
201
10
Saubere Funktionen in Python
218
11
Clean Code in Klassen
240
12
Abhängigkeiten & Modularität
262
13
Code-Stil & Formatierung
292
14
Tests als Teil von Clean Code
310
15
Refactoring Schritt für Schritt
347
16
Häufige Clean-Code-Fehler in Python
371
17
Von „es läuft“ zu „es ist gut“
396
Anh
Clean Code als Gewohnheit
423
Anh
Weiterführende Empfehlungen
438
Anh
Wie man dauerhaft sauber programmiert
454
Anh
Clean-Code-Checkliste für Python
478
Anh
Vorher/Nachher-Codebeispiele
505
Anh
Mini-Cheat-Sheet
533
In der Welt der Softwareentwicklung gibt es einen fundamentalen Unterschied zwischen Code, der funktioniert, und Code, der verstanden wird. Python, eine der beliebtesten Programmiersprachen unserer Zeit, bietet uns die einzigartige Möglichkeit, nicht nur funktionalen, sondern auch eleganten und lesbaren Code zu schreiben. Diese Einleitung führt Sie in die Philosophie und Praxis des lesbaren Python-Codes ein – eine Reise, die Ihr Verständnis von Programmierung grundlegend verändern wird.
Python wurde von Guido van Rossum mit einer klaren Vision entwickelt: Code sollte so lesbar wie möglich sein. Der berühmte Ausspruch "Code wird häufiger gelesen als geschrieben" trifft auf keine andere Sprache so sehr zu wie auf Python. Während andere Programmiersprachen oft kryptische Syntax und komplexe Konstrukte verwenden, ermutigt Python zu einem Stil, der natürlicher Sprache ähnelt.
Die Bedeutung lesbaren Codes zeigt sich besonders deutlich in der täglichen Entwicklungsarbeit. Ein Entwickler verbringt durchschnittlich 70% seiner Zeit damit, bestehenden Code zu lesen und zu verstehen, bevor er neue Funktionen hinzufügt oder Fehler behebt. In Python-Projekten kann diese Zeit durch gut geschriebenen, lesbaren Code erheblich reduziert werden.
# Schlecht lesbarer Code
defcalc(x, y, z):
return x * y + z if z >0else x * y
# Gut lesbarer Code
defcalculate_total_with_bonus(base_price, quantity, bonus_amount):
"""Berechnet den Gesamtpreis mit optionalem Bonus."""
if bonus_amount >0:
return base_total + bonus_amount
return base_total
Der Unterschied zwischen diesen beiden Funktionen liegt nicht in ihrer Funktionalität, sondern in ihrer Klarheit und Verständlichkeit. Der zweite Ansatz macht die Absicht des Codes sofort ersichtlich und erleichtert die Wartung und Weiterentwicklung erheblich.
Um die Bedeutung lesbaren Codes in Python vollständig zu verstehen, müssen wir uns mit dem "Zen of Python" beschäftigen – einer Sammlung von Prinzipien, die die Designphilosophie von Python verkörpern. Diese Prinzipien können Sie jederzeit in der Python-Konsole aufrufen:
python -c "import this"
Die wichtigsten Prinzipien für lesbaren Code sind:
Prinzip
Bedeutung für lesbaren Code
Python-Beispiel
"Beautiful is better than ugly"
Code sollte ästhetisch ansprechend sein
Verwendung von aussagekräftigen Variablennamen
"Explicit is better than implicit"
Klarheit vor Kürze
Explizite Typhinweise und Dokumentation
"Simple is better than complex"
Einfache Lösungen bevorzugen
Vermeidung unnötiger Verschachtelungen
"Readability counts"
Lesbarkeit ist wichtig
Konsistente Formatierung und Struktur
"There should be one obvious way to do it"
Eindeutige Lösungswege
Verwendung idiomatischer Python-Patterns
Unlesbarer Code verursacht erhebliche Kosten in Softwareprojekten. Diese Kosten manifestieren sich in verschiedenen Bereichen:
Entwicklungszeit und Produktivität
Wenn Entwickler Zeit damit verbringen müssen, kryptischen Code zu entschlüsseln, verlangsamt sich der gesamte Entwicklungsprozess. In Python-Projekten kann schlechte Lesbarkeit besonders frustrierend sein, da die Sprache eigentlich für ihre Klarheit bekannt ist.
# Unlesbarer Code mit versteckter Logik
defprocess_data(data):
for item in data:
ifisinstance(item, (int, float)) and item >0:
result.append(item **2if item <10else item *2)
return result
# Lesbarer Code mit klarer Struktur
defprocess_positive_numbers(numbers):
"""
Verarbeitet positive Zahlen nach spezifischen Regeln.
Kleine Zahlen (< 10) werden quadriert,
größere Zahlen werden verdoppelt.
"""
for number in numbers:
ifnotisinstance(number, (int, float)):
continue
if number <=0:
continue
if number <10:
else:
processed_numbers.append(processed_number)
return processed_numbers
Fehlerrate und Debugging
Unlesbarer Code führt zu mehr Fehlern und macht das Debugging zu einem zeitaufwändigen Prozess. Python bietet viele Werkzeuge für bessere Lesbarkeit, die gleichzeitig die Fehlerrate reduzieren.
Teamarbeit und Wissenstransfer
In Entwicklungsteams ist lesbarer Code essentiell für effektive Zusammenarbeit. Python-Code sollte so geschrieben werden, dass neue Teammitglieder schnell produktiv werden können.
Namen sind das wichtigste Kommunikationsmittel in Code. Python ermutigt zu längeren, beschreibenden Namen:
# Schlecht
defcalc_tax(p, r):
return p * r
# Gut
defcalculate_sales_tax(price, tax_rate):
return price * tax_rate
Python-Funktionen sollten eine einzige, klar definierte Aufgabe erfüllen:
# Schlecht - zu viele Verantwortlichkeiten
defprocess_user_data(user_data):
# Validierung
ifnot user_data.get('email'):
raiseValueError("Email required")
# Formatierung
# Speicherung
save_to_database(user_data)
# Benachrichtigung
send_welcome_email(user_data['email'])
# Gut - getrennte Verantwortlichkeiten
defvalidate_user_data(user_data):
"""Validiert Benutzerdaten."""
ifnot user_data.get('email'):
raiseValueError("Email required")
defformat_user_data(user_data):
"""Formatiert Benutzerdaten."""
return formatted_data
defcreate_new_user(user_data):
"""Erstellt einen neuen Benutzer."""
validate_user_data(user_data)
save_to_database(formatted_data)
send_welcome_email(formatted_data['email'])
Python's PEP 8 Style Guide bietet klare Richtlinien für Formatierung:
# Installation des Code-Formatters
pip install black flake8 isort
# Automatische Formatierung
black my_script.py
# Stil-Überprüfung
flake8 my_script.py
# Import-Sortierung
isort my_script.py
Python bietet verschiedene Möglichkeiten für Dokumentation:
defcalculate_compound_interest(principal, rate, time, compound_frequency1):
"""
Berechnet Zinseszins für eine gegebene Investition.
Args:
principal (float): Anfangskapital
rate (float): Jährlicher Zinssatz (als Dezimalzahl)
time (float): Anlagezeitraum in Jahren
compound_frequency (int): Häufigkeit der Zinskapitalisierung pro Jahr
Returns:
float: Endkapital nach der angegebenen Zeit
Example:
>>> calculate_compound_interest(1000, 0.05, 2, 4)
1104.89
"""
return principal * (1+ rate / compound_frequency) ** (compound_frequency * time)
Python bietet ein reichhaltiges Ökosystem von Werkzeugen zur Verbesserung der Code-Qualität:
Werkzeug
Zweck
Installation
Verwendung
Black
Code-Formatierung
pip install black
black script.py
Flake8
Stil-Überprüfung
pip install flake8
flake8 script.py
Pylint
Umfassende Code-Analyse
pip install pylint
pylint script.py
mypy
Typ-Überprüfung
pip install mypy
mypy script.py
isort
Import-Sortierung
pip install isort
isort script.py
Betrachten wir ein praktisches Beispiel, das die Prinzipien lesbaren Python-Codes demonstriert:
"""
Bibliotheksverwaltungssystem - Demonstration lesbaren Python-Codes
"""
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import List, Optional
@dataclass
classBook:
"""Repräsentiert ein Buch in der Bibliothek."""
isbn: str
title: str
author: str
publication_year: int
is_available: boolTrue
@dataclass
classMember:
"""Repräsentiert ein Bibliotheksmitglied."""
member_id: str
name: str
email: str
def__post_init__(self):
ifself.borrowed_books isNone:
classLibraryManager:
"""Verwaltet Bücher und Mitglieder einer Bibliothek."""
def__init__(self):
self.loan_period_days 14
defadd_book(self, book: Book) -> None:
"""Fügt ein neues Buch zur Bibliothek hinzu."""
ifself._find_book_by_isbn(book.isbn):
raiseValueError(f"Buch mit ISBN {book.isbn} existiert bereits")
self.books.append(book)
defregister_member(self, member: Member) -> None:
"""Registriert ein neues Mitglied."""
ifself._find_member_by_id(member.member_id):
raiseValueError(f"Mitglied mit ID {member.member_id} existiert bereits")
self.members.append(member)
defborrow_book(self, member_id: str, isbn: str) -> bool:
"""
Verleiht ein Buch an ein Mitglied.
Returns:
bool: True wenn erfolgreich, False wenn nicht möglich
"""
member self._find_member_by_id(member_id)
book self._find_book_by_isbn(isbn)
ifnot member ornot book:
returnFalse
ifnot book.is_available:
returnFalse
iflen(member.borrowed_books) >=3: # Maximale Anzahl ausgeliehener Bücher
returnFalse
book.is_available False
member.borrowed_books.append(isbn)
returnTrue
def_find_book_by_isbn(self, isbn: str) -> Optional[Book]:
"""Findet ein Buch anhand der ISBN."""
for book inself.books:
return book
returnNone
def_find_member_by_id(self, member_id: str) -> Optional[Member]:
"""Findet ein Mitglied anhand der ID."""
for member inself.members:
return member
returnNone
Die Python-Community entwickelt kontinuierlich neue Werkzeuge und Praktiken für besseren Code. Moderne Entwicklungen wie Type Hints, Dataclasses und das Walrus-Operator (:=) zeigen, dass Python sich weiterhin in Richtung noch besserer Lesbarkeit entwickelt.
# Moderne Python-Features für bessere Lesbarkeit
from typing import TypedDict, Union
classUserData(TypedDict):
name: str
age: int
email: str
defprocess_users(users: List[UserData]) -> List[str]:
"""Verarbeitet Benutzerdaten und gibt E-Mail-Adressen zurück."""
for user in users:
# Walrus-Operator für bessere Lesbarkeit
if (email : user.get('email')) and'@'in email:
valid_emails.append(email)
return valid_emails
Lesbarer Python-Code ist nicht nur ein Luxus, sondern eine Notwendigkeit für erfolgreiche Softwareentwicklung. Die Investition in bessere Lesbarkeit zahlt sich durch reduzierte Entwicklungszeit, weniger Fehler und bessere Teamarbeit aus. Python bietet uns die Werkzeuge und die Philosophie, um Code zu schreiben, der nicht nur funktioniert, sondern auch verstanden und geschätzt wird.
In den folgenden Kapiteln werden wir tiefer in spezifische Techniken und Praktiken eintauchen, die Ihnen helfen werden, Ihren Python-Code von gut funktionierend zu außergewöhnlich lesbar zu transformieren. Jedes Kapitel baut auf den hier vorgestellten Grundprinzipien auf und zeigt praktische Wege, wie Sie sauberer programmieren, besser denken und nachhaltig entwickeln können.
Die Reise zu besserem Python-Code beginnt mit dem Verständnis, dass Code in erster Linie für Menschen geschrieben wird – für Sie selbst in sechs Monaten, für Ihre Kollegen und für zukünftige Entwickler, die Ihr Werk weiterführen werden. Python macht es uns leicht, diesen menschenzentrierten Ansatz zu verfolgen, und die folgenden Kapitel werden Ihnen zeigen, wie Sie das volle Potenzial dieser wunderbaren Sprache ausschöpfen können.
In der Welt der Softwareentwicklung gibt es eine fundamentale Wahrheit, die oft übersehen wird: Code wird häufiger gelesen als geschrieben. Diese simple Erkenntnis bildet das Fundament für das Konzept des sauberen Codes. Aber was genau bedeutet "sauberer Code"? Wie unterscheidet sich gut geschriebener Code von schlecht geschriebenem Code? Und warum sollte uns das überhaupt interessieren?
Sauberer Code ist mehr als nur funktionierender Code. Es ist Code, der nicht nur das tut, was er soll, sondern dies auf eine Art und Weise, die elegant, verständlich und wartbar ist. Es ist Code, der wie gut geschriebene Prosa fließt und seine Absichten klar kommuniziert, ohne dass der Leser zwischen den Zeilen lesen muss.
Sauberer Code zeichnet sich durch mehrere charakteristische Eigenschaften aus:
Lesbarkeit: Der Code sollte so geschrieben sein, dass ein anderer Entwickler (oder Sie selbst in sechs Monaten) ihn ohne große Anstrengung verstehen kann. Dies bedeutet aussagekräftige Variablennamen, klare Funktionsstrukturen und eine logische Organisation.
Einfachheit: Sauberer Code folgt dem Prinzip "Keep It Simple, Stupid" (KISS). Er löst Probleme auf die einfachste mögliche Art und Weise, ohne unnötige Komplexität hinzuzufügen.
Konsistenz: Einheitliche Formatierung, Namenskonventionen und Strukturen sorgen dafür, dass der Code vorhersagbar und leicht zu navigieren ist.
Testbarkeit: Sauberer Code ist so strukturiert, dass er leicht getestet werden kann. Dies führt zu zuverlässigerer Software und erleichtert Änderungen.
Robert C. Martin, auch bekannt als "Uncle Bob", definiert sauberen Code folgendermaßen: "Sauberer Code ist Code, der von jemandem geschrieben wurde, dem es wichtig war." Diese Definition trifft den Kern der Sache. Sauberer Code entsteht nicht zufällig; er ist das Ergebnis bewusster Entscheidungen und kontinuierlicher Verbesserung.
Die Bedeutung sauberen Codes wird besonders deutlich, wenn wir die wirtschaftlichen Auswirkungen betrachten. Studien zeigen, dass Entwickler 80% ihrer Zeit damit verbringen, bestehenden Code zu lesen und zu verstehen, und nur 20% damit, neuen Code zu schreiben.
Aktivität
Zeitanteil
Auswirkung bei schlechtem Code
Code lesen und verstehen
80%
Drastisch verlängerte Entwicklungszeiten
Neuen Code schreiben
20%
Höhere Fehlerrate durch Missverständnisse
Debugging
Variable
Exponentiell schwieriger bei unleserlichem Code
Code-Reviews
15-25%
Ineffektiv bei schlecht strukturiertem Code
Schlechter Code führt zu dem, was als "technische Schuld" bezeichnet wird. Wie finanzielle Schulden akkumuliert auch technische Schuld Zinsen. Je länger schlechter Code im System verbleibt, desto schwieriger und kostspieliger wird es, ihn zu ändern oder zu erweitern.
In einem Team ist sauberer Code noch wichtiger. Wenn jeder Entwickler seinen eigenen Stil hat und keine gemeinsamen Standards befolgt, wird die Zusammenarbeit ineffizient. Sauberer Code fungiert als gemeinsame Sprache, die es Teammitgliedern ermöglicht, effektiv zusammenzuarbeiten.
Schlechter Code:
# Schlecht: Unklare Variablennamen
defcalc(x, y, z):
if z 1:
return x * y *0.1
elif z 2:
return x * y *0.05
else:
return x * y
Sauberer Code:
# Gut: Selbsterklärender Code
defcalculate_price_with_discount(base_price, quantity, customer_type):
"""
Berechnet den Gesamtpreis basierend auf Kundenkategorie.
Args:
base_price (float): Grundpreis pro Einheit
quantity (int): Anzahl der Artikel
customer_type (int): 1=Premium, 2=Standard, andere=Basis
Returns:
float: Endpreis nach Rabattabzug
"""
PREMIUM_DISCOUNT0.1
STANDARD_DISCOUNT0.05
if customer_type 1: # Premium-Kunde
return total_price * (1-PREMIUM_DISCOUNT)
elif customer_type 2: # Standard-Kunde
return total_price * (1-STANDARD_DISCOUNT)
else: # Basis-Kunde
return total_price
Schlechter Code:
# Schlecht: Monolithische Funktion
defprocess_user_data(data):
# Validierung
ifnot data ornotisinstance(data, dict):
raiseValueError("Invalid data")
# E-Mail-Validierung
if'@'notin email or'.'notin email:
raiseValueError("Invalid email")
# Passwort-Validierung
iflen(password) <8:
raiseValueError("Password too short")
# Datenbank-Speicherung
import sqlite3
cursor.execute("INSERT INTO users (email, password) VALUES (?, ?)",
(email, password))
conn.commit()
conn.close()
# E-Mail senden
import smtplib
server.starttls()
server.login('[email protected]', 'password')
server.sendmail('[email protected]', email, message)
server.quit()
returnTrue
Sauberer Code:
# Gut: Aufgeteilte Verantwortlichkeiten
import re
import sqlite3
import smtplib
from typing import Dict, Any
defvalidate_user_data(data: Dict[str, Any]) -> None:
"""Validiert die Benutzerdaten."""
ifnot data ornotisinstance(data, dict):
raiseValueError("Ungültige Datenstruktur")
_validate_email(data.get('email', ''))
_validate_password(data.get('password', ''))
def_validate_email(email: str) -> None:
"""Validiert das E-Mail-Format."""
email_pattern r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
ifnot re.match(email_pattern, email):
raiseValueError("Ungültiges E-Mail-Format")
def_validate_password(password: str) -> None:
"""Validiert die Passwort-Anforderungen."""
iflen(password) <8:
raiseValueError("Passwort muss mindestens 8 Zeichen lang sein")
defsave_user_to_database(email: str, password: str) -> None:
"""Speichert Benutzerdaten in der Datenbank."""
with sqlite3.connect('users.db') as conn:
cursor.execute(
"INSERT INTO users (email, password) VALUES (?, ?)",
(email, password)
)
conn.commit()
defsend_welcome_email(email: str) -> None:
"""Sendet eine Willkommens-E-Mail an den neuen Benutzer."""
try:
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login('[email protected]', 'password')
server.sendmail('[email protected]', email, message)
exceptExceptionas e:
print(f"Fehler beim Senden der E-Mail: {e}")
defprocess_user_data(data: Dict[str, Any]) -> bool:
"""
Verarbeitet Benutzerdaten vollständig.
Args:
data: Dictionary mit Benutzerdaten
Returns:
bool: True bei erfolgreicher Verarbeitung
"""
try:
validate_user_data(data)
save_user_to_database(email, password)
send_welcome_email(email)
returnTrue
exceptExceptionas e:
print(f"Fehler bei der Benutzerverarbeitung: {e}")
returnFalse
Auch in Bash-Skripten gelten die Prinzipien sauberen Codes. Hier ein Beispiel für ein Backup-Skript:
Schlechter Code:
#!/bin/bash
# Schlecht: Unstrukturiertes Backup-Skript
cp -r /home/user/documents /backup/
tar -czf /backup/docs_$(date +%Y%m%d).tar.gz /backup/documents
rm -rf /backup/documents
echo"done"
Sauberer Code:
#!/bin/bash
# Gut: Strukturiertes Backup-Skript mit Fehlerbehandlung
set -euo pipefail # Beendet bei Fehlern, undefinierte Variablen sind Fehler
# Konfiguration
readonly SOURCE_DIR="/home/user/documents"
readonly BACKUP_DIR="/backup"
readonly DATE_STAMP=$(date +%Y%m%d_%H%M%S)
readonly BACKUP_FILE="${BACKUP_DIR}/docs_${DATE_STAMP}.tar.gz"
readonly TEMP_DIR="${BACKUP_DIR}/temp_${DATE_STAMP}"
# Logging-Funktion
log() {
echo"[$(date '+%Y-%m-%d %H:%M:%S')] $*">&2
}
# Fehlerbehandlung
error_exit() {
log "FEHLER: $1"
cleanup
exit 1
}
# Aufräumen
cleanup() {
if [[ -d"$TEMP_DIR" ]];then
log "Räume temporäre Dateien auf..."
rm -rf "$TEMP_DIR"
fi
}
# Trap für Cleanup bei Skript-Ende
trap cleanup EXIT
# Validierung der Voraussetzungen
validate_prerequisites() {
log "Validiere Voraussetzungen..."
if [[ !-d"$SOURCE_DIR" ]];then
error_exit "Quellverzeichnis $SOURCE_DIR existiert nicht"
fi
if [[ !-d"$BACKUP_DIR" ]];then
error_exit "Backup-Verzeichnis $BACKUP_DIR existiert nicht"
fi
if!command -v tar &> /dev/null;then
error_exit "tar-Befehl nicht verfügbar"
fi
# Prüfe verfügbaren Speicherplatz
local available_space
available_space=$(df "$BACKUP_DIR" | awk 'NR==2 {print $4}')
local required_space
required_space=$(du -s "$SOURCE_DIR" | awk '{print $1}')
if(( available_space < required_space *2 ));then
error_exit "Nicht genügend Speicherplatz verfügbar"
fi
}
# Hauptfunktion für das Backup
create_backup() {
log "Starte Backup von $SOURCE_DIR..."
# Erstelle temporäres Verzeichnis
mkdir -p "$TEMP_DIR"|| error_exit "Kann temporäres Verzeichnis nicht erstellen"
# Kopiere Dateien
log "Kopiere Dateien..."
cp -r "$SOURCE_DIR""$TEMP_DIR/"|| error_exit "Fehler beim Kopieren der Dateien"
# Erstelle komprimiertes Archiv
log "Erstelle komprimiertes Archiv..."
tar -czf "$BACKUP_FILE" -C "$TEMP_DIR""$(basename "$SOURCE_DIR")"||\
error_exit "Fehler beim Erstellen des Archivs"
# Validiere das erstellte Archiv
log "Validiere Archiv..."
if! tar -tzf "$BACKUP_FILE"> /dev/null;then
error_exit "Erstelltes Archiv ist beschädigt"
fi
log "Backup erfolgreich erstellt: $BACKUP_FILE"
}
# Alte Backups bereinigen (behalte nur die letzten 7)
cleanup_old_backups() {
log "Bereinige alte Backups..."
local backup_count
backup_count=$(find "$BACKUP_DIR" -name "docs_*.tar.gz" | wc -l)
if(( backup_count >7 ));then
log "Lösche alte Backups (behalte die neuesten 7)..."
find "$BACKUP_DIR" -name "docs_*.tar.gz" -type f -printf '%T@ %p\n'|\
sort -n | head -n -7 | cut -d' ' -f2- |\
log "Lösche altes Backup: $file"
rm -f "$file"
done
fi
}
# Hauptprogramm
main() {
log "Backup-Skript gestartet"
validate_prerequisites
create_backup
cleanup_old_backups
log "Backup-Prozess erfolgreich abgeschlossen"
}
# Skript ausführen
main "$@"
Metrik
Beschreibung
Zielwert
Messung
Zyklomatische Komplexität
Anzahl linear unabhängiger Pfade
< 10 pro Funktion
Automatische Tools
Zeilen pro Funktion
Länge einzelner Funktionen
< 20-30 Zeilen
Einfache Zählung
Funktionen pro Klasse
Anzahl Methoden in einer Klasse
< 20 Methoden
Code-Analyse
Duplizierung
Prozentsatz doppelter Code-Zeilen
< 5%
DRY-Prinzip-Checker
Test-Abdeckung
Prozentsatz getesteter Code-Zeilen
> 80%
Coverage-Tools
Dokumentationsgrad
Verhältnis Kommentare zu Code
10-30%
Automatische Analyse
Python-Tools:
# Installation der wichtigsten Code-Qualitäts-Tools
pip install pylint flake8 black mypy pytest coverage
# Pylint für umfassende Code-Analyse
pylint my_module.py
# Flake8 für Style-Guide-Compliance
flake8 --max-line-length=88 my_module.py
# Black für automatische Code-Formatierung
black my_module.py
# MyPy für Typ-Checking
mypy my_module.py
# Coverage für Test-Abdeckung
coverage run -m pytest
coverage report
Bash-Script-Validierung:
# ShellCheck für Bash-Script-Analyse
sudo apt-get install shellcheck
shellcheck my_script.sh
# Bash-Syntax-Prüfung
bash -n my_script.sh
Technische Schuld ist ein Konzept, das die impliziten Kosten zusätzlicher Nacharbeitung beschreibt, die durch die Wahl einer einfachen (begrenzten) Lösung anstelle eines besseren Ansatzes entstehen, der mehr Zeit in Anspruch genommen hätte.
Arten technischer Schuld:
Bewusste Schuld
: Entwickler entscheiden sich bewusst für eine schnelle, aber suboptimale Lösung
Unbewusste Schuld
: Entsteht durch mangelndes Wissen oder schlechte Praktiken
Umweltbedingte Schuld
: Resultiert aus sich ändernden Anforderungen oder Technologien
Studien zeigen, dass Teams mit hoher technischer Schuld bis zu 50% ihrer Entwicklungszeit mit dem Umgang mit Legacy-Code verbringen, anstatt neue Features zu entwickeln.
Das DRY-Prinzip besagt, dass jedes Stück Wissen in einem System eine einzige, eindeutige, autoritative Repräsentation haben sollte.
Beispiel:
# Schlecht: Code-Duplizierung
defcalculate_circle_area(radius):
return3.14159* radius * radius
defcalculate_circle_circumference(radius):
return2*3.14159* radius
# Gut: Konstante extrahiert
import math
defcalculate_circle_area(radius):
return math.pi * radius * radius
defcalculate_circle_circumference(radius):
return2* math.pi * radius
Die SOLID-Prinzipien sind fünf Grundprinzipien der objektorientierten Programmierung:
Single Responsibility Principle (SRP)
: Eine Klasse sollte nur einen Grund haben, sich zu ändern
Open/Closed Principle (OCP)
: Software-Entitäten sollten offen für Erweiterungen, aber geschlossen für Modifikationen sein
Liskov Substitution Principle (LSP)
: Objekte einer Superklasse sollten durch Objekte einer Subklasse ersetzbar sein
Interface Segregation Principle (ISP)
: Clients sollten nicht von Interfaces abhängen, die sie nicht verwenden
Dependency Inversion Principle (DIP)
: Module hoher Ebene sollten nicht von Modulen niedrigerer Ebene abhängen
Sauberer Code ist eine Investition in die Zukunft. Er reduziert Wartungskosten, verbessert die Teamproduktivität und macht Software zuverlässiger. Die Prinzipien sauberen Codes sind nicht nur theoretische Konzepte, sondern praktische Werkzeuge, die jeden Tag angewendet werden können.
Der Weg zu sauberem Code beginnt mit dem Bewusstsein für dessen Wichtigkeit und der bewussten Entscheidung, Zeit und Aufmerksamkeit in die Qualität des geschriebenen Codes zu investieren. Wie Robert C. Martin sagte: "Der einzige Weg, schnell zu gehen, ist, gut zu gehen."
In den folgenden Kapiteln werden wir tiefer in spezifische Techniken und Praktiken eintauchen, die Ihnen dabei helfen, konsistent sauberen, wartbaren und eleganten Code zu schreiben.
Pythonisches Denken ist mehr als nur das Schreiben von funktionierendem Code – es ist eine Denkweise, die Klarheit, Eleganz und Lesbarkeit über clevere Tricks und komplexe Konstrukte stellt. Der Begriff "pythonisch" beschreibt Code, der nicht nur die Syntax von Python befolgt, sondern auch den Geist und die Philosophie der Sprache verkörpert. Tim Peters fasste diese Philosophie im berühmten "Zen of Python" zusammen, das mit dem Befehl import this in jeder Python-Installation abrufbar ist.
Die Grundprinzipien pythonischen Denkens basieren auf der Überzeugung, dass Code häufiger gelesen als geschrieben wird. Daher sollte jede Zeile Code so klar und verständlich wie möglich sein. Diese Philosophie führt zu wartbarerem, fehlerresistentem und kollaborationsfreundlichem Code.
Der Zen of Python besteht aus zwanzig Leitsätzen, die die Designphilosophie von Python verkörpern. Hier sind die wichtigsten Prinzipien mit detaillierter Erklärung:
Prinzip
Erklärung
Praktische Anwendung
"Beautiful is better than ugly"
Code sollte ästhetisch ansprechend und gut strukturiert sein
Verwende konsistente Einrückung und aussagekräftige Variablennamen
"Explicit is better than implicit"
Mache Absichten im Code deutlich sichtbar
Vermeide versteckte Seiteneffekte und undokumentierte Annahmen
"Simple is better than complex"
Bevorzuge einfache Lösungen gegenüber komplizierten
Wähle klare Algorithmen über clevere, schwer verständliche Tricks
"Readability counts"
Lesbarkeit ist ein Hauptziel
Schreibe Code, den andere (und du selbst) in sechs Monaten verstehen
"There should be one obvious way to do it"
Vermeide Multiple Lösungsansätze für dasselbe Problem
Nutze etablierte Patterns und idiomatische Konstrukte
# Nicht pythonisch: Implizit und schwer lesbar
defprocess_data(d):
return [x*2if x%20else x+1for x in d if x>0]
# Pythonisch: Explizit und lesbar
defdouble_even_increment_odd_positive_numbers(numbers):
"""
Verarbeitet eine Liste von Zahlen:
- Verdoppelt gerade positive Zahlen
- Erhöht ungerade positive Zahlen um 1
- Ignoriert negative Zahlen und Null
"""
for number in numbers:
if number <=0:
continue
if number %20:
else:
result.append(processed_number)
return result
Anmerkungen zur Code-Verbesserung:
List Comprehensions sind ein Markenzeichen pythonischen Codes, aber sie sollten mit Bedacht eingesetzt werden:
# Einfache, lesbare List Comprehension
# Komplexere Comprehension mit Bedingung
# Generator Expression für speichereffiziente Verarbeitung
sum_of_squares sum(x**2for x inrange(1000000))
# Verschachtelte Comprehensions (mit Vorsicht verwenden)
Bash-Kommandos zum Testen:
# Python-Skript erstellen und ausführen
print(squares)"> comprehension_test.py
python3 comprehension_test.py
# Speicherverbrauch vergleichen
python3 -c "
import sys
# List Comprehension
print(f'List Comprehension: {sys.getsizeof(list_comp)} bytes')
# Generator Expression
print(f'Generator Expression: {sys.getsizeof(gen_exp)} bytes')
"
Context Manager verkörpern das Prinzip "explicit is better than implicit" perfekt:
# Nicht pythonisch: Manuelles Ressourcenmanagement
defread_file_old_way(filename):
file open(filename, 'r')
try:
content file.read()
return content
finally:
file.close()
# Pythonisch: Automatisches Ressourcenmanagement
defread_file_pythonic_way(filename):
withopen(filename, 'r') asfile:
returnfile.read()
# Eigene Context Manager erstellen
classDatabaseConnection:
def__init__(self, connection_string):
def__enter__(self):
print(f"Verbindung zu {self.connection_string} wird hergestellt...")
# Simuliere Datenbankverbindung
returnself.connection
def__exit__(self, exc_type, exc_val, exc_tb):
print("Datenbankverbindung wird geschlossen...")
returnFalse# Exceptions nicht unterdrücken
# Verwendung des eigenen Context Managers
with DatabaseConnection("postgresql://localhost:5432/mydb") as db:
print(f"Arbeite mit: {db}")
# Automatisches Schließen beim Verlassen des Blocks
Decorators ermöglichen es, Funktionalität auf elegante Weise zu erweitern:
import time
import functools
from typing import Callable, Any
deftiming_decorator(func: Callable) -> Callable:
"""Misst die Ausführungszeit einer Funktion."""
@functools.wraps(func)
defwrapper(*args: Any, **kwargs: Any) -> Any:
print(f"{func.__name__} dauerte {execution_time:.4f} Sekunden")
return result
return wrapper
defretry_decorator(max_attempts: int3, delay: float1.0):
"""Wiederholt eine Funktion bei Fehlern."""
defdecorator(func: Callable) -> Callable:
@functools.wraps(func)
defwrapper(*args: Any, **kwargs: Any) -> Any:
for attempt inrange(max_attempts):
try:
return func(*args, **kwargs)
exceptExceptionas e:
if attempt < max_attempts -1:
print(f"Versuch {attempt + 1} fehlgeschlagen: {e}")
time.sleep(delay)
else:
print(f"Alle {max_attempts} Versuche fehlgeschlagen")
raise last_exception
return wrapper
return decorator
# Anwendung der Decorators
@timing_decorator
@retry_decorator(max_attempts3, delay0.5)
defunreliable_network_call(data: str) -> str:
"""Simuliert einen unzuverlässigen Netzwerkaufruf."""
import random
if random.random() <0.7: # 70% Fehlerwahrscheinlichkeit
raiseConnectionError("Netzwerkfehler simuliert")
return f"Erfolgreich verarbeitet: {data}"
PEP 8 definiert die Coding-Standards für Python. Hier sind die wichtigsten Regeln:
# Installation der wichtigsten Tools
pip install black flake8 pylint mypy isort
# Black: Automatische Code-Formatierung
black my_script.py
# Flake8: Style-Guide-Überprüfung
flake8 my_script.py
# Pylint: Umfassende Code-Analyse
pylint my_script.py
# MyPy: Statische Typenprüfung
mypy my_script.py
# isort: Import-Sortierung
isort my_script.py
# Alle Tools in einem Durchgang
black my_script.py && isort my_script.py && flake8 my_script.py && mypy my_script.py
pyproject.toml Konfiguration:
[tool.black]
[tool.isort]
[tool.mypy]
[tool.pylint.messages_control]
[tool.pylint.format]
Type Hints machen Code selbstdokumentierend und ermöglichen bessere IDE-Unterstützung:
from typing import List, Dict, Optional, Union, Callable, TypeVar, Generic
from dataclasses import dataclass
from enum import Enum
# Basis-Type-Hints
defgreet_user(name: str, age: int) -> str:
return f"Hallo {name}, du bist {age} Jahre alt!"
# Komplexere Typen
defprocess_user_data(
users: List[Dict[str, Union[str, int]]],
filter_func: Callable[[Dict[str, Union[str, int]]], bool]
) -> List[Dict[str, Union[str, int]]]:
return [user for user in users if filter_func(user)]
# Generische Typen
T TypeVar('T')
classStack(Generic[T]):
def__init__(self) -> None:
defpush(self, item: T) -> None:
self._items.append(item)
defpop(self) -> Optional[T]:
ifnotself._items:
returnNone
returnself._items.pop()
defpeek(self) -> Optional[T]:
ifnotself._items:
returnNone
returnself._items[-1]
defis_empty(self) -> bool:
returnlen(self._items) 0
# Dataclasses für strukturierte Daten
@dataclass
classUser:
name: str
email: str
age: int
is_active: boolTrue
def__post_init__(self) -> None:
ifself.age <0:
raiseValueError("Alter kann nicht negativ sein")
if'@'notinself.email:
raiseValueError("Ungültige E-Mail-Adresse")
# Enums für konstante Werte
classUserRole(Enum):
ADMIN"admin"
MODERATOR"moderator"
USER"user"
GUEST"guest"
defhas_permission(self, action: str) -> bool:
UserRole.ADMIN: ["read", "write", "delete", "admin"],
UserRole.MODERATOR: ["read", "write", "moderate"],
UserRole.USER: ["read", "write"],
UserRole.GUEST: ["read"]
}
return action in permissions.get(self, [])
# Walrus Operator (Python 3.8)
defprocess_lines(filename: str) -> List[str]:
withopen(filename, 'r') asfile:
while (line :file.readline()): # Walrus Operator
iflen(line.strip()) >0:
processed_lines.append(line.strip().upper())
return processed_lines
# Positional-only und keyword-only Parameter (Python 3.8)
defcreate_user(name: str, /, *, email: str, age: int18) -> User:
"""
name: Nur positionaler Parameter
email: Nur Keyword-Parameter
age: Nur Keyword-Parameter mit Standardwert
"""
return User(namename, emailemail, ageage)
# Match-Case Statement (Python 3.10)
defhandle_user_action(action: str, user_role: UserRole) -> str:
match (action, user_role):
case ("read", _):
return"Lesezugriff gewährt"
case ("write", UserRole.ADMIN| UserRole.MODERATOR| UserRole.USER):
return"Schreibzugriff gewährt"
case ("delete", UserRole.ADMIN):
return"Löschzugriff gewährt"
case ("admin", UserRole.ADMIN):
return"Administratorzugriff gewährt"
case _:
return"Zugriff verweigert"
import logging
from typing import Optional, Union
from pathlib import Path
# Logging-Konfiguration
logging.basicConfig(
levellogging.INFO,
format'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
classFileProcessingError(Exception):
"""Benutzerdefinierte Exception für Dateiverarbeitungsfehler."""
def__init__(self, filename: str, message: str):
super().__init__(f"Fehler bei {filename}: {message}")
defsafe_file_reader(filepath: Union[str, Path]) -> Optional[str]:
"""
Liest eine Datei sicher und behandelt mögliche Fehler.
Args:
filepath: Pfad zur zu lesenden Datei
Returns:
Dateiinhalt oder None bei Fehlern
Raises:
FileProcessingError: Bei kritischen Fehlern
"""
try:
# Defensive Programmierung: Eingaben validieren
ifnot filepath.exists():
logger.warning(f"Datei {filepath} existiert nicht")
returnNone
ifnot filepath.is_file():
raiseFileProcessingError(str(filepath), "Pfad ist keine Datei")
if filepath.stat().st_size >10*1024*1024: # 10 MB Limit
raiseFileProcessingError(str(filepath), "Datei zu groß (>10MB)")
# Eigentliche Dateiverarbeitung
with filepath.open('r', encoding'utf-8') asfile:
content file.read()
logger.info(f"Datei {filepath} erfolgreich gelesen ({len(content)} Zeichen)")
return content
exceptPermissionError:
logger.error(f"Keine Berechtigung zum Lesen von {filepath}")
returnNone
exceptUnicodeDecodeErroras e:
logger.error(f"Encoding-Fehler in {filepath}: {e}")
returnNone
exceptOSErroras e:
logger.error(f"Systemfehler beim Lesen von {filepath}: {e}")
returnNone
exceptExceptionas e:
# Unerwartete Fehler loggen und weiterwerfen
logger.exception(f"Unerwarteter Fehler bei {filepath}")
raiseFileProcessingError(str(filepath), f"Unerwarteter Fehler: {e}")
# Context Manager für Ressourcen-Management
classDatabaseTransaction:
def__init__(self, connection_string: str):
self.transaction_active False
def__enter__(self):
logger.info("Transaktion wird gestartet...")
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
if exc_type isnotNone:
logger.error(f"Transaktion wird zurückgerollt: {exc_val}")
self.rollback()
else:
logger.info("Transaktion wird bestätigt")
self.commit()
returnFalse# Exceptions nicht unterdrücken
defcommit(self):
ifself.transaction_active:
logger.info("Commit erfolgreich")
self.transaction_active False
defrollback(self):
ifself.transaction_active:
logger.info("Rollback erfolgreich")
self.transaction_active False
import unittest
from unittest.mock import patch, mock_open
import pytest
from pathlib import Path
classTestSafeFileReader(unittest.TestCase):
defsetUp(self):
"""Setup für jeden Test."""
self.test_content "Dies ist ein Testinhalt"
@patch("pathlib.Path.exists")
@patch("pathlib.Path.is_file")
@patch("pathlib.Path.stat")
@patch("pathlib.Path.open", new_callablemock_open, read_data"test content")
deftest_successful_file_reading(self, mock_file, mock_stat, mock_is_file, mock_exists):
"""Testet erfolgreiches Lesen einer Datei."""
# Arrange
mock_stat.return_value.st_size 1024# 1KB
# Act
# Assert
self.assertEqual(result, "test content")
mock_file.assert_called_once_with('r', encoding'utf-8')
@patch("pathlib.Path.exists")
deftest_nonexistent_file(self, mock_exists):
"""Testet Verhalten bei nicht existierender Datei."""
# Arrange
mock_exists.return_value False
# Act
# Assert
self.assertIsNone(result)
@patch("pathlib.Path.exists")
@patch("pathlib.Path.is_file")
@patch("pathlib.Path.stat")
deftest_file_too_large(self, mock_stat, mock_is_file, mock_exists):
"""Testet Verhalten bei zu großer Datei."""
# Arrange
mock_stat.return_value.st_size 20*1024*1024# 20MB
# Act & Assert
withself.assertRaises(FileProcessingError) as context:
safe_file_reader(self.test_filepath)
self.assertIn("zu groß", str(context.exception))
# Pytest-basierte Tests
deftest_user_creation_with_valid_data():
"""Testet User-Erstellung mit gültigen Daten."""
assert user.name "Max Mustermann"
assert user.email "[email protected]"
assert user.age 30
assert user.is_active isTrue
deftest_user_creation_with_invalid_age():
"""Testet User-Erstellung mit ungültigem Alter."""
with pytest.raises(ValueError, match"Alter kann nicht negativ sein"):
User(name"Test", email"[email protected]", age=-5)
@pytest.mark.parametrize("role,action,expected", [
(UserRole.ADMIN, "read", True),
(UserRole.ADMIN, "admin", True),
(UserRole.USER, "read", True),
(UserRole.USER, "admin", False),
(UserRole.GUEST, "write", False),
])
deftest_user_permissions(role, action, expected):
"""Testet Benutzerberechtigungen mit verschiedenen Kombinationen."""
Bash-Kommandos für Testing:
# Unit Tests ausführen
python -m unittest discover -s tests -p "test_*.py" -v
# Pytest ausführen
pytest -v --tb=short
# Coverage-Report erstellen
pip install coverage pytest-cov
coverage run -m pytest
coverage report -m
coverage html
# Tests mit verschiedenen Python-Versionen (tox)
pip install tox
echo"[tox]
[testenv]
pytest-cov
tox
