Hier eine umfassende, aber für Einsteiger verständliche Einführung in **black**, den „opinionated“ Python-Code-Formatter. --- ## 1. Grundidee von *black* **Was ist black?** - *black* ist ein **automatisches Formatierungs-Tool** für Python. - Es ändert **nur die Formatierung**, nicht die Logik deines Codes. - Es ist **„opinionated“**: Es gibt nur sehr wenige Einstellungen – black entscheidet den Stil für dich. **Zentrale Idee:** > „*You are not your code style.*“ > Statt darüber zu diskutieren, ob ein Leerzeichen hier oder dort besser ist, überlässt du das black. > > Ziel: > - einheitlicher Stil > - weniger Diskussionen in Code Reviews > - Fokus auf inhaltliche Fehler, nicht auf Formatierung --- ## 2. Was macht black konkret? Black nimmt deinen Python-Code, parst ihn und schreibt ihn nach festen Regeln neu. Beispiele: ### 2.1 Installation ```bash pip install black ``` ### 2.2 Einfache Nutzung Eine einzelne Datei formatieren: ```bash black main.py ``` Ein ganzes Projekt: ```bash black . ``` Nur anzeigen, was geändert würde (ohne zu schreiben): ```bash black --diff --check . ``` --- ## 3. Praxisnahe Formatierungsbeispiele ### 3.1 Zeilenumbrüche und Einrückung **Vorher:** ```python def very_long_function_name(arg1,arg2,arg3,arg4,arg5,arg6,arg7=False,arg8=None): return (arg1+arg2+arg3+arg4+arg5+arg6) ``` **Nachher (black):** ```python def very_long_function_name( arg1, arg2, arg3, arg4, arg5, arg6, arg7=False, arg8=None, ): return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 ``` Was passiert? - Argumente werden sauber untereinander geschrieben. - Operatoren (+) werden mit Leerzeichen versehen. - Abschluss-Komma nach dem letzten Argument (hilft bei späteren Änderungen). --- ### 3.2 Strings und Anführungszeichen Black bevorzugt fast immer **doppelte Anführungszeichen**. **Vorher:** ```python name = 'Alice' message = 'Hello, ' + name + '!' ``` **Nachher:** ```python name = "Alice" message = "Hello, " + name + "!" ``` Ausnahmen: - Wenn ein String doppelte Anführungszeichen enthält, kann black einzelne beibehalten, um weniger zu escapen. --- ### 3.3 Leerzeichen und Klammern **Vorher:** ```python result=(1+2)*3 if x==42: print( 'Answer',x ) ``` **Nachher:** ```python result = (1 + 2) * 3 if x == 42: print("Answer", x) ``` Black: - fügt Leerzeichen um Operatoren hinzu (`1 + 2`, `x == 42`), - entfernt unnötige Leerzeichen (`print( 'Answer',x )` → `print("Answer", x)`). --- ### 3.4 Collections (Listen, Dicts, Sets) **Vorher:** ```python config = {"host":"localhost","port":5432,"debug":True} ``` **Nachher:** ```python config = { "host": "localhost", "port": 5432, "debug": True, } ``` Vorteile: - Bessere Lesbarkeit - Leicht, neue Einträge hinzuzufügen (wegen Abschluss-Komma). --- ### 3.5 Lange Ausdrücke **Vorher:** ```python query = session.query(User).filter(User.is_active==True, User.signup_date>=start_date, User.signup_date<=end_date).order_by(User.signup_date.desc()) ``` **Nachher:** ```python query = ( session.query(User) .filter( User.is_active == True, User.signup_date >= start_date, User.signup_date <= end_date, ) .order_by(User.signup_date.desc()) ) ``` Black bricht lange Zeilen so um, dass: - sie unter der vorgegebenen Maximalbreite bleiben (standard: 88 Zeichen), - die Struktur des Codes klarer sichtbar wird. --- ## 4. Abgrenzung zu verwandten Tools ### 4.1 Formatter vs. Linter vs. Typprüfer - **Formatter** (Formatierer): Passen das **Aussehen** deines Codes an – z. B. black, autopep8, yapf. - **Linter**: Finden mögliche **Fehler, Stilprobleme oder unschöne Konstrukte** – z. B. flake8, pylint, ruff. - **Typprüfer**: Prüfen, ob Typen konsistent sind (z. B. mit `typing`) – z. B. mypy, pyright. Black ist **nur** ein Formatter. --- ### 4.2 Black vs. autopep8 **autopep8**: - Ziel: Code so anpassen, dass er PEP8-konform ist. - Orientierung direkt an den PEP8-Regeln. - Viele Optionen (z. B. bestimmte Checks an- oder abschalten). **black**: - Ziel: **konsequenter, einheitlicher Stil** – nicht nur PEP8, sondern zusätzliche strenge Regeln. - Sehr wenige Konfigurationsmöglichkeiten (bewusst!). - Output ist oft deutlich „strenger” und einheitlicher als autopep8. --- ### 4.3 Black vs. yapf **yapf**: - Google-Tool zur Formatierung von Python. - Sehr konfigurierbar: du kannst deinen Style stark beeinflussen (ähnlich wie bei C++/clang-format). - Mehr Freiheit, aber dadurch auch mehr Diskussionen möglich. **black**: - „Meine Regeln oder gar nicht“. - Ziel: Diskussionen vermeiden, daher kaum Konfigurationsoptionen. - Sehr stabiler, vorhersehbarer Output. --- ### 4.4 Black vs. isort **isort** ist ein Tool, um **Imports** zu sortieren und zu gruppieren. - Sortiert `import`-Zeilen alphabetisch und nach Gruppen: - Standardbibliothek - Third-Party - Projektinterne Module Black: - Formatiert auch Imports (Zeilenumbrüche, Leerzeichen), - sortiert sie aber **nicht** nach Paketnamen. Typischerweise nutzt man: ```bash isort . black . ``` Oder beides zusammen über Tools wie `ruff` oder `pre-commit`. --- ## 5. Welche Probleme löst black? ### 5.1 Konsistenter Stil in Teams Ohne Tool: - Jeder schreibt „sein“ Python. - Unterschiedliche Leerzeichen, Umbrüche, String-Stile, etc. - Code wirkt „bunt“ und uneinheitlich. Mit black: - Jedes Commit, jede Datei, jede Funktion hat denselben Stil. - Neue Teammitglieder lernen schneller, was „üblich“ ist – es ist einfach: das, was black macht. --- ### 5.2 Weniger Diskussionen in Code Reviews Vor black: - Kommentare wie: „Bitte hier ein Leerzeichen einfügen.“ - „Kannst du die Argumente untereinander schreiben?“ - „Wir verwenden eigentlich doppelte Anführungszeichen.“ Mit black: - Reviewer sagen: „Bitte einmal black drüber laufen lassen.“ - Fokus liegt auf: - Ist der Algorithmus korrekt? - Sind die Funktionen gut benannt? - Sind Tests vorhanden? --- ### 5.3 Bessere Lesbarkeit & Wartbarkeit - Lange Zeilen werden sinnvoll umgebrochen. - Verschachtelte Ausdrücke werden strukturiert. - Datensammlungen (Listen, Dicts) werden mehrzeilig und übersichtlich dargestellt. Beispiel: Eine unübersichtliche Dict-Liste wird automatisch gut lesbar formatiert. **Vorher:** ```python users=[{"id":1,"name":"Alice","active":True},{"id":2,"name":"Bob","active":False}] ``` **Nachher:** ```python users = [ {"id": 1, "name": "Alice", "active": True}, {"id": 2, "name": "Bob", "active": False}, ] ``` --- ### 5.4 Weniger „Rauschen“ in Git-Diffs Manuell Änderungen + Formatierung: - Du änderst eine Zeile, formatierst etwas, - der Diff zeigt viele Änderungen, obwohl nur wenig Logik geändert wurde. Mit black: - Wenn alle Dateien bereits formatiert sind, entstehen bei späteren Änderungen klarere Diffs: - Format ist überall gleich, - nur die wirklich geänderte Logik fällt auf. --- ## 6. Herausforderungen und typische Stolpersteine ### 6.1 Einstieg in ein bestehendes Projekt Problem: - Du führst black in einem **alten, großen Projekt** ein. - Beim ersten Durchlauf ändert black Hunderte/tausende Dateien. - Git-Diff ist riesig. Lösungen / Best Practices: - Einmaliger „Formatting-Commit“ (nur Style): - In einem eigenen Commit alle Dateien mit black formatieren. - Danach neue Commits nur mit funktionalen Anpassungen. - Oder schrittweise: - Nur neue/aktuell bearbeitete Module mit black formatieren. - z. B. mit `pre-commit`-Hook nur geänderte Dateien behandeln. --- ### 6.2 „Mir gefällt der Stil nicht!“ Black ist sehr strikt: - Du kannst nicht „mal eben“ sagen: - „Ich möchte lieber 120 statt 88 Zeichen pro Zeile“ (ok, das **geht** als Option) - Aber: Du kannst nicht festlegen, wie exakt bestimmte Konstrukte formatiert werden. Beispiel: Viele stören sich zunächst an: - doppelten Anführungszeichen, - „ungewöhnlichen“ Zeilenumbrüchen. Wichtig: - black ist ein **Team-Tool**. - Man einigt sich darauf, den Stil zu akzeptieren, um Diskussionen zu vermeiden. --- ### 6.3 Integration mit anderen Tools (z. B. isort, flake8, ruff) Typische Stolperfallen: - `isort` und `black` können sich widersprechen, wenn sie unterschiedliche Maximalzeilenlängen nutzen. - Linter können sich über Formatierung beschweren, wenn sie anders konfiguriert sind als black. Best Practice: - Einheitliche Konfiguration, z. B. in `pyproject.toml`: ```toml [tool.black] line-length = 88 [tool.isort] profile = "black" line_length = 88 ``` So passen Formatierung und Importsortierung zusammen. --- ### 6.4 Änderungen zwischen Black-Versionen Black entwickelt sich weiter. - In seltenen Fällen ändert eine neue Version den Stil leicht. - Dann kann ein erneuter Durchlauf große Diffs erzeugen. Abhilfe: - Black-Version in `pyproject.toml` oder `requirements.txt` fest pinnen: ```text black==24.4.2 ``` - Gelegentlich bewusst aktualisieren und einmaliger Reformatting-Commit. --- ### 6.5 Performance bei sehr großen Projekten Für normale Projekte ist black schnell genug. Bei **sehr großen Repositories** kann einmaliges Formatieren aber dauern. Lösungen: - Nur geänderte Dateien formatieren (z. B. über `pre-commit`). - In CI nur `black --check .` laufen lassen (prüft, ob alles formatiert ist, ohne neu zu schreiben). --- ## 7. Black in der Praxis: Workflows ### 7.1 Integration in den Editor/IDE Die meisten Editoren können black beim Speichern ausführen: - **VS Code**: - Erweiterung „Python“ installieren. - In `settings.json`: ```json { "python.formatting.provider": "black", "editor.formatOnSave": true } ``` - **PyCharm**: - Externes Tool konfigurieren oder über Plugins. Vorteil: - Du denkst nicht mehr über Formatierung nach: Speichern = fertig formatiert. --- ### 7.2 Verwendung mit `pre-commit` `pre-commit` ist ein Tool, das Skripte ausführt, bevor ein Git-Commit akzeptiert wird. Installieren: ```bash pip install pre-commit ``` In `.pre-commit-config.yaml`: ```yaml repos: - repo: https://github.com/psf/black rev: 24.4.2 # Version anpassen hooks: - id: black ``` Aktivieren: ```bash pre-commit install ``` Jetzt wird bei jedem `git commit` black auf die geänderten Dateien angewandt. Das stellt sicher, dass alles im Repo immer formatiert ist. --- ### 7.3 Black in der CI-Pipeline In CI-Systemen (GitHub Actions, GitLab CI, etc.) kannst du prüfen, ob der Code schon formatiert ist. Beispiel (CLI): ```bash black --check . ``` - Exit-Code 0: alles ok. - Exit-Code ≠ 0: irgendetwas müsste formatiert werden. Das verhindert, dass unformatierter Code in `main` oder `master` landet. --- ## 8. Zusammenfassung - **black** ist ein **sehr strenger, kaum konfigurierbarer Formatter** für Python-Code. - Ziel: - **einheitlicher Stil**, - **keine Diskussionen** über Formatierung, - Fokus auf Logik und Qualität statt auf Leerzeichen. - Abgrenzung: - black formatiert nur – es ist kein Linter und kein Typprüfer. - Im Vergleich zu autopep8/yapf: weniger konfigurierbar, dafür klarer, einheitlicher Stil. - Es löst: - Stil-Inkonsistenz, Formatierungs-Diskussionen, „Rauschen“ in Diffs. - Herausforderungen: - Einstieg in ältere Projekte, - wenig Einfluss auf Stil, - Abstimmung mit anderen Tools, Versionsänderungen. - In der Praxis: - Nutze black im Editor (Format-on-save), - im `pre-commit`-Hook, - und als `--check` in der CI. Wenn du möchtest, kann ich dir im nächsten Schritt ein kleines Beispielprojekt konstruieren (mit `pyproject.toml`, `pre-commit`, isort/black-Konfiguration), damit du siehst, wie man black von Anfang an sauber einrichtet.