Почему unicode () использует str () для моего объекта только без заданной кодировки? - PullRequest
6 голосов
/ 20 сентября 2008

Я начинаю с создания строковой переменной с некоторыми не-ascii utf-8 данными в ней:

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

Использование unicode() вызывает ошибки ...

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

... но если я знаю кодировку, я могу использовать ее в качестве второго параметра:

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

Теперь, если у меня есть класс, который возвращает этот текст в методе __str__():

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

unicode(r), кажется, использует str() на нем, так как он вызывает ту же ошибку, что и unicode(text) выше:

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

До сих пор все идет по плану!

Но, как никто и не ожидал, unicode(r, 'utf-8') даже не будет пытаться:

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

Почему? Почему это противоречивое поведение? Это ошибка? это предназначено? Очень неловко.

Ответы [ 2 ]

7 голосов
/ 20 сентября 2008

Поведение кажется запутанным, но интенциональным. Я воспроизвожу здесь всю документацию по Unicode из документации по встроенным функциям Python (для версии 2.5.2, когда я пишу это):

Unicode ([объект [, кодировка [, ошибки]]])

Возвращает строковую версию Unicode объекта, используя один из следующих режимов:

Если заданы кодировка и / или ошибки, unicode () будет декодировать объект, который может быть 8-битной строкой или символьным буфером используя кодек для кодирования. Параметр кодирования является строкой дать название кодировки; если кодировка не известна, LookupError повышается. Обработка ошибок осуществляется в соответствии с ошибки; это определяет обработку символов, которые неверно во входной кодировке. Если ошибки «строгие» ( по умолчанию), ValueError возникает при ошибках, а значение «игнорировать» приводит к тому, что ошибки игнорируются, а значение 'replace' вызывает официальный символ замены Unicode, U + FFFD, используется для замены вводимых символов, которые нельзя декодируется. См. Также модуль кодеки .

Если дополнительные параметры не заданы, unicode () будет имитировать поведение str () за исключением того, что он возвращает строки Unicode вместо 8-битных строк. Точнее, если объект является Unicode строка или подкласс, он вернет эту строку Unicode без применяется любое дополнительное декодирование.

Для объектов, которые предоставляют метод __unicode __ (), он вызовет этот метод без аргументов для создания строки Unicode. За все остальные объекты, 8-битная строковая версия или представление запрошенный и затем преобразованный в строку Unicode, используя кодек для кодировки по умолчанию в «строгом» режиме.

Новое в версии 2.0. Изменено в версии 2.2: добавлена ​​поддержка __unicode __ ().

Итак, когда вы вызываете unicode(r, 'utf-8'), он требует 8-битную строку или символьный буфер в качестве первого аргумента, поэтому он приводит ваш объект в действие с помощью метода __str__() и пытается декодировать его с помощью utf-8 кодек. Без utf-8 функция unicode() ищет метод __unicode__() для вашего объекта и, не найдя его, вызывает метод __str__(), как вы и предлагали, пытаясь использовать кодек по умолчанию для преобразования в юникод .

4 голосов
/ 20 сентября 2008

unicode не угадывает кодировку вашего текста. Если ваш объект может печатать себя как unicode, определите метод __unicode__(), который возвращает строку Unicode.


Секрет в том, что unicode(r) на самом деле не вызывает __str__(). Вместо этого он ищет метод __unicode__(). Реализация по умолчанию __unicode__() вызовет __str__(), а затем попытается декодировать его с использованием кодировки ascii. Когда вы передаете кодировку, unicode() ожидает, что первый объект будет чем-то, что может быть декодировано, то есть экземпляром basestring.


Поведение странное, потому что оно пытается расшифровать как ascii, если я не передам 'utf-8'. Но если я передам 'utf-8', это выдаст другую ошибку ...

Это потому, что когда вы указываете "utf-8", он обрабатывает первый параметр как строковый объект, который нужно декодировать. Без него он обрабатывает параметр как объект, который будет приведен к юникоду.

Я не понимаю путаницы. Если вы знаете, что атрибут text объекта всегда будет в кодировке UTF-8, просто определите __unicode__(), и тогда все будет работать нормально.

...