天天看點

《Python面向對象程式設計指南》——2.2 __format__()方法

本節書摘來自異步社群《python面向對象程式設計指南》一書中的第2章,第2.2節,作者[美]steven f. lott, 張心韬 蘭亮 譯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

string.format()和内置的format()函數都使用了__format__()方法。它們都是為了獲得給定對象的一個符合要求的字元串表示。

下面是給__format__()傳參的兩種方式。

someobject.__format__(""):當應用程式中出現format(someobject)或者"{0}".format(someobject)時,會預設以這種方式調用__format__()。在這些情況下,會傳遞一個空字元串,__format__()的傳回值會以預設格式表示。

someobject.__format__(specification):當應用程式中出現format (someobject, specification)或者"{0:specification}".format (someobject)"時,會預設以這種方式調用__format__()。

注意,"{0!r}".format()和"{0!s}".format()并不會調用__format__()方法。它們會直接調用__repr__()或者__str__()。

當specification是""時,一種合理的傳回值是return str(self),這為各種對象的字元串表示形式提供了明确的一緻性。

在一個格式化字元串中,":"之後的文本都屬于格式規範。當我們寫"{0:06.4f}"時,06.4f是應用在項目0上的格式規範。

python标準庫的6.1.3.1節定義了一個複雜的數值規範,它是一個包括9個部分的字元串。這就是格式規範的基本文法,它的文法如下。

<code>[[fill]align][sign][#][0][width][,][.precision][type]</code>

這些規範的正則表示如下。

這個正規表達式将規範分解為8個部分。第1部分同時包括了原本規範中的fill和alignment字段。我們可以利用它們定義我們的類中的數值類型的格式。

但是,python格式規範的文法有可能不能很好地應用到我們之前定義的類上。是以,我們可能需要定義我們自己的規範化文法,并且使用我們自己的__format__()方法來處理它。如果我們定義的是數值類型,那麼我們應該使用python中内建的文法。但是,對于其他類型,沒有理由堅持使用預定義的文法。

例如,下面是我們自定義的一個微型語言,用%r來表示rank,用%s來表示suit,用%代替%%,所有其他的文本保持不變。

我們可以用下面的格式化方法擴充card類。

方法簽名中,需要一個format_spec作為格式規範參數。如果沒有提供這個參數,那麼就會使用str()函數來傳回結果。如果提供了格式規範參數,就會用rank、suit和%字元替換規範中對應的部分,來生成最後的結果。

這允許我們使用下面的方法來格式化牌。

<code>print( "dealer has {0:%r of %s}".format( hand.dealer_card) )</code>

其中,("%r of %s")作為格式化參數傳入__format__()方法。通過這種方式,我們能夠為描述自定義對象提供統一的接口。

或者,我們可以用下面的方法:

這種方法的優點是把所有與字元串表示相關的邏輯放在__format__()方法中,而不是分别寫在__format__()和__str__()裡。但是,這樣做有一個缺點,因為并非每次都需要實作__format__()方法,但是我們總是需要實作__str__()。

string.format()方法可以處理{}中内嵌的執行個體,替換其中的關鍵字,生成新的格式規範。這種替換是為了生成最後傳入__format__()中的格式化字元串。通過使用這種内嵌的替換,我們可以使用一種更加簡單的帶參數的更加通用的格式規範,而不是使用相對複雜的數值格式。

下面是使用内嵌格式規範的一個例子,它讓format參數中的width更容易改變:

我們定義了一個通用的格式,"{hand}{count:{width}d}",它需要一個width參數,才算是一個正确的格式規範。

通過width=參數提供的值會被用來替換{width}。替換完成後,完整的格式化字元串會作為__format__()方法的參數使用。

當格式化一個包含集合的對象時,我們有兩個難題:如何格式化整個對象和如何格式化集合中的對象。以hand為例,其中包含了cards類的集合。我們會更希望可以将hand中一部分格式化的邏輯委托給card執行個體完成。

下面是hand中的format()方法。

hand集合中的每個card執行個體都會使用format_specification參數。對于每一個card對象,都會使用内嵌格式規範的方法,用format_specification建立基于"{0:{fs}}"的格式。通過這樣的方法,一個hand對象,player_hand,可以以下面的方法格式化:

<code>"player: {hand:%r%s}".format(hand=player_hand)</code>

這會将%r%s格式規範應用在hand對象中的每個card執行個體上。