Python Every Class Needs a __repr__
一、思考
當我們在Python中定義一個類的時候,如果我們通過print列印這個類的例項化物件,或者我們直接輸入這個類例項化物件會返回怎麼樣的結果,如下程式碼:
>>> class People(object): ...def __init__(self, name, age): ...self.name = name ...self.age = age ... >>> tom = People("Tom", 23) >>> print(tom) <__main__.People object at 0x00000000027A7160> >>> tom <__main__.People object at 0x00000000027A7160> >>>
預設情況下,你得到的是一個字串,其中包含類名和物件例項的id(這是CPython中物件的記憶體地址),其實有更加Pythonic的方式去控制不同情況下將物件進行轉換為字串,也就是控制其顯示的結果內容。 我們把上面的程式碼進行更改的內容如下:
>>> class People(object): ...def __init__(self, name, age): ...self.name = name ...self.age = age ...def __str__(self): ...return f"people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) people name is Tom >>> tom <__main__.People object at 0x00000000021B7208> >>>
str 是python的內建方法,並且當你在嘗試去吧一個物件轉換為一個字串的時候怎麼呼叫這個str方法,如我們進行如下操作時:
>>> class People(object): ...def __init__(self, name, age): ...self.name = name ...self.age = age ...def __str__(self): ...return f"people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) people name is Tom >>> str(tom) 'people name is Tom' >>> '{}'.format(tom) 'people name is Tom' >>> tom <__main__.People object at 0x00000000021E7208> >>>
二、__str__ vs __repr__
在上面的程式碼中我們添加了str_方法之後,當我們將物件轉換為字串的時候都會呼叫str方法,並得到我們自己定義的內容,但是並沒有影響到我們在python互動模式下直接輸入物件的返回內容。其實
repr 和 str 其實是非常類似的,只不過用的場景不同,將上面的程式碼進行調整:
>>> class People(object): ...def __init__(self, name, age): ...self.name = name ...self.age = age ...def __str__(self): ...return f"people name is {self.name}" ...def __repr__(self): ...return f"__repr__: people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) people name is Tom >>> str(tom) 'people name is Tom' >>> '{}'.format(tom) 'people name is Tom' >>> tom __repr__: people name is Tom >>>
其實這裡也就驗證了,在Python的互動模式下,檢查一個物件,其實就是在呼叫物件的repr方法,還有一個你可能沒有發現的地方就是當你在list,dict等容器中儲存物件的時候,我們列印看到的都是repr的內容,我們把上面的tom存到列表裡, 然後列印檢視如下:
>>> print([tom]) [__repr__: people name is Tom] >>>
為了驗證我們到底應該怎麼用這兩個方法,畢竟這兩個方法的作用還是非常類似的,我們可以通過Python標準庫來驗證一下,跟著標準庫走總不會有錯
>>> import datetime >>> today = datetime.date.today() >>> today datetime.date(2019, 3, 5) >>> str(today) '2019-03-05' >>> repr(today) 'datetime.date(2019, 3, 5)' >>>
從這個Python標準庫的用法,我們也能非常好的理解str方法其實就是為了返回一個人們容易理解的字串型別的結果,而repr方法更偏向於程式設計師方便去除錯,能從結果中看到更加有用的資訊,它甚至包括完整的模組和類
三、Why Every Class Needs a repr
我們先看一下我們將我們上面程式碼的str方法去掉之後的結果:
>>> class People(object): ...def __init__(self, name, age): ...self.name = name ...self.age = age ...# def __str__(self): ...#return f"people name is {self.name}" ...def __repr__(self): ...return f"__repr__: people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) __repr__: people name is Tom >>> str(tom) '__repr__: people name is Tom' >>> '{}'.format(tom) '__repr__: people name is Tom' >>> tom __repr__: people name is Tom >>>
從這裡我們發現噹噹你把物件進行字串轉換的時候,就會先去找str方法,如果沒有則再去找repr方法執行
所以還是建議在自己定義的類中都至少有一個repr方法,這樣不管在上面情況下,你都能有一個對你來說有用的字串返回結果,而不再是一個乾巴巴的記憶體地址,所以在最後我們規範一下我們寫一個Python類時的程式碼:
class People(object): def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'people name is {self.name}' def __repr__(self): return (f'{self.__class__.__name__}(' f'{self.name!r}, {self.age!r})')
在最後的repr的返回中我們用了!r 這個意味著我們要的repr(self.name) repr(self.age)而不是要str(self.name) str(self.age)的返回結果