Kurs
PEP-8, die Python Enhancement Proposal, fasst zentrale Punkte zusammen, mit denen du deinen Code strukturierter und lesbarer machst. Wie Python-Erfinder Guido Van Rossum sagt:
Code wird viel öfter gelesen als geschrieben.
In diesem Beitrag tauchst du mit Codebeispielen in PEP-8 ein! Folgendes steht auf dem Programm:
- Erstmal lernst du PEP-8 kennen: Was ist es und warum brauchst du es?
- Dann geht’s um Einrückungen – ein Dauerbrenner unter Programmiererinnen und Programmierern. Tabs oder Leerzeichen? Die Antwort findest du in diesem Abschnitt.
- Unerwartet, aber wahr: Es gibt Richtlinien zur maximalen Zeilenlänge.
- Außerdem gibt es Empfehlungen zum Umgang mit Leerzeilen.
- Leerzeichen in Ausdrücken und Anweisungen kannst du als Einsteiger leicht richtig setzen.
- Was ist Encoding und warum brauchst du es in Python? Was ist das Standard-Encoding in Python 3? Der Abschnitt zur Quelltextkodierung klärt das.
- Du importierst beim Coden ständig? Hier erfährst du mehr über die Reihenfolge der Imports, absolute und relative Imports sowie Wildcard-Imports usw.
- Dokumentation ist essenziell, um alle Aspekte einer Anwendung nachzuhalten und die Qualität des Endprodukts zu steigern. Kommentare sind hier zentral!
- Kennst du Modul-„Dunder“-Namen? Sie sind besonders hilfreich in Docstrings!
- Zum Schluss geht’s um Benennungsregeln: Wie findest du gute Funktionsnamen, welche Namensstile nutzt man typischerweise und vieles mehr.
- Ist dein Code PEP-8-konform? Diese Frage solltest du dir stellen. Der letzte Abschnitt zeigt Tools, mit denen du prüfst, ob dein Code die hier vorgestellten (und viele weitere) Richtlinien erfüllt.
Einführung in PEP-8
Python hat sich zur bevorzugten Programmiersprache vieler entwickelt. Sie ist relativ leicht zu lernen, unterstützt mehrere Programmierparadigmen, bietet unzählige Open-Source-Module und ist in der Data-Science- und Webentwicklungs-Community ein Standardwerkzeug.
Von diesen Vorteilen profitierst du aber nur, wenn du deine Ideen im Code klar ausdrückst. Python wurde mit bestimmten Zielen entwickelt, die du siehst, wenn du import this eingibst.
import this
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!
Das sind die 20 Prinzipien, die als Zen of Python bekannt sind. Du siehst darin auch „Readability Counts“ – Lesbarkeit zählt. Das sollte beim Schreiben dein Hauptanliegen sein: Andere Entwicklerinnen, Entwickler oder Data Scientists müssen deinen Code verstehen und daran mitarbeiten können, damit die Aufgabe gelöst wird.
In den nächsten Abschnitten erfährst du, wie du genau das erreichst!
Einrückungen
In Python arbeitest du definitiv mit Einrückungen. Sei dabei sorgfältig, sonst drohen Syntaxfehler. Empfohlen sind vier Leerzeichen pro Einrückungsebene. Dieses Statement nutzt zum Beispiel vier Leerzeichen:
if True:
print("If works")
Auch diese for-Schleife mit print ist mit vier Leerzeichen eingerückt:
for element in range(0, 5):
print(element)
Bei langen Ausdrücken ist es am besten, die Zeilen vertikal auszurichten. So entsteht ein „hängender Einzug“ (hanging indent).
Hier ein paar Varianten, wie du hängende Einzüge in großen Ausdrücken nutzt:
-
value = square_of_numbers(num1, num2, num3, num4) -
def square_of_number( num1, num2, num3, num4): return num1**2, num2**2, num3**2, num4**2 -
value = square_of_numbers( num1, num2, num3, num4) -
list_of_people = [ "Rama", "John", "Shiva" ] -
dict_of_people_ages = { "ram": 25, "john": 29, "shiva": 26 }
Früher oder später stellt sich jede Entwicklerin und jeder Entwickler die Frage: Tabs oder Leerzeichen? Die Diskussion ist alt und lebhaft. Lies zum Beispiel diesen Stackoverflow-Artikel.
Im Allgemeinen sind Leerzeichen die erste Wahl. Wenn ein vorhandenes Skript aber Tabs nutzt, bleib dabei. Andernfalls solltest du die Einrückung im gesamten Skript konsequent auf Leerzeichen umstellen.
Hinweis: Python 3 erlaubt kein Mischen von Tabs und Leerzeichen bei Einrückungen. Wähle also eine Variante und bleib konsequent!
Maximale Zeilenlänge
Als Richtwert gilt: 79 Zeichen pro Zeile.
Das hat mehrere Vorteile, zum Beispiel:
- Du kannst Dateien nebeneinander öffnen und vergleichen.
- Du siehst Ausdrücke ohne horizontales Scrollen – das fördert Lesbarkeit und Verständnis.
Kommentare sollten maximal 72 Zeichen pro Zeile haben. Mehr zu Kommentarkonventionen erfährst du weiter unten!
Am Ende hängt es vom Team und Projekt ab, wie strikt du diese Grenze nimmst. In Open-Source-Projekten wirst du die PEP-8-Vorgabe zur Zeilenlänge meist einhalten wollen bzw. müssen.
Beim +-Operator sorgen saubere Zeilenumbrüche für bessere Lesbarkeit:
| So ist es gut … | … das solltest du vermeiden |
|---|---|
|
|
|
Alternativ ginge auch:
total = A
+ B
+ C
Kurz gesagt: Du kannst vor oder nach einem binären Operator umbrechen – Hauptsache, du bleibst konsistent. Bei neuem Code empfiehlt PEP-8, vor dem Operator umzubrechen (wie im letzten Beispiel).
Leerzeilen
Auf oberster Ebene werden Funktionen und Klassen durch zwei Leerzeilen getrennt. Methoden innerhalb von Klassen trennst du durch eine Leerzeile. Das zeigt dieses Beispiel:
import unittest
class SwapTestSuite(unittest.TestCase):
"""
Swap Operation Test Case
"""
def setUp(self):
self.a = 1
self.b = 2
def test_swap_operations(self):
instance = Swap(self.a,self.b)
value1, value2 =instance.get_swap_values()
self.assertEqual(self.a, value2)
self.assertEqual(self.b, value1)
class OddOrEvenTestSuite(unittest.TestCase):
"""
This is the Odd or Even Test case Suite
"""
def setUp(self):
self.value1 = 1
self.value2 = 2
def test_odd_even_operations(self):
instance1 = OddOrEven(self.value1)
instance2 = OddOrEven(self.value2)
message1 = instance1.get_odd_or_even()
message2 = instance2.get_odd_or_even()
self.assertEqual(message1, 'Odd')
self.assertEqual(message2, 'Even')
Die Klassen SwapTestSuite und OddOrEvenTestSuite sind durch zwei Leerzeilen getrennt. Methoden wie .setUp() und .test_swap_operations() jeweils durch eine. Der Code läuft, erzeugt aber keine Ausgabe, da der Testlaufcode fehlt (hier geht es nur um Best Practices).
Leerzeichen in Ausdrücken und Anweisungen
Vermeide unnötige Leerzeichen – diese Gegenüberstellung zeigt, wie es besser ist:
| So ist es gut … | … das solltest du vermeiden |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
oder
oder
|
|
|
|
|
|
|
Diese Beispiele stammen aus PEP-8.
Kodierung von Quelldateien
Ein Computer speichert keine „Buchstaben“, „Zahlen“ oder „Bilder“, sondern nur Bits mit binären Werten: ja oder nein, wahr oder falsch, 1 oder 0 usw. Da ein Computer mit Elektrizität arbeitet, steht ein „Bit“ letztlich für das Vorhandensein oder Fehlen eines elektrischen Impulses, üblicherweise als 1 bzw. 0 dargestellt.
Um mit Bits etwas anderes darzustellen, brauchst du Regeln – ein Kodierungsschema (Encoding), das Bitfolgen in Buchstaben, Zahlen oder Bilder übersetzt. Beispiele sind ASCII, UTF-8 usw.:
- Der American Standard Code for Information Interchange (ASCII) ist das gebräuchlichste Format für Textdateien auf Computern und im Internet. Jedes Zeichen wird durch eine 7-Bit-Zahl dargestellt (eine Folge aus sieben 0en oder 1en).
- Der Unicode-Standard ist ein System zum „Austausch, zur Verarbeitung und Anzeige der Schriftsysteme der modernen Welt“. Kurz: Unicode soll sämtliche bekannten Schriftsysteme abdecken. Unicode nutzt dafür drei Encodings: UTF-8, UTF-16 und UTF-32.
- UTF-16 ist variabel lang: Codepoints werden mit ein oder zwei 16-Bit-Einheiten kodiert.
- UTF-8 ist ebenfalls variabel lang und nutzt ein bis vier 8-Bit-Bytes.
- UTF-32 ist fest 32 Bit pro Unicode-Codepoint lang.
Tipp: Wenn du tiefer einsteigen willst, lies diesen Beitrag.
Warum ist das wichtig?
Strings gehören zu den am häufigsten genutzten Datentypen in Python. Früher oder später arbeitest du mit Zeichen, die nicht im Standard-ASCII enthalten sind – etwa Akzentzeichen wie á, ž, ç usw.
In Python 3 ist UTF-8 das Standard-Source-Encoding. In Python 2 hingegen ist es ASCII.
Was passiert also, wenn ein String ein Nicht-ASCII-Zeichen enthält, etwa "Flügel"?
In Python 2 erhältst du beim Referenzieren des Strings Folgendes:
>>> s
'Fl\xfcgel'
Sieht nicht wie dein String aus! Was passiert beim Drucken?
>>> print(s)
Flügel
Beim Ausgeben siehst du den zugewiesenen Wert. Das Nicht-ASCII-Zeichen ÃŒ wurde kodiert – daher das \xfc bei der Referenz. Zum Umgang damit nutzt du die String-Methoden .encode() und .decode(). .encode() liefert eine 8-Bit-Version des Unicode-Strings im gewünschten Encoding, .decode() interpretiert eine Bytefolge mit dem angegebenen Encoding.
Imports
Bibliotheken und Module zu importieren gehört im Data-Science-Alltag mit Python dazu. Importiere Libraries immer am Anfang deines Skripts.
Hinweis: Bei vielen Imports gilt: Jeder Import in eine eigene Zeile.
Diese Tabelle macht es deutlich:
| So ist es gut … | … das solltest du vermeiden |
|---|---|
|
oder
|
|
Außerdem gibt es eine sinnvolle Import-Reihenfolge, an die du dich halten solltest:
- Standardbibliothek
- Verwandte Third-Party-Pakete
- Lokale Anwendungs-/Bibliotheks-Imports
Absolute und relative Imports
Unterscheide zwischen absoluten und relativen Imports. Absolute Imports sind in Python bevorzugt, da sie lesbarer sind. Mit wachsender Projektkomplexität können auch relative Imports sinnvoll sein. Implizite relative Imports solltest du nie verwenden; sie wurden in Python 3 entfernt.
Was bedeutet das konkret?
-
Ein absoluter Import nutzt den vollständigen Pfad zur Funktion oder Klasse, getrennt durch
.. Zum Beispiel:import sklearn.linear_model.LogisticRegression -
Ein relativer Import bezieht sich auf die Position der aktuellen Datei. Bei größeren Projektstrukturen steigert das oft die Lesbarkeit. Angenommen, deine Struktur sieht so aus:
. ├── __init__.py ├── __init__.pyc ├── __pycache__ │ ├── __init__.cpython-35.pyc │ ├── bubble_sort.cpython-35.pyc │ ├── selection_sort.cpython-35.pyc ├── bubble_sort.py ├── heap_sort.py ├── insertion_sort.py ├── insertion_sort.pyc ├── merge_sort.py ├── merge_sort.pyc ├── quick_sort.py ├── radix_sort.py ├── selection_sort.py ├── selection_sort.pyc ├── shell_sort.py ├── tests │ ├── test1.py
Dann könntest du den Bubblesort-Algorithmus BubbleSort aus bubble_sort.py in test1 relativ so importieren:
from ..bubble_sort import BubbleSort
Mehr zu absoluten und relativen Imports findest du in PEP 328.
Wildcard-Imports
Wildcard-Imports solltest du vermeiden, da sie die Lesbarkeit schmälern. Man sieht nicht, welche Klassen, Methoden oder Variablen tatsächlich genutzt werden, z. B.:
from scikit import *
Kommentare
Kommentare dienen der In-Code-Dokumentation und verbessern das Verständnis. Es gibt vielfältige Tools, um Dokumentation wie Kommentare und Docstrings für eigene Module zu erzeugen. Kommentare sollten so formuliert sein, dass Lesende den Code und sein Zusammenspiel mit anderen Teilen nachvollziehen können.
Kommentare beginnen mit #. Alles dahinter wird vom Interpreter ignoriert. Der folgende Code gibt nur "This is a Python comment" aus.
# This is a Python single line comment
print("This is a Python comment")
Merke: Kommentare sollten maximal 72 Zeichen pro Zeile haben!
Es gibt drei Typen von Kommentaren:
- Block-Kommentare beschreiben komplexeren oder ungewohnten Code. Sie sind meist länger und beziehen sich auf den folgenden Codeblock. Block-Kommentare sind auf gleicher Ebene wie der Code eingerückt. Jede Zeile beginnt mit
#und einem Leerzeichen. Mehrere Absätze trennst du durch eine Zeile mit nur#.
Ein Auszug aus der scikit-learn-Bibliothek zeigt das gut:
if Gram is None or Gram is False:
Gram = None
if copy_X:
# force copy. setting the array to be fortran-ordered
# speeds up the calculation of the (partial) Gram matrix
# and allows to easily swap columns
X = X.copy('F')
- Inline-Kommentare sparsam einsetzen – sie sind nützlich, um einzelne Zeilen zu erläutern oder Kolleginnen und Kollegen Kontext zu geben. Sie stehen in derselben Zeile hinter der Anweisung und beginnen ebenfalls mit
#und einem Leerzeichen.
Beispiel:
counter = 0 # initialize the counter
- Dokumentationsstrings (Docstrings) stehen am Anfang öffentlicher Module, Dateien, Klassen und Methoden. Sie beginnen und enden mit
""":
"""
This module is intended to provide functions for scientific computing
"""
Modulweite Dunder-Namen
Zu Docstrings passen die modulweiten Dunder-Namen – also Namen mit zwei führenden und zwei nachgestellten Unterstrichen. Das sind spezielle Namen, die Python reserviert, damit sie nicht mit benutzerdefinierten Namen kollidieren. Mehr dazu in diesem Artikel.
Modulweite Dunder wie (__all__, __author__, __version__) platzierst du direkt unter dem zentralen Modul-Docstring und vor allen import-Anweisungen. from __future__-Imports stehen vor jedem anderen Code – außer Docstrings:
"""
Algos module consists of all the basic algorithms and their implementation
"""
from __future__ import print
__all__ = ['searching', 'sorting']
__version__ = '0.0.1'
__author__ = 'Chitrank Dixit'
import os
import sys
Tipp: Sieh dir auch die Konventionen für Docstrings an.
Benennungsregeln
Beim Programmieren in Python nutzt du fast immer Benennungsregeln – also Vorgaben, wie Bezeichner für Variablen, Typen, Funktionen und andere Entitäten in Code und Doku gestaltet werden.
Diese Namensstile solltest du kennen:
boder ein einzelner KleinbuchstabeBoder ein einzelner GroßbuchstabelowercaseUPPERCASElower_case_with_underscoresUPPER_CASE_WITH_UNDERSCORESCapitalizedWords, auchCapWords,CamelCaseoderStudlyCapsmixedCaseCapitalized_Words_With_Underscores_single_leading_underscore: schwacher Hinweis auf „intern“.from M import *importiert solche Namen nicht.single_trailing_underscore_: zur Vermeidung von Konflikten mit Python-Schlüsselwörtern, z. B.Tkinter.Toplevel(master, class_='ClassName')__double_leading_underscore: bei Klassenattributen führt zu Name Mangling (in KlasseFooBarwird aus__boo_FooBar__boo).__double_leading_and_trailing_underscore__: „magische“ Objekte oder Attribute im benutzerkontrollierten Namespace, z. B.__init__,__import__,__file__. Solche Namen erfindest du nicht selbst, du nutzt nur dokumentierte.
Allgemeine Benennungsregeln
Diese Tabelle gibt dir grobe Leitplanken für Bezeichner:
| Bezeichner | Konvention |
|---|---|
| Modul | lowercase |
| Klasse | CapWords |
| Funktionen | lowercase |
| Methoden | lowercase |
| Typparameter | CapWords |
| Konstanten | UPPERCASE |
| Paket | lowercase |
- Verwende nicht „l“, „O“ oder „I“ als Einzelbuchstaben-Variablen: In manchen Fonts sehen sie aus wie Ziffern
0bzw.1. - Kurze Namen sind gut – wo nötig, erhöhen Unterstriche die Lesbarkeit.
Ausnahmen von diesen allgemeinen Regeln findest du in diesem Abschnitt.
Ist dein Code PEP-8-konform?
Nach diesem Überblick fragst du dich sicher, wie du prüfst, ob dein Code die Richtlinien einhält (inklusive der vielen Punkte, die hier nicht behandelt wurden).
Neben der Lektüre von PEP-8 selbst lohnt sich ein Blick auf das praktische Modul pep8, das Paket coala und weitere Alternativen in den nächsten Abschnitten!
Python pep8-Paket
Mit pep8 prüfst du deinen Code auf PEP-8-Verstöße und erhältst Vorschläge zur Behebung. Installiere das Modul mit pip:
pip install pep8
Lege zum Testen eine Datei example.py mit folgendem Code an:
def my_function(a, b):
print("The sum of a and b is: ", a + b)
a = 1
b = 2
my_function(a,b)
Speichere die Datei und führe dann im Terminal den Befehl pep8 darauf aus:
pep8 example.py
Die Ausgabe zeigt erkannte PEP-8-Verstöße, zum Beispiel:
example.py:1:1: E302 expected 2 blank lines, found 0
example.py:3:5: E225 missing whitespace around operator
example.py:5:5: E225 missing whitespace around operator
Zeile 1 verlangt zwei Leerzeilen vor der Funktionsdefinition. Die nächsten Zeilen bemängeln fehlende Leerzeichen um den Plus-Operator in print und bei den Zuweisungen.
So behebst du die Punkte:
def my_function(a, b):
print("The sum of a and b is:", a + b)
a = 1
b = 2
my_function(a, b)
Wenn du nun pep8 example.py erneut ausführst, gibt es keine Ausgabe mehr – der Code ist PEP-8-konform.
Mit --show-source kannst du dir die betroffene Quellzeile anzeigen lassen:
$ pep8 --show-source --show-pep8 testsuite/E40.py
testsuite/E40.py:2:10: E401 multiple imports on one line
import os, sys
^
Imports should usually be on separate lines.
Okay: import os\nimport sys
E401: import sys, os
Oder du zeigst mit --statistics, wie oft jeder Fehler auftrat:
$ pep8 --statistics -qq Python-2.5/Lib
232 E201 whitespace after '['
599 E202 whitespace before ')'
631 E203 whitespace before ','
842 E211 whitespace before '('
2531 E221 multiple spaces before operator
4473 E301 expected 1 blank line, found 0
4006 E302 expected 2 blank lines, found 1
165 E303 too many blank lines (4)
325 E401 multiple imports on one line
3615 E501 line too long (82 characters)
612 W601 .has_key() is deprecated, use 'in'
1188 W602 deprecated form of raising exception
Tipp: Schau dir auch flake8, autopep8 oder pylint an!
Codeanalyse mit coala
coala bietet Linting und Fixes für viele Sprachen – hier interessiert uns Python. Installiere coala mit pip:
$ pip3 install coala-bears
Im Befehl siehst du coala-bears: „Bears“ sind Plugins/Module, die coala je nach Sprache erweitern. Für PEP-8 nutzt du PEP8Bear, der Verstöße findet und direkt behebt. Sehr empfehlenswert für deine Python-Checks.
$ coala -S python.bears=PEP8Bear python.files=\*\*/\*.py \
python.default_actions=PEP8Bear:ApplyPatchAction --save
# other output ...
Executing section python...
[INFO][11:03:37] Applied 'ApplyPatchAction' for 'PEP8Bear'.
[INFO][11:03:37] Applied 'ApplyPatchAction' for 'PEP8Bear'.
pep8online: Prüfe deinen Python-Code online
Neben dem pep8-Modul und coala kannst du deinen Code auch auf pep8online prüfen. Einfach Code einfügen, auf „Check code“ klicken und Feedback erhalten. Schnell und praktisch!
Fazit
Bei der Arbeit mit Python leidet die Codequalität manchmal unter dem Druck, Features schnell zu releasen. Die in diesem Tutorial beschriebenen Praktiken – und viele weitere – sollten Teil deines Develop-Staging-Test-Deploy-Zyklus sein. So versteht das ganze Team den Code besser, und Änderungen gelingen oft ohne tiefes Einarbeiten oder Debugging. In Open-Source-Projekten werden sich Beitragende über PEP-8-konformen Code freuen – er ist der gemeinsame Standard, dem Python-Entwicklerinnen und -Entwickler folgen.
Jetzt, da du das Tutorial durchgearbeitet hast, lohnt sich der Blick in PEP-8 im Original. Da gibt es noch viel zu entdecken.
Hast du weitere Tipps zur PEP-8-Konformität oder fehlt dir hier etwas Wichtiges? Sag uns gern Bescheid @DataCamp.