Python string.Formatter - 取得 format string 中的 placeholders

Posted on  Mar 14, 2018  in  Python 程式設計 - 高階  by  Amo Chen  ‐ 2 min read

有時候我們需要取得 format string(例如: This is a format string: Hello, {name} )中的 placeholders(例如: {name} )進行比較,例如檢查翻譯人員是否不小心把翻譯字串中的 placeholder 給遺漏了,因為這可能會造成翻譯後的字串漏字。

所幸這種需求可以透過 string.Formatter 幫忙取得字串中的 placeholders 。

直接看範例:

>>> from string import Formatter
>>> iterable = Formatter().parse('{greeting} {name}, this is an example')
>>> iterable
<formatteriterator object at 0x101cba0c0>
>>>
>>> for r in iterable:
...     print(r)
...
('', 'greeting', '', None)
(' ', 'name', '', None)
(', this is an example', None, None, None)

上述可以看到 Formatter().parse('{greeting} {name}, this is an example') 在解析完 format string 後會回傳一個 formatteriterator object ,接著我們走訪這個 object 取出每一個結果,每個結果都是一個長度 4 的 tuple ,分別代表的是 (literal_text, field_name, format_spec, conversion) ,其中 field_name 就是我們想要的資訊,因此要比對 2 個字串是否含有相同 placeholders 時,只要比對 field_name 即可。

接著,稍微淺談一下 format_specconversion

Format string 可以有所謂的 format specification 與 conversion ,例如: {number:,.2f} 中的 ,.2 代表數字超過千分位顯示逗號以及顯示到小數點後第 2 位,這就是 format_spec

>>> '{number:,.2f}'.format(number=1000.1314)
'1,000.13'
>>>
>>> for r in Formatter().parse('{number:,.2f}'):
...     print(r)
...
('', 'number', ',.2f', None)

而 conversion 目前則有 3 種表示方式,分別為 !s , !r!a ,分別代表 1 個變數在被放進 placeholder 之前會分別用 str() , repr() , ascii() 呼叫。

Three conversion flags are currently supported: ‘!s’ which calls str() on the value, ‘!r’ which calls repr() and ‘!a’ which calls ascii().

p.s. 由於 ascii() 用到的情況相對少,跟 repr() 行為也很像,因此在此不討論

直接看個範例會更加了解文件的意思,以下模擬 1 個 class 有 2 種 methods ( __str__ , __repr__ ) ,然後分別看他們被放到不同 conversion 的 format string 會發生何事:

class User(object):

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def __repr__(self):
        return 'User("{}")'.format(self.name)


user = User('amo')

print('str -> {!s}'.format(user))
print('repr -> {!r}'.format(user))

執行上述程式會出現以下結果:

str -> amo
repr -> User("amo")

看到這就可以明白 user 被放到 format string 之前,被分別用 str(user)repr(user) 取得結果後再放進去 format string ,最後變成我們看到的樣子。

p.s. 關於 str()repr() 為什麼會執行 __str____repr__ 可以參考 object.__str__

以上, Happy Coding!

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!