Python 字符串格式化全解析:%、format () 与 f-string 的前世今生

2026年01月29日 18:53

Python 字符串格式化全解析:%format() f-string 的前世今生

字符串格式化是程序开发中不可或缺的基础能力,它负责将变量、表达式等动态内容嵌入固定文本模板中,生成人类可读的字符串。Python 提供了三种主流的格式化方式:

1. 传统的 % 占位符

2. 功能丰富的 str.format() 方法

3. 现代简洁的 f-string(格式化字符串字面值)

这三种方式各有设计背景、语法特点和适用场景。深入理解它们的差异与联系,能帮助开发者写出更高效、更易维护的代码。

一、% 格式化:Python 格式化的元老

% 格式化是 Python 最早引入的字符串格式化机制,其设计灵感源自 C 语言的 printf() 函数。对于熟悉 C 语言的开发者,上手成本低。

1.1 基本语法与占位符

核心语法:模板字符串 % (值1, 值2, ...)

常见占位符对照表

占位符

含义

示例

%s

字符串(调用 str()

%s "hello"

%r

原始表示(调用 repr()

%r "'hello'"

%d

十进制整数

%d 42

%f

浮点数

%f 3.141593

%x/%X

十六进制整数

%x 2a%X 2A

%%

字面量 %

%% "%"

代码示例

Python
name = "Alice"
age = 30
print("Name: %s, Age: %d" % (name, age))  # 输出:Name: Alice, Age: 30

score = 95.5
print("Score: %f" % score)  # 输出:Score: 95.500000

1.2 进阶用法:格式控制

语法:%[宽度][.精度][类型]

Python
pi = 3.1415926535
print("PI: %.2f" % pi)  # 保留2位小数,输出:PI: 3.14

num = 42
print("Number: %5d" % num)   # 右对齐,宽度5,输出:Number:    42
print("Number: %-5d" % num)  # 左对齐,宽度5,输出:Number: 42   

text = "Hello, World!"
print("Text: %.5s" % text)   # 截断到5个字符,输出:Text: Hello

1.3 局限性

1. 类型严格匹配,易抛出类型错误

2. 多变量需封装为元组,顺序错误会导致输出混乱

3. 不支持嵌套表达式或直接调用对象方法

4. 扩展性差,无法实现自定义格式逻辑

Python
# 类型不匹配报错
print("Age: %d" % "30")  # TypeError: %d format: a real number is required, not str

# 多变量顺序易错
age = 30
name = "Alice"
print("%s is %d years old" % (age, name))  # 输出混乱:30 is Alice years old

二、str.format():功能升级的继承者

Python 2.6 引入 str.format()Python 3 完善了该机制。它使用 {} 作为占位符,支持位置参数、关键字参数、嵌套访问和自定义对象格式化,解决了 % 格式化的诸多痛点。

2.1 基本语法与参数传递

核心语法:模板字符串.format(参数1, 参数2, ..., 关键字参数1=值1, ...)

位置参数

Python
# 按顺序匹配
print("Name: {}, Age: {}".format("Bob", 25))  # 输出:Name: Bob, Age: 25

# 按索引匹配,支持重复使用
print("Second: {1}, First: {0}, First again: {0}".format("A", "B"))  # 输出:Second: B, First: A, First again: A

关键字参数

Python
# 按参数名匹配,无需关注顺序
print("User: {name}, ID: {id}".format(name="Charlie", id=1001))  # 输出:User: Charlie, ID: 1001

混合使用

Python
# 位置参数在前,关键字参数在后
print("{}: {name} ({age})".format("Info", name="David", age=28))  # 输出:Info: David (28)

2.2 进阶用法:格式规范与类型转换

对齐与宽度

语法:{:[对齐方式][宽度]},对齐方式包括 <(左对齐)、^(居中)、>(右对齐)

Python
print("|{:<10}|{:^10}|{:>10}|".format("Left", "Center", "Right"))
# 输出:|Left      |  Center  |     Right|

浮点数精度 & 千位分隔符

Python
pi = 3.1415926535
print("PI: {:.3f}".format(pi))  # 保留3位小数,输出:PI: 3.142

num = 1234567
print("Number: {:,}".format(num))  # 千位分隔符,输出:Number: 1,234,567

类型转换

Python
text = "Hello"
print("str: {!s}, repr: {!r}".format(text, text))
# 输出:str: Hello, repr: 'Hello'

日期 & 百分比

Python
from datetime import datetime

now = datetime(2025, 11, 12, 15, 30)
print("Date: {:%Y-%m-%d %H:%M}".format(now))  # 输出:Date: 2025-11-12 15:30

ratio = 0.753
print("Ratio: {:.1%}".format(ratio))  # 格式化为百分比,保留1位小数,输出:Ratio: 75.3%

2.3 高级特性:嵌套与自定义对象

嵌套结构访问(字典、列表)

Python
person = {"name": "Eve", "age": 32}
hobbies = ["reading", "hiking"]

print("Name: {p[name]}, Hobby: {h[0]}".format(p=person, h=hobbies))
# 输出:Name: Eve, Hobby: reading

自定义对象格式化(实现 __format__ 方法)

Python
class Temperature:
    def __init__(self, c):
        self.celsius = c
    
    def __format__(self, spec):
        # spec 接收格式化指令,自定义摄氏度/华氏度转换
        return f"{self.celsius:.1f}°C" if spec != 'f' else f"{self.celsius*9/5+32:.1f}°F"

temp = Temperature(25)
print("Temp: {t:c}".format(t=temp))  # 输出:Temp: 25.0°C
print("Temp: {t:f}".format(t=temp))  # 输出:Temp: 77.0°F

2.4 优缺点总结

优点

1. 语法灵活,无需严格匹配变量类型

2. 格式控制能力丰富,支持多种特殊格式(千位分隔符、日期等)

3. 支持嵌套访问和自定义对象格式化,扩展性强

4. 兼容 Python 2.6+ 和所有 Python 3 版本

缺点

1. 语法相对冗长,变量较多时模板字符串可读性下降

2. 复杂场景下书写繁琐,效率低于 f-string

三、f-string:现代 Python 的最优解

Python 3.6+ 引入 f-string(格式化字符串字面值),只需在字符串前加 f  F,即可直接在 {} 中嵌入变量或表达式,实现模板与值的无缝融合,是目前最推荐的格式化方式。

3.1 基本语法

Python
name = "Frank"
age = 27
print(f"Name: {name}, Age: {age}")  # 输出:Name: Frank, Age: 27

x, y = 10, 20
print(f"Sum: {x+y}, Product: {x*y}")  # 直接嵌入表达式,输出:Sum: 30, Product: 200

3.2 格式控制

f-string 兼容 str.format() 的所有格式规范,语法更简洁:{变量/表达式:[格式规范]}

Python
name = "Grace"
# 对齐与宽度
print(f"|{name:<10}|{name:^10}|{name:>10}|")
# 输出:|Grace     |  Grace   |     Grace|

pi = 3.14159
ratio = 0.62
# 浮点数精度与百分比
print(f"PI: {pi:.2f}, Ratio: {ratio:.0%}")
# 输出:PI: 3.14, Ratio: 62%

from datetime import datetime
now = datetime.now()
# 日期格式化
print(f"Today: {now:%Y年%m月%d日}")
# 输出:Today: 2026年01月30日(随当前日期变化)

3.3 高级特性

函数调用

Python
def greet(n):
    return f"Hello, {n}!"

print(f"{greet('Henry')}")  # 直接调用函数,输出:Hello, Henry!

条件表达式

Python
score = 85
print(f"Result: {'Pass' if score>=60 else 'Fail'}")  # 输出:Result: Pass

嵌套结构访问

Python
person = {"name": "Ivy", "age": 29}
hobbies = ["painting", "coding"]

print(f"Name: {person['name']}, Hobby: {hobbies[1]}")
# 输出:Name: Ivy, Hobby: coding

多行 f-string

使用三引号包裹,保留换行格式,注意 {} 内的语法正确性

Python
name = "Jack"
age = 33
bio = f"""
User Profile:
- Name: {name}
- Age: {age}
- Status: {'Active' if age<40 else 'Inactive'}
"""
print(bio.strip())  # strip() 去除首尾多余换行

3.4 性能与安全

性能对比

f-string 在编译时解析,无需运行时额外处理,性能优于 % 格式化和 str.format()

Python
import timeit

def test_percent():
    return "%s" % "test"

def test_format():
    return "{}".format("test")

def test_fstring():
    return f"{'test'}"

print("Percent:", timeit.timeit(test_percent, number=10_000_000))
print("Format:", timeit.timeit(test_format, number=10_000_000))
print("F-string:", timeit.timeit(test_fstring, number=10_000_000))

注意事项

1. 转义 {}:若需在 f-string 中显示字面量 {  },需使用 {{  }} 进行转义

2. 模板复用:f-string 直接绑定当前变量,无法像 str.format() 那样复用模板,需封装为函数实现复用

3. 安全风险:避免嵌入不可信用户输入,防止代码注入(所有格式化方式均需注意此点)

Python
# 转义示例
print(f"Literal {{}}: {{123}}")  # 输出:Literal {}: {123}

四、三种方式的对比与选型指南

4.1 特性对比表

特性

% 格式化

str.format()

f-string

语法简洁

中等

中等

⭐⭐⭐⭐⭐

可读性

⭐⭐⭐⭐⭐

功能丰富

基础

丰富

丰富

表达式支持

⚠️(有限支持)

自定义格式

性能

中等

中等

版本兼容

Python2/3

2.6+/3+

3.6+

模板复用

⚠️(需函数封装)

4.2 场景化选型

1. 日常开发(Python 3.6+):f-string

○ 原因:简洁、可读、性能佳,满足绝大多数场景需求

2. 兼容旧版本(Python 2.x 3.5 及以下):str.format()

○ 原因:功能灵活,兼容性强,无类型匹配痛点

3. 日志系统:% 格式化

○ 原因:日志模块(如 logging)对 % 格式化有优化,支持延迟执行,提升性能

4. 动态模板 / 配置文件:str.format()

○ 原因:支持模板与数据分离,可多次复用同一模板进行不同值替换

5. 表达式计算、函数调用嵌入:f-string

○ 原因:可直接嵌入任意合法 Python 表达式,无需额外封装,书写高效

五、总结:格式化方式的演进与最佳实践

5.1 演进核心逻辑

Python 字符串格式化的演进体现了语言设计对开发者体验的持续优化:

1. % 格式化:奠定基础,满足简单场景,兼容传统 C 语言习惯

2. str.format():弥补 % 格式化的不足,提升灵活性和扩展性

3. f-string:在简洁性、可读性和性能上达到平衡,成为现代 Python 开发的首选

5.2 最佳实践

1. 统一项目格式化风格,避免多种方式混用,提升代码可维护性

2. 简单场景优先使用 f-string,复杂格式(如动态模板)使用 str.format()

3. 自定义类可实现 __str__ (默认字符串表示)与 __format__ (自定义格式化)方法,提升对象的易用性

4. 循环中避免重复生成格式化模板,可提前定义模板变量(str.format())或封装为函数(f-string),提升执行效率

5. 掌握三种方式的核心差异,可灵活应对不同版本环境和业务场景的需求