Python 字符串处理进阶——f-string 高级用法、模板引擎、文本格式化
Python 字符串处理进阶——f-string 高级用法、模板引擎、文本格式化
适读人群:Python 开发者、做数据处理和报告生成的工程师 | 阅读时长:约15分钟 | 核心价值:掌握工程级字符串处理全套方案
从一份"拼字符串地狱"说起
有个做运营的朋友小霞,她们公司每周要给客户发定制化报告邮件,模板里有客户名、数据指标、日期,100多个客户,每次都是手动改 Excel 表格,再一封封复制粘贴,每周至少要花半天时间。
她找到我,说能不能帮她做个自动化。我说当然可以。
但当我看了她们之前一个实习生写的"自动化"脚本时,我沉默了——200行代码,全是:
body = "尊敬的" + client_name + ",\n您好,本周" + week + "的数据如下:\n" + "销售额:" + str(sales) + "元\n" + "增长率:" + str(growth_rate * 100) + "%\n"这不叫自动化,这叫把手动劳动变成了"手动打字劳动"。
重构之后,核心代码不超过30行,而且可扩展、可维护。这次重构让我系统梳理了一遍 Python 字符串处理的最佳实践,今天完整分享给大家。
一、f-string 高级用法——你可能只用了20%
f-string 是 Python 3.6 引入的,但大多数人只用到了 {变量名} 这一层,进阶用法知道的人不多。
格式化规范(Format Spec)
# 数字格式化
price = 12345.678
ratio = 0.1234
count = 1234567
print(f"价格: {price:,.2f}") # 12,345.68(千位分隔,2位小数)
print(f"占比: {ratio:.1%}") # 12.3%(百分比)
print(f"数量: {count:,}") # 1,234,567(千位分隔)
print(f"科学: {price:.2e}") # 1.23e+04(科学计数)
print(f"填充: {count:0>12,}") # 0001,234,567(右对齐,0填充)
# 字符串对齐
for name, score in [("张三", 95), ("李四", 88), ("王五", 100)]:
bar = "█" * (score // 5)
print(f"{name:4s} {score:3d} {bar}")
# 日期格式化
from datetime import datetime
now = datetime.now()
print(f"当前时间: {now:%Y年%m月%d日 %H:%M:%S}")
# 条件表达式
user_level = 3
print(f"用户级别: {'VIP' if user_level >= 3 else '普通'}会员")
# 嵌套 f-string(Python 3.12+ 支持更复杂的嵌套)
width = 20
for item in ["苹果", "香蕉", "草莓"]:
print(f"{'商品: ' + item:>{width}}")调试利器:= 修饰符(Python 3.8+)
x = 42
y = [1, 2, 3]
name = "老张"
# 自动输出变量名和值,调试时非常好用
print(f"{x=}") # x=42
print(f"{y=}") # y=[1, 2, 3]
print(f"{name=}") # name='老张'
print(f"{x + y[0]=}") # x + y[0]=43
print(f"{len(name)=}") # len(name)=2
# 配合格式规范
total = 1234567.89
print(f"{total=:,.2f}") # total=1,234,567.89二、字符串模板引擎对比
string.Template——安全模板
from string import Template
# 适合用户输入的场景,$ 语法,不执行代码,更安全
template = Template(
"尊敬的 $client_name,\n"
"本周($week)您的销售额为 $sales 元,\n"
"环比增长 $growth_rate。\n"
"感谢您的信任!"
)
data = {
"client_name": "张总",
"week": "第3周",
"sales": "125,680",
"growth_rate": "+12.3%",
}
print(template.substitute(data))
# safe_substitute:遇到缺少的变量不报错,保留原始占位符
incomplete_data = {"client_name": "李总", "week": "第4周"}
print(template.safe_substitute(incomplete_data))
# 缺少的 $sales 和 $growth_rate 会原样保留Jinja2——工程级模板引擎
对于复杂的报告生成、代码生成、HTML 渲染,Jinja2 是标准选择:
pip install jinja2from jinja2 import Environment, FileSystemLoader, select_autoescape
from pathlib import Path
import json
# 方式1:从字符串创建模板
from jinja2 import Template
email_template = Template("""
尊敬的{{ client_name }},
本周数据摘要({{ week }}):
{% for metric in metrics %}
- {{ metric.name }}:{{ metric.value | format_number }}{% if metric.change > 0 %} ↑{{ metric.change }}%{% elif metric.change < 0 %} ↓{{ metric.change | abs }}%{% endif %}
{% endfor %}
{% if alerts %}
注意事项:
{% for alert in alerts %}
⚠️ {{ alert }}
{% endfor %}
{% endif %}
如有疑问,请联系您的专属顾问。
老张数据团队
""")
data = {
"client_name": "张总",
"week": "2024年第3周",
"metrics": [
{"name": "销售额", "value": 125680, "change": 12.3},
{"name": "新客数", "value": 58, "change": -5.2},
{"name": "复购率", "value": 0.68, "change": 2.1},
],
"alerts": ["库存预警:SKU-2341 库存不足7天"],
}
# 注册自定义过滤器
env = Environment()
def format_number(value):
if isinstance(value, float) and value < 1:
return f"{value:.0%}"
return f"{value:,.0f}"
env.filters["format_number"] = format_number
# 方式2:从文件加载模板(推荐用于大型项目)
TEMPLATE_DIR = Path("templates")
TEMPLATE_DIR.mkdir(exist_ok=True)
# 创建模板文件
(TEMPLATE_DIR / "weekly_report.html").write_text("""
<!DOCTYPE html>
<html>
<head><title>周报 - {{ client_name }}</title></head>
<body>
<h1>{{ client_name }} 周度报告</h1>
<p>统计周期:{{ week }}</p>
<table border="1">
<thead>
<tr><th>指标</th><th>数值</th><th>变化</th></tr>
</thead>
<tbody>
{% for m in metrics %}
<tr>
<td>{{ m.name }}</td>
<td>{{ m.value | format_number }}</td>
<td style="color: {{ 'green' if m.change > 0 else 'red' }}">
{{ '%+.1f' % m.change }}%
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
""", encoding="utf-8")
file_env = Environment(
loader=FileSystemLoader(str(TEMPLATE_DIR)),
autoescape=select_autoescape(["html", "xml"]),
)
file_env.filters["format_number"] = format_number
template = file_env.get_template("weekly_report.html")
html = template.render(**data)
print(html[:300])三、文本格式化——实用工具集
textwrap——自动折行和缩进
import textwrap
long_text = """Python 是一门高级编程语言,以其简洁的语法和强大的生态系统著称。它被广泛用于数据科学、人工智能、Web 开发、自动化脚本等领域。"""
# 自动折行
wrapped = textwrap.fill(long_text, width=40)
print(wrapped)
# 缩进
indented = textwrap.indent(long_text, prefix=" | ")
print(indented)
# 去除公共缩进(处理多行字符串的利器)
code = """
def hello():
print("Hello, World!")
return True
"""
dedented = textwrap.dedent(code)
print(repr(dedented))
# 截断长文本
short = textwrap.shorten(long_text, width=50, placeholder="...")
print(short) # "Python 是一门高级编程语言,以其简洁的语法..."表格格式化
from tabulate import tabulate # pip install tabulate
data = [
["张三", 95, "优秀", "2024-01-15"],
["李四", 78, "良好", "2024-01-16"],
["王五", 62, "及格", "2024-01-17"],
]
headers = ["姓名", "成绩", "评级", "考试日期"]
# 多种表格样式
print(tabulate(data, headers=headers, tablefmt="grid"))
print(tabulate(data, headers=headers, tablefmt="pipe")) # Markdown 格式
print(tabulate(data, headers=headers, tablefmt="github")) # GitHub 风格
# 对齐控制
print(tabulate(
data,
headers=headers,
tablefmt="simple",
colalign=("left", "right", "center", "right")
))四、踩坑实录
踩坑实录1:f-string 中的引号嵌套问题
现象:f-string 里需要用字典访问,导致引号冲突,语法错误。
原因:f-string 内外层使用了相同的引号类型。
解法:外层用双引号,内层用单引号;或者先把值赋给变量。
data = {"name": "老张", "score": 95}
# 错误写法
# print(f"姓名: {data["name"]}") # SyntaxError
# 正确写法1:混用引号
print(f"姓名: {data['name']}")
# 正确写法2:提前赋值(更清晰)
name = data["name"]
print(f"姓名: {name}")
# Python 3.12+ 允许在 f-string 中使用相同引号
# print(f"姓名: {data["name"]}") # 3.12+ 才支持踩坑实录2:Jinja2 模板自动转义破坏 HTML
现象:往 Jinja2 模板里传入 HTML 片段,输出结果里 < 变成了 <。
原因:开启了 autoescape,防 XSS 的同时把合法 HTML 也转义了。
解法:用 | safe 过滤器标记可信 HTML,或者用 Markup 类包装。
from jinja2 import Markup
# 在模板里
# {{ html_content | safe }}
# 在 Python 代码里
safe_html = Markup("<b>这是安全的HTML</b>")
template.render(content=safe_html) # 不会被转义踩坑实录3:多行 f-string 里忘加括号,变成了字符串拼接
现象:多行输出缺少换行,所有内容连在一行。
原因:Python 的字符串字面量自动拼接特性,误把两个独立字符串拼在一起了。
解法:多行 f-string 用括号包裹,或者用 \n 显式换行。
五、选型建议
| 场景 | 推荐方案 |
|---|---|
| 简单变量插值 | f-string |
| 用户提供的模板 | string.Template(更安全) |
| 报告/邮件/HTML 生成 | Jinja2 |
| 命令行输出格式化 | tabulate + textwrap |
| 日志格式化 | logging 模块的 Format |
| 代码生成 | Jinja2 |
小霞的那个报告系统,最终用 Jinja2 做模板,f-string 做细节格式化,tabulate 做表格,整个系统不到100行,每周全自动运行,她现在可以用那半天时间去做更有价值的工作了。
