当前位置:  开发笔记 > 编程语言 > 正文

LBYL与Java中的EAFP?

如何解决《LBYL与Java中的EAFP?》经验,为你挑选了5个好方法。

我最近在教自己Python并在代码执行之前发现了关于错误检查的LBYL/EAFP习语.在Python中,似乎接受的样式是EAFP,它似乎与该语言一起使用.

LBYL(大号 OOK 安伏ý OU 大号 EAP):

def safe_divide_1(x, y):
    if y == 0:
        print "Divide-by-0 attempt detected"
        return None
    else:
        return x/y

EAFP(它的ê asier到一个 SK ˚F orgiveness比P ermission):

def safe_divide_2(x, y):
    try:
        return x/y
    except ZeroDivisionError:  
        print "Divide-by-0 attempt detected"
        return None

我的问题是:我从来没有听说过使用EAFP作为主要数据验证结构,来自Java和C++背景.EAFP在Java中使用是否明智?或者是否存在过多的异常开销?我知道实际抛出异常时只有开销,所以我不确定为什么不使用更简单的EAFP方法.这只是偏好吗?



1> Jonathan Lef..:

如果您正在访问文件,EAFP比LBYL更可靠,因为LBYL中涉及的操作不是原子操作,并且文件系统可能会在您查看的时间和跳跃的时间之间发生变化.实际上,标准名称是TOCTOU - 检查时间,使用时间; 由不准确的检查引起的错误是TOCTOU错误.

考虑创建一个必须具有唯一名称的临时文件.找出所选文件名是否存在的最佳方法是尝试创建它 - 确保使用选项以确保如果文件已存在则操作失败(在POSIX/Unix术语中,O_EXCL标志为open()).如果您尝试测试文件是否已存在(可能正在使用access()),那么在说"否"的时间与您尝试创建文件的时间之间,某人或其他人可能已创建该文件.

相反,假设您尝试读取现有文件.您检查文件是否存在(LBYL)可能会说"它在那里",但是当您实际打开它时,您会发现"它不存在".

在这两种情况下,您都必须检查最终操作 - 并且LBYL没有自动帮助.

(如果你搞乱了SUID或SGID程序,access()问一个不同的问题;它可能与LBYL有关,但代码仍然必须考虑失败的可能性.)


很好的例子,乔纳森.对于处理并发编程和双重检查锁定习惯的java开发人员来说,这可能很有意义.

2> Jeff Shannon..:

除了Python和Java中异常的相对成本之外,请记住它们之间的哲学/态度存在差异.Java试图对类型(以及其他所有内容)非常严格,要求对类/方法签名进行明确,详细的声明.它假定您应该随时知道您正在使用的对象类型以及它能够执行的操作.相比之下,Python的"鸭子打字"意味着你不确定(并且不应该关心)对象的清单类型是什么,你只需要关心它在你提出要求时嘎嘎叫.在这种宽松的环境中,唯一理智的态度就是假定事情会起作用,但如果不这样做,就要准备好应对后果.Java的自然限制性没有' 这种随意的方法非常适合.(这并不是为了贬低任何一种方法或语言,而是说这些态度是每种语言成语的一部分,而在不同语言之间复制习语往往会导致尴尬和沟通不畅......)


此外,http://oranlooney.com/lbyl-vs-eafp/为每种方法提供了一系列优缺点.

3> mipadi..:

Python中的异常处理比Java更有效,这至少部分是为什么你在Python中看到这个构造.在Java中,以这种方式使用异常在效率方面(在性能方面)效率更低.


@duffymo我有同样的问题,并在此处找到:http://stackoverflow.com/questions/598157/cheap-exception-handling-in-python
mipadi,您对python如何实现这一点有什么了解吗?
@duffymo大多数讨论围绕LBYL与EAFP,但其中一个答案与CPython中实际实现异常有关:http://docs.python.org/c-api/intro.html#exceptions

4> Yuval Adam..:

就个人而言,我认为这是通过惯例支持的,EAFP永远不是一个好方法.您可以将其视为以下内容的等效项:

if (o != null)
    o.doSomething();
else
    // handle

而不是:

try {
    o.doSomething()
}
catch (NullPointerException npe) { 
    // handle
}

此外,请考虑以下事项:

if (a != null)
    if (b != null)
        if (c != null)
            a.getB().getC().doSomething();
        else
            // handle c null
    else
        // handle b null
else
    // handle a null

这可能看起来不那么优雅(是的,这是一个粗略的例子 - 与我一起承担),但它在处理错误方面给你更大的粒度,而不是将它全部包装在try-catch中以获得它NullPointerException,然后试着找出你得到它的地点和原因.

我认为不应该使用EAFP,除了极少数情况.此外,由于您提出了问题:是的,即使没有抛出异常,try-catch块也会产生一些开销.


这种EAFP部分取决于您测试的异常是否会经常发生.如果不太可能,那么EAFP是合理的.如果它们很常见,那么LBYL可能会更好.答案可能还取决于可用的异常处理范例.在C中,LBYL是必要的.
EAFP还可以在多线程环境中具有更好的安全属性(消除某些竞争条件).
"普通例外"根本不是例外,所以当然LBYL会是首选,你不觉得吗?
说"try-catch块确实会产生一些开销,即使没有抛出异常"就像是说"方法定义确实会产生一些开销,即使没有调用该方法".换句话说,在调用方法/异常之前,开销可以忽略不计.[埃里克森(http://stackoverflow.com/questions/2633834/should-java-try-blocks-be-scoped-as-tightly-as-possible)最好的解释是,当我问这个问题.
我同意乔纳森的观点.在Python中,EAFP是一个很好的方法.
我完全不同意.从逻辑上讲,EAFP*是一种很好的验证方式.因为如果抛出异常的检查已经存在,那么额外的检查只能不同步并且不会保存任何内容.但是由于C++,Java和C#中的异常相对较慢,当预计会发生故障时很少需要LBYL.
EAFP是DRY的一个很好的例子.如果您使用除异常之外的逻辑,您(a)可能会筛选实际上不是错误的条件,因为您的检查代码与您正在调用的服务或库不同步; (b)反之亦然,您的检查代码可能无法处理实际上错误的条件 - 并且您最终还是必须使用例外.如果底层服务或库在(权威地)遇到错误时将使用异常,那么您应该使用异常.加上Jonathan Leffler描述的竞争条件问题表明EAFP是可行的方法.

5> Veedrac..:

考虑以下代码片段:

def int_or_default(x, default=0):
    if x.isdigit():
        return int(x)
    else:
        return default

def int_or_default(x, default=0):
    try:
        return int(x)
    except ValueError:
        return default

他们俩看起来都正确,对不对?但是其中之一不是。

使用LBYL的前者由于与之间的细微区别isdigit而失败isdecimal。当使用字符串“?²³??”调用时,它将引发错误,而不是正确返回默认值。

根据定义,使用EAFTP的后者会导致正确的处理。没有余地行为不匹配,因为需要要求的代码断言要求的代码。

使用LBYL意味着获取内部逻辑并将其复制到每个呼叫站点。不必对需求进行规范的编码,而是有机会在每次调用函数时弄乱自己。

值得一提的是,EAFTP 不是有关异常,和Java代码尤其是不应该被普遍地使用异常。它是为正确的代码块提供正确的工作。例如,使用Optional返回值是编写EAFTP代码的一种完全有效的方法,并且比LBYL更有效地确保了正确性。

推荐阅读
Chloemw
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有