Python 格式化输出指南
在 Python 中,格式化输出是将变量或值插入到字符串中的过程,以便创建更具可读性、更动态的文本。Python 提供了多种方法来实现这一点,它们在不同版本中演进,各有优劣。
我们将详细探讨以下四种主要方法:
- f-strings(格式化字符串字面值):现代首选
str.format()方法:功能强大,仍然常用%操作符(C 风格格式化):旧式方法,仍在部分场景使用string.Template类:特定场景使用,安全性高
f-strings(格式化字符串字面值)
f-strings 是在 Python 3.6 中引入的,是目前最推荐、最直观、性能最好的字符串格式化方法。
核心语法
在字符串字面值前加上 f 或 F,然后在字符串内部使用花括号 {} 来包裹变量或表达式。
name = "世界"
year = 2025
pi = 3.1415926
# 基本用法
print(f"你好, {name}!")
# -> 你好, 世界!
# 可以直接嵌入表达式
print(f"明年是 {year + 1} 年。")
# -> 明年是 2026 年。
# 可以调用函数
print(f"你好, {name.upper()}!")
# -> 你好, 世界!
# 甚至可以创建复杂的对象
from datetime import datetime
print(f"现在时间是: {datetime.now()}")
# -> 现在时间是: 2025-07-24 09:46:20.123456 (示例)
格式规范迷你语言 (Format Specifier Mini-Language)
f-strings 的真正威力在于花括号内的“格式规范”。语法是:{value:specifier},其中 specifier 的通用结构是:[[fill]align][sign][#][0][width][,][.precision][type]。
让我们逐一分解这些选项。
fill 和 align(填充与对齐)
fill:用于填充的单个字符(默认为空格)。align<:左对齐 (默认);>:右对齐;^:居中对齐;=:(仅对数字有效)将填充字符放在符号和数字之间。
text = "Python"
# 左对齐,使用*填充,总宽度为10
print(f"'{text:*<10}'") # -> 'Python****'
# 右对齐,使用-填充,总宽度为10
print(f"'{text:->10}'") # -> '----Python'
# 居中对齐,使用#填充,总宽度为10
print(f"'{text:#^10}'") # -> '##Python##'
number = -123.45
# '=' 对齐,使用0填充,总宽度为10
print(f"'{number:0=10}'") # -> '-00123.45'
sign(符号显示)
+:始终显示符号(正数前加+,负数前加-);-:仅在负数前显示符号(默认);(空格):正数前留一个空格,负数前加-。
positive = 42
negative = -42
print(f"带'+'号: {positive:+}, {negative:+}") # -> 带'+'号: +42, -42
print(f"默认'-'号: {positive}, {negative}") # -> 默认'-'号: 42, -42
print(f"带' '号: {positive: }, {negative: }") # -> 带' '号: 42, -42
#(替代形式):对于整数类型,# 会在输出前加上 0b(二进制)、0o(八进制)或 0x(十六进制)。
num = 255
print(f"二进制: {num:b}, 十六进制: {num:x}") # -> 二进制: 11111111, 十六进制: ff
print(f"替代形式: {num:#b}, {num:#x}, {num:#o}") # -> 替代形式: 0b11111111, 0xff, 0o377
0(零填充):0 是一个简写,等效于 fill='0' 和 align='='。它会用 0 来填充数字的左侧以达到指定的宽度。
num = 42
print(f"'{num:05}'") # -> '00042'
# 等效于
print(f"'{num:0=5}'") # -> '00042'
width(宽度):一个整数,定义了输出的最小总宽度。如果值的长度小于 width,则会用 fill 字符填充。
print(f"|{'Hi':10}|") # -> |Hi |
print(f"|{'Hi':>10}|") # -> | Hi|
, 或 _(千位分隔符):用于数字,使其更易读。
large_number = 1000000000
print(f"{large_number:,}") # -> 1,000,000,000
print(f"{large_number:_}") # -> 1_000_000_000 (在代码中也是合法的数字字面量)
.precision(精度)
- 对于浮点数 (
f,F,e,E):表示小数点后的位数; - 对于通用类型 (
g,G):表示有效数字的总位数; - 对于字符串:表示输出的最大字符数。
pi = 3.1415926535
# 浮点数精度
print(f"{pi:.2f}") # -> 3.14
# 通用类型精度
print(f"{pi:.5g}") # -> 3.1416 (5位有效数字,会四舍五入)
# 字符串截断
message = "你好, 这是一个很长的消息"
print(f"{message:.5s}") # -> 你好, 这
type(类型):指定值的输出类型。
- 字符串
s; - 整数
d:十进制整数;b:二进制;o:八进制;x/X:十六进制(小写/大写字母);c:字符(将整数转换为对应的 Unicode 字符)。
- 浮点数
f/F:定点表示法(默认精度为 6);e/E:科学计数法;g/G:通用格式。自动在定点和科学计数法之间选择(当指数小于 -4 或不小于精度时使用科学计数法);%:百分比。将数字乘以 100 并以定点格式(f)显示,后面跟一个%。
# 整数类型
num = 97
print(f"十进制: {num:d}, 二进制: {num:b}, 字符: {num:c}") # -> 十进制: 97, 二进制: 1100001, 字符: a
# 浮点数类型
value = 12345.6789
print(f"定点: {value:f}") # -> 12345.678900
print(f"科学计数: {value:e}") # -> 1.234568e+04
print(f"通用: {value:g}") # -> 12345.7
# 百分比
ratio = 0.765
print(f"成功率: {ratio:.1%}") # -> 成功率: 76.5%
f-strings 高级特性
自我记录表达式 (Python 3.8+):使用 = 可以方便地进行调试,它会自动打印出表达式和它的值。
user = "admin"
is_logged_in = True
print(f"{user=} {is_logged_in=}")
# -> user='admin' is_logged_in=True
转换标志:在花括号内的变量名后使用 ! 可以改变其表示形式:
!s:调用str()(默认);!r:调用repr();!a:调用ascii()。
text = "你好"
print(f"{text!s}") # -> 你好
print(f"{text!r}") # -> '你好' (包含引号)
print(f"{text!a}") # -> '\u4f60\u597d' (非ASCII字符的转义表示)
嵌套和引号:f-strings 可以嵌套,但必须使用不同的引号。
width = 10
precision = 3
value = 12.34567
# 动态设置格式
print(f"结果: {value:{width}.{precision}f}") # -> 结果: 12.346
# 注意引号的使用
print(f'{"这是一个 \"引用的\" 字符串":^30}')
# -> ' 这是一个 "引用的" 字符串 '
str.format() 方法
这是在 Python 2.6 中引入的,并在 Python 3 中得到改进。在 f-strings 出现之前,它是格式化字符串的首选方法。它比 % 操作符更灵活、更强大。
核心语法
template_string.format(arg1, arg2, ...)。
占位符类型
自动编号/位置参数
# 自动编号 (Python 2.7+ and 3.1+)
print("你好, {}! 今天是 {}。".format("张三", "星期三"))
# -> 你好, 张三! 今天是 星期三。
# 手动编号
print("你好, {0}! 今天是 {1}。{1}天气不错。".format("张三", "星期三"))
# -> 你好, 张三! 今天是 星期三。星期三天气不错。
关键字参数
print("你好, {name}! 你的年龄是 {age}。".format(name="李四", age=30))
# -> 你好, 李四! 你的年龄是 30。
访问属性和索引
class Person:
def __init__(self, name):
self.name = name
p = Person("王五")
data = ["坐标", (10, 20)]
print("姓名: {p.name}, 类型: {d[0]}, 坐标: {d[1][0]}".format(p=p, d=data))
# -> 姓名: 王五, 类型: 坐标, 坐标: 10
格式规范
str.format() 使用的格式规范迷你语言与 f-strings 完全相同。你只需要将它放在占位符的冒号后面。
pi = 3.14159
# 对齐和填充
print("'{:*>10}'".format("hi")) # -> '********hi'
# 数字格式化
print("圆周率约等于 {:.2f}".format(pi)) # -> 圆周率约等于 3.14
# 组合使用
print("用户ID: {id:04d}, 余额: ${balance:,.2f}".format(id=7, balance=1234.5))
# -> 用户ID: 0007, 余额: $1,234.50
何时使用 str.format()?
尽管 f-strings 更简洁,但在某些情况下 str.format() 仍然很有用:比如格式化字符串模板和数据是分开定义的时候。例如,模板存储在配置文件中,而数据在运行时才生成。
# template.txt 文件内容: "Hello, {name}!"
with open('template.txt', 'r') as f:
template = f.read()
print(template.format(name="世界"))
# -> Hello, 世界!
% 操作符(C 风格格式化)
这是从 C 语言的 printf 函数借鉴过来的最古老的格式化方法。它简单易用,但在处理多个参数和不同类型时容易出错。
核心语法
"format_specifier" % (values)。
- 如果只有一个值,可以省略括号;
- 如果有多个值,必须使用元组
()。
name = "赵六"
age = 55
# 单个值
print("你好, %s" % name)
# -> 你好, 赵六
# 多个值 (必须用元组)
print("姓名: %s, 年龄: %d" % (name, age))
# -> 姓名: 赵六, 年龄: 55
格式化标志
标志紧跟在 % 之后。
%s:字符串(使用str()转换);%r:字符串 (使用repr()转换);%d或%i:有符号十进制整数;%f:浮点数;%e/%E:科学计数法的浮点数;%o:八进制数;%x/%X:十六进制数(小写/大写);%%:字面量%。
宽度和精度
语法为 %[flags][width][.precision]type。
width:最小字段宽度;.precision:小数点后的位数(对浮点数)或最大字符数(对字符串);flags-:左对齐;+:显示符号;0:零填充。
pi = 3.1415926
print("ID: %04d" % 7) # -> ID: 0007
print("浮点数: %.2f" % pi) # -> 浮点数: 3.14
print("左对齐: |%-10s|" % "test") # -> 左对齐: |test |
print("右对齐: |%10s|" % "test") # -> 右对齐: | test|
字典作为参数
当格式化字符串中包含很多变量时,使用字典可以提高可读性。
data = {"name": "孙七", "balance": 99.9}
print("你好, %(name)s. 你的余额是 %(balance).2f" % data)
# -> 你好, 孙七. 你的余额是 99.90
何时使用 % 操作符?
- 遗留代码:维护旧的 Python 2/3 代码库时会经常遇到;
logging模块:Python 的标准日志模块内部优化了%格式化,它只在日志消息需要被实际发出时才执行格式化,从而提高性能。
import logging
logging.warning("处理用户 %s 时发生错误", "admin") # 推荐写法
# logging.warning(f"处理用户 {'admin'} 时发生错误") # 不推荐,因为f-string会立即求值
string.Template 类
这是最简单、最安全的格式化方法,但功能也最有限。它主要用于处理由用户提供的模板字符串,因为它不执行任何代码,可以防止恶意注入。
核心语法
使用 $ 来标记占位符。
$variable:简单形式;${variable}:当占位符后紧跟字母数字字符时使用。
from string import Template
template_str = "你好, $name! 欢迎来到 ${location}。"
template = Template(template_str)
# 使用 substitute()
output = template.substitute(name="周八", location="我的世界")
print(output)
# -> 你好, 周八! 欢迎来到 我的世界。
# 如果缺少键,substitute() 会抛出 KeyError
try:
template.substitute(name="周八")
except KeyError as e:
print(f"错误: {e}") # -> 错误: 'location'
# 使用 safe_substitute()
# 如果缺少键,它会保留占位符
output_safe = template.safe_substitute(name="周八")
print(output_safe)
# -> 你好, 周八! 欢迎来到 ${location}。
string.Template 不支持任何复杂的格式化(如对齐、精度等)。
总结与建议
| 特性 | f-strings (推荐) | str.format() |
% 操作符 |
string.Template |
|---|---|---|---|---|
| 可读性 | 极高 | 良好 | 一般 | 良好 |
| 性能 | 最快 | 较快 | 较慢 | 最慢 |
| 简洁性 | 非常简洁 | 较冗长 | 一般 | 冗长(需实例化) |
| 功能性 | 非常强大 | 非常强大 | 有限 | 非常有限 |
| 安全性 | 一般(可执行代码) | 一般(可执行代码) | 一般 | 最高(不执行代码) |
| 引入版本 | Python 3.6+ | Python 2.6+ / 3.0+ | Python 2 & 3 | Python 2.4+ |
最终建议:
- 首选 f-strings:在所有支持 Python 3.6+ 的新项目中,始终使用 f-strings。它们更易读、更简洁、性能也最好。
str.format()作为次选:当你的格式化模板需要与数据分离时(例如,从文件或数据库加载模板),str.format()是一个绝佳的选择。- 谨慎使用
%操作符:仅在维护需要兼容旧版本的遗留代码,或在使用logging等特定库时使用它。 string.Template用于特定安全场景:当处理来自不可信来源(如用户输入)的模板时,使用string.Template来防止安全漏洞。

浙公网安备 33010602011771号