如何在不引发UnicodeEncodeError的情况下覆盖str函数?

时间:2016-05-06 21:55:08

标签: python unicode python-2.x

我很困惑,为类定义__str__似乎对在类实例上使用str函数没有影响。例如,我在Django文档中读到:

  

print语句和str内置调用__str__(),用于确定对象的人类可读表示。

但这似乎并非如此。这是一个模块中的示例,其中text始终被假定为unicode:

import six

class Test(object):

    def __init__(self, text):
        self._text = text

    def __str__(self):
        if six.PY3:
            return str(self._text)
        else:
            return unicode(self._text)

    def __unicode__(self):
        if six.PY3:
            return str(self._text)
        else:
            return unicode(self._text)

在Python 2中,它提供了以下行为:

>>> a=Test(u'café')
>>> print a.__str__()
café
>>> print a # same error with str(a)
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-63-202e444820fd> in <module>()
----> 1 str(a)

UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 3: ordinal not in range(128)

有没有办法重载str函数?

1 个答案:

答案 0 :(得分:5)

对于Python 2,您从__str__方法返回了错误的类型。您将返回unicode,而必须返回str

def __str__(self):
    if six.PY3:
        return str(self._text)
    else:
        return self._text.encode('utf8')

由于self._text尚未属于str类型,因此您需要对其进行编码。因为您返回了Unicode,所以Python必须先对其进行编码,但默认的ASCII编码无法处理非ASCII é字符。

打印对象会导致右输出,因为我的终端配置为处理UTF-8:

>>> a = Test(u'café')
>>> str(a)
'caf\xc3\xa9'
>>> print a
café
>>> unicode(a)
u'caf\xe9'

请注意,Python 3中有 no __unicode__方法;您在该方法中的if six.PY3完全是多余的。以下也适用:

class Test(object):
    def __init__(self, text):
        self._text = text

    def __str__(self):
        if six.PY3:
            return self._text
        else:
            return self._text.encode('utf8')

    def __unicode__(self):
        return self._text

但是,如果您使用的是six库,那么使用@six.python_2_unicode_compatible decorator要好得多,并且只为__str__方法定义Python 3版本:

@six.python_2_unicode_compatible
class Test(object):
    def __init__(self, text):
        self._text = text

    def __str__(self):
        return self._text

假设text始终是Unicode。如果您正在使用Django,那么您可以从django.utils.encoding module获得相同的装饰器。