跳轉到

2016:f-string 與 Python 3.6——格式化字串的終極形態#

事件#

2016 年 12 月,Python 3.6 發佈,主要新特性:

  1. f-string(PEP 498):f"Hello {name}, {x:.2f}"——格式化字串字面值
  2. 變數標註(PEP 526):count: int = 0——為型別提示補上變數層語法
  3. 非同步產生器與非同步理解式(PEP 525/530):async for、async with
  4. secrets 模組(密碼學安全的隨機數)
  5. dict 保序成為語言保證(3.7 正式寫入規範)

為何重要#

  • f-string 是 Python 字串格式化的第四代:%(C 風格,1991)→ str.format()(2.6/3.0)→ f-string(3.6)。每一代都在補上一代的可讀性缺陷,f-string 成為公認的終極形態。
  • 變數標註補齊了型別提示的最後一塊:PEP 484 只有函式參數/回傳值,模組級與區域變數無從標註。
  • 非同步產生器補齊了 async/await 與產生器兩大體系之間的缺口。

理論與實用原因#

  • 理論原因(f-string):% 格式化與 str.format() 都是「模板與資料分離」——變數在字串裡是佔位符 {},實際值在後面,讀者要來回對照。f-string 改用內插(interpolation)理論:變數直接寫在字串內、執行期在當前作用域求值——所見即所得,名稱即表達式(甚至支援 {x*y=} 除錯)。這與 JavaScript(ES2015 模板字串)、C#(內插字串)、Ruby(內插)同期趨同。
  • 理論原因(變數標註):漸進式型別(PEP 484)的完整性要求——型別資訊要能覆蓋所有綁定點(函式參數、回傳值、變數)。標註存入 __annotations__,執行期不檢查,維持「零執行成本」原則。
  • 實用原因:str.format() 在多參數時可讀性崩壞;日誌、SQL 拼接、報表生成是格式化的大宗場景。secrets 則是因為無數專案誤用 random 生成金鑰——安全上的真實缺陷。

彌補了什麼缺陷#

  • f-string 彌補了 %/str.format()「模板與值分離、來回對照、易錯序」的可讀性缺陷。
  • 變數標註彌補了型別提示「無法標註變數」的缺口。
  • 非同步產生器彌補了「非同步資料流無法用 async for 迭代」的缺口。

程式範例#

f-string——字串格式化的四代演進:

name, x = "World", 3.14159

# 第一代(1991):C 風格 %,型別要自己寫對
"Hello, %s! x = %.2f" % (name, x)     # 來回對照、易錯序

# 第二代(2.6/3.0):str.format(),模板與值分離
"Hello, {n}! x = {v:.2f}".format(n=name, v=x)   # 冗長

# 第三代(3.6):f-string——內插,所見即所得
f"Hello, {name}! x = {x:.2f}"         # "Hello, World! x = 3.14"
f"{x * 2 = }"                          # 表達式也行(3.8 的 = 除錯語法)

變數標註(PEP 526)——補齊型別提示:

# PEP 484 只有函式參數/回傳值;模組級變數無從標註
count: int = 0            # PEP 526:變數層的型別標註
names: list[str] = []     # 執行期不檢查,由 mypy 靜態檢查

非同步產生器(PEP 525)——補齊 async 與產生器的缺口:

async def readings(n):
    for i in range(n):
        yield i               # 3.5 做不到!async 產生器是 3.6 才補上

async def main():
    async for v in readings(5):   # PEP 530:非同步理解式
        print(v)

相關條目#