Code muss funktionieren, bevor Sie ihn freigeben. Das stellen Sie mit schlauen Werkzeugen wie PyLint, Flake8 oder Black sicher.
Guten Python-Code schreiben
Teil 1: Python-Programmcode validieren
Teil 2: Code optimieren
Teil 3: Dokumentation prüfen
Guten, lesbaren und funktionierenden Programmcode zu schreiben, fällt nicht immer leicht. Skriptsprachen wie Ruby, Tcl, Perl und Python setzen die Schwelle jedoch sehr niedrig an und erlauben, innerhalb kürzester Zeit ein produktives Ergebnis zu erzielen. Nicht ohne Grund verteidigt Python jährlich wiederkehrend seine Position als beliebteste Programmiersprache [1].
Zu einem Projekt gehören eine Codeanalyse und Bewertung sowie regelmäßige, automatisierte Tests (Unit- und Integration-Tests [2]). Sie helfen, den Programmcode valide zu halten und identifizieren unbenutzte und optimierfähige Programmteile. Letzteres von Hand zu finden und zu bereinigen, gelingt häufig nur bei kleinem, selbstgeschriebenen Code und erweist sich als fehleranfällig. Im Teil 2 dieser kleinen Serie besprechen wir Werkzeuge, die uns dabei helfen, überflüssige Codezeilen aufzuspüren. In Teil 3 widmen wir uns der Dokumentation und prüfen darin Codebeispiele auf Korrektheit.
Hinter der Codeanalyse steht häufig der Wunsch nach einer Einschätzung, wie gut der Programmcode ist und welcher Zeitrahmen und welches Budget man für die weitere Entwicklung, Pflege/Wartung sowie Tests überhaupt einplanen muss. Als Basis der Abschätzung dienen unterschiedliche Softwaremetriken (siehe Kasten “Softwaremetriken”) sowie die Projektlaufzeit, Dokumentation und der Schulungsaufwand für die Nutzer.
Softwaremetriken
Metriken sind Kriterien zur Messbarkeit eines bestimmten Zustands. Bei Software fließt die Größe des Quellcode in Zeilen oder Zeichen mit ein, ebenso die Verständlichkeit und Lesbarkeit des Quellcodes. Zum Einsatz kommen als Verfahren:
- Anzahl der Codezeilen, Lines of Code (LOC),
- die Function-Point-Analyse (FPA) zur Aufwandsabschätzung in der Analysephase,
- Constructive Cost Model (COCOMO) zur Errechnung von Projektkosten aus anderen Kennzahlen,
- die zyklomatische Komplexität (nach McCabe) zur Komplexitätsbestimmung eines Programmmodules,
- die Halstead-Metrik zur Implementierungsabschätzung in der Entwurfsphase,
- Kontrollflussorientierte Metriken (Überdeckungstests) wie Anweisungsüberdeckung, Zweigüberdeckung, Pfadüberdeckung oder Bedingungsüberdeckung.
Die Ergebnisse der einzelnen Tests werden gewichtet und miteinander kombiniert, woraus sich dann das Gesamtergebnis errechnet.
Prägnant formuliert, kommt es bei der Analyse Ihres Programmcodes auf das Worauf, Womit und Wie an. Worauf umfasst dabei verschiedene Softwaremetriken sowie eine Überprüfung hinsichtlich stilistischer, logischer und analytischer Fehler im Code. Womit bezieht sich auf die Analyse durch spezielle Softwarewerkzeuge. Hinter Wie verbirgt sich die statistische und semantische Codeanalyse.
Den drei genannten Punkten gehen wir detaillierter nach und stellen dabei ausgewählte Werkzeuge vor, die Sie bei der Bewertung des Programmcodes unterstützen. Vielfach erstellen die Tools einen abstrakten Syntaxbaum (Abstract Syntax Tree, AST), um damit den Python-Code nach deklarierten, aber ungenutzten Variablen, Funktionen, Klassen und Methoden zu durchforsten. Einige der Werkzeuge führen dabei den Programmcode aus und bieten Zwischenergebnisse. Damit klärt sich nicht nur, ob der Programmcode syntaktisch korrekt ist, sondern auch, ob er das erwartete Ergebnis liefert.
Werkzeuge
Die Tabelle “Werkzeugübersicht zur Prüfung von Code” stellt die verfügbaren, unter einer freien Lizenz stehenden Werkzeuge und deren Funktion gegenüber. Vielfach ergeben sich dabei Überschneidungen in deren Funktionalität, sodass eine saubere Kategorisierung nicht immer gelingt. Wir gehen davon aus, dass Werkzeuge in unserer Auflistung fehlen, die wir noch nicht kennen, und freuen uns über diesbezügliche Ergänzungen. Weitere, ebenso kommerzielle Werkzeuge sammelt die Webseite Analysis Tools ein [3].
|
Aufgabe/Funktion |
Werkzeug/Modul |
|---|---|
|
Code auf Korrektheit prüfen |
Pylint [4], PEP8Online [5], Pythonbuddy [6], Pychecker [7], Flake8 [8], Pylama [9], Pyflakes [10], Py_compile [11], Compileall [12], Ast [13], Prospector [14], Mypy [15], Ruff [16] |
|
Code stilistisch prüfen |
Pylint, PEP8, Flake8, Pylama, Pycodestyle [17], Prospector, Wemake-python-styleguide [18], Dlint [19] |
|
Code gemäß PEP8 formatieren |
|
|
Ungenutzten Code finden |
Vulture [22], Pyflakes, Coverage [23], Uncalled [24], Dead [25], Pychecker, Pycallgraph3 [26], Unimport [27], Pyanalyze [28] |
Korrekt?
Den Schritt der Korrektheitsprüfung übernimmt der Python-Interpreter automatisch, bevor er daraus ausführbaren Bytecode erzeugt. Sie lösen ihn mithilfe der drei Python-Module Py_compile, Compileall und Ast selbst aus. Das erstgenannte Tool kann nur mit einem einzelnen Skript umgehen, Compileall und Cst hingegen kommen mit einem gesamten Verzeichnis voller Skripte zurecht. Die ersten beiden rufen Sie mit dem Parameter -m Modulname auf, beispielsweise für Py_compile und das Python-Skript liste.py so: python3 -m py_compile liste.py.
Abbildung 1 zeigt die Fehlermeldung, die Py_compile nach der Prüfung des Codes ausgibt. Gefunden hat das Modul hier eine fehlende schließende Klammer für eine Liste in der ersten Zeile des Skripts liste.py.
Das Modul Ast analysiert den Code und erstellt daraus einen abstrakten Syntaxbaum (AST). Erneut ließe sich ein Python-Einzeiler als Aufruf nutzen, weitaus besser funktioniert jedoch ein ausgeklügeltes Skript mit erweiterter Fehlerbehandlung (Listing 1). In den Zeilen 3 bis 5 entnimmt Python daraus zunächst den Namen des zu prüfenden Skripts aus dem Aufrufparameter in die Variable dateiname und liest danach den Dateiinhalt in die Variable quellcode ein.
In Zeile 6 setzen Sie die Variable valid auf den Wert True. Damit fungiert sie als Ergebnis, wobei erstmal davon ausgegangen wird, dass sich aus dem zu prüfenden Programmcode überhaupt ein fehlerfreier AST erzeugen lässt (Zeile 8). Gelingt das, erhalten Sie als Ausgabe ein schlichtes True. Geht etwas schief, indem ein Fehler auftaucht, erhält die Variable valid den Wert False. Anschließend wird der Stacktrace mittels der Funktion print_exc() aus dem Modul traceback ausgegeben (Zeile 11). Zudem erscheint am Ende die zusätzliche Ausgabe False (siehe Abbildung 2).
Listing 1
Syntaxprüfung durch Erstellung eines abstrakten Syntaxbaums
import ast, traceback, sys
dateiname = sys.argv[1]
with open(dateiname) as f:
quellcode = f.read()
valid = True
try:
ast.parse(quellcode)
except SyntaxError:
valid = False
traceback.print_exc()
print(valid)
Geht es um mehr als eine reine Syntaxprüfung, kommen andere Werkzeuge wie Pychecker, Pyflakes, Pylint, Pylama oder Prospector ins Spiel. In die Beurteilung des Codes fließen dann zusätzlich stilistische Tests und eine Komplexitätsprüfung mit ein.
Stylecheck
Python gibt Entwicklern sehr großen Freiraum und lässt ihnen bei der Nutzung von Paradigmen und Konzepten absolut die Wahl. Um jedoch für Programmcode eine gewisse Einheitlichkeit in der Schreibweise zu erreichen, nahm die Community bereits 2001 den Verbesserungsvorschlag Python Enhancement Proposal 8 (PEP8) [29] an. Er fällt relativ umfangreich aus: PEP8 begrenzt beispielsweise die Länge einer Codezeile auf 79 Zeichen und die eines Kommentars auf 72 Zeichen, gibt Empfehlungen für Zeilenumbrüche und Leerzeichen sowie für die Anordnung von Operatoren zur besseren Lesbarkeit in Funktionsaufrufen.
Diverse Werkzeuge – PEP8, PEP8 Online, Pylama, Pylint, Pyflakes, Flake8, Pythonbuddy und auch Pycodestyle (als Nachfolger von PEP8) – prüfen den Python-Code gegen die PEP8-Konvention. Als Ergebnis liefern die Tools eine Übersicht, welche Programmkomponenten den Stilvorgaben aus PEP8 bereits entsprechen, und welche noch nicht. Letztere müssen Sie dementsprechend überprüfen.
Listing 2 zeigt ein Skript zur Berechnung der Fakultät. Es enthält dazu die rekursive Funktion fakultaet() (Zeilen 1 bis 7). Als Parameter nimmt diese Funktion einen Zahlenwert entgegen und errechnet daraus über einen rekursiven Funktionsaufruf den Wert für die Fakultät für den Zahlenwert. Zeile 9 enthält eine Zahlenliste aus den vier Werten 15, 4, -8 und 0. In den Zeilen 10 bis 15 durchläuft eine For-Schleife diese Zahlenliste, ruft für jeden Zahlenwert die zuvor definierte Funktion fakultaet() auf und gibt anschließend, sofern möglich, den berechneten Wert für die Fakultät aus, ansonsten eine Fehlermeldung. Nachfolgend werfen wir den Programmcode aus Listing 2 mehreren zuvor genannten Werkzeugen zur Analyse vor und werten aus, wie diese den Programmcode einstufen.
Listing 2
Berechnung der Fakultät
def fakultaet(zahl):
if zahl < 1:
return -1
elif zahl == 1:
return 1
else:
return zahl * fakultaet(zahl - 1)
zahlenliste = [15, 4, -8, 0]
for wert in zahlenliste:
ergebnis = fakultaet(wert)
if ergebnis == -1:
print("Kann keine Fakultät für Werte < 1 berechnen")
else:
print("Fakultät für %i ist %i" % (wert,ergebnis))
PEP8 und PEP8Online
Die naheliegendsten Werkzeuge zur Beurteilung von Programmcode sind PEP8 für die Kommandozeile und das webbasierte Pendant PEP8Online. Zur Analyse ruft die webbasierte Variante im Hintergrund das Kommandozeilenwerkzeug auf – die Ergebnisse sind identisch und unterscheiden sich lediglich in ihrer Darstellung. Abbildung 3 zeigt die Einschätzung des Python-Codes aus Listing 2 durch PEP8Online.
Das Tool bemängelt hierbei nur zwei eher stilistische Schwachstellen: ein fehlendes Leerzeichen zwischen den beiden Aufrufparametern wert und ergebnis in Zeile 15 sowie ein fehlender Zeilenumbruch am Ende derselben Zeile. Ob der Kürze des Ergebnisses wiegt Sie das in Sicherheit, wirkt aber etwas mager und geht besser.
Pylint
Abbildung 4 und Abbildung 5 demonstrieren eine Evaluierung des Programmcodes mittels Pylint – jeweils mit dem Originalcode aus Listing 2 und nach der Umsetzung der Verbesserungsvorschläge. Die Ausgabe von Pylint umfasst Verbesserungsvorschläge aus unterschiedlichen Kategorien (Tabelle “Kategorien zur Beurteilung der Codequalität durch Pylint”) sowie am Ende einen Punktwert aus dem Bereich von 0 bis 10. Je höher der Wert liegt, umso größer fällt die Übereinstimmung mit PEP8 aus, und umso qualitativ höherwertiger ist der Programmcode einzuschätzen.
Rangierte der ursprüngliche Wert bei 6.67 Punkten, ließ sich mit wenigen Schritten ein Maximum von 10 Punkten erreichen. Die Schreibweise der Bedingung if-else in der Funktion vereinfachten wir. Das ermöglicht dem Python-Interpreter, daraus einfacheren Prozessorcode (“processor instructions”) zu generieren, indem intern weniger Sprunganweisungen und passende Labels benötigt werden. Weiterhin lassen sich die Paare aus den Zeilen 5 und 6 sowie 7 und 8 parallel ausführen und verringern die Laufzeit. Die Ergänzung um Dokumentation für das Modul und die Funktion mittels Docstrings (Zeilen 1 und 4) [30] sowie das Nutzen von f-Strings anstatt von C-artigen Platzhaltern (Zeile 17) brachten aufgrund besserer Lesbarkeit zusätzliche Punkte.

Abbildung 5: Der auf der Basis der Vorschläge von Pylint verbesserte Programmcode erzielt die Maximalpunktzahl 10.
|
Kategorie |
Abkürzung |
Beschreibung |
|---|---|---|
|
Informational messages |
I |
Informationen und Anmerkungen (fließen nicht in die Bewertung ein) |
|
Refactor messages |
R |
Code entspricht nicht den Gepflogenheiten |
|
Convention messages |
C |
Code entspricht nicht dem Programmierstandard |
|
Warning |
W |
Warnung zu Stilfehlern oder minimalen Programmschwächen |
|
Error |
E |
Hinweis zu wichtigen Programmschwächen oder Fehlern |
|
Fatal |
F |
Ernsthafte Fehler, die die weitere Ausführung verhindern |
In früheren Veröffentlichungen von Pylint war eine grafische Oberfläche zu dem Kommandozeilenwerkzeug enthalten. Diese setzte auf TK auf und wird seit 2018 nicht mehr gepflegt. Ein äquivalenter Nachfolger zu Pylint-gui ist uns nicht bekannt. Programmierumgebungen wie Visual Studio Code bieten jedoch passende Module zur Erweiterung an [31].
Flake8 und Pylama
Flake8 und Pylama stechen aus der Liste der zuvor genannten Werkzeuge insofern hervor, dass sie mehrere einzelne Werkzeuge miteinander kombinieren. Beide nehmen Pyflakes und Ned Batchelders McCabe-Skript [32] als Grundlage. Flake8 ergänzt dazu noch Pycodestyle, Pylama hingegen PEP8, PEP257 und Pylint. Die damit erzielte Aussage zur Code-Komplexität fällt somit genauer aus als die der Einzelwerkzeuge für sich betrachtet.
Lassen wir unser bereits zuvor verbessertes Skript durch Flake8 und Pylama bewerten, finden beide mithilfe von Pycodestyle noch zwei stilistische Fehler. Außerdem bemängeln die Tools fehlende Leerzeilen nach den Zeilen 3 und 11 zur besseren Lesbarkeit des Python-Codes (siehe Abbildung 6).

Abbildung 6: Die Bewertung durch Flake8 und Pylama fallen wegen der Kombination mehrerer Tools genauer aus.
Vorschläge umsetzen
Die bisher aufgezählten Werkzeuge vergleichen den Programmcode lediglich mit einer Stilfibel, ohne selbst Änderungen daran vorzunehmen. Weitaus invasiver operieren Black und Autopep8. Beide formatieren den Programmcode gemäß den Vorgaben von PEP8 auf der Basis eigener Codestile. Dabei verarbeiten die Tools komplette Dateien, nicht nur einzelne Ausschnitte. Beispielhaft nutzen wir nachfolgend Black.
Als Ausgangspunkt dient die Überprüfung, ob Black in unserem Code überhaupt Änderungen vornehmen würde. Dazu liefert das Werkzeug den Schalter --check mit. Mit dem zusätzlichen Schalter -t (Langform --target-version) stellt sich Black zudem auf eine bestimmte Python-Version ein, für die es den Code anpasst. Für Python 3.9 lautet die Angabe -t py39, für Python 3.12 -t py312. Abbildung 7 zeigt zunächst die Rückmeldung von Black für die Überprüfung, Abbildung 8 die tatsächliche Korrektur auf Python 3.9. In vorliegendem Fall halten sich die Änderungen in Grenzen, sprich: Unser Code ist bereits sehr gut. Black ergänzte lediglich noch zwei Leerzeilen zur besseren Lesbarkeit des Programmcodes.

Abbildung 7: Black meldet nach der Prüfung auf mögliche Korrekturen, dass es eine fehlerhafte Datei identifiziert hat.
Zusammenfassung und Fazit
In unseren Tests mit Programmcode aus verschiedenen Quellen waren die Änderungen mehr oder weniger stark, aber stets mit einem konsistenten Resultat. Aus unserer Sicht helfen Black und Autopep8 dabei, Programmcode unterschiedlicher Entwickler (an demselben Projekt) stilistisch zu vereinheitlichen. Über entsprechende Plugins integrieren Sie beide außerdem in IDEs und Editoren wie Emacs, Vim, Atom, Pycharm oder Visual Studio Code. Als Pre-commit [33] erfüllen die Werkzeuge ebenso zuverlässig ihre Funktion bei Git, beispielsweise in Kombination mit Flake8 [34].
Nicht mehr ignorieren lässt sich der Hype um künstliche Intelligenz und die Idee, damit Programmcode zu verbessern. Hier sticht Github Copilot [35] heraus, das Empfehlungen auf Basis von Programmcode aus anderen, öffentlich verfügbaren Github-Repositories gibt. Der Tenor zu den Ergebnissen der Anwendung von Github Copilot erscheint gemischt, aber überwiegend positiv, sofern es als Anregung und Inspiration aufgenommen wird.
Die Fragen, die sich für uns dabei stellen, betreffen Urheberschaft und Lizenz. Entlehnte Codefragmente aus anderen Repositories kennzeichnet Github Copilot nicht konsequent als solche [36], ebenso die Lizenz des vorgeschlagenen Fragments. Unklar ist außerdem, ob es sich um die korrekte Lizenz handelt – Programmcode kann mehrfach lizenziert sein, etwa unterschiedlich für kommerzielle und nicht-kommerzielle Nutzung. Davon hängt maßgeblich ab, ob die Empfehlung von Github Copilot im konkreten Fall überhaupt im eigenen Projekt zum Einsatz kommen darf. Ohne manuelle Überprüfung des Vorschlags lauert hier die Falle der Urheberrechts- und Lizenzverletzung.
Nun liegt uns validierter Code vor – eine große Verbesserung verglichen mit dem Ausgangszustand. Diese Situation nutzen wir, um den Code im Folgeschritt dahingehend noch zu überprüfen, ob dieser überflüssige Zeilen enthält. Im zweiten Teil dieser Artikelserie rücken wir Werkzeuge und Methoden in den Mittelpunkt, die Ihnen dabei helfen, den Code zu verschlanken. (csi)
Danksagung
Der Autor bedankt sich bei Saif du Plessis für seine Hilfe und Kritik bei der Vorbereitung des Artikels.
Infos
-
Die beliebtesten Programmiersprachen 2021: https://www.netzwoche.ch/news/2021-08-30/das-sind-die-beliebtesten-programmiersprachen-2021
-
Anthony Shaw: Getting Started With Testing in Python: https://realpython.com/python-testing/
-
Analysis Tools: https://analysis-tools.dev/tag/python
-
Pylint: https://pylint.org/
-
PEP8Online: http://pep8online.com/
-
Pythonbuddy: https://github.com/ethanchewy/PythonBuddy
-
Pychecker: http://pychecker.sourceforge.net/
-
Flake8: https://github.com/pycqa/flake8
-
Pylama: https://github.com/klen/pylama
-
Pyflakes: https://pypi.org/project/pyflakes/
-
Pythonmodul Py_compile: https://docs.python.org/3/library/py_compile.html
-
Pythonmodul Compileall: https://docs.python.org/3/library/compileall.html
-
Pythonmodul Ast: https://docs.python.org/3/library/ast.html
-
Prospector: https://pypi.org/project/prospector/
-
Pycodestyle: https://pycodestyle.pycqa.org/en/latest/
-
Wemake-python-styleguide: https://wemake-python-stylegui.de/
-
Autopep8: https://github.com/hhatto/autopep8
-
Coverage: https://pypi.org/project/coverage/
-
Uncalled: https://pypi.org/project/uncalled/
-
Pycallgraph3: https://pypi.org/project/pycallgraph3/
-
Unimport: https://pypi.org/project/unimport/
-
Pyanalyze: https://pypi.org/project/pyanalyze/
-
Python Enhancement Proposal 8 – PEP-8-Style-Guide for Python Code: https://www.python.org/dev/peps/pep-0008/
-
Aditya Sharma: Docstrings in Python Tutorial: https://www.datacamp.com/tutorial/docstrings-python
-
Pylint-Erweiterung für Visual Studio Code: https://marketplace.visualstudio.com/items?itemName=ms-python.pylint
-
McCabe-Skript: https://pypi.org/project/mccabe/
-
Pre-Commit: https://pre-commit.com/
-
Lj Miranda: Automate Python workflow using pre-commits: black and flake8: https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/
-
Github Copilot: https://github.com/features/copilot
-
KI-Assistent Github Copilot will Code nicht klauen, sondern sauber referenzieren: https://www.heise.de/news/KI-Assistent-Github-Copilot-will-Code-nicht-klauen-sondern-sauber-referenzieren-9234532.html
-
Axel Beckert, Frank Hofmann: Debian-Paketmanagement-Buch: https://dpmb.org/
-
Frank Hofmann arbeitet meist von unterwegs aus als Entwickler, Trainer und Autor. Er gehört zu den Verfassern des Debian-Paketmanagement-Buchs [37].










