本文為譯文,原文 by Joanna Jablonski,地址:https://realpython.com/python-f-strings/
Python 3.6 引入了新的字符串格式化方式 f-strings,與其它格式化方式相比,不僅簡潔明了,可讀性更好,更不容易出錯,而且運(yùn)行效率也更高。
你應(yīng)該馬上開始使用這種新的格式化方式,本文將解釋其原因與具體用法。
但在此之前,讓我們先看一下 f-strings 之前的字符串格式化方式。
在 Python 3.6 之前,把 Python 表達(dá)式嵌入字符串,主要有兩種方式:% 或 str.format()。它們的用法與主要不足如下:
這是最原始的、 Python 官方指南所用的格式化方式,相關(guān)信息可以閱讀官方文檔。
值得注意的是,官方文檔其實(shí)并不推薦使用這種方式,相關(guān)說明如下:
“此處描述的格式化操作存在一些奇怪之處,容易導(dǎo)致一些常見錯誤(比如無法正確格式化元組與字典等)。
使用新的格式化方式或str.format()函數(shù)可以避免這些錯誤。這些格式化方式不僅更強(qiáng)大,而且更靈活,更易于擴(kuò)展。(來源)
如何使用 % 進(jìn)行格式化
字符串對象有一個內(nèi)建操作符%,可以用于格式化操作,具體用法如下:
如果要在字符串中嵌入多個變量,則必須使用元組,例如:
>>> name = 'Eric'>>> age = 74 >>> 'Hello, %s. You are %s.' % (name, age) 'Hello Eric. You are 74.'
% 格式化的問題
上面的代碼看起來還不錯,但如果在更長的字符串中嵌入更多變量,代碼的可讀性就會成為一個問題:
因此,這種格式化方式的主要問題是過于繁瑣,容易出錯,不能正確格式化元組與字典。
這種方式是在 Python 2.6 引入的,可以在 官方文檔 找到相關(guān)介紹。
str.format() 的用法
str.format()方式是對 % 方式的一次改進(jìn)。用戶可以通過修改對象的 __format__() 方法來自定義 格式化方式 。
通過大括號,我們可以在字符串中嵌入變量:
>>> 'Hello, {}. You are {}.'.format(name, age) 'Hello, Eric. You are 74.'
也可以指定變量的引用順序:
或者直接使用變量名稱:
>>> person = {'name': 'Eric', 'age': 74} >>> 'Hello, {name}. You are {age}.'.format(name=person['name'], age=person['age']) 'Hello, Eric. You are 74.'
在引用字典時,可以用**操作符進(jìn)行字典拆包:
總的來說,str.format()是 % 格式化方式的一次升級,但依然存在一些問題。
str.format() 的問題
和 % 方式相比,使用str.format() 的代碼可讀性要好很多。但如果在較長的字符串中嵌入多個變量,依然會顯得繁瑣:
>>> first_name = 'Eric' >>> last_name = 'Idle' >>> age = 74 >>> profession = 'comedian' >>> affiliation = 'Monty Python' >>> print(('Hello, {first_name} {last_name}. You are {age}. ' + >>> 'You are a {profession}. You were a member of {affiliation}.') \ >>> .format(first_name=first_name, last_name=last_name, age=age, \ >>> profession=profession, affiliation=affiliation)) 'Hello, Eric Idle. You are 74. You are a comedian. You were a member of Monty Python.'
當(dāng)然,如果我們提前把需要嵌入的變量放在一個字典中,就可以通過 ** 進(jìn)行拆包操作,從而顯得簡潔一些。
但一定還存在著更好的格式化方式。
Python 3.6 引入了新的字符串格式化方式,這種方式來自于 Eric V. Smith 在 2015 年 8 月提出的方案,具體可以參考PEP 498。
f-strings 也稱作“格式化的字符串字面量”,它是一個帶有 f 前綴的字符串,通過大括號嵌入所需的 Python 表達(dá)式,這些表達(dá)式的具體值是在運(yùn)行時確定的,背后依賴的也是嵌入對象的 __format()__ 接口。查看 官方文檔 可以獲得更多信息。
以下是一些具體使用方式:
f-strings 的句法類似于str.format(),但要更簡潔,你可以感受一下它的可讀性:
前綴f也可以使用大寫的F。
>>> F'Hello, {name}. You are {age}.' 'Hello, Eric. You are 74.'
你是不是已經(jīng)開始喜歡上這種格式化方式了?
由于 f-strings 是在運(yùn)行時計(jì)算具體值的,我們得以在字符串中嵌入任意有效的 Python 表達(dá)式,從而寫出更優(yōu)雅的代碼。
你可以使用很直接的計(jì)算式,比如說:
也可以在里面調(diào)用函數(shù):
>>> def to_lowercase(input): ... return input.lower() >>> name = 'Eric Idle' >>> f'{to_lowercase(name)} is funny.' 'eric idle is funny.'
或者直接調(diào)用對象的方法:
甚至可以在對象的字符串方法中直接使用 f-strings,例如有以下類:
class Comedian: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def __str__(self): return f'{self.first_name} {self.last_name} is {self.age}.' def __repr__(self): return f'{self.first_name} {self.last_name} is {self.age}. Surprise!'
你可以有如下代碼:
__str__()方法與__repr__()方法用于處理對象的字符串顯示方式,我們有必要至少定義其中一個。如果必須二選一的話,建議使用__repr__(),在__str__()方法沒有定義的情況下,解釋器會自動調(diào)用__repr__()方法。
__str__()方法返回的是對象的非正式字符串表示,主要考慮可讀性,而__repr__()方法返回的是對象的正式字符串表示,主要考慮精確性。調(diào)用這兩個函數(shù)時,比較推薦的方式是直接使用內(nèi)置函數(shù)str()和repr()。
f-strings 會默認(rèn)調(diào)用對象的__str__()方法,如果要強(qiáng)制使用__repr__()方法,則可以在變量之后加上轉(zhuǎn)換標(biāo)志!r:
>>> f'{new_comedian}''Eric Idle is 74.' >>> f'{new_comedian!r}' 'Eric Idle is 74. Surprise!'
想要了解更多 f-strings 的轉(zhuǎn)換問題,可以閱讀 相關(guān)資料。
你可以這樣定義一個多行字符串:
要注意的是,在每一行字符串之前,都要加上f前綴。以下代碼是無效的:
>>> message = (... f'Hi {name}. '... 'You are a {profession}. '... 'You were in {affiliation}.'... )>>> message'Hi Eric. You are a {profession}. You were in {affiliation}.'
如果有需要,也可以通過反斜線 \ 拼接多行字符串:
但在使用'''時,則可能出現(xiàn)以下情況:
>>> message = f'''... Hi {name}.... You are a {profession}. ... You were in {affiliation}.... '''...>>> message'\n Hi Eric.\n You are a comedian.\n You were in Monty Python.\n'
關(guān)于代碼中的縮進(jìn)問題,可以參考PEP 8。
f-string 中的f前綴也可以是 “fast” 的意思。
f-strings 比 % 和 str.format()格式化都要快。正如之前所說,f-strings 是在運(yùn)行時確定表達(dá)式的具體值的,以下是文檔中的相關(guān)描述:
F-strings 使用最簡單的句法,提供了一種在字符串字面量中嵌入表達(dá)式的方式。值得注意的是,f-string 是在運(yùn)行時確定表達(dá)式的值的,而不是帶入一個固定值。
在 Python 代碼中,一個 f-string 就是一個帶有f前綴的字符串,并通過大括號嵌入表達(dá)式,這些表達(dá)式最后將由他們的具體值取代。(來源)
代碼執(zhí)行時,大括號中的表達(dá)式會在其命名空間中計(jì)算具體值,并返回它與其它部分的組合結(jié)果。
以下是速度比較:
正如我們所看到的,f-strings 的效率是最高的。
其實(shí),在最初實(shí)現(xiàn)時,f-strings 存在一些 效率問題,之后引入了一種特殊的操作碼 BUILD_STRING進(jìn)行解決。
現(xiàn)在,我們已經(jīng)了解了 f-strings 的優(yōu)勢,相信大家都已經(jīng)準(zhǔn)備開始使用了。以下是一些需要注意的小細(xì)節(jié)。
我們可以在表達(dá)式中使用各種引號,但應(yīng)該注意,表達(dá)式中的引號不要與外部的引號一樣。
以下代碼是正確的:
>>> f'{'Eric Idle'}''Eric Idle'
以下代碼也可以運(yùn)行:
我們也可以使用三引號:
>>> f'''Eric Idle''''Eric Idle'>>> f'''Eric Idle''''Eric Idle'
如果確實(shí)要在字符串中使用與外部一樣的引號的話,可以使用反斜線\進(jìn)行轉(zhuǎn)義:
關(guān)于引號的問題,需要注意表達(dá)式中的字典取值。如果外部字符串使用的是單引號,字典取值就應(yīng)該用雙引號。
以下代碼是可以工作的:
但以下代碼則會報錯:
>>> comedian = {'name': 'Eric Idle', 'age': 74}>>> f'The comedian is {comedian['name']}, aged {comedian['age']}.' File '', line 1 f'The comedian is {comedian['name']}, aged {comedian['age']}.' ^SyntaxError: invalid syntax
如果在內(nèi)部表達(dá)式中使用同樣的引號,解釋器會將其判斷為字符串的結(jié)束符號。
如果想在表達(dá)式中使用大括號,我們必須使用兩次大括號:
注意,使用三次大括號并不會給你兩個大括號:
>>> f'{{{74}}}''{74}'
當(dāng)然,如果使用四次,就可以給你兩個大括號了:
如之前所見,我們可以在 f-strings 的字符串部分通過反斜線對一些字符進(jìn)行轉(zhuǎn)義。
但是,在表達(dá)式中的反斜線是沒有轉(zhuǎn)義效果的:
>>> f'{\'Eric Idle\'}' File '', line 1 f'{\'Eric Idle\'}' ^SyntaxError: f-string expression part cannot include a backslash
有需要時,可以提前定義一個變量來繞過這種限制:
f-strings 中不應(yīng)包括帶 # 號的注釋,否則會導(dǎo)致句法錯誤:
>>> f'Eric is {2 * 37 #Oh my!}.' File '', line 1 f'Eric is {2 * 37 #Oh my!}.' ^SyntaxError: f-string expression part cannot include '#'
你依然可以使用原來的字符串格式化方式,但 f-strings 提供了一種更加簡潔,更具可讀性,更方便的格式化方式,不僅運(yùn)行更快,也更不容易出錯。因此,如果你還沒切換到 3.6 或更高的版本的話,f-strings 或許是一個值得考慮的切換理由。(如果你還在使用 Python 2,別忘了 2020年 就快到了?。?/p>
根據(jù) Python之禪,當(dāng)你需要做一件事的時候,“總有一種——最好是只有一種——最為明顯的方式?!?f-strings 或許不是字符串格式化的唯一方式,但很可能是最為明顯的方式。
聯(lián)系客服