Python基础:字符串格式化之百分号%方式
Python基础:字符串格式化之百分号%方式
一、开篇:最古老的Python格式化方式
Python有三种字符串格式化方式,按出现的时间顺序:
- 百分号
%:Python诞生时就有的,最古老 str.format():Python 2.6引入,功能更强大- f-string(
f'...'):Python 3.6引入,最现代、最推荐
⌨️ 今天我们先讲第一种——百分号格式化。虽然它是"老古董",但很多老项目中仍然大量使用,而且某些场景下它写起来特别简洁。了解它能帮你读懂和维护存量代码,也能在一些简单场景中快速完成任务。
二、百分号格式化的基本语法
2.1 语法形式
# 基本语法result="格式化字符串"%(值1,值2,...)# 最简单的例子name='小明'age=25text='我叫%s,今年%d岁'%(name,age)print(text)# 我叫小明,今年25岁语法结构:
%s、%d这些是占位符,标记了"这里要插入一个值"- 字符串末尾的
%是运算符,连接模板和实际值 (name, age)是值的元组,按顺序填入占位符
2.2 占位符速查表
| 占位符 | 含义 | 示例 |
|---|---|---|
%s | 字符串(自动调用str()) | '%s' % 'hello'→'hello' |
%d | 十进制整数 | '%d' % 42→'42' |
%i | 整数(同%d) | '%i' % 42→'42' |
%f | 浮点数 | '%f' % 3.14→'3.140000' |
%e | 科学计数法(小写e) | '%e' % 1000→'1.000000e+03' |
%E | 科学计数法(大写E) | '%E' % 1000→'1.000000E+03' |
%x | 十六进制(小写) | '%x' % 255→'ff' |
%X | 十六进制(大写) | '%X' % 255→'FF' |
%o | 八进制 | '%o' % 8→'10' |
%c | 字符(接受整数或单个字符) | '%c' % 65→'A' |
%r | 字符串repr()表示 | '%r' % 'hello'→"'hello'" |
%% | 百分号本身 | '%%'→'%' |
2.3 基本使用示例
# %s——万能占位符(适合快速输出)name='小明'score=95.5items=[1,2,3]print('姓名:%s'%name)# 姓名:小明print('分数:%s'%score)# 分数:95.5(自动转字符串)print('列表:%s'%items)# 列表:[1, 2, 3]# %d——整数(不接受浮点数)print('年龄:%d'%25)# 年龄:25# print('年龄:%d' % 25.5) # TypeError!%d不能接受浮点数# %f——浮点数print('π ≈ %f'%3.14159)# π ≈ 3.141590(默认6位小数)# 多个占位符name='小明'age=25height=1.75print('%s,%d岁,身高%.2f米'%(name,age,height))# 小明,25岁,身高1.75米⚠️ 注意:当只有一个值时,括号可以省略;但如果是元组,需要括号:
print('%s'%'hello')# 单个值,可以不写括号# print('%s' % ('hello',)) # 也可以写括号# print('%s %s' % 'a', 'b') # 错误!多个值不用括号会报错print('%s %s'%('a','b'))# 正确三、格式化精度控制
3.1 控制浮点数的小数位数
pi=3.141592653589793print('%.2f'%pi)# 3.14(保留2位小数)print('%.4f'%pi)# 3.1416(保留4位小数,四舍五入)print('%.10f'%pi)# 3.1415926536print('%f'%pi)# 3.141593(默认6位)# 整数也可以用小数控制(但通常不需要)print('%.2f'%42)# 42.003.2 控制宽度和对齐
# 固定宽度(右对齐,默认)print('%10s'%'hello')# ' hello'print('%10d'%42)# ' 42'print('%10.2f'%3.14)# ' 3.14'# 左对齐(负号)print('%-10s'%'hello')# 'hello 'print('%-10d'%42)# '42 '# 补零(数字前面补0)print('%010d'%42)# '0000000042'print('%010.2f'%3.14)# '0000003.14'# 居中对齐?百分号不支持,需要format()或f-string3.3 格式化实战
# 打印对齐的表格students=[('小明',20,85.5),('小红',21,92.0),('小刚',19,78.5),]print('%-8s %-6s %-8s'%('姓名','年龄','成绩'))print('-'*24)forname,age,scoreinstudents:print('%-8s %-6d %-8.1f'%(name,age,score))# 输出:# 姓名 年龄 成绩# ------------------------# 小明 20 85.5# 小红 21 92.0# 小刚 19 78.5四、%s 和 %r 的区别
# %s:调用str()——给人看的# %r:调用repr()——给调试看的x='hello'print('%s'%x)# helloprint('%r'%x)# 'hello'(带引号)# 对于字符串,区别很明显name='小明'print('名字是%s'%name)# 名字是小明print('名字是%r'%name)# 名字是'小明'# 对于数字,区别不大print('%s'%42)# 42print('%r'%42)# 42# repr在调试中很有用value=' hello \n'print('str: [%s]'%value)# str: [ hello ]print('repr: [%r]'%value)# repr: [' hello \n'](看到换行符和空格)💡%s适合最终输出(给用户看),%r适合调试日志(给开发者看)。
五、百分号格式化的高级用法
5.1 用字典传值
# 当格式化字符串很长、需要插入的值很多时,用字典可以避免一一对应的混乱person={'name':'小明','age':25,'city':'北京','job':'软件工程师'}template='我叫%(name)s,今年%(age)d岁,来自%(city)s,职业是%(job)s。'print(template%person)# 我叫小明,今年25岁,来自北京,职业是软件工程师。# 用字典的优势:可以重复使用同一个值template='%(name)s的年龄是%(age)d,%(name)s住在%(city)s。'print(template%person)# 小明的年龄是25,小明住在北京。5.2 动态宽度(用*传宽度)
# 宽度可以动态指定width=10precision=3print('%*.*f'%(width,precision,3.14159))# ' 3.142'(宽度10,保留3位小数)# 这在生成动态格式的报告中很有用defprint_table(data,col_widths):forrowindata:format_str=''forwidthincol_widths:format_str+='%-*s '# 构建: '%-*s %-*s %-*s 'print(format_str%tuple(itemforpairinzip(col_widths,row)foriteminpair))data=[('小明','北京','工程师'),('小红','上海','设计师')]print_table(data,[8,6,8])六、常见错误和注意事项
6.1 占位符和值的数量不匹配
# ❌ 占位符比值多# print('%s %s %s' % ('a', 'b')) # TypeError: not enough arguments# ❌ 值比占位符多# print('%s' % ('a', 'b')) # TypeError: not all arguments converted# ✅ 占位符和值数量对应print('%s %s'%('a','b'))# a b# 只有一个值时不需要元组(但如果值本身是元组要注意)print('%s'%('hello',))# helloprint('%s'%(('a','b'),))# ('a', 'b') —— 整个元组被当作一个值6.2 占位符和值的类型不匹配
# %d 不接受浮点数# print('分数:%d' % 95.5) # TypeError# 解决方法:先转换print('分数:%d'%int(95.5))# 分数:95# 或者直接用%sprint('分数:%s'%95.5)# 分数:95.5# %f 不接受字符串# print('价格:%f' % '十元') # TypeError6.3 忘记转义百分号
# 如果你想在格式化字符串中显示百分号本身# ❌ 错误写法# print('完成度:%d%' % 80) # ValueError# ✅ 用 %% 转义print('完成度:%d%%'%80)# 完成度:80%# 多个百分号print('增长率为%.2f%%'%15.678)# 增长率为15.68%七、百分号格式化 vs format() vs f-string
name='小明'age=25score=95.5# 三种方式对比# 1. 百分号——古老简洁s1='我叫%s,今年%d岁,成绩%.1f分'%(name,age,score)# 2. format()——功能强大s2='我叫{},今年{}岁,成绩{:.1f}分'.format(name,age,score)# 3. f-string——最现代最推荐s3=f'我叫{name},今年{age}岁,成绩{score:.1f}分'print(s1)# 三种方式输出一样print(s2)print(s3)| 特性 | % 格式化 | str.format() | f-string |
|---|---|---|---|
| Python版本 | 所有版本 | 2.6+ | 3.6+ |
| 可读性 | 一般 | 较好 | 最好 |
| 表达式支持 | 不支持 | 支持 | 支持 |
| 字典引用 | 支持 | 支持 | 支持 |
| 性能 | 一般 | 一般 | 最快 |
| 重复使用模板 | 支持 | 支持 | 需重新构建 |
💡 选择建议:
- 新代码:优先使用 f-string
- 需要复用模板:使用
str.format() - 维护老代码:了解百分号语法,不要强行改
- 简单快速调试:百分号一行搞定
'%s=%r' % (name, value)
八、何时仍然使用百分号
# 场景一:日志模块——logging推荐使用百分号importlogging logging.basicConfig(level=logging.INFO)# logging内部使用百分号格式化# 这样写可以让日志系统延迟执行字符串格式化(性能优化)name='小明'logging.info('用户 %s 登录了系统',name)# 注意:这里是逗号分隔,不是%运算符# 场景二:维护使用百分号的老代码# 如果整个项目已经用了百分号风格,保持一致# 不要在新旧风格间跳来跳去# 场景三:简单的调试输出print('x=%d, y=%d, sum=%d'%(x,y,x+y))# 虽然 f'{x=} {y=}' 更好,但老习惯改不掉九、实战:用百分号格式化生成报告
defgenerate_sales_report(month,data):"""生成销售报告"""report=[]report.append('='*50)report.append('%s月销售报告'%month)report.append('='*50)report.append('')report.append('%-20s %-10s %-10s'%('商品名称','销量','销售额'))total_quantity=0total_revenue=0.0foritem_name,quantity,priceindata: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))十、本篇小结
✅ 百分号格式化是Python最古老的字符串格式化方式:
- 基本语法:
"模板%s %d" % (值1, 值2) - 常用占位符:
%s(字符串)、%d(整数)、%f(浮点数) - 精度控制:
%.2f(保留2位小数)、%10s(宽度10右对齐)、%-10s(左对齐) - 字典传值:
%(name)s按名称引用,适合长模板 - 转义百分号:用
%%输出一个%
📝 虽然在新项目中推荐使用 f-string,但百分号格式化在日志模块、老项目维护、简单调试中仍然有其位置。下一篇我们进入str.format()——功能更强大的格式化方式。
