von: Konstantin
aktualisiert: Sept. 17, 2024
Du programmierst in Python und hast das Gefühl, dein Code könnte übersichtlicher und verständlicher sein? Keine Sorge, du bist nicht allein! In diesem Artikel zeige ich dir, wie du sauberen, "clean" Code schreibst, der nicht nur funktioniert, sondern auch leicht lesbar und verständlich ist. Du erfährst, wie du Funktionen und Variablen benennen solltest, was guten von schlechtem Code unterscheidet und wie du deinen Code verbesserst.
Was soll das heißen: "Clean Code"? Kurz gesagt geht es darum Code für Menschen und nicht für Maschinen zu schreiben. Du solltest direkt beim Schreiben daran denken, dass du den Code in Zukunft auch noch verstehst oder (noch schlimmer), dass andere mit deinem Code auch etwas anfangen können. Der Code solle also leicht lesbar und verständlich sein. Sauberer "Clean" Code lässt sich einfacher debuggen, erweitern und optimieren.
Hier gebe ich dir ein paar Anhaltspunkte wie du dabei vorgehen kannst. Du musst nicht alle Punkte sofort umsetzen, aber wenn du merkst, dass du deinen Code nach ein paar Wochen selbst nicht mehr verstehst, dann wird es höchste Zeit, dass du zumindest einige der Prinzipien für sauberen Code umsetzt!
Wenn du import this
in dein Python-Terminal eingibst, bekommst du eine kurze, aber wertvolle Liste von Design-Prinzipien für Python, bekannt als Zen of Python. Diese Hinweise von Tim Peters gelten für Python, aber auch für andere Programmiersprachen. Und ich finde es eine coole Sache, dass die Design und Clean Code Prinzipien quasi in Python eingebaut sind.
import this
# Ausgabe:
"""
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""
Schauen wir uns einige wichtige Punkte nochmal genauer an:
Das sind natürlich alles schöne Schlagworte, aber du wirst dich sicher fragen, wie du das denn genau umsetzten sollst? Schau dir dazu die nächsten Abschnitte an!
Schau dir die folgenden beiden Code-Beispiele an. Welches Beispiel is einfacher zu lesen?
Beispiel 1:
def x(a, b):
return a + b
Beispiel 2:
def addiere_zwei_zahlen(zahl1, zahl2):
return zahl1 + zahl2
Im zweiten Beispiel hat die Funktion einen sinnvollen Namen bekommen. Du hättest die Funktion auch addiere_zahlen()
nennen können. Das ist bereit ein sinnvoller Name. Der Name addiere_zwei_zahlen()
ist allerdings nicht nur sinnvoll, sondern auch präzise.
Gute Namensgebung ist der Schlüssel zu sauberem Code. Variablen, Funktionen und jedwede Objekte solltet du so benennen, dass sie genau das beschreiben, was sie tun. Vermeide kryptische Abkürzungen und sei so präzise wie möglich.
Für Python gibt es eine Reihe von Konventionen zur Namensgebung, sogenannte naming conventions oder naming styles. Die Idee ist, dass du bereits an der Schreibweise erkennst, um welche Art von Funktion, Variable oder Objekt es sich handelt.
Funktionsnamen:
Verwende snake_case für Funktionsnamen. Das bedeutet, dass alle Buchstaben klein geschrieben werden und Wörter durch Unterstriche getrennt sind. Das macht den Code leicht lesbar und vermeidet Missverständnisse.
Beispiele: calculate_overall_profit()
, send_email()
, calculate_interest()
Klassen:
Für Klassen solltest du den CamelCase verwenden, bei dem jedes Wort großgeschrieben wird. Klassennamen sollten immer Substantive sein und den Zweck oder die Rolle der Klasse widerspiegeln.
Beispiele: UserProfile
, BlogPost
Konstanten:
Für Konstanten verwendest du Großbuchstaben. Konstanten sind Werte, die sich während der Laufzeit eines Programmes nicht ändern sollen. Konstanten solltest du an zentraler Stelle deklarieren!
Beispiele: MAX_WIDTH
, PI
, DEBUG_STATE
, DEFAULT_TIMEOUT
Python unterstützt keine Konstanten wie sie etwa aus JavaScript bekannt sind. Es handelt sich lediglich um eine Konvention! Obwohl eine Variable in Großbuchstaben geschrieben ist, kannst du den zugewiesen Wert dennoch ändern! Verwendest du also Code einer Kollegin oder hast dir eine Bibliothek von Github gezogen, dann solltest du Variablen in GROSSBUCHSTABEN nicht während der Laufzeit des Programmes ändern! Ebenso solltest du die Konvention nutzen, um deinen eigenen Code lesbarer zu machen und klar zu machen, dass es sich um einen festen Wert handelt!
Private Variablen und Methoden:
Du solltest einen Unterstrich (_
) vor den Namen setzen, um anzuzeigen, dass eine Variable oder Methode privat ist und nur innerhalb der Klasse oder des Moduls verwendet werden sollte. Dies wird oft gemacht, um Konflikte zu vermeiden oder um anzuzeigen, dass Programmteile isoliert funktionieren sollen.
Beispiele: _helper_function
, _user_data
Zusätzlich gibt es den sogenannten "Double Underscore" oder "Dunder". Wenn du zwei Unterstriche (__
) vor einem Attributnamen setzt, bewirkt das name-mangling, wodurch der Name intern verändert wird. Dadurch kannst du nicht direkt auf das Attribut zugreifen. Das wird oft bei Attributen verwendet, die in einer Vererbungshierarchie geschützt werden sollen.
Module und Paketnamen:
Für Modul- und Paketnamen solltest du ebenfalls snake_case sowie kurze und prägnante Namen verwenden. Außerdem solltest du Kleinschreibung nutzen.
Beispiele: database_manager.py
, data_processing
Endungen für Funktionen und Variablen:
Manchmal ist es sinnvoll, dass du die Funktion oder Eigenschaft einfach durch die Endung klar machst:
_list
für Listen: z. B. user_list
_dict
für Dictionaries: z. B. settings_dict
_str
für Strings: z. B. username_str
Namenskonventionen für Tests
In Tests kannst du das Präfix test_
verwenden, um Testfunktionen zu benennen, damit Test-Frameworks wie pytest
die Tests automatisch erkennen und ausführen können. Die Namen der Testmethoden sollten aussagekräftig sein und den erwarteten Zustand oder das Verhalten widerspiegeln.
Beispiel:
def test_calculate_average():
assert calculate_average([10, 20, 30]) == 20
Neben den Namenskonventionen solltet du dich auch mit der Formatierung von Python Code befassen. Zum Glück gibt es hierfür den PEP 8 Style Guide für Python. PEP-8 legt fest, wie Code aussehen sollte, um konsistent und lesbar zu bleiben. Im folgenden Abschnitt zeige ich dir die wichtigsten PEP-8-Regeln, die du kennen solltest, um sauberen, professionellen Code zu schreiben. Los geht's!
PEP-8 ist die Referenz für sauberen Code. Hier sind Namenskonventionen, Formatierungsregeln und Best Practices festgelegt. Und wenn Code von allen Entwicklern auf die gleiche Art und Weise geschrieben wird, ist es für alle leichter, den Code zu lesen, zu verstehen und zu pflegen. Besonders in großen Teams ist das ein Muss. Ein Code-Style, den jeder kennt, spart Zeit und Nerven.
Eine der wichtigsten Regeln: Verwende 4 Leerzeichen pro Einrückungsebene. Kein Tab, keine 2 Leerzeichen, sondern immer genau 4. Das macht den Code übersichtlich.
def my_function():
if True:
print("Eingerückt mit 4 Leerzeichen!")
Du solltest die Zeilenlänge auf 79 Zeichen beschränken. Natürlich stößt du mit 79 Zeichen schnell an eine Grenze. Allerdings kannst du Code einfach mittels Backslash in mehrere Zeilen aufteilen. Oder du kannst Klammern verwenden, um Code, der sich über mehrere Zeilen erstreckt zu einer Einheit zusammenzufassen. Ich habe das hier genauer erläutert.
Zwischen Klassen und Funktionen sollten zwei Leerzeilen stehen, zwischen Methoden einer Klasse eine Leerzeile. Außerdem kannst du Leerzeilen verwenden, um logische Blöcke voneinander zu trennen.
Sinnvolle Kommentare gehören zu guten Code dazu. Ich habe hier einen ganzen Artikel zum Thema Kommentare geschrieben. Deshalb nur die wichtigsten Punkte:
Da Python dir ziemlich viele Freiheiten lässt, musst du gerade beim Setzen von Leerzeichen auf Konsistenz achten. In diesen Fällen solltest du Leerzeichen vermeiden:
# Falsch:
Meine_Liste = [ 1, 2, 3 ]
# Korrekt:
Meine_Liste = [1, 2, 3]
x = 1
y = 2
# Falsch:
print(x , y)
# Korrekt
print(x, y)
Verwendest du Vergleichsoperatoren, logische Operatoren oder weißt einer Variablen Werte zu, dann solltest du vor und nach dem Operator ein Leerzeichen setzen.
# Falsch:
x=1
print(x>0)
# Korrekt:
x = 1
print(x > 0)
Vermeide freistehende Leerzeichen am Ende der Codezeile. Diese werden oft übersehen. Leerzeichen nach dem Backslash oder line continuation marker führen zu einem Syntax-Fehler.
y = 1 * 2 * 3 * 4 \
* 5 * 6
y = 1 * 2 * 3 * 4 \
^
SyntaxError: unexpected character after line continuation character
Alle Imports sollten am Anfang der Datei stehen. Trenne sie nach drei Kategorien: Standardbibliothek, externe Pakete und eigene Module. Verwende eine neue Zeile für einen neuen Import. Einzelimports aus einem Modul dürfen in einer Zeile stehen.
# Standardbibliothek, separate Zeile pro Import
import os
import sys
import math
# Externe Pakete
import requests
# Eigene Module
import my_module
from my_other_module import x, y, z
Niemand kann alle PEP-8-Regeln immer im Kopf haben. Aber es gibt Tools die dir dabei helfen. Diese Tools überprüfen deinen Code auf PEP-8 Verstöße:
Ein weiteres populäres Tool, dass deinen Code automatisch formatiert ist Black. Black wird von vielen namhaften Organisationen genutzt und ist weit verbreitet und somit sicherlich eine gute Wahl, wenn du einfach mal loslegen willst! Weitere Infos für die Installation findest du hier. Übrigens: Black sowie andere Formatierungstools gibt es auch als Erweiterung für VSCode.
Das DRY-Prinzip besagt, dass du Redundanzen vermeiden solltest. Wenn du denselben Code an mehreren Stellen wiederholst, ist es oft besser, den Code einmal zentral (z.B. als flexible Funktion) zu definieren und diesen wiederzuverwenden.
So sollte es nicht aussehen (Code wiederholt sich):
user_age = 25
if user_age >= 18:
print("User is an adult")
admin_age = 30
if admin_age >= 18:
print("Admin is an adult")
So kann es mit DRY aussehen:
def check_adult(age):
if age >= 18:
print("User is an adult")
check_adult(user_age)
check_adult(admin_age)
Das DRY Prinzip kannst du auch sehr gut mit Hilfe von Klassen umsetzen. Hier lagerst du gemeinsame Funktionalität in eine Basisklasse aus. Anschließend leitest du von der Basisklasse Klassen ab. Wie das genau funktioniert und was du dabei beachten musst, habe ich hier näher beschrieben.
Zusammengefasst: sobald du feststellst, dass du dabei bist, Code zu wiederholen, überlege dir wie du die Wiederholung z.B. mit Hilfe einer Schleife, einer Funktion oder einer Klasse vermeiden kannst. Das hat viele Vorteile:
Fehler passieren, das ist unvermeidlich. Aber es ist wichtig, sie richtig zu behandeln, damit dein Programm nicht abstürzt oder unerwartete Ergebnisse liefert. Vermeide es, generische Exception-Handler zu verwenden, die alles abfangen, und sei stattdessen spezifisch.
Das ist keine gute Idee:
try:
result = 10 / 0
except:
print("Fehler!")
So ist es besser:
try:
result = 10 / 0
except ZeroDivisionError:
print("Teilung durch 0 ist nicht erlaubt!")
pathlib
für Dateisystemoperationen oder argparse
für Argumenten-Parsing.