QQA: Python 中的 str 與 repr
有時候,你會需要為你的類實現 __str__
或 __repr__
方法,你知道它們的作用是什麼嗎?它們有什麼區別嗎?這個問題的答案一搜就能找到,如果恰巧這是你第一次看到這個問題,不妨看看吧。
__repr__
用於生成正式的表示。可以認為是將物件序列化的方法,原則上要能反序列化回物件。__str__
用於生成非正式的表示。format
或print
會呼叫它來為使用者生成“友好的”顯示。- 如果你需要自己實現,一般實現
__str__
即可。
#Python 中一切皆物件
Python Data Model 中指出,Python 中的所有資料都是“物件”(Object)。Python 中幾乎所有(不確定有沒有反例)的操作都可以對應到物件的某個特殊方法。因此可以通過手工實現它們來覆蓋預設的邏輯。
比如說迭代器(iterator)取長度操作 len(iter)
對應 obj.__len__
;加法操作 a + b
對應a.__add__(b)
;函式呼叫 func(...)
對應 func.__cal__(...)
。當然也包括我們要介紹的 __repr__
和 __str__
。
#repr 用於 Debug
Python 中執行 repr(obj)
可以獲取 obj
的字串表示,而這個操作相當於呼叫了
obj.__repr__()
。而這個字串表示“原則上”需要能反序列化回 obj
本身。看下面程式碼:
x = [1,2,3]repr(x)#> '[1, 2, 3]'eval('[1, 2, 3]' |
我們看到 '[1, 2, 3]'
(注意到逗號後面帶了空格) 是資料 [1,2,3]
的字串表示,用 eval
來反序列化可以得到原資料。那麼如果變數是自定義的類又如何呢?
class MyClass: def __init__(self, arg): self.arg = argx = MyClass(10)repr(x)#> '<__main__.MyClass object at 0x10a40ef98>' |
可以看到,repr(x)
給出了物件的型別及物件的 ID (記憶體地址)。但如果我們用
eval(repr(x))
class MyClass: def __init__(self, arg): self.arg = arg def __repr__(self): return 'MyClass({})'.format(self.arg)x = MyClass(10)repr(x)#> 'MyClass(10)'eval(repr(x))#> MyClass(10) |
可以看到對 __repr__
的覆蓋起了效果,也可以正常反序列化了。
上面這幾個例子都是為了說明 repr
生成的字串到底有什麼用。
repr
並不強制生成的字串可以反序列化repr
生成的字串一般用於 debug,所以一般生成的字串一般要包含儘可能多的資訊,資訊要儘可能明確(如預設實現裡用 ID 區分開兩個不同的物件)。- 不要使用
repr
和eval
來做序列化/反序列化,用 pickle 或 json。
#str 用於顯示
obj.__str__()
方法會在 print(obj)
或 '{}'.format(obj)
時被呼叫,一般是為了給使用者提供"友好的"顯示,所以 __str__
不像 __repr__
那樣原則上對返回值有約定,想怎麼搞都行。
另外,__str__
的預設實現是直接呼叫了 __repr__
方法。因此如果覆蓋了
__repr__
方法,__str__
的結果也會隨之改變。