Python __str__ против __unicode__

213

Есть ли соглашение Python, когда вы должны реализовать __str__()против __unicode__(). Я видел, как классы переопределяют __unicode__()чаще, чем, __str__()но они не выглядят согласованными. Существуют ли конкретные правила, когда лучше реализовать одно против другого? Это необходимо / хорошая практика для реализации обоих?

Cory
источник

Ответы:

257

__str__()это старый метод - он возвращает байты. __unicode__()это новый, предпочтительный метод - он возвращает символы. Имена немного сбивают с толку, но в 2.x мы застряли с ними из соображений совместимости. Как правило, вы должны поместить все форматирование строк __unicode__()и создать __str__()метод- заглушку :

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

В strверсии 3.0 содержатся символы, поэтому такие же методы называются __bytes__()и __str__(). Они ведут себя как ожидалось.

Джон Милликин
источник
2
Вы имеете в виду создание методов как unicode, так и str или просто держите строки в _ (u "") и создаете строку (без метода unicode)?
Muntu
12
Есть ли подводные камни в реализации только одного из них? Что происходит, когда вы только внедряете, __unicode__а потом делаете str(obj)?
RickyA
9
unicodeподнимает NameErrorна Python 3, это простой шаблон, который работает на 2 и 3?
bradley.ayers
1
@ bradley.ayers futureпакет также предоставляет python_2_unicode_compatibleбез Django в качестве зависимости.
Monkpit
1
Это зависит. Поскольку python3 не использует юникод, а вместо него str ;) для юникода
Eddwin Paz
23

Если бы я не особенно заботился о микрооптимизации для данного класса, я бы всегда реализовывал __unicode__только ее, так как она более общая. Когда я забочусь о таких незначительных проблемах производительности (что является исключением, а не правилом), имея__str__ только один (когда я могу доказать, что в строковом выводе никогда не будет символов, не являющихся ASCII) или оба (когда возможны оба) Помогите.

Я думаю, что это твердые принципы, но на практике это очень распространено в KNOW, что не будет ничего, кроме символов ASCII, без усилий доказать это (например, в строковой форме есть только цифры, пунктуация и, возможно, короткое имя ASCII ;-), в котором в случае, если довольно типично перейти непосредственно к «справедливому __str__» подходу (но если бы команда программистов, с которой я работал, предложила местное руководство, чтобы избежать этого, я был бы +1 в предложении, поскольку в этих вопросах легко ошибиться И «преждевременная оптимизация - корень всего зла в программировании» ;-).

Алекс Мартелли
источник
2
В python 2.6.2 меня недавно запутали, потому что экземпляры определенного встроенного подкласса Exception давали разные результаты с str (e) и unicode (e). str (e) дал удобный вывод; Unicode (e) дал другой, недружественный пользователю вывод. Это считается ошибочным поведением? Класс UnicodeDecodeError; Я не назвал это заранее, чтобы избежать путаницы - тот факт, что исключение связано с юникодом, не имеет особого значения.
Пол Дю Буа
13

Поскольку мир становится все меньше, есть вероятность, что любая строка, с которой вы столкнетесь, в конечном итоге будет содержать Unicode. Так что для любых новых приложений вы должны по крайней мере предоставить __unicode__(). Вне __str__()зависимости от того, переопределите ли вы, это просто вопрос вкуса.

Аарон Дигулла
источник
8

Если вы работаете в python2 и python3 в Django, я рекомендую декоратор python_2_unicode_compatible:

Django предоставляет простой способ определения методов str () и unicode (), которые работают на Python 2 и 3: вы должны определить метод str (), возвращающий текст, и применить декоратор python_2_unicode_compatible ().

Как отмечалось в предыдущих комментариях к другому ответу, некоторые версии future.utils также поддерживают этот декоратор. В моей системе мне нужно было установить более новый модуль future для python2 и установить future для python3. После этого, вот функциональный пример:

#! /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))

Вот пример выходных данных (где venv2 / venv3 являются экземплярами 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__
шалфей
источник
3

Python 2: Реализуйте только __str __ () и возвращайте юникод.

Когда __unicode__()опущено и кто-то вызывает unicode(o)или u"%s"%o, Python вызывает o.__str__()и конвертирует в Unicode, используя системную кодировку. (См. Документацию__unicode__() .)

Обратное не верно. Если вы реализуете, __unicode__()но нет __str__(), тогда, когда кто-то вызывает str(o)или "%s"%o, Python возвращается repr(o).


обоснование

Почему бы работать, чтобы вернуть unicodeиз __str__()?
Если __str__()возвращает юникод, Python автоматически конвертирует его strв системную кодировку.

В чем выгода?
① Это освобождает вас от беспокойства о том, что такое системная кодировка (т.е. locale.getpreferredencoeding(…)). Мало того, что это грязно, лично, но я думаю, что система должна заботиться в любом случае. ② Если вы осторожны, ваш код может оказаться кросс-совместимым с Python 3, в котором __str__()возвращается юникод.

Разве не обманчиво возвращать юникод из вызываемой функции __str__()?
Немного. Тем не менее, вы, возможно, уже делаете это. Если у вас есть from __future__ import unicode_literalsверхняя часть файла, есть большая вероятность, что вы вернете юникод, даже не зная об этом.

А как насчет Python 3?
Python 3 не использует __unicode__(). Однако, если вы реализуете __str__()так, что он возвращает юникод в Python 2 или Python 3, то эта часть вашего кода будет кросс-совместимой.

Что, если я хочу unicode(o)существенно отличаться от str()?
Реализуйте оба __str__()(возможно, возвращение str) и __unicode__(). Я предполагаю, что это было бы редко, но вы могли бы хотеть существенно различного вывода (например, ASCII-версии специальных символов, например, ":)"для u"☺").

Я понимаю, что некоторые могут найти это спорным.

Алекс Куинн
источник
1

Стоит указать тем, кто не знаком с этой __unicode__функцией, некоторые из поведений по умолчанию, окружающих ее еще в Python 2.x, особенно когда они определены рядом с __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 ))

выдает следующий вывод консоли ...

__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

Теперь, когда я раскомментирую __str__метод

__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
источник