AA_snake_sxc1063321_HansThoursie.jpg

© Hans Thoursie, sxc.hu

Falsche Schlange?

Typische Fehler in Python-Code vermeiden

17.01.2011
Python ist einfach – aber nicht so einfach, dass man es allein durch Lesen von Code lernen könnte. Mit etwas Hintergrundwissen vermeiden Sie typische Fehler in Python-Code und schreiben so bessere Programme.

Python ist eine relativ leicht zu erlernende Sprache, mit der man in kurzer Entwicklungszeit viel erreichen kann. Das verführt vor allem Einsteiger dazu, Python [1] nur durch Anschauen von Python-Code zu "lernen", was oft unangenehme Überraschungen nach sich zieht: Python verhält sich nicht unbedingt so, wie man es von den jeweils bisher gelernten Sprachen kennt. Fehler, die zügig zu einer unwarteten Ausnahme mit einem Traceback führen, stellen da noch das geringste Problem dar. Kniffliger sind Fehler in Gestalt von Code, der nur scheinbar das Gewünschte tut.

Dieser Artikel spricht solche Missverständnisse zur Funktionsweise von Python an und erklärt die Hintergründe. Darüber hinaus gibt er diverse Tipps, um kompakten und dennoch lesbaren Code zu schreiben. Der Schwerpunkt des Artikels liegt auf Python 2.x, da sich diese Versionen noch wesentlich häufiger im Einsatz befinden, unter anderem für Systemprogramme diverser Linux-Distributionen.

Einrückungen: Vorsicht, Tabulator!

Python erkennt einen Anweisungsblock wie etwa einen Schleifenrumpf an seiner einheitlichen Einrückung. Dabei empfiehlt der "Style Guide for Python Code" [2] eine Einrücktiefe von vier Zeichen pro logischer Ebene und das ausschließliche Verwenden von Leerzeichen zum Einrücken.

Probleme treten unter Umständen dann auf, wenn Sie sowohl Leerzeichen als auch Tabulatorzeichen zur Einrückung nutzen. Für den Python-Interpreter entspricht ein Tabulatorzeichen acht Leerzeichen beziehungsweise füllt auf das nächste Vielfache von acht Leerzeichen auf. Betrachtet man einen Quelltext mit einer anderen Tabulator-Weite, sieht man unter Umständen eine andere Logik im Code, als sie der Interpreter wahrnimmt.

Ein Beispiel zeigt das Listing 1. Die vier Zeichen breite Sequenz >--- steht hier (in Anlehnung an den beliebten Editor Vim) für einen Tabulator, den der Editor mit vier Zeichen Breite darstellt. Auf den ersten Blick scheint Python die Anweisung b = h(b) auf jeden Fall auszuführen. Da ein Tabulatorzeichen aber acht Leerzeichen entspricht, bearbeitet der Interpreter diese Zeile wie die vorherige aber nur im Fall a > b.

Listing 1

def f(a, b):
    if a > b:
        a = g(a)
>---b = h(b)
    return a, b

Glück im Unglück: Das Mischung von Leerzeichen und Tabulatoren führt eher zu Syntaxfehlern als zu falsch funktionierendem Code. Ein Beispiel zeigt Listing 2. Hier ist aus Sicht von Python das Schlüsselwort else genauso weit eingerückt wie die Zuweisungen an a und b – ein Syntaxfehler.

Listing 2

def f(a, b):
    if a > b:
        a = g(a)
>---else:
        b = h(b)
    return a, b

Um die potenzielle Verwirrung durch einen Mix von Leerzeichen und Tabulatoren von vornherein zu vermeiden, lassen Sie den Editor auch bei Drücken der Tabulatortaste immer mit vier Stellen pro logischer Ebene einrücken. Natürlich wären auch andere Regeln denkbar, aber vier Leerzeichen sind wie gesagt die im "Style Guide" empfohlene Variante und daher in den allermeisten Python-Projekten üblich.

Haben Sie Code im Verdacht, inkonsistent eingerückt zu sein, machen Sie mittels des Kommandos

$ find . -name "*.py" -exec grep -EnH "\t" {} \;

alle Python-Dateien im und unterhalb des aktuellen Verzeichnisses ausfindig, die Tabulatorzeichen enthalten. Geht es nur um wenige Dateien, ist es praktikabel, im Editor Tabulatorzeichen explizit anzuzeigen. In Vim geht das mit dem Befehl :set list (Abbildung 1). Viele andere Programmiereditoren bieten ähnliche Möglichkeiten.

Abbildung 1: Zwei Ansichten der gleichen fehlerhaften Datei in Vim, oben ohne, unten mit gesetzter list-Option. Diese macht Tabulator- und Zeilenende-Zeichen sichtbar.

Objekte und Namen: Who is who

In Python gibt es keine Variablen wie bei C oder Pascal, die einen bestimmten Speicherbereich kennzeichnen. Die Python-Zuweisung a = 1 verknüpft lediglich das Ganzzahl-Objekt 1 mit dem Namen a verknüpft. Ein und dasselbe Objekt lässt sich prinzipiell unter beliebig vielen Namen erreichen. Es gibt auch anonyme (namenlose) Objekte, wie die Zahl 2 in der Anweisung L = [1, 2, 3].

Wichtig zum Verständnis von Python sind die Konzepte der Identität und der Gleichheit. Identität bedeutet, dass es sich um ein und dasselbe Objekt handelt, während Gleichheit zweier Objekte aussagt, dass diese den gleichen Wert haben. Ob zwei Objekte identisch sind, stellen Sie mithilfe des Operators is fest (Listing 3).

Listing 3

x = 1.0
y = x  # Das Objekt 1.0 mit dem Namen x ist
       # jetzt auch über den Namen y erreichbar.
print x is y  # True
print x == y  # True
y = 1
print x is y  # False
print x == y  # True, 1.0 bezeichnet den gleichen Wert wie 1

Für von Ihnen definierte Datentypen legen Sie selbst fest, wie Gleichheit zwischen deren Instanzen definiert ist. Wie Listing 4 verdeutlicht, könnten Sie auf diese Weise sogar dafür sorgen, dass zwei Objekte zwar identisch, aber dennoch ungleich sind!

Listing 4

class ImmerUngleich(object):
    def __eq__(self, rechte_seite):
        """Definiere Verhalten des Operators ==."""
        return False
x = ImmerUngleich()
y = x
print x is y  # True
print x == y  # False

In Python-Code bedeutet der Wert None üblicherweise, dass ein Name keinen "richtigen" Wert hat, zum Beispiel weil er bisher nicht explizit initialisiert wurde. Prüfungen eines Namens auf None sollten Sie immer mit is vornehmen, nicht mit ==. Analog zu Listing 4 kann eine Klasse ja den Vergleichsoperator so definieren, dass alle Vergleiche einen wahren Wert liefern (Listing 5). Nur die Operation in der letzten Zeile des Listings, die is verwendet, funktioniert zuverlässig.

Listing 5

class ImmerGleich(object):
    def __eq__(self, rechte_seite):
        """Definiere Verhalten des Operators ==."""
        return True
x = ImmerGleich()
print x == None  # True, obwohl x nicht None ist
print None == x  # True, obwohl x nicht None ist
print x is None  # False

Wie schon erwähnt, führt eine Zuweisung nur zur Verknüpfung von Namen und Objekten. Objekte werden dabei nicht kopiert. Das erklärt auch das Ergebnis in Listing 6, das bei Python-Einsteigern immer wieder für Überraschungen sorgt.

Listing 6

L1 = [1, 2]
L2 = L1
L1.append(3)
print L1        # [1, 2, 3]
print L2        # ebenfalls [1, 2, 3]
print L1 is L2  # True

Die erste Zuweisung erzeugt eine Liste mit den Elementen 1 und 2, die zweite Zuweisung gibt der derart erzeugten Liste einen zweiten Namen L2. L1 und L2 stehen nun für ein- und dasselbe Objekt. Die dritte Anweisung hängt die Zahl 3 ans Ende der Liste. Da L2 nur ein anderer Name für dieselbe Liste ist, bekommen Sie bei der Ausgabe von L1 und L2 auch ein identisches Ergebnis. Die letzte Zeile bestätigt das vorher Gesagte: L1 und L2 bezeichnen ein und dieselbe Liste.

Objekt-Verknüpfungen können natürlich auch komplexer ausfallen. So greifen Sie nach den ersten drei Anweisungen in Listing 7 auf die erzeugte Liste sowohl unter dem zuerst vergebenen Namen L als auch über das Tupel t zu.

Listing 7

L = [1]
t = (L,)  # Tupel mit der Liste als einzigem Element
L.append(2)
print L          # [1, 2]
print t[0]       # [1, 2]
print L is t[0]  # True

LinuxCommunity kaufen

Einzelne Ausgabe
 
Abonnements
 

Ähnliche Artikel

Kommentare

Infos zur Publikation

LU 12/2014: ANONYM & SICHER

Digitale Ausgabe: Preis € 4,95
(inkl. 19% MwSt.)

Mit der Zeitschrift LinuxUser sind Sie als Power-User, Shell-Guru oder Administrator im kleinen Unternehmen monatlich auf dem aktuelle Stand in Sachen Linux und Open Source.

Sie sind sich nicht sicher, ob die Themen Ihnen liegen? Im Probeabo erhalten Sie drei Ausgaben zum reduzierten Preis. Einzelhefte, Abonnements sowie digitale Ausgaben erwerben Sie ganz einfach in unserem Online-Shop.

NEU: DIGITALE AUSGABEN FÜR TABLET & SMARTPHONE

HINWEIS ZU PAYPAL: Die Zahlung ist auch ohne eigenes Paypal-Konto ganz einfach per Kreditkarte oder Lastschrift möglich!       

Tipp der Woche

Ubuntu 14.10 und VirtualBox
Ubuntu 14.10 und VirtualBox
Tim Schürmann, 08.11.2014 18:45, 0 Kommentare

Wer Ubuntu 14.10 in einer virtuellen Maschine unter VirtualBox startet, der landet unter Umständen in einem Fenster mit Grafikmüll. Zu einem korrekt ...

Aktuelle Fragen

Nach Ubdates alles weg ...
Maria Hänel, 15.11.2014 17:23, 4 Antworten
Ich brauche dringen eure Hilfe . Ich habe am wochenende ein paar Ubdates durch mein Notebook von...
Brother Drucker MFC-7420
helmut berger, 11.11.2014 12:40, 1 Antworten
Hallo, ich habe einen Drucker, brother MFC-7420. Bin erst seit einigen Tagen ubuntu 14.04-Nutzer...
Treiber für Drucker brother MFC-7420
helmut berger, 10.11.2014 16:05, 2 Antworten
Hallo, ich habe einen Drucker, brother MFC-7420. Bin erst seit einigen Tagen ubuntu12.14-Nutzer u...
Can't find X includes.
Roland Welcker, 05.11.2014 14:39, 1 Antworten
Diese Meldung erhalte ich beim Versuch, kdar zu installieren. OpenSuse 12.3. Gruß an alle Linuxf...
DVDs über einen geeigneten DLNA-Server schauen
GoaSkin , 03.11.2014 17:19, 0 Antworten
Mein DVD-Player wird fast nie genutzt. Darum möchte ich ihn eigentlich gerne abbauen. Dennoch wür...