Python 2.x geht beim String-Handling sonderbare Wege...

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
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Kein direktes Problem, sondern nur etwas, was mir gerade aufgefallen ist:

Code: Alles auswählen

Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> u'spam' == 'spam'
True
>>> d = {u'spam': 'foo', 'spam': 'bar'}
>>> d
{u'spam': 'bar'}
Python 3.x entspricht dann eher dem, was ich erwartet hätte:

Code: Alles auswählen

Python 3.1.2 (release31-maint, Sep 17 2010, 20:34:23) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> b'spam' == 'spam'
False
>>> d = {b'spam': 'foo', 'spam': 'bar'}
>>> d
{'spam': 'bar', b'spam': 'foo'}
In Python 2.x gehen ja auch so wirsche Sache wie:

Code: Alles auswählen

>>> unicode > str
True
Python 3.x wirft hingegen einen `TypeError', was ich auch besser finde.

Ginge es nach mir, würde ja noch mehr Typsicherheit bestehen, so dass allein schon der Vergleich `1 == 1.0` einen Fehler wegen inkompatibler Typen wirft. Aber damit handelt man sich sicher an anderen Stellen wieder Nachteile ein. Rein mathematisch ist das ja ohnehin korrekt und es würde sicher einige Leute verwirren, wenn sich das ändert. Aber zumindest mal die Einführung eines Basistyps für Zahlen wäre vom Vererbungsprinzip her trotzdem nicht schlecht, finde ich.

//edit: Huch, es gibt ja das `numbers`-Modul... Den Typ `Number` könnte man dann vielleicht mal als Built-in zur Verfügung stellen.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

snafu hat geschrieben:Ginge es nach mir, würde ja noch mehr Typsicherheit bestehen, so dass allein schon der Vergleich `1 == 1.0` einen Fehler wegen inkompatibler Typen wirft. Aber damit handelt man sich sicher an anderen Stellen wieder Nachteile ein. Rein mathematisch ist das ja ohnehin korrek
Warum sollte man jetzt einen Fehler wegen inkompatibler Typen auslösen, wenn der Vergleich laut deiner eigenen Aussage „mathematisch“ zulässig ist?

Mit Typsicherheit hat das auch erstmal nichts zu tun. Nur etwas damit, ob es sinnvoll ist die Gleichheit von Objekten verschiedener Typen bestimmen zu wollen oder nicht. Selbst wenn man == zwischen float und int Verbieten würde, wäre einem damit immer noch nicht geholfen, die Gleichheit zweier Gleitkommazahlen bestimmen zu wollen ist z.B. meist nicht sinnvoll, auch wenn es „Typsicher“ wäre. Da ist gerade der Vergleich zwischen int und float sehr viel sinnvoller. 7.0 ist nämlich tatsächlich gleich 7.
//edit: Huch, es gibt ja das `numbers`-Modul... Den Typ `Number` könnte man dann vielleicht mal als Built-in zur Verfügung stellen..
Nein, erstmal sollte man `Number` reparieren.

Code: Alles auswählen

>>> isinstance(decimal.Decimal(1), numbers.Number)
False
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Darii: Wie kommst du jetzt von Vergleichen zwischen unterschiedlichen Typen auf das Thema Gleitkommazahlen...? :o
Die Idee an sich ist aber vermutlich trotzdem blödsinnig von mir gedacht.

Übrigens, der `ininstance()`-Check ergibt bei mir unter Python 2.6 und Python 3.1 jeweils `True`.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

snafu hat geschrieben:@Darii: Wie kommst du jetzt von Vergleichen zwischen unterschiedlichen Typen auf das Thema Gleitkommazahlen...? :o
Von dir? Du sprichst von Typsicherheit und führst 1 == 1.0 als Beispiel auf, also Gleichheit von Zahlen plus Sinnhaftigkeit von Vergleichen verschiedener Typen in einem Satz. Da liegt das Thema Fließkommazahlen als (Gegen-)Beispiel nur nahe.
Übrigens, der `ininstance()`-Check ergibt bei mir unter Python 2.6 und Python 3.1 jeweils `True`.
Muss ich wohl mal ipython updaten, das ist nur 2.6.1, wurde wohl inzwischen gefixt. Komisch ich hatte angenommen ipython nutzt den normalen Python-Interpreter.

Edit: ok liegt an Apple, muss ich wohl mal auf Lion updaten, da gibts auch python 2.6.6.
Zuletzt geändert von Darii am Sonntag 24. Juli 2011, 13:05, insgesamt 1-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

`1 == 1.0` ist der Vergleich des Typs `int` mit einem `float`. Ist das wirklich so schwer oder stellst du dich gerade absichtlich dumm an? :)

Wahrscheinlich reden wir aneinander vorbei. Ich wundere mich halt, dass du von dem Thema auf die Problematik beim Vergleich zweier Fließkommazahlen kommst.
Zuletzt geändert von snafu am Sonntag 24. Juli 2011, 13:09, insgesamt 1-mal geändert.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

snafu hat geschrieben:`1 == 1.0` ist der Vergleich des Typs `int` mit einem `float`.
Und der Vergleich von Zahlen. Das eine schließt das andere nicht aus. Worauf willst du hinaus?

Worauf ich hinauswollte: Man sollte Vergleiche zwischen verschiederen Typen nicht kategorisch verbieten, weil der Vergleich sinnvoll sein kann. Ob der Vergleich sinnvoll ist, sollen doch bitte die Typen selbst entscheiden. Beispiel: Warum sollte man nicht zwei Zahlen verschiedenen Typs miteinander vergleichen dürfen, wenn das manchmal sogar sinnvoller als der Vergleich zweier Zahlen gleichen Typs sein kann?
snafu hat geschrieben: Ist das wirklich so schwer oder stellst du dich gerade absichtlich dumm an?
Hinlegen und mit dem anderen Fuß nochmal aufstehen?
Zuletzt geändert von Darii am Sonntag 24. Juli 2011, 13:17, insgesamt 4-mal geändert.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

snafu: Du bist sicherlich an dem kleinen Projekt "unicode-nazi" von mitsuhiko interessiert: https://github.com/mitsuhiko/unicode-nazi.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Darii hat geschrieben:
snafu hat geschrieben:`1 == 1.0` ist der Vergleich des Typs `int` mit einem `float`.
Und der Vergleich von Zahlen. Das eine schließt das andere nicht aus. Worauf willst du hinaus?
Ich erkläre mal anders, worauf ich hinaus will: In Python 3.x wird wie gesagt/gezeigt zwischen `'foo'` und `b'foo'` unterschieden. So gesehen ist das aber nur eine andere Form der Darstellung. Es kann ohne Verluste zwischen beiden Formen umgewandelt werden. Was ist also das entscheidende Kriterium, welches die unterschiedliche Behandlung rechtfertigt und hingegen bei `1.0` <=> `1` die unterschiedliche Behandlung *nicht* rechtfertigt?

Ich glaube aber, ich habe mir die Antwort schon selbst am Beispiel `'späm'` gegeben: Das `'ä'` in Unicode-Form entspricht dem Wert 228 (durch `ord()`). In Byteform, wo nur 8 Bit zur Verfügung stehen, muss hingegen ein zweites Zeichen zur Repräsentation hinzugenommen werden (`'\xc3\xa4'`). Ich vermute, dies ist der entscheidende Unterschied, richtig?

Inzwischen ist mir sogar klar geworden, was du meintest, als du gesagt hast, es gehe nicht um Typsicherheit, sondern darum, ob ein Vergleich überhaupt sinnvoll ist. In Python entscheiden ja die Typen - so wie du es auch schon gesagt hast - für sich selbst, ob sie ein Objekt, welches ihnen "angeboten" wird (hier im Falle eines Vergleichs), für die gewünschte Operation als "akzeptabel" erachten, oder ob sie stattdessen einen Fehler werfen. Sorry also für die harschen Worte von mir. Offenbar war eher ich derjenige, der es nicht verstanden hatte. :oops:
BlackJack

@snafu: Der Wert 228 (≤ 255) passt prima in ein Byte. Selbst Unicode-Codepoints mit Werten die nicht mehr in 8 Bit passen, kann man als ein Byte kodieren. Das kommt halt auf die Kodierung an, die verwendet wird.

Ich denke das in Python 2.x ``u'inhalt' == 'inhalt'`` gilt, solange der Inhalt nur aus ASCII besteht, wurde gemacht um den Übergang etwas einfacher zu machen. Und vielleicht auch um Speicher zu sparen. Es gibt ja zum Beispiel Bibliotheken, die `str` zurück geben solange nur ASCII enthalten ist, und sonst `unicode`. ElementTree ist so eine Bibliothek. Man hat wohl auch gedacht, dass genau wie bei `int`/`float` ASCII-`str` eine Untermenge von `unicode` ist, so wie `int` eine Untermenge von `float` ist, und es sinnvoll sein kann, dass man dort vergleichen kann. Und `str` automatisch in `unicode` zu wandeln, so wie in Rechnungen `int`\s zu `float`\s werden, wenn `float`\s beteiligt sind. Dann hat man in der Praxis gesehen, dass es die Leute oft davon abhält sich die Beziehung zwischen Byte- und (echten) Zeichenketten klar zu machen und sie immer blöde Fragen stellen wenn der `UnicodeDecodeError` zuschlägt. :-)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:@snafu: Der Wert 228 (≤ 255) passt prima in ein Byte.
Ja, gut. Entscheidend war für mich hier eher, dass halt zwei Bytes verwendet werden. Die Begründung, dass es an dieser Stelle etwas mit der Größe eines Bytes zu tun hätte, war natürlich unpassend.
BlackJack hat geschrieben:Selbst Unicode-Codepoints mit Werten die nicht mehr in 8 Bit passen, kann man als ein Byte kodieren.
Ja, das funktioniert dann mit einem anderen Zeichensatz, wodurch andere Zeichen heraus fliegen, damit keine Doppelbelegung entsteht. Schon klar soweit. Ich hatte auch nicht bedacht, dass ASCII ohnehin nur 7 Bit hat.
BlackJack hat geschrieben:Man hat wohl auch gedacht, dass genau wie bei `int`/`float` ASCII-`str` eine Untermenge von `unicode` ist, so wie `int` eine Untermenge von `float` ist, und es sinnvoll sein kann, dass man dort vergleichen kann.
Ah, es hat ein bißchen gedauert bis ich diesen Teil wirklich verstanden hatte - aber es stimmt schon: Python 2.x lässt Vergleiche zwischen Unicode und "String" nur für Strings mit Zeichen zu, die im ASCII-Zeichensatz vorhanden sind (und damit in Byte-Form nicht verändert werden müssen) bzw gibt andernfalls ein `False` zurück. Es wird netterweise auch eine `UnicodeWarning` geworfen, wenn man z.B. ein `'späm' == u'späm'` probiert. Man sieht zudem auch mittels `len('ä') == len(u'ä')`, dass dort mit der Gleichbehandlung Schluss ist. Python hat insofern also - wie du schon sagtest - nur seine "Meinung" geändert, dass diese kleine Unterstützung, was die Behandlung als Untermenge angeht, eher zu Verwirrung geführt hat und in der Praxis in einigen Situationen eher hinderlich sein konnte, wenn man sich nicht bewusst war, wie Pythons String-Handling intern vonstatten geht.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

snafu hat geschrieben:Python hat insofern also - wie du schon sagtest - nur seine "Meinung" geändert, dass diese kleine Unterstützung, was die Behandlung als Untermenge angeht, eher zu Verwirrung geführt hat und in der Praxis in einigen Situationen eher hinderlich sein konnte, wenn man sich nicht bewusst war, wie Pythons String-Handling intern vonstatten geht.
Jein, Python3 hat da mehr als nur an dieser kleinen Stelle angesetzt. Bytestrings wurden nämlich abgeschafft und durch Bytearrays ersetzt (bytes is eine Liste von Integern < 255 und nicht eine Liste von 1-Elementigen Strings wie vorher). Was man daran sieht, dass diese nämlich ziemlich unbrauchbar sind, wenn man damit wirkich arbeiten muss (kleine Übertreibung meinerseits, aber es hapert schon daran, dass bytes .format() nicht unterstützt).
lunar

@Darii: Naja, wieso sollte "bytes" auch ".format()" unterstützen? Per se kann man rohe Bytes halt nicht anzeigen, weil ihnen keine Interpretierung anhaftet.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

lunar hat geschrieben:@Darii: Naja, wieso sollte "bytes" auch ".format()" unterstützen? Per se kann man rohe Bytes halt nicht anzeigen, weil ihnen keine Interpretierung anhaftet.
Irgendeine „bytes“-Implementierung könnte es aus den selben Gründen wie Python2 unterstützen (und einer wäre: „Ist einfach praktisch“)…aber so verhunzt wie bytes bei Python3 ist, macht das bei dieser speziellen Implementierung tatsächlich keinen Sinn.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Aber wieso sollte man Bytes auch großartig supporten? Das bevorzugte Format sind nun mal Strings.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

snafu hat geschrieben:Aber wieso sollte man Bytes auch großartig supporten? Das bevorzugte Format sind nun mal Strings.
Weil Computer intern nunmal nicht mit Unicode-String arbeiten. Es gibt genug Fälle, in denen man Bytes braucht. HTTP ist einer davon. Für alle anderen Fälle gibt es "from __future__ import unicode_literals".
BlackJack

Rechner arbeiten intern auch nicht mit der Dezimaldarstellung, trotzdem muss man kein ``from __future__ import decimal_literals`` benutzen um nicht alle Zahlen in Binärdarstellung eingeben zu müssen. ;-)

Ich verstehe die Argumentation immer noch nicht ganz. Klar braucht man ab und an mal Bytes, aber dann braucht man doch auch tatsächlich Bytes und nicht Zeichen. Und wenn man Zeichen als Bytes benötigt, dafür gibt es die `encode()`-Methode.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Darii: So wie auch bei anderen Anwendungsgebieten würde ich einen Wert doch erst bei der konkreten Übergabe an die Schnittstelle in das für sie erforderliche Format umwandeln. Du kannst alle Vorbereitungen hinsichtlich des String-Formattings doch auf "echten" Zeichenketten anwenden und die Konvertierung erst in dem Moment machen, wenn die nachfolgenden Arbeiten tatsächlich durch das angebundene Protokoll (oder ähnliches) übernommen werden. Oder siehst du irgendwelche Nachteile in diesem Vorgehen?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Darii hat geschrieben:
snafu hat geschrieben:Aber wieso sollte man Bytes auch großartig supporten? Das bevorzugte Format sind nun mal Strings.
Weil Computer intern nunmal nicht mit Unicode-String arbeiten. Es gibt genug Fälle, in denen man Bytes braucht. HTTP ist einer davon.
Es ist IMHO wichtig, zwischen Byte-Folgen und Zeichenketten zu unterscheiden und sich dieses Unterschieds auch bewusst zu sein.

Die Octets eines HTTP-Headers sind nicht einfach nur Bytes, sondern die Spezifikation spricht auch hier von verschiedenen möglichen Kodierungen, um daraus Zeichenketten zu machen. Die Bytes 65-90 usw. werden gemäß ANSI X3.4-1986 als A..Z usw. interpretiert, Text bestehend aus Bytes >= 32 wird gemäß ISO-8859-1 interpretiert und RFC 2047 beschreibt, wie man diese Interpretation überschreiben kann. Und für den Rumpf einer HTTP-Nachricht gelten noch mal komplett andere Regeln.

Stefan
Antworten