Как использовать список объектов Python, представление которых Unicode - PullRequest
4 голосов
/ 09 мая 2009

У меня есть объект, который содержит данные Unicode, и я хочу использовать его в своем представлении например,

# -*- coding: utf-8 -*-

class A(object):

    def __unicode__(self):
        return u"©au"

    def __repr__(self):
        return unicode(self).encode("utf-8")

    __str__ = __repr__ 

a = A()


s1 = u"%s"%a # works
#s2 = u"%s"%[a] # gives unicode decode error
#s3 = u"%s"%unicode([a])  # gives unicode decode error

Теперь, даже если я верну юникод из repr , он все равно выдаст ошибку поэтому вопрос в том, как я могу использовать список таких объектов и создать из него еще одну строку юникода?

сведения о платформе:

"""
Python 2.5.2 (r252:60911, Jul 31 2008, 17:28:52)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
'Linux-2.6.24-19-generic-i686-with-debian-lenny-sid'
""" 

также не уверен, почему

print a # works
print unicode(a) # works
print [a] # works
print unicode([a]) # doesn't works 

группа python отвечает на это http://groups.google.com/group/comp.lang.python/browse_thread/thread/bd7ced9e4017d8de/2e0b07c761604137?lnk=gst&q=unicode#2e0b07c761604137

Ответы [ 7 ]

4 голосов
/ 09 мая 2009

s1 = u"%s"%a # works

Это работает, потому что при работе с 'a' он использует свое представление в юникоде (то есть метод Юникод ),

когда вы заключаете его в список, такой как '[a]' ... когда вы пытаетесь поместить этот список в строку, вызывается юникод ([a]) (который совпадает с repr в случае со списком), строковое представление списка, которое будет использовать repr (a) для представления вашего элемента в его выводе. Это вызовет проблему, так как вы передаете объект 'str' (строку байтов), который содержит кодированную версию utf-8 'a', и когда формат строки пытается внедрить это в вашу строку Unicode, он будет Попробуйте преобразовать его обратно в объект Unicode, используя кодировку по умолчанию, то есть ASCII. так как у ascii нет персонажа, который он пытается конвертировать, он терпит неудачу

то, что вы хотите сделать, должно быть сделано следующим образом: u"%s" % repr([a]).decode('utf-8') при условии, что все ваши элементы кодируются в utf-8 (или ascii, который является подмножеством utf-8 с точки зрения юникода).

для лучшего решения (если вы все еще хотите, чтобы строка выглядела как строка списка), вам придется использовать то, что было предложено ранее, и использовать join, как-то так:

и '[%s]' % u','.join(unicode(x) for x in [a,a])

хотя это не позаботится о списке, содержащем список ваших объектов А.

Мое объяснение звучит ужасно неясно, но я надеюсь, что вы сможете в этом разобраться.

3 голосов
/ 09 мая 2009

Попробуйте:

s2 = u"%s"%[unicode(a)] 

Ваша главная проблема в том, что вы делаете больше конверсий, чем ожидаете. Давайте рассмотрим следующее:

s2 = u"%s"%[a] # gives unicode decode error

С Документация Python ,

    's'     String (converts any python object using str()).
    If the object or format provided is a unicode string, 
    the resulting string will also be unicode.

Когда обрабатывается строка формата% s, применяется str ([a]). На данный момент у вас есть строковый объект, содержащий последовательность байтов Юникода. Если вы попытаетесь распечатать это, проблем не возникнет, потому что байты проходят прямо к вашему терминалу и обрабатываются терминалом.

>>> x = "%s" % [a]
>>> print x
[©au]

Проблема возникает, когда вы пытаетесь преобразовать это обратно в Unicode. По сути, функция unicode вызывается для строки, которая содержит последовательность байтов в кодировке Unicode, и именно это приводит к сбою кодека ascii.

    >>> u"%s" % x
    Traceback (most recent call last):
      File "", line 1, in 
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 1: ordinal not in range(128)
    >>> unicode(x)
    Traceback (most recent call last):
      File "", line 1, in 
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 1: ordinal not in range(128)
2 голосов
/ 11 мая 2009

Прежде всего, спросите себя, чего вы пытаетесь достичь. Если все, что вам нужно, это представление списка с возможностью округления, вы должны просто сделать следующее:

class A(object):
    def __unicode__(self):
        return u"©au"
    def __repr__(self):
        return repr(unicode(self))
    __str__ = __repr__

>>> A()
u'\xa9au'
>>> [A()]
[u'\xa9au']
>>> u"%s" % [A()]
u"[u'\\xa9au']"
>>> "%s" % [A()]
"[u'\\xa9au']"
>>> print u"%s" % [A()]
[u'\xa9au']

Вот как это должно работать. Строковое представление списков Python - это не то, что должен видеть пользователь, поэтому имеет смысл иметь в них экранированные символы.

1 голос
/ 11 мая 2009

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

Все сводится к реализации __unicode__ и __repr__ встроенного класса list. В основном это эквивалентно:

class list(object):
    def __repr__(self):
        return "[%s]" % ", ".join(repr(e) for e in self.elements)
    def __str__(self):
        return repr(self)
    def __unicode__(self):
        return str(self).decode()

На самом деле, list даже не определяет __unicode__ и __str__ методы , что имеет смысл, когда вы об этом думаете.

Когда вы пишете:

u"%s" % [a]                          # it expands to
u"%s" % unicode([a])                 # which expands to
u"%s" % repr([a]).decode()           # which expands to
u"%s" % ("[%s]" % repr(a)).decode()  # (simplified a little bit)
u"%s" % ("[%s]" % unicode(a).encode('utf-8')).decode()  

Эта последняя строка является расширением repr (a) с использованием реализации __repr__ в вопросе.

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

Как уже упоминалось в некоторых других ответах, вы можете написать свою собственную функцию или даже список подклассов, например:

class mylist(list):
    def __unicode__(self):
        return u"[%s]" % u", ".join(map(unicode, self))

Обратите внимание, что этот формат не допускает циклическое переключение. Это может даже вводить в заблуждение:

>>> unicode(mylist([]))
u'[]'
>>> unicode(mylist(['']))
u'[]'

Конечно, вы можете написать quote_unicode функцию, чтобы сделать ее способной к круговому переключению, но сейчас самое время спросить себя в чем смысл . Функции unicode и str предназначены для создания представления объекта, которое имеет смысл для пользователя. Для программистов есть функция repr. Необработанные списки - это не то, что пользователь когда-либо должен видеть. Вот почему класс list не реализует метод __unicode__.

Чтобы получить лучшее представление о том, что происходит, поиграйте с этим небольшим классом:

class B(object):
    def __unicode__(self):
        return u"unicode"
    def __repr__(self):
        return "repr"
    def __str__(self):
        return "str"


>>> b
repr
>>> [b]
[repr]
>>> unicode(b)
u'unicode'
>>> unicode([b])
u'[repr]'

>>> print b
str
>>> print [b]
[repr]
>>> print unicode(b)
unicode
>>> print unicode([b])
[repr]
1 голос
/ 09 мая 2009

Если вы хотите использовать список unicode() способных объектов для создания строки в юникоде, попробуйте что-то вроде:

u''.join([unicode(v) for v in [a,a]])
0 голосов
/ 09 мая 2009

repr и str оба должны возвращать объекты str, по крайней мере, до Python 2.6.x. Вы получаете ошибку декодирования, потому что repr () пытается преобразовать ваш результат в str, и он терпит неудачу.

Я считаю, что это изменилось в Python 3.x.

0 голосов
/ 09 мая 2009
# -*- coding: utf-8 -*-

class A(object):
    def __unicode__(self):
        return u"©au"

    def __repr__(self):
        return unicode(self).encode('ascii', 'replace')

    __str__ = __repr__

a = A()

>>> u"%s" % a
u'\xa9au'
>>> u"%s" % [a]
u'[?au]'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...