cookie: (PYTHON) Unicode-Strings innerhalb anderer Datenstrukturen

Hallo,

Ich benutze Python 2.5 unter Fedora 8 Linux (x86).
wie man im Folgenden sieht, kann ich mit print problemlos Strings mit Sonderzeichen ausgeben, aber wenn derselbe String innerhalb einer Liste steht, wandelt print ihn um:

a = 'ähnlich'
print a

ähnlich

b = ['ähnlich']
print b

['\xc3\xa4hnlich']

b = [u'ähnlich']
print b

[u'\xe4hnlich']

b = [U'ähnlich']
print b

[u'\xe4hnlich']

c = b[0]
print c

ähnlich

print b[0]

ähnlich

Ich habe auch mit u und U herumgewurschtelt, einfach um zu zeigen, dass auch die explizite Deklaration als Unicode-String keinen Unterschied macht. Das ist übrigens auch so eine Sache, die ich beim Lesen verschiedener Internet-Tutorials zu Python nicht so ganz verstanden habe: Welchen Wert hat es denn eigentlich, einen String als Unicode zu bezeichnen, wenn es keine Angabe zu der verwendeten Codierung gibt (z.B. UTF-8, UTF-2 etc.)?
Auf jeden Fall möchte ich nun gerne, dass Strings innerhalb von anderen Datentypen bei der Ausgabe ebenso behandelt wie einzelne Strings. Ich wünsche mir also eine Ausgabe wie:

Beispiel für die gewünschte Ausgabe:

b = ['ähnlich']
print b

['ähnlich']

Wie wäre das zu bewerkstelligen?
Ich finde das Verhalten von Python in diesem Punkt sehr überraschend. Auf diversen Webseiten habe ich Hinweise gefunden, dass print vor der Ausgabe eine 'string conversion' durchführt, aber dies erklärt für mich nicht, warum ein Unterschied zwischen einzelnen Strings und Strings innerhalb anderer Datenstrukturen besteht (dasselbe Verhalten gibt es z.B. auch bei Strings in Dictionaries.).

Viele Grüße
cookie

  1. echo $begrüßung;

    wie man im Folgenden sieht, kann ich mit print problemlos Strings mit Sonderzeichen ausgeben, aber wenn derselbe String innerhalb einer Liste steht, wandelt print ihn um:

    a = 'ähnlich'
    print a
    ähnlich
    b = ['ähnlich']
    print b
    ['\xc3\xa4hnlich']

    Das selbe passiert, wenn du print repr(a) eingibst. Wenn du eine komplexe Struktur ausgeben willst, wird sie offensichtlich in der von repr() gelieferten Darstellung ausgegeben.

    print b[0]
    ähnlich

    Na bitte, den String einzeln ausgegeben ist doch alles bestens.

    Welchen Wert hat es denn eigentlich, einen String als Unicode zu bezeichnen, wenn es keine Angabe zu der verwendeten Codierung gibt (z.B. UTF-8, UTF-2 etc.)?

    Die Kodierung muss erst bei der Ausgabe berücksichtigt werden. Die interne Darstellung kann doch sein wie sie will.

    Den Default-Wert der Ausgabekodierung kannst du mit

    import sys
      print sys.getdefaultencoding()

    abfragen. Setzen geht beispielsweise in einer Datei namens sitecustomize.py. Liegen kann sie wohl an mehreren Stellen. Bei mir liegt sie unter /usr/lib/python2.5/site-packages/. Ihr Inhalt ist

    import sys
      sys.setdefaultencoding("utf-8")

    Diese Datei wird beim Start von Python aufgerufen. Nach erfolgtem Start wird sys.setdefaultencoding entfernt und die Default-Kodierung lässt sich nicht mehr einstellen. Dann bleibt nur noch die individuelle Kodierung.

    Auf jeden Fall möchte ich nun gerne, dass Strings innerhalb von anderen Datentypen bei der Ausgabe ebenso behandelt wie einzelne Strings.

    Dann überschreib dir __str__. Die Ausgabe von Strukturen oder Objekten hat doch nur Kontrollausgabencharakter. Sie ist sicher nicht für Anwenderaugen bestimmt.

    echo "$verabschiedung $name";

    1. Hallo dedlfix,

      erstmal vielen Dank für Deine Antwort.

      Das selbe passiert, wenn du print repr(a) eingibst. Wenn du eine komplexe Struktur ausgeben willst, wird sie offensichtlich in der von repr() gelieferten Darstellung ausgegeben.

      Ja, das kann gut sein. Aber warum nur bei komplexen Strukturen und nicht bei einem einzelnen String?

      Welchen Wert hat es denn eigentlich, einen String als Unicode zu bezeichnen, wenn es keine Angabe zu der verwendeten Codierung gibt (z.B. UTF-8, UTF-2 etc.)?

      Die Kodierung muss erst bei der Ausgabe berücksichtigt werden. Die interne Darstellung kann doch sein wie sie will.

      Okay, aber wozu gibt es dann das u bzw. U vor dem String? Der Python-Interpreter merkt es doch sowieso, wenn ein String Nicht-ASCII-Zeichen enthält.
      Wenn die interne Darstellung "sein kann wie sie will", dann brauche ich die Kodierung doch tatsächlich erst zum Zeitpunkt der Ausgabe festzulegen, und nicht schon beim Erstellen des Strings.

      Den Default-Wert der Ausgabekodierung kannst du mit

      import sys
        print sys.getdefaultencoding()

      abfragen.

      Bei mir kommt da: 'ascii'.
      Was bedeutet das jetzt? Die folgende Sequenz, die ich durchführen kann, dürfte doch gar nicht möglich sein, weil ich 'ähnlich' nicht als Unicode angegeben habe:

      a = 'ähnlich'
      print a

      ähnlich

      Was passiert hier? Müsste nicht mit einer Fehlermeldung abgebrochen werden, da der vermeintliche ASCII-String ein Zeichen enthält, das im ASCII-Zeichenvorrat doch gar nicht vorkommt?
      Wieso kann Python mit der Ausgabekodierung ASCII, die ich nirgendwo beeinflusst oder geändert habe, einen Nicht-ASCII-String ausgeben?

      Setzen geht beispielsweise in einer Datei namens sitecustomize.py. Liegen kann sie wohl an mehreren Stellen. Bei mir liegt sie unter /usr/lib/python2.5/site-packages/.

      Das Verzeichnis gibt es bei mir auch, die Datei aber nicht.

      Auf jeden Fall möchte ich nun gerne, dass Strings innerhalb von anderen Datentypen bei der Ausgabe ebenso behandelt wie einzelne Strings.

      Dann überschreib dir __str__. Die Ausgabe von Strukturen oder Objekten hat doch nur Kontrollausgabencharakter. Sie ist sicher nicht für Anwenderaugen bestimmt.

      Das stimmt. ;-)
      Gute Idee, __str__ zu überschreiben, auch wenn ich das Verhalten von Python in diesem Punkt inkonsistent und unpraktisch finde - solche "Kunstgriffe" sollten IMHO gar nicht nötig sein.

      Nochmal danke!

      Viele Grüße
      cookie

      1. echo $begrüßung;

        Das selbe passiert, wenn du print repr(a) eingibst. Wenn du eine komplexe Struktur ausgeben willst, wird sie offensichtlich in der von repr() gelieferten Darstellung ausgegeben.

        Ja, das kann gut sein. Aber warum nur bei komplexen Strukturen und nicht bei einem einzelnen String?

        Beim Ausgeben von Strings erwartet man eine "normale" Darstellung. Komplexe Strukturen sind zum Datenspeichern gedacht und nicht zum Ausgaben am Bildschirm. Dass du da eine Darstellung bekommst, dient zu Kontrollzwecken. Du bekommst da ja auch Zeichen wie ,[]{} usw. angezeigt, die zur Syntax und nicht zu den Daten gehören. Du könntest genauso gut gegen diese opponieren. Wenn du eine andere als die Standard-Ausgabe haben möchtest, kannst du das bei Python ja wenigstens durch überschreiben der __Funktionen__ tun. Andere Systeme sind da eingeschränkter.

        Okay, aber wozu gibt es dann das u bzw. U vor dem String? Der Python-Interpreter merkt es doch sowieso, wenn ein String Nicht-ASCII-Zeichen enthält.

        Auch Python war nicht von Anfang an Unicode-fähig. Die herkömmlichen Strings arbeiten nach dem Ein-Zeichen-ein-Byte-Prinzip und sind damit den üblichen Einschränkungen unterworfen. Auf Mehrbyte-Kodierungen umzusteigen ist nicht so einfach. Da wollen jede Menge Nebenwirkungen beachtet werden. Deshalb hat man wohl auch die Unicode-Strings als Zusatz eingebaut.

        Wenn die interne Darstellung "sein kann wie sie will", dann brauche ich die Kodierung doch tatsächlich erst zum Zeitpunkt der Ausgabe festzulegen, und nicht schon beim Erstellen des Strings.

        Das setzt voraus, dass Python intern komplett fähig wäre, um bei der Ausgabe sämtliche Kodierungen bedienen zu können. Sprich: Es dürfen bei der internen Verarbeitung keine Zeichen unberücksichtigt bleiben. Das Ein-Zeichen-ein-Byte-Prinzip kann jedoch nur 256 Zeichen berücksichtigen und ist aus historischen Gründen noch vorhanden. Irgendwie muss man nun unterscheiden, welches der beiden Systeme man haben möchte. Die neueren Unicode-Strings bekamen deshalb zur Kennzeichnung das u. Bei Python 3 wird das anders werden. Bis dahin musst du mit den zwei Systemen leben. (http://docs.python.org/dev/3.0/whatsnew/3.0.html#strings-and-bytes)

        Den Default-Wert der Ausgabekodierung [...]
        Bei mir kommt da: 'ascii'.

        a = 'ähnlich'
        print a
        ähnlich
        Was passiert hier? Müsste nicht mit einer Fehlermeldung abgebrochen werden, da der vermeintliche ASCII-String ein Zeichen enthält, das im ASCII-Zeichenvorrat doch gar nicht vorkommt?
        Wieso kann Python mit der Ausgabekodierung ASCII, die ich nirgendwo beeinflusst oder geändert habe, einen Nicht-ASCII-String ausgeben?

        Die Antworten muss ich dir schuldig bleiben. Damit habe ich mich nicht weiter beschäftigt. Ich nehm nur UTF-8. Auch einen kurzen Rechercheversuch konnte ich nicht erfolgreich beenden.

        Setzen geht beispielsweise in einer Datei namens sitecustomize.py. Liegen kann sie wohl an mehreren Stellen. Bei mir liegt sie unter /usr/lib/python2.5/site-packages/.
        Das Verzeichnis gibt es bei mir auch, die Datei aber nicht.

        Die ist zum Anlegen bei Bedarf gedacht. Du kannst ja mal recherchieren, wo sie überall liegen darf. Nicht immer will man das komplette System umstellen. Deswegen gibt es auch die anderen Stellen.

        Gute Idee, __str__ zu überschreiben, auch wenn ich das Verhalten von Python in diesem Punkt inkonsistent und unpraktisch finde - solche "Kunstgriffe" sollten IMHO gar nicht nötig sein.

        Bei verschiedenen Anwendungsfällen ist es durchaus nicht unüblich verschiedene Verhaltensweisen zu verwenden. Ebensogut könntest du meinen, dass bei einer Ausgabe von Typen (print bool) und Objekten eine andere als die derzeitige Darstellung konsistenter wäre. Konsistent gegenüber welchem (vorwiegend üblichem) Anwendungsfall?

        echo "$verabschiedung $name";

        1. Hallo dedlfix,

          abermals danke für Deine Bemühungen!
          Ich habe Deine Ausführungen zu meiner Frage, warum die Ausgabe bei Unicode-Strings anders ist als bei Unicode-Strings innerhalb von komplexen Datenstrukturen, mit Interesse gelesen, wenngleich ich weiterhin das Verhalten von Python in diesem Punkt unglücklich finde. Ich verstehe aber Deinen Standpunkt, und mir ist klar, dass ich es nur selbst ändern kann (auf dem Weg, auf den Du mich bereits hingewiesen hattest). Ich glaube, Du stimmst mir zu, dass wir das daher nicht weiter diskutieren müssen.
          Das mit dem u vor einem Unicode-String werde ich einfach mal so akzeptieren. Jetzt aber trotzdem nochmal zur endgültigen Klärung:
          Mal *unabhängig* davon, dass ich das hier machen kann:

          a = 'ähnlich'
          print a

          ähnlich

          Stimmt es, dass es in jedem Fall *besser* wäre, es wie folgt zu machen, also *mit* einem u für Unicode?

          a = u'ähnlich'
          print a

          ähnlich

          [bezüglich der Datei sitecustomize.py:]
          Die ist zum Anlegen bei Bedarf gedacht. Du kannst ja mal recherchieren, wo sie überall liegen darf. Nicht immer will man das komplette System umstellen. Deswegen gibt es auch die anderen Stellen.

          Ah, danke für den Hinweis! Jetzt gehts auch bei mir!

          Viele Grüße
          cookie

          1. echo $begrüßung;

            Mal *unabhängig* davon, dass ich das hier machen kann:

            a = 'ähnlich'
            print a
            ähnlich

            Stimmt es, dass es in jedem Fall *besser* wäre, es wie folgt zu machen, also *mit* einem u für Unicode?

            a = u'ähnlich'
            print a
            ähnlich

            Nein, "in jedem Fall" ist es nicht besser, da es nicht unbedingt nötig ist. Bei Text mit ASCII-Zeichen reicht auch die Notation als herkömmlicher String.

            Interessant wird es bei der weiteren Verarbeitung. print len(a) wird bei dir im ersten Beispiel 8 liefern im zweiten 7. Fall 1 dürfte sich mit der Betriebssystemvorgabe UTF-8 erklären lassen. Mit der u-Notation wird sich auch Python "bewusst", dass hier nicht das Ein-Zeichen-ein-Byte-Prinzip gilt.

            Hier noch ein paar Beispiele:

            print len('€')

            3

            print len(u'€')

            1

            print repr('€')

            '\xe2\x82\xac'

            print repr(u'€')

            u'\u20ac'

            echo "$verabschiedung $name";

            1. Hallo dedlfix,

              danke nochmal für Deine Erläuterungen.
              Also, ich weiß jetzt, wie ich die Default-Ausgabe-Kodierung ändern kann. Diese habe ich jetzt auf UTF-8 eingestellt, und da allem Anschein nach der 'Rest des Systems' dieselbe Kodierung verwendet, dürfte diese Wahl die wenigsten Konflikte hervorrufen.
              Ich weiß jetzt weiterhin, dass im Falle von Nicht-ASCII-Zeichen ein u beim Erstellen des Strings verwendet werden sollte. Bei reinen ASCII-Strings ist das u nicht notwendig, kann aber auch nicht schaden. Ich habe jetzt auch zumindest eine Ahnung, warum ein fehlendes u trotzdem zu korrekter Ausgabe führen kann.
              Und ich weiß jetzt, was ich tun kann, wenn mir die Ausgabe von Unicode-Strings innerhalb von anderen Datenstrukturen wie Listen nicht gefällt.
              Ich glaube, das ist vorerst alles, was ich über Unicode bei Python wissen muss. :-)

              Viele Grüße
              cookie