對於一種語言,我們主張關注於用一種合適的方法來處理大部分的情況,Python字串格式化卻是一個另類,而且其越來越多樣化。從 Python 3.6 開始我們有三種字串格式化的方式(除了簡單的連線或使用 string.Template):
- 使用%操作符
- str.format
- 插入字串
(如果你不想主動閱讀所有的這些,我會在PyGraz meetup in February 2016裡給你一個關於這個稍微擴充套件的閃電式演講,裡面會有更多一點的例子)
%-formatting
%-formatting從1.0版本開始就成為語言的一部分了。如果你用過Python3之前的版本,你會知道這個。
1 |
"%s %s" % ('Hello', 'World',) |
這個或多或少的有點像C語言的 sprintf
。它會起作用,但是使用起來有點複雜。
因為它只支援有限的型別,所以在傳值到字串格式化器之前,你得把你的自定義物件轉換成它支援的型別之一。
很多年後,本地字串資料型別擴充套件了一種 format
方法:
str.format()
在2008年10月被新增進Python2.6,類似於上下文管理器。在PEP-3101有詳細描述,它著力於解決老的二元操作符%的一些不足,比如只支援有限的型別,以及在實際處理整個表示式右半部分的時候,會出現一些容易導致錯誤特殊的情況。
1 2 3 4 |
>>> "%s" % ("lala",) 'lala' >>> "%s" % "lala" 'lala' |
由於 .format
是一個方法而不是操作符(被對映到一個二元函式),處理引數會更加明確。如果你傳入一個字串,它就會被解釋為一個字串。如果你傳入只包含一個字串的元組,那它就被解釋為只包含一個字串的元組。
1 2 3 4 |
>>> "{}".format("lala") 'lala' >>> "{}".format(("lala",)) "('lala',)" |
對比與%操作符,它還支援使用命名引數而不需要字典。
1 |
"{firstname} {lastname}".format(firstname="Horst", lastname="Gutmann") |
起初,它的發明是為了完全取代%操作符(它被計劃用來反對Python 3.1的老式格式化功能)但是並沒有完全發生。這個字串格式化器的核心功能基本是和老的%操作符一樣的,但語法略有不同,恕我直言更為直觀一些。實際上,因為Ulrich和我建立了pyformat.info來幫助人們遷移到新系統。
但是,很顯然,pep – 3101並沒有停留在只是清理舊的特性。它還引入了一個協議,允許使用有更多樣化互動的自定義類:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Country: def __init__(self, name, iso): self.name, self.iso = name, iso def __format__(self, spec): if spec == 'short': return self.iso return self.name country = Country("Austria", "AUT") print("{}".format(country)) print("{:short}".format(country)) |
你可以想象對於字串格式化 __format__
方法像 __str__
一樣你可以傳遞選擇項。如果你在你的物件中有 __format__
方法,當你使用格式化方法時它就會代替 __str__
(除非你做些什麼比如 "{!s}".format(country)"
)。
事實上你會在 datetime.date class in Python 3.4找到一個關於如何使用的很好的例子:
1 2 3 4 5 6 |
class date: ... def __format__(self, fmt): if len(fmt) != 0: return self.strftime(fmt) return str(self) |
這允許你使用“parent”字串格式直接來格式化日期,這樣就不需要首先把你的日期物件轉換成一個字串,然後再傳遞到字串格式化器中:
1 2 3 |
import datetime print("Today is {:%A}".format(datetime.datetime.now())) # Today is Thursday |
PEP-0498: 字串插入
這是現在推薦的字串格式化方法,.format
太繁瑣了:
1 2 3 4 5 |
a = "Hello" b = "World" "{} {}".format(a, b) # vs. "%s %s" % (a, b,) |
PEP-0498致力於通過提供一些在別的語言比如Ruby,Scala,Perl中常見且存在一段時間的東西:字串插入,來改善這種情況。這裡表示式可以直接被整合到字串,這意味著你不必再顯式地呼叫任何額外的函式。
ES2015最近將這個特性引入到了JavaScript中,它被稱為“模板字串”:
1 2 |
const username = "Horst"; const welcomeMsg = `Hello, ${username}!`; |
因為Python 3.0的一點歷史問題,引號在Python中並不可用。再次引入也會再次影響到語言的基本語法。相代替的,另一個字串字首被引入: f
.
1 2 3 4 |
a = "Hello" b = "World" f"{a} {b}" f"{a + ' ' + b}" |
你不再需要對一個字串顯式地呼叫 .format()
方法了,只是簡單地用 f
字首標記一下格式以及內聯最終字串中你想要包括的表示式。否則它們就會提供和 .format()
相同的功能。這些格式化字串在文件中也被稱為“f-strings”。
這看起來確實十分美好,但因為python 3.6會在12個月後才能夠釋出,你恐怕還要等上一段時間。話雖這麼說,程式碼卻已經在那兒了,所以你能做的就是獲取一個python 3.6預釋出版本或者使用一些像 pyenv 的小訣竅,然後讓它執行就行了。
其實還有很多,這裡有其他的PEP(0501),它想要引入i-strings,而這類字串導致了字串的懶惰計算,以至於例如你能在最終評估之前能做一些國際化(i18n)或者安全檢查。雖然這個提案已延緩至進一步的討論,但這看起來是個很好的想法。
但是回到f-strings:如果你想知道更多關於字串插入的解決方案,看看PEP-0502 ,它包括一個關於背後的動機和來自其他語言特性靈感的詳細討論。