- Durch den aktuellen Trend in der AI-Entwicklung habe ich ernsthaft begonnen, Python zu lernen und zu nutzen, und bin inzwischen sehr zufrieden mit diesem Ökosystem
- Python hat sich im Vergleich zu früher zu einer deutlich schnelleren und moderneren Sprache entwickelt, und ich spüre die rasanten Fortschritte, etwa bei der Performance durch Cython
- Ich setze moderne Entwicklungstools und Bibliotheken wie uv, ruff, pytest und Pydantic aktiv in meinem Workflow ein und steigere so die Produktivität
- Ich nutze außerdem Projektstrukturen und Automatisierung, um die Unterschiede zwischen Produktionsumgebungen und einer auf Jupyter-Notebooks/Skripten basierenden Entwicklung zu verringern
- Mit GitHub Actions, Docker usw. habe ich CI/CD, Tests und Infrastrukturverwaltung effizient aufgebaut
Zusammenfassung von I’m Switching to Python and Actually Liking It
Warum der Wechsel zu Python
- In AI-zentrierten Entwicklungsumgebungen ist Python zur de-facto-Standardsprache geworden
- Früher wurde es nur für einfache Skripte verwendet, inzwischen nutze ich es ernsthaft, um „praxisnahe Anwendungen“ wie RAG, Agenten und generative AI zu bauen
- Dabei wurde mir klar, dass sich das Python-Ökosystem im Vergleich zu früher enorm weiterentwickelt hat
Drei Stärken von Python
- Reiches Ökosystem aus Bibliotheken und Tools: spezialisiert auf Datenverarbeitung, Analyse, Web und AI
- Performance-Verbesserungen durch Cython usw.: compilerbasierte Optimierung ist möglich
- Verbesserte Lesbarkeit der Syntax: Legacy-Syntax wie
__init__, __new__ wird verborgen, stattdessen gibt es intuitivere Syntax
Projektstruktur (auf Monorepo-Basis)
Wichtige Tools und Einstellungen
-
uv
- Ein moderner Python-Paketmanager und Build-Tool von Astral
- Erledigt die meisten Aufgaben wie Abhängigkeitsverwaltung, Erzeugen virtueller Umgebungen und Projektinitialisierung sehr schnell
pyproject.toml ist die zentrale Konfigurationsdatei, in der alle Metadaten und Abhängigkeitsinformationen zusammengeführt werden
- Mit den Befehlen
uv init, uv add, uv sync lässt sich die Projektumgebung schnell aufsetzen
-
ruff
- Ein ultraschneller Python-Linter und Code-Formatter
- Ein Tool, das
isort, flake8, autoflake usw. integriert
- Mit
ruff check, ruff format lassen sich Linting und automatische Korrekturen ausführen
- Grundlegende Unterstützung für den PEP-8-Coding-Style-Guide
-
ty
- Ein von Astral entwickelter statischer Typprüfer für Python
- In Kombination mit
typing wirksam für statische Analyse und das frühe Verhindern von Bugs
- Trotz früher Entwicklungsphase bereits stabil genug für den praktischen Einsatz
-
pytest
- Das bekannteste Python-Test-Framework für Unit-Tests und erweiterbare Testumgebungen
- Mit einfachen Dateinamensregeln und einem einzigen Befehl sind sofort Integrationstests möglich
- Tests werden als
test_*.py angelegt und mit uv run pytest ausgeführt
- Knappe Syntax, reiches Plugin-Ökosystem
-
Pydantic
- Eine Bibliothek für Datenvalidierung und Verwaltung von Umgebungseinstellungen
- Lädt Konfigurationen auf Basis von
.env-Umgebungsvariablen und validiert Typen
- Mit der Klasse
BaseSettings lassen sich API-Keys oder DB-URLs sicher verwalten
-
MkDocs
- Unterstützt die einfache Erstellung statischer Websites und Dokumentation für Python-Projekte
- Ansprechendes Design im Stil von Open-Source-Projekten lässt sich schnell anwenden
- Auch die Integration mit GitHub Pages ist unkompliziert
-
FastAPI
- Framework zum schnellen Aufbau von RESTful APIs
- Vorteile sind automatische Validierung und Dokumentation, hohe Performance und einfache Integration mit Pydantic
- Auf Basis von Starlette und Pydantic mit hoher Typsicherheit und Performance
-
Dataclasses
- Eine Python-Standardfunktion, mit der sich datenzentrierte Klassen einfach definieren lassen
- Durch automatische Erzeugung spezieller Methoden wird Boilerplate-Code stark reduziert
Versionsverwaltung und Automatisierung
-
GitHub Actions
- Für
project-api und project-ui werden jeweils separate CI-Pipelines eingerichtet
- Bietet Workflows, die sich ideal für den Aufbau von CI-Pipelines auf verschiedenen Betriebssystemen eignen
- Mit Docker-basierten Testumgebungen sind Tests in derselben Umgebung wie in der Produktion möglich
-
Dependabot
- Automatisiert Updates von Abhängigkeiten und die Verwaltung von Sicherheits-Patches
-
Gitleaks
- Ein Tool zur Verhinderung des Lecks sensibler Informationen wie Passwörter oder API-Keys und führt Sicherheitsprüfungen vor Git-Commits aus
-
Pre-commit Hooks
- Ein Tool für automatisches Linting, Formatting und Sicherheitsprüfungen vor Commits
- Zusammen mit ruff, gitleaks usw. sorgt es für konsistenten und hochwertigen Code
Infrastruktur-Automatisierung
-
Make
- Unterstützt einen konsistenten Entwicklungs-Workflow mit Befehlen wie
make test, make infrastructure-up
- Sowohl im Projekt-Root als auch in
project-api gibt es jeweils ein Makefile
-
Docker & Docker Compose
project-api und project-ui werden jeweils in getrennten Containern ausgeführt
- Mit
docker compose up --build -d lässt sich die gesamte App in einer Zeile starten
- Das
Dockerfile enthält die Installation von uv sowie den Befehl zum Starten der FastAPI-App
Fazit
- Mit einer modernen Python-Entwicklungsumgebung wie dieser lässt sich ein effizienter und robuster Produktions-Workflow aufbauen
- In Bereichen wie AI, Daten und Webentwicklung lassen sich viele Vorteile aus dem Wachstum des Python-Ökosystems und der Weiterentwicklung seiner Tools ziehen
- Von Monorepo-Strukturen über Automatisierungstools, Linter und Typprüfer bis hin zu sofort nutzbaren Testumgebungen, Dokumentation und Infrastruktur-Orchestrierung lässt sich eine integrierte Entwicklungskultur umsetzen
6 Kommentare
Python bietet zwar eine Fülle an Bibliotheken und Frameworks, aber ein Nachteil ist, dass die Verwaltung von Paketversionen nicht besonders gut funktioniert und es häufig zu Konflikten kommt.
In der Tendenz sind die Vor- und Nachteile ähnlich wie beim Java von früher.
uv, das auch im Artikel erwähnt wird, ist wirklich ein starkes Tool. Es ist nicht nur schnell, sondern kümmert sich auch um Versions- und Abhängigkeitsverwaltung ähnlich wienpm, deshalb stelle ich gerade komplett aufuvum.Aber heutzutage scheinen sich Versionsverwaltung und Konflikte mit
uvundpoetrygrößtenteils gelöst zu haben.Ist es auch dafür geeignet, das Ökosystem bis hin zu React und solchen Bereichen abzudecken?
Die direkte Anbindung an React ist wegen der unterschiedlichen Sprachen in manchen Bereichen schwierig, aber je nachdem, was Sie möchten, dürfte manches möglich sein.
Persönlich denke ich, dass Frontend-Entwicklung mit Python kein Bereich ist, der besonders stark verbreitet ist.
Hacker-News-Kommentar
Wenn im Code bei fehlenden Umgebungsvariablen die Meldung „YOUTUBE_API_KEY oder YOUTUBE_CHANNEL_ID fehlt“ mit einem OR angezeigt wird, nervt das die Nutzer in einer Situation, in der ein OR gar nicht nötig ist. Es ist eine deutlich bessere User Experience, jeden Wert einzeln zu prüfen und klar anzugeben, welcher fehlt. Da der zusätzliche Entwicklungsaufwand praktisch null ist, würde ich das so empfehlen.
Das ist zwar eine Diskussion, die sich an kleinen Details festbeißt, aber ich finde, für solche Fälle eignet sich der
:=-Operator (Walrus-Operator) perfekt. Zum Beispiel kann man direktif not (API_KEY := os.getenv("API_KEY")):schreiben. Persönlich lasse ich bei internen Tools ausos.environ["API_KEY"]einfach denKeyErrorhochkommen. Auch das ist meiner Meinung nach hinreichend klar.Noch besser ist es meiner Meinung nach, die Bedingungen einzeln zu prüfen und, wenn etwas fehlt, alles auf einmal anzuzeigen. So vermeidet man den Ärger, das Programm wegen einer fehlenden Variablen zu starten und danach gleich den nächsten Fehler für eine andere Variable zu sehen. Je nach Situation lässt sich der Aufwand nicht ganz vermeiden, aber wenn möglich, ist es besser, alles in einem Durchgang zu zeigen.
Am besten ist es, alle Umgebungsvariablen einzulesen und dann alle fehlenden gesammelt zu melden.
Man kann auch mit einem Boolean-Flag arbeiten und erst am Ende einmal
exit(1)aufrufen. So lassen sich alle fehlenden Umgebungsvariablen auf einmal anzeigen.Man kann auch mit
exit("Missing ...")direkt eine Meldung ausgeben und mit Code 1 beenden.Wenn du nach einem Tool suchst, das die Projektstruktur automatisch erzeugt, empfehle ich cookiecutter. Ich habe ein paar Templates, die ich oft benutze, darunter python-lib, click-app, datasette-plugin und llm-plugin. Verwendet wird es so:
uvx cookiecutter gh:simonw/python-libIch habe in Ruby etwas namens baker gebaut. baker kopiert kein Template-Repo, sondern erstellt eine Aufgabenliste mit den nötigen Schritten und kann manuelle Aufgaben (einen API key besorgen und konfigurieren) mit automatischen Aufgaben (
uv initusw.) mischen. Man kann Markdown-Syntax, Ruby-String-Interpolation und Bash verwenden. Ich habe es gebaut, weil ich yml-basierte Configs gründlich leid war.Das Tool, das gerade im Trend ist, heißt Copier. Details stehen in der Copier-Dokumentation.
Ich richte neue Projekte eher gern ein. Ich habe nicht unbedingt das Bedürfnis, so etwas zu automatisieren.
Ich finde, solche Strukturierungs- und Automatisierungstools passen heute eigentlich perfekt zu agentenbasierten LLM-Entwicklungs-Workflows.
Die Einschätzung, „Python sei menschenfreundlicher, weil es auf den meisten Unix-Systemen vorinstalliert ist“, ist etwas optimistisch. Sobald man über
import jsonhinausgeht, landet man schnell in der virtualenv-Hölle. Wenn etwas unter Python 3.13.x auf Ubuntu 22.04 oder 24.04, Rocky 9 usw. laufen soll, sindvenv, Container oder Versionsmanager am Ende Pflicht.Selbst eine Standardbibliothek wie
import jsonmüsste man bei Sprachen ohne so etwas extra installieren, aber Python hat dank seiner Standardbibliothek anfangs eine hohe Produktivität. Für große Projekte reicht die Standardbibliothek natürlich nicht aus, aber ich habe tatsächlich mehrere produktive Programme nur mit der Standardbibliothek ausgeliefert und dabei keine Probleme mit Deployment oder Sicherheitsmanagement gehabt. Auch das Verwalten vonvenvist nicht mehr so schwer wie früher, und die Paketmanager haben sich verbessert.Eine meiner halb scherzhaften Theorien ist, dass Docker/Container sich zur Hälfte deshalb so schnell verbreitet haben, weil sie geholfen haben, die Python-Abhängigkeits-Hölle zu überwinden. Meine erste Python-Erfahrung war 2012 die Installation eines Python-Dienstes auf einem Server, und das war schrecklich: Abhängigkeits-Hölle,
venv-Kommandos, schwer handhabbare Umgebungs-Setups. Auch mitpip,brewund in der macOS-Umgebung bin ich nur gegen Wände gelaufen, und sobald ich Python sah, habe ich einen Bogen darum gemacht. Aber in letzter Zeit fühlt sich Python dankuvselbst aus Sicht von Einsteigern deutlich besser an.uv init,uv addunduv runreichen schon.Ich finde, man sollte immer
virtualenvverwenden. Am Ende ist es nur ein einzelnes Verzeichnis, und inzwischen erscheinen sogar Warnungen, wenn man versucht, mitpipsystemweit zu installieren. So schwierig wie früher ist das nicht mehr.Es ist besser, unbedingt
virtualenvoder Container zu verwenden. Selbst wenn sie sich unbequem anfühlen, verhindert man damit, dass Updates oder neue Bibliotheksversionen systemweit Auswirkungen haben.Früher war oft nur Python 2 im System enthalten, und manchmal hing sogar das System selbst von diesem Python 2 ab, was es eher riskant machte.
Python wirkt auf mich zugleich geschwätzig und unzureichend. Wenn man etwas Einfaches machen will, zieht man entweder 500 Abhängigkeiten hinein, oder der Code wächst selbst für Kleinigkeiten schnell auf Dutzende bis Hunderte Zeilen an. Deshalb vermeide ich Python, weil es zu viel unnötigen Aufwand erzeugt. Mit Perl kann ich Dinge viel schneller und knapper erledigen, daher bevorzuge ich Perl. Python fühlt sich eher wie Programmieren um des Programmierens willen an als wie tatsächliche Arbeitserledigung.
Ich baue auch viele Projekte ohne Abhängigkeiten. Mit der Standardbibliothek und einer einzigen Datei kann man wirklich viel machen. Wenn Python installiert ist, kann man es direkt per
curlherunterladen und ausführen. Ich habe zum Beispiel ein 2000-Zeilen-CLI-Tool für Geldverwaltung, plutus, das etwa 12 Standardmodule verwendet. Rund 25 % des Codes sindargparsefür das Command-Parsing. Ich schreibe so etwas gern explizit, mit einer Zeile pro Parameter.Du hast gesagt, Perl sei schneller und mächtiger als Python. Mich würde interessieren, welche konkreten Beispiele du dafür hast.
An Python ist bequem, dass man bei verschachtelten Datenstrukturen nicht groß nachdenken muss. Listen, Tupel und Dictionaries lassen sich frei mischen, und man greift mit einer einheitlichen Syntax darauf zu. Perl ist zweifellos cleverer und unterhaltsamer, aber gerade deshalb verknotet es mir leichter den Kopf, also ist es nicht meins. Python ist vielleicht etwas langweilig, dafür aber so klar, dass ich den Code auch in fünf Jahren noch verstehen kann.
Ich finde, Python ist schon allein mit der Standardbibliothek gut brauchbar.
Ich bin eigentlich ein Fan von Monorepos, aber in einem früheren Unternehmen ist daraus durch diese Arbeitsweise eine riesige, überladene Struktur geworden, an die niemand mehr heran wollte, weil man Angst hatte, versehentlich Code anderer Teams zu berühren. Das Kernproblem war weniger das Repo selbst als vielmehr, dass eine einzige
requirements.txtfür das ganze Repo verwendet wurde oder die Build-Skripte verheddert waren. Theoretisch sollte ein einmaliges Dependency-Update allen Code auf den neuesten, sicheren Patch-Stand bringen, aber in der Praxis konnte das niemand anfassen. Monorepos funktionieren nur dann gut, wenn eine Organisation stark von NIH geprägt ist, also wie Google alles selbst baut. Wegen solcher Erfahrungen schätze ich inzwischen eher Microservices, bei denen jeder Service zur Teamstruktur der Organisation passt. Siehe auch Conway's law.Python ist die Sprache, die meinem geschriebenen Pseudocode am nächsten kommt. An jeder Stelle, an der ich im Kopf denke „das ist klar“, liefert Python tatsächlich eine intuitive Abstraktion. Da ich aus einem mathematisch geprägten Hintergrund komme, hat mich das sehr angesprochen. Natürlich mag ich inzwischen auch andere Sprachen, aber Python hat immer noch seinen Reiz.
Ich baue meine Projekte fast immer nach demselben Muster auf. Es ist so ähnlich, dass es schon unheimlich ist. Ich frage mich, ob das Python-Ökosystem für Entwickler allmählich auf einen sehr ähnlichen Stil konvergiert. Früher dachte ich, meine Entscheidungen seien einzigartig, aber wenn am Ende alle dasselbe tun, frage ich mich, wo mein freier Wille geblieben ist. Es fühlt sich an wie bei beliebten Babynamen: Man hält seine Wahl für originell und stellt dann fest, dass sie in Wahrheit auf Platz 2 der Hitliste steht.
Solche Strukturen sind in Python schon seit zehn Jahren beliebt. Wahrscheinlich landen viele vernünftige Ingenieure nach reiflicher Überlegung ganz natürlich bei diesem Muster.
Es fühlt sich an, als läge das menschliche Ego wie eine Pilotwellen-Quantenwelle über das ganze Spektrum und würde sich daraus in Sein verwandeln. becoming-being, da muss ich lachen.
Es freut mich, wenn auch andere Python mögen. Ich habe ursprünglich Ruby bevorzugt, musste aber wegen Kundenanforderungen zwangsläufig Python verwenden. Früher war Ruby ziemlich langsam, doch während ich mich widerwillig in Python eingearbeitet habe, habe ich mich allmählich daran gewöhnt und inzwischen macht es mir durchaus Spaß. Bei der Verwendung von Make sehe ich einiges etwas anders: Wenn man überhaupt keine Abhängigkeiten nutzt, unterscheidet es sich kaum von einem Skript mit
case-Anweisungen ... halb im Scherz gesagt, aber wenn ich sehe, wie wenig die heutige Generation mit Make vertraut ist, macht mich das etwas wehmütig. Ganz nach dem Motto: zu meiner Zeit.Ruby hat eine viel schönere Syntax. Dass Python Scopes nur über Einrückung unterscheidet, ist nicht mein Stil.
Ich habe zuerst mit einem Skript auf Basis von
case-Anweisungen angefangen und bin dann bei einem flachen Makefile gelandet. Ein Makefile ist standardisierter und leichter zu überblicken als irgendein zufälliges Skript.Ich frage mich, wann man Dataclass und wann Pydantic Basemodel verwenden sollte. Wenn man ohnehin schon Pydantic benutzt, könnte man dann nicht einfach alles mit Pydantic vereinheitlichen? Ich überlege, ob es überhaupt noch Gründe gibt, bewusst Dataclass zu verwenden.
Das attrs-Projekt hat dazu einen sehr guten Vergleich. Es gibt den offiziellen Vergleich von attrs; natürlich ist er ein wenig voreingenommen, aber ich finde die Argumentation trotzdem schlüssig. Auch dieser Blog ist hilfreich.
Dataclass unterstützt keine Validierung verschachtelter Objekte. Deshalb ist
dataclassbesser für einfache flache Strukturen geeignet, die man etwa zur Übergabe von Funktionsargumenten verwendet. Das ist klarer, als zu viele Argumente einfach in einer Liste zu übergeben.Wegen der Datenvalidierung bei der Erzeugung gibt es Performance-Einbußen. Es gibt deutlich leichtere und schnellere Alternativen wie
msgspec.Wenn man weder Validierung noch Serialisierung braucht, ist Pydantic eher unnötiger Overhead. Meine Faustregel ist: Wenn Serialisierung nötig ist, Pydantic, sonst
dataclass.Man kann mit
TypeAdapter(MyDataclass)eine vorhandene Dataclass direkt verwenden; da frage ich mich, warum man überhaupt noch ein separates Pydantic-Modell anlegen sollte.In letzter Zeit bin ich eher von Python auf andere Sprachen umgestiegen und damit zufriedener. Meine Gedanken zu Python habe ich in diesem Text zusammengefasst. Wenn ich künftig wieder Python verwenden sollte, würde ich auf jeden Fall
uv,ruffundtyausprobieren.asyncaber der größte Produktivitätsschub. Python hat zwarasyncio, aber es gibt mehrere konkurrierende Ansätze, ohne dass sich ein Standard wirklich durchgesetzt hat. Bei JS gefällt mir, dass alles auf einen Ansatz hinausläuft, das war für mich deutlich angenehmer. Auch viele kleine Punkte summieren sich am Ende zu einem großen Unterschied: Scoping über Einrückung in Python, Probleme mit relativen Pfaden bei Imports, die Objekt-Syntax in JS und andere Dinge fühlen sich angenehmer an. Siehe auch diese Erläuterung.