Warum sollten wir NICHT sys.setdefaultencoding ("utf-8") in einem py-Skript verwenden?

165

Ich habe einige Py-Skripte gesehen, die dies oben im Skript verwenden. In welchen Fällen sollte man es benutzen?

import sys
reload(sys)
sys.setdefaultencoding("utf-8")
mlzboy
quelle
2
Es gibt ein Problem mit der Verwendung in Ipython.% time funktioniert nicht mehr. github.com/ipython/ipython/issues/8071
seanv507
3
@ Seanv507, lesen Sie die Antworten - es wird ernsthaft davon abgeraten
Alastair McCormack
2
Wie ist das nicht ein genaues Duplikat der Gefahren von sys.setdefaultencoding ('utf-8') ? Obwohl diese (2010) Frage älter ist als diese (2015)? Aber diese Frage hat auch gute Antworten. Was ist zu tun? Um klar zu sein, macht diese Frage nur in Python 2 Sinn, nicht in 3, aber das ist nirgends markiert oder erwähnt.
smci
lesenswert vor dem Eintauchen
ccpizza

Antworten:

141

Gemäß Dokumentation: Auf diese Weise können Sie vom Standard-ASCII zu anderen Codierungen wie UTF-8 wechseln, die die Python-Laufzeit immer dann verwendet, wenn ein Zeichenfolgenpuffer in Unicode dekodiert werden muss.

Diese Funktion ist nur beim Start von Python verfügbar, wenn Python die Umgebung scannt. Es muss in einem systemweiten Modul aufgerufen werden. sitecustomize.pyNachdem dieses Modul ausgewertet wurde, wird die setdefaultencoding()Funktion aus dem sysModul entfernt.

Die einzige Möglichkeit, es tatsächlich zu verwenden, ist ein Reload-Hack, der das Attribut zurückbringt.

Auch von der Verwendung von sys.setdefaultencoding()wurde immer abgeraten , und es ist in py3k ein No-Op geworden. Die Codierung von py3k ist fest mit "utf-8" verbunden, und das Ändern dieser Codierung führt zu einem Fehler.

Ich schlage einige Hinweise zum Lesen vor:

pyfunc
quelle
6
Tolles Zeug, obwohl es hier ein bisschen Tod durch zu viele Informationen gibt. Ich habe am meisten gelernt, mich nur auf diesen Artikel zu konzentrieren: blog.notdot.net/2010/07/Getting-unicode-right-in-Python
mbb
3
Ich möchte hinzufügen, dass die Standardcodierung auch für die Codierung verwendet wird (beim Schreiben in sys.stdouteine NoneCodierung, z. B. beim Umleiten der Ausgabe eines Python-Programms).
Eric O Lebigot
14
+1 für "die Verwendung von sys.setdefaultencoding()wurde immer entmutigt"
jfs
7
'fest mit utf-8 verbunden' ist nicht wahr, es ist nicht fest verdrahtet und es ist nicht immer UTF-8. LC_ALL=en_US.UTF-8 python3 -c 'import sys; print(sys.stdout.encoding)'gibt UTF-8aber LC_ALL=C python3 -c 'import sys; print(sys.stdout.encoding)'gibt ANSI_X3.4-1968(oder vielleicht etwas anderes)
Tino
7
@Tino, die Konsolencodierung unterscheidet sich von der Standardcodierung.
Alastair McCormack
58

tl; dr

Die Antwort ist NIE ! (es sei denn, Sie wissen wirklich, was Sie tun)

Das 9/10-fache der Lösung kann mit einem angemessenen Verständnis der Codierung / Decodierung gelöst werden.

1/10 Personen haben ein falsch definiertes Gebietsschema oder eine falsch definierte Umgebung und müssen Folgendes festlegen:

PYTHONIOENCODING="UTF-8"  

in ihrer Umgebung, um Konsolendruckprobleme zu beheben.

Was tut es?

sys.setdefaultencoding("utf-8")(durchgestrichen, um eine Wiederverwendung zu vermeiden) Ändert die Standardcodierung / -decodierung, die verwendet wird, wenn Python 2.x einen Unicode () in einen str () konvertieren muss (und umgekehrt) und die Codierung nicht angegeben wird. Dh:

str(u"\u20AC")
unicode("€")
"{}".format(u"\u20AC") 

In Python 2.x ist die Standardcodierung auf ASCII festgelegt, und die obigen Beispiele schlagen fehl mit:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)

(Meine Konsole ist als UTF-8 konfiguriert "€" = '\xe2\x82\xac', daher Ausnahme auf \xe2)

oder

UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)

sys.setdefaultencoding("utf-8")wird zulassen, dass diese für mich funktionieren , aber nicht unbedingt für Leute, die UTF-8 nicht verwenden. Die Standardeinstellung von ASCII stellt sicher, dass Annahmen zur Codierung nicht in Code eingebettet werden

Konsole

sys.setdefaultencoding("utf-8")hat auch den Nebeneffekt sys.stdout.encoding, dass es beim Drucken von Zeichen auf die Konsole angezeigt wird. Python verwendet das Gebietsschema des Benutzers (Linux / OS X / Un * x) oder die Codepage (Windows), um dies festzulegen. Gelegentlich ist das Gebietsschema eines Benutzers fehlerhaft und es muss nur PYTHONIOENCODINGdie Konsolencodierung korrigiert werden .

Beispiel:

$ export LANG=en_GB.gibberish
$ python
>>> import sys
>>> sys.stdout.encoding
'ANSI_X3.4-1968'
>>> print u"\u20AC"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
>>> exit()

$ PYTHONIOENCODING=UTF-8 python
>>> import sys
>>> sys.stdout.encoding
'UTF-8'
>>> print u"\u20AC"
€

Was ist so schlimm an sys.setdefaultencoding ("utf-8") ?

Die Leute entwickeln seit 16 Jahren gegen Python 2.x mit dem Verständnis, dass die Standardcodierung ASCII ist. UnicodeErrorEs wurden Ausnahmebehandlungsmethoden geschrieben, um die Konvertierung von Zeichenfolgen in Unicode für Zeichenfolgen zu verarbeiten, die Nicht-ASCII enthalten.

Von https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/

def welcome_message(byte_string):
    try:
        return u"%s runs your business" % byte_string
    except UnicodeError:
        return u"%s runs your business" % unicode(byte_string,
            encoding=detect_encoding(byte_string))

print(welcome_message(u"Angstrom (Å®)".encode("latin-1"))

Vor dem Festlegen der Standardcodierung konnte dieser Code das „Å“ in der ASCII-Codierung nicht dekodieren und gab dann den Ausnahmehandler ein, um die Codierung zu erraten und sie ordnungsgemäß in Unicode umzuwandeln. Drucken: Angstrom (Å®) führt Ihr Unternehmen. Sobald Sie die Standardcodierung auf utf-8 gesetzt haben, stellt der Code fest, dass der byte_string als utf-8 interpretiert werden kann. Dadurch werden die Daten entstellt und stattdessen zurückgegeben: Angstrom (Ů) führt Ihr Unternehmen aus.

Das Ändern einer Konstanten hat dramatische Auswirkungen auf die Module, von denen Sie abhängig sind. Es ist besser, nur die Daten zu korrigieren, die in Ihren Code ein- und ausgehen.

Beispiel Problem

Während die Einstellung der Standardcodierung auf UTF-8 im folgenden Beispiel nicht die Hauptursache ist, zeigt sie, wie Probleme maskiert werden und wie der Code bei Änderungen der Eingabecodierung auf nicht offensichtliche Weise unterbrochen wird: UnicodeDecodeError: 'utf8' Codec kann Byte 0x80 an Position 3131 nicht dekodieren: ungültiges Startbyte

Alastair McCormack
quelle
2
Es gibt zwar Überraschungen sys.setdefaultencoding("utf-8"), aber es ist gut, den Code eher wie Python 3 zu verhalten. Es ist jetzt 2017. Selbst als Sie die Antwort im Jahr 2015 geschrieben haben, war es meiner Meinung nach schon besser, vorwärts als rückwärts zu schauen. Es war tatsächlich die einfachste Lösung für mich, als ich feststellte, dass sich mein Code in Python 2 unterschiedlich verhält, je nachdem, ob die Ausgabe umgeleitet wird (sehr unangenehmes Problem für Python 2). Unnötig zu # coding: utf-8erwähnen , dass ich dies bereits getan habe und keine Problemumgehungen für Python 3 benötige (ich muss die setdefaultencodingÜberprüfung der verwendeten Version tatsächlich maskieren ).
Yongwei Wu
Das ist großartig und funktioniert für Sie, sys.setdefaultencoding("utf-8")macht Ihren Py 2.x-Code jedoch nicht mit Python 3 kompatibel. Es werden auch keine externen Module repariert, bei denen davon ausgegangen wird, dass die Standardcodierung ASCII ist. Die Kompatibilität Ihres Codes mit Python 3 ist sehr einfach und erfordert diesen bösen Hack nicht. Zum Beispiel, warum dies sehr reale Probleme verursacht, siehe meine Erfahrung mit Amazon, die mit dieser Annahme herumspielt
Alastair McCormack
1
@AlastairMcCormack du rockst, meine Seite ist seit Monaten und konnte nicht herausfinden, was zu tun ist. Schließlich PYTHONIOENCODING="UTF-8"half meine Python2.7 Django-1.11-Umgebung. Vielen Dank.
Sam
Ich weiß, dass Sie das Beispiel kopiert haben, aber ich kann herausfinden, welches Paket enthält detect_encoding.
Dlamblin
@dlamblin Das Codebeispiel dient zum Nachweis des Zitats und darf nicht in Ihrem Code verwendet werden. Stellen Sie sich vor, dies detect_encodingist eine Methode, mit der die Codierung einer Zeichenfolge anhand von Sprachhinweisen erkannt werden kann.
Alastair McCormack
18
#!/usr/bin/env python
#-*- coding: utf-8 -*-
u = u'moçambique'
print u.encode("utf-8")
print u

chmod +x test.py
./test.py
moçambique
moçambique

./test.py > output.txt
Traceback (most recent call last):
  File "./test.py", line 5, in <module>
    print u
UnicodeEncodeError: 'ascii' codec can't encode character 
u'\xe7' in position 2: ordinal not in range(128)

Bei Shell funktioniert das Senden an sdtout nicht, das ist also eine Problemumgehung, um an stdout zu schreiben.

Ich habe einen anderen Ansatz gewählt, der nicht ausgeführt wird, wenn sys.stdout.encoding nicht definiert ist oder mit anderen Worten, zuerst PYTHONIOENCODING = UTF-8 exportiert werden muss, um in stdout zu schreiben.

import sys
if (sys.stdout.encoding is None):            
    print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout." 
    exit(1)


Verwenden Sie also dasselbe Beispiel:

export PYTHONIOENCODING=UTF-8
./test.py > output.txt

wird funktionieren

Sérgio
quelle
3
Dies beantwortet die gestellte Frage nicht. Eher einige tangentiale Gedanken zu diesem Thema.
ivan_pozdeev
3
  • Die erste Gefahr liegt in reload(sys).

    Wenn Sie ein Modul neu laden, erhalten Sie in Ihrer Laufzeit tatsächlich zwei Kopien des Moduls. Das alte Modul ist wie alles andere ein Python-Objekt und bleibt am Leben, solange Verweise darauf vorhanden sind. Die Hälfte der Objekte zeigt also auf das alte Modul und die Hälfte auf das neue. Wenn Sie Änderungen vornehmen, werden Sie diese nie sehen, wenn ein zufälliges Objekt die Änderung nicht sieht:

    (This is IPython shell)
    
    In [1]: import sys
    
    In [2]: sys.stdout
    Out[2]: <colorama.ansitowin32.StreamWrapper at 0x3a2aac8>
    
    In [3]: reload(sys)
    <module 'sys' (built-in)>
    
    In [4]: sys.stdout
    Out[4]: <open file '<stdout>', mode 'w' at 0x00000000022E20C0>
    
    In [11]: import IPython.terminal
    
    In [14]: IPython.terminal.interactiveshell.sys.stdout
    Out[14]: <colorama.ansitowin32.StreamWrapper at 0x3a9aac8>
  • Nun sys.setdefaultencoding()richtig

    Alles, was davon betroffen ist, ist die implizite Konvertierungstr<->unicode . utf-8Ist nun die vernünftigste Codierung auf dem Planeten (abwärtskompatibel mit ASCII und allen), funktioniert die Konvertierung jetzt "nur", was könnte möglicherweise schief gehen?

    Nun, alles. Und das ist die Gefahr.

    • Möglicherweise gibt es Code, der davon abhängt, UnicodeErrordass er für Nicht-ASCII-Eingaben ausgegeben wird, oder die Transcodierung mit einem Fehlerhandler, der jetzt ein unerwartetes Ergebnis erzeugt. Und da der gesamte Code mit der Standardeinstellung getestet wird, befinden Sie sich hier ausschließlich auf "nicht unterstütztem" Gebiet , und niemand gibt Ihnen Garantien für das Verhalten des Codes.
    • Die Transcodierung kann zu unerwarteten oder unbrauchbaren Ergebnissen führen, wenn nicht alles auf dem System UTF-8 verwendet, da Python 2 tatsächlich mehrere unabhängige "Standard-String-Codierungen" hat . (Denken Sie daran, dass ein Programm für den Kunden auf der Ausrüstung des Kunden funktionieren muss.)
      • Das Schlimmste ist wiederum , dass Sie das nie erfahren werden, da die Konvertierung implizit ist - Sie wissen nicht wirklich, wann und wo sie stattfindet. (Python Zen, koan 2 ahoi!) Sie werden nie wissen, warum (und ob) Ihr Code auf einem System funktioniert und auf einem anderen kaputt geht. (Oder noch besser, funktioniert in IDE und bricht in der Konsole.)
ivan_pozdeev
quelle