3

在处理 unicode 问题时,我发现unicode(self)self.__unicode__()有不同的行为:

#-*- coding:utf-8 -*-
import sys
import dis
class test():
    def __unicode__(self):
        s = u'中文'
        return s.encode('utf-8')

    def __str__(self):
        return self.__unicode__()
print dis.dis(test)
a = test()
print a

上面的代码可以正常工作,但是如果我更改self.__unicode__()unicode(self),它将显示错误:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

有问题的代码是:

#-*- coding:utf-8 -*-
import sys
import dis
class test():
    def __unicode__(self):
        s = u'中文'
        return s.encode('utf-8')

    def __str__(self):
        return unicode(self)
print dis.dis(test)
a = test()
print a

很好奇python如何处理这个问题,我尝试了dis模块但没有看到太多区别:

Disassembly of __str__:
 12           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (__unicode__)
              6 CALL_FUNCTION            0
              9 RETURN_VALUE   

VS

Disassembly of __str__:
 10           0 LOAD_GLOBAL              0 (unicode)
              3 LOAD_FAST                0 (self)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE       
4

4 回答 4

5

bytes从你的__unicode__方法返回。

说清楚:

In [18]: class Test(object):
    def __unicode__(self):
        return u'äö↓'.encode('utf-8')
    def __str__(self):
        return unicode(self)
   ....:     

In [19]: class Test2(object):
    def __unicode__(self):
        return u'äö↓'
    def __str__(self):
        return unicode(self)
   ....:     

In [20]: t = Test()

In [21]: t.__str__()
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
/home/dav1d/<ipython-input-21-e2650f29e6ea> in <module>()
----> 1 t.__str__()

/home/dav1d/<ipython-input-18-8bc639cbc442> in __str__(self)
      3         return u'äö↓'.encode('utf-8')
      4     def __str__(self):
----> 5         return unicode(self)
      6 

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

In [22]: unicode(t)
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
/home/dav1d/<ipython-input-22-716c041af66e> in <module>()
----> 1 unicode(t)

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

In [23]: t2 = Test2()

In [24]: t2.__str__()
Out[24]: u'\xe4\xf6\u2193'

In [25]: str(_) # _ = last result
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
/home/dav1d/<ipython-input-25-3a1a0b74e31d> in <module>()
----> 1 str(_) # _ = last result

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)'

In [26]: unicode(t2)
Out[26]: u'\xe4\xf6\u2193'

In [27]: class Test3(object):
def __unicode__(self):
    return u'äö↓'
def __str__(self):
    return unicode(self).encode('utf-8')
....:     

In [28]: t3 = Test3()

In [29]: t3.__unicode__()
Out[29]: u'\xe4\xf6\u2193'

In [30]: t3.__str__()
Out[30]: '\xc3\xa4\xc3\xb6\xe2\x86\x93'

In [31]: print t3
äö↓

In [32]: print unicode(t3)
äö↓

print a或者在我的情况下,print t将调用t.__str__which es expected to returnbytes你让它返回unicode,因此它会尝试对其进行编码,ascii这将不起作用。

简单修复:让__unicode__返回 unicode 和__str__字节。

于 2012-06-20T10:14:12.930 回答
3
s = u'中文'
return s.encode('utf-8')

这将返回一个非 Unicode 字节字符串。这就是encode正在做的事情。utf-8 并不是一个能神奇地将数据变成 Unicode 的东西;如果有的话,它是相反的 - 一种以字节(数据,或多或少)表示 Unicode(抽象)的方式。

我们在这里需要一些术语。编码就是获取一个 Unicode 字符串并使用某种编码制作一个表示它的字节字符串。解码是相反的:获取一个字节字符串(我们认为它编码一个 Unicode 字符串),并使用指定的编码将其解释为 Unicode 字符串。

当我们编码为一个字节字符串然后使用相同的编码进行解码时,我们得到了原始的 Unicode。

utf-8是一种可能的编码。还有很多很多。

有时 Python 会UnicodeDecodeError在你调用encode. 为什么?因为你尝试encode一个字节串。此过程的正确输入是 Unicode 字符串,因此 Python “有帮助”decode首先尝试将字节字符串转换为 Unicode。但它不知道要使用什么编解码器,所以它假设ascii. 在可以接收各种数据的环境中,此编解码器是最安全的选择。它只报告字节 >= 128 的错误,这些错误在各种 8 位编码中以无数种不同的方式进行处理。(还记得在过去尝试将带有字母的 Word 文件é从 Mac 导入 PC 或反之亦然吗?你会在另一台计算机上得到一些其他奇怪的符号,因为平台内置编码不同.)

让事情变得更复杂的是,在 Python 2 中encode/decode机制还用于实现一些与解释 Unicode 无关的其他简洁的东西。例如,有一个 Base64 编码器,以及一个自动处理字符串转义序列的东西(即,它会将反斜杠,后跟字母 't' 更改为制表符)。其中一些会一个字节串“编码”或“解码”到一个字节串,或者从 Unicode 到 Unicode。

顺便说一句,这一切都完全不同 - 更清楚的是,恕我直言 - 在 Python 3 中。

类似地,当__unicode__返回一个字节字符串(它不应该,作为一种风格)时,Pythonunicode()内置函数会自动将其解码为ascii; 当__str__返回一个 Unicode 字符串(它也不应该)时,str()会将其编码为ascii. 这发生在幕后,在您无法控制的代码中。但是,您可以修复__unicode____str__执行他们应该执行的操作。

(实际上,您可以unicode通过传递第二个参数来覆盖 的编码。但是,这是错误的解决方案,因为您应该已经从 . 返回了一个 Unicode 字符串__unicode__。并且str不采用编码参数,所以您是运气不好。)

所以,现在我们可以解决问题了。

问题:我们想要__unicode__返回 Unicode 字符串u'中文',并且我们想要__str__返回它的utf-8-encoded 版本。

解决方案:直接在中返回该字符串__unicode__,并在中显式地进行编码__str__

class test():
    def __unicode__(self):
        return u'中文'

    def __str__(self):
        return unicode(self).encode('utf-8')
于 2012-06-20T10:38:01.047 回答
0

当您调用unicodePython 对象时,输出是您传递给该unicode方法的参数的 unicode 表示形式。

由于您尚未指定应使用哪种编码,因此您会收到一个错误,即无法仅使用 ASCII 来表示参数。

当您使用时,__unicode__您指定应该使用 utf-8 来编码该字符串,这是正确的并且没有问题。

您可以使用所需的编码作为该unicode方法的第二个参数,例如:

unicode( str, "utf-8" )

这应该与您的__unicode__方法相同。

于 2012-06-20T10:19:02.917 回答
0

当您定义__unicode__特殊方法时,您告诉它使用什么编码。当您简单地调用时unicode,您没有指定编码,因此 Python 使用默认的“ascii”。

顺便说一句,__str__应该返回一串字节,而不是 unicode。并且__unicode__应该返回 unicode,而不是字节字符串。所以这段代码是倒退的。由于它没有返回 unicode,因此 Python 可能正在尝试使用默认编码对其进行转换。

于 2012-06-20T10:19:30.123 回答