Python百分号格式化教程:占位符、精度控制与错误排查
Python 的字符串格式化有三种常见方式:百分号 %、str.format() 和 f-string。百分号 % 是 Python 诞生时就存在的方式,虽然在新项目中通常优先用 f-string,但大量存量代码、logging 模块以及简单调试输出仍然会用到它。理解它的占位符、宽度精度控制和异常原因,能减少维护老项目时的排错成本。本文围绕百分号 % 的实际用法展开,示例基于 Python 3。一、基本语法与执行规则
百分号格式化的形式是:模板字符串 % 值或值元组。模板中的 %s、%d 等叫占位符,末尾的 % 是运算符,右侧的值按顺序填入占位符。
name = '小明'
age = 25
text = '我叫%s,今年%d岁' % (name, age)
print(text)# 我叫小明,今年25岁
当只有一个值时可省略括号;多个值必须使用元组,否则会被解析成多个参数并报错。
print('%s' % 'hello') # hello
print('%s %s' % ('a', 'b'))# a b
# print('%s %s' % 'a', 'b')# 错误,多个值必须加括号
常用占位符如下:
%s:字符串,自动调用 str();
%d / %i:十进制整数;
%f:浮点数,默认保留 6 位小数;
%e / %E:科学计数法,小写 e 或大写 E;
%x / %X:十六进制,小写或大写;
%o:八进制;
%c:字符,可接受整数或单个字符;
%r:调用 repr() 的字符串表示;
%%:输出一个百分号本身。
二、精度、宽度与对齐控制
浮点数常用 %.2f 控制小数位数,默认会四舍五入。宽度可以用 %10s、%10d、%10.2f 指定,默认右对齐;负号表示左对齐,如 %-10s。数字补零用 %010d 或 %010.2f。百分号格式化不支持居中对齐,居中对齐需要改用 format() 或 f-string。
pi = 3.141592653589793
print('%.2f' % pi) # 3.14
print('%.4f' % pi) # 3.1416
print('%f' % pi) # 3.141593
print('%10s' % 'hello') # ' hello'
print('%-10s' % 'hello')# 'hello '
print('%010d' % 42) # '0000000042'
对齐输出表格是常见场景:
students = [
('小明', 20, 85.5),
('小红', 21, 92.0),
('小刚', 19, 78.5),
]
print('%-8s %-6s %-8s' % ('姓名', '年龄', '成绩'))
print('-' * 24)
for name, age, score in students:
print('%-8s %-6d %-8.1f' % (name, age, score))
三、%s 与 %r 的区别
%s 调用 str(),偏向给用户看的输出;%r 调用 repr(),偏向调试和日志。字符串使用 %r 时会带引号,空白和换行等不可见字符也会显示出来。
x = 'hello'
print('%s' % x)# hello
print('%r' % x)# 'hello'
value = ' hello \n'
print('str: [%s]' % value) # str: [ hello ]
print('repr: [%r]' % value)# repr: [' hello \n']
四、字典传值与动态宽度
当模板很长、需要插入的字段很多时,可以用字典传值,占位符写成 %(name)s 的形式。这样不必严格依赖参数顺序,同一个值还可以重复引用。
person = {
'name': '小明',
'age': 25,
'city': '北京',
'job': '软件工程师'
}
template = '我叫%(name)s,今年%(age)d岁,来自%(city)s,职业是%(job)s。'
print(template % person)
template = '%(name)s的年龄是%(age)d,%(name)s住在%(city)s。'
print(template % person)
动态宽度可以用 *,宽度和精度从参数中取。例如 %*.*f 分别接收宽度、精度和数值。这个写法适合按列宽生成文本报告。
width = 10
precision = 3
print('%*.*f' % (width, precision, 3.14159))
# ' 3.142'
也可以按列宽构建格式字符串:
def print_table(data, col_widths):
for row in data:
format_str = ''
for width in col_widths:
format_str += '%-*s '
print(format_str % tuple(
item for pair in zip(col_widths, row) for item in pair
))
data = [('小明', '北京', '工程师'), ('小红', '上海', '设计师')]
print_table(data, )
五、常见错误与排查
第一类错误是占位符和值的数量不匹配。占位符多于值会抛 TypeError: not enough arguments;值多于占位符会抛 TypeError: not all arguments converted。排查时先数清模板里有多少个需要替换的占位符,再检查右侧元组元素数量。
第二类错误是类型不匹配。%d 不接受浮点数,例如 '%d' % 95.5 会报 TypeError;可以先用 int() 转换,或者改用 %s。%f 也不接受字符串。
第三类错误是忘记转义百分号。要在结果中显示 %,模板里必须写 %%。如果写成 '完成度:%d%' % 80,会触发 ValueError。正确写法是 '完成度:%d%%' % 80。多个百分号同理,例如 '增长率为%.2f%%' % 15.678。
print('%s %s' % ('a', 'b')) # a b
print('分数:%d' % int(95.5)) # 分数:95
print('分数:%s' % 95.5) # 分数:95.5
print('完成度:%d%%' % 80) # 完成度:80%
print('增长率为%.2f%%' % 15.678) # 增长率为15.68%
只有一个值且该值本身是元组时,需要再包一层元组,否则整个元组会被当成多个参数或单个参数,具体取决于占位符数量。例如 print('%s' % (('a', 'b'),)) 会输出 ('a', 'b')。
六、和 format()、f-string 的选择
同一条输出可以用三种方式完成:
name = '小明'
age = 25
score = 95.5
s1 = '我叫%s,今年%d岁,成绩%.1f分' % (name, age, score)
s2 = '我叫{},今年{}岁,成绩{:.1f}分'.format(name, age, score)
s3 = f'我叫{name},今年{age}岁,成绩{score:.1f}分'
print(s1)
print(s2)
print(s3)
从版本看,百分号所有 Python 版本都支持,str.format() 从 Python 2.6 开始,f-string 从 Python 3.6 开始。可读性上 f-string 最好,并且支持表达式;百分号写法简单直接,但功能相对有限;str.format() 适合需要复用模板的场景。新代码优先 f-string;维护老代码时保持原有风格;简单调试可以继续使用 '%s=%r' % (name, value)。
七、哪些场景仍适合百分号
logging 模块是典型场景。logging 推荐写成 logging.info('用户 %s 登录了系统', name),这里用逗号分隔参数,而不是先做字符串拼接。这样日志系统可以延迟执行格式化,在日志级别不满足时不进行字符串运算。
import logging
logging.basicConfig(level=logging.INFO)
name = '小明'
logging.info('用户 %s 登录了系统', name)
此外,维护已经大量使用百分号的老项目时,保持一致比混用风格更清晰;在一些很短的一行调试输出中,百分号格式化也很方便。
八、实战:生成销售报告
下面的函数用 %-20s、%-10d、¥%-9.2f 等格式生成对齐的文本报告,并用 '\n'.join(report) 汇总输出。它体现了百分号格式化在纯文本报告、表格输出中的实际用法。
def generate_sales_report(month, data):
"""生成销售报告"""
report = []
report.append('=' * 50)
report.append('%s月销售报告' % month)
report.append('=' * 50)
report.append('')
report.append('%-20s %-10s %-10s' % ('商品名称', '销量', '销售额'))
total_quantity = 0
total_revenue = 0.0
for item_name, quantity, price in data:
revenue = quantity * price
report.append('%-20s %-10d ¥%-9.2f' % (item_name, quantity, revenue))
total_quantity += quantity
total_revenue += revenue
report.append('-' * 50)
report.append('%-20s %-10d ¥%-9.2f' % ('合计', total_quantity, total_revenue))
report.append('=' * 50)
return '\n'.join(report)
data = [
('Python编程书', 150, 59.9),
('机械键盘', 80, 299.0),
('显示器支架', 45, 159.0),
('USB数据线', 300, 19.9),
]
print(generate_sales_report('5', data))
总结:百分号格式化的核心是“模板 + 占位符 + 值元组”。掌握 %s、%d、%f、%.2f、%10s、%-10s、%% 和 %(name)s 后,就能读懂大多数老代码。遇到 TypeError 或 ValueError 时,优先检查值数量、值类型和百分号转义。新项目可优先 f-string,但日志、老项目维护和简单调试中,百分号格式化仍有明确位置。
Re: Python百分号格式化教程:占位符、精度控制与错误排查
这篇整理得挺实用。百分号格式化虽然老,但老项目、logging 和临时调试输出里确实绕不开。占位符数量不匹配、%d 传浮点数、忘记把百分号写成 %% 这几个坑,排查那节说得很清楚。宽度精度和对齐的例子也直观,%-8s 这种做简单表格输出很方便。字典传值和动态宽度平时用得少,看完例子清楚多了。感谢分享,收藏了。Re: Python百分号格式化教程:占位符、精度控制与错误排查
楼主整理得挺全面,百分号格式化确实属于那种平时不主动用、但看老代码和 logging 时绕不开的东西。占位符列表、宽度精度、%s 和 %r 的区别都讲得很清楚,尤其是用 %r 看空白和换行这个点,排查字符串问题时很实用。字典传值和动态宽度 %*.*f 也算进阶技巧了,做文本报表能省不少拼接。常见错误部分总结得也到位,数量不匹配、类型不匹配、%% 转义这三个基本都踩过。最后好像停在 prin 那里,是不是没贴完?期待补上。Re: Python百分号格式化教程:占位符、精度控制与错误排查
写得很系统,特别是 %s 和 %r 那段,用 ' hello \n' 做对比很直观,以前只知道一个带引号一个不带,没细想过是为了把不可见字符暴露出来。我们组维护的旧系统里 logging 基本都是 %s 风格,看完第二、四节再回去看那些模板就顺眼多了,动态宽度用 * 那招之前完全没意识到,按列宽生成报告时应该能少写不少拼接。 也分享一个自己踩过的坑:数据库字段有时候返回 Decimal 或者已经是浮点,直接套 %d 就抛 TypeError,后来统一改成 %s 或者先 int() 才稳。还有 %% 转义那个,模板和参数分开定义时特别容易漏,日志里少个百分号倒还好,要是往报表或者接口返回里塞,格式就悄悄错了,排查起来反而费劲。 顺便问一下,字典传值那节,如果模板里写了 %(name)s 但传进去的字典少这个 key,抛的是 KeyError 吧?我记得和数量不匹配那种 TypeError 不是一回事,想确认下思路对不对。 另外提一句,帖子最后好像被截断了,那个 prin 后面是不是还有内容没贴完,比如错误示例或者小结?
页:
[1]