¿Por qué NO deberíamos usar sys.setdefaultencoding("utf-8") en un script py?
He visto algunos scripts py que usan esto en la parte superior del script. ¿En qué casos se debe utilizar?
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
Según la documentación: esto le permite cambiar del ASCII predeterminado a otras codificaciones como UTF-8, que el tiempo de ejecución de Python utilizará siempre que tenga que decodificar un búfer de cadena a Unicode.
Esta función solo está disponible en el momento del inicio de Python, cuando Python escanea el entorno. Debe llamarse en un módulo de todo el sistema. sitecustomize.py
Una vez evaluado este módulo, la setdefaultencoding()
función se elimina del sys
módulo.
La única forma de usarlo realmente es con un truco de recarga que recupere el atributo.
Además, siempre se ha desaconsejado el uso desys.setdefaultencoding()
y se ha convertido en una opción no operativa en py3k. La codificación de py3k está cableada a "utf-8" y cambiarla genera un error.
Sugiero algunos consejos para la lectura:
- http://blog.ianbicking.org/illusive-setdefaultencoding.html
- http://nedbatchelder.com/blog/200401/printing_unicode_from_python.html
- http://www.diveintopython3.net/strings.html#one-ring-to-rule-them-all
- http://boodebr.org/main/python/all-about-python-and-unicode
- http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python
tl; dr
¡La respuesta es NUNCA ! (a menos que realmente sepas lo que estás haciendo)
9/10 veces la solución se puede resolver con una comprensión adecuada de codificación/decodificación.
1 de cada 10 personas tienen una ubicación o entorno definido incorrectamente y necesitan configurar:
PYTHONIOENCODING="UTF-8"
en su entorno para solucionar problemas de impresión de la consola.
¿Qué hace?
(tachado para evitar su reutilización) cambia la codificación/decodificación predeterminada utilizada siempre que Python 2.x necesita convertir un Unicode() a str() (y viceversa) y no se proporciona la codificación. Es decir:sys.setdefaultencoding("utf-8")
str(u"\u20AC")
unicode("€")
"{}".format(u"\u20AC")
En Python 2.x, la codificación predeterminada está configurada en ASCII y los ejemplos anteriores fallarán con:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)
(Mi consola está configurada como UTF-8, por lo tanto "€" = '\xe2\x82\xac'
, existe una excepción \xe2
)
o
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
permitirá que estos funcionen para mí , pero no necesariamente funcionarán para personas que no usan UTF-8. El valor predeterminado de ASCII garantiza que las suposiciones de codificación no se incluyan en el código.sys.setdefaultencoding("utf-8")
Consola
También tiene el efecto secundario de parecer arreglar sys.setdefaultencoding("utf-8")
sys.stdout.encoding
, usado al imprimir caracteres en la consola. Python usa la configuración regional del usuario (Linux/OS X/Un*x) o la página de códigos (Windows) para configurar esto. En ocasiones, la configuración regional de un usuario no funciona y solo es necesario PYTHONIOENCODING
corregir la codificación de la consola .
Ejemplo:
$ 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"
€
¿ Qué tiene de malo sys.setdefaultencoding("utf-8") ?
La gente ha estado desarrollando Python 2.x durante 16 años entendiendo que la codificación predeterminada es ASCII. UnicodeError
Se han escrito métodos de manejo de excepciones para manejar conversiones de cadenas a Unicode en cadenas que no contienen ASCII.
De 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"))
Antes de configurar la codificación predeterminada, este código no podría decodificar "Å" en la codificación ascii y luego ingresaría el controlador de excepciones para adivinar la codificación y convertirla correctamente en Unicode. Impresión: Angstrom (Å®) dirige su negocio. Una vez que haya configurado la codificación predeterminada en utf-8, el código encontrará que byte_string se puede interpretar como utf-8, por lo que alterará los datos y devolverá esto: Angstrom (Ů) dirige su negocio.
Cambiar lo que debería ser una constante tendrá efectos dramáticos en los módulos de los que depende. Es mejor simplemente arreglar los datos que entran y salen de su código.
Problema de ejemplo
Si bien la configuración de codificación predeterminada en UTF-8 no es la causa principal en el siguiente ejemplo, muestra cómo se enmascaran los problemas y cómo, cuando cambia la codificación de entrada, el código se rompe de una manera no obvia: UnicodeDecodeError: el códec 'utf8' puede No decodifica el byte 0x80 en la posición 3131: byte de inicio no válido
#!/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)
en Shell funciona, no se envía a sdtout, por lo que esa es una solución alternativa: escribir en stdout.
Hice otro enfoque, que no se ejecuta si sys.stdout.encoding no está definido o, en otras palabras, primero necesito exportar PYTHONIOENCODING=UTF-8 para escribir en stdout.
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)
entonces, usando el mismo ejemplo:
export PYTHONIOENCODING=UTF-8
./test.py > output.txt
trabajará
El primer peligro reside en
reload(sys)
.Cuando recargas un módulo, en realidad obtienes dos copias del módulo en tu tiempo de ejecución. El módulo antiguo es un objeto de Python como todo lo demás y permanece vivo mientras haya referencias a él. Entonces, la mitad de los objetos apuntarán al módulo antiguo y la otra mitad al nuevo. Cuando realiza algún cambio, nunca lo verá venir cuando algún objeto aleatorio no vea el cambio:
(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>
Ahora
sys.setdefaultencoding()
bienTodo lo que afecta es la conversión implícita
str<->unicode
. Ahora bien,utf-8
si es la codificación más sensata del planeta (compatible con versiones anteriores de ASCII y todo), la conversión ahora "simplemente funciona", ¿ qué podría salir mal?Bueno, cualquier cosa. Y ese es el peligro.
- Puede haber algún código que dependa del
UnicodeError
lanzamiento para entradas que no sean ASCII, o que realice la transcodificación con un controlador de errores, lo que ahora produce un resultado inesperado. Y dado que todo el código se prueba con la configuración predeterminada, aquí se encuentra estrictamente en territorio "no compatible" y nadie le ofrece garantías sobre cómo se comportará su código. - La transcodificación puede producir resultados inesperados o inutilizables si no todo en el sistema usa UTF-8 porque Python 2 en realidad tiene múltiples "codificaciones de cadena predeterminadas" independientes . (Recuerde, un programa debe funcionar para el cliente, en el equipo del cliente).
- Una vez más, lo peor es que nunca lo sabrás porque la conversión es implícita : realmente no sabes cuándo ni dónde ocurre. (Python Zen, koan 2 ¡ahoy!) Nunca sabrás por qué (y si) tu código funciona en un sistema y falla en otro. (O mejor aún, funciona en IDE y se interrumpe en la consola).
- Puede haber algún código que dependa del