在Python里什么時候用 .__repr__() 和 .__str__()

原文鏈接:https://realpython.com/python-repr-vs-str/
by
Mar 22, 2023
簡短地說:
.__repr__()
給程序員看而.__str__()
給用戶看如何獲取一個對象的字符串表示?
你應該在自定義類里定義
.__repr__()
和.__str__()
嗎?結語
電腦程序執(zhí)行的最常見的任務之一就是展示數(shù)據(jù)。程序通常把信息展示給使用者看。然而,一個程序也應該把信息展示給程序員看,以便開發(fā)和維護。程序員需要的關于對象的信息跟展示給用戶的應該有所不同,這就是.__repr__()
vs .__str__()
的由來。
一個Python對象有多個提供特定行為的特殊方法。有兩個相似的特殊方法都是以字符串形式來描述對象。這就是 .__repr__()
和 .__str__()
。 .__repr__()
方法返回一則程序員在維護和debug時所需的詳細說明。而 .__str__()
返回一則簡短信息給用戶。
.__repr__()
和 .__str__()
方法是你能在任何類里定義的特殊方法。讓你能在一些常見的輸出方式里控制對象的展示方式,例如用 print()
函數(shù)獲取到的結果,格式化字符串和交互環(huán)境。
在本教程中,你將學到如何區(qū)分 .__repr__()
和 .__str__()
以及如何在定義類時使用這些特殊方法。有效地定義這些方法會使你編寫的類可讀性更強、更容易debug和維護。所以,你什么時候該選擇.__repr__()
或.__str__()
?
Free Download: that shows you Python’s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.
簡短地說: .__repr__()
給程序員看而 .__str__()
給用戶看
dunder method
(雙下劃線方法)因為它們的名字里有雙下劃線。
.__repr__()
和 .__str__()
特殊方法都返回對象的字符串表示。字符串表示是一個展示了對象信息的字符串。你可以為不同的受眾定制信息,例如用戶或你的程序員小伙伴。
之所以有2個方法來展示一個對象,是因為它們的用處不同:
.__repr__()
提供了一個對象的正式字符串表示,目的是給程序員看。.__str__()
提供了一個對象的非正式字符串表示,目的是給用戶看。
.__repr__()
返回的字符串表示用來供程序員開發(fā)、維護程序。通常來說,它提供了這個對象的詳細的、無歧義的信息。另一個正式字符串表示的重要性質是,一個程序員應該可以比照它來創(chuàng)建一個跟原始對象一樣的新對象。
.__str__()
方法提供了面向用戶的字符串表示,這個用戶不一定要是Python程序員。因此,這種表示得確保任何用戶都能理解對象里蘊含的信息。一般地, .__str__()
更簡單、更易閱讀。
注意:這里有關于這兩個方法的一場討論, 。
展示對象兩種表示的一個方式是使用 .__repr__()
返回的字符串表示。與之相對,內置的 print()
函數(shù)展示了 .__str__()
返回的非正式字符串表示。
你可以看看 datetime
類的對象的 .__repr__()
和 .__str__()
返回的字符串:
你使用 today
的 datetime.datetime
對象。這個方法返回當前日期和時間。當你對僅包含變量 today
的一行代碼求值時,解釋器會展示由 .__repr__()
返回的字符串表示。這種表示展現(xiàn)了數(shù)據(jù)類型的名字以及重新創(chuàng)建這樣一個對象所需的參數(shù)。
當你使用 print()
,解釋器會展示 .__str__()
返回的表示形式。這條字符串展示了 (國際標準格式)的日期和時間。因此,這不是Python里的特定格式,而是廣泛用來表示日期、時間的一個標準格式。
通常來講,正式的字符串表示形式是一個有效的Python語句,你可以用同樣的值來創(chuàng)建新的對象。通過復制 datetime.datetime
對象的正式字符串表示形式并賦值給一個新的變量,就能確認這一點。你也可以試試復制非正式的字符串表示形式,但不會起作用的:
你對 today
求值時,從解釋器那得到輸出能創(chuàng)建出一個跟原來對象一樣的新對象。
然而, 你用 print()
得到的 .__str__()
的字符串表示不是有效的Python表達式,所以報了 SyntaxError
。
你也可以看看內置數(shù)據(jù)類型的字符串表示形式:
如果你對一行僅有一個對象或變量名的代碼求值時,Python解釋器就返回正式字符串表示。但是用 print()
時又發(fā)生了什么呢?在上面的例子里,正式的、非正式的字符串表示形式一致或非常相近。兩種表示形式都基本由你創(chuàng)建對象時使用的 (字面量)決定。
其他內置數(shù)據(jù)類型也有相同的正式、非正式字符串表示形式。在這些情況里,兩種表示形式都跟創(chuàng)建對象時的字面量相同。
注意:雖然大多數(shù)內置數(shù)據(jù)類型的兩種字符串表示形式都與字面量保持一致,但也有例外。例如,
set()
是空集合的正式與非正式字符串形式,因為字面量{}
代表的是空字典。
在下面這個例子中,你可以看看列表和字典的表示形式:
這些表示形式都沒有歧義,并且可以用同樣的值來創(chuàng)建新的對象。而且,它們對程序使用者也足夠清晰,因為它們傳達了對象里的信息,也無法進一步簡化了。這些對象不需要針對程序員和用戶具有不同的字符串表達。
注意:你可以在Python里使用 來把你的對象格式化成更方便閱讀的樣式,這在debug時很有用。
在這個章節(jié),你學到了由 .__repr__()
返回的正式字符串表示形式應該包含對象的無歧義信息,而且是面向程序員。你通??梢杂眠@種表示形式來創(chuàng)建一個跟原始對象相同的對象。
與之相對, .__str__()
返回的非正式字符串表示形式面向用戶,而且通常更簡單。大部分內置數(shù)據(jù)類型的字面量就是它的正式和非正式字符串表示。
在下一個章節(jié),你會學到其他獲取這兩種字符串表示形式的方法。
如何獲取一個對象的字符串表示?
你已經(jīng)看過了如何在標準Python解釋器里展示兩種字符串表示。到目前為止,你已經(jīng)使用了特殊方法來做到這一點。但通常來講,你也可以用內置的 repr()
函數(shù)和 str()
函數(shù)來獲取正式的和非正式的字符串表示。
當你把一個對象傳給 repr()
或 str()
,背后就是在調用這個對象的 .__repr__()
或 .__str__()
方法。你可以通過先前章節(jié)創(chuàng)建的 datetime.datetime
對象來確認這點:
當你把 today
傳給 內置函數(shù)repr
,程序會調用 today.__repr__()
。雖然你可以直接調用特殊方法 today.__repr__()
,但最好還是使用內置函數(shù) repr()
,因為在任何情況下你都不應該直接訪問特殊方法。特殊方法的目的是給對象增加功能,而不是直接調用。
類似地,你也可以用 str()
。它會調用傳入?yún)?shù)的 .__str__()
方法。
注意:表面上來看,用
str()
跟用repr()
確實差不多。然而,repr()
是一個內置函數(shù),而str
是一個類。因此,str()
通過把傳入?yún)?shù)轉為字符串創(chuàng)建了一個str
類的實例。這種差異并不會影響你使用str()
和repr()
的方式,基于它們都是 的事實,你的用法跟它們是個函數(shù)還是類的構造器無關。
你必須將一個對象作為參數(shù)傳給 str()
和 repr()
。然而,.__repr__()
和 .__str__()
是不需要傳額外參數(shù)的方法。和所有的 (實例方法)一樣,對象自己會作為第一個參數(shù)傳給方法。對于 .__repr__()
和 .__str__()
,對象也是唯一參數(shù),并且這些方法不需要另外的參數(shù)。
你也可以使用內置的 datetime
對象 today
傳給 format()
函數(shù)和 .format()
方法:
默認情況下,這些返回 .__str__()
返回的對象的非正式字符串表示。在接下來的章節(jié)里學習f-strings時,你會看到如何覆蓋默認的表示形式。
你在格式化字符串時最有可能使用的就是f-strings了,因為它們是Python里最新的字符串格式方式。你可以通過使用f-strings展示 today
類:
就跟 format()
和 .format()
一樣,f-strings 也展示了 .__str__()
返回的非正式字符串表示。你可以通過在f-string里使用 ?(字符串轉換標識)"!r"來得到正式的字符串表示:
轉換標識"!r"覆寫了默認的f-string,調用了對象的 .__repr__()
方法。
你也可以在f-string里使用等號(=)來同時展示變量名和值,主要在debug時這么做:
注意當你使用等號時,f-string默認使用 .__repr__()
返回的正式字符串表示。在這種情況下,由于在f-string里用等號通常都是在debug時使用,所以這種表示是面向程序員的。
你也可以通過使用轉換標識"!s"來覆寫這種行為:
當你使用"!s"轉換標識,f-string會使用非正式字符串表示。
在下一個章節(jié),你會學習往自定義的類中加入 .__repr__()
和 .__str__()
。
你應該在自定義類里定義 .__repr__()
和 .__str__()
嗎?
當你定義一個類,你可以定義一些特殊方法,以便給它增加功能。關于如何定義類,你在這篇教程里能學到更多: ?
。你可以通過定義一個 Book
類來學習自定義類的字符串表示。你可以用一個 book.py
腳本來定義這個類:
你定義的類 Book
有一個 .__init__()
方法。初始化時需要2個參數(shù), title
和 author
。
你帶著標題和作者創(chuàng)建了 The Odyssey 這本書的實例,然后你把 odyssey
傳給 print()
函數(shù)。你使用 print()
是因為當你在腳本里對一行只有變量名的代碼求值時,沒有任何輸出。讓你運行這個腳本,會得到下面的輸出:
這個輸出就是繼承自 object 類的對象的默認字符串表示。object 類是所有Python類的基類。它展示了:
__main__.Book
: 類名以及定義位置0x1025c4ed0
: 對象的內存地址
默認的字符串表示告訴了你類名和內存地址,以一個id()
)來獲取對象的標識,此時返回十進制數(shù)而不是十六進制數(shù)。
然而,內存地址鮮有用處。默認的表示形式并不提供任何對用戶或程序員有幫助的額外對象信息。
repr()
和 str()
都會返回這種默認的字符串表示:
你調用 repr(odyssey)
和 str(odyssey)
然后打印出它們的返回值。輸出都是默認的表示形式:
目前,輸出只能獲取到默認的表示形式。
你可以給這個類定義 .__repr__()
特殊方法:
.__repr__()
方法除了 self
以外沒有別的參數(shù),并且必須返回一個字符串。這段代碼的輸出展示了由 repr(odyssey)
和 str(odyssey)
返回的字符串表示,這種表示是你剛定義的:
repr()
內置函數(shù)調用了對象的 .__repr__()
方法。如果一個類沒有定義 .__str__()
方法,那么 str()
默認也會調用 .__repr__()
方法。
你定義了 .__repr__()
的返回值,包括類名、緊跟在后面的括號和兩個用于初始化類的參數(shù)。這種格式是一種理想的正式字符串表示,因為它是一段有效的Python表達式,能創(chuàng)建出和原對象一樣的對象。不論何時,你都應該用這種格式來定義正式字符串表示。
下一步,你可以為這個類定義 .__str__
方法:
你定義了 .__str__()
特殊方法,除了 self
外沒有任何額外參數(shù),并且應該返回一個字符串。這種表示形式可以是任何你認為能幫助到用戶的字符串。這個版本的代碼輸出順序展現(xiàn)了正式和非正式的字符串表示形式:
.__str__()
返回的非正式字符串表示僅僅展示出了書名,也沒有對類名的引用。你可以自定義非正式字符串表示來滿足你的需求:
現(xiàn)在,.__str__()
返回的字符串是用引號括起來的書名和緊隨其后的作者名。注意如果你想在字符串內部用雙引號,可以用單引號表示字符串。這段代碼的輸出先是展示了正式的字符串表示,然后就是修改后的非正式字符串表示:
正式的字符串表示包含了一個程序員需要的所有細節(jié),并允許你復制對象進行深入研究。這么做有2個主要好處:
使程序更易維護
有助于debug
你可以使用這個字符串表示里的表達式來創(chuàng)建一個跟原來一樣的對象。這種特性在debug時很有用,因為你或者你的小伙伴可以復制一個對象來深入研究。
然而,非正式字符串表示是一種對用戶來說更合適、更友好的格式。這種表示對他們來說更容易閱讀。
注意:當實現(xiàn)這兩個特殊方法時,應該采取額外的防范措施,避免泄露敏感信息,例如用戶密碼是無論如何都不能暴露出來的。最好跳過這種屬性,或是加密,然后再返回相關字符串。
當你定義了一個類,最好要定義 .__repr__()
,這樣(你的類)就有了一個正式的字符串表示。用過實現(xiàn)這個方法,你避開了用處不大的默認字符串表示形式。這個方法也是非正式字符串表示的備選方案,如果你的兩種表示形式本來就是一樣的,就顯得很方便。
如果你需要把對象展示給用戶,可以定義 .__str__()
。這個方法的輸出對用戶來講更容易理解。
如果你在用Python的 .__repr__()
,除非你想覆蓋默認格式。
現(xiàn)在你已經(jīng)準備好在任何自定義的類中實現(xiàn)字符串表示了。
結語
在本篇教程中,你學到了Python對象中正式、非正式字符串表示的區(qū)別。特殊方法 .__repr__()
返回正式字符串表示,供程序員開發(fā)和維護程序。而特殊方法 .__str__()
返回的非正式字符串表示,則對用戶來說更加友好。
現(xiàn)在,你可以區(qū)分這兩種表示,也知道在哪種情況下使用哪種表示。如果你還沒有這么做,那么至少應該在類里實現(xiàn) .__repr__()
方法來提供正式字符串表示。
Free Download: