Python 格式化输出指南

在 Python 中,格式化输出是将变量或值插入到字符串中的过程,以便创建更具可读性、更动态的文本。Python 提供了多种方法来实现这一点,它们在不同版本中演进,各有优劣。

我们将详细探讨以下四种主要方法:

  1. f-strings(格式化字符串字面值):现代首选
  2. str.format() 方法:功能强大,仍然常用
  3. % 操作符(C 风格格式化):旧式方法,仍在部分场景使用
  4. string.Template 类:特定场景使用,安全性高

f-strings(格式化字符串字面值)

f-strings 是在 Python 3.6 中引入的,是目前最推荐、最直观、性能最好的字符串格式化方法。

核心语法

在字符串字面值前加上 fF,然后在字符串内部使用花括号 {} 来包裹变量或表达式。

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]

让我们逐一分解这些选项。

fillalign(填充与对齐)

  • 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+

最终建议:

  1. 首选 f-strings:在所有支持 Python 3.6+ 的新项目中,始终使用 f-strings。它们更易读、更简洁、性能也最好。
  2. str.format() 作为次选:当你的格式化模板需要与数据分离时(例如,从文件或数据库加载模板),str.format() 是一个绝佳的选择。
  3. 谨慎使用 % 操作符:仅在维护需要兼容旧版本的遗留代码,或在使用 logging 等特定库时使用它。
  4. string.Template 用于特定安全场景:当处理来自不可信来源(如用户输入)的模板时,使用 string.Template 来防止安全漏洞。
posted @ 2025-07-24 11:39  AFewMoon  阅读(101)  评论(0)    收藏  举报