淺談 Python 的字串格式化
一直很習慣 f"{datetime.now():%Y}"
這類方便的 python 字串格式化工具,但好像一直沒有系統化的理解這些知識,於是在網路上查了一些資料並做了一些實驗,統整在這篇文章。
modulo
相信寫過 C 的一定不陌生這種寫法:
|
|
缺點就是 %
是一個 binary operator,代表說它只能左右各接受一個參數,也就是說如果要帶入多個參數時,就必須要使用 Tuple 或 Dictionary 之類的資料結構把多個參數當作一個參數傳入,這樣做就會因此失去了一些彈性。
因為很明顯的如果我們在後方使用 Tuple 的話,就沒辦再使用 Dictionary 傳入,也就沒辦法結合兩種資料結構的優勢。
於是乎 format 就出現了。
format
Python 3.0+
來看看 format 是怎麼用的:
|
|
format 看 ast 就可以很明顯的看出來是一個 function call 的樣子了,s
與 f
跑到 args,i
跑到 keywords,其實這也就對上 function 上的 *args, **kwargs
這兩個參數。
既然是 function call,後面也是可以放 expression 的:
|
|
除了 function call 的優點,當然也得遵守 function call 的規矩,不能把 args 放到 kwargs 後面,但這又是另一個可以討論的議題了,這邊不說這件事。
Format Specifiers
前面討論的是某個 instance 要擺在字串的哪個位置,至於那個 instance 要用什麼樣子的方式來呈現,可以透過 Format Specifiers 來決定。
PEP 3101 有提到一些 Standard Format Specifiers,我們可以直接拿出來用,像是一些進制轉換:
|
|
也可以用一些比較特殊的用法,有時候需要動態決定空格的數量,這時候可以把變數直接當作 Format Specifiers 的一部分,最後再交給 format 格式化:
|
|
那我們可以自幹 Format Specifiers 嗎?相信有用過 str 的人也一定幹過自訂 __str__
這個 function 的事,像是:
|
|
format 其實也是相同道理:
|
|
可以透過 parse spec
這個變數來選擇你要 return 回什麼字串。以 datetime 為例:
|
|
他會呼叫 strftime
這個 function 來格式化字串,一切都變得非常合理了。
f-strings
Python 3.6+
f-strings 其實就是 format 的語法糖,讓你少打 “.format()” 這幾個字,而且也讓整個字串更連續,更易閱讀,以上面的 Standard Format Specifiers 為例:
|
|
細節請參考 PEP 498 – Literal String Interpolation
String Template
最後是比較少用到的 String Template,跟 formatter 的概念類似,只不過他不支援 format specifiers:
|
|
在網路上看到常用的情境是要提供給 User 輸入的資料時,用 String Template 可以提供較好的安全性,像是:
|
|
如果 user_input 是 {func.__globals__[SECRET_KEY]}
,就可以直接拿到 super secret key
,這時候如果換成 String Template:
|
|
就算 user_input 是 {$func.__globals__[SECRET_KEY]}
也因為在 String Template 中無法執行 Expression 的緣故而只能得到 {<function func at 0x7f291b7100d0>.__globals__[SECRET_KEY]}
總結
其實自從知道 f-string 後,就很少再使用其他種字串格式化方法了,其中一個原因也是因為如果用多打 format
這幾個字,很常就會因為字數過長被 Formatter 給拆成兩行,而且 Formatter 總是會把 Code 拆得很醜,差一個換行、空白還沒對齊真的會讓人看得很痛苦,強迫症無法忍 Orz。
總之紀錄一下一些實驗的過程讓未來的自己參考吧,Python 更新的這麼頻繁,或許過了幾個版本後又長的完全不一樣了。
參考資料
5.6.2. String Formatting Operations
PEP 3101 – Advanced String Formatting