Was nicht so geht, wie erwartet ...

Zurück zur Übersicht

 

Die Python - Dokumentation wächst und wächst. Immer mehr Module kommen in der Standardausstattung hinzu. Immer mehr Funktionen werden implementiert. Man kann sich kaum vorstellen, dass Python noch besser wird, als es schon ist, aber es geht tatsächlich! Und es wächst weiter.

Da nimmt es nicht wunder, dass auch mal etwas nicht so geht, wie erwartet. Oft ist es ein Veständnisproblem, weil die Funktion irgendeiner Sache nicht so gemeint war, wie der unbedarfte Programmierer es vielleicht aufgefasst hat. Aber es gibt auch andere Sachen, die wirklich nicht so gehen, wie beschrieben. Es sind oft auch nicht die lebensnotwendigen Dinge, sondern selten benötigte Funktionen. Möglicherweise liegt es auch an den rasch aufeinanderfolgenden Versionen von Python, den in dichter Folge einschlagenden neuen Ideen ...

Die hier nachfolgend beschrieben Einzelbeispiele will ich hier zur Diskussion stellen. Ich fange erstmal mit wenigen Beispielen an. Immer wenn mir wieder was auffällt, füge ich das dann hinzu. Sollte ich mich irgendwie irren, weil ich etwas nicht beachtet oder falsch interpretiert habe, bin ich für jede Aufklärung dankbar. Schließlich geht es um meine Lieblingsprogrammiersprache :-).


Fall 1:

Hier geht es um die Standardein- und ausgabe. In der Referenz zur Standardbibliothek (nachzulesen in der deutschen Ausgabe von M&T Seite 93) wird eine Beispielfunktion abgedruckt, die praktisch dasselbe macht, wie die Funktion raw_input(). Sie lautet so:

import sys

def getstring():
    text = ""
    while 1:
        c = sys.stdin.read(1)
        text = text + c
        if c == '\n':
            break
    return text

Sie ist ein Beispiel dafür, wie man byteweise Zeichen einlesen kann. Wenn man sie so im Ganzen ausprobiert, geht es auch scheinbar, denn der gesamte String wird brav zurückgegeben. Sie erweckt aber den Eindruck, dass mit read(1) die Zeichen einzeln aus dem Tastaturpuffer genommen werden. Als ich diese Funktion vorfand, freute ich mich riesig, wollte ich doch mit einfachen Mitteln dem Nutzer ein Menue anbieten, dass durch Druck einer Zifferntaste die gewünschte Funktion gleich ausführt, ohne auf <Enter> zu warten. Denkste!! Es ging nicht. Als ich nach dem read(1) - Befehl dann mal ein print c einfügte, sah ich die Bescherung. Der Tastaturpuffer sammelt alle Tastenanschläge und läßt sie dann im Block nach <Enter> auf unser armes Python los! Na unter diesen Bedingungen nützt mir das byteweise Auslesen natürlich nichts! Es wäre ja schön gewesen, weil es plattformunabhängig geblieben wäre. Nun bleibt wieder nur "curses unter Linux" und "WConio unter Windows" anzuwenden.

 

Fall 2:

In der Zusatzdokumentation für curses (zu finden bei www.python.org), werden vordefinierte Konstante für Sondertasten bereitgehalten. Diese Werte werden aber bei Betätigung dieser Tasten nicht angeboten, sondern es kommen dafür Einzelbytes, und die sind für verschiedene Python - Versionen auch noch unterschiedlich! Im abgdruckten Beispiel wird aber locker die Eingabe mit getch() eingesetzt, die nun wirklich keine Integerwerte bringt. Alles kein Problem, läßt sich alles programmiertechnisch lösen. Aber die Doku stimmt da irgendwie nicht.

 

Fall 3:

Diesmal geht es mal nicht um die Dokumentation, die ist hier korrekt, aber die Überraschung lauert wieder für den alten Pascalprogrammierer. Es ist doch wohl (in anderen Programmiersprachen) immer so gewesen, dass (5 and 6) das Resultat 4 zurückbringt, den der bitweise Vergleich der beiden Integer zeigt, die beiden Zahlen haben nur das Bit 2 zugleich gesetzt. Die anderen Bits sind unterschiedlich. Nicht so bei Python!

print 5 and 6

>>> 6

Ich weiß, es steht doch klar in den Büchern, dass hier jede Zahl ungleich 0 ein True ist und die Kommandozeile wird eben von links nach rechts abgearbeitet. Das merkt man, wenn man eingibt

print 6 and 5

>>> 5

Aber man schaut doch nur in die Bücher, wenn man anders nicht weiterkann, oder? Und ich mußte hier reinschauen, wo ich dann schnell fand, wie man es machen muss - natürlich einfach und elegant wie immer in Python:

print 5 & 6

>>> 4

Geben wir das sicherheitshalber auch mal mit vertauschten Operanden ein:

print 6 & 5

>>> 4

Das Ergebnis wie erwartet. Die Welt ist wieder in Ordnung.

Fall 4:

Nun noch etwas mit Spaß. Geben wir doch mal ab Version 2.2 im Interpreter ein:

import this

eigentlich dürfte jetzt nichts passieren, weil es kein Modul mit diesem Namen gibt. Aber weit gefehlt, hier handelt es sich wohl um ein eingebautes Späßchen. Es kommen einige Druckzeilen auf dem Bildschirm, die bei näherer Betrachtung sich als sehr wahr erweisen.

Was aber nun, wenn es doch ein Modul mit diesem namen gibt, weil wir es selbst geschrieben haben? Nun probieren wir es aus und schreiben ein kleines this.py mit dem Inhalt

print 'Hallo'

Nach dem Befehl "import this" im Hauptprogramm kommt nun tatsächlich der Ausdruck "Hallo". Das heißt unser o.g. Osterei kommt nur, wenn kein anderes Modul mit diesem Namen existiert.

 

Fall 5:

Auch unter Tkinter kann das Konsolenfenster mitwirken. Besonders während der Programmentwicklung ist es schön, wenn kontrollierende Ausgaben den Programmablauf verfolgen. Nun wollte ich auch mal aus einem Tkinter - Programm ein Beep ausgeben und zwar als

print '\a'

also wirklich nichts Besonderes! Um so größer war meine Überraschung, als es unter Windows klappte, unter Linux das Beep aber nicht kam. Erst beim Beenden des Programms kam es schnell hinterher. Was ist da geschehen? Nach einigem Probieren wurde es langsam klar. Es "piept" nur, wenn ein Wagenrücklauf mit ausgegeben wird. Ich hatte zuvor nämlich geschrieben:

print '\a',

um zu vermeiden, dass der Kursor im Konsolen - Fenster losrennt. Den konkreten Anwendungsfall kann man hier sehen. Es handelt sich um meinen Timer, der alle 10 Minuten ein Beep loslassen sollte, um meine Online - Zeit zu überwachen.


Fall 6:

na das ist ja interessant! Da wollte ich doch verhindern, dass jemand Buchstaben/Zeichen eingibt und nur Zahlen zulassen. Nichts einfacher als das, wozu gibts in Python die Eingabe mit input(), die ja nur Zahlen zulässt. Haha, angeschmiert!

# vorgegebene ganzzahlen eingeben

a = 1

def vorgabe(ende):
werte = []
for i in range(1, ende+1):
werte.append(i)
print i,
print
e = input('>> ')
if e in werte:
return e
else:
return 0

b = vorgabe(12)
if b:
print b
else:
print 'falsch'

Es ging nämlich trotzdem, obwohl input() aufgerufen wurde. Hinter dem von mir beispielhaft eingegebenen Buchstaben 'a', der eigentlich einen Fehler werfen sollte, verbarg sich zufällig ein Variablenname. Und diese Variable war eben zufällig mit einer gültigen Zahl belegt. Also aufgepasst!

Übrigens bestätigt sich dabei mal wieder eine alte Regel, dass man auf die gestandene erfahrenen Hasen hören sollte und eine bekannte Regel lautet ja, die Eingabe eines unbedarften Nutzers mit äusserster Vorsicht herzunehmen. Also sollte man grundsätzlich mit raw_input arbeiten und eine möglichst intelligente Prüfroutine auf die Daten anwenden.