应根据契约是否被破坏决定返回none或raise异常:none表示合法的空状态(如查无此人),raise表示非法执行路径(如io失败);混用会导致调用方需双重防御,且易引发静默错误。

函数该返回None还是raise异常,取决于契约是否被破坏
Python函数默认返回None不是bug,是设计;但用None掩盖本该失败的逻辑,就是反模式。关键看调用方是否需要区分「没结果」和「出错了」。
比如查询用户:get_user_by_id(999)——如果数据库里真没这个ID,返回None合理;但如果网络超时、连接被拒绝、SQL语法错误,还返回None,调用方就完全无法感知故障,只能靠后续AttributeError暴露问题,排查成本陡增。
-
None适合表示「合法的空状态」:查无此人、列表为空、配置未设置 -
raise适合表示「非法的执行路径」:参数类型错、IO失败、权限不足、数据不一致 - 混用会导致调用方被迫写大量
if result is None:+try/except双重防御,既啰嗦又漏判
递归函数里忘写return result,是None最典型的“静默陷阱”
递归调用本身不自动转发返回值。常见错误是写了login(log)却没加return login(log),导致上层拿到None而不是底层实际返回的True或False。
这种错误不会报错,运行时逻辑就悄悄失效。调试时print能看见底层返回了值,但最终变量却是None,容易误判为条件没进分支。
- 所有递归调用点,只要期望传递结果,必须显式
return func(...) - 静态检查工具(如mypy)对这类遗漏识别有限,靠人工review容易漏
- 用IDE的「show return type」功能可快速发现某分支缺少return路径
用is None判断 vs 布尔判断,选错就埋雷
if not user: 和 if user is None: 完全不是一回事。空字符串""、空列表[]、数值0在布尔上下文中都是False,但它们不是None。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
如果你的函数约定「找不到时返回None,找到时返回非空对象」,那必须用is None判断;若用if not user,可能把合法的user.name = ""也当成「未找到」。
- 返回
None的函数,调用方应统一用result is None做存在性检查 - 返回「可能为假值的对象」的函数(如字符串、数字),需在文档里明确说明语义,避免歧义
-
is not None比!= None更安全,后者在重载__eq__时可能出意外
类型提示不标-> None,会让调用方误以为函数有返回值
写def log_message(msg): print(msg)却不加-> None,mypy默认推断返回类型是Any,调用方可能放心地写x = log_message("hi"),结果x是None,后续调用x.upper()才爆AttributeError。
显式标注-> None不只是风格问题,它让类型检查器能提前捕获「把无返回函数当有返回值用」的错误。
- 所有不返回有效数据的函数,都应加上
-> None - 用
typing.NoReturn标记那些永远不正常返回的函数(如总raise SystemExit) - 团队代码规范里,应把
-> None作为强制项,而非可选项
最常被忽略的其实是「返回None的意图是否被上下游共同理解」——它不像异常那样自带上下文,也不像类型提示那样能被机器校验。一旦契约模糊,None就成了藏在返回值里的幽灵。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










