我首先创建一个字符串变量,其上包含一些非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
为什么呢?为什么这种不一致的行为?这是一个错误吗?是打算吗?非常尴尬。
答案 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-8
,unicode()
函数会在您的对象上查找__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__()
然后一切都会正常工作。