盤一盤 Python 系列特別篇 - 格式化 String

  • 2020 年 3 月 13 日
  • 筆記

引言

格式化字元串(string formatting)是以指定輸出參數格式和相對位置來「美化」字元串。輸出參數格式包括數字的小數點位數、字元串大小寫等,相對位置標註出被格式化的詞是在句中的位置。比如

 'It costs %.2f.' %(123.456)

其中 %.2f 是 123.456 的輸出參數格式,.2f 代表保留小數點兩位,而 %.2f 所在的位置在 costs 後面,意思說將 123.456 變成 123.46 後插在 costs 後面。

print('It costs %.2f.' %(123.456))
It costs 123.46.

格式化字元串有四種方法:

  1. 用 % 字元 (上面用的)
  2. 用 $ 字元
  3. 用 format() 函數
  4. 用 f-string

本帖思路極其簡單

  • 第一章先比較上述四種格式化方法,讓它們完成同樣的任務,誰好誰壞一目了然。
  • 第二章選出兩個勝者,format() 函數和 f-string,對它們再做一個全方位的對比(),選出一個最後勝者 f-string。
  • 第三章總結,用 f-string!用 f-string!用 f-string!

1、四種格式化方法

整帖中都以下面這句話舉例子,即「在 2020 年 3 月 4 日,阿里巴巴的市值為 553789 百萬美元」。

s = 'On 04 Mar 2020, the market cap of Alibaba is 553789 mio USD.'  s
'On 04 Mar 2020, the market cap of Alibaba is 553789 mio USD.'

首先定義市值(Mcap)和貨幣單位(curr)兩個變數。

Mcap = 553789  curr = 'USD'

下面開始比較四種格式化的方法,用心去感受哪種最好。

用 % 字元格式化

'The market cap of Alibaba is %d mio %s.' %(Mcap, curr)
'The market cap of Alibaba is 553789 mio USD.'

該方法的核心範式是:

首先寫句子;再用 % 加小括弧,裡面列出要被格式化的變數名;最後在句子一一對應變數名的地方寫出 %x(d 代表數字,s 代表字元串,等等)。

照著上面具體例子品一遍,該方法不是很方便對吧。

用 $ 字元格式化

from string import Template  info = dict(Mcap=Mcap, curr=curr)  Template('The market cap of Alibaba is $Mcap mio $curr.')          .substitute(info)
'The market cap of Alibaba is 553789 mio USD.'

該方法的核心範式是:

在此方法下,我們需要將變數名和變數值作為鍵和值,創建一個字典。

首先要調用 Template 庫函數並將句子傳給它;再把字典傳入 substitute() 函數;最後在句子一一對應變數名的地方寫出 $k(k 代表字典中的鍵)。

照著上面具體例子品一遍,該方法也不是很方便對吧,而且還要引用額外的庫函數。

用 format 函數格式化

'The market cap of Alibaba is {} mio {}.'.format(Mcap, curr)
'The market cap of Alibaba is 553789 mio USD.'

該方法的核心範式是:

首先寫句子;再用 format() 函數,裡面列出要被格式化的變數名;最後在句子一一對應變數名的地方只用寫出 {}。

照著上面具體例子品一遍,該方法還可以對吧,但還是有些冗長。

用 f-string 方法格式化

f'The market cap of Alibaba is {Mcap} mio {curr}.'
'The market cap of Alibaba is 553789 mio USD.'

該方法的核心範式是:

首先寫出句子,以 f' ' 或 F' ' 的形式;然後在句子只用寫出 {v},其中 v 是要被格式的變數。

照著上面具體例子品一遍,f-string 不能更方便是吧。很顯然它比其他三個用起來更簡單更自然,更強的是它還是最快的。

當決定用哪種格式化時,通常我們評估它是否簡單直觀而且符合自己的習慣,比如

  • 那些有 C 語言背景的使用者就偏向用 「%」 字元
  • 那些偏好標準化的使用者就偏向用 "$" 字元
  • 那些偏好個性化的使用者就偏向用 format 函數

而我就喜歡 f-string 方法,下節就來比較 format 函數和 f-string 方法。我發現只要 format 函數能做到的,f-string 都能做到,而且程式碼寫出來更漂亮,運行更快,那為何不用呢?

2、Format Vs f-string

本節是兩強相爭,format() 函數和 f-string,它們兩個能做的事幾乎一樣,比如都能接收字典型數據、列表型數據、類數據;都能給數字型變數保留小數點,加分隔符;都能個性化日期型變數,等等。

字典型數據

創建字典。

info = dict(Mcap=Mcap, curr=curr)  info
{'Mcap': 553789, 'curr': 'USD'}

先看 format() 函數。

大括弧用 0 和 1 對應著 format() 函數中第 1 個和第 2 個參數。

'The market cap of Alibaba is {0} mio {1}.'  .format(info['Mcap'], info['curr'])
'The market cap of Alibaba is 553789 mio USD.'

這樣寫有些麻煩,format() 函數只傳進字典 info,而大括弧里的 0 就代表它,0[Mcap] 和 0[curr] 就是用鍵來獲取值。

'The market cap of Alibaba is {0[Mcap]} mio {0[curr]}.'.format(info)
'The market cap of Alibaba is 553789 mio USD.'

我們來看看 f-string,怎麼出錯了???

f'The market cap of Alibaba is {info['Mcap']} mio {info['curr']}.'
File "<ipython-input-26-1b50e8c7dcf6>", line 1      f'The market cap of Alibaba is {info['Mcap']} mio {info['curr']}.'                                               ^  SyntaxError: invalid syntax

因為一個完整的字元串句子是有前後單引號包住的,注意 f 後面有個單引號,而 info['Mcap'] 的前單引號被 Python 誤認為一連串字元串結束了。


怎麼辦呢?把 f-string 里的單引號改用雙引號就可以了。

f"The market cap of Alibaba is {info['Mcap']} mio {info['curr']}."
'The market cap of Alibaba is 553789 mio USD.'

關鍵詞

創建字典,其鍵可當成關鍵詞(keyword)。

info = {'Mcap':Mcap, 'curr':curr}  info
{'Mcap': 553789, 'curr': 'USD'}

先看 format() 函數。

用 ** 將字典打散傳入 format 就可以了。

'The market cap of Alibaba is {Mcap} mio {curr}.'  .format(**info)
'The market cap of Alibaba is 553789 mio USD.'

**info 和 format(info['Mcap'], info['curr']) 是一樣的。

列表型數據

創建列表。

info = [Mcap, curr]  info
[553789, 'USD']

先看 format() 函數。

format() 函數只傳進列表 info,而大括弧里的 0 就代表它,0[0] 和 0[1] 就是用索引來獲取值。

'The market cap of Alibaba is {0[0]} mio {0[1]}.'  .format(info)
'The market cap of Alibaba is 553789 mio USD.'

再看 f-string,也是用索引來獲取列表 info 中的值,但是程式碼更簡潔。

f"The market cap of Alibaba is {info[0]} mio {info[1]}."
'The market cap of Alibaba is 553789 mio USD.'

類數據

創建類 Info 並初始化一個對象 info。

class Info():      def __init__(self, Mcap, curr):             self.Mcap = Mcap             self.curr = curr
info = Info(Mcap, curr)  info
<__main__.Info at 0x21113395048>

先看 format() 函數。

format() 函數只傳進對象 info,而大括弧里的 0 就代表它,0.Mcap 和 0.curr 就是用 info.method 的方式來獲取值。

'The market cap of Alibaba is {0.Mcap} mio {0.curr}.'  .format(info)
'The market cap of Alibaba is 553789 mio USD.'

再看 f-string,也是用 info.method 的方式來獲取列表 info 中的值,但是程式碼更簡潔。

f"The market cap of Alibaba is  {info.Mcap} mio {info.curr}."
'The market cap of Alibaba is 553789 mio USD.'

每三位數加逗號 ,

還是用字典作為輸入來舉例。

info = dict(Mcap=Mcap, curr=curr)  info
{'Mcap': 553789, 'curr': 'USD'}

一般在列印錢的數額時候,每三位需要一個逗號隔開而便於閱讀。

先看 format() 函數。

用 {0:,} 格式可以做到,重點是冒號和逗號。0 代表是 info[Mcap],即市值,是 553789。冒號後面跟著具體的格式,而逗號就是這個具體的格式。

'The market cap of Alibaba is {0:,} mio {1}.'  .format(info['Mcap'], info['curr'])
'The market cap of Alibaba is 553,789 mio USD.'

用 f-string 添加逗號的用法一樣,只不過程式碼簡潔些。

f"The market cap of Alibaba is  {info['Mcap']:,} mio {info['curr']}."
'The market cap of Alibaba is 553,789 mio USD.'

換算單位

現在加入我們想把百萬(mio)單位換算成十億(bio),需要在市值 553789 上除以 1000。

首先在字典中多添加一個鍵值對,unit:'bio'。

info = dict(Mcap=Mcap, curr=curr, unit='bio')  info
{'Mcap': 553789, 'curr': 'USD', 'unit': 'bio'}

接下來看 format() 函數和 f-string 的格式化用法,後者明顯簡潔。

'The market cap of Alibaba is {0:,} {1} {2}.'  .format(info['Mcap']/1000, info['unit'], info['curr'])
'The market cap of Alibaba is 553.789 bio USD.'
f"The market cap of Alibaba is  {info['Mcap']/1000:,} {info['unit']} {info['curr']}."
'The market cap of Alibaba is 553.789 bio USD.'

保留小數點位數

現在假如我們覺得小數點 3 位太多了,不要任何小數點。

先看 format() 函數。

用 {0:,.0f} 格式可以做到,冒號和逗號上面上面解釋過。.0f 代表小數點後面保留 0 位。

'The market cap of Alibaba is {0:,.0f} {1} {2}.'  .format(info['Mcap']/1000, info['unit'], info['curr'])
'The market cap of Alibaba is 554 bio USD.'

用 f-string 的用法一樣,只不過程式碼簡潔些。

f"The market cap of Alibaba is  {info['Mcap']/1000:,.0f} {info['unit']} {info['curr']}."
'The market cap of Alibaba is 554 bio USD.'

加入日期

首先引入 datetime,創建日期。

from datetime import  datetimedate = datetime(2020, 3, 4)

在字典中增加一個鍵值對,date: datetime(2020, 3, 4)。

info = dict(date=date, Mcap=Mcap, curr=curr, unit='bio')  info
{'date': datetime.datetime(2020, 3, 4, 0, 0),   'Mcap': 553789,   'curr': 'USD',   'unit': 'bio'}

接下來比較 format() 函數和 f-string,後者明顯簡潔。

'On {3}, the market cap of Alibaba is {0:,.0f} {1} {2}.'  .format(info['Mcap']/1000, info['unit'], info['curr'], info['date'])
'On 2020-03-04 00:00:00, the market cap of Alibaba is 554 bio USD.'
f"On {info['date']}, the market cap of Alibaba is  {info['Mcap']/1000:,.0f} {info['unit']} {info['curr']}."
'On 2020-03-04 00:00:00, the market cap of Alibaba is 554 bio USD.'

但日期格式太難看了,調整日期格式可參考 https://strftime.org/。

  • %Y - 年的全稱 (如 2020)
  • %b - 月份的簡稱 (如 Mar)
  • %d - 天數 (如果是個位數用零填充,如 03)
  • %a - 日期的簡稱 (Wed)

結果如下:

'On {3:%b %d, %Y (%a)}, the market cap of Alibaba is {0:,.0f} {1} {2}.'  .format(info['Mcap']/1000, info['unit'], info['curr'], info['date'])
'On Mar 04, 2020 (Wed), the market cap of Alibaba is 554 bio USD.'
f"On {info['date']:%b %d, %Y (%a)}, the market cap of Alibaba is  {info['Mcap']/1000:,.0f} {info['unit']} {info['curr']}."
'On Mar 04, 2020 (Wed), the market cap of Alibaba is 554 bio USD.'

3、總結

困死了,不想總結了。以後想格式化字元串用 f-string 就完事了。如果真嗷嗷想用別的那就看張圖吧。

Tags: