Python 中的 __str__ 與 __repr__ 到底有什麼差別

2021-01-11 開發小凱

很多時候我們自己編寫一個類,在將它的實例在終端上列印或查看的時候,我們往往會看到一個不太滿意的結果。

類默認轉化的字符串基本沒有我們想要的一些東西,僅僅包含了類的名稱以及實例的 ID (理解為 Python 對象的內存地址即可)。雖說這總比沒有好,但確實是沒什麼用處啊。

所以,我們可能會手動列印對象的一些屬性或者是在類裡自己實現一個方法來返回我們需要的信息。

這沒有什麼不對的地方,但是我們可以使用更 Pythonic 的方式來解決這個問題。

使用 __str__ 實現類到字符串的轉化

不用自己另外定義一個方法,和 JAVA 的 toString() 方法類似,你可以在類裡實現__str__ 和 __repr__ 方法從而自定義類的字符串描述,這兩種都是比較 Pythonic 的方式去控制對象轉化為字符串的方式。

下面我們通過做實驗慢慢的來看這兩種方式是怎麼工作的。首先,我們先加一個 __str__ 方法到前面的類中看看情況。

當你重新列印和查看這個類的實例的時候,你會看到一個稍微不同的結果

查看 my_car 的時候的輸出仍然和之前一樣,不過列印 my_car 的時候返回的內容和新加的 __str__ 方法的返回一致。類的 __str__ 方法會在某些需要將對象轉為字符串的時候被調用。比如下面這些情況

有了 __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__ 方法的實現。

相關焦點

  • Python繪圖筆記:繪製四色散點圖和誤差條形圖
    #設置透明度和點的邊緣色為無 ax.scatter(x, y, c=color, s=scale, label=color, alpha=0.3, edgecolors='none')#設置圖例和網格線ax.legend()ax.grid(True)plt.show()2.填充標記#python2裡面,dict.items返回的是數組
  • 從零開始學 Python 之輸入與輸出
    如果你希望輸出的形式更加多樣,可以使用 str.format() 函數來格式化輸出值。如果你希望將輸出的值轉成字符串,可以使用 repr() 或 str() 函數來實現。str(): 函數返回一個用戶易讀的表達形式。repr(): 產生一個解釋器易讀的表達形式。
  • 223個Python小例子(1-60)
    (i)Out[15]: '100'In [16]: str([])Out[16]: '[]'In [17]: str(tuple())Out[17]: '()'於是就有了slice對象。__class__Out[38]: __main__.Student問題在於,Student 類有 __class__屬性,如果有,返回的又是什麼?In [39]: xiaoming.__class__.
  • Python 標準庫之 sys & copy
    本文字數:3091 字閱讀本文大概需要:8 分鐘寫在之前在前天的文章(標準庫的自我介紹)中我們學習了什麼是標準庫,但是標準庫的內容非常多,有人專門為標準庫寫過一本書,在接下來的幾天我會根據我自己的理解,選幾個給大家學一下,一來是為了顯示一下標準庫的強大,二來演示如何理解和使用標準庫
  • 打基礎一定要吃透這12類 Python 內置函數
    內置函數就是python給你提供的, 拿來直接用的函數,比如print.,input等。截止到python版本3.6.2 python一共提供了68個內置函數,我將它們分成 12 類,方便你學習。1. 和數字相關1.
  • Python2 倒計時,還不快來掌握 Python3 酷炫的新特性?|原力計劃
    類型提示 Type hinting(最低 Python 版本為 3.5)程式語言有很多類型,靜態編譯型語言和動態解釋型語言的對比是軟體工程中一個熱門的話題,幾乎每個人對此有自己的看法。在靜態語言中類型標註無疑是讓人又愛又恨,愛的是編譯速度加快,團隊合作中準確了解函數方法的入參類型,恨的是Coding時極其繁瑣的標註。
  • 初識python
    2,python歷史。宏觀上:python2 與 python3 區別:python2 源碼不標準,混亂,重複代碼太多,python3 統一 標準,去除重複代碼。3,python的環境。編譯型:一次性將所有程序編譯成二進位文件。缺點:開發效率低,不能跨平臺。優點:運行速度快。
  • 從0開始學python-6.2 用python讀寫文件
    上節課我們學習了什麼文件系統,文件樹的組成結構是什麼樣的。我們還學習了用python來查找、重命名一個文件。這節課,我們一起學習一下怎麼用python操作一個文件的內容。我們來看看用python怎麼做這些事情。打開文件在對文件內容操作之前,我們首先要打開文件。我們可以使用open函數打開文件,看代碼:file = open('./hello.py', 'r')open函數需要兩個參數,第一個參數是文件位置,就是我們要打開哪個文件。第二個參數是打開模式。什麼是打開模式呢?
  • 乾貨| 完美Python入門基礎知識點總結
    在 Python 中,所有標識符可以包括英文、數字以及下劃線(_),但不能以數字開頭。Python 中的標識符是區分大小寫的。以下劃線開頭的標識符是有特殊意義的。python的字串列表有2種取值順序從左到右索引默認0開始的,最大範圍是字符串長度少1從右到左索引默認-1開始的,最大範圍是字符串開頭List(列表) 是 Python 中使用最頻繁的數據類型列表可以完成大多數集合類的數據結構實現。
  • Python基礎學習之常用六大數據類型
    Python六大常用數據類型: int 整數 float 浮點數 str 字符串 list 列表 tuple 元組 dict 字典講解這些先說一下python中的變量與變量名。變量其實本質上是一個具有特殊格式的內存,變量名則是指向這個內存的別名。python中的變量不需要聲明,所有的變量必須賦值了才能使用。
  • python的核心數據類型有哪些?
    數據類型數字: int,long,float,complex,bool字符: str,unicode列表: list字典: dict元組: tuple文件: file其他類型: 集合(set),frozenset,類類型,None類型轉換str(),repr()或format(): 將非字符類型數據轉換為字符int(): 轉為整數float
  • python基礎-bytes和bytearray的用法
    Python中的序列類型有bytes和bytearray。二進位序列類型的用法比較少見,是python中少用的一種序列類型,對於二進位序列類型,大家基本了解即可。'>print(type(b"ffff")) # 字節類型 <class 'bytes'># bytes是byte的序列,字符串是字符的序列# str---》bytess1 = "中"b1 = s1.encode('utf-8') # 編碼(encode)print
  • 相比於Java,python到底有哪些優勢?
    導讀 日新月異,在計算機行業中更是如此。談到編程,首先不可避免的是程式語言。由於在AI的帶動下python更是異軍突起,撼動了許多老大哥的地位。可唯獨java穩如泰山,不可動搖!自然而然的就會出現python與Java的討論聲。
  • 用Python實現簡易超市售貨系統
    今天來實現一個簡單的超市售貨系統數據存儲形式為json的數據首先是讀取數據,這裡用到的Python的json庫,用於處理json類型的數據```pythondef load(): # 數據讀取j = open('goods.txt', 'r', encoding='utf-8')# 逐行讀取文件中的數據
  • python工具箱_python遺傳算法工具箱多變量 - CSDN
    'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr
  • python|圖像識別
    今天以女神宋慧喬的兩張不同照片為例,利用python識別其相似度,從而判定是否是同一人,同時讓我們對圖像識別有個初步的了解,什麼?照片可以換蒼老師和波老師嗎?我懷疑你們在開車,可是我沒有證據!requestswith open("1.jpg", "rb") as f:    pic1 = f.read()with open("2.jpg", "rb") as f:    pic2 = f.read()image_data = json.dumps(    [        {"image": str
  • 科悟學院介紹什麼是Python、python能做什麼?
    2020年什麼是另一個風口?哪些人能再一次的崛起?這是很多人想知道的,今天小編就給你揭秘一個行業——Python(AI人工智慧),有人會問python到底是什麼?能做什麼?下面科悟學院介紹什麼是python和python能做什麼,希望對於正在學習的你有所幫助。
  • 幾個整蠱的Python程序 自己娛樂就好 勿做其它用途
    過程中如果出現 BUG(一般是編碼錯誤),點擊導航查看解決方案無聊程序之一while True:     n = input("猜猜我在想啥?")     你的朋友將永遠無法知道你在想什麼。當然我安裝 360 之後,程序沒了。有興趣研究免殺的,可以在給本文點個讚,點讚過 100,橡皮擦出套 Python 免殺教程。