Seltsames Verhalten der replace()-Funktion für Strings

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

Einen schönen guten Abend wünsche ich. :)

Ich habe gerade ein mir bisher unbekanntes Verhalten der replace()-Funktion festgestellt, das ich nicht ganz nachvollziehen kann. Kann mir bitte von euch jemand das folgende Verhalten erklären? Ich verwende Python 2.6.5.

Code: Alles auswählen

>>> word = "Schlumpf"
>>> word = word.replace("o", "e")
>>> type(word)
<type 'str'>
>>> word = word.replace(u"o", "e")
>>> type(word)
<type 'unicode'>
Zwei Dinge verstehe ich nicht:
1. Wenn man einen Substring sucht, der ein Unicode-Objekt ist, warum wird dann der gesamte durchsuchte String auch in ein Unicode-Objekt umgewandelt?
2. Warum wird ein durchsuchter String selbst dann in ein Unicode-Objekt umgewandelt, wenn der gesuchte Substring gar nicht gefunden wurde?

Kann man das Verhalten so beeinflussen, dass ein String nicht in ein Unicode-Objekt umgewandelt wird, wenn der gesuchte Substring nicht gefunden wurde?

Vielen Dank. :)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Unicode ist kaputt in 2.x aber keine Sorge durch die stdlib ist es in 3.x noch schlimmer. Wenn du Glück hast ist dass ganze gar kein Problem.
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

DasIch hat geschrieben:Unicode ist kaputt in 2.x aber keine Sorge durch die stdlib ist es in 3.x noch schlimmer. Wenn du Glück hast ist dass ganze gar kein Problem.
Das ist tatsächlich ein Bug? Für einen Bug hätte ich es jetzt nicht gehalten, aber ich finde dieses Verhalten unlogisch. Das Problem ist, dass mein Programm hierdurch kein konsistentes Verhalten zeigt. Mal wird ein String in Unicode umgewandelt, ein anderes Mal nicht. Das nervt.

Das fällt mir spontan ein: Verfügt die sub()-Funktion aus dem Modul re auch über dieses Verhalten?

EDIT: Habs getestet, die re.sub()-Funktion zeigt dieses unlogische Verhalten nicht. Dann werde ich diese anstelle der replace()-Funktion verwenden. Ist dieser Bug den Entwicklern eigentlich bereits bekannt?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Es ist nicht wirklich ein Bug, es hat wahrscheinlich mit der string coercion zu tun die intern dann solche Ergebnisse bringt. str.join bzw. unicode.join ergibt ebenfalls scheinbar komische Ergebnisse und es gibt sicherlich noch mehr ähnliche Fälle. Glücklicherweise spielt es durch diesen Effekt auch nicht wirklich eine Rolle was am Ende dabei rum kommt.
BlackJack

@Dingels: Das ist kein Bug, DasIch mag das Verhalten bloss nicht.

Ad 1) Wenn man eine Zeichenkette in einer anderen Zeichenkette sucht, müssen die vom gleichen Typ sein und Python wandelt hier halt immer in `unicode` um wenn `str` und `unicode` beteiligt sind weil dass der Typ mit dem grösseren Wertebereich ist. Das ist vergleichbar mit dem mischen von ganzen Zahlen und Fliesskommazahlen -- da werden `int`\s ja auch in `float`\s umgewandelt.

Ad 2) Weil zum Suchen bereits umgewandelt werden muss und da steht ja noch gar nicht fest, dass nichts ersetzt wird. Um nochmal das Beispiel mit den Zahlen anzuführen: ``42 * 0.0`` ergibt ja auch eine Fliesskommazahl obwohl sich am Wert eigentlich nichts ändert.

Ich sehe an dem Verhalten auch nichts unlogisches. Wenn man `str` und `unicode` mischt, dann sollte man entweder sicher sein, dass die `str`-Werte garantiert nur ASCII enthalten, dann ist es egal was rauskommt. Wenn dem nicht so ist, rennt man früher oder später sowieso in interessante Probleme.
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

@ BlackJack:
Ok, wenn man es aus dieser Perspektive betrachtet, ergibt dieses Verhalten sogar irgendwie Sinn. Dennoch habe ich mich nun für re.sub() entschieden. Eine Funktion, die ich geschrieben habe, soll nämlich nur dann ein Unicode-Objekt zurückgeben, wenn sie auch mit einem Unicode-Objekt gefüttert wurde. Diese Konsistenz finde ich sinnvoller, zumal ich viel mit Zeichen arbeite, die nicht im ASCII-Bereich enthalten sind.

Jedenfalls vielen Dank für die Erläuterungen. :)
BlackJack

@Dingels: Aber das was Du da beschreibst leistet doch `replace()` auch schon: Wenn keine `unicode`-Objekte beteiligt sind, dann wird auch nichts dahin umgewandelt.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Dingels hat geschrieben:Ok, wenn man es aus dieser Perspektive betrachtet, ergibt dieses Verhalten sogar irgendwie Sinn. Dennoch habe ich mich nun für re.sub() entschieden. Eine Funktion, die ich geschrieben habe, soll nämlich nur dann ein Unicode-Objekt zurückgeben, wenn sie auch mit einem Unicode-Objekt gefüttert wurde. Diese Konsistenz finde ich sinnvoller, zumal ich viel mit Zeichen arbeite, die nicht im ASCII-Bereich enthalten sind.
Hört sich für mich so an, als würdest du dir da was basteln, was dir früher oder später um die Ohren fliegt.
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

BlackJack hat geschrieben:@Dingels: Aber das was Du da beschreibst leistet doch `replace()` auch schon: Wenn keine `unicode`-Objekte beteiligt sind, dann wird auch nichts dahin umgewandelt.
Richtig, aber mein Szenario sieht etwas anders aus: In meiner Funktion, die mit einem String als Argument gefüttert wird, suche ich auch nach einigen Zeichen im String, die nicht mehr im ASCII-Wertebereich enthalten sind. Weil ich im Quelltext nur ASCII-Zeichen haben möchte, verwende ich code points für Nicht-ASCII-Zeichen. Wenn der String, mit dem die Funktion gefüttert wird, aber nicht schon in Unicode vorliegt und auch nur ASCII-Zeichen enthält, halte ich es für überflüssig, diesen dann von replace() in Unicode umwandeln zu lassen. Ich habe jetzt wie gesagt re.sub() genommen und eine weitere kleine Funktion geschrieben, die einen String nur dann in Unicode umwandelt, wenn mindestens ein Zeichen innerhalb des Strings nicht mehr im ASCII-Wertebereich liegt.

Diese Vorgehensweise halte ich persönlich für am sinnvollsten. Denn ich schreibe momentan an einem Modul, das ich später der Allgemeinheit zur Verfügung stellen möchte. Die Nutzer sollen dann möglichst selbst entscheiden können, wann ein String umgewandelt wird. In meinem Fall wird er halt nur dann in Unicode umgewandelt, wenn es wirklich nötig ist.
BlackJack

@Dingels: Was Du da beschreibst macht absolut keinen Sinn. Wenn Du in einer Zeichenkette nach Werten ausserhalb von ASCII suchst und dafür Unicode-Codepoints nimmst, *musst* Du die Zeichenkette *selbst* und *explizit* in `unicode` umwandeln. Automatisch geht das nicht, denn da wird immer ASCII angenommen. Also hast Du an der Stelle schon *beides* als Unicode vorliegen und es wird nichts automatisch umgewandelt.

Und das alles hat überhaupt nichts damit zu tun ob der Quelltext nun in ASCII gespeichert wird oder in einer anderen Kodierung. Man kann ja Werte ausserhalb von ASCII auch problemlos in `str`-Literalen als Escape-Sequenz eingeben.
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

BlackJack hat geschrieben:@Dingels: Was Du da beschreibst macht absolut keinen Sinn. Wenn Du in einer Zeichenkette nach Werten ausserhalb von ASCII suchst und dafür Unicode-Codepoints nimmst, *musst* Du die Zeichenkette *selbst* und *explizit* in `unicode` umwandeln. Automatisch geht das nicht, denn da wird immer ASCII angenommen. Also hast Du an der Stelle schon *beides* als Unicode vorliegen und es wird nichts automatisch umgewandelt.
Ich glaube, Du hast meine Ausführungen nicht genau gelesen. Das weiß ich alles. Strings, die Zeichen außerhalb des ASCII-Bereichs enthalten, werden ja nun wie gesagt von einer kleinen Nebenfunktion in Unicode umgewandelt. Wenn ein String keine Nicht-ASCII-Zeichen enthält, dann muss er auch nicht umgewandelt werden. re.sub() funktioniert ja dann trotzdem, nur gibt sie halt dann den ursprünglichen String wieder zurück.
BlackJack

@Dingels: Was ist denn so verdammt schlimm daran wenn eine Zeichenkette mit ausschliesslich ASCII darin, in `unicode` umgewandelt wird, dass man sich das Programm dafür komplexer machen muss als nötig? Ist das ein nachweisbar, messbares Problem oder ist das etwas Eingebildetes?
lunar

@Dingels: Selbst wenn ein "str" nur ASCII-Zeichen enthält, muss er trotzdem in unicode umgewandelt werden, wenn Du auf Zeichen arbeiten willst. "str" ist – anders als der Name vermuten lässt – keine Zeichenkette, sondern eine Bytefolge. In Python 2.x funktioniert das Mischen dieser Typen nur dank laxer Regeln zur impliziten Konvertierung.

Arbeite lieber komplett mit Unicode. Dann kannst Du echte ASCII-only Zeichenketten erzeugen, die Du dann später wenn nötig immer noch in "str" umwandeln kannst.
Dingels
User
Beiträge: 61
Registriert: Dienstag 23. Dezember 2008, 19:50

@BlackJack & lunar:
Ja, wahrscheinlich habt ihr doch recht. Beim Schreiben meines Programms dachte ich halt nur, dass es für die späteren Nutzer sinnvoller wäre, selbst entscheiden zu können, wann ein String in Unicode umgewandelt wird und wann nicht. Wenn aber ausnahmslos alle Strings in Unicode umgewandelt werden, hat das keine Auswirkungen auf die Ergebnisse. Na gut, dann lass ich das lieber sein. Ärgerlich, dass ich für mein Projekt noch nicht mit Python 3.* arbeiten kann. Das würde dieses ganze Kodierungshickhack mal etwas vereinfachen. Hoffentlich darf ich noch irgendwann den Tag erleben, an dem endlich alle Kodierungen bis auf UTF-8 abgeschafft werden.

Nochmals danke für eure Tipps. :)
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Dingels hat geschrieben:Ärgerlich, dass ich für mein Projekt noch nicht mit Python 3.* arbeiten kann. Das würde dieses ganze Kodierungshickhack mal etwas vereinfachen.
Mit Python3 wird das nicht unbedingt besser. Die Bytestrings(die man am besten als Byte-Integer-Array-Strings bezeichnen sollte, die haben nämlich von allem etwas) wurden da ziemlich verhunzt. Ich persönliche lasse jedenfalls erstmal die Finger von Python3 und warte ab, was sich da noch ergibt.
Hoffentlich darf ich noch irgendwann den Tag erleben, an dem endlich alle Kodierungen bis auf UTF-8 abgeschafft werden.
Sowie utf-16 und utf-32 die behalten weiterhin ihre Berechtigung.
Antworten