Python __str__ versus __unicode__

213

Existe uma convenção python para quando você deve implementar __str__()versus __unicode__(). Vi classes substituir com __unicode__()mais frequência do que __str__()mas não parece ser consistente. Existem regras específicas quando é melhor implementar uma versus a outra? É necessário / boa prática implementar ambos?

Cory
fonte

Respostas:

257

__str__()é o método antigo - ele retorna bytes. __unicode__()é o novo método preferido - ele retorna caracteres. Os nomes são um pouco confusos, mas no 2.x estamos presos a eles por motivos de compatibilidade. Geralmente, você deve colocar toda a sua formatação de string __unicode__()e criar um __str__()método de stub :

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

No 3.0, strcontém caracteres, portanto, os mesmos métodos são nomeados __bytes__()e __str__(). Estes se comportam conforme o esperado.

John Millikin
fonte
2
você quer dizer criar métodos unicode e str ou simplesmente manter strings em _ (u "") e criar strings (sem o método unicode)?
muntu
12
Existe alguma armadilha na implementação de apenas um deles? O que acontece quando você apenas implementa __unicode__e depois faz str(obj)?
RickyA
9
unicodegera um NameErrorno Python 3, é um padrão simples que funciona tanto no 2 quanto no 3?
Bradley.ayers
1
@ bradley.ayers o futurepacote também fornece python_2_unicode_compatiblesem ter o Django como uma dependência.
Monkpit
1
Depende. Porque python3 não usa unicode mas em vez str ;) para python 2 unicode
Eddwin Paz
23

Se eu não me importasse especialmente com a otimização de micro-otimização para uma determinada classe, eu sempre implementaria __unicode__apenas, pois é mais geral. Quando eu me importo com esses problemas de desempenho minuciosos (que é a exceção, não a regra), ter __str__apenas (quando eu puder provar que nunca haverá caracteres não-ASCII na saída com string) ou ambos (quando ambos forem possíveis), poderá Socorro.

Eu acho que são princípios sólidos, mas, na prática, é muito comum SABER que não haverá nada além de caracteres ASCII sem fazer esforço para provar isso (por exemplo, a forma estrita tem apenas dígitos, pontuação e talvez um nome ASCII curto ;-) no qual caso seja bastante comum seguir diretamente para a __str__abordagem " justa " (mas se uma equipe de programação com a qual trabalhei propusesse uma diretriz local para evitar isso, eu seria +1 na proposta, pois é fácil errar nessas questões) "otimização prematura é a raiz de todo mal na programação" ;-).

Alex Martelli
fonte
2
No python 2.6.2, recentemente fui enganado porque as instâncias de uma subclasse de exceção interna específica apresentaram resultados diferentes com str (e) e unicode (e). str (e) deu saída amigável; O unicode (e) deu uma saída diferente e não amigável ao usuário. Isso é considerado comportamento de buggy? A classe é UnicodeDecodeError; Eu não o nomeei para evitar confusão - o fato de a exceção estar relacionada ao unicode não é particularmente relevante.
Paul Du Bois
13

Com o mundo cada vez menor, é provável que qualquer sequência que você encontre conterá Unicode eventualmente. Portanto, para qualquer novo aplicativo, você deve pelo menos fornecer __unicode__(). Se você também substitui __str__()é apenas uma questão de gosto.

Aaron Digulla
fonte
8

Se você estiver trabalhando em python2 e python3 no Django, recomendo o decorador python_2_unicode_compatible:

O Django fornece uma maneira simples de definir métodos str () e unicode () que funcionam em Python 2 e 3: você deve definir um método str () retornando texto e aplicar o decorador python_2_unicode_compatible ().

Como observado nos comentários anteriores de outra resposta, algumas versões do future.utils também suportam esse decorador. No meu sistema, eu precisava instalar um módulo futuro mais recente para python2 e instalar o futuro para python3. Depois disso, aqui está um exemplo 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))

Aqui está um exemplo de saída (em que venv2 / venv3 são instâncias 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__
sábio
fonte
3

Python 2: implemente apenas __str __ () e retorne um unicode.

Quando __unicode__()é omitido e alguém chama unicode(o)ou u"%s"%o, Python chama o.__str__()e converte em unicode usando a codificação do sistema. (Veja a documentação de__unicode__() .)

O oposto não é verdadeiro. Se você implementar, __unicode__()mas não __str__(), quando alguém chamar str(o)ou "%s"%o, o Python retornará repr(o).


Fundamentação

Por que isso funciona para retornar a unicodepartir de __str__()?
Se __str__()retornar um unicode, o Python o converterá automaticamente strusando a codificação do sistema.

Qual o benefício?
① Isso evita que você se preocupe com o que é a codificação do sistema (ou seja, locale.getpreferredencoeding(…)). Não é apenas isso confuso, pessoalmente, mas acho que é algo que o sistema deve resolver de qualquer maneira. ② Se você for cuidadoso, seu código pode ser compatível com o Python 3, no qual __str__()retorna unicode.

Não é enganoso retornar um unicode de uma função chamada __str__()?
Um pouco. No entanto, você já pode estar fazendo isso. Se você estiver from __future__ import unicode_literalsna parte superior do arquivo, há uma boa chance de retornar um unicode sem nem mesmo saber.

E o Python 3?
Python 3 não usa __unicode__(). No entanto, se você implementar __str__()para que ele retorne unicode no Python 2 ou Python 3, essa parte do seu código será compatível com outras versões.

E se eu quiser unicode(o)ser substancialmente diferente str()?
Implemente ambos __str__()(possivelmente retornando str) e __unicode__(). Eu imagino que isso seria raro, mas você pode querer uma saída substancialmente diferente (por exemplo, versões ASCII de caracteres especiais, como ":)"para u"☺").

Sei que alguns podem achar isso controverso.

Alex Quinn
fonte
1

Vale ressaltar para aqueles que não estão familiarizados com a __unicode__função alguns dos comportamentos padrão que a envolvem no Python 2.x, especialmente quando definidos lado a lado __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 ))

produz a seguinte saída do console ...

__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

Agora, quando eu descomente o __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
fonte