Python 四種字符串格式化方式

Python 四種字符串格式化方式

格式化(formatting)是指把數據填寫到預先定義的文本模板里面,形成一條用戶可讀的消息,并把這條消息保存成字符串的過程。

% 格式化

Python 里面最常用的字符串格式化方式是采用 % 格式化操作符。

這個操作符左邊的文本模板叫作格式字符串(format string),我們可以在操作符右邊寫上某個值或者由多個值所構成的元組(tuple),用來替換格式字符串里的相關符號。

例如,下面這段代碼通過 % 操作符把難以閱讀的二進制和十六進制數值,顯示成十進制的形式。

a = 0b10111011
b = 0xc5f
print('Binary is %d, hex is %d' % (a, b))
# >>>
# Binary is 187, hex is 3167

格式字符串里面可以出現 %d 這樣的格式說明符,這些說明符的意思是,% 右邊的對應數值會以這樣的格式來替換這一部分內容。格式說明符的寫法來自 C 語言的 printf 函數,所以,常見的 printf 選項都可以當成 Python 的格式說明符來用,例如 %s、%x、%f 等,此外還可以控制小數點的位值,并指定填充與對齊方式。

但是,C 風格的格式字符串,在 Python 里有四個缺點。

第一個缺點是,如果 % 右側那個元組里面的值在類型或順序上有變化,那么程序可能會因為轉換類型時發生不兼容問題而出現錯誤。例如一個簡單的例子:

key = 'my_var'
value = 1.234
formatted = '%-10s = %.2f' % (key, value)
print(formatted)
# >>>
# my_var     = 1.23

但如果把 key 跟 value 互換位置,那么程序就會在運行時出現異常。

reordered_tuple = '%-10s = %.2f' % (value, key)
# >>>
# TypeError: must be real number, not str

如果 % 右側的寫法不變,但左側那個格式字符串里面的兩個說明符對調了順序,那么程序同樣會發生這個錯誤。

reordered_string = '%.2f = %-10s' % (key, value)
# >>>
# TypeError: must be real number, not str

要想避免這種問題,必須經常檢查 % 操作符左右兩側的寫法是否相互兼容。

第二個缺點是,在填充模板之前,經常要先對準備填寫進去的這個值稍微做一些處理,但這樣一來,整個表達式可能就會寫得很長,讓人覺得比較混亂。

下面這段代碼用來羅列廚房里的各種食材,現在的這種寫法并沒有對填入格式字符串里面的那三個值(也就是食材的編號 i、食材的名稱 item,以及食材的數量 count)預先做出調整。

pantry = [('avocados', 1.25),('bananas', 2.5),('cherries', 15),
]
for i, (item, count) in enumerate(pantry):print('#%d: %-10s = %.2f' % (i, item, count))
# >>>
# #0: avocados   = 1.25
# #1: bananas    = 2.50
# #2: cherries   = 15.00

如果想讓打印出來的信息更好懂,那可能得把這幾個值稍微調整一下,但是調整之后,% 操作符右側的那個三元組就特別長,所以需要多行拆分才能寫得下,這會影響程序的可讀性。

for i, (item, count) in enumerate(pantry):print('#%d: %-10s = %d' % (i + 1,item.title(),round(count)))
# >>>
# #1: Avocados   = 1
# #2: Bananas    = 2
# #3: Cherries   = 15 

第三個缺點是,如果想用同一個值來填充格式字符串里的多個位置,那么必須在 % 操作符右側的元組中相應地多次重復該值。

template = '%s loves food. See %s cook.'
name = 'Max'
formatted = template % (name, name)
print(formatted)
# >>>
# Max loves food. See Max cook.

如果想在填充之前把這個值修改一下,那么必須同時修改多處才行,例如,如果這次要填的不是 name 而是 name.title(),那就必須提醒自己,要把所有的 name 都改成 name.title()。若是有的地方改了,有的地方沒改,那輸出的信息可能就不一致了。

template = '%s loves food. See %s cook.'
name = 'brad'
formatted = template % (name.title(), name.title())
print(formatted)
# >>>
# Brad loves food. See Brad cook.

為了解決上面提到的一些問題,Python 的 % 操作符允許我們用 dict 取代 tuple,這樣的話,我們就可以讓格式字符串里面的說明符與 dict 里面的鍵以相應的名稱對應起來,例如 %(key)s 這個說明符,意思就是用字符串(s)來表示 dict 里面名為 key 的那個鍵所保存的值。下面通過這種辦法解決剛才講的第一個缺點,也就是 % 操作符兩側的順序不匹配問題。

key = 'my_var'
value = 1.234
old_way = '%-10s = %.2f' % (key, value)
new_way = '%(key)-10s = %(value).2f' % {'key': key, 'value': value}  # Original
reordered = '%(key)-10s = %(value).2f' % {'value': value, 'key': key}  # Swapped
assert old_way == new_way == reordered

這種寫法還可以解決剛才講的第三個缺點,也就是用同一個值替換多個格式說明符的問題。改用這種寫法之后,我們就不用在 % 操作符右側重復這個值了。

name = 'Max'
template = '%s loves food. See %s cook.'
before = template % (name, name)  # Tuple
template = '%(name)s loves food. See %(name)s cook.'
after = template % {'name': name}  # Dictionary
assert before == after

但是,這種寫法會讓剛才講的第二個缺點變得更加嚴重,因為字典格式字符串的引入,我們必須給每一個值都定義鍵名,而且要在鍵名的右側加冒號,格式化表達式變得更加冗長,看起來也更加混亂。把不采用 dict 的寫法與采用 dict 的寫法對比一下,可以更明確地意識到這種寫法的缺點。

for i, (item, count) in enumerate(pantry):before = '#%d: %-10s = %d' % (i + 1, item.title(), round(count))after = '#%(loop)d: %(item)-10s = %(count)d' % {'loop': i + 1, 'item': item.title(), 'count': round(count)}assert before == after

第四個缺點是,把 dict 寫到格式化表達式里面會讓代碼變多。每個鍵都至少要寫兩次:一次是在格式說明符中,還有一次是在字典中作為鍵,另外,定義字典的時候,可能還要專門用一個變量來表示這個鍵所對應的值,而且這個變量的名稱或許也和鍵名相同,這樣算下來就是三次了。

soup = 'lentil'
formatted = 'Today\'s soup is %(soup)s.' % {'soup': soup}
print(formatted)
# >>>
# Today's soup is 1enti1.

除了要反復寫鍵名,在格式化表達式里面使用 dict 的辦法還會讓表達式變得特別長,通常必須拆分為多行來寫,同時,為了與格式字符串的多行寫法相對應,定義字典的時候,也要一行一行地給每個鍵設定對應的值。

內置的 format 函數與 str 類的 format 方法

Python3 添加了高級字符串格式化(advanced string formatting)機制,它的表達能力比老式 C 風格的格式字符串要強,且不再使用 % 操作符。

下面這段代碼,演示了這種新的格式化方式。在傳給 format 函數的格式里面,逗號表示顯示千位分隔符,^ 表示居中對齊。

a = 1234.5678
formatted = format(a, ',.2f')
print(formatted)b = 'my string'
formatted = format(b, '^20s')
print('*', formatted, '*')
# >>>
# 1,234.57
# *      my string       *

如果 str 類型的字符串里面有許多值都需要調整格式,則可以調用 str 的新 format 方法。該方法不使用 %d 這樣的 C 風格格式說明符。而是把格式有待調整的那些位置在字符串里面先用 {} 代替,然后按從左到右的順序,把需要填寫到那些位置的值傳給 format 方法,使這些值依次出現在字符串中的相應位置。

key ='my_var'
value = 1.234
formatted = '{} = {}'.format(key, value)
print(formatted)
# >>>
# my_var = 1.234

可以在 {} 里寫個冒號,然后把格式說明符寫在冒號的右邊,用以規定 format 方法所接收的這個值應該按照怎樣的格式來調整。

formatted = '{:<10} = {:.2f}'.format(key, value)
print(formatted)
# >>>
# my_var     = 1.23

這種寫法的效果可以這樣理解:系統先把 str.format 方法接收到的每個值傳給內置的 format 函數,并找到這個值在字符串里對應的 {},同時將 {} 里面寫的格式也傳給 format 函數,例如系統在處理 value 的時候,傳的就是 format(value,‘.2f’)。然后,系統會把 format 函數所返回的結果寫在整個格式化字符串 {} 所在的位置。另外,每個類都可以通過 __format__ 這個特殊的方法定制相應的邏輯,這樣的話,format 函數在把該類實例轉換成字符串時,就會按照這種邏輯來轉換。

C 風格的格式字符串采用 % 操作符來引導格式說明符,所以如果要將這個符號照原樣輸出,那就必須轉義,也就是連寫兩個 %。同理,在調用 str.format 的時候,如果想把 str 里面的 {、} 照原樣輸出,那么也得轉義。

print('%.2f%%' % 12.5)
print('{} replaces {{}}'.format(1.23))
# >>>
# 12.50%
# 1.23 replaces {}

調用 str.format 方法的時候,也可以給 str 的 {} 里面寫上數字,用來指代 format 方法在這個位置所接收到的參數值位置索引。以后即使這些 {} 在格式字符串中的次序有所變動,也不用調換傳給 format 方法的那些參數。于是,這就避免了前面講的第一個缺點所提到的那個順序問題。

formatted = '{1} = {0}'.format(key, value)
print(formatted)
# >>>
# 1.234 = my_var

同一個位置索引可以出現在 str 的多個 {} 里面,這些{}指代的都是 format 方法在對應位置所收到的值。這就不需要把這個值重復地傳給 format 方法,于是就解決了前面提到的第三個缺點。

name = 'Max'
formatted = '{0} loves food. See {0} cook.'.format(name)
print(formatted)
# >>>
# Max loves food. See Max cook.

然而,這個新的 str.format 方法并沒有解決上面講的第二個缺點。如果在對值做填充之前要先對這個值做出調整,那么用這種方法寫出來的代碼還是跟原來一樣亂,閱讀性差。

當然,這種 {} 形式的說明符,還支持一些比較高級的用法,例如可以查詢 dict 中某個鍵的值,可以訪問 list 里某個位置的元素,還可以把值轉化成 Unicode 或 repr 字符串。下面這段代碼把這三項特性結合了起來。

menu = {'soup': 'lentil','oyster': 'kumamoto','special': 'schnitzel',
}
formatted = 'First letter is {menu[oyster][0]!r}'.format(menu=menu)
print(formatted)
# >>>
# First letter is 'k'

但是這些特性,依然不能解決前面提到的第四個缺點,也就是鍵名需要多次重復的那個問題。所以并不推薦大家用 str.format 方法。當然,還是必須掌握新的格式說明符所使用的這套迷你語言(mini language),可以在 str 的 {} 里面按照這套迷你語言的規則來指定冒號右側的格式。系統內置的 format 函數也會用到這套規則。

插值格式字符串

Python 3.6 添加了一種新的特性,叫作插值格式字符串(interpolated format string,簡稱 f-string),可以解決上面提到的所有問題。新語法特性要求在格式字符串的前面加字母 f 作為前綴,這跟字母 b 與字母 r 的用法類似。

f-string 把格式字符串的表達能力發揮到了極致,它徹底解決了上文提到的第四個缺點,也就是鍵名重復導致的程序冗余問題。可以直接在 f-string 的 {} 里面引用當前 Python 范圍內的所有名稱,進而達到簡化的目的。

key = 'my_var'
value = 1.234
formatted = f'{key} = {value}'
print(formatted)
# >>>
# my_var = 1.234

str.format 方法所支持的那套迷你語言,也就是在 {} 內的冒號右側所采用的那套規則,現在也可以用到 f-string 里面,而且還可以像早前使用 str.format 時那樣,通過 ! 符號把值轉化成 Unicode 及 repr 形式的字符串。

formatted = f'{key!r:<10} = {value:.2f}'
print(formatted)
# >>>
# 'my_var' = 1.23

同一個問題,使用 f-string 來解決總是比通過 % 操作符使用 C 風格的格式字符串簡單,而且也比 str.format 方法簡單。

f_string = f'{key:<10} = {value:.2f}'
c_tuple = '%-10s = %.2f' % (key, value)
str_args = '{:<10} = {:.2f}'.format(key, value)
str_kw = '{key:<10} = {value:.2f}'.format(key=key, value=value)
c_dict = '%(key)-10s = %(value).2f' % {'key': key, 'value': value}
assert c_tuple == c_dict == f_string
assert str_args == str_kw == f_string

在 f-string 方法中,各種 Python 表達式都可以出現在 {} 里,于是這就解決了前面提到的第二個缺點。我們現在可以用相當簡潔的寫法對需要填充到字符串里面的值做出微調。

pantry = [('avocados', 1.25),('bananas', 2.5),('cherries', 15),
]
for i, (item, count) in enumerate(pantry):old_style = '#%d: %-10s = %d'% (i + 1,item.title(),round(count))new_style = '#{}: {:<10s} = {}'.format(i + 1,item.title(),round(count))f_string = f'#{i+1}: {item.title():<10s} = {round(count)}'assert old_style == new_style == f_string

要是想表達得更清楚一些,可以把 f-string 寫成多行的形式,類似于 C 語言的相鄰字符串拼接(adjacent-string concatenation)。

pantry = [('avocados', 1.25),('bananas', 2.5),('cherries', 15),
]
for i, (item, count) in enumerate(pantry):print(f'#{i+1}: 'f'{item.title():<10s} = 'f'{round(count)}')
# >>>
# #1: Avocados   = 1
# #2: Bananas    = 2
# #3: Cherries   = 15

Python 表達式也可以出現在格式說明符中。例如,下面的代碼把小數點之后的位數用變量來表示,然后把這個變量的名字 places 用 {} 括起來放到格式說明符中,這樣寫比采用硬代碼更靈活。

places = 3
number = 1.23456
print(f'My number is {number:.{places}f}')
# >>>
# My number is 1.235

在 Python 內置的四種字符串格式化辦法里面,f-string 可以簡潔而清晰地表達出許多種邏輯,這使它成為程序員的最佳選擇。如果你想把值以適當的格式填充到字符串里面,那么首先應該考慮的就是采用 f-string 來實現。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/43948.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/43948.shtml
英文地址,請注明出處:http://en.pswp.cn/web/43948.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

4000字|手把手教你:從0到1搭建跨境電商生意

有小伙伴問我能不能系統的聊下跨境電商的運作思路&#xff0c;因為過去的文章基本都是逐塊的在分享各種心得&#xff0c;對于一些想要系統學習跨境電商的朋友來說有點晦澀難懂&#xff0c;剛好趕上羊羊羊&#xff0c;索性花點時間來認真聊聊這個。 在開始聊這個話題之前&#…

探索數據的奧秘:sklearn中的聚類分析技術

探索數據的奧秘&#xff1a;sklearn中的聚類分析技術 在數據科學領域&#xff0c;聚類分析是一種無監督學習方法&#xff0c;它的目標是將數據集中的樣本劃分為多個組或“簇”&#xff0c;使得同一組內的樣本相似度高&#xff0c;而不同組間的樣本相似度低。scikit-learn&…

qdma enable jtag debugger

ip上的m_axi_lite 是連接到qdma_v4_0_11_dma5_rtl_top這個ip的 和jtag debugger沒有關系 qdma enable jtag debugger 讀取的是ip內部reg ///home/nic626/smart_nic/build_dir/qdma_no_sriov_ex/qdma_no_sriov_ex.srcs/sources_1/ip/qdma_no_sriov.xcix!/qdma_no_sriov/ip_0/so…

SQL基礎-DQL 小結

SQL基礎-DQL 小結 學習目標&#xff1a;學習內容&#xff1a;SELECTFROMWHEREGROUP BYHAVINGORDER BY運算符ASC 和 DESC 總結 學習目標&#xff1a; 1.理解DQL&#xff08;Data Query Language&#xff09;的基本概念和作用。 2.掌握SQL查詢的基本語法結構&#xff0c;包括SEL…

Linux文件編程(標準C庫)

目錄 一、標準C庫打開/創建文件&#xff0c;讀寫文件&#xff0c;光標移動 二、標準C庫寫入結構體到文件 三、其他函數補充 1.fputc函數 2.feof函數和fgetc函數 前面講到的open函數都是基于linux內核的&#xff0c;也就是說在Windows系統上無法運行&#xff0c;移植性比較…

使用simulink進行esp32開發,進行串口收發數據需要注意的地方,為什么收發不成功

1&#xff0c;主要是因為simulink里的配置文件配置的波特率和串口接受軟件配置的波特不一致導致的 2&#xff0c;主要有以下三個界面 a.配置文件 b.模型 模型直接選擇使用的是那組串口就行了&#xff0c;一般情況下我們收發使用同一組就可以&#xff0c;這樣收發模塊填寫的端…

20240711編譯友善之臂的NanoPC-T6開發板的Buildroot

20240711編譯友善之臂的NanoPC-T6開發板的Buildroot 2024/7/11 21:02 百度&#xff1a;nanopc t6 wiki https://wiki.friendlyelec.com/wiki/index.php/NanoPC-T6/zh NanoPC-T6/zh 4.4 安裝系統 4.4.1 下載固件 4.4.1.1 官方固件 訪問此處的下載地址下載固件文件 (位于網盤的&q…

社交媒體中智能品牌視覺識別系統的設計與應用

社交媒體中智能品牌視覺識別系統的設計與應用開題報告與任務書分析 一、引言 隨著社交媒體的蓬勃發展,品牌與消費者之間的互動日益頻繁,品牌視覺識別系統作為品牌傳播的重要載體,其設計與應用顯得尤為重要。特別是在智能技術的推動下,如何設計并應用智能品牌視覺識別系統…

【Python】已解決:ModuleNotFoundError: No module named ‘sklearn.cross_validation

文章目錄 一、問題背景二、可能出錯的原因三、錯誤代碼示例四、正確代碼示例五、注意事項 &#xff08;機器學習分割數據問題&#xff09;解決“ModuleNotFoundError: No module named ‘sklearn.cross_validation’” 一、問題背景 在機器學習的實踐中&#xff0c;數據分割是…

CSS技巧專欄:一日一例 4.純CSS實現兩款流光溢彩的酷炫按鈕特效

大家好&#xff0c;今天是 CSS技巧專欄&#xff1a;一日一例 第三篇《純CSS實現兩款流光溢彩的酷炫按鈕特效》 先看圖&#xff1a; 特此說明&#xff1a; 本專題專注于講解如何使用CSS制作按鈕特效。前置的準備工作和按鈕的基本樣式&#xff0c;都在本專欄第一篇文章中又詳細…

Python基礎教學之四:面向對象編程——邁向更高級編程

Python基礎教學之四&#xff1a;面向對象編程——邁向更高級編程 一、面向對象編程概念 1. 類和對象 定義&#xff1a;在面向對象編程(OOP)中&#xff0c;類是創建對象的模板&#xff0c;它定義了對象的屬性和方法。對象是類的實例&#xff0c;具體存在的實體&#xff0c;擁有…

socket編程(1)

socket編程 1. 預備知識點1.1 網絡字節序1.2 ip地址轉換函數1.3 sockaddr數據結構 最后 1. 預備知識點 1.1 網絡字節序 多字節數據有大端和小端之分&#xff0c;網絡數據流采用大端字節序&#xff0c;如果主機采用的是小端字節序&#xff0c;那么需要轉換。 大端&#xff1a…

ffmpeg和imagemagick制作gif動圖

from: https://blog.csdn.net/hufang360/article/details/107291163?ops_request_misc%257B%2522request%255Fid%2522%253A%2522167876076516800186587476%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id167876076516800186587476&biz_i…

【JavaScript 報錯】未捕獲的范圍錯誤:Uncaught RangeError

&#x1f525; 個人主頁&#xff1a;空白詩 文章目錄 一、錯誤原因分析1. 遞歸調用次數過多2. 數組長度超出限制3. 數值超出允許范圍 二、解決方案1. 限制遞歸深度2. 控制數組長度3. 檢查數值范圍 三、實例講解四、總結 Uncaught RangeError 是JavaScript中常見的一種錯誤&…

347. 前 K 個高頻元素(中等)

347. 前 K 個高頻元素 1. 題目描述2.詳細題解3.代碼實現3.1 Python3.2 Java 1. 題目描述 題目中轉&#xff1a;347. 前 K 個高頻元素 2.詳細題解 尋找出現頻率前 k k k高的元素&#xff0c;因此需要先統計各個元素出現的次數&#xff0c;該步驟時間復雜度為 O ( n ) O(n) O(n)…

柔性接觸力學及其建模仿真方法

柔性接觸力學是研究柔性體&#xff08;如柔性機器人、柔性結構等&#xff09;在接觸過程中產生的力學效應和相互作用的學科。它涉及到接觸力的計算、接觸變形的分析以及接觸過程中的能量轉換等多個方面。由于柔性體具有變形能力&#xff0c;其接觸過程往往比剛性體接觸更為復雜…

Transformer學習過程中常見的問題與解決方案 - Transformer教程

在機器學習領域&#xff0c;Transformer模型已經成為了處理自然語言處理&#xff08;NLP&#xff09;任務的主流工具。然而&#xff0c;在學習和使用Transformer的過程中&#xff0c;很多人會遇到各種各樣的問題。今天我們就來聊一聊Transformer學習過程中常見的問題以及對應的…

C++模板總結

文章目錄 寫在前面1. 函數模板1.1 函數模板的概念1.2 函數模板的原理1.3 函數模板的實例化1.4 函數模板的實例化模板參數的匹配原則 2. 類模板3. 非類型模板參數4. 模板的特化4.1 概念4.2 函數模板特化4.3 類模板特化 5. 模板分離編譯6. 總結 寫在前面 進入C以后&#xff0c;C…

智能小車——初步想法

需要參考輪趣的智能小車自己搭建一臺智能機器人&#xff0c;這里從底層控制開始逐步搭建。 控制模式 之后要自行搭建智能小車&#xff0c;所以將輪趣的底盤代碼進行學習&#xff0c;根據開發手冊先大致過一遍需要的內容。 有做很多個控制方法&#xff0c;包括了手柄、串口、…

MySQL中的JOIN、LEFT JOIN、RIGHT JOIN講解

在 MySQL 中&#xff0c;JOIN 是一種非常強大的功能&#xff0c;它允許你將兩個或多個表中的行結合起來&#xff0c;基于兩個表之間的共同字段。這種操作在數據庫查詢中非常常見&#xff0c;特別是在處理關系型數據庫時。下面我將分別解釋 JOIN、LEFT JOIN&#xff08;也稱為 L…