it-swarm.dev

Por que não devemos usar sys.setdefaultencoding ("utf-8") em um script py?

Eu vi alguns scripts py que usam isso no topo do script. Em que casos deve-se usá-lo?

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

De acordo com a documentação: Isso permite alternar do padrão ASCII para outras codificações como UTF-8, que o tempo de execução do Python usará sempre que tiver que decodificar um buffer de string para unicode.

Esta função só está disponível em Python tempo de inicialização, quando Python examina o ambiente. Tem que ser chamado em um módulo de todo o sistema, sitecustomize.py, Após este módulo ter sido avaliado, a função setdefaultencoding() é removida do módulo sys.

A única maneira de realmente usá-lo é com um hack de recarga que traz o atributo de volta.

Além disso, o uso de sys.setdefaultencoding() sempre foi desencorajado , e tornou-se um não operacional em py3k. A codificação do py3k é conectada a "utf-8" e sua alteração gera um erro.

Eu sugiro alguns ponteiros para leitura:

133
pyfunc

tl; dr

A resposta é NUNCA(a menos que você realmente saiba o que está fazendo)

9/10 vezes a solução pode ser resolvida com uma compreensão adequada de codificação/decodificação.

1/10 pessoas têm um local ou ambiente definido incorretamente e precisam definir:

PYTHONIOENCODING="UTF-8"  

em seu ambiente para corrigir problemas de impressão do console.

O que isso faz?

sys.setdefaultencoding("utf-8") (pressionado para evitar reutilização) altera a codificação/decodificação padrão usada sempre que Python 2.x precisa converter um Unicode () em um str () (e vice-versa) e a codificação não é dado. Ou seja:

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

Em Python 2.x, a codificação padrão é definida como ASCII e os exemplos acima falharão com:

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

(Meu console está configurado como UTF-8, então "€" = '\xe2\x82\xac', portanto, exceção em \xe2)

ou

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

sys.setdefaultencoding("utf-8") permitirá que estes trabalhem para me, mas não necessariamente funcionarão para pessoas que não usam UTF-8. O padrão de ASCII garante que as suposições de codificação não estejam embutidas no código

Console

sys.setdefaultencoding("utf-8") também tem um efeito colateral de aparecer para corrigir sys.stdout.encoding, usado ao imprimir caracteres no console. Python usa a localidade do usuário (Linux/OS X/Un * x) ou a página de códigos (Windows) para definir isso. Ocasionalmente, a localidade de um usuário é quebrada e requer apenas PYTHONIOENCODING para corrigir o console de codificação.

Exemplo:

$ 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"
€

O que é tão ruim com sys.setdefaultencoding ("utf-8")?

As pessoas têm desenvolvido contra Python 2.x por 16 anos com o entendimento de que a codificação padrão é ASCII. Os métodos de manipulação de exceção UnicodeError foram gravados para manipular a cadeia de caracteres para conversões Unicode em cadeias que contêm não-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 definir defaultencoding este código não seria capaz de decodificar o "Å" na codificação ASCII e, em seguida, entraria no manipulador de exceção para adivinhar a codificação e transformá-lo corretamente em unicode. Impressão: Angstrom (Å®) administra seus negócios. Depois de definir a codificação padrão para utf-8, o código descobrirá que o byte_string pode ser interpretado como utf-8 e, portanto, irá distorcer os dados e retornar isso: Angstrom (Ů) executa seus negócios.

Mudar o que deve ser uma constante terá efeitos dramáticos nos módulos dos quais você depende. É melhor apenas consertar os dados que entram e saem do seu código.

Exemplo de problema

Embora a configuração da codificação padrão para UTF-8 não seja a causa raiz no exemplo a seguir, ela mostra como os problemas são mascarados e como, quando a codificação de entrada é alterada, o código é interrompido de maneira incomum: nicodeDecodeError: ' utf8 'codec não pode decodificar byte 0x80 na posição 3131: byte inicial inválido

52
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)

em Shell funciona, enviando para sdtout não, de modo que é uma solução alternativa, para gravar em stdout.

Fiz outra abordagem, que não é executada se sys.stdout.encoding não for definir ou, em outras palavras, precisar exportar PYTHONIOENCODING = UTF-8 primeiro para gravar no 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)


então, usando o mesmo exemplo:

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

vai funcionar

18
Sérgio
  • O primeiro perigo está em reload(sys).

    Quando você recarrega um módulo, você obtém duas cópias do módulo em seu tempo de execução. O módulo antigo é um objeto Python como todo o resto, e permanece ativo enquanto houver referências a ele. Então, metade dos objetos estará apontando para o antigo módulo, e metade para o novo. Quando você faz alguma mudança, você nunca verá isso vindo quando algum objeto aleatório não vê a mudança. :

    (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>
    
  • Agora, sys.setdefaultencoding() propriamente dito

    Tudo o que isso afeta é conversão implícita str<->unicode . Agora, utf-8 é a codificação mais segura do planeta (compatível com ASCII e tudo), a conversão agora "simplesmente funciona", o que poderia dar errado?

    Bem, qualquer coisa. E esse é o perigo.

    • Pode haver algum código que depende do UnicodeError sendo lançado para entrada não-ASCII ou da transcodificação com um manipulador de erro, que agora produz um resultado inesperado. E ma vez que todo o código é testado com a configuração padrão, você está estritamente em território "não suportado" aqui, e ninguém lhe dá garantias sobre como o código irá se comportar.
    • A transcodificação pode produzir resultados inesperados ou inutilizáveis ​​se nem tudo no sistema usar UTF-8 porque Python 2 na verdade tem várias codificações de strings padrão "independentes" . (Lembre-se, um programa deve funcionar para o cliente, no equipamento do cliente.)
      • Novamente, o pior é você nunca saberá disso porque a conversão é implícita - você não sabe quando e onde isso acontece. ( Python Zen, koan 2 ahoy!) Você nunca saberá porque (e se) seu código funciona em um sistema e se quebra em outro. (Ou melhor ainda, funciona em IDE e quebra no console.)
3
ivan_pozdeev