你有没有写过这样的代码:
name = "张三"
age = 25
city = "北京"
# 很多人这样写
result = "姓名:" + name + ",年龄:" + str(age) + ",城市:" + city
然后代码review的时候,老大看了三秒,说了句:"你知道现在都2024年了吗?"
你愣住了。我也愣住了。
直到后来我发现,Python老手写字符串拼接,用的都是这种骚操作...
从痛苦到解脱:字符串格式化的进化史
第一阶段:原始社会的+号拼接
就像我刚学Python时那样,很多人用+号:
# ❌ 别这么写了,求你了
first_name = "小明"
last_name = "王"
full_name = first_name + last_name # 王小明
# 看起来还行,对吧?但是遇到数字就懵了
age = 18
message = "我今年" + age + "岁" # TypeError: can only concatenate str (not "int") to str
痛点有多痛:
- 手动转类型:
str(age) - 代码像意大利面,可读性差
- 性能差(每次拼接都创建新字符串对象)
第二阶段:%格式化(听起来很老对吧)
# ✅ 比+号强一点,但依然丑陋
name = "李四"
age = 30
message = "我叫%s,今年%d岁" % (name, age)
# 如果参数多了...
message = "我叫%s,今年%d岁,住在%s,月薪%d元" % (name, age, city, salary)
# 这里的参数顺序千万别搞错,不然就等着debug吧
依然存在的问题:
- 顺序敏感,容易搞混
- %s、%d、%f...记不住啊
- 不支持字典变量,很难维护
第三阶段:str.format()(曾经过渡的王者)
# ✅ 这么写已经很不错了
name = "王五"
age = 28
message = "我叫{},今年{}岁".format(name, age)
# 还可以指定位置
message = "我叫{0},今年{1}岁,{0}你好".format(name, age)
# 甚至可以用关键字参数
message = "我叫{name},今年{age}岁".format(name=name, age=age)
format()的好处:
- 顺序不敏感
- 支持关键字参数,可读性up
- 支持复杂的格式化选项
但...依然有点啰嗦,对吧?
第四阶段:f-string(真香警告)
# ✅ 终极形态,简洁到爆炸
name = "赵六"
age = 32
message = f"我叫{name},今年{age}岁" # 就这么简单!
# 还能直接写表达式
score = 85
message = f"考试得分:{score},{'及格' if score >= 60 else '挂科'}"
# 甚至可以调用函数
def get_status(score):
return "优秀" if score >= 90 else "良好" if score >= 80 else "及格"
message = f"评级:{get_status(score)}"
f-string的优雅之处:
- 语法简洁,
f"{}"一眼就懂 - 变量直接嵌入,不用记顺序
- 支持表达式和函数调用
- 性能比format()和%都快
实战对比:让你看到差距
假设你要生成一个用户信息报告:
user = {
"id": 1001,
"name": "张三丰",
"email": "zhangsanfeng@wudang.com",
"balance": 8888.88,
"level": "VIP",
"register_date": "2024-01-15"
}
# ❌ +号写法(代码地狱)
report = "用户ID:" + str(user["id"]) + "\n"
report += "姓名:" + user["name"] + "\n"
report += "邮箱:" + user["email"] + "\n"
report += "余额:" + str(user["balance"]) + "元\n"
report += "等级:" + user["level"] + "\n"
report += "注册时间:" + user["register_date"] + "\n"
# ❌ %格式化(参数地狱)
report = "用户ID:%d\n姓名:%s\n邮箱:%s\n余额:%.2f元\n等级:%s\n注册时间:%s\n" % (
user["id"], user["name"], user["email"], user["balance"], user["level"], user["register_date"]
)
# ❌ format()写法(还是有点啰嗦)
report = "用户ID:{id}\n姓名:{name}\n邮箱:{email}\n余额:{balance:.2f}元\n等级:{level}\n注册时间:{register_date}\n".format(
id=user["id"],
name=user["name"],
email=user["email"],
balance=user["balance"],
level=user["level"],
register_date=user["register_date"]
)
# ✅ f-string写法(优雅到哭)
report = f"""用户ID:{user["id"]}
姓名:{user["name"]}
邮箱:{user["email"]}
余额:{user["balance"]:.2f}元
等级:{user["level"]}
注册时间:{user["register_date"]}"""
看出差距了吗?f-string就像在聊天,其他的就像在写说明书。
高级骚操作:f-string的隐藏技能
1. 数字格式化
price = 1234.5678
# 保留两位小数
print(f"价格:{price:.2f}") # 价格:1234.57
# 千分位分隔符
print(f"价格:{price:,}") # 价格:1,234.5678
# 百分比
percentage = 0.2567
print(f"完成率:{percentage:.1%}") # 完成率:25.7%
# 对齐
name = "张三"
print(f"姓名:{name:>10}") # 右对齐,占10位
print(f"姓名:{name:<10}") # 左对齐,占10位
print(f"姓名:{name:^10}") # 居中,占10位
2. 日期时间格式化
from datetime import datetime
now = datetime.now()
print(f"当前时间:{now:%Y-%m-%d %H:%M:%S}")
print(f"今天星期:{now:%A}")
print(f"今年第几天:{now:%j}")
3. 调试模式(Python 3.8+)
x = 10
y = 20
# 普通写法
print(f"x={x}, y={y}, sum={x+y}")
# 调试模式写法(f-string的f前面再加个=)
print(f"{x=}, {y=}, {x+y=}")
# 输出:x=10, y=20, x+y=30
4. 嵌套f-string(骚到飞起)
name = "孙悟空"
weapon = "金箍棒"
# 动态生成格式字符串
template = f"我叫{name},武器是{weapon}"
print(template)
# 甚至可以嵌套
padding = 10
name = "猪八戒"
print(f"{'#' * padding}{name}{'#' * padding}")
# ##########猪八戒##########
性能对比:用数据说话
让我用一个简单的性能测试:
import timeit
name = "测试用户"
age = 25
# +号拼接
def plus_concat():
return "姓名:" + name + ",年龄:" + str(age)
# %格式化
def percent_format():
return "姓名:%s,年龄:%d" % (name, age)
# format()方法
def str_format():
return "姓名:{},年龄:{}".format(name, age)
# f-string
def f_string():
return f"姓名:{name},年龄:{age}"
# 测试性能
print("性能测试(执行100万次):")
print(f"+号拼接:{timeit.timeit(plus_concat, number=1000000):.4f}秒")
print(f"%格式化:{timeit.timeit(percent_format, number=1000000):.4f}秒")
print(f"format():{timeit.timeit(str_format, number=1000000):.4f}秒")
print(f"f-string:{timeit.timeit(f_string, number=1000000):.4f}秒")
测试结果(大概):
- +号拼接:0.8秒
- %格式化:0.6秒
- format():0.5秒
- f-string:0.3秒
f-string不仅代码简洁,性能也是最快的!
什么时候不用f-string?
虽然f-string很香,但也不是万能的:
1. Python版本限制
f-string是Python 3.6+才有的,如果你的项目还在用Python 2.7或者3.5,那就老老实实用format()吧。
2. 模板系统
如果你在做复杂的模板系统(比如邮件模板、报告模板),建议用专门的模板引擎如Jinja2,而不是硬编码f-string。
3. 国际化
做多语言支持时,用专门的国际化库比f-string更合适。
真实项目中的最佳实践
日志记录
import logging
# ❌ 这样记录日志,性能差
logging.debug("用户" + username + "执行了" + action + "操作")
# ✅ 好一点
logging.debug("用户%s执行了%s操作", username, action)
# ✅ 更好(Python 3.8+)
logging.debug(f"用户{username}执行了{action}操作")
SQL查询
# ❌ 永远不要这样(SQL注入风险!)
sql = f"SELECT * FROM users WHERE name = '{user_input}'"
# ✅ 使用参数化查询
sql = "SELECT * FROM users WHERE name = %s"
cursor.execute(sql, (user_input,))
配置文件生成
config = {
"host": "localhost",
"port": 8080,
"debug": True
}
# 生成配置文件
config_content = f"""# 服务器配置
host = {config["host"]}
port = {config["port"]}
debug = {config["debug"]}"""
with open("config.ini", "w", encoding="utf-8") as f:
f.write(config_content)
总结
记住一句话:代码是写给人看的,顺便给机器执行。
f-string的优雅不在于性能多快,而在于它最接近人类的思维方式。
下次再写字符串拼接,直接上f-string,别犹豫了!
你在项目中是怎么处理字符串拼接的?
评论区聊聊,看看有没有更骚的操作。
(如果还在用+号的同学,别害羞,承认吧,我们都经历过那个阶段 😄)