在python类中,unicode(self)和self.unicode(self)有什么区别?

what's the difference between unicode(self) and self.__unicode__() in a Python Class?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
#-*- 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)的话,会显示错误:

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

有问题的代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
#-*- 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模块,但没有看到太多不同之处:

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

VS

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


从您的__unicode__方法返回bytes

清楚地说:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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__,它预期会返回bytes,你让它返回unicode,所以它试图用ascii编码,但这不起作用。

简单解决:让__unicode__返回unicode和__str__字节。


1
2
s = u'中文'
return s.encode('utf-8')

这将返回非Unicode字节字符串。这就是encode所做的。UTF-8并不是一种能神奇地将数据转换成Unicode的东西;如果有的话,它是相反的——一种用字节(数据,或多或少)表示Unicode(抽象)的方法。

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

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

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

有时,当您调用encode时,python会报告一个UnicodeDecodeError。为什么?因为您尝试使用一个字节字符串。这个过程的正确输入是一个unicode字符串,所以python"乐于助人"首先尝试将字节字符串decode转换为unicode。但它不知道要使用什么编解码器,所以它假定使用ascii。在您可以接收各种数据的环境中,此编解码器是最安全的选择。它只报告字节数大于等于128的错误,这些错误以不同的方式以各种8位编码处理。(还记得当年试图从Mac电脑中导入带有é等字母的Word文件,还是从PC机中导入带有é等字母的Word文件?你会在另一台电脑上看到一些奇怪的符号,因为平台内置的编码是不同的。)

使事情变得更加复杂,在python2中,encode/decode机制还用于实现与解释unicode无关的其他一些整洁的事情。例如,有一个base64编码器和一个自动处理字符串转义序列的东西(即,它将把一个反斜杠,后面跟一个字母"t",变成一个制表符)。其中一些执行从字节字符串到字节字符串或从Unicode到Unicode的"编码"或"解码"。

(顺便说一下,这一切的工作方式完全不同——更清楚地说,imho——在python3中。)

同样,当__unicode__返回一个字节字符串(从样式上看,它不应该返回),python unicode()内置函数自动将其解码为ascii;当__str__返回一个unicode字符串(同样,它不应该解码),str()将其编码为ascii。这是在幕后发生的,在您无法控制的代码中。但是,您可以修复__unicode____str__,以执行它们应该执行的操作。

(实际上,您可以通过传递第二个参数来覆盖unicode的编码。但是,这是错误的解决方案,因为您应该已经从__unicode__返回了一个unicode字符串。而且str不带编码参数,所以你在那里不走运。)

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

问题:我们希望__unicode__返回unicode字符串u'中文',并且希望__str__返回该字符串的utf-8编码版本。

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

1
2
3
4
5
6
class test():
    def __unicode__(self):
        return u'中文'

    def __str__(self):
        return unicode(self).encode('utf-8')


当您定义__unicode__特殊方法时,您告诉它要使用什么编码。当您简单地调用unicode时,没有指定编码,所以python使用默认的"ascii"。

btw,__str__应该返回一个字节字符串,而不是unicode。并且__unicode__应该返回unicode,而不是字节字符串。所以这个代码是向后的。因为它不返回Unicode,所以python可能试图使用默认编码来转换它。


在python对象上调用unicode时,输出是传递给unicode方法的参数的Unicode表示。

由于您没有指定应该使用什么编码,您会得到一个错误,即参数不能只用ASCII来表示。

当您使用__unicode__时,您指定应该使用utf-8对该字符串进行编码,这是正确的,并且不会出现任何问题。

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

1
unicode( str,"utf-8" )

这应该和你的__unicode__方法一样有效。