Python——花式打印對象的若干種方法



0c8530aeeea545bb6497a56db607c8f7.jpeg



今天是Python專題的第10篇文章,咱們來聊聊Python當中的類。編程


打印實例


咱們先從類和對象當中最簡單的打印輸出開始講起,打印一個實例是一個很是不起眼的應用,可是在實際的編程當中卻很是重要。緣由也很簡單,由於咱們debug的時候每每會想看下某個類當中的內容是否是符合咱們的預期。可是咱們直接print輸出的話,只會獲得一個地址。ide

咱們來看一個例子:函數

 
 

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y


if __name__ == "__main__":
    p = point(34)
    print(p)
spa

在這段代碼當中咱們定義了一個簡單的類,它當中有x和y兩個元素,可是若是咱們直接運行的話,屏幕上會輸出這樣一個結果:debug

<__main__.point object at 0x10a18c210>

這個是解釋器在執行的時候這個實例的一些相關信息,可是對於咱們來講幾乎沒有參考意義,咱們想要的是這個實例當中具體的值,而不是一個內存當中的地址。設計

想要實現這個功能,咱們有不少方法,下面咱們一一來看。3d


__str__方法


__str__方法你們應該都不陌生,它相似於Java當中的toString方法,能夠根據咱們的須要返回實例轉化成字符串以後的結果。調試

好比,咱們能夠在類當中重載這個方法,就能夠根據咱們的須要輸出結果了:code

 
 

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)
orm

當咱們運行它,獲得的結果會是:

x: 3, y: 4

__str__和__init__, __len__不少函數同樣是Python中的特殊函數,在咱們建立類的時候,系統會咱們隱式創造許多這樣的特殊函數。咱們能夠根據須要重載其中的一部分完成咱們想要的功能。好比若是咱們寫的是一棵二叉樹的類,咱們還能夠在__str__函數當中進行遞歸遍歷全部的節點,打印出完整的樹來。


__repr__方法


你也許可能也據說過__repr__函數,它也能夠實現根據咱們的須要自定義輸出的功能。好比咱們把上面的代碼改下函數名,也能夠獲得同樣的結果。

 
 

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

咱們運行它,一樣會獲得:

x: 3, y: 4

這是爲何呢,難道__repr__和__str__是同樣的嗎?若是是同樣的,Python的設計者幹嗎要保留兩個徹底相同的函數呢,爲何不去掉其中一個呢?

在分析緣由以前,咱們先來作一個實驗,若是咱們兩個函數都重載,那麼當咱們輸出的時候,程序執行的是哪個呢?爲了作好區分,咱們把repr當中的輸出的格式稍微修改一下。

 
 

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

    def __repr__(self):
        return '<point x: %s, y: %s>' % (self.x, self.y)

咱們運行以後,會發現輸出的結果仍是:

x: 3, y: 4

先彆着急下結論,咱們再把這段代碼拷貝到jupyter notebook當中,咱們此次不經過打印輸出,而經過jupyter自帶的交互框輸出交互結果,咱們再來看下:

奇怪,怎麼結果就變成了__repr__的結果了呢?

其實這正是反應了二者的區別,若是簡單理解,這兩個函數都是將一個實例轉成字符串。可是不一樣的是,二者的使用場景不一樣,其中__str__更加側重展現。因此當咱們print輸出給用戶或者使用str函數進行類型轉化的時候,Python都會默認優先調用__str__函數。而__repr__更側重於這個實例的報告,除了實例當中的內容以外,咱們每每還會附上它的類相關的信息,由於這些內容是給開發者看的。因此當咱們在交互式窗口輸出的時候,它會優先調用__repr__。

這兩個函數若是咱們只實現了一個,Python在調用的過程中,都會執行那個被咱們實現的。可是這二者自己的應用場景是有區別的,只是Python爲了咱們方便作了適配。

理論上來講,對於一個合格的__repr__函數要可以作到:

eval(repr(obj)) == obj

也就是說咱們經過__repr__輸出的內容執行以後能夠再還原獲得這個實例自己,固然在一些場景下這個很是難以實現,因此咱們退而求其次,保證__repr__當中輸出類和對象足夠多的信息,方便開發者調試和使用便可。

另外多說一句,repr是report的縮寫,因此它有一個報告的意思在裏面,而str就只是轉化成字符串而已。這二者仍是有必定區別的。


format


Python當中最經常使用的輸出函數除了上面兩個以外,還有一個就是format

比較簡單的用法就是經過{}表明變量,而後按照順序依次輸入:

7e14a9ea25e127b75f0cf5a468c112f0.jpeg

除此以外,咱們還能夠進一步寫明花括號裏的變量名稱,進一步增長可讀性:

7fc8023e5544f1237c89d989ebe768aa.jpeg

format的功能遠不止如此,它還支持許多參數,相似於C語言當中的printf,能夠經過不一樣的參數作到各類各樣的輸出。好比控制小數點後面保留的位數,或者是轉化成百分數、科學記數法、左右對齊等功能。這裏不一一列舉了,你們用到的時候再查詢便可。

咱們固然可使用format從新__repr__和__str__當中的邏輯,但這並不能體現它的強大。由於在Python當中,也爲類提供了__format__這個特殊函數,經過重寫__format__和使用format,咱們能夠作到更牛的功能。


format聯合__format__


咱們能夠在類當中重載__format__函數,這樣咱們就能夠在外部直接經過format函數來調用對象,輸出咱們想要的結果。

咱們來看代碼:

 
 

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

    def __format__(self, code):
        return 'x: {x}, y: {y}'.format(x = self.x, y = self.y)

咱們把剛纔的__repr__改爲了__format__,可是須要注意一個細節,咱們多加了一個參數code,這是因爲format當中支持經過參數來對處理邏輯進行配置的功能,因此咱們必需要在接口處多加一個參數。加好了之後,咱們就能夠直接調用format(p)了。

2e85566ecc8c31cdca658e33ee64405a.jpeg

到這裏尚未結束,在有些場景當中,對於同一個對象咱們可能有多種輸出的格式。好比點,在有些場景下咱們可能但願輸出(x, y),有時候咱們又但願輸出x: 3, y: 4,可能還有些場景當中,咱們但願輸出<x, y>。

咱們針對這麼多場景,若是各自實現不一樣的接口會很是麻煩。這個時候利用__format__當中的這個參數,就能夠大大簡化這個過程,咱們來看代碼:

 
 

formats = {
    'normal''x: {p.x}, y: {p.y}',
    'point' : '({p.x}, {p.y})',
    'prot''<{p.x}, {p.y}>'
}

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return 'x: %s, y: %s' % (self.x, self.y)

    def __format__(self, code):
        return formats[code].format(p=self)

咱們在調用的時候就能夠經過參數來控制咱們究竟使用哪種格式來格式化對象了:

951b81f53dbae452f7b4bdcfc07ddba9.jpeg

也就是說經過重載__format__方法,咱們把本來固定的格式化的邏輯作成了可配置的。這樣大大增長了咱們使用過程中的靈活性,這種靈活性在一些問題場景當中能夠大大簡化和簡潔咱們的代碼。對於Python這門語言來講,我我的感受實現功能只是其中很小的一個部分,把代碼寫得簡潔美觀,纔是其中的大頭。這也是爲何不少人都說Python易學難精的緣由。

相關文章
相關標籤/搜索