Python __str__ versus __unicode__

213

¿Existe una convención de Python sobre cuándo debe implementar __str__()versus __unicode__()? He visto que las clases se anulan con __unicode__()más frecuencia __str__()pero no parece ser consistente. ¿Existen reglas específicas cuando es mejor implementar una versus la otra? ¿Es necesario / una buena práctica implementar ambos?

Cory
fuente

Respuestas:

257

__str__()es el método antiguo: devuelve bytes. __unicode__()es el nuevo método preferido: devuelve caracteres. Los nombres son un poco confusos, pero en 2.x estamos atrapados con ellos por razones de compatibilidad. En general, debe poner todo su formato de cadena __unicode__()y crear un __str__()método de código auxiliar :

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

En 3.0, strcontiene caracteres, por lo que se nombran los mismos métodos __bytes__()y __str__(). Estos se comportan como se esperaba.

John Millikin
fuente
2
¿Te refieres a crear los métodos unicode y str o simplemente mantener las cadenas en _ (u "") y crear cadenas (sin el método unicode)?
muntu
12
¿Hay algún inconveniente en implementar solo uno de ellos? ¿Qué sucede cuando solo implementa __unicode__y luego lo hace str(obj)?
RickyA
99
unicodeplantea un NameErrorPython 3, ¿es un patrón simple que funciona tanto en 2 como en 3?
bradley.ayers
1
@ bradley.ayers el futurepaquete también proporciona python_2_unicode_compatiblesin tener Django como dependencia.
Monkpit
1
Depende. Porque python3 no usa unicode, sino str ;) para python 2 unicode
Eddwin Paz
23

Si no me importara especialmente la cadena de micro-optimización para una clase dada, siempre la implementaría __unicode__solo, ya que es más general. Cuando me preocupo por esos problemas de rendimiento minuciosos (que es la excepción, no la regla), tener __str__solo (cuando puedo probar que nunca habrá caracteres no ASCII en la salida en cadena) o ambos (cuando ambos son posibles), podría ayuda.

Creo que estos son principios sólidos, pero en la práctica es muy común SABER que no habrá más que caracteres ASCII sin hacer un esfuerzo para demostrarlo (por ejemplo, la forma en cadena solo tiene dígitos, puntuación y quizás un nombre ASCII corto ;-) en el que en caso de que sea bastante típico pasar directamente al __str__enfoque "justo " (pero si un equipo de programación con el que trabajé propusiera una directriz local para evitar eso, estaría +1 en la propuesta, ya que es fácil equivocarse en estos asuntos Y "La optimización prematura es la raíz de todo mal en la programación" ;-).

Alex Martelli
fuente
2
En python 2.6.2, recientemente me tropecé porque las instancias de una subclase de excepción incorporada en particular dieron resultados diferentes con str (e) y unicode (e). str (e) dio una salida fácil de usar; Unicode (e) dio una salida diferente, amigable para el usuario. ¿Se considera esto un comportamiento defectuoso? La clase es UnicodeDecodeError; No lo mencioné por adelantado para evitar confusiones; el hecho de que la excepción esté relacionada con Unicode no es particularmente relevante.
Paul Du Bois
13

Con el mundo cada vez más pequeño, es probable que cualquier cadena que encuentre contenga Unicode eventualmente. Entonces, para cualquier aplicación nueva, al menos debe proporcionar __unicode__(). Si usted también anula __str__()es solo una cuestión de gustos.

Aaron Digulla
fuente
8

Si está trabajando en python2 y python3 en Django, le recomiendo el decorador compatible python_2_unicode_compatible:

Django proporciona una forma sencilla de definir los métodos str () y unicode () que funcionan en Python 2 y 3: debe definir un método str () que devuelva texto y aplicar el decorador python_2_unicode_compatible ().

Como se señaló en los comentarios anteriores a otra respuesta, algunas versiones de future.utils también admiten este decorador. En mi sistema, necesitaba instalar un módulo futuro más nuevo para python2 e instalar future para python3. Después de eso, aquí hay un ejemplo funcional:

#! /usr/bin/env python

from future.utils import python_2_unicode_compatible
from sys import version_info

@python_2_unicode_compatible
class SomeClass():
    def __str__(self):
        return "Called __str__"


if __name__ == "__main__":
    some_inst = SomeClass()
    print(some_inst)
    if (version_info > (3,0)):
        print("Python 3 does not support unicode()")
    else:
        print(unicode(some_inst))

Aquí hay un ejemplo de salida (donde venv2 / venv3 son instancias virtualenv):

~/tmp$ ./venv3/bin/python3 demo_python_2_unicode_compatible.py 
Called __str__
Python 3 does not support unicode()

~/tmp$ ./venv2/bin/python2 demo_python_2_unicode_compatible.py 
Called __str__
Called __str__
sabio
fuente
3

Python 2: Implemente solo __str __ () y devuelva un Unicode.

Cuando __unicode__()se omite y alguien llama unicode(o)o u"%s"%o, Python llama o.__str__()y convierte a unicode usando la codificación del sistema. (Ver documentación de__unicode__() .)

Lo contrario no es verdad. Si implementa __unicode__()pero no __str__(), cuando alguien llama str(o)o "%s"%o, Python regresa repr(o).


Razón fundamental

¿Por qué sería trabajar para volver a unicodepartir de __str__()?
Si __str__()devuelve un Unicode, Python lo convierte automáticamente para strusar la codificación del sistema.

Cual es el beneficio?
① Le libera de preocuparse acerca de cuál es la codificación del sistema (es decir, locale.getpreferredencoeding(…)). No solo es desordenado, personalmente, sino que creo que es algo de lo que el sistema debería ocuparse de todos modos. ② Si tiene cuidado, su código puede ser incompatible con Python 3, en el que se __str__()devuelve unicode.

¿No es engañoso devolver un unicode de una función llamada __str__()?
Un poco. Sin embargo, es posible que ya lo estés haciendo. Si tiene from __future__ import unicode_literalsla parte superior de su archivo, es muy probable que devuelva un Unicode sin siquiera saberlo.

¿Qué hay de Python 3?
Python 3 no usa __unicode__(). Sin embargo, si implementa de __str__()manera que devuelva unicode en Python 2 o Python 3, esa parte de su código será compatible.

¿Qué pasa si quiero unicode(o)ser sustancialmente diferente de str()?
Implemente ambos __str__()(posiblemente regresando str) y __unicode__(). Me imagino que esto sería raro, pero es posible que desee una salida sustancialmente diferente (por ejemplo, versiones ASCII de caracteres especiales, como ":)"for u"☺").

Me doy cuenta de que algunos pueden encontrar esto controvertido.

Alex Quinn
fuente
1

Vale la pena señalar a aquellos que no están familiarizados con la __unicode__función algunos de los comportamientos predeterminados que lo rodean en Python 2.x, especialmente cuando se definen junto con __str__.

class A :
    def __init__(self) :
        self.x = 123
        self.y = 23.3

    #def __str__(self) :
    #    return "STR      {}      {}".format( self.x , self.y)
    def __unicode__(self) :
        return u"UNICODE  {}      {}".format( self.x , self.y)

a1 = A()
a2 = A()

print( "__repr__ checks")
print( a1 )
print( a2 )

print( "\n__str__ vs __unicode__ checks")
print( str( a1 ))
print( unicode(a1))
print( "{}".format( a1 ))
print( u"{}".format( a1 ))

produce la siguiente salida de consola ...

__repr__ checks
<__main__.A instance at 0x103f063f8>
<__main__.A instance at 0x103f06440>

__str__ vs __unicode__ checks
<__main__.A instance at 0x103f063f8>
UNICODE 123      23.3
<__main__.A instance at 0x103f063f8>
UNICODE 123      23.3

Ahora cuando descomento el __str__método

__repr__ checks
STR      123      23.3
STR      123      23.3

__str__ vs __unicode__ checks
STR      123      23.3
UNICODE  123      23.3
STR      123      23.3
UNICODE  123      23.3
jxramos
fuente