官方 Python 文档和 Index of Python Enhancement Proposal 似乎都没有明确说明如何覆盖这些方法,除了 3.3 Special method names,其中提到了 __repr__():
如果可能的话,这应该看起来像一个有效的 Python 表达式
可用于重新创建具有相同值的对象 [...]
这通常用于调试,因此重要的是
表示信息丰富且明确。
我喜欢从 __repr__() 在一些标准库模块中的实现方式中获得灵感,例如socket.socket:
$ python3
>>> from socket import socket
>>> socket()
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
所以这里的模式是<self.__class__.__module__ + "." + self.__class__.__name__ attribute1=value1, ..., attributeN=valueN>。
虽然__repr__() 更适合用于调试/测试目的,但__str__() 的范围更为非正式,我推断适用的规则更宽松。请注意,如果__repr__() 被覆盖但__str__() 未被覆盖,则__repr__() 调用__str__()。
再说一次,如果我必须选择一些规则,我更喜欢 __str__() 类似于 __repr__(),但要修改:
- 显示的项目数。我不需要像
__repr__ 要求那样冗长。
- 显示的值的类型。我包括了最“重要”的值,甚至包括不反映最初传递给
__init__() 的参数的值。
另外几个例子来自我一直在研究的一个 PDF 库。有两个PdfFileReader 和PdfFileWriter 类,它们的__repr__() 和__str__() 方法有以下输出:
r = PdfFileReader("samplecode/pdfsamples/jpeg.pdf")
w = PdfFileWriter()
print(r)
print(str(r))
print(repr(r))
print(w)
print(str(w))
print(repr(w))
$ python3 repr.py
<pypdf.pdf.PdfFileReader _filepath=samplecode/pdfsamples/jpeg.pdf, stream=<_io.BytesIO object at 0x7eff60f07e60>, strict=True, debug=False>
<pypdf.pdf.PdfFileReader _filepath=samplecode/pdfsamples/jpeg.pdf, stream=<_io.BytesIO object at 0x7eff60f07e60>, strict=True, debug=False>
<pypdf.pdf.PdfFileReader _filepath=samplecode/pdfsamples/jpeg.pdf, stream=<_io.BytesIO object at 0x7eff60f07e60>, strict=True, debug=False>
<pypdf.pdf.PdfFileWriter _header=%PDF-1.3, debug=False>
<pypdf.pdf.PdfFileWriter _header=%PDF-1.3, debug=False>
<pypdf.pdf.PdfFileWriter _header=%PDF-1.3, debug=False>
对于repr(),另见2. Built-in Functions:
[...] 对于许多类型,此函数会尝试返回一个字符串
在传递给 eval() 时会产生一个具有相同值的对象,
否则表示是用尖括号括起来的字符串
包含对象类型的名称以及
附加信息通常包括姓名和地址
目的。 [...]