¿Por qué NO deberíamos usar sys.setdefaultencoding("utf-8") en un script py?

Resuelto mlzboy asked hace 13 años • 4 respuestas

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")
mlzboy avatar Sep 30 '10 14:09 mlzboy
Aceptado

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.pyUna vez evaluado este módulo, la setdefaultencoding()función se elimina del sysmó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
pyfunc avatar Sep 30 '2010 07:09 pyfunc

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?

sys.setdefaultencoding("utf-8")(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:

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)

sys.setdefaultencoding("utf-8")permitirá que estos funcionen para , 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.

Consola

sys.setdefaultencoding("utf-8")También tiene el efecto secundario de parecer arreglar 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 PYTHONIOENCODINGcorregir 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. UnicodeErrorSe 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

Alastair McCormack avatar Dec 20 '2015 07:12 Alastair McCormack
#!/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á

Sérgio avatar Jul 19 '2011 03:07 Sérgio
  • 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()bien

    Todo lo que afecta es la conversión implícitastr<->unicode . Ahora bien, utf-8si 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 UnicodeErrorlanzamiento 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).
ivan_pozdeev avatar Apr 21 '2018 22:04 ivan_pozdeev