← 返回题目列表

Python 字符串格式化有几种方式?f-string 和 % / format 有什么区别?

高频 中等 第 15 / 22 题 更新于 2026/07/31
Pythonf-string字符串格式化format

简化版

**Python 字符串格式化主要有四种方式,推荐用 f-string(Python 3.6+):① % 格式化(老式,"%s=%d" % (name, age),类似 C 的 printf);② str.format()"{}={}".format(name, age),用 {} 占位);③ f-string(f-字符串,f"{name}={age}",把表达式直接写进大括号,最简洁、最快);④ 模板字符串 string.Template$name 占位,安全但功能弱,用于用户提供模板的场景)。**f-string 的优势:① 直观——变量/表达式直接写在 {} 里,一眼看懂;② 快——编译期就解析成拼接,运行时最快;③ 功能全——支持格式规格({x:.2f}{n:,}{x:>10})、{x!r} 转 repr、{x=} 调试(3.8+,打印 x=值)。格式规格 mini-language(三种方式共用)::.2f 保留 2 位小数、:, 千分位、:>10/:<10/:^10 右/左/居中对齐补宽、:05d 补零、:x 十六进制、:% 百分比。核心记忆:优先用 f-string(简洁快功能全),格式规格 {值:格式} 三种方式通用;% 是老式、Template 用于安全场景。

详细版

四种格式化方式对比

方式语法特点
% 格式化"%s is %d" % (s, n)老式、类 C,简单但易错
str.format()"{} is {}".format(s, n)灵活、可复用模板
f-stringf"{s} is {n}"最简洁、最快、功能全(3.6+)
TemplateTemplate("$s").substitute(s=x)安全、用于用户模板
name, age, pi = "Tom", 25, 3.14159

# ① % 格式化(老式)
print("%s is %d" % (name, age))          # Tom is 25
print("%.2f" % pi)                        # 3.14

# ② str.format()
print("{} is {}".format(name, age))       # 位置
print("{n} is {a}".format(n=name, a=age)) # 命名
print("{0} {1} {0}".format("a", "b"))     # 索引复用 → a b a

# ③ f-string(推荐)
print(f"{name} is {age}")                 # Tom is 25
print(f"{name} is {age * 2}")             # 表达式:Tom is 50
print(f"{pi:.2f}")                        # 3.14
print(f"{age=}")                          # age=25(3.8+ 调试)
print(f"{name!r}")                        # 'Tom'(repr)

# 格式规格(三种方式通用的 mini-language)
print(f"{1234567:,}")                     # 1,234,567(千分位)
print(f"{0.25:.1%}")                      # 25.0%(百分比)
print(f"{42:>10}")                        # 右对齐宽10:"        42"
print(f"{42:05d}")                        # 补零:"00042"
print(f"{255:x}")                         # ff(十六进制)

⚠️ f-string 之所以最快,是因为它在「编译期」就被解析成字符串拼接操作,运行时不需要像 %.format() 那样解析格式字符串、查找参数——所以既最快又最直观。但要注意几个坑:① f-string 的表达式在定义时立即求值(不是延迟到打印时),所以它不能像 Template 那样存起来当「可复用模板」;② 不要用 f-string 拼接不可信输入到 SQL / shell 命令(那是注入漏洞,SQL 要用参数化查询、shell 要用参数列表);③ 日志建议用 logging% 惰性格式log.info("x=%s", x) 只在真要输出时才格式化,比 log.info(f"x={x}") 省开销);④ f-string 里想要字面大括号用 {{ }}。选型:日常输出/拼接一律 f-string;用户提供的模板文本用 Template.format 会执行 {} 里的属性访问、有风险);日志用 % 惰性

完整版教学

一、四种格式化方式

先认识四种方式各自的样子:

① % 格式化(printf 风格,最老):
   "%s" % x           # 字符串
   "%d" % n           # 整数
   "%.2f" % f         # 浮点2位
   "%s and %s" % (a, b)   # 多个用元组
   来源:C 的 printf,Python 元老级语法

② str.format()(Python 2.6+):
   "{}".format(x)              # 自动位置
   "{0} {1}".format(a, b)     # 索引
   "{name}".format(name=x)    # 命名
   "{:.2f}".format(f)         # 格式规格
   灵活,模板可先写好后填

③ f-string(Python 3.6+,推荐):
   f"{x}"                     # 变量
   f"{a + b}"                 # 表达式
   f"{obj.attr}"              # 属性/方法/下标
   f"{x:.2f}"                 # 格式规格
   最简洁:占位处直接写表达式

④ string.Template(安全场景):
   from string import Template
   Template("$name").substitute(name=x)
   只支持简单 $var 替换,不执行代码 → 安全
   用于:用户提供的模板文本

演进:% → format → f-string,越来越简洁强大
Template 是并列的"安全特化"分支

所以四种:% (老式 printf)、format(灵活模板)、f-string(最简推荐)、Template(安全)

四种方式:% 格式化(printf 风格、最老,"%s"%x"%.2f"%f、多个用元组,来源 C 的 printf);② str.format()(Py2.6+,"{}".format(x) 自动位置、"{0}" 索引、"{name}" 命名,灵活可复用模板);③ f-string(Py3.6+ 推荐,f"{x}"f"{a+b}" 表达式、f"{x:.2f}",占位处直接写表达式最简洁);④ string.Template(安全场景,$name 占位、只支持简单替换不执行代码、用于用户提供的模板)。演进 % → format → f-string 越来越简洁强大,Template 是并列的「安全特化」分支。理解「四种:%(老式 printf)、format(灵活模板)、f-string(最简推荐 3.6+)、Template(安全 $var);演进 %→format→f-string 越来越简洁」,就认识了四种方式。

二、f-string 为什么推荐

理解 f-string 的三大优势:

f-string 的三大优势:

① 直观——表达式直接写在占位处
   f"{user.name} 有 {len(items)} 件商品,总价 {price:.2f} 元"
   → 一眼看懂哪个变量放哪,不用对着 format 的参数数位置
   对比 format:
   "{} 有 {} 件,总价 {:.2f}".format(user.name, len(items), price)
   → 要在脑子里对应占位符和参数,容易错位

② 快——编译期解析成拼接
   f-string 在编译时就转成等价的字符串拼接/构造操作
   运行时不需要解析格式串、不需要查参数
   → 比 % 和 .format() 都快(基准测试通常快 1.5~2 倍)

③ 功能全——支持所有格式规格 + 独有特性
   格式规格:f"{x:.2f}"、f"{n:,}"、f"{s:>10}"
   转换:f"{x!r}"(repr)、f"{x!s}"(str)、f"{x!a}"(ascii)
   调试(3.8+):f"{x=}" → 输出 "x=值"(打印变量名和值)
   嵌套:f"{x:{width}.{prec}f}"(宽度/精度也用变量)

f-string 里能写什么:
  变量、算术、函数调用、属性、下标、三元、推导式...
  f"{'偶' if n%2==0 else '奇'}"
  f"{[i**2 for i in range(3)]}"
  几乎任意表达式(不能有反斜杠转义、3.12 前不能同引号嵌套)

所以 f-string 优势:直观(表达式就地写)+快(编译期拼接)+功能全(格式规格/调试)

f-string 的三大优势① 直观——表达式直接写在占位处(f"{user.name} 有 {len(items)} 件" 一眼看懂哪个变量放哪,format 要对应占位符和参数容易错位);② 快——编译期解析成拼接(编译时转成等价拼接操作、运行时不解析格式串不查参数,比 % 和 format 快约 1.5~2 倍);③ 功能全——支持所有格式规格({x:.2f}{n:,})+ 转换({x!r} repr、{x!s} str)+ 调试(3.8+ {x=} 输出「x=值」)+ 嵌套({x:{width}.{prec}f})。f-string 里能写几乎任意表达式(变量/算术/函数/属性/下标/三元/推导式)。理解「f-string 优势:①直观(表达式就地写不用对参数)②快(编译期拼接快 1.5-2 倍)③功能全(格式规格+!r 转换+{x=}调试+嵌套);里面能写几乎任意表达式」,就理解了为什么推荐 f-string。

三、格式规格 mini-language

理解三种方式通用的格式规格:

格式规格(format spec):冒号后面的部分 {值:格式}
  三种方式(%除外,%用自己的语法)通用:
  f"{x:格式}"、"{:格式}".format(x)

通用结构(按顺序,都可选):
  [[fill]align][sign][#][0][width][,][.prec][type]

① 精度/小数:.n
   f"{3.14159:.2f}"  → 3.14(2位小数)
   f"{3.14159:.0f}"  → 3(整数)

② 千分位:,  或 _
   f"{1234567:,}"    → 1,234,567
   f"{1234567:_}"    → 1_234_567

③ 对齐 + 宽度:< 左 > 右 ^ 居中
   f"{'hi':>10}"     → "        hi"(右对齐宽10)
   f"{'hi':<10}"     → "hi        "
   f"{'hi':^10}"     → "    hi    "
   f"{'hi':*^10}"    → "****hi****"(用*填充)

④ 补零:0 或 类型前加0
   f"{42:05d}"       → "00042"
   f"{3.14:08.2f}"   → "00003.14"

⑤ 进制/类型:
   f"{255:x}"        → ff(十六进制小写)
   f"{255:X}"        → FF
   f"{255:b}"        → 11111111(二进制)
   f"{255:o}"        → 377(八进制)
   f"{255:#x}"       → 0xff(带前缀)

⑥ 百分比:%
   f"{0.1234:.1%}"   → 12.3%(自动×100加%)

⑦ 正负号:+
   f"{5:+d}"         → +5
   f"{-5:+d}"        → -5

⑧ 千分位+小数+对齐组合:
   f"{1234.5:>12,.2f}" → "    1,234.50"

所以格式规格 {值:格式}:.2f 小数、, 千分位、>^< 对齐、05 补零、x 进制、% 百分比

格式规格(format spec) 是冒号后的部分 {值:格式},三种方式(% 除外)通用。通用结构(都可选):[[fill]align][sign][#][0][width][,][.prec][type]① 精度/小数 .n{3.14159:.2f}→3.14);② 千分位 ,_{1234567:,}→1,234,567);③ 对齐+宽度 <>^居中{'hi':>10}{'hi':*^10}→“hi” 用 * 填充);④ 补零 0{42:05d}→00042);⑤ 进制/类型{255:x}→ff、{255:b}→二进制、{255:#x}→0xff);⑥ 百分比 %{0.1234:.1%}→12.3%);⑦ 正负号 +{5:+d}→+5);⑧ 组合{1234.5:>12,.2f})。理解「格式规格{值:格式}:.2f 小数、,千分位、><^对齐+宽度、05 补零、x/b/o 进制、#带前缀、%百分比、+正负号,可组合」,就掌握了格式规格。

四、% 格式化——老式但仍常见

理解 % 格式化及其在哪还常用:

% 格式化(printf 风格):
  "%[flags][width][.prec]type" % 值

常见转换符:
  %s  字符串(对任意对象调 str())
  %r  repr()
  %d  整数
  %f  浮点(%.2f 两位)
  %x  十六进制
  %%  字面 %

单个 vs 多个:
  "%s" % x            # 单个直接
  "%s %s" % (a, b)    # 多个用元组
  "%(name)s" % {"name": x}   # 命名用字典

% 的坑:
  ① 单个元组值要小心:
     "%s" % (1,)  → "1",但 "%s" % (1,2) → 报错(太多参数)
     → 想传元组本身要 "%s" % ((1,2),)
  ② 类型不匹配报错:%d 传字符串 → TypeError

% 现在还在哪用(别急着淘汰):
  ① logging 惰性格式(重要):
     log.info("user %s did %s", user, action)
     → 只有日志真要输出时才格式化(DEBUG 关闭时零开销)
     → 比 log.info(f"user {user}...") 省(f-string 总会先算)
  ② 遗留代码、C 风格偏好

所以 % 是老式 printf 风格,logging 惰性格式仍推荐用它(省开销)

% 格式化(printf 风格)"%[flags][width][.prec]type" % 值。常见转换符:%s(字符串、调 str)、%r(repr)、%d(整数)、%f(浮点、%.2f 两位)、%x(十六进制)、%%(字面 %)。单个直接 "%s" % x、多个用元组 "%s %s" % (a,b)、命名用字典 "%(name)s" % {...}% 的坑① 单个元组值要小心("%s" % (1,2) 报错、想传元组本身要 ((1,2),))、② 类型不匹配报错% 现在还在哪用① logging 惰性格式(重要)log.info("user %s did %s", user, action)——只有日志真要输出时才格式化(DEBUG 关闭时零开销,比 log.info(f"...") 省,因 f-string 总会先算);② 遗留代码、C 风格偏好。理解「%老式 printf 风格:%s/%r/%d/%f/%x;多个用元组、命名用字典;坑单个元组要小心;★logging 惰性格式仍推荐用%(log.info(‘%s’,x)只在要输出时才格式化省开销)」,就掌握了 % 格式化。

五、选型与安全

理解不同场景该用哪种、安全注意:

选型指南:
  日常输出/拼接        → f-string(简洁快功能全)
  日志                → % 惰性(log.info("%s", x))
  用户提供的模板文本    → string.Template(安全)
  需要模板复用/延迟填充  → str.format() 或 Template
  遗留代码            → 维持 % 或按需迁移

安全注意(重要):

① 别用 f-string / format 拼 SQL:
   ✗ f"SELECT * FROM users WHERE name='{name}'"  # SQL 注入!
   ✓ cursor.execute("SELECT * FROM users WHERE name=%s", (name,))
     # 参数化查询(这里的 %s 是 DB 驱动的占位,不是字符串格式化)

② 别用 f-string 拼 shell 命令:
   ✗ os.system(f"rm {filename}")  # 命令注入!
   ✓ subprocess.run(["rm", filename])  # 参数列表

③ str.format() 的注入风险:
   用户控制格式字符串时,format 能访问对象属性:
   "{x.__class__}".format(x=obj)  # 泄露内部
   "{0.__init__.__globals__}"     # 可能泄露敏感数据
   → 用户提供模板一定用 Template(只做 $var 替换,不执行)

④ f-string 的求值时机:
   f"{x}" 定义时立即求值 → 不能当"模板"存起来复用
   要延迟/复用用 format 或 Template

所以选型:日常 f-string、日志%惰性、用户模板 Template;别用 f-string 拼 SQL/shell

选型指南:日常输出/拼接用 f-string、日志用 % 惰性、用户提供的模板文本用 Template(安全)、需要模板复用/延迟填充用 format 或 Template、遗留代码维持 %。安全注意① 别用 f-string/format 拼 SQLf"...name='{name}'" 是 SQL 注入,要用参数化查询 cursor.execute("...=%s", (name,)));② 别用 f-string 拼 shellos.system(f"rm {filename}") 是命令注入,用 subprocess.run([...]) 参数列表);③ str.format 的注入风险(用户控制格式串时 format 能访问对象属性 "{0.__init__.__globals__}" 泄露敏感数据,用户模板一定用 Template);④ f-string 定义时立即求值(不能当模板存起来复用,要延迟用 format/Template)。理解「选型日常 f-string、日志%惰性、用户模板 Template;安全别用 f-string/format 拼 SQL(参数化查询)/shell(subprocess 列表)、format 用户控制格式串有注入风险(用 Template)、f-string 立即求值不能存复用」,就掌握了选型与安全。

六、总结与速查

总结字符串格式化:

四种方式速查:
  f-string   f"{x:.2f}"           日常首选(简洁快功能全,3.6+)
  format     "{:.2f}".format(x)   需要复用模板/延迟填充
  %          "%.2f" % x           日志惰性、遗留代码
  Template   Template("$x")       用户提供模板(安全)

f-string 独有特性:
  {x=}       调试(输出 "x=值",3.8+)
  {x!r}      repr 转换
  {x:{w}.{p}f}  嵌套(宽度精度用变量)
  表达式     f"{a+b}"、f"{func()}"

格式规格通用(f-string / format 共用):
  .2f  两位小数    ,    千分位     %    百分比
  >10  右对齐宽10  <10  左对齐    ^10  居中
  05d  补零       +d   带正负号   x/b/o 进制  #  带前缀

安全红线:
  别用 f-string/format 拼 SQL(参数化查询)
  别用 f-string 拼 shell(subprocess 列表)
  用户提供的模板用 Template(不执行代码)

核心总结:
  优先 f-string(简洁、最快、功能全)
  格式规格 {值:格式} 三种方式通用
  日志用 % 惰性、用户模板用 Template
  别拿格式化拼 SQL/shell(注入)

所以优先 f-string,格式规格{值:格式}通用,日志%惰性,安全别拼 SQL/shell

四种方式速查:f-string(f"{x:.2f}" 日常首选)、format("{:.2f}".format(x) 复用模板/延迟填充)、%("%.2f" % x 日志惰性/遗留)、Template($x 用户模板安全)。f-string 独有特性{x=} 调试、{x!r} repr、{x:{w}.{p}f} 嵌套、表达式。格式规格通用.2f 小数、, 千分位、% 百分比、>^< 对齐、05d 补零、+d 正负号、x/b/o 进制、# 前缀。安全红线:别用 f-string/format 拼 SQL(参数化查询)、别拼 shell(subprocess 列表)、用户模板用 Template。理解「速查 f-string 首选/format 复用/‰惰性/Template 安全;f-string 独有{x=}调试/!r/嵌套;格式规格通用;安全别拼 SQL/shell 用户模板 Template」,就掌握了总结与速查。

记忆钩子:「Python 字符串格式化四种:①%格式化(老式 printf,‘%s’%x、多个用元组,logging 惰性格式仍推荐 log.info(‘%s’,x)只在要输出时才算省开销)②str.format()(’{}‘.format(x)、‘{0}‘索引、‘{name}‘命名,可复用模板/延迟填充)③f-string(3.6+推荐 f’{x}‘,把表达式直接写进{}最简洁最快)④string.Template($name 只做简单替换不执行代码,用户提供模板用它最安全);★f-string 三大优势:直观(表达式就地写不用对参数)+快(编译期解析成拼接、比%和 format 快 1.5-2 倍)+功能全(独有{x=}调试输出’x=值’3.8+、{x!r}转 repr、{x:{w}.{p}f}嵌套);格式规格{值:格式}三种通用:.2f 小数、,千分位、%百分比、>^<对齐+宽度、05d 补零、x/b/o 进制、#前缀、+正负号;安全红线:别用 f-string/format 拼 SQL(用参数化查询)/shell(用 subprocess 列表)、用户控制的格式串用 Template(format 能访问__globals__泄露)、f-string 定义时立即求值不能当模板存」

七、常见误区与追问

  • 误区:f-string 只是语法糖,和 format 一样慢。 不一样——f-string 更快:它在编译期就被解析成等价的字符串拼接/构造操作,运行时不需要像 %.format() 那样解析格式字符串、查找和匹配参数;基准测试中 f-string 通常比 .format()% 快约 1.5~2 倍;所以 f-string 既最直观又最快,是日常首选。
  • 误区:所有格式化都应该换成 f-string。 不是——日志应该保留 % 惰性格式:log.info("user %s did %s", user, action) 只在日志级别真要输出时才格式化(DEBUG 关闭时零开销),而 log.info(f"user {user}...") 会无条件先算好 f-string 再传给 logging(浪费);用户提供的模板文本应该用 string.Template(安全);需要「先定义模板、后填充/复用」的场景用 .format()
  • 误区:用 f-string 拼 SQL 查询很方便。 极其危险——f"SELECT * FROM users WHERE name='{name}'" 是 SQL 注入漏洞(如果 name 来自用户输入,可以构造恶意值篡改查询);正确做法是用数据库驱动的参数化查询 cursor.execute("... WHERE name=%s", (name,))(这里的 %s 是驱动的参数占位符、由驱动安全转义,不是字符串格式化);同理别用 f-string 拼 shell 命令(用 subprocess 的参数列表)。
  • 误区:str.format() 用在任何字符串上都是安全的。 不是——如果格式字符串本身来自用户输入,.format() 会成为漏洞:因为它能访问对象的属性和方法,"{0.__class__.__init__.__globals__}".format(obj) 之类的格式串能泄露全局变量、敏感数据;所以「用户能控制格式字符串」的场景绝不能用 .format(),要用 string.Template(只做简单的 $var 替换、不执行任何代码/属性访问)。
  • 追问:f-string 相比 % 和 format 有哪些独有的特性? 主要有:① 表达式就地嵌入——{} 里可以直接写任意表达式(f"{a+b}"f"{func()}"f"{obj.attr}"f"{'偶' if n%2==0 else '奇'}"),不用像 format 那样把值作为参数传;② 自文档化调试(3.8+)——f"{x=}" 会输出 x=值(同时打印变量名和值,调试神器);③ 嵌套格式规格——f"{x:{width}.{prec}f}"(宽度和精度也能用变量动态指定);④ 转换标志 {x!r}(repr)、{x!s}(str)、{x!a}(ascii);这些让 f-string 既简洁又强大。
  • 追问:格式规格里怎么实现「保留两位小数的千分位金额、右对齐补齐到 12 宽」? 用组合的格式规格 f"{amount:>12,.2f}"> 表示右对齐、12 是总宽度、, 是千分位分隔、.2f 是保留两位小数的浮点;比如 f"{1234.5:>12,.2f}" 得到 " 1,234.50"(前面补空格到 12 宽);格式规格的顺序是 [[fill]align][sign][#][0][width][grouping][.precision][type],各部分都可选、按这个顺序组合;这套 mini-language 在 f-string 和 .format() 里通用。
  • 追问:为什么 logging 推荐用 log.info("x=%s", x) 而不是 log.info(f"x={x}") 因为「惰性格式化」省开销:log.info("x=%s", x) 把格式字符串和参数分开传给 logging,只有当这条日志真的会被输出时(日志级别允许)logging 才会去做 % 格式化;如果当前级别是 INFO 而这是 DEBUG 日志、或这条日志被过滤掉了,格式化根本不发生、零开销;而 log.info(f"x={x}") 里 f-string 在调用前就已经无条件求值拼好了字符串(不管这条日志最终会不会输出),在高频日志、复杂对象的 repr 很贵时会造成明显浪费;所以 logging 是少数仍推荐用 % 的场景。

八、加强记忆

Python 字符串格式化主要四种,推荐 f-string% 格式化(老式 printf 风格,"%s"%x、多个用元组、%.2flogging 惰性格式仍推荐用它——log.info("%s", x) 只在日志真要输出时才格式化、省开销);str.format()"{}".format(x)"{0}" 索引、"{name}" 命名,可复用模板/延迟填充);③ f-string(3.6+ 推荐)f"{x}",把表达式直接写进 {} 最简洁);string.Template$name 只做简单替换不执行代码,用户提供的模板用它最安全)。f-string 三大优势:直观(表达式就地写)+ 快(编译期解析成拼接、比 % 和 format 快约 1.5~2 倍)+ 功能全(独有 {x=} 调试输出「x=值」、{x!r} 转 repr、{x:{w}.{p}f} 嵌套)。格式规格 {值:格式} 三种通用.2f 小数、, 千分位、% 百分比、>^< 对齐+宽度、05d 补零、x/b/o 进制、# 前缀、+ 正负号(可组合 {x:>12,.2f})。安全红线:别用 f-string/format 拼 SQL(用参数化查询)或 shell(用 subprocess 列表)、用户控制的格式串用 Template。一句话「优先 f-string(直观+编译期拼接最快+功能全 like {x=}调试);格式规格{值:格式}三种通用(.2f/,/%/对齐/进制);日志用%惰性(只在要输出时才算)、用户模板用 Template(安全);别拿格式化拼 SQL/shell(注入)」。