应根据失败是否可预期来选择:可预期且可恢复时返回none或哨兵值,否则抛异常;多值返回优先用命名元组或数据类;提前返回宜限于函数开头的守门检查;显式标注->none有助于防止误用。

返回 None 还是抛异常?看调用方是否能合理处理失败
Python 不强制函数必须有明确返回值,但随意返回 None 容易埋雷。比如 dict.get() 返回 None 是安全的——调用方本就预期“可能没找到”;但 json.loads() 遇到非法 JSON 直接抛 JSONDecodeError,因为解析失败不是“正常分支”,而是输入错误。
- 如果失败是业务逻辑中可预期、可恢复的情况(如查缓存未命中),返回
None或特殊哨兵值(如sentinel = object())更合适 - 如果失败意味着输入违法、状态异常或外部依赖不可用(如文件不存在、网络超时),该抛异常,别默默返回
None - 别在同一个函数里混用:一会儿返回
dict,一会儿返回None,又不加类型提示,调用方只能靠 try/except 猜
def find_user_by_id(user_id: int) -> Optional[User]:
# 明确标注可能为 None,调用方有心理准备
return db.query(User).filter(User.id == user_id).first()
多值返回要不要用 tuple?优先考虑命名元组或数据类
Python 支持 return a, b, c,解包也方便,但问题在于语义模糊:x, y = process() 中的 x 和 y 到底代表什么?几个月后连你自己都得翻源码。
- 简单脚本、临时计算、且返回值含义极其明确(如
divmod())可用 tuple - 一旦涉及两个以上值,或值有业务含义(如
status_code, headers, body),立刻改用namedtuple、dataclass或TypedDict -
typing.NamedTuple比普通namedtuple更好,支持类型注解,IDE 能补全,mypy 能校验
from typing import NamedTuple
class HttpResponse(NamedTuple):
status_code: int
headers: dict
body: bytes
<p>def fetch(url: str) -> HttpResponse:
...</p>
return 提前退出太多?说明函数职责过重
满屏 if ... return 看起来“简洁”,实则掩盖了逻辑分层问题。比如一个函数开头检查参数类型、非空、权限、配额、限流……每项失败都提前 return,后续主逻辑缩进深、难测试、难复用。
- 提前返回只适合守门人式检查(如
if not user: return),且应集中放在函数开头 - 权限、配额、限流等属于横切关注点,该抽成装饰器或中间件,别塞进业务函数里
- 如果不得不做多重校验,用 guard clause 模式:每个检查独立成行,失败统一抛特定异常(如
ValidationError),让上层决定怎么响应
def create_order(items: list, user: User) -> Order:
if not items:
raise ValueError("items cannot be empty")
if not user.is_active:
raise PermissionError("user is disabled")
# 主逻辑从这里开始,缩进浅、意图清
return Order.create(items, user)
类型提示写不写 -> None?写了比不写强,但别为了类型而类型
def cleanup() -> None: 看似多余,其实有用:它告诉调用方“别指望这个函数给你回东西”,也堵死了误用 result = cleanup() 的可能。但问题在于,很多人把 -> None 当成形式主义,反而漏掉真正该标注的返回类型。
立即学习“Python免费学习笔记(深入)”;
- 所有无显式
return的函数,Python 默认返回None,此时显式写-> None是好习惯 - 但如果你写了
-> None,却在某个分支里return "done",mypy 会报错——这反而是好事,逼你修正逻辑 - 别为了加类型提示硬拆函数:比如把
-> List[User]改成-> Optional[List[User]]只为覆盖空列表场景,不如让空列表本身成为合法返回值
最常被忽略的一点:返回值设计不是孤立问题,它和错误传播路径、调用方的防御性编程习惯、以及团队对“空值容忍度”的共识强相关。写完一个函数,不妨问自己一句:如果我三天后第一次调用它,看到这个返回类型,会不会犹豫要不要加 if result is not None:?










