iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 10
1
Data Technology

30天python雜談系列 第 10

repr與str雜談———暴風雨前的輕鬆小品技術文

python repr與str雜談

囉唆一下:緊接這篇之後就是大魔王系列———版本差異雜談了,所以就特別發篇小品文讓自己稍做休息~

大家在用python shell做一些輸出測試的時候,應該會好奇直接在shell裡輸入變數跟在shell裡調用print function同樣都會有輸出,但是輸出的內容有時候會不一樣,這是怎麼回事呢?其實在python中每一個對象都會去implement __str__還有__repr__兩個方法,這兩個方法都會回傳一個代表這個對象的字串,而如果調用了print function,其輸出是__str__返回的字串,若是直接輸入變數,輸出則為__repr__返回的字串:

In python3 shell:
>>> class Test():
...     def __init__(self):
...             pass
...     def __repr__(self):
...             return "the repr"
...     def __str__(self):
...             return "the str"
... 
>>> a = Test()
>>> a
the repr
>>> print(a)
the str

在python中,每個對象都可以用str()與repr()來轉成可顯示的字串,str()'可讀性'較高,是給開發者閱讀對象中的'有用資訊'的字串,而repr()的英文全名是representation,其產生的字串是給python的直譯器看的,這個字串會顯示'明確且教詳盡的資訊',通常'可以讓python得知究竟這字串所代表的對象為何'。聽起來有點繞口,沒關係,我會試著從例子解釋清楚。

In python3 shell:
>>> a = 1
>>> print(a) # 使用print函數等同於在螢幕上打印出str(a)
1
>>> a # 直接輸入對象本身等同於在螢幕上打印出repr(a)
1
>>> b = '1' 
>>> print(b) # 使用print函數等同於在螢幕上打印出str(b)
1
>>> b # 直接輸入對象本身等同於在螢幕上打印出repr(b)
'1' 

例子裡a是一個整數1,b是一個字串1,先來檢視一下print函數得出來的結果,相信大家都同意他們用print函數得出來的結果(也就是str method得出的字串)都是1,因為的確就是我們在這兩個變數中所認為的'有用資訊',然後剛剛好這兩個變數的'有用資訊'看起來一樣。但實際上a和b這兩個變數所代表的對象是不一樣的,因此,這兩個變數的repr字串就會是不同的,而且'可以讓python得知究竟這字串所代表的對象為何',所以repr(b)的字串才會多帶有兩個單引號('),這樣python才知道這是個字串物件。

題外話,也因為repr()顯示的字串可以讓python了解其所指的的對象為何,所以通常把repr字串傳入eval()[註一]的參數可以得回原始的對象,因此有人說repr()和eval()是互為可逆的轉換。

In python3 shell:
>>> a = 1
>>> a==eval(repr(a))
True
>>> a='example'
>>> a==eval(repr(a))
True
>>> a=1.234
>>> a==eval(repr(a))
True
>>> a=[1,2,5]
>>> a==eval(repr(a))
True
>>> a=(1,2,5)
>>> a==eval(repr(a))
True
>>> a=set({1,2,4})
>>> a==eval(repr(a))
True
>>> a={'x':1,'y':1}
>>> a==eval(repr(a))
True

但注意,雖然許多內建對象都有這種行為但不是所有對象都可以這樣做,並沒有硬性規定說這兩個方法一定互為可逆,比如說自定義的類別物件(因為自己也能隨意定義//repr//),或是內部結構複雜以及內部資訊過多的對象(ex. numpy array),python只會試圖讓repr字串可以在eval()中被執行,repr()和eval()互為可逆聽聽就好,詳細定義與表示方法可以參考:https://docs.python.org/3/library/functions.html#repr。

另外,如果現在我想要觀察的對象在一個list,tuple,set或是dict時,他的輸出行為看起來又是怎樣呢?

In python3 shell:
>>> class Test():
...     def __init__(self):
...         pass
...     def __repr__(self):
...         return "the repr"
...     def __str__(self):
...         return "the str"
... 
>>> a=Test()
>>> _list=[a]
>>> _dict={'a':a}
>>> _set = set({a})
>>> _tuple = (1,a)
>>> _list
[the repr]
>>> print(_list)
[the repr]
>>> _dict
{'a': the repr}
>>> print(_dict)
{'a': the repr}
>>> _tuple
(1, the repr)
>>> print(_tuple)
(1, the repr)
>>> _set
{the repr}
>>> print(_set)
{the repr}
>>> 

結果是全部都會調用repr字串,好到此為止,特別在凌晨發一篇文,這樣明天就不用煩惱鐵人賽了,我們後天大魔王系列見。

[註一]: python的eval和exec是一個可以動態執行程式碼的函數,凡傳進這兩個函數裡的字串參數,都會被當作一段代碼來執行,不同的是eval只能執行一行expression(表達式),exec可以執行多行程式碼,這兩個函數讓python可以動態產生新的程式碼並執行,這實在是一個強大的功能,但是這樣的自由度也帶來一定的危險性,因為光是一行字串就可能給予整個程式或是整個系統莫大的影響,比如說"os.system('rm -rf /')",這會讓你整個作業系統的檔案全部被刪除!


上一篇
UnicodeError雜談之三———快點投靠python3啦啦啦
下一篇
版本差異雜談之一———print與括號的糾葛
系列文
30天python雜談30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言