Python 常见陷阱与最佳实践
识别 Python 里最容易踩的坑,写地道、健壮的 Python 代码
- 识别可变默认参数等经典陷阱
- 掌握 Pythonic 写法
- 学会防御性编程
- 理解 Python 的隐式行为
陷阱 1:可变默认参数
这是 Python 最著名的坑。默认参数在函数定义时只计算一次,不是每次调用时创建:
def 定义的默认参数,在模块加载时就会求值一次,之后每次调用函数都会共享同一个对象。None 是不可变的单例,每次进函数检查到 None,就创建新列表,这是标准写法。
陷阱 2:浮点数精度
陷阱 3:循环中修改列表
陷阱 4:闭包延迟绑定
陷阱 5:is vs ==
if x: 比 if x == True: 好;if not x: 比 if x == False: 好;if x is None: 和 if x is not None: 用于判断 None。不要写 if len(x) > 0:,直接写 if x:。
Pythonic 写法
Python 有自己的惯用法,写出「地道」的 Python 代码:
# 1. 用 with 管理资源
with open("file.txt") as f:
data = f.read()
# 2. 用 enumerate 同时取索引和值
for i, item in enumerate(items):
print(i, item)
# 3. 用 zip 同时遍历多个列表
for name, score in zip(names, scores):
print(name, score)
# 4. 用字典的 get 避免 KeyError
name = user.get("name", "匿名") # 不存在时返回默认值
# 5. 用 in 判断成员(不要用 find + 索引)
if "python" in text:
...
# 6. 多重赋值
a, b = b, a # 交换变量,不需要临时变量
# 7. 解包
first, *rest = [1, 2, 3, 4] # first=1, rest=[2,3,4]
# 8. 用 any/all
if all(score > 60 for score in scores): # 全部及格
...
if any(score < 60 for score in scores): # 有人不及格
...防御性编程清单
# 1. 输入校验
def get_age(data):
age = data.get("age")
if age is None:
raise ValueError("缺少 age 字段")
if not isinstance(age, (int, float)):
raise TypeError("age 应为数字,实际为 " + str(type(age)))
if age < 0 or age > 150:
raise ValueError("age 不合理: " + str(age))
return age
# 2. 早返回(Guard Clause)减少嵌套
def process(user):
if user is None:
return
if not user.is_active:
return
# 正常逻辑...
# 3. 不要裸 except
try:
risky()
except Exception as e:
logger.error("risky failed: %s", e)
raise
# 4. 类型注解
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("除数不能为0")
return a / b写函数前先想清楚输入输出和异常情况。对外部数据(用户输入、API返回、文件内容)永远不信任。错误要明确、具体、可操作。日志记录足够上下文(谁、什么时候、什么数据、什么错误)。写完代码先自己审查一遍再提交。
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
常见根因:内存泄漏导致 OOM 被系统杀掉、连接/文件句柄没关闭耗尽、事件循环里混入同步阻塞、未捕获异常让进程退出。12-Factor App 的建议很实用:配置环境化、日志当成事件流输出、进程保持无状态(状态放数据库/缓存),这样才能随时加副本做水平扩展。
挑战任务
找出陷阱
直接看代码,找里面的 Python 经典陷阱,把所有问题都修好,再给每个陷阱说清楚为啥会出问题。
课后作业
代码审查练习
找你之前写的 Python 程序,检查这几个问题:可变默认参数、裸except、循环中修改列表、浮点数直接比较、过度嵌套。列出来,再修复。