为什么unicode()只在没有给出编码的情况下对我的对象使用str()?

时间:2008-09-20 00:53:24

标签: python unicode encoding

我首先创建一个字符串变量,其上包含一些非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 个答案:

答案 0 :(得分:7)

这种行为确实令人困惑,但却是内涵。我在这里复制了Python Built-In Functions documentation中的整个unicode文档(对于版本2.5.2,正如我写的那样):

  

unicode([object [,encoding [,errors]]])

     

使用以下模式之一返回对象的Unicode字符串版本:

     

如果给出了编码和/或错误,unicode()将解码      对象,可以是8位字符串或字符缓冲区      使用编解码器进行编码。 encoding参数是一个字符串      给出编码的名称;如果编码未知,      引发了LookupError。错误处理是根据      错误;这指定了对字符的处理      输入编码无效。如果错误是“严格的”(      默认情况下,会在出现错误时引发ValueError,而值为      'ignore'导致错误被忽略,值为      'replace'导致官方的Unicode替换字符,      U + FFFD,用于替换不能输入的字符      解码。另请参阅codecs模块。

     

如果没有给出可选参数,unicode()将模仿      str()的行为,除了它返回Unicode字符串      而不是8位字符串。更确切地说,如果object是Unicode      字符串或子类,它将返回没有的Unicode字符串      任何额外的解码应用。

     

对于提供__unicode __()方法的对象,它将调用      这个方法没有参数来创建Unicode字符串。对于      所有其他对象,8位字符串版本或表示      请求,然后使用编解码器转换为Unicode字符串      对于'严格'模式下的默认编码。

     

2.0版中的新功能。在2.2版中更改:添加了对__unicode __()的支持。

因此,当您调用unicode(r, 'utf-8')时,它需要一个8位字符串或一个字符缓冲区作为第一个参数,因此它使用__str__()方法强制您的对象,并尝试使用utf-8编解码器。如果没有utf-8unicode()函数会在您的对象上查找__unicode__()方法,并且找不到它,会按照您的建议调用__str__()方法,尝试使用默认编解码器转换为unicode。

答案 1 :(得分:4)

unicode不会猜测文字的编码。如果您的对象可以将自身打印为unicode,请定义返回Unicode字符串的__unicode__()方法。


秘密是unicode(r)实际上并没有调用__str__()本身。相反,它正在寻找__unicode__()方法。 __unicode__()的默认实现将调用__str__(),然后尝试使用ascii字符集对其进行解码。传递编码时,unicode()期望第一个对象成为可以解码的对象 - 即basestring的实例。


  

行为很奇怪,因为如果我没有传递'utf-8',它会尝试解码为ascii。但如果我通过'utf-8'就会出现不同的错误......

那是因为当你指定“utf-8”时,它会将第一个参数视为要解码的类字符串对象。没有它,它会将参数视为要强制转换为unicode的对象。

我不明白这种困惑。如果你知道对象的text属性将始终是UTF-8编码,只需定义__unicode__()然后一切都会正常工作。