Por que unicode () usa str () no meu objeto somente com nenhuma codificação dado?
Pergunta
Gostaria de começar por criar uma variável string com alguns não-ascii utf-8 dados codificados nele:
>>> text = 'á'
>>> text
'\xc3\xa1'
>>> text.decode('utf-8')
u'\xe1'
Usando unicode()
sobre ele gera erros ...
>>> unicode(text)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0:
ordinal not in range(128)
... mas se eu sei a codificação eu posso usá-lo como segundo parâmetro:
>>> unicode(text, 'utf-8')
u'\xe1'
>>> unicode(text, 'utf-8') == text.decode('utf-8')
True
Agora, se eu tiver uma classe que retorna este texto no método __str__()
:
>>> class ReturnsEncoded(object):
... def __str__(self):
... return text
...
>>> r = ReturnsEncoded()
>>> str(r)
'\xc3\xa1'
unicode(r)
parece usar str()
sobre ele, uma vez que aumenta o mesmo erro que unicode(text)
acima:
>>> unicode(r)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0:
ordinal not in range(128)
Até agora tudo está como planejado!
Mas, como ninguém jamais iria esperar, unicode(r, 'utf-8')
não vai mesmo tentar:
>>> unicode(r, 'utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: coercing to Unicode: need string or buffer, ReturnsEncoded found
Por quê? Por que esse comportamento inconsistente? É um bug? se destina? Muito estranho.
Solução
O comportamento parece confuso, mas intensional. Reproduzo aqui a totalidade da documentação unicode do Python Built-In documentação Funções (para a versão 2.5.2, enquanto escrevo isso):
Unicode ([objeto [, que codifica [, erros]]]) Voltar a versão seqüência de caracteres Unicode de objeto usando um dos seguintes modos:
Se a codificação e / ou erros são dadas, Unicode () irá descodificar o objecto que pode ser uma cadeia de caracteres de 8 bits, ou um buffer de caracteres usando o codec para a codificação. O parâmetro codificação é uma string dando o nome de uma codificação; Se a codificação não é conhecido, LookupError é levantada. tratamento de erros é feito de acordo com a erros; este define o tratamento de caracteres que sejam inválidos na codificação de entrada. Se os erros é 'estrita' (o padrão), um ValueError é gerado em erros, enquanto um valor de 'Ignorar' causa erros para ser ignorado, e um valor de 'Substituir' faz com que o caráter oficial de substituição Unicode, L + FFFD, para ser usado para substituir os caracteres de entrada, que não pode ser decodificado. Veja também a codecs módulo.
Se há parâmetros opcionais são dadas, unicode () irá imitar o comportamento de str (), excepto que ele retorna cadeias Unicode em vez de cordas de 8 bits. Mais precisamente, se o objeto é um Unicode string ou subclasse ele irá retornar essa seqüência Unicode sem qualquer decodificação adicional aplicado.
Para objetos que proporcionam uma __unicode __ () método, ele irá chamar este método sem argumentos para criar uma seqüência de caracteres Unicode. Para todos os outros objetos, a versão seqüência de 8-bit ou representação é solicitado e, em seguida, convertido em uma seqüência de caracteres Unicode usando o codec para o padrão de codificação no modo 'rigorosa'.
Novo na versão 2.0. Alterado na versão 2.2: Suporte para __unicode __ () acrescentou.
Assim, quando você chamar unicode(r, 'utf-8')
, requer uma seqüência de 8 bits ou um buffer de caracteres como o primeiro argumento, por isso coage o seu objeto usando o método __str__()
, e tentativas de decodificação que usando o codec utf-8
. Sem a utf-8
, a aparência de função unicode()
para um para um método __unicode__()
em seu objeto, e não encontrá-lo, chama o método __str__()
, como você sugeriu, tentando usar o codec padrão para converter para unicode.
Outras dicas
unicode
não acho que a codificação do seu texto. Se o objeto pode imprimir-se como unicode
, definir o método __unicode__()
que retorna uma seqüência de caracteres Unicode.
O segredo é que unicode(r)
não está realmente chamando __str__()
si. Em vez disso, ele está à procura de um método __unicode__()
. A implementação padrão de __unicode__()
chamará __str__()
e depois tentar decodificá-lo usando o charset ascii
. Quando você passar a codificação, unicode()
espera que o primeiro objeto a ser algo que pode ser decodificado -. Ou seja, uma instância de basestring
O comportamento é estranho, porque ele tenta decodificar como ascii se eu não passar 'utf-8'. Mas se eu passar 'utf-8' dá um erro diferente ...
Isso porque quando você especificar "utf-8", ele trata o primeiro parâmetro como uma string-como objeto a ser decodificado. Sem ela, ele trata o parâmetro como um objeto a ser coagidos a unicode.
Eu não entendo a confusão. Se você sabe que atributo text
do objeto será sempre UTF-8 codificado, basta definir __unicode__()
e então tudo vai funcionar bem.