Печать объектов и Unicode, что под капотом?Каковы хорошие рекомендации? - PullRequest
6 голосов
/ 24 августа 2010

Я борюсь с печатью и преобразованием юникода.Вот некоторый код, выполняемый в интерпретаторе 2.5 Windows.

>>> import sys
>>> print sys.stdout.encoding
cp850
>>> print u"é"
é
>>> print u"é".encode("cp850")
é
>>> print u"é".encode("utf8")
├®
>>> print u"é".__repr__()
u'\xe9'

>>> class A():
...    def __unicode__(self):
...       return u"é"
...
>>> print A()
<__main__.A instance at 0x0000000002AEEA88>

>>> class B():
...    def __repr__(self):
...       return u"é".encode("cp850")
...
>>> print B()
é

>>> class C():
...    def __repr__(self):
...       return u"é".encode("utf8")
...
>>> print C()
├®

>>> class D():
...    def __str__(self):
...       return u"é"
...
>>> print D()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128)

>>> class E():
...    def __repr__(self):
...       return u"é"
...
>>> print E()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 0: ordinal not in range(128)

Итак, когда выводится строка в кодировке Юникод, это не функция __repr__(), которая вызывается и печатается.
Но когда объектвызывается напечатано __str__() или __repr__() (если __str__ не реализовано), а не __unicode__().Оба не могут вернуть строку в юникоде.
Но почему?Почему, если __repr__() или __str__() возвращает строку в юникоде, разве это не должно быть таким же поведением, как при печати строки в юникоде?Другими словами: почему print D() отличается от print D().__str__()

Я что-то упустил?

Эти примеры также показывают, что если вы хотите напечатать объект, представленный строками Unicode, у вас естьзакодировать его в строку объекта (типа str).Но для хорошей печати (избегайте "├®"), это зависит от кодировки sys.stdout.
Итак, нужно ли добавлять u"é".encode(sys.stdout.encoding) для каждого из моих методов __str__ или __repr__?Или вернуть repr (u "é")?Что если я использую трубопровод?Это та же кодировка, что и sys.stdout?

Моя основная проблема - сделать класс "пригодным для печати", т.е. print A() печатает что-то полностью читаемое (не с символами \ x *** unicode).Вот плохое поведение / код, который необходимо изменить:

class User(object):
    name = u"Luiz Inácio Lula da Silva"
    def __repr__(self):
        # returns unicode
        return "<User: %s>" % self.name
        # won't display gracefully
        # expl: print repr(u'é') -> u'\xe9'
        return repr("<User: %s>" % self.name)
        # won't display gracefully
        # expl: print u"é".encode("utf8") -> print '\xc3\xa9' -> ├®
        return ("<User: %s>" % self.name).encode("utf8")

Спасибо!

Ответы [ 2 ]

8 голосов
/ 24 августа 2010

Python не имеет много ограничений семантического типа для данных функций и методов, но имеет несколько , и вот один из них: __str__ (в Python 2. *) должен возвращать байтовую строку. Как обычно, если объект unicode обнаружен там, где требуется байтовая строка, текущая кодировка по умолчанию (обычно 'ascii') применяется в попытке создать требуемую строку байтов из рассматриваемого объекта Unicode.

Для этой операции кодировка (если таковая имеется) любого заданного файлового объекта не имеет значения, потому что то, что возвращается из __str__, может быть напечатано или подвергнется совершенно другой и не связанной обработке. Ваша цель в вызове __str__ не имеет значения для самого вызова и его результатов; Python, как правило, не учитывает «будущий контекст» операции (что вы собираетесь делать с результатом после выполнения операции) при определении семантики операции.

Это потому, что Python не всегда знает ваши будущие намерения и пытается минимизировать количество неожиданностей. В частности, print str(x) и s = str(x); print s (одни и те же операции, выполняемые одним глотком против двух) должны иметь одинаковые эффекты; если во втором случае, будет исключение, если str(x) не сможет корректно создать строку байтов (то есть, например, x.__str__() не может), и, следовательно, исключение должно также произойти в другом случае.

print сам (начиная с версии 2.4, я полагаю) при представлении с объектом Unicode учитывает атрибут .encoding (если есть) целевого потока (по умолчанию sys.stdout); другие операции, которые еще не связаны с каким-либо конкретным целевым потоком, этого не делают, и str(x) (т. е. x.__str__()) является именно такой операцией.

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

Редактировать : теперь ОП поясняет: «Моя главная проблема - сделать класс« пригодным для печати », то есть print A () печатает что-то полностью читаемое (не с символами \ x *** unicode). ». Вот подход, который, я думаю, лучше всего подходит для этой конкретной цели:

import sys

DEFAULT_ENCODING = 'UTF-8'  # or whatever you like best

class sic(object):

    def __unicode__(self):  # the "real thing"
        return u'Pel\xe9'

    def __str__(self):      # tries to "look nice"
        return unicode(self).encode(sys.stdout.encoding or DEFAULT_ENCODING,
                                    'replace')

    def __repr__(self):     # must be unambiguous
        return repr(unicode(self))

То есть, этот подход фокусируется на __unicode__ в качестве основного способа для экземпляров класса отформатировать себя - но поскольку (в Python 2) print вызывает __str__, вместо этого у него есть один делегат __unicode__ с лучшим, что он может сделать с точки зрения кодирования. Не идеально, но тогда утверждение Python 2 print далеко не идеально; -).

__repr__, со своей стороны, должен стремиться к быть однозначным , то есть не , чтобы «хорошо выглядеть» за счет риска двусмысленности (в идеале, когда это возможно, он должен возвращать байтовую строку, которая, если она будет передана в eval, сделает экземпляр равным текущему ... это далеко не всегда возможно, но отсутствие неоднозначности является абсолютным ядром различие между __str__ и __repr__, и я настоятельно рекомендую соблюдать это различие!).

0 голосов
/ 24 августа 2010

Полагаю, ваш sys.getdefaultencoding() по-прежнему 'ascii'.И я думаю, что это используется всякий раз, когда применяется str () или repr () объекта.Вы можете изменить это с помощью sys.setdefaultencoding().Как только вы пишете в поток, будь то STDOUT или файл, вы должны соблюдать его кодировку.Это также относится к трубам на оболочке, IMO.Я предполагаю, что «print» учитывает кодировку STDOUT, но исключение происходит до того, как вызывается «print», при построении аргумента.

...