不少時候咱們本身編寫一個類,在將它的實例在終端上打印或查看的時候,咱們每每會看到一個不太滿意的結果。編碼
類默認轉化的字符串基本沒有咱們想要的一些東西,僅僅包含了類的名稱以及實例的 ID (理解爲 Python 對象的內存地址便可)。雖然說這總比沒有好,但確實是沒什麼用處啊。spa
因此,咱們可能會手動打印對象的一些屬性或者是在類裏本身實現一個方法來返回咱們須要的信息。命令行
這沒有什麼不對的地方,可是咱們可使用更 Pythonic 的方式來解決這個問題。3d
使用 __str__ 實現類到字符串的轉化調試
不用本身另外定義一個方法,和 JAVA 的 toString() 方法相似,你能夠在類裏實現__str__ 和 __repr__ 方法從而自定義類的字符串描述,這兩種都是比較 Pythonic 的方式去控制對象轉化爲字符串的方式。代碼規範
下面咱們經過作實驗慢慢的來看這兩種方式是怎麼工做的。首先,咱們先加一個 __str__ 方法到前面的類中看看狀況。code
當你從新打印和查看這個類的實例的時候,你會看到一個稍微不一樣的結果對象
查看 my_car 的時候的輸出仍然和以前同樣,不過打印 my_car 的時候返回的內容和新加的 __str__ 方法的返回一致。類的 __str__ 方法會在某些須要將對象轉爲字符串的時候被調用。好比下面這些狀況blog
有了 __str__ 這個方法,你就不用手動去打印對象的一些信息或者添加額外的方法去達到目的。類到字符串的轉化使用 __str__ 這種 Pythonic 的方式實現便可。內存
使用 __repr__ 也有相似的效果
有的朋友可能發現,上面咱們查看 my_car 對象的時候,輸出的還是相似 <__main__.Car object at 0x10b142128> 這樣比較奇怪的結果。這是由於 Python 3 中一共有 2 中方式控制類到字符串的轉化,第一種就是咱們前面提到的 __str__ 方法,另外一個就是 __repr__ 方法。後者的工做方式與前者相似,可是它被調用的時機不一樣。
Python 2 中還有一個 __unicode__ 方法,後面我會說明,暫時跳過。
這裏有個簡單的例子,一樣是在以前的類上做改動
咱們經過下面的操做來感受下何時調用 __str__ ,何時調用的 __repr__ 。
從上面能夠看出,當咱們查看對象的時候(上圖的最後一個操做)調用的是 __repr__ 方法。
另外,列表以及字典等容器老是會使用 __repr__ 方法。即便你顯式的調用 str 方法,也是如此。
若是咱們須要顯示的指定以何種方式進行類到字符串的轉化,咱們可使用內置的 str() 或 repr() 方法,它們會調用類中對應的雙下劃線方法。(固然,上面的狀況除外)
固然,若是你直接調用 __str__ 或 __repr__ 方法,也能達到一樣的方法,可是不推薦這麼作。
那麼 __str__ 和 __repr__ 的差異是什麼
如今你可能在想,__str__ 和 __repr__ 的差異究竟在哪裏,它們的功能都是實現類到字符串的轉化,它們的特定並無體現出用途上的差別。
帶着這個這個問題,咱們試着去 Python 的標準庫中找找答案。咱們就來看看 datetime.date 這個類是怎麼在使用這兩個方法的。
所以,咱們有個初步的答案。
__str__ 的返回結果可讀性強。也就是說,__str__ 的意義是獲得便於人們閱讀的信息,就像上面的 '2018-04-03' 同樣。
__repr__ 的返回結果應更準確。怎麼說,__repr__ 存在的目的在於調試,便於開發者使用。細心的讀者會發現將 __repr__ 返回的方式直接複製到命令行上,是能夠直接執行的。
上面應該就是這兩個方法的意義所在吧(便於描述,後面我稱這爲一般的原則吧)。
可是於我的來講,若是按照一般的原則去編寫代碼會作不少額外的工做,兩個方法的返回結果只須要對開發者友好就能夠了,並不必定須要存儲某個對象的完整狀態。後面我會根據這一點,寫部分有實踐意義的代碼實例,並不徹底按照一般的原則。
爲何每一個類都最好有一個 __repr__ 方法
若是你沒有添加 __str__ 方法,Python 在須要該方法但找不到的時候,它會去調用 __repr__ 方法。所以,我推薦在寫本身的類的時候至少添加一個 __repr__ 方法,這能保證類到字符串始終有一個有效的自定義轉換方式。
咱們爲 Car 類添加一個 __repr__ 方法
注意,咱們這裏用了 !r 標記,是爲了保證 self.color 與 self.mileage 在轉化爲字符串的時候使用 repr(self.color) 和 repr(self.mileage) ,而不是 str(self.color) 和 str(self.mileage) 。
這個能正常工做,不過有個缺點,就是咱們把類的名稱寫死了。這有一個小技巧能夠改進這種方式,就是使用對象的 __class__.__name__ 屬性,該屬性總表明着類的名稱。
這樣作的話,當類名被修改的時候,咱們不須要修改 __repr__ 方法,這也符合軟件開發的 DRY 原則( Don’t Repeat Yourself )。
這種寫法也有一個很差的地方,就是格式化字符串太長了。固然,咱們好好調整一個格式也能符合 PEP 8 的代碼規範。
實現了 __repr__ 方法後,當咱們查看類的實例或者直接調用 repr() 方法,就能獲得一個比較滿意的結果了。
打印或直接調用 str() 方法也能獲得相同的結果,由於 __str__ 的默認實現就是調用 __repr__ 方法。
這樣就能以比較少的工做量,讓兩個方法都能工做,而且也有必定的可讀性,因此通常狀況下,我都推薦至少添加一個 __repr__ 方法。
下面是比較全的代碼示例
Python 2 中的 __unicode__ 方法
Python 3 中字符串用 str 類型表示,表明 unicode 字符串。而 Python 2 中字符串有兩種類型,一是 str ,只能存儲 ASCII 碼,另外一種是 unicode ,與 Python 3 中的 str 等同。
一般來講,用 __unicode__ 來控制類到字符串的轉化更容易被你們接受。和 __str__ 和 __repr__ 相似,你可使用內置的 unicode() 來顯示調用 __unicode__ 方法。
打印語句和 str() 會調用 __str__ 方法,unicode() 會先找 __unicode__ 方法,找不到的話會調用 __str__ 方法,並將其結果按當時的編碼方式解碼返回。
相對於Python 3 ,Python 2 中的類到字符串的轉化,顯得稍微複雜一些。不過,下面我給了個便於實踐的思路。因爲使用 unicode 處理字符串更方便,這也是趨勢,因此咱們總會實現本身的 __unicode__ 方法。同時,__str__ 方法的實現則依靠於 __unicode__ ,主要邏輯是調用 __unicode__ 方法並將其結果使用 UTF-8 編碼後返回。
因此,大部分狀況下,__str__ 方法都不須要作修改,對於新建的類,能夠直接把這個 __str__ 方法複製進去,而把關注點只放在 __unicode__ 方法的實現上。
下面是在 Python 2 中一段比較完整的示例
小結
* 咱們可使用 __str__ 和 __repr__ 方法定義類到字符串的轉化方式,而不須要手動打印某些屬性或是添加額外的方法。
* 通常來講,__str__ 的返回結果在於強可讀性,而 __repr__ 的返回結果在於準確性。
* 咱們至少須要添加一個 __repr__ 方法來保證類到字符串的自定義轉化的有效性,__str__ 是可選的。由於默認狀況下,在須要卻找不到 __str__ 方法的時候,會自動調用 __repr__ 方法。
* 在 Python 2 中,咱們可能更在乎類的 __unicode__ 方法的實現。