str.format()是模板引擎式解析而非简单拼接,先编译格式串为指令序列再执行参数提取、类型转换等,性能低于f-string;支持动态字段名与多版本兼容,但format_spec解析逻辑分散、易出错。

format 方法不是字符串拼接,而是模板引擎式解析
Python 的 str.format() 在调用时并不直接做字符串替换,而是先编译格式字符串为内部指令序列(类似小型字节码),再逐条执行:提取参数、类型转换、对齐填充、进制转换等。这意味着它比 % 格式化更重,也比 f-string 慢一个数量级。
常见错误现象:KeyError: 'name' 不是因为字典没这个 key,而是格式字符串里写了 {name},但传入的是位置参数而非命名参数;或者用了 **data 但 data 里真缺 key。
- 使用场景:需要动态字段名、复用同一模板多次渲染、或兼容旧 Python 版本(
str.format()从 2.6 就支持) - 参数差异:
'{0} {name}'.format('hi', name='bob')混用位置和命名参数是合法的,但反过来 —— 命名参数后不能跟纯位置参数(会报ValueError: cannot switch from manual field specification to automatic field numbering) - 性能影响:每次调用都重新解析格式串;若格式串固定,可提前用
string.Formatter().parse()缓存解析结果,但极少有人这么做——f-string 更快更直观
format 的 format_spec 解析逻辑不透明,容易误解对齐和精度
format_spec(冒号后面的部分)看似简单,实则规则分散在不同类型实现里。比如 '{:05d}'.format(42) 走的是 int.__format__,而 '{:.2f}'.format(3.1415) 走的是 float.__format__,两者对 0、^、, 等符号的处理逻辑不同,且文档藏得深。
常见错误现象:'{:08x}'.format(-1) 报 ValueError: Unknown format code 'x' for object of type 'int' —— 因为负数不支持 x,必须先取绝对值或用 & 0xFF 截断;又如 '{:05}'.format('hi') 补的是空格不是 0,因为字符串默认忽略前导填充字符。
立即学习“Python免费学习笔记(深入)”;
功能列表:底层程序与前台页面分离的效果,对页面的修改无需改动任何程序代码。完善的标签系统,支持自定义标签,公用标签,快捷标签,动态标签,静态标签等等,支持标签内的vbs语法,原则上运用这些标签可以制作出任何想要的页面效果。兼容原来的栏目系统,可以很方便的插入一个栏目或者一个栏目组到页面的任何位置。底层模版解析程序具有非常高的效率,稳定性和容错性,即使模版中有错误的标签也不会影响页面的显示。所有的标
- 对齐符号
/<code>>/^只对字符串和数字生效,但数字默认右对齐,字符串默认左对齐,混用易出错 - 精度(
.N)对字符串是截断长度,对浮点数是小数位数,对整数加f格式才生效(如'{:0.2f}'.format(42)→'42.00') - 逗号千位分隔符
,对int和float有效,但对Decimal需要显式指定 locale 或用format(..., ',')
自定义类怎么让 format 正常工作?别只写 __format__
仅实现 __format__(self, format_spec) 不够。如果用户写了 '{!r}'.format(obj),走的是 __repr__;写了 '{!s}',走的是 __str__;只有没加转换标志时,才调用 __format__。而且 __format__ 必须自己解析 format_spec,Python 不帮你拆。
常见错误现象:自定义类返回 int 子类,以为能直接用 '{:04x}',结果报错 —— 因为子类没继承 int.__format__,必须显式委托:return int(self).__format__(format_spec)。
- 正确做法:在
__format__开头先检查format_spec是否为空,再按需分发;或直接调用内置类型同名方法(如str(self).__format__(format_spec)) - 不要在
__format__里 raiseValueError后不说明支持哪些 spec,用户根本不知道该传什么 - 如果类语义上就是数值(如带单位的
Duration),建议同时实现__int__/__float__,方便用户手动转再 format
format 和 f-string 的行为差异不止是性能
二者在语法糖层面相似,但语义不同:f-string 是编译期求值,format 是运行期解析。这导致一些边界情况表现不一致,比如作用域、异常时机、甚至字符串内容本身。
常见错误现象:f'{x}' 中 x 未定义,报 NameError 在表达式求值时;而 '{}'.format(x) 同样报 NameError,但发生在 format() 调用时 —— 如果这个调用被包在 try/except 里,就能捕获;f-string 的错误无法被运行时捕获。
- 嵌套大括号:
f'{{ {x} }}'要写两对大括号才输出一对;'{{ {} }}'.format(x)则只需一对外层大括号({和}在 format 字符串里需转义为{{和}}) - format 支持
!s/!r/!a转换标志,f-string 不支持(必须手动调str(x)或repr(x)) - format 的字段名支持属性链和下标,如
'{obj.items[0].name}'.format(obj=container);f-string 只支持表达式,等价写法是f'{container.items[0].name}',但调试时更难定位哪一级出错
真正复杂的地方在于 format_spec 的解析分散在各类型中,没有统一入口;你改了一个类的 __format__,可能影响下游所有用它做字段的 format 调用,而且很难测全。









