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