¿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 usará cada vez que tenga que decodificar un búfer de cadena a Unicode.

Esta función solo está disponible en el momento de inicio de Python, cuando Python analiza el entorno. Se debe llamar en un módulo de todo el sistema, sitecustomize.py . Después de que se haya evaluado este módulo, la función setdefaultencoding() se elimina del módulo sys .

La única forma de usarlo en realidad es con un hack de recarga que devuelve el atributo.

Además, el uso de sys.setdefaultencoding() siempre se ha desaconsejado , y se ha convertido en un no operativo en py3k. La encoding de py3k está cableada a “utf-8” y su cambio genera un error.

Sugiero algunos consejos para la lectura:

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 la encoding / deencoding.

1/10 personas tienen una configuración o entorno incorrectamente definido 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 encoding / deencoding predeterminada utilizada cada vez que Python 2.x necesita convertir un Unicode () en un str () (y viceversa) y La encoding no está dada. Es decir:

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

En Python 2.x, la encoding predeterminada se establece 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 que "€" = '\xe2\x82\xac' , por lo tanto, la excepción en \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á para las personas que no usan UTF-8. El valor predeterminado de ASCII garantiza que las suposiciones de encoding no se incluyan en el código

Consola

sys.setdefaultencoding("utf-8") también tiene el efecto secundario de parecer que corrige sys.stdout.encoding , que se usa 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. Ocasionalmente, la configuración regional de un usuario está dañada y solo requiere PYTHONIOENCODING para corregir la encoding 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 "", line 1, in  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é es tan malo con sys.setdefaultencoding (“utf-8”) ?

La gente ha estado desarrollando contra Python 2.x durante 16 años en el entendimiento de que la encoding predeterminada es ASCII. Se han escrito métodos de manejo de excepciones de UnicodeError para manejar conversiones de cadena a Unicode en cadenas que se encuentran que no son ASCII.

Desde 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 encoding predeterminada, este código no podría decodificar la “Å” en la encoding ascii y luego ingresaría el controlador de excepciones para adivinar la encoding y convertirla correctamente en unicode. Impresión: Angstrom (Å®) dirige su negocio. Una vez que haya establecido la encoding predeterminada en utf-8, el código encontrará que la cadena de bytes se puede interpretar como utf-8, por lo que se eliminarán los datos y se devolverá esto: Angstrom (Ů) administra su negocio.

Cambiar lo que debería ser una constante tendrá efectos dramáticos en los módulos de los que depende. Es mejor corregir los datos que entran y salen de su código.

Problema de ejemplo

Si bien la configuración de la encoding predeterminada a UTF-8 no es la causa principal en el siguiente ejemplo, muestra cómo se enmascaran los problemas y cómo, cuando cambia la encoding de entrada, el código se interrumpe de forma no obvia: UnicodeDecodeError: el códec ‘utf8’ puede No decodifique 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  print u UnicodeEncodeError: 'ascii' codec can't encode character u'\xe7' in position 2: ordinal not in range(128) 

en shell funciona, enviando a sdtout no, por lo que 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, necesita exportar PYTHONIOENCODING = UTF-8 primero 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 está en reload(sys) .

    Cuando vuelve a cargar un módulo, en realidad obtiene dos copias del módulo en su 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 mitad al nuevo. Cuando realice algún cambio, nunca lo verá venir cuando algún objeto aleatorio no lo vea:

     (This is IPython shell) In [1]: import sys In [2]: sys.stdout Out[2]:  In [3]: reload(sys)  In [4]: sys.stdout Out[4]: ', mode 'w' at 0x00000000022E20C0> In [11]: import IPython.terminal In [14]: IPython.terminal.interactiveshell.sys.stdout Out[14]:  
  • Ahora, sys.setdefaultencoding() propiamente dicho

    Todo lo que afecta es conversión implícita str<->unicode . Ahora, utf-8 es la encoding más segura del planeta (compatible con ASCII y todo), la conversión ahora “simplemente funciona”, ¿qué podría salir mal?

    Bueno, cualquier cosa. Y ese es el peligro.

    • Puede haber algo de código que se basa en el UnicodeError para una entrada no ASCII, o la transencoding con un controlador de errores, que ahora produce un resultado inesperado. Y dado que todo el código se prueba con la configuración predeterminada, usted está estrictamente en territorio “no compatible” aquí , y nadie le garantiza cómo se comportará su código.
    • La transencoding 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 progtwig debe funcionar para el cliente, en el equipo del cliente).
      • Nuevamente, lo peor es que nunca lo sabrá porque la conversión es implícita , realmente no sabe cuándo y dónde ocurre. (Python Zen, koan 2 ¡ahoy!) Nunca sabrás por qué (y si) tu código funciona en un sistema y se rompe en otro. (O mejor aún, funciona en IDE y se rompe en la consola).