Seite 1 von 1
Zuweisung in Testbedingung einer Schleife
Verfasst: Montag 22. Oktober 2007, 13:41
von windner
In Python ist ja das Ergebnis einer Zuweisung unbestimmt. Ich muss deshalb öfter so etwas schreiben:
Code: Alles auswählen
s = stream.read(1)
while s != "":
# mache etwas mit s
s = stream.read(1)
Das gefällt mir aber nicht, weil die Zuweisung zweimal auftaucht. Wie macht man das besser? Sollte ich die next-Methode des Streams überschreiben, um nicht nur über Zeilen iterieren zu können? Oder gibt es da einen Trick, mit dem man das schreiben kann wie in C:
Da fällt mir gerade auf, dass es auch schön wäre, Daten auf einen (file-artigen) stream zurückzulegen, so wie ungetchar(). Oder die Daten auf dem Stream zu lesen, ohne sie zu entfernen. Geht das?
Verfasst: Montag 22. Oktober 2007, 14:08
von BlackJack
Die Aussage das Ergebnis einer Zuweisung sei unbestimmt klingt IMHO etwas komisch. Zuweisungen sind Anweisungen und keine Ausdrücke und Anweisungen dürfen nicht in der Bedingung bei ``if`` und ``while`` stehen. Das wäre eine Erklärung die eher mit der Terminologie aus der Python-Doku harmoniert.
Die doppelte Zuweisung kann man ganz einfach loswerden:
Code: Alles auswählen
for s in iter(lambda: stream.read(1), ''):
# Mache etwas mit `s`.
Wenn Du auch Zeichen in den Stream zurückschieben willst, musst Du wohl eine eigene Klasse dafür schreiben. Ungetestet:
Code: Alles auswählen
class CharacterStream(object):
def __init__(self, file_obj):
self.file_obj = file_obj
self.pushed = None
def __iter__(self):
return self
def next(self):
if self.pushed is None:
result = self.file_obj.read(1)
else:
result = self.pushed
self.pushed = None
if result:
return result
else:
raise StopIteration()
def push_back(self, character):
if self.pushed is not None:
# TODO: Bessere Ausnahme und besserer Text. :-)
raise Exception("Is' zu voll hier...")
self.pushed = character
Verfasst: Montag 22. Oktober 2007, 19:40
von mitsuhiko
Auch wenn man drum rumfuhrwerken kann nervt es trotzdem tierisch. birkenfeld und ich (eher mehr birkenfeld ^^) haben da mal einen Patch für Python3 geschrieben, der "while expr as f:" erlaubt:
http://dev.pocoo.org/hg/sandbox/file/ti ... locks.diff
Keine Ahnung ob der auch mit den aktuellen Python3 Sources geht.
Verfasst: Donnerstag 8. November 2007, 18:10
von windner
Ich habe jetzt eingehend darüber nachgedacht.
Dass man mit iter() über Funktionsergebnisse iterieren kann, habe ich jetzt gelernt, und finde ich sehr elegant. CharacterStream funktioniert bestens und ist leicht verständlich. Habe davon einen WordStraem abgeleitet, analog zu getword() nach K&R:
Code: Alles auswählen
class WordStream(CharacterStream):
def __init__(self, file_obj, letters=string.letters+string.digits):
self.letters = letters
CharacterStream.__init__(self, file_obj)
def next(self):
word = c = CharacterStream.next(self)
while c in self.letters:
c = CharacterStream.next(self)
if c in self.letters:
word = word + c
else:
CharacterStream.push_back(self, c)
break
return word
Danke schön!
Nebenbei bemerkt wäre
while a as b viel besser als die Variante mit iter(). Wenn ich bei zwei verschiedenen möglichen Rückgabewerten abbrechen will, zB "\r" oder "\n", bräuchte ich eine iter()-Funktion, der ich keinen Terminator, sondern eine Vergleichsfunktion mitgeben kann. Oder ich mach's so:
Code: Alles auswählen
__ans=None
def call(f, *args):
global __ans
__ans = f(*args)
return __ans
def ans():
global __ans
return __ans
# mit iter() über sys.stdin.read könnte ich hier kein "or" anbringen:
while call(sys.stdin.read, 1) != '\n' or ans() != '\r':
# mache etwas mit ans()
Verfasst: Donnerstag 8. November 2007, 19:42
von BlackJack
Iiih was ist das zweite für ein Beispiel? ``global``!?
Das sieht nach einem Fall für `itertools.takewhile()` aus.
Code: Alles auswählen
characters = iter(lambda: sys.stdin.read(1), '')
for character in takewhile(lambda c: c not in '\n\r', characters):
print character
Verfasst: Donnerstag 15. November 2007, 02:30
von windner
Hab' heute endlich Zeit zum Lesen gehabt -- Danke! Die itertools muss man kennen.
Verfasst: Donnerstag 15. November 2007, 14:43
von Frank aka Ch3ck3r
Ist es nicht einfacher die Schliefe etwas zu verändern?
Code: Alles auswählen
while True:
s = stream.read(1)
if s == "": break
# mache etwas mit s
Verfasst: Donnerstag 15. November 2007, 15:25
von BlackJack
Einfacher als was? Das hier ist ja nun auch nicht gerade kompliziert:
Code: Alles auswählen
for s in iter(lambda: stream.read(1), ''):
# Tue etwas mit `s`.
Es hat den Vorteil, dass man in der ersten Zeile schon sieht worüber iteriert wird und das der Schleifenkörper völlig unabhängig davon ist wo die Buchstaben herkommen.
Man könnte das Objekt hinter dem ``in`` durch eine Zeichenkette, ein Tupel, eine Liste oder sonst irgendwas passendes ersetzen ohne das sich die Verarbeitung der Zeichen darum zu kümmern bräuchte. Es wäre zum Beispiel möglich das in eine Funktion zu stecken, die ein "iterable" über Zeichen entgegennimmt. Das ist viel generischer als eine Funktion die ein "file like" mit einer `read()`-Methode haben möchte.