軌道
/
Python
Python
/
課程大綱
/
字串格式化
字串

字串格式化 在 Python

1 個練習

關於 字串格式化

Python 中的字串格式化

字串格式化是將值轉換成字串,再把它們插入字串範本的過程。 Python 之禪主張,應該只有一種 顯而易見 的做法來完成一件事。 但談到字串格式化,情況就有點……_沒那麼禪_了。

你可能會很意外地發現,Python 中有四種主要的字串格式化方式,各自適用於不同的情境。 部分原因來自 Python 悠久的歷史,部分則來自國際化或輸入清理等考量。 我們會從字串格式化工具箱中最新加入的方法開始,再一路往回介紹「舊式」或「printf() 風格」的字串格式化。

字面字串插值:f-string

在 Python 3.6 中引入的 f-strings(「格式化字串」的簡稱)或字面字串插值,是一種在引號前加上 f(或 F)前置字元(像這樣:f'{object}'),以便快速有效率地將運算式和字串求值(並格式化)成 str 型別的方法。

f-string 可以用於所有種類的引號字串,例如單行(' 或 ")和多行(''' 或 """)。 放在 {} 內的任何變數、運算式或其他型別,都會先求值,接著轉換成 str,最後再與大括號外的任何 str 串接起來。

在下面這個例子中,我們把兩個變數的值插入一個句子(一個 str 和一個 float):

>>> name = 'eighth'
>>> value = 1/8

# The f-string, using the two values.
# The .2f format code truncates, so the value displays as 0.12.
>>> f'An {name} is approximately {value:.2f}.'
'An eighth is approximately 0.12.'

可以求值的運算式幾乎可以是任何東西。 可以求值的可能性(範圍很廣)包括:str、數字、變數、算術運算式、條件運算式、內建型別、切片、函式、lambda、推導式,或是任何定義了 __str__ 或 __repr__ 方法的物件。

從簡單到複雜:

插入變數。f-string 最簡單的用法,就是把變數直接放進字串裡。

# Assigning a variable.
>>> name = "World"

# Inserting that variable.
>>> f'Hello, {name}!'
'Hello, World!'

在 {} 內使用運算式。任何合法的 Python 運算式都可以在大括號內求值。 請注意,在單引號的 f-string 內使用雙引號(或反過來),就能避免使用跳脫序列:

# A dictionary of key:value pairs.
>>> waves = {'water': 1, 'light': 3, 'sound': 5}

# Inserting the whole dict.
>>> f'Wave ranks: {waves}'
"Wave ranks: {'water': 1, 'light': 3, 'sound': 5}"

# An expression can be evaluated inline:
>>> f"Tenfold the value of 'light' is {waves['light'] * 10}."
"Tenfold the value of 'light' is 30."

# A method call can also be evaluated inline:
>>> f'{"hello world!".title()} is a classic greeting.'
'Hello World! is a classic greeting.'

# An f-string can be nested inside another f-string:
>>> f"{f'hello world!'.title()} is a classic greeting."
'Hello World! is a classic greeting.'

輸出格式化。你可以用格式規格迷你語言來控制對齊、數值精度等等。 格式規格放在值之後,並以 : 分隔。

# Right-align a value to ten characters, and round it to 3 decimal places.
>>> value = 1 / 7
>>> f'One seventh is {value:10.3f}.'
'One seventh is      0.143.'

# A format specification can be set using variables as well.
>>> padding = 10
>>> precision = 3
>>> f'One seventh is {value:{padding}.{precision}f}.'
'One seventh is      0.143.'

全部組合起來。變數、運算式、函式呼叫,以及輸出格式化:

>>> precision = 3
>>> f"{30e8 * 111_000:6.{precision}e}"
'3.330e+14'

>>> verb = 'meet'
>>> the_end = ['end', 'of', 'transmission']
>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {30e8 * 111_000:6.{precision}e} light-years."{the_end}'
'"Have a nice day, I will meet you after 3.330e+14 light-years."[\'end\', \'of\', \'transmission\']'

# Did you notice the escaped single-quotes in the previous example?
# Using double quotes instead of single quotes for the f-string means the list's single-quoted strings print cleanly.
>>> f"Have a nice day. {the_end}"
"Have a nice day. ['end', 'of', 'transmission']"

有兩個主要的限制需要注意。 f-string 的運算式不能是空的。 此外,在 Python 3.12 之前,它們不能包含註解。

>>> f"An empty expression will error: {}"
SyntaxError: f-string: empty expression not allowed

>>> word = 'word'
>>> f"""A comment in a triple quoted f-string: {
    word # I chose a nice variable
}"""
'A comment in a triple quoted f-string: word'
Caution

字串插值無法與用於國際化(I18N)和在地化(L10N)的 GNU gettext API 一起使用,因此在任何「字串包裝」的翻譯情境中,建議改用下面介紹的 string.Template 類別或 str.format() 方法,而不要使用 f-string。

另外請記住,在 f-string 的括號 {} 內使用運算式,類似於使用 eval() 或 exec(),所以並不是很安全,而且絕對不要用在使用者輸入上。

str.format() 方法

str.format() 方法會把字串中的預留位置替換成當作引數傳入函式的值。 預留位置可以用名稱({price})、數字({0} 或索引)來識別,甚至可以是空的(位置)預留位置 {}。

例如:

# A named placeholder and a positional placeholder.
>>> 'My text: {placeholder_1} and {}.'.format(12, placeholder_1='named placeholder')
'My text: named placeholder and 12.'

和 f-string 一樣,Python 的 str.format() 支援各式各樣的迷你語言格式指定符,可以用來對齊文字、截斷浮點數等等。

完整的格式指定符模式是 {[<name>][!<conversion>][:<format_specifier>]}:

  • <name> 可以是具名預留位置、數字,或留空。
  • !<conversion> 是選用的,應為下列三種轉換之一:!s 對應 str(),!r 對應 repr(),!a 對應 ascii()。 預設會使用 str()。
  • :<format_specifier> 是選用的,用來控制值的顯示方式。 關於可能的選項,更多資訊可以在這裡找到。

以下是帶變音符號字母的轉換範例:

# Fills in the object at index zero, converted to a string.
>>> 'An e with an umlaut: {0!s}'.format('ë')
'An e with an umlaut: ë'


# Fills in the object at index zero, converted to a repr.
>>> 'An e with an umlaut (object representation): {0!r}'.format('ë')
"An e with an umlaut (object representation): 'ë'"


# Fills in the object at index zero, converted to ASCII.
>>> 'An e with an umlaut converted into ASCII: {0!a}'.format('ë')
"An e with an umlaut converted into ASCII: '\xeb'"


# Fills in the object in the first position,
# then fills in the object in the second position formatted as a repr.
>>> 'She said her name is not {} but {!r}.'.format('Chloe', 'Zoë')
"She said her name is not Chloe but 'Zoë'."

常見格式指定符的範例:

# Integer and binary/hex representations of the same number.
>>> my_num = 42
>>> f"{my_num} in binary is {my_num:b}. In hex, it is {my_num:x}"
"42 in binary is 101010. In hex, it is 2a"

# Alignment: left (<), right (>), and center (^) using up to ten characters total.
>>> f"[{"left":<10}] [{"right":>10}] [{"center":^10}]"
"[left      ] [     right] [  center  ]"

# Float precision and scientific notation (up to three decimal places).
>>> pi = 3.141592653589793
>>> f"Fixed: {pi:.3}  Scientific: {pi:.3e}"
"Fixed: 3.142  Scientific: 3.142e+00"

# Thousands separator and percentage.
>>> balance = 1000
>>> rate = 0.0225
>>> f"Balance: ${balance:,.0f}  Interest rate: {rate:.1%}"
"Balance: $1,000  Interest rate: 2.2%"

# Putting it all together:
>>> items = [("Widget", 1250, 9.991), ("Gadget", 37, 24.503), ("Doohickey", 4, 149.002)]
>>> header = f"{"Item":<12} {"Qty":>6} {"Price":>9}"
>>> print(header)
Item            Qty     Price
>>> for name, qty, price in items:
...     print(f"{name:<12} {qty:>6} {price:>9.2f}")
Widget         1250      9.99
Gadget           37     24.50
Doohickey         4    149.00

更多範例可以在這份文件的結尾看到。

% 格式化,或 printf() 風格的格式化

使用 % 運算子來格式化,是 Python 中最古老的字串格式化方法。 它源自 C 語言,允許使用位置引數來建立 str。

這個方法已被 f-string 和 str.format() 取代,這也是 % 格式化的暱稱是「舊式」的原因。 在 Python 2 或舊版程式碼中仍然可以看到它。 雖然這種方法在 Python 3.x 中仍能運作,但 % 格式化通常會被避免使用,因為它容易出錯、效率較低、可用選項較少,而且預留位置和引數只要對不上就會引發例外。 使用 % 運算子類似於 printf(),所以有時也稱為 printf 格式化。

# Assigning a variable.
>>> name = "Anna-conda"

# Building a string using %.
>>> "The snake's name is %s." % name
"The snake's name is Anna-conda."

在上面的例子中,% 運算子會在執行時把預留位置 %s 替換成變數 name。 如果想在字串中加入多個變數,你需要在 % 後面提供一個元組,裡面每個預留位置各對應一個物件:

# Assigning variables.
>>> name = "Billy the Kid"
>>> fruit = "grapes"

# Building a string using %.
>>> "Surprisingly, %s's favorite snack was %s." %(name, fruit)
"Surprisingly, Billy the Kid's favorite snack was grapes."

範本字串

string.Template()(別和 Python 3.14 的 t-strings 搞混)是 string 模組(有別於內建的 str 型別)中的一個類別,它屬於 Python 標準函式庫的一部分,但使用前必須先匯入。 範本字串支援以 $ 為基礎的替換,而且比這裡提到的其他選項簡單許多,功能也少得多。 不過,對於複雜的國際化或清理外部輸入,它們可能非常有用。

string.Template 被認為對不受信任的使用者輸入較為安全,因為它會防止求值任意運算式或存取物件屬性,藉此減輕格式字串注入攻擊。

>>> from string import Template

>>> name = "Anna-Conda"

# Creating a Template with placeholder text.
>>> template_string = Template("The snake called `$snake_name` has escaped!")

# Calling .substitute() to replace the placeholder with a value.
>>> template_string.substitute(snake_name=name)
'The snake called `Anna-Conda` has escaped!'

關於 string.Template 的更多資訊,可以在 Python 的文件中找到。

該如何選擇要用哪種格式化方法?

面對這麼多選項和迷你語言,格式化 Python 字串時,你該如何決定要用哪一個? 這裡有幾個簡單的原則:

  1. f-strings 最新,也最容易閱讀。 如果你不需要國際化,它們應該是 Python 3.6 以上版本的首選方法。

  2. str.format() 用途廣泛、功能強大,並且與 gnu gettext 和大多數 Python 版本相容。

  3. 如果你需要的是簡單、安全,或大量的國際化,string.Template() 可以用來在必須處理使用者輸入時降低風險,也適合用來包裝翻譯字串。

  4. 對新程式碼來說,% 運算子一般被視為已過時,不過它在現代 Python 中仍然可用。 它主要應該用於和較舊的程式碼庫相容。 % 格式化比其他方法更容易出錯(功能也較少),而且可能導致顯示非 ASCII 字元的問題。 如果你打算使用它,請查閱你使用的 Python 版本,了解支援的詳細情況。

如果你想更深入探索,關於格式化的一切 和 Python 字串格式化最佳實務 都是不錯的起點。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟