Python Style Guide (PEP 8)¶
Einführung¶
Dieses Dokument beschreibt die Coding Conventions für Python-Code in der Standard-Library. Es ist wichtig zu beachten, dass viele Projekte ihre eigenen Style Guides haben, die im Konfliktfall Vorrang haben.
Wichtigste Prinzipien¶
"Code is read much more often than it is written." - Guido van Rossum
- Konsistenz ist wichtig, aber nicht absolut
- Konsistenz innerhalb eines Projekts ist wichtiger als die Einhaltung dieses Guides
- Konsistenz innerhalb eines Moduls oder einer Funktion ist am wichtigsten
- Manchmal ist es sinnvoll, den Guide zu ignorieren
Gründe, den Guide zu ignorieren:¶
- Die Anwendung würde den Code weniger lesbar machen
- Um mit umgebendem Code konsistent zu bleiben
- Der Code ist älter als die Guideline
- Kompatibilität mit älteren Python-Versionen erforderlich
Code Layout¶
Einrückung¶
Verwende 4 Leerzeichen pro Einrückungsebene.
Fortsetzungszeilen - Korrekte Beispiele:¶
# Mit öffnender Klammer ausgerichtet
foo = long_function_name(var_one, var_two,
var_three, var_four)
# Zusätzliche Einrückung zur Unterscheidung von Argumenten
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
# Hanging indent
foo = long_function_name(
var_one, var_two,
var_three, var_four)
Fortsetzungszeilen - Falsche Beispiele:¶
# ❌ Argumente in erster Zeile ohne vertikale Ausrichtung
foo = long_function_name(var_one, var_two,
var_three, var_four)
# ❌ Weitere Einrückung erforderlich
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
Mehrzeilige if-Statements:¶
# Keine zusätzliche Einrückung
if (this_is_one_thing and
that_is_another_thing):
do_something()
# Kommentar hinzufügen
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
# Zusätzliche Einrückung auf der Fortsetzungszeile
if (this_is_one_thing
and that_is_another_thing):
do_something()
Schließende Klammern:¶
# Option 1: Unter dem ersten Nicht-Whitespace-Zeichen
my_list = [
1, 2, 3,
4, 5, 6,
]
# Option 2: Unter dem ersten Zeichen der Zeile
my_list = [
1, 2, 3,
4, 5, 6,
]
Tabs oder Leerzeichen?¶
Leerzeichen sind die bevorzugte Einrückungsmethode.
- Tabs nur verwenden, um mit bereits mit Tabs eingerücktem Code konsistent zu bleiben
- Python erlaubt keine Mischung von Tabs und Leerzeichen
Maximale Zeilenlänge¶
Begrenze alle Zeilen auf maximal 79 Zeichen.
- Für fließende Textblöcke (Docstrings, Kommentare): 72 Zeichen
- Teams können sich auf bis zu 99 Zeichen einigen (Docstrings/Kommentare bleiben bei 72)
- Python Standard Library verwendet strikt 79 Zeichen
Zeilenumbrüche:¶
# Bevorzugt: Implizite Zeilenfortsetzung mit Klammern
with open('/path/to/some/file/you/want/to/read') as file_1, \
open('/path/to/some/file/being/written', 'w') as file_2:
file_2.write(file_1.read())
Sollte vor oder nach einem binären Operator umgebrochen werden?¶
Neu: Vor dem Operator (Knuth-Stil)
# ✅ Korrekt: Einfach Operatoren mit Operanden zu verbinden
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
# ❌ Falsch: Operatoren weit von ihren Operanden entfernt
income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest)
Leerzeilen¶
- Zwei Leerzeilen umgeben Top-Level-Funktionen und Klassendefinitionen
- Eine Leerzeile umgibt Methodendefinitionen innerhalb einer Klasse
- Verwende Leerzeilen sparsam innerhalb von Funktionen, um logische Abschnitte zu kennzeichnen
Source File Encoding¶
- Core Python Distribution sollte immer UTF-8 verwenden
- Keine Encoding-Deklaration verwenden
- Alle Bezeichner in der Python Standard Library MÜSSEN ASCII-only sein
Imports¶
Grundregeln¶
# ✅ Korrekt: Imports auf separaten Zeilen
import os
import sys
# ✅ Auch okay:
from subprocess import Popen, PIPE
Import-Reihenfolge¶
Imports sollten in folgender Reihenfolge gruppiert werden (mit Leerzeile zwischen Gruppen):
- Standard Library Imports
- Related Third-Party Imports
- Local Application/Library Specific Imports
Absolute vs. Relative Imports¶
# ✅ Bevorzugt: Absolute Imports
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
# ✅ Auch akzeptabel: Explizite relative Imports
from . import sibling
from .sibling import example
Weitere Import-Regeln¶
# ✅ Korrekt: Klassen aus Modulen importieren
from myclass import MyClass
from foo.bar.yourclass import YourClass
# Bei Namenskonflikten:
import myclass
import foo.bar.yourclass
# Dann: myclass.MyClass und foo.bar.yourclass.YourClass verwenden
- Wildcard Imports vermeiden:
from <module> import * - Nur akzeptabel beim Republishing eines internen Interfaces als Teil einer öffentlichen API
Module Level Dunder Names¶
"""This is the example module.
This module does stuff.
"""
from __future__ import barry_as_FLUFL
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'
import os
import sys
Reihenfolge:
1. Module Docstring
2. from __future__ imports
3. Dunder names (__all__, __author__, __version__, etc.)
4. Andere Imports
String Quotes¶
- Single-quoted und double-quoted Strings sind gleich
- Wähle eine Regel und bleibe dabei
- Bei Strings mit Quotes: Verwende die andere Quote-Art, um Backslashes zu vermeiden
Whitespace in Ausdrücken und Statements¶
Pet Peeves (Vermeiden)¶
# ✅ Korrekt:
spam(ham[1], {eggs: 2})
foo = (0,)
if x == 4: print(x, y); x, y = y, x
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
spam(1)
dct['key'] = lst[index]
x = 1
y = 2
long_variable = 3
# ❌ Falsch:
spam( ham[ 1 ], { eggs: 2 } )
bar = (0, )
if x == 4 : print(x , y) ; x , y = y , x
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
spam (1)
dct ['key'] = lst [index]
x = 1
y = 2
long_variable = 3
Slicing¶
# ✅ Korrekt:
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
Andere Empfehlungen¶
Binäre Operatoren: Immer ein Leerzeichen auf beiden Seiten¶
# Assignments (=), Augmented Assignments (+=, -=, etc.)
# Comparisons (==, <, >, !=, <=, >=, in, not in, is, is not)
# Booleans (and, or, not)
Operatoren mit unterschiedlicher Priorität¶
Funktionsannotationen¶
Keyword-Argumente und Default-Werte¶
# ✅ Korrekt: Keine Leerzeichen bei unannotated
def complex(real, imag=0.0):
return magic(r=real, i=imag)
# ✅ Korrekt: Leerzeichen bei annotated mit Default
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
# ❌ Falsch:
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...
Compound Statements¶
# ❌ Falsch:
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
# ❌ Definitiv falsch:
if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()
try: something()
finally: cleanup()
Trailing Commas¶
Wann verwenden?¶
Bei Version Control hilfreich:¶
Kommentare¶
Wichtig: Widersprüchliche Kommentare sind schlimmer als keine Kommentare!
Allgemeine Regeln¶
- Kommentare sollten vollständige Sätze sein
- Erstes Wort großschreiben (außer bei Identifiern mit Kleinbuchstaben)
- Block-Kommentare bestehen aus vollständigen Sätzen, die mit Punkt enden
- Ein oder zwei Leerzeichen nach Satzende-Punkt in mehrsätzigen Kommentaren
- Schreibe Kommentare auf Englisch, außer du bist 120% sicher, dass der Code nie von Nicht-Muttersprachlern gelesen wird
Block-Kommentare¶
# Block-Kommentare gelten für den folgenden Code
# Sie sind auf das gleiche Level wie der Code eingerückt
# Jede Zeile beginnt mit # und einem Leerzeichen
# Absätze innerhalb eines Block-Kommentars werden durch
# eine Zeile mit nur einem # getrennt
#
# Wie hier.
Inline-Kommentare¶
# Sparsam verwenden!
# Mindestens zwei Leerzeichen vom Statement trennen
x = x + 1 # Compensate for border
# ❌ Nicht so:
x = x + 1 # Increment x
Documentation Strings (Docstrings)¶
Nach PEP 257:
# ✅ Für alle öffentlichen Module, Funktionen, Klassen und Methoden
def public_function():
"""Return a foobang.
Optional plotz says to frobnicate the bizbaz first.
"""
pass
# ✅ One-liner: Schließendes """ auf der gleichen Zeile
def short_function():
"""Return an ex-parrot."""
pass
- Private Methoden brauchen keine Docstrings, aber sollten einen Kommentar nach
defhaben - Das schließende
"""bei mehrzeiligen Docstrings sollte auf einer eigenen Zeile stehen
Namenskonventionen¶
Naming Styles Übersicht¶
| Style | Verwendung |
|---|---|
b |
Einzelner Kleinbuchstabe |
B |
Einzelner Großbuchstabe |
lowercase |
Kleinbuchstaben |
lower_case_with_underscores |
Kleinbuchstaben mit Unterstrichen |
UPPERCASE |
Großbuchstaben |
UPPER_CASE_WITH_UNDERSCORES |
Großbuchstaben mit Unterstrichen |
CapitalizedWords oder CapWords |
PascalCase / CamelCase |
mixedCase |
camelCase (initial lowercase!) |
Capitalized_Words_With_Underscores |
⚠️ Hässlich! |
Spezielle Formen mit Unterstrichen¶
_single_leading_underscore: Schwacher "internal use" Indikatorsingle_trailing_underscore_: Vermeidung von Konflikten mit Python Keywords__double_leading_underscore: Name Mangling bei Klassen-Attributen__double_leading_and_trailing_underscore__: "Magic" Objekte (z.B.__init__,__file__)
Zu vermeidende Namen¶
Niemals verwenden: l (lowercase L), O (uppercase O), oder I (uppercase i) als einzelne Zeichen-Variablennamen.
In manchen Schriften sind diese nicht von 1 und 0 zu unterscheiden.
Konkrete Benennungsregeln¶
Package und Module Namen¶
# ✅ Korrekt:
import mypackage
import my_module
# Module: kurz, kleingeschrieben, Unterstriche erlaubt
# Packages: kurz, kleingeschrieben, Unterstriche vermeiden
Klassennamen¶
Type Variable Namen¶
from typing import TypeVar
# ✅ CapWords, kurze Namen bevorzugt
T = TypeVar('T')
AnyStr = TypeVar('AnyStr')
Num = TypeVar('Num')
# Mit Co/Contravariance
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
Exception Namen¶
# ✅ Klassen-Convention + "Error" Suffix
class ValidationError(Exception):
pass
class DatabaseConnectionError(Exception):
pass
Globale Variablennamen¶
# Gleiche Konvention wie Funktionen
global_variable = 42
GLOBAL_CONSTANT = 100
# Bei "from M import *": __all__ verwenden oder _ prefix
__all__ = ['public_function', 'PublicClass']
_internal_helper = "nicht exportiert"
Funktions- und Variablennamen¶
# ✅ lowercase mit Unterstrichen
def my_function():
pass
my_variable = 42
another_variable = "test"
# mixedCase nur in bereits bestehenden mixedCase-Kontexten (z.B. threading.py)
Funktions- und Methoden-Argumente¶
# ✅ Immer 'self' für erste Instanzmethode
class MyClass:
def instance_method(self, arg):
pass
# ✅ Immer 'cls' für erste Klassenmethode
@classmethod
def class_method(cls, arg):
pass
# Bei Keyword-Konflikt: Trailing Underscore
def function(class_): # Besser als 'clss'
pass
Methoden und Instance Variables¶
class MyClass:
# ✅ Public: Lowercase mit Unterstrichen
def public_method(self):
pass
public_attribute = "visible"
# ✅ Non-public: Ein führender Unterstrich
def _internal_method(self):
pass
_internal_attribute = "not for external use"
# ✅ Name Mangling: Zwei führende Unterstriche
# Verwendet um Konflikte mit Subklassen zu vermeiden
def __private_method(self):
pass
Konstanten¶
Design für Vererbung¶
Entscheide immer, ob Attribute public oder non-public sein sollen:
- Public Attributes: Keine führenden Unterstriche
- Für externe Clients gedacht
-
Commitment zu Backward-Compatibility
-
Non-public Attributes: Ein führender Unterstrich
- Nicht für Dritte gedacht
-
Keine Garantie für Stabilität
-
Subclass API: Möglicherweise zwei führende Unterstriche
- Nur für spezielle Fälle (Name-Mangling)
- Vermeidet Attribute-Namenskonflikte
class BaseClass:
# Public
public_attribute = "everyone can use"
# Non-public (internal)
_internal_attribute = "implementation detail"
# Name mangling (vermeidet Subclass-Konflikte)
__private_attribute = "very private"
def public_method(self):
"""Public API method."""
pass
def _internal_method(self):
"""Internal helper method."""
pass
Package- und Projektstruktur¶
Projekt-Layout Best Practices¶
my_project/
├── README.md
├── LICENSE
├── setup.py / pyproject.toml
├── requirements.txt
├── .gitignore
├── docs/
│ ├── conf.py
│ └── index.md
├── tests/
│ ├── __init__.py
│ ├── test_module1.py
│ └── test_module2.py
├── src/ # Optional: "src layout"
│ └── mypackage/
│ ├── __init__.py
│ ├── module1.py
│ ├── module2.py
│ └── subpackage/
│ ├── __init__.py
│ └── submodule.py
└── mypackage/ # Alternative: "flat layout"
├── __init__.py
├── module1.py
└── module2.py
Ordner- und Package-Namenskonventionen¶
Package-Namen (Verzeichnisse mit __init__.py)¶
# ✅ KORREKT:
mypackage/
my_package/
myapp/
dataprocessing/
# ❌ VERMEIDEN:
MyPackage/ # Keine Großbuchstaben
my-package/ # Keine Bindestriche (nicht importierbar)
My_Package/ # Keine gemischte Schreibweise
Regeln für Package-Namen: - Kurz und aussagekräftig - Nur Kleinbuchstaben - Unterstriche nur wenn nötig für Lesbarkeit - Keine Bindestriche (würde Python-Syntax brechen) - Muss ein gültiger Python-Identifier sein
Modul-Dateinamen¶
# ✅ KORREKT:
user_authentication.py
data_processor.py
api_client.py
utils.py
config.py
# ❌ VERMEIDEN:
UserAuthentication.py # Keine CapWords für Module
data-processor.py # Keine Bindestriche
API_Client.py # Gemischte Schreibweise
Verzeichnisstruktur-Beispiele¶
Beispiel 1: Einfache Package-Struktur¶
myproject/
├── myproject/
│ ├── __init__.py
│ ├── core.py
│ ├── utils.py
│ ├── config.py
│ └── models/
│ ├── __init__.py
│ ├── user.py
│ └── product.py
├── tests/
│ ├── __init__.py
│ ├── test_core.py
│ └── test_utils.py
├── setup.py
└── README.md
Beispiel 2: Größeres Projekt mit Subpackages¶
webapp/
├── webapp/
│ ├── __init__.py
│ ├── api/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── auth.py
│ │ └── validators.py
│ ├── database/
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── connection.py
│ │ └── migrations/
│ │ ├── __init__.py
│ │ └── version_001.py
│ ├── services/
│ │ ├── __init__.py
│ │ ├── email_service.py
│ │ └── payment_service.py
│ └── utils/
│ ├── __init__.py
│ ├── logging.py
│ └── decorators.py
├── tests/
│ ├── unit/
│ │ └── test_services.py
│ └── integration/
│ └── test_api.py
├── config/
│ ├── development.py
│ ├── production.py
│ └── testing.py
├── docs/
├── requirements/
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
└── setup.py
Beispiel 3: Src-Layout (empfohlen für Bibliotheken)¶
mylib/
├── src/
│ └── mylib/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ └── test_core.py
├── docs/
├── pyproject.toml
└── README.md
Vorteile des src-Layouts: - Verhindert versehentlichen Import aus dem Arbeitsverzeichnis - Zwingt zur korrekten Installation vor Tests - Klarere Trennung zwischen Source und Tests
Spezielle Verzeichnisse¶
Tests-Verzeichnis¶
tests/
├── __init__.py
├── conftest.py # pytest fixtures
├── unit/
│ ├── __init__.py
│ ├── test_models.py
│ └── test_utils.py
├── integration/
│ ├── __init__.py
│ └── test_api.py
└── fixtures/
├── data.json
└── sample_files/
Namenskonvention für Tests:
- Dateien: test_*.py oder *_test.py
- Test-Funktionen: test_*
- Test-Klassen: Test*
Docs-Verzeichnis¶
docs/
├── conf.py # Sphinx config
├── index.rst
├── api/
│ ├── core.rst
│ └── utils.rst
├── tutorials/
│ └── getting_started.rst
└── _static/
└── custom.css
Scripts/Tools-Verzeichnis¶
Konfigurationsdateien¶
project/
├── .gitignore
├── .editorconfig
├── .flake8
├── pyproject.toml # Modern (PEP 518)
├── setup.py # Falls benötigt
├── setup.cfg # Falls benötigt
├── requirements.txt # Oder:
├── requirements/
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
├── Makefile
└── tox.ini
Private/Internal Packages¶
mypackage/
├── __init__.py
├── public_module.py
└── _internal/ # Unterstrich = internal
├── __init__.py
├── _helpers.py
└── _vendored/ # Vendored dependencies
└── thirdparty/
Namespace Packages (PEP 420)¶
# Ohne __init__.py für Namespace-Packages
namespace/
├── package1/
│ ├── __init__.py # Reguläres Package
│ └── module.py
└── package2/
├── __init__.py
└── module.py
# Beide können als namespace.package1 und namespace.package2 importiert werden
Häufige Fehler vermeiden¶
# ❌ VERMEIDEN:
# Bindestriche im Package-Namen
my-package/ # Nicht importierbar!
# Konflikt mit Standard-Library
email/ # Überschreibt stdlib 'email'
test/ # Überschreibt stdlib 'test'
# Zu generische Namen
utils/ # Zu vage
helpers/ # Zu vage
common/ # Zu vage
# Zu lange Namen
my_extremely_long_and_descriptive_package_name/
# ✅ BESSER:
# Klare, kurze Namen
myapp_email/ # Kein Konflikt
myapp_testing/ # Kein Konflikt
# Spezifischere Namen
string_utils/ # Was für Utils?
auth_helpers/ # Was für Helpers?
shared_models/ # Was ist gemeinsam?
# Angemessene Länge
myapp/
Import-Pfade optimieren¶
# Package-Struktur:
myapp/
├── __init__.py
├── models/
│ ├── __init__.py
│ ├── user.py
│ └── product.py
└── services/
├── __init__.py
└── auth.py
# In myapp/models/__init__.py:
from .user import User
from .product import Product
__all__ = ['User', 'Product']
# Erlaubt dann:
from myapp.models import User, Product
# Statt:
from myapp.models.user import User
from myapp.models.product import Product
Programming Recommendations¶
Python-Implementierung-Agnostisch¶
# ❌ Nicht verlassen auf CPython-Optimierung:
a = a + b # Nur effizient in CPython bei bestimmten Typen
# ✅ Stattdessen für Performance-kritische Teile:
result = ''.join(string_list)
Vergleiche mit None¶
# ✅ Korrekt:
if x is None:
pass
if x is not None:
pass
# ❌ Falsch:
if x == None:
pass
if not x is None:
pass
Vorsicht bei truthiness-Tests¶
# ❌ Gefährlich:
if x: # Was wenn x ein leerer Container ist?
pass
# ✅ Wenn None gemeint ist, explizit sein:
if x is not None:
pass
Rich Comparisons¶
# ✅ Alle sechs implementieren:
class MyClass:
def __eq__(self, other):
pass
def __ne__(self, other):
pass
def __lt__(self, other):
pass
def __le__(self, other):
pass
def __gt__(self, other):
pass
def __ge__(self, other):
pass
# Oder verwende functools.total_ordering Decorator
from functools import total_ordering
@total_ordering
class MyClass:
def __eq__(self, other):
pass
def __lt__(self, other):
pass
Lambda vs. def¶
Exception Hierarchien¶
# ✅ Von Exception ableiten, nicht BaseException
class ValidationError(Exception):
pass
class ConfigError(Exception):
pass
# Exception-Hierarchie basierend auf dem "Was ging schief?"
class DatabaseError(Exception):
"""Base class for database errors."""
pass
class ConnectionError(DatabaseError):
"""Could not connect to database."""
pass
class QueryError(DatabaseError):
"""Query execution failed."""
pass
Exception Chaining¶
# ✅ Explizite Verkettung
try:
do_something()
except SomeError as e:
raise NewError("Context information") from e
# ✅ Explizites Unterdrücken
try:
do_something()
except SomeError:
raise NewError("New context") from None
Spezifische Exceptions fangen¶
# ✅ Korrekt:
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
# ❌ Falsch:
try:
import platform_specific_module
except: # Fängt auch KeyboardInterrupt!
platform_specific_module = None
Try/Except-Klauseln minimieren¶
# ✅ Korrekt:
try:
value = collection[key]
except KeyError:
return key_not_found(key)
else:
return handle_value(value)
# ❌ Falsch:
try:
# Zu breit!
return handle_value(collection[key])
except KeyError:
# Würde auch KeyError von handle_value() fangen
return key_not_found(key)
Context Manager¶
# ✅ Für Ressourcen
with open('file.txt') as f:
data = f.read()
# ✅ Explizit bei mehr als Resource Management
with conn.begin_transaction():
do_stuff_in_transaction(conn)
# ❌ Implizit ist verwirrend
with conn:
do_stuff_in_transaction(conn)
Return Statements¶
# ✅ Konsistent:
def foo(x):
if x >= 0:
return math.sqrt(x)
else:
return None
def bar(x):
if x < 0:
return None
return math.sqrt(x)
# ❌ Inkonsistent:
def foo(x):
if x >= 0:
return math.sqrt(x)
# Implizites return None
def bar(x):
if x < 0:
return # Explizit ohne Wert
return math.sqrt(x)
String-Methoden¶
# ✅ Korrekt:
if foo.startswith('bar'):
pass
if foo.endswith('.txt'):
pass
# ❌ Fehleranfällig:
if foo[:3] == 'bar':
pass
if foo[-4:] == '.txt':
pass
Typ-Vergleiche¶
Leere Sequenzen¶
# ✅ Korrekt: Nutze die Truthiness
if not seq:
pass
if seq:
pass
# ❌ Falsch:
if len(seq):
pass
if not len(seq):
pass
Boolean-Vergleiche¶
# ✅ Korrekt:
if greeting:
pass
# ❌ Falsch:
if greeting == True:
pass
# ❌ Noch schlimmer:
if greeting is True:
pass
Flow Control in finally¶
# ❌ VERMEIDEN: return/break/continue in finally
def foo():
try:
1 / 0
finally:
return 42 # Unterdrückt die Exception!
Function und Variable Annotations¶
Type Hints (PEP 484)¶
# ✅ Korrekt:
def greeting(name: str) -> str:
return f'Hello {name}'
def process_data(items: List[int], threshold: float = 0.5) -> Dict[str, Any]:
pass
# Variable Annotations
code: int
count: int = 0
class Point:
coords: Tuple[int, int]
label: str = '<unknown>'
# ❌ Falsch:
code:int # Kein Leerzeichen nach Doppelpunkt
code : int # Leerzeichen vor Doppelpunkt
class Test:
result: int=0 # Keine Leerzeichen um =
Type Ignore¶
# Wenn andere Annotation-Verwendung gewünscht:
# type: ignore
# Am Anfang der Datei, wenn Type Hints nicht verwendet werden sollen
Tools für Code-Qualität¶
Linter und Formatter¶
# flake8: PEP 8 Checker
pip install flake8
flake8 myproject/
# black: Auto-Formatter (opinionated)
pip install black
black myproject/
# pylint: Umfassender Linter
pip install pylint
pylint myproject/
# mypy: Type Checker
pip install mypy
mypy myproject/
# isort: Import Sortierer
pip install isort
isort myproject/
Pre-commit Hooks¶
# .pre-commit-config.yaml
repos:
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
Zusammenfassung - Die wichtigsten Punkte¶
Code Layout¶
- ✅ 4 Leerzeichen für Einrückung
- ✅ Maximal 79 Zeichen pro Zeile (99 für Teams nach Absprache)
- ✅ Umbruch vor binären Operatoren (Knuth-Stil)
- ✅ 2 Leerzeilen zwischen Top-Level-Definitionen
- ✅ 1 Leerzeile zwischen Methoden
Naming¶
- ✅
lowercase_with_underscoresfür Funktionen und Variablen - ✅
CapWordsfür Klassen - ✅
UPPER_CASE_WITH_UNDERSCORESfür Konstanten - ✅
_leading_underscorefür non-public - ✅ Packages: kurz, lowercase, Unterstriche nur wenn nötig
Imports¶
- ✅ Standard Library → Third-Party → Local
- ✅ Absolute Imports bevorzugen
- ✅ Ein Import pro Zeile
- ✅ Keine Wildcard-Imports
Strings und Whitespace¶
- ✅ Konsistent bei Quotes bleiben
- ✅ """ für Docstrings
- ✅ Kein Whitespace vor Kommas/Klammern
- ✅ Leerzeichen um Operatoren (außer bei Prioritäten)
Best Practices¶
- ✅
is/is notfür None-Vergleiche - ✅ Spezifische Exceptions
- ✅ Context Manager für Ressourcen
- ✅ Type Hints für neue Projekte
- ✅ Docstrings für Public API
Referenzen¶
- PEP 8: https://peps.python.org/pep-0008/
- PEP 257: Docstring Conventions
- PEP 484: Type Hints
- PEP 526: Variable Annotations
- PEP 518: pyproject.toml
- Python Packaging Guide: https://packaging.python.org/