4. Module, Pakete und Projektstruktur¶
Python-Projekte professionell organisieren und moderne Werkzeuge für Paketverwaltung und Entwicklungsumgebungen einsetzen.
Während kleine Python-Skripte oft aus einer einzigen Datei bestehen, wachsen reale Anwendungen schnell zu größeren Projekten heran. Spätestens dann werden Fragen nach Projektstruktur, Wiederverwendung, Abhängigkeiten und Wartbarkeit wichtig.
Python bietet mit Modulen und Paketen ein einfaches, aber äußerst flexibles System zur Organisation von Quellcode. Gleichzeitig hat sich in den vergangenen Jahren das Ökosystem rund um Paketverwaltung und Projektkonfiguration erheblich weiterentwickelt.
Moderne Python-Projekte setzen zunehmend auf pyproject.toml als zentrale Konfigurationsdatei und verwenden Werkzeuge wie uv zur Verwaltung von Abhängigkeiten und virtuellen Umgebungen. Dennoch ist es wichtig, auch die klassischen Werkzeuge wie pip und venv zu verstehen, da sie in vielen bestehenden Projekten weiterhin anzutreffen sind.
In diesem Kapitel lernen Sie, wie professionelle Python-Projekte aufgebaut werden und welche Werkzeuge sich in der Praxis bewährt haben.
Module¶
Python-Module: Struktur und Organisation¶
In Python ist ein Modul eine einzelne Datei mit der Endung .py, die Python-Code enthält. Module sind das primäre Mittel, um Code zu strukturieren und Wiederverwendbarkeit zu schaffen. Anders als in Java oder C#, wo Klassen oft in eigenen Dateien liegen, ist in Python ein Modul flexibler: Es kann Funktionen, Klassen, Variablen und ausführbaren Code enthalten.
Modul-Docstrings und Lesbarkeit¶
Jedes Modul sollte mit einem Docstring beginnen, der kurz den Zweck und die enthaltenen Funktionalitäten beschreibt. Dies fördert die Lesbarkeit und erleichtert Tools wie help() oder Dokumentationsgeneratoren.
"""
Modul zur Verarbeitung von API-Anfragen und Antwortvalidierung.
"""
def validate_response(response: dict) -> bool:
"""Validiert die API-Antwort auf korrekte Struktur."""
# Implementierung
return 'status' in response
Warum Module? Vorteile gegenüber monolithischen Skripten¶
- Wartbarkeit: Kleine, fokussierte Module sind leichter zu verstehen und zu testen.
- Wiederverwendbarkeit: Module können in verschiedenen Projekten importiert werden.
- Namespace-Isolation: Variablen und Funktionen werden in ihrem Modul-Namespace gekapselt, was Namenskonflikte vermeidet.
Importieren von Modulen: Best Practices¶
Python bietet verschiedene Importvarianten:
import modulename
from modulename import function_name
from modulename import * # wird nicht empfohlen
import modulenameist klar und vermeidet Namenskollisionen.from modulename import function_nameeignet sich, wenn nur wenige Funktionen benötigt werden.from modulename import *sollte vermieden werden, da es den Namespace verschmutzt und die Lesbarkeit verschlechtert.
Typische Fehler beim Modulgebrauch¶
- Ausführung von Code beim Import: Python führt beim Import den gesamten Modulcode aus. Initialisierungen oder Tests sollten daher in
if __name__ == "__main__":ausgelagert werden. - Zirkuläre Importe: Vermeiden Sie zyklische Abhängigkeiten, da sie zu Importfehlern führen.
Vergleich zu Java und C¶
In Java oder C# ist die Datei-Struktur oft strikt an Klassen gebunden, während Python mehr Freiheit bietet. Diese Flexibilität erlaubt schnelle Prototypen und modulare Designs, erfordert aber Disziplin bei der Organisation.
Beispiel: Modulstruktur in einem Webprojekt¶
webapp/
├── __init__.py
├── api.py # API-Endpunkte
├── auth.py # Authentifizierung
├── db.py # Datenbankzugriff
└── utils.py # Hilfsfunktionen
Jedes Modul hat klar umrissene Verantwortlichkeiten, was Wartung und Testbarkeit verbessert.
Fazit¶
Module sind das Fundament der Python-Projektstruktur. Sie fördern eine klare Trennung von Verantwortlichkeiten und erleichtern das Teilen und Wiederverwenden von Code. Ein gut dokumentiertes Modul mit einem prägnanten Docstring und bewusster Importstrategie ist ein Zeichen professionellen Python-Codes.
Importe verstehen¶
Das Python-Importsystem: Grundlagen und Philosophie¶
Python-Module und deren Importe sind das Rückgrat jeder größeren Anwendung. Ein Import lädt ein Modul nur einmal pro Interpreter-Session, was Performancevorteile bringt, aber auch Seiteneffekte beim Importieren bedeuten kann.
Absolute vs. relative Importe¶
Python unterscheidet zwischen absoluten und relativen Importen. Absolute Importe beziehen sich auf den vollständigen Pfad vom Projekt- oder Paket-Root aus, während relative Importe sich auf die Position des aktuellen Moduls beziehen.
# Absolute Importe
from myproject.utils import helpers
# Relative Importe (innerhalb eines Pakets)
from .helpers import some_function
from ..core import main
Warum diese Unterscheidung?
- Absolute Importe sind klar und robust, besonders bei größeren Projekten oder wenn Module außerhalb des aktuellen Pakets importiert werden.
- Relative Importe erhöhen die Wartbarkeit innerhalb eines Pakets, da sie weniger anfällig für Umbenennungen oberer Paketstrukturen sind.
Importvarianten und ihre Auswirkungen¶
import modulnamelädt das Modul und bindet es untermodulname.from modulname import symbolbindet das Symbol direkt, was den Code kürzer, aber potenziell weniger transparent macht.
Best Practice: Verwende import modulname für Klarheit und vermeide Namenskonflikte. Nutze from modul import symbol gezielt, wenn die Lesbarkeit dadurch steigt und keine Konflikte entstehen.
Der Importpfad und sys.path¶
Python sucht Module in den Verzeichnissen, die in sys.path gelistet sind. Dieses umfasst u.a.:
- Das aktuelle Arbeitsverzeichnis
- Standardbibliothek
- Installierte Drittanbieter-Pakete
Verzeichnisse können zur Laufzeit manipuliert werden, was dynamische Importmechanismen ermöglicht, aber mit Vorsicht zu genießen ist.
Import-Hooks und dynamische Importe¶
Fortgeschrittene Anwendungen nutzen importlib für dynamische Importe oder um Module zur Laufzeit zu laden. Dies ist besonders in Plugin-Architekturen oder bei API-Clients hilfreich.
import importlib
module_name = 'myplugin'
plugin = importlib.import_module(module_name)
plugin.run()
Häufige Fehler und Stolperfallen¶
- Namenskonflikte: Module im Projekt mit gleichen Namen wie Standardbibliothek oder Drittanbieter-Pakete können zu unerwartetem Verhalten führen.
- Relative Importe außerhalb von Paketen: Relative Importe funktionieren nur innerhalb von Paketen, nicht in Skripten, die direkt ausgeführt werden.
- Import-Zyklen: Zirkuläre Importe können zu
ImportErroroderAttributeErrorführen. Hier hilft eine klare Trennung von Verantwortlichkeiten und ggf. Lazy-Imports.
Zusammenfassung¶
Python-Importe sind flexibel und mächtig, erfordern aber ein Verständnis der Paketstruktur und des Laufzeitkontexts. Absolute Importe bieten Robustheit, relative Importe fördern die Paketkapselung. Explizite und klar strukturierte Importe erhöhen die Lesbarkeit und Wartbarkeit, besonders in größeren Projekten.
Im nächsten Abschnitt werden wir sehen, wie Module zu Paketen zusammengefasst werden und wie __init__.py Dateien die Paketstruktur definieren.
Pakete¶
Pakete als logische Gruppierung von Modulen¶
In Python sind Pakete eine zentrale Möglichkeit, um mehrere Module sinnvoll zu strukturieren und logisch zu gruppieren. Ein Paket ist im Kern ein Verzeichnis mit mindestens einer __init__.py-Datei, die das Verzeichnis als Paket kennzeichnet. Diese Datei kann leer sein, dient aber oft dazu, Initialisierungscode auszuführen oder die öffentliche API des Pakets zu definieren.
Beispiel einer einfachen Paketstruktur¶
myproject/
├── mypackage/
│ ├── __init__.py
│ ├── database.py
│ ├── api.py
│ └── utils.py
└── main.py
mypackage/__init__.pykann z.B. wichtige Klassen oder Funktionen importieren, um sie direkt beim Import vonmypackageverfügbar zu machen.
init.py: API-Definition und Initialisierung¶
# mypackage/__init__.py
from .database import DatabaseConnection
from .api import fetch_data
__all__ = ['DatabaseConnection', 'fetch_data']
Dieses Muster sorgt dafür, dass from mypackage import * nur die explizit exportierten Namen lädt, was die API klar definiert und ungewollte Importe vermeidet.
Flat-Layout vs. Src-Layout¶
Zwei verbreitete Projektstrukturen sind:
Flat-Layout¶
- Einfach und direkt, aber birgt das Risiko, dass lokale Skripte versehentlich mit Systemmodulen kollidieren.
Src-Layout¶
- Besser geeignet für größere Projekte, da es klare Trennung zwischen Quellcode und anderen Dateien schafft und Importkonflikte vermeidet.
Importverhalten in Paketen¶
Innerhalb von Paketen sind relative Importe üblich und empfohlen, um Abhängigkeiten klar zu halten und Refaktorisierungen zu erleichtern:
Absolute Importe sind ebenfalls möglich, werden aber bei komplexen Paketen oft unübersichtlich.
Best Practices¶
- Definieren Sie in
__init__.pynur die öffentliche API, keine Implementierungsdetails. - Nutzen Sie relative Importe innerhalb von Paketen, um Kopplung zu reduzieren.
- Verwenden Sie das Src-Layout für größere Projekte, um Namenskonflikte zu vermeiden.
- Vermeiden Sie zu tiefe Paketstrukturen, da Python-Importe sonst unübersichtlich werden.
Zusammenfassung¶
Pakete sind in Python das Mittel der Wahl, um Module logisch zu gruppieren und eine saubere API zu definieren. Die __init__.py-Datei ist dabei mehr als nur ein Marker: Sie ermöglicht Initialisierung und API-Definition. Die Wahl der Projektstruktur (Flat vs. Src) beeinflusst die Wartbarkeit und Importklarheit maßgeblich. Für erfahrene Entwickler ist das Verständnis dieser Konzepte essenziell, um Python-Projekte professionell zu organisieren und langfristig wartbar zu gestalten.
Virtuelle Umgebungen¶
Warum virtuelle Umgebungen in Python?¶
In Python ist die Verwaltung von Abhängigkeiten und Laufzeitumgebungen komplexer als in vielen kompilierenden Sprachen wie Java oder C#. Dort sind Abhängigkeiten meist zentral im Build-Tool (z.B. Maven, MSBuild) definiert und Versionierung erfolgt projektübergreifend. Python-Pakete werden jedoch global oder benutzerspezifisch installiert, was zu Versionskonflikten führt („Dependency Hell“).
Eine virtuelle Umgebung isoliert Projektabhängigkeiten vollständig vom globalen Python-Interpreter und anderen Projekten. So können unterschiedliche Projekte unterschiedliche Versionen derselben Bibliothek nutzen, ohne sich gegenseitig zu beeinflussen. Dies ist essenziell für reproduzierbare Builds, sauberes Deployment und parallele Entwicklung.
Das Modul venv¶
Python bringt seit Version 3.3 das Modul venv mit, um virtuelle Umgebungen zu erstellen und zu verwalten. Im Gegensatz zu externen Tools wie virtualenv ist venv Teil der Standardbibliothek und genügt für die meisten Anwendungsfälle.
Erstellen einer virtuellen Umgebung¶
Dies legt im aktuellen Verzeichnis einen Ordnerenv an, der eine isolierte Python-Umgebung enthält.
Aktivieren der Umgebung¶
- Auf Unix/macOS:
- Auf Windows (PowerShell):
Nach Aktivierung ist der Python-Interpreter und pip auf diese Umgebung beschränkt.
Deaktivieren¶
Typische Struktur eines Projekts mit venv¶
my_project/
├── env/ # virtuelle Umgebung (meist im .gitignore)
├── src/ # Quellcode
├── tests/ # Tests
├── requirements.txt # Abhängigkeiten
└── README.md
Warum nicht globale Installation?¶
- Versionskonflikte vermeiden: Zwei Projekte benötigen unterschiedliche Versionen von
requests. - Reproduzierbarkeit: CI/CD-Pipelines nutzen dieselben Abhängigkeiten wie lokal.
- Sauberkeit: Globale Installation bleibt unverändert, System-Python bleibt stabil.
Vergleich zu Java/C#¶
In Java oder C# sind Abhängigkeiten typischerweise im Projekt-Manifest (pom.xml, csproj) definiert und werden beim Build automatisch heruntergeladen. Die Laufzeitumgebung ist meist ein global installierter JRE/JDK oder .NET Runtime, die keine Isolation auf Bibliotheksebene bietet. Python trennt Laufzeit (Interpreter) und Bibliotheken stärker, was virtuelle Umgebungen notwendig macht.
Best Practices¶
- Lege virtuelle Umgebungen außerhalb des Quellcodes oder ignoriere sie im Versionskontrollsystem.
- Nutze
requirements.txtoder moderne Tools wieuv(siehe späteres Kapitel) zur Verwaltung von Abhängigkeiten. - Aktiviere die virtuelle Umgebung immer vor der Entwicklung oder dem Ausführen von Skripten.
Beispiel: Installation und Nutzung einer Bibliothek¶
python3 -m venv env
source env/bin/activate
pip install requests
python
>>> import requests
>>> response = requests.get('https://api.example.com/data')
>>> print(response.status_code)
200
Fazit¶
Virtuelle Umgebungen sind in Python unverzichtbar, um saubere, reproduzierbare und konfliktfreie Entwicklungsumgebungen zu schaffen. Das in der Standardbibliothek enthaltene venv bietet eine einfache und robuste Lösung, die sich nahtlos in den Python-Workflow integriert.
Paketverwaltung mit pip¶
Pakete installieren und verwalten mit pip¶
pip ist das zentrale Werkzeug zur Paketverwaltung in Python und vergleichbar mit NuGet in C# oder Maven/Gradle in Java, jedoch mit einem Fokus auf Einfachheit und Flexibilität. Es ermöglicht das Installieren, Aktualisieren und Entfernen von Paketen aus dem Python Package Index (PyPI) oder anderen Quellen.
Installation und Upgrade von Paketen¶
Pakete werden typischerweise projektbezogen in virtuellen Umgebungen installiert, um Versionskonflikte zu vermeiden. Die grundlegenden Befehle lauten:
pip install requests==2.28.1 # Exakte Version installieren
pip install --upgrade requests # Paket aktualisieren
pip uninstall requests # Paket entfernen
Die Versionsspezifikation ist wichtig, um reproduzierbare Umgebungen zu gewährleisten. Anders als in vielen statisch typisierten Sprachen, wo Abhängigkeiten oft zentral und strikt im Build-Tool definiert sind, ist Python hier flexibler, was aber auch zu "Dependency Hell" führen kann, wenn nicht sorgfältig verwaltet.
Anforderungen dokumentieren: requirements.txt¶
Für reproduzierbare Builds und Teamarbeit ist es üblich, alle Abhängigkeiten in einer requirements.txt-Datei zu dokumentieren:
Diese Datei kann mit
eingelesen werden. So wird sichergestellt, dass alle Entwickler und Deployment-Umgebungen dieselben Paketversionen verwenden.
Best Practices bei requirements-Dateien¶
- Exakte Versionen (==) für Produktion: Minimiert Überraschungen durch API-Änderungen.
- Versionseinschränkungen (~=, >=, <) für Entwicklung: Erlaubt Flexibilität bei Bugfixes.
- Mehrere Dateien:
requirements.txtfür Laufzeit,requirements-dev.txtfür Entwicklungswerkzeuge.
Unterschied zu Java/C¶
Im Vergleich zu Maven oder NuGet sind pip und requirements.txt weniger formal und bieten keine automatische Transitivitätsauflösung mit Sperrdateien (Lockfiles) im Standard. Werkzeuge wie uv (siehe späteres Kapitel) oder Poetry ergänzen diese Lücke.
Pip und virtuelle Umgebungen¶
pip installiert Pakete standardmäßig in die aktive Python-Umgebung. Um globale Konflikte zu vermeiden, sollten virtuelle Umgebungen (z.B. mit venv) genutzt werden. Innerhalb einer solchen Umgebung ist pip lokal und isoliert.
Beispiel: Paketinstallation und Verwaltung in einem Webprojekt¶
python -m venv .venv
source .venv/bin/activate # Linux/macOS
.\.venv\Scripts\activate # Windows
pip install flask requests
pip freeze > requirements.txt
pip freeze listet alle aktuell installierten Pakete mit exakten Versionen auf und ist die Basis für die requirements.txt.
Häufige Fehler¶
- Pakete global installieren: Führt oft zu Versionskonflikten.
- Keine Versionsspezifikation: Erschwert reproduzierbare Deployments.
- Manuelles Editieren von requirements.txt ohne
pip freeze: Kann zu Inkonsistenzen führen.
Zusammenfassung¶
pip ist das Rückgrat der Python-Paketverwaltung und erlaubt durch einfache Befehle eine flexible Handhabung von Abhängigkeiten. Die Kombination mit virtuellen Umgebungen und requirements.txt-Dateien ist essenziell für professionelle Projekte, um Konsistenz und Wartbarkeit sicherzustellen. Anders als in streng typisierten Ökosystemen ist Python hier pragmatisch und setzt auf Entwicklerdisziplin und ergänzende Tools wie uv für eine moderne Projektverwaltung.
Moderne Projektverwaltung mit uv¶
Einführung in uv¶
uv ist ein modernes Kommandozeilenwerkzeug zur Verwaltung von Python-Projekten, das virtuelle Umgebungen, Abhängigkeiten und Projektstrukturen nahtlos integriert. Im Gegensatz zu klassischen Tools wie venv oder pip kombiniert uv die Funktionen von virtuellen Umgebungen und Paketmanagement in einem Workflow und ermöglicht so eine effiziente Projektverwaltung.
Projekt erstellen mit uv¶
Ein neues Projekt wird mit einem einzigen Befehl initialisiert:
Dieser Befehl erzeugt eine standardisierte Projektstruktur inklusive einer virtuellen Umgebung und einer pyproject.toml-Datei, die als zentrale Konfigurationsdatei dient.
Typische Struktur nach uv init:
Diese Struktur trennt Quellcode (src) und Tests klar voneinander, was der Best Practice entspricht und die Wartbarkeit erhöht.
Abhängigkeiten verwalten¶
Abhängigkeiten werden über uv komfortabel hinzugefügt und verwaltet:
Dabei wird die virtuelle Umgebung automatisch aktiviert, und die pyproject.toml wird entsprechend angepasst. Laufzeit- und Entwicklungsabhängigkeiten werden sauber getrennt, was reproduzierbare Builds unterstützt.
Virtuelle Umgebung und Shell¶
uv kapselt die virtuelle Umgebung und bietet einen einfachen Zugang zur Shell:
Damit wird eine neue Shell mit aktivierter virtueller Umgebung gestartet. Dies vermeidet das manuelle Aktivieren mit source .venv/bin/activate und ist plattformübergreifend konsistent.
Programme starten mit uv run¶
Anstatt Skripte direkt mit python auszuführen, empfiehlt sich der Aufruf über uv run:
uv run sorgt dafür, dass die virtuelle Umgebung aktiviert ist und alle Abhängigkeiten geladen werden. Zudem können Umgebungsvariablen und Laufzeitparameter zentral verwaltet werden.
Vorteile gegenüber klassischen Workflows¶
- Integrierte Verwaltung: Kein manuelles Erstellen und Aktivieren von virtuellen Umgebungen.
- Zentrale Konfiguration:
pyproject.tomlals Single Source of Truth für Abhängigkeiten und Projektmetadaten. - Konsistenz: Einheitliche Befehle für Installation, Ausführung und Shell-Zugang.
- Reproduzierbarkeit: Klare Trennung von Laufzeit- und Entwicklungsabhängigkeiten.
Best Practices¶
- Nutze
uvals zentrales Werkzeug für alle Projektmanagement-Aufgaben. - Verwalte Abhängigkeiten ausschließlich über
uv addunduv remove. - Starte Programme immer über
uv run, um Umgebungsprobleme zu vermeiden.
Zusammenfassung¶
uv modernisiert die Python-Projektverwaltung durch Integration von virtuellen Umgebungen, Abhängigkeitsmanagement und Ausführung in einem Tool. Für Entwickler mit Erfahrung in Rust oder JavaScript bietet uv eine vertraute, konsistente und reproduzierbare Art, Python-Projekte zu strukturieren und zu betreiben.
Die pyproject.toml¶
Einführung in die pyproject.toml¶
Die Datei pyproject.toml ist heute das zentrale Konfigurationsformat moderner Python-Projekte. Sie wurde mit PEP 518 eingeführt, um Build-Tools, Abhängigkeiten und Projektmetadaten in einer einzigen, klar strukturierten Datei zu vereinheitlichen. Im Vergleich zu früheren, fragmentierten Ansätzen (z.B. setup.py, requirements.txt, setup.cfg) bietet pyproject.toml eine deklarative und standardisierte Möglichkeit, Projekte zu konfigurieren.
Aufbau und zentrale Bereiche¶
Die pyproject.toml ist im TOML-Format geschrieben, das sich durch einfache Lesbarkeit und klare Struktur auszeichnet. Die wichtigsten Abschnitte sind:
[project]: Enthält Metadaten zum Projekt wie Name, Version, Autoren, Lizenz und Abhängigkeiten.[build-system]: Definiert das Build-Backend und dessen Anforderungen.- Tool-spezifische Konfigurationen (z.B. für Linter, Formatter, Test-Frameworks) unter
[tool.<name>].
Beispiel: Minimaler Projektabschnitt¶
[project]
name = "my_api_client"
version = "0.1.0"
description = "Asynchroner HTTP-Client für REST APIs"
authors = ["Max Mustermann <max@example.com>"]
# Laufzeitabhängigkeiten
dependencies = [
"httpx>=0.23",
"pydantic>=1.10"
]
[build-system]
requires = ["setuptools>=65.5", "wheel"]
build-backend = "setuptools.build_meta"
Laufzeit- vs. Entwicklungsabhängigkeiten¶
Im Gegensatz zu requirements.txt trennt pyproject.toml klar zwischen Laufzeit- und Entwicklungsabhängigkeiten. Laufzeitabhängigkeiten werden im [project]-Block unter dependencies gelistet. Entwicklungsabhängigkeiten (z.B. für Tests, Linting oder Dokumentation) werden typischerweise über das Tool-Management (z.B. uv) verwaltet und in eigenen Abschnitten oder Dateien gepflegt.
Diese Trennung fördert reproduzierbare Umgebungen und klare Abgrenzungen, ähnlich wie in Java oder C# mit Build- und Test-Konfigurationen.
Tool-Konfigurationen¶
Viele Tools lesen ihre Konfiguration direkt aus pyproject.toml. So vermeidet man separate Konfigurationsdateien und reduziert die Komplexität.
Beispiel für black und mypy:
Build-Backends¶
Der Abschnitt [build-system] definiert, wie das Projekt gebaut wird. Python unterstützt verschiedene Build-Backends, z.B. setuptools, flit oder poetry (hier nur am Rande erwähnt).
Dieser Abschnitt ist essenziell, damit Werkzeuge wie pip das Projekt korrekt bauen und installieren können.
Vergleich zu Java/C¶
In Java oder C# sind Build- und Projektkonfigurationen oft in XML- oder JSON-Dateien (z.B. pom.xml bei Maven, .csproj bei .NET) organisiert. Python setzt mit pyproject.toml auf eine deklarative, leicht lesbare Syntax, die gleichzeitig flexibel genug ist, um verschiedene Tools und Workflows zu integrieren.
Best Practices¶
- Versionierung: Halte
versionim[project]-Block aktuell und konsistent. - Abhängigkeiten: Nutze semantische Versionierung und setze Mindestversionen, um Kompatibilitätsprobleme zu vermeiden.
- Tool-Konfiguration: Bündle alle Tool-Einstellungen in
pyproject.toml, um verstreute Konfigurationsdateien zu vermeiden. - Build-Backend: Wähle ein Build-Backend, das zu deinem Workflow passt, und dokumentiere es im Projekt.
Zusammenfassung¶
Die pyproject.toml ist das Herzstück moderner Python-Projektorganisation. Sie vereint Metadaten, Abhängigkeiten, Build-Informationen und Tool-Konfigurationen in einer einzigen, gut strukturierten Datei. Für Entwickler mit Java- oder C#-Hintergrund bietet sie ein vergleichbares, aber flexibleres und Python-idiomatisches Konzept für Projektmanagement und Build-Prozesse.
Abhängigkeiten verwalten¶
Laufzeit- und Entwicklungsabhängigkeiten trennen¶
In Python-Projekten ist es üblich, zwischen Laufzeitabhängigkeiten (Dependencies) und Entwicklungsabhängigkeiten (Dev-Dependencies) zu unterscheiden. Laufzeitabhängigkeiten sind Bibliotheken, die Ihre Anwendung benötigt, um korrekt zu funktionieren. Entwicklungsabhängigkeiten dienen ausschließlich Werkzeugen wie Test-Frameworks, Lintern oder Build-Tools.
Diese Trennung verbessert die Wartbarkeit und vermeidet unnötige Pakete in Produktionsumgebungen – ein Konzept, das in Java oder C# oft über unterschiedliche Build-Profile (z. B. Maven Profiles, NuGet-Konfigurationen) umgesetzt wird.
Verwaltung mit uv¶
Das Tool uv (Universal Virtualenv) unterstützt diese Trennung elegant über die pyproject.toml. Beispiel:
[project]
dependencies = [
"requests>=2.28",
]
[dependency-groups]
dev = [
"pytest>=7.0",
"black",
]
Reproduzierbare Umgebungen schaffen¶
Reproduzierbarkeit ist in Python-Projekten essenziell, um "funktioniert auf meinem Rechner"-Probleme zu vermeiden. Neben virtuellen Umgebungen ist die exakte Versionierung der Abhängigkeiten entscheidend.
Dazu werden häufig sogenannte Lock-Dateien verwendet, die alle transitive Abhängigkeiten mit ihren exakten Versionen festhalten. uv generiert automatisch eine uv.lock-Datei, ähnlich wie package-lock.json in JavaScript oder packages.lock.json in .NET.
Diese Lock-Datei sollte ins Versionskontrollsystem eingecheckt werden, um konsistente Builds zu gewährleisten.
Umgang mit requirements.txt¶
Obwohl pyproject.toml und Lock-Dateien moderner sind, begegnet man in vielen Projekten noch requirements.txt. Diese Datei listet Abhängigkeiten zeilenweise auf, z. B.:
Für komplexere Projekte ist das Format jedoch weniger geeignet, da es keine Trennung zwischen Laufzeit- und Entwicklungsabhängigkeiten oder Lock-Mechanismen unterstützt.
Best Practices für Abhängigkeitsmanagement¶
- Trennung von Laufzeit- und Entwicklungsabhängigkeiten: Vermeiden Sie unnötige Pakete in Produktionsumgebungen.
- Verwenden Sie Lock-Dateien: Sorgen Sie für reproduzierbare Umgebungen.
- Nutzen Sie
uvfür konsistente Verwaltung:uvintegriert virtuelle Umgebungen, Installation und Locking in einem Workflow. - Regelmäßige Updates mit Bedacht: Aktualisieren Sie Abhängigkeiten kontrolliert, um Kompatibilitätsprobleme zu vermeiden.
Beispiel: Setup eines Web-API-Projekts¶
Hierbei sind fastapi und uvicorn für den Betrieb notwendig, während pytest und httpx nur für Tests gebraucht werden.
Zusammenfassung¶
Abhängigkeitsmanagement in Python profitiert von klarer Trennung, reproduzierbaren Umgebungen und modernen Tools wie uv. Im Vergleich zu Java oder C# ist Python hier flexibler, aber auch anfälliger für "Dependency Hell" ohne konsequente Locking-Strategien. Die Python-Denkweise setzt auf einfache, deklarative Konfigurationen und automatisierte Umgebungsverwaltung, um Wartbarkeit und Stabilität zu gewährleisten.
Ausführbare Module und main¶
Ausführbare Module mit __main__¶
In Python ist das Konzept eines ausführbaren Moduls ein zentrales Idiom, das sich von der Main-Methode in Java oder C# unterscheidet. Statt eine spezielle Main-Methode zu definieren, nutzt Python die magische Variable __name__ und die Datei __main__.py, um den Einstiegspunkt eines Programms zu bestimmen.
if __name__ == "__main__"¶
Jedes Python-Modul besitzt die Variable __name__. Wird ein Modul direkt ausgeführt, erhält __name__ den Wert "__main__". Wird es hingegen importiert, entspricht __name__ dem Modulnamen.
Diese Eigenschaft wird genutzt, um Code nur beim direkten Ausführen auszuführen:
Wird modul.py importiert, passiert nichts, da der Codeblock nicht ausgeführt wird. Dies entspricht dem Schutzmechanismus in Java oder C# für den Einstiegspunkt, ist aber flexibler und ermöglicht modulare Wiederverwendung.
Ausführbare Pakete und __main__.py¶
Pakete können ebenfalls ausführbar sein, wenn sie eine Datei __main__.py enthalten. Diese wird ausgeführt, wenn das Paket mit python -m paketname gestartet wird.
Beispiel Projektstruktur:
In __main__.py befindet sich typischerweise der Programmstartcode:
Der Aufruf erfolgt dann über:
Dies ist besonders nützlich, um Pakete als eigenständige Programme anzubieten und gleichzeitig ihre Module importierbar zu halten.
Vorteile gegenüber klassischen Main-Methoden¶
- Modularität: Der Einstiegspunkt ist nur ein Teil des Moduls, andere Funktionen bleiben importierbar.
- Flexibilität: Ein und dasselbe Modul kann als Skript oder Bibliothek genutzt werden.
- Paketintegration: Pakete können als Programme gestartet werden, ohne separate Skripte zu benötigen.
Typische Fehler und Best Practices¶
- Nicht
__main__prüfen: Code, der beim Import unerwartet ausgeführt wird, erschwert Tests und Wiederverwendung. - Komplexen Code in
if __name__ == "__main__"packen: Besser ist es, die Logik in Funktionen auszulagern und nur den Aufruf in den Block zu schreiben.
Beispiel: CLI-Tool als Paket¶
# cli_tool/commands.py
import argparse
def run() -> None:
parser = argparse.ArgumentParser(description="Beispiel CLI Tool")
parser.add_argument('--verbose', action='store_true')
args = parser.parse_args()
if args.verbose:
print("Verbose Mode aktiviert")
else:
print("Standardmodus")
Durch python -m cli_tool startet das Tool, ohne dass separate Skripte nötig sind.
Ausführbare Module und __main__.py sind ein eleganter Mechanismus, um Python-Programme flexibel, modular und idiomatisch zu strukturieren. Sie ermöglichen es, Pakete und Module sowohl als Bibliotheken als auch als eigenständige Anwendungen zu verwenden – ein Paradigma, das sich deutlich von dem statischen Main-Methoden-Konzept anderer Sprachen unterscheidet und die dynamische Natur von Python unterstreicht.