Graças aos fragmentos de várias respostas, acho que podemos explicar uma explicação.
Ao tentar imprimir uma cadeia de caracteres unicode, u '\ xe9', o Python tenta codificar implicitamente essa cadeia usando o esquema de codificação atualmente armazenado em sys.stdout.encoding. Na verdade, o Python pega essa configuração no ambiente em que foi iniciada. Se não conseguir encontrar uma codificação adequada do ambiente, só então reverterá para o padrão ASCII.
Por exemplo, eu uso um shell bash que codifica como padrão UTF-8. Se eu iniciar o Python, ele pega e usa essa configuração:
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Vamos sair por um momento do shell Python e definir o ambiente do bash com alguma codificação falsa:
$ export LC_CTYPE=klingon
# we should get some error message here, just ignore it.
Em seguida, inicie o shell python novamente e verifique se ele realmente reverte para a codificação ASCII padrão.
$ python
>>> import sys
>>> print sys.stdout.encoding
ANSI_X3.4-1968
Bingo!
Se você agora tentar gerar algum caractere unicode fora do ascii, deverá receber uma boa mensagem de erro
>>> print u'\xe9'
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9'
in position 0: ordinal not in range(128)
Vamos sair do Python e descartar o shell bash.
Vamos agora observar o que acontece depois que o Python produz strings. Para isso, primeiro iniciaremos um shell bash dentro de um terminal gráfico (eu uso o Gnome Terminal) e definiremos o terminal para decodificar a saída com a ISO-8859-1, também conhecida como latin-1 (os terminais gráficos geralmente têm uma opção para definir caracteres Codificação em um de seus menus suspensos). Observe que isso não altera a codificação do ambiente de shell real , apenas altera a maneira como o próprio terminal decodifica a saída fornecida, um pouco como um navegador da Web. Portanto, você pode alterar a codificação do terminal, independentemente do ambiente do shell. Vamos então iniciar o Python a partir do shell e verificar se sys.stdout.encoding está definido como a codificação do ambiente do shell (UTF-8 para mim):
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
>>> print '\xe9' # (1)
é
>>> print u'\xe9' # (2)
é
>>> print u'\xe9'.encode('latin-1') # (3)
é
>>>
(1) python gera uma string binária como está, o terminal a recebe e tenta combinar seu valor com o mapa de caracteres latin-1. Em latin-1, 0xe9 ou 233 produz o caractere "é" e é isso que o terminal exibe.
(2) o python tenta codificar implicitamente a cadeia Unicode com qualquer esquema atualmente definido em sys.stdout.encoding; nesse caso, é "UTF-8". Após a codificação UTF-8, a sequência binária resultante é '\ xc3 \ xa9' (consulte a explicação posterior). O terminal recebe o fluxo como tal e tenta decodificar 0xc3a9 usando latin-1, mas latin-1 vai de 0 a 255 e, portanto, apenas decodifica os fluxos de 1 byte por vez. 0xc3a9 tem 2 bytes, o decodificador latin-1, portanto, o interpreta como 0xc3 (195) e 0xa9 (169) e que gera 2 caracteres: Ã e ©.
(3) python codifica o ponto de código unicode u '\ xe9' (233) com o esquema latin-1. Acontece que o intervalo de pontos de código latin-1 é de 0 a 255 e aponta exatamente para o mesmo caractere que Unicode dentro desse intervalo. Portanto, os pontos de código Unicode nesse intervalo produzirão o mesmo valor quando codificados em latin-1. Portanto, u '\ xe9' (233) codificado em latin-1 também produzirá a cadeia binária '\ xe9'. O terminal recebe esse valor e tenta correspondê-lo no mapa de caracteres latin-1. Assim como no caso (1), produz "é" e é isso que é exibido.
Vamos agora alterar as configurações de codificação do terminal para UTF-8 no menu suspenso (como você alteraria as configurações de codificação do navegador da web). Não há necessidade de parar o Python ou reiniciar o shell. A codificação do terminal agora corresponde à do Python. Vamos tentar imprimir novamente:
>>> print '\xe9' # (4)
>>> print u'\xe9' # (5)
é
>>> print u'\xe9'.encode('latin-1') # (6)
>>>
(4) python gera uma string binária como está. O terminal tenta decodificar esse fluxo com UTF-8. Mas o UTF-8 não entende o valor 0xe9 (veja a explicação posterior) e, portanto, não pode convertê-lo em um ponto de código unicode. Nenhum ponto de código encontrado, nenhum caractere impresso.
(5) python tenta codificar implicitamente a cadeia Unicode com o que estiver em sys.stdout.encoding. Ainda "UTF-8". A sequência binária resultante é '\ xc3 \ xa9'. O terminal recebe o fluxo e tenta decodificar 0xc3a9 também usando UTF-8. Ele retorna o valor do código 0xe9 (233), que no mapa de caracteres Unicode aponta para o símbolo "é". O terminal exibe "é".
(6) python codifica string unicode com latin-1, produz uma string binária com o mesmo valor '\ xe9'. Novamente, para o terminal, isso é praticamente o mesmo do caso (4).
Conclusões: - Python gera strings não unicode como dados brutos, sem considerar sua codificação padrão. O terminal apenas os exibe se sua codificação atual corresponder aos dados. - Python gera seqüências de caracteres Unicode após codificá-las usando o esquema especificado em sys.stdout.encoding. - Python obtém essa configuração do ambiente do shell. - o terminal exibe a saída de acordo com suas próprias configurações de codificação. - a codificação do terminal é independente da do shell.
Mais detalhes sobre unicode, UTF-8 e latin-1:
Unicode é basicamente uma tabela de caracteres em que algumas chaves (pontos de código) foram convencionalmente atribuídas para apontar para alguns símbolos. Por exemplo, por convenção, foi decidido que a chave 0xe9 (233) é o valor que aponta para o símbolo 'é'. ASCII e Unicode usam os mesmos pontos de código de 0 a 127, assim como latin-1 e Unicode de 0 a 255. Ou seja, 0x41 aponta para 'A' em ASCII, latin-1 e Unicode, 0xc8 aponta para 'Ü' em latin-1 e Unicode, 0xe9 aponta para 'é' em latin-1 e Unicode.
Ao trabalhar com dispositivos eletrônicos, os pontos de código Unicode precisam de uma maneira eficiente de serem representados eletronicamente. É disso que tratam os esquemas de codificação. Existem vários esquemas de codificação Unicode (utf7, UTF-8, UTF-16, UTF-32). A abordagem de codificação mais intuitiva e direta seria simplesmente usar o valor de um ponto de código no mapa Unicode como valor para sua forma eletrônica, mas atualmente o Unicode possui mais de um milhão de pontos de código, o que significa que alguns deles exigem que sejam necessários 3 bytes. expressa. Para trabalhar eficientemente com texto, um mapeamento 1 para 1 seria bastante impraticável, pois exigiria que todos os pontos de código fossem armazenados exatamente na mesma quantidade de espaço, com um mínimo de 3 bytes por caractere, independentemente da necessidade real.
A maioria dos esquemas de codificação tem deficiências em relação ao requisito de espaço, os mais econômicos não cobrem todos os pontos de código unicode, por exemplo, o ascii cobre apenas os primeiros 128, enquanto o latin-1 cobre os primeiros 256. Outros que tentam ser mais abrangentes também acabam ser um desperdício, uma vez que exigem mais bytes do que o necessário, mesmo para caracteres "baratos" comuns. O UTF-16, por exemplo, usa no mínimo 2 bytes por caractere, incluindo aqueles no intervalo ascii ('B', que é 65, ainda requer 2 bytes de armazenamento no UTF-16). UTF-32 é ainda mais inútil, pois armazena todos os caracteres em 4 bytes.
O UTF-8 resolveu o dilema de maneira inteligente, com um esquema capaz de armazenar pontos de código com uma quantidade variável de espaços de bytes. Como parte de sua estratégia de codificação, o UTF-8 amarra pontos de código com bits de flag que indicam (presumivelmente para decodificadores) seus requisitos de espaço e seus limites.
Codificação UTF-8 de pontos de código unicode no intervalo ascii (0-127):
0xxx xxxx (in binary)
- os x mostram o espaço real reservado para "armazenar" o ponto de código durante a codificação
- O 0 inicial é um sinalizador que indica ao decodificador UTF-8 que esse ponto de código exigirá apenas 1 byte.
- na codificação, o UTF-8 não altera o valor dos pontos de código nesse intervalo específico (ou seja, 65 codificados no UTF-8 também são 65). Considerando que Unicode e ASCII também são compatíveis no mesmo intervalo, faz com que o UTF-8 e o ASCII também sejam compatíveis nesse intervalo.
por exemplo, o ponto de código Unicode para 'B' é '0x42' ou 0100 0010 em binário (como dissemos, é o mesmo em ASCII). Após a codificação em UTF-8, torna-se:
0xxx xxxx <-- UTF-8 encoding for Unicode code points 0 to 127
*100 0010 <-- Unicode code point 0x42
0100 0010 <-- UTF-8 encoded (exactly the same)
Codificação UTF-8 de pontos de código Unicode acima de 127 (não ascii):
110x xxxx 10xx xxxx <-- (from 128 to 2047)
1110 xxxx 10xx xxxx 10xx xxxx <-- (from 2048 to 65535)
- os bits iniciais '110' indicam para o decodificador UTF-8 o início de um ponto de código codificado em 2 bytes, enquanto '1110' indica 3 bytes, 11110 indica 4 bytes e assim por diante.
- os bits de flag '10' internos são usados para sinalizar o início de um byte interno.
- novamente, os x marcam o espaço em que o valor do ponto de código Unicode é armazenado após a codificação.
por exemplo, o ponto de código Unicode 'é' é 0xe9 (233).
1110 1001 <-- 0xe9
Quando UTF-8 codifica esse valor, ele determina que o valor é maior que 127 e menor que 2048; portanto, deve ser codificado em 2 bytes:
110x xxxx 10xx xxxx <-- UTF-8 encoding for Unicode 128-2047
***0 0011 **10 1001 <-- 0xe9
1100 0011 1010 1001 <-- 'é' after UTF-8 encoding
C 3 A 9
O código 0xe9 Unicode aponta após a codificação UTF-8 se tornar 0xc3a9. Qual é exatamente como o terminal o recebe. Se seu terminal estiver configurado para decodificar strings usando latin-1 (uma das codificações herdadas não unicode), você verá à ©, porque acontece que 0xc3 em latin-1 aponta para à e 0xa9 para ©.