utf8 unicodeencodeerror setdefaultencoding open has bytes attribute python encoding utf-8 python-2.x sys

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



python open encoding (4)

He visto pocos scripts py que usan esto en la parte superior del script. ¿En qué casos uno debería usarlo?

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


tl; dr

¡La respuesta es NUNCA ! (a menos que realmente sepa lo que está haciendo)

9/10 veces la solución se puede resolver con una comprensión adecuada de la codificación / decodificación.

1/10 personas tienen una configuración regional o un entorno incorrectamente definidos y necesitan establecer:

PYTHONIOENCODING="UTF-8"

en su entorno para solucionar los 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 cuando Python 2.x necesita convertir un Unicode () en un str () (y viceversa) y la codificación no está dada. Es decir:

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

En Python 2.x, la codificación 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, 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 funcionen para , pero no necesariamente funcionará para las personas que no usan UTF-8. El valor predeterminado de ASCII asegura que las suposiciones de codificación no estén codificadas en el código

Consola

sys.setdefaultencoding("utf-8") también tiene un efecto secundario de aparecer para corregir 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á rota y solo requiere PYTHONIOENCODING para 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é hay de malo con sys.setdefaultencoding ("utf-8") ?

La gente ha estado desarrollando Python 2.x durante 16 años en el entendimiento de que la codificación predeterminada es ASCII. Los métodos de manejo de excepciones UnicodeError se han escrito para manejar cadenas de conversión Unicode en cadenas que contengan componentes que no sean 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 establecer la codificación predeterminada, este código no podría decodificar el "Å" en la codificación ascii y luego ingresaría el manejador de excepciones para adivinar la codificación y convertirla en unicode. Impresión: Angstrom (Å®) dirige su negocio. Una vez que haya establecido la codificación predeterminada en utf-8, el código encontrará que byte_string se puede interpretar como utf-8, por lo que modificará los datos y lo devolverá en su lugar: 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 arreglar los datos que entran y salen de tu código.

Problema de ejemplo

Si bien la configuración de la codificación predeterminada para UTF-8 no es la causa raíz en el siguiente ejemplo, muestra cómo se enmascaran los problemas y cómo, cuando la codificación de entrada cambia, el código se rompe de una manera no obvia: UnicodeDecodeError: el códec ''utf8'' puede ''decodificar byte 0x80 en la posición 3131: byte de inicio no válido


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á cada vez que tenga que decodificar un búfer de cadenas para unicode.

Esta función solo está disponible en el momento de inicio de Python, cuando Python escanea el entorno. Se debe sitecustomize.py 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 manera de usarlo realmente es con un truco de recarga que recupera el atributo.

Además, el uso de sys.setdefaultencoding() siempre se ha desaconsejado , y se ha convertido en una operación no operativa en py3k. La codificación de py3k está cableada a "utf-8" y al cambiarla se genera un error.

Sugiero algunos consejos para leer:


#!/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 trabajos de shell, enviando a sdtout no, por lo que es una solución alternativa, para 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 radica en reload(sys) .

    Cuando recarga un módulo, en realidad obtiene dos copias del módulo en su tiempo de ejecución. El antiguo módulo es un objeto Python como todo lo demás, y se mantiene activo mientras haya referencias a él. Por lo tanto, la mitad de los objetos apuntarán al módulo anterior y la mitad al nuevo. Cuando haces algún cambio, nunca lo verás 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() propiamente dicho

    Todo lo que afecta es la conversión implícita str<->unicode . Ahora, utf-8 es la codificación más segura del planeta (compatible con versiones anteriores 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 algún código que dependa de que se UnicodeError para una entrada que no sea ASCII, o la transcodificación 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 el territorio "no compatible" aquí , y nadie le da 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).
      • De nuevo, lo peor es que nunca lo sabrás porque la conversión es implícita : no sabes realmente cuándo y dónde sucede. (Python Zen, koan 2 ahoy!) Nunca sabrá por qué (y si) su código funciona en un sistema y se rompe en otro. (O mejor aún, funciona en IDE y se rompe en la consola).