这两种其实在python里属于防御性编程下属的两种风格

  • if...else属于LBYL(Look before you leap)
  • try...catch属于EAFP(easier to ask for forgiveness than permission)

就代码风格上差异来说,主要的差异点好像在于以下

总结

针对不同类型的业务代码,其实是根据场景都适用的。

  • 当业务逻辑中,异常场景不少见的时候,用LBYL处理更合适
  • 当业务逻辑中,大部分场景是走的正常分支,只有少部分时候由于环境、网络波动等因素出现异常时,用EAFP更合适。
    • 按python哲学, 是更推荐EAFP的, 但是也不排斥利用静态语言的特性做一定程度的防御式编程
    • 也有人更推荐LBYL[^5]

Python 的动态类型(duck typing)决定了 EAFP,而 Java等强类型(strong typing)决定了 LBYL。语言之间的设计哲学差异,Java 对类型要求非常严格,要求明确,类/方法等,它假定你应该知道,任何时候你正在使用的对象类型,以及它能做什么。相反,Python 的鸭子类型意味着你不必明确的知道对象的显示类型是什么,你只需要关心你在使用时候它能有相应的反馈。在这种宽松的限制下,唯一明确的态度就是认为代码会工作,准备面对结果。 这无关语言的好坏,每一门语言都有自己的哲学与态度,正确的对待,理解。

上面这句话说的很对, 对于python来说, 他提供了动态/静态语言的能力, 剩下的取决于开发者自身的运用了.

类型 预计成功比例 业务逻辑可阅读性 存在潜在不可控问题时的处理逻辑 预检查成本 多线程race condtion 风险
EAFP 高(异常拆分恰当) 省力. 通过父异常统一拦截
LBYL 高(在python3.6, mypy之后, 参数校验可从业务逻辑中去除) 费力. 需要一个个异常判定, 工作量大, 风险高 一般低, 偶尔高

EAFP

  • 一旦出现异常,存在性能上较差的问题,但是如果在正常逻辑,倒是没有固定成本
    • 即预期大多数时间会是成功的
  • 业务逻辑代码阅读会非常顺畅,所有的异常判断全部在业务逻辑外
    • 这里应该有一点前提,业务逻辑封装的足够合适,否则每行做异常封装的话,看上去也并不美观
  • 潜在不可控的问题: 如各种 io 操作,包括但不限于网络请求、文件读者、数据库 crud...
  • 预检查成本高: 如对下层产生的压力足以等同于正常请求, 为了性能可能更推荐EAFP.

FAQ

  • 如果是EAFP,因为未进行各种检查引起的问题,是否会需要回滚?
    • EAFP每个独立的exception针对对应的逻辑其实都应该做到幂等性, 进行独立rollback. 即EAFP的逻辑其实也是要拆分的恰当的, 不能通过一个大的try...catch...全包裹住, 异常
    • 如果拆分的足够合适,做了基本的入参检查之后,代码中在调用其他函数时,进行异常处理就可以.
      • 建议捕捉下层收到的系统层级异常,然后抛出一个自定义的异常. 而不是将底层异常直接上传. 因为每个逻辑层次的错误代表不同的含义, 如最下层收到的参数的类型错误, 对应上层其实是某个逻辑错误.

LBYL

  • 固定的检查耗时成本,但是其实消耗不大,因为if是最适合编译器和处理器做分支预测优化的部分了*。
    • 仅部分正常校验也会对下层业务产生较大压力时, 性能优化可解
  • 存在多线程race condition的潜在问题
  • 预检查会混杂在业务逻辑中
    • 不过这里其实也取决于代码内聚解耦能力, 如果封装的恰当,应该很多逻辑都是在函数入口封装掉,这样的话倒也不会影响到业务逻辑部分的阅读。
    • 参数类型检查, 在python3.6前的开始推行type annotion后, 通过typing特性的类型标注后, LBYL的参数检查可以不用再显式isinstance来判断了, 即解除了一个缺点: 参数检查会混杂在业务逻辑中的劣势.
  • 预计操作失败的时间占比较高时

静态分析对Exception的支持

好像在mypy里这个是在计划支持中, 具体进展暂未了解到. 但大致的趋势是2者皆可用. 并不绝对.

ceph社区的某个提交的意见

  • we should not use exception in the normal code path. in C++, exception is not designed to be efficient or semantically a language facility to be part of the normal code path. so, from the readability perspective, we should not use exception here. as all encoded KeyRings are in plaintext.

Reference

  1. Python Tips - 防御性编程风格 EAFP vs LBYL - 知乎
  2. 如何理解 EAFP 和 LBYL 两种编程风格的区别? - 知乎
  3. 方式 1 和方式 2 的却别到底在哪里? - V2EX
  4. Write Cleaner Python: Use Exceptions
  5. 13 – Joel on Software
  6. Python鸭子类型 duck typing_Keyboard Interrupt的博客-CSDN博客
  7. Python and EAFP principle: any way of differentiating betweeen 2 exceptions of the same type? - Stack Overflow