__str__() 是否在幕后调用 decode() 方法?

0 投票
4 回答
878 浏览
提问于 2025-04-15 13:33

我觉得内置的函数 __repr____str__ 在基本定义上有一个重要的区别。

>>> t2 = u'\u0131\u015f\u0131k'
>>> print t2
ışık
>>> t2
Out[0]: u'\u0131\u015f\u0131k'

t2.decode 会报错,因为 t2 是一个 Unicode 字符串。

>>> enc = 'utf-8'
>>> t2.decode(enc)
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
  File "C:\java\python\Python25\Lib\encodings\utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordin
al not in range(128)

当调用 __str__ 时,会报错,就好像在调用 decode() 函数一样:

>>> t2.__str__()
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordin
al not in range(128)

但是 __repr__ 则没有问题:

>>> t2.__repr__()
Out[0]: "u'\\u0131\\u015f\\u0131k'"

为什么 __str__ 会出错,而 __repr__ 却能正常工作呢?

这个小小的区别似乎在我正在开发的一个 Django 应用中引发了一个 bug。

4 个回答

2

为了补充一下约翰的好回答:

要理解这两个方法的名字 encode()decode(),你只需要知道在Python中,像 u'...' 这样的unicode字符串被认为是 参考格式。你用 encode 方法是把参考格式转换成其他格式(比如utf-8),而用 decode 方法则是从其他格式转换回参考格式。unicode格式总是被认为是“真实的东西” :-).

5

一般来说,调用 str.__unicode__()unicode.__str__() 是个很糟糕的主意,因为字节(bytes)不能安全地转换成Unicode字符点,反之亦然。唯一的例外是ASCII值,它们在所有单字节编码中通常是一样的。问题在于你使用了错误的转换方法。

如果你想把 unicode 转换成 str,应该使用 encode()

>>> t1 = u"\u0131\u015f\u0131k"
>>> t1.encode("utf-8")
'\xc4\xb1\xc5\x9f\xc4\xb1k'

如果你想把 str 转换成 unicode,则使用 decode()

>>> t2 = '\xc4\xb1\xc5\x9f\xc4\xb1k'
>>> t2.decode("utf-8")
u'\u0131\u015f\u0131k'
7

简单来说,__str__ 只能输出 ASCII 字符串。因为 t2 包含了一些超出 ASCII 范围的 Unicode 字符,所以它不能仅用字符串来表示。另一方面,__repr__ 则是尝试输出重建这个对象所需的 Python 代码。你会发现,使用 repr(t2) 的输出(这种写法比 t2.__repr_() 更推荐)正好和你在第一行给 t2 赋的值是一样的。repr 的结果大致看起来像 ['\', 'u', '0', ...],这些都是 ASCII 值,而 str 的输出则试图是 [chr(0x0131), chr(0x015f), chr(0x0131), 'k'],其中大部分字符超出了 Python 字符串可以接受的范围。一般来说,在处理 Django 应用时,你应该使用 __unicode__ 来处理所有内容,而不要碰 __str__

更多信息可以查看 Django 文档中的字符串部分

撰写回答