525 lines
12 KiB
Markdown
Executable File
525 lines
12 KiB
Markdown
Executable File
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. |