2008-09-20 8 views
5

que empezar por crear una variable de cadena con algunos no-asciiUTF-8 datos codificados en él:¿Por qué unicode() usa str() en mi objeto solo sin codificación?

>>> text = 'á' 
>>> text 
'\xc3\xa1' 
>>> text.decode('utf-8') 
u'\xe1' 

Usando unicode() en él plantea errores ...

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

. ... pero si conozco la codificación, puedo usarla como segundo parámetro:

>>> unicode(text, 'utf-8') 
u'\xe1' 
>>> unicode(text, 'utf-8') == text.decode('utf-8') 
True 

Ahora bien, si tengo una clase que devuelve este texto en el método __str__():

>>> class ReturnsEncoded(object): 
...  def __str__(self): 
...   return text 
... 
>>> r = ReturnsEncoded() 
>>> str(r) 
'\xc3\xa1' 

unicode(r) parece utilizar str() en él, ya que plantea el mismo error que unicode(text) arriba:

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

Hasta ahora ¡todo está según lo planeado!

Pero como nadie nunca esperar, unicode(r, 'utf-8') ni siquiera se trate:

>>> 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 qué este comportamiento inconsistente? ¿Es un error? es intencionado? Muy incomodo.

Respuesta

7

El comportamiento parece confuso, pero intensional. Reproduzco aquí la totalidad de la documentación de Unicode del Python Built-In Functions documentation (para la versión 2.5.2, mientras escribo esto):

Unicode ([objeto [, codificación [, errores]]])

devolver la versión de cadena Unicode de objeto usando uno de los siguientes modos:

Si se proporcionan codificaciones y/o errores, unicode() decodificará el objeto que puede ser una cadena de 8 bits o un buffer de caracteres utilizando el códec para la codificación. El parámetro de codificación es una cadena que proporciona el nombre de una codificación; si no se conoce la codificación, se genera LookupError. El manejo de errores se realiza de acuerdo con errores; esto especifica que el tratamiento de los caracteres que son no es válido en la codificación de entrada. Si los errores son 'estrictos' ( por defecto), un ValueError se genera en errores, mientras que un valor de 'ignora' hace que los errores se ignoren silenciosamente, y un valor de 'replace' causa el carácter de reemplazo Unicode oficial, U + FFFD, que se utilizará para reemplazar los caracteres de entrada que no pueden ser decodificados. Ver también el módulo codecs.

Si no se proporcionan parámetros opcionales, unicode() imitará el comportamiento de str() excepto que devuelve cadenas Unicode en lugar de cadenas de 8 bits. Más precisamente, si el objeto es una cadena o subclase Unicode , devolverá esa cadena Unicode sin con cualquier descodificación adicional aplicada.

Para los objetos que proporcionan un método __unicode __(), llamará a este método sin argumentos para crear una cadena Unicode. Para todos los demás objetos, la versión o representación de cadena de 8 bits es solicitada y luego convertida a una cadena Unicode utilizando el códec para la codificación predeterminada en modo 'estricto'.

Nuevo en la versión 2.0. Modificado en la versión 2.2: Se agregó soporte para __unicode __().

Por lo tanto, cuando se llama unicode(r, 'utf-8'), se requiere una cadena de 8 bits o un búfer de caracteres como primer argumento, por lo que coacciona su objeto mediante el método __str__(), y los intentos de descifrar que el uso de los códec utf-8. Sin el utf-8, la función unicode() busca un método __unicode__() en su objeto, y al no encontrarlo, llama al método __str__(), como sugirió, intentando utilizar el códec predeterminado para convertir a unicode.

4

unicode no adivina la codificación de su texto. Si su objeto puede imprimirse como unicode, defina el método __unicode__() que devuelve una cadena Unicode.


El secreto es que en realidad no es unicode(r) llamando __str__() sí. En cambio, está buscando un método __unicode__(). La implementación predeterminada de __unicode__() llamará al __str__() y luego intentará decodificarlo usando el juego de caracteres ascii. Cuando pase la codificación, unicode() espera que el primer objeto sea algo que pueda decodificarse, es decir, una instancia de basestring.


El comportamiento es raro porque intenta decodificar como ASCII, si no paso 'UTF-8'. Pero si paso 'utf-8' da un error diferente ...

Esto se debe a que cuando especifica "utf-8", trata el primer parámetro como un objeto similar a una cuerda para decodificar. Sin él, trata el parámetro como un objeto que se forzará a unicode.

No entiendo la confusión. Si sabe que el atributo text del objeto siempre estará codificado en UTF-8, simplemente defina __unicode__() y todo funcionará bien.

+0

Creo que no me he dejado claro. Yo sé eso. Lo que quiero decir es saber POR QUÉ Unicode (r) tiene un comportamiento diferente que Unicode (r, 'utf-8') ??? – nosklo

+0

El comportamiento es extraño porque trata de descodificar como ascii si no paso 'utf-8'. Pero si paso 'utf-8' da un error diferente ... – nosklo

Cuestiones relacionadas