1. 程式人生 > >QQA: Python 中的 str 與 repr

QQA: Python 中的 str 與 repr

有時候,你會需要為你的類實現 __str____repr__ 方法,你知道它們的作用是什麼嗎?它們有什麼區別嗎?這個問題的答案一搜就能找到,如果恰巧這是你第一次看到這個問題,不妨看看吧。

  • __repr__ 用於生成正式的表示。可以認為是將物件序列化的方法,原則上要能反序列化回物件。
  • __str__ 用於生成非正式的表示。formatprint 會呼叫它來為使用者生成“友好的”顯示。
  • 如果你需要自己實現,一般實現 __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]eval(repr(x)) == x#> True

我們看到 '[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 區分開兩個不同的物件)。
  • 不要使用 repreval 來做序列化/反序列化,用 picklejson

#str 用於顯示

obj.__str__() 方法會在 print(obj)'{}'.format(obj) 時被呼叫,一般是為了給使用者提供"友好的"顯示,所以 __str__ 不像 __repr__ 那樣原則上對返回值有約定,想怎麼搞都行。

另外,__str__ 的預設實現是直接呼叫了 __repr__ 方法。因此如果覆蓋了 __repr__ 方法,__str__ 的結果也會隨之改變。