2012-07-03 13 views
9

Me dieron a entender que llamando al print obj llamaría al obj.__str__(), que a su vez devolvería una cadena para imprimir en la consola. Ahora me dirijo un problema con Unicode donde no pude imprimir ningún carácter no ascii. Obtuve el típico "ascii fuera de alcance".Diferencia de Python entre imprimir obj e imprimir obj .__ str __() [al menos con Unicode?]

Mientras se experimenta lo siguiente trabajaron:

print obj.__str__() 
print obj.__repr__() 

Con ambas funciones haciendo exactamente lo mismo (__str__() sólo devuelve self.__repr__()). Lo que no funcionó:

print obj 

El problema ocurrió solo con el uso de un carácter fuera del rango ascii. La solución final fue la siguiente en __str__():

return self.__repr__().encode(sys.stdout.encoding) 

Ahora funciona para todas las partes. Mi pregunta ahora es: ¿Dónde está la diferencia? ¿Por qué funciona ahora? Me sale si nada funcionó, por qué esto funciona ahora. Pero, ¿por qué solo funciona la parte superior, no la inferior?

El sistema operativo es Windows 7 x64 con un símbolo del sistema predeterminado de Windows. También se informa que la codificación es cp850. Esta es más una pregunta general para entender Python. Mi problema ya está resuelto, pero no estoy 100% contento, sobre todo porque ahora llamando al str(obj) producirá una cadena que no está codificada de la manera que yo quería.

# -*- coding: utf-8 -*- 
class Sample(object): 

    def __init__(self): 
     self.name = u"üé" 

    def __repr__(self): 
     return self.name 

    def __str__(self): 
     return self.name 

obj = Sample() 
print obj.__str__(), obj.__repr__(), obj 

Quite el último obj y funciona. Sigue así y se estrella con

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128) 
+0

¿Qué versión de Python son estás corriendo? –

+0

Muestra un ejemplo mínimo de la clase obj con muestras de las cadenas que imprimes. –

+2

¿Quizás estabas buscando 'obj .__ unicode __()'? –

Respuesta

4

Mi conjetura es que la impresión hace algo como lo siguiente para un objeto obj que está destinado a imprimir:

  1. Comprueba si el obj es un unicode. Si es así, lo codifica en sys.stdout.encoding e imprime.
  2. Comprueba si el obj es un str. Si es así, imprime directamente.
  3. Si obj es algo más, llama al str(obj) e imprime eso.

Paso 1. es por eso que print obj.__str__() funciona en su caso.

Ahora, lo que hace es str(obj):

  1. llamada obj.__str__().
  2. Si el resultado es una str, devolverlo
  3. Si el resultado es una unicode, lo codifica a "ascii" y volver que
  4. De lo contrario, algo que en su mayoría inútiles.

Llamar al obj.__str__() salta directamente los pasos 2-3, por lo que no se obtiene la falla de codificación.

El problema no se debe a la forma en que funciona print, sino a cómo funciona str(). str() ignora sys.stdout.encoding. Como no sabe qué quiere hacer con la cadena resultante, la codificación predeterminada que utiliza puede considerarse arbitraria; ascii es una elección tan buena o tan mala como cualquier otra.

Para evitar este error, asegúrese de devolver str de __str__() como lo indica la documentación. Un patrón se puede utilizar para Python 2.x podría ser:

class Foo(): 
    def __unicode__(self): 
     return u'whatever' 
    def __str__(self): 
     return unicode(self).encode(sys.stdout.encoding) 

(Si está seguro de que no es necesario la representación str() para nada más que la impresión de la consola.)

+0

Gracias esa es la explicación perfecta que estaba buscando. Esto seguramente explica mi problema. Ahora, ¿qué sucede si * * quiero tener más que la salida de la consola? ¿Cuál sería una buena solución? Mi enfoque fue definir un segundo parámetro como este: '__str __ (self, encoding = sys.stdout.encoding)'. ¿Esto parece una buena idea? – javex

+1

@ user1461135 No hay realmente una situación en la que pase parámetros adicionales a '__str __()', ya que no está destinado a llamarlo directamente. Solo usaría 'unicode (obj) .encode ('yadda')' donde quiera llamar 'obj .__ str __ (encoding = 'yadda')', es menos probable que sorprenda a las personas. – millimoose

+0

¡Gracias ** muy ** mucho! – javex

1

En primer lugar, si nos fijamos en the online documentation, __str__ y __repr__ tienen diferentes propósitos y debe crear diferentes salidas. Por lo tanto, llamar a __repr__ desde __str__ no es la mejor solución.

En segundo lugar, print llamarán __str__ y no esperar a recibir caracteres no ASCII, porque, bueno, print no puede adivinar cómo convertir el carácter no-ASCII.

Finalmente, en las versiones recientes de Python 2.x, __unicode__ es el método preferido para crear una representación de cadena para un objeto. Hay una explicación interesante en Python str versus unicode.

Por lo tanto, para tratar y realmente responder a la pregunta, usted podría hacer algo como:

class Sample(object): 

    def __init__(self): 
     self.name = u"\xfc\xe9" 

    # No need to implement __repr__. Let Python create the object repr for you 

    def __str__(self): 
     return unicode(self).encode('utf-8') 

    def __unicode__(self): 
     return self.name 
+1

Técnicamente, en versiones realmente recientes de Python (3.x), la distinción ya no existe. – millimoose

+0

@millimoose Tienes razón. Estoy considerando Python 2.6+ – Rodrigue

+0

'__unicode__' en realidad podría ser incluso anterior a 2.6 – Rodrigue

Cuestiones relacionadas