LBYL与EAFP漫谈

LBYL 的意思是“Look before you leap.” 指在程序执行之前做好检查。比如下面这段代码:

EAFP 的意思是“Easier to ask for forgiveness than permission.” 在编程方面指的是相信程序会正确执行,如果出错了再处理错误,比如上面这段代码用 EAFP 风格写就是下面这样:

很多情况下,两种方式的写法是可以互相替换的,但是 Python 鼓励 EAFP。原因是这种方法的可读性更高,速度也更快(只有在出错的时候才需要处理,而 LBYL 需要每次运行都检查)。

现在的大多数语言都有异常处理机制了,比如 Java, Python, Ruby 等,没有异常处理机制的都是一些古老的语言,比如 C 语言。但是没有异常处理机制并不代表程序总能运行正确,所以 C 语言需要其他形式来处理程序(函数)不能正确运行的情况,一般的方法是返回 0 表示运行正常,其他值表示运行异常。

但是这种处理形式有两个缺点(这部分可以参考《代码之髓》这本书的第 6 章):第一是可能遗漏错误。比如一个函数在大部分情况下都能正确运行,只有在很少的情况下会出错,或者只有改变了环境之后才会出错。那么很可能程序员会默认这个函数总是正确的,而忘记处理这个异常。如果是 Java 的话,程序员就必须在调用函数的地方处理掉所有可能的异常(虽然很多 Java 程序员喜欢不优雅地用 RuntimeError 处理所有的异常)。更让人头疼的事情是,一旦这“极少数”的情况发生了,那么错误经常不在错误出现的地方,而在很外层的一个调用处。你要花很多时间调试才能找到最终出错的地方。如果有异常处理机制的话,异常栈通常会直接给出 Exception 发生的地方。

第二个缺点是会使代码的可读性下降。因为要检查函数的返回值,要写很多 if-else ,程序真正的逻辑就变得难以阅读。我记得有位高人说过这么一句话,具体是谁说的记不得了,大意是:“高级语言和低级语言的区别是,需要不需要写很多与程序的逻辑无关的东西。” 很多 if-else ,很难看出这个只是判断,还是程序逻辑/业务的判断。如果用 try-catch ,那么 try 代码块里面可以只写程序的逻辑,在 except 里面处理所有的异常。

 

即使在有异常处理机制的语言中,比如 Python,很多人喜欢做的一个事情是在子函数用 if 判断,然后 logger.error + return。其实不如 raise ,然后在调用者那里 try-catch,更能表达逻辑。

此外,Python 语言内置的协议也大量使用了异常的机制。比如《fluent python》(314页)关于重载加运算符 __add__ 就提到,为了遵守鸭子精神,不要测试 other 操作数的类型,而是应该捕获异常,然后抛出 NotImplemented 。

这样的好处是,比如我们定义了一个新的数据类型,支持和 int 相加 a.__add__(4) 。但是内置的 int 可不支持和我们自定义的类型相加,4.__add__(a) 就会抛出异常。这时解释器尝试用 __radd__ 来处理(即 a.__add__(4) )。如果 int 的 __add__ 是实现是相加对象的类型,如果不符合预期就抛出一个 TypeError ,就没有这样的便利了。

 

但是 EAFP 在某些情况下可能是不可行的,它的一个问题就是等错误发生的时候,程序已经运行了一半,如果函数会造成一些副作用,那么这个时候副作用已经发生了。这种情况下,如果没有数据事务这种外部的东西来提供原子性的话,就比较麻烦了,需要手动清理副作用的状态。而 LBFY 这种风格可以尽量保证提供给程序的参数正确,可以顺利运行完成。

其实我想讨论这二者的区别的真正地方是,在生活中也会有这两种风格的处理方式。

团队管理上 try-catch 更自由,更人性化,大家可以关注自己“做事的逻辑”而没有很多条条框框,在程序上 if 可能就多了一次判断,但是在实际生活中的话,这种 if 可能是各种各样的沟通和 ask permission,效率可就不只低这么多了。但是换句话来说,严谨的系统容不得做到半路才出现 “Exception”,还是需要“Look before you leap”的。

 

参考资料:

  1. https://www.codeproject.com/Tips/490765/If-else-instead-of-try-catch
  2. https://www.quora.com/When-should-I-use-try-catch-instead-of-if-else
  3. https://en.wikiquote.org/wiki/Grace_Hopper
  4. https://stackoverflow.com/questions/12265451/ask-forgiveness-not-permission-explain
  5. https://stackoverflow.com/questions/11360858/what-is-the-eafp-principle-in-python

LBYL与EAFP漫谈”已经有2条评论

  1. try-catch机制很好用,但是有时候也容易被滥用,很多业务中该报错该崩溃的点因为try-catch导致在测试的时候没报错而被忽略。以前写java的时候就常有这种感觉

    • 是的,except 所有的 Exception 这种倒不如不写,这相当于手动取消了编程语言的 Exception 的功能吧,淹没掉 Exception 而不处理。

      Debug 起来太痛苦。

Leave a comment

电子邮件地址不会被公开。 必填项已用*标注