Wie man lesbaren Code schreibt mit PEP8 – Teil 5

Leerraum („whitespace“) in Ausdrücken und Anweisungen

„Spärlich ist besser als dicht.“

– Das Zen von Python

Leerraum kann in Ausdrücken und Anweisungen sehr hilfreich sein, wenn er richtig verwendet wird. Wenn zu wenig Leerraum vorhanden ist, kann der Code schwer zu lesen sein, weil alles zusammengewürfelt ist. Wenn zu viel Leerraum vorhanden ist, kann es schwierig sein, zusammengehörige Begriffe in einer Anweisung visuell zu kombinieren.

Leerraum um binäre Operatoren

Man umgibt die folgenden binären Operatoren mit einem einzelnen Leerzeichen auf jeder Seite:

  • Zuweisungsoperatoren (=, +=, -=, usw.) 
  • Vergleiche (==, !=, >, <. >=, <=) und (is, is not, in, not in) 
  • Boolesche Operatoren (and, not, or) 

Hinweis: Wenn = verwendet wird, um einem Funktionsargument einen Standardwert zuzuweisen, darf man es nicht mit Leerzeichen umgeben.

# empfohlen
def function(default_parameter=5):
    # ...

# nicht empfohlen
def function(default_parameter = 5):
    # ...

Wenn eine Anweisung mehr als einen Operator enthält, kann das Hinzufügen eines einzelnen Leerzeichens vor und nach jedem Operator verwirrend wirken. Stattdessen ist es besser, nur um die Operatoren mit der niedrigsten Priorität Leerzeichen einzufügen, insbesondere bei mathematischen Formeln. Hier sind ein paar Beispiele:

# empfohlen
y = x**2 + 5
z = (x+y) * (x-y)

# nicht empfohlen
y = x ** 2 + 5
z = (x + y) * (x - y)

Man kann dies auch auf if-Anweisungen anwenden, die mehrere Bedingungen enthalten:

# nicht empfohlen
if x > 5 and x % 2 == 0:
    print('x ist größer als 5 und durch 2 teilbar!')

Im obigen Beispiel hat der Operator ‚and‘ die niedrigste Priorität. Es könnte daher klarer sein, die if-Anweisung wie folgt auszudrücken:

# empfohlen
if x>5 and x%2==0:
    print('x ist größer als 5 und durch 2 teilbar!')

Es steht frei auszuwählen, was lesbarer ist, mit dem Vorbehalt, dass man auf beiden Seiten des Operators die gleiche Menge an Leerzeichen verwenden muss.

Das Folgende ist nicht akzeptabel:

# Dies sollte man auf gar keinen Fall tun!
if x >5 and x% 2== 0:
    print('x ist größer als 5 und durch 2 teilbar!')

In Slices fungieren Doppelpunkte als binäre Operatoren. Daher gelten die im vorigen Abschnitt beschriebenen Regeln, und es sollte auf beiden Seiten die gleiche Menge an Leerzeichen vorhanden sein. Die folgenden Beispiele für Listen-Slices sind gültig:

list[3:4]

# Den Doppelpunkt als den Operator mit der niedrigsten Priorität behandeln
list[x+1 : x+2]

# In einem erweiterten Slice müssen beide Doppelpunkte
# von der gleichen Menge an Leerzeichen umgeben sein
list[3:4:5]
list[x+1 : x+2 : x+3]

# Das Leerzeichen wird weggelassen, wenn ein Slice-Parameter weggelassen wird
list[x+1 : x+2 :]

Zusammenfassend lässt sich sagen, dass man die meisten Operatoren mit Leerzeichen umgeben sollte. Es gibt jedoch einige Einschränkungen dieser Regel, z. B. in Funktionsargumenten oder wenn man mehrere Operatoren in einer Anweisung kombiniert.

Wann Leerzeichen zu vermeiden sind

In manchen Fällen kann das Hinzufügen von Leerzeichen die Lesbarkeit von Code erschweren. Zu viel Leerraum kann den Code übermäßig spärlich und schwer zu verfolgen machen. PEP 8 beschreibt sehr klare Beispiele, wo Leerzeichen unangebracht sind.

Die wichtigste Stelle, an der keine Leerzeichen hinzugefügt werden sollten, ist das Ende einer Zeile. Dies wird als „trailing whitespace“ bezeichnet. Es ist unsichtbar und kann zu Fehlern führen, die schwer zu finden sind.

In der folgenden Liste sind einige Fälle aufgeführt, in denen man das Hinzufügen von Leerzeichen vermeiden sollte:

– Unmittelbar innerhalb von runden, eckigen oder geschweiften Klammern:

# empfohlen
my_list = [1, 2, 3]

# nicht empfohlen
my_list = [ 1, 2, 3, ]

– Vor einem Komma, Semikolon oder Doppelpunkt:

x = 5
y = 6

# empfohlen
print(x, y)

# nicht empfohlen
print(x , y)

– Vor der offenen Klammer, mit der die Argumentliste eines Funktionsaufrufs beginnt:

def double(x):
    return x * 2

# empfohlen
double(3)

# nicht empfohlen
double (3)

– Vor der offenen Klammer, mit der ein Index oder Slice beginnt:

# empfohlen
list[3]

# nicht empfohlen
list [3]

Zwischen einem nachgestellten Komma und einer schließenden Klammer:

# empfohlen
tuple = (1,)

# nicht empfohlen
tuple = (1, )

Um Zuweisungsoperatoren auszurichten:

# empfohlen
var1 = 5
var2 = 6
some_long_var = 7

# nicht empfohlen
var1          = 5
var2          = 6
some_long_var = 7

Empfehlungen zur Programmierung

„Einfach ist besser als komplex.“

– Das Zen von Python

Man wird oft feststellen, dass es mehrere Möglichkeiten gibt, eine ähnliche Aktion in Python (und jeder anderen Programmiersprache) auszuführen. In diesem Abschnitt findet man einige der Vorschläge, die PEP 8 anbietet, um diese Mehrdeutigkeit zu beseitigen und Konsistenz zu wahren.

Oft wird man prüfen müssen, ob ein boolescher Wert True oder False ist, dazu sollte man die Werte aber nicht mit dem Äquivalenzoperator vergleichen, auch wenn es intuitiv erscheint, wie folgenden Beispiel:

# nicht empfohlen
my_bool = 6 > 5
if my_bool == True:
    return '6 is bigger than 5'

Die Verwendung des Äquivalenzoperators == ist hier unnötig. bool kann nur die Werte True oder False annehmen. Es genügt, die folgende Abfrage zu verwenden:

# empfohlen
if my_bool:
    return '6 is bigger than 5'

Diese Art, eine if-A nweisung mit einem Booleschen Wert auszuführen, erfordert weniger Code und ist einfacher, weshalb PEP 8 sie fördert.

Ebenso nutzt man die Tatsache, dass leere Sequenzen False sind. Wenn man prüfen will, ob eine Liste leer ist, kann man versucht sein, die Länge der Liste zu prüfen. Wenn die Liste leer ist, hat sie die Länge 0, was in einer if-Anweisung gleichbedeutend mit False ist. Hier ist ein Beispiel:

# nicht empfohlen
my_list = []
if not len(my_list):
    print('Liste ist leer!')

In Python ist jedoch jede leere Liste, Zeichenkette oder jedes leere Tupel False. Daher gibt es eine einfachere Alternative zu der obigen Lösung:

# empfohlen
my_list = []
if not my_list:
    print('Liste ist leer!')

Während beide Beispiele „Liste ist leer!“ ausgeben, ist die zweite Option einfacher und wird daher von PEP 8 unterstützt.

Man verwendet ‚is not‘ anstelle von ’not … is‘ in if-Anweisungen. Will man prüfen, ob eine Variable einen definierten Wert hat, gibt es zwei Möglichkeiten. Die erste ist, eine if-Anweisung mit x is not None auszuwerten, wie im folgenden Beispiel:

# empfohlen
if x is not None:
    return 'x exists!'

Eine zweite Möglichkeit wäre, x is None und dann eine if-Anweisung auf der Grundlage von ’nicht das Ergebnis‘ zu bewerten:

# nicht empfohlen
if not x is None:
    return 'x exists!'

Während beide Optionen korrekt ausgewertet werden, ist die erste einfacher, weshalb PEP 8 sie unterstützt.

Man sollte ‚if x:‘ nicht verwenden, wenn man ‚if x is not None:‘ meint. Es kann vorkommen, dass man eine Funktion mit Argumenten hat, die standardmäßig None sind. Ein häufiger Fehler bei der Überprüfung, ob ein solches Argument, einen anderen Wert erhalten hat, ist folgende Verwendung:

# nicht empfohlen
if arg:
    # Do something with arg...

Dieser Code prüft, ob arg True ist. Stattdessen will man prüfen, dass arg nicht None ist, also wäre es besser, das Folgende zu verwenden:

# empfohlen
if arg is not None:
    # Do something with arg...

Der Fehler, der hier gemacht wird, ist die Annahme, dass not None und True gleichwertig sind. Man hätte arg = [] setzen können. Wie wir oben gesehen haben, werden leere Listen in Python als False ausgewertet. Obwohl also das Argument arg zugewiesen wurde, ist die Bedingung nicht erfüllt, so dass der Code innerhalb der if-Anweisung nicht ausgeführt wird.

Man sollte .startswith() und .endswith() verwenden anstelle von slicing. Wenn man prüfen will, ob eine Zeichenkette mit dem Wort ‚cat‘ als Präfix oder Suffix vorkommt, könnte es sinnvoll sein, List Slicing zu verwenden. List Slicing ist jedoch fehleranfällig, und man muss die Anzahl der Zeichen im Präfix oder Suffix hart codieren. Außerdem ist es für jemanden, der mit dem List Slicing in Python nicht so vertraut ist, nicht klar, was man damit erreichen will:

# nicht empfohlen
if word[:3] == 'cat':
    print('The word starts with "cat"')

Dies ist jedoch nicht so gut lesbar wie die Verwendung von .startswith():

# empfohlen
if word.startswith('cat'):
    print('The word starts with "cat"')

Dasselbe Prinzip gilt auch für die Prüfung auf Suffixe. Das folgende Beispiel zeigt, wie man prüfen kann, ob eine Zeichenkette mit jpg endet:

# nicht empfohlen
if file_name[-3:] == 'jpg':
    print('The file is a JPEG')

Das Ergebnis ist zwar korrekt, aber die Schreibweise ist etwas schwer zu lesen. Stattdessen kann man .endswith() wie im folgenden Beispiel verwenden:

# empfohlen
if file_name.endswith('jpg'):
    print('The file is a JPEG')

Wie bei den meisten dieser Programmierempfehlungen ist das Ziel die Lesbarkeit und Einfachheit. In Python gibt es viele verschiedene Möglichkeiten, ein und dieselbe Aktion auszuführen, daher sind Richtlinien darüber, welche Methoden zu wählen sind, hilfreich.

Wann sollte man PEP 8 ignorieren?

Die kurze Antwort auf diese Frage lautet: niemals. Wenn man PEP 8 buchstabengetreu befolgt, kann man garantieren, dass man sauberen, professionellen und lesbaren Code haben wird. Davon profitieren sowohl man selbst als auch Kollegen.

Allerdings sind einige Richtlinien in PEP 8 in den folgenden Fällen unpraktisch:

  • Wenn die Einhaltung von PEP 8 die Kompatibilität mit bestehender Software beeinträchtigen würde. 
  • Wenn der Code, an dem man gerade arbeitet, nicht mit PEP 8 vereinbar ist. 
  • Wenn der Code mit älteren Versionen von Python kompatibel bleiben muss.