Java 中的 LBYL 与 EAFP?

发布于 2024-07-10 16:11:18 字数 843 浏览 4 评论 0原文

我最近正在自学 Python,并发现了有关代码执行前错误检查的 LBYL/EAFP 习惯用法。 在Python中,似乎公认的风格是EAFP,并且它似乎与该语言配合得很好。

LBYL(LYLeap之前B em>):

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

EAFP(A更容易Ask Forgiveness 比Permission):

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

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

I was recently teaching myself Python and discovered the LBYL/EAFP idioms with regards to error checking before code execution. In Python, it seems the accepted style is EAFP, and it seems to work well with the language.

LBYL (Look Before You Leap):

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

EAFP (it's Easier to Ask Forgiveness than Permission):

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

My question is this: I had never even heard of using EAFP as the primary data validation construct, coming from a Java and C++ background. Is EAFP something that is wise to use in Java? Or is there too much overhead from exceptions? I know that there is only overhead when an exception is actually thrown, so I'm unsure as to why the simpler method of EAFP is not used. Is it just preference?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

汐鸠 2024-07-17 16:11:18

如果你正在访问文件,EAFP比LBYL更可靠,因为LBYL涉及的操作不是原子的,并且文件系统可能在你查看的时间和你跳跃的时间之间发生变化。 实际上,标准名称是 TOCTOU - Time of Check、Time of Use; 由于检查不准确而导致的错误是TOCTOU错误。

考虑创建一个必须具有唯一名称的临时文件。 查明所选文件名是否存在的最佳方法是尝试创建它 - 确保使用选项来确保如果文件已经存在则操作失败(在 POSIX/Unix 术语中,O_EXCL 标志为 打开())。 如果您尝试测试文件是否已存在(可能使用access()),那么在显示“否”的时间和您尝试创建文件的时间之间,某人或其他东西可能会已创建该文件。

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

在这两种情况下,您都必须检查最终操作 - 并且 LBYL 不会自动提供帮助。

(如果您正在搞乱 SUID 或 SGID 程序,access() 会提出不同的问题;它可能与 LBYL 相关,但代码仍然必须考虑失败的可能性。)

If you are accessing files, EAFP is more reliable than LBYL, because the operations involved in LBYL are not atomic, and the file system might change between the time you look and the time you leap. Actually, the standard name is TOCTOU - Time of Check, Time of Use; bugs caused by inaccurate checking are TOCTOU bugs.

Consider creating a temporary file that must have a unique name. The best way to find out whether the chosen file name exists yet is to try creating it - making sure you use options to ensure that your operation fails if the file does already exist (in POSIX/Unix terms, the O_EXCL flag to open()). If you try to test whether the file already exists (probably using access()), then between the time when that says "No" and the time you try to create the file, someone or something else may have created the file.

Conversely, suppose that you try to read an existing file. Your check that the file exists (LBYL) may say "it is there", but when you actually open it, you find "it is not there".

In both these cases, you have to check the final operation - and the LBYL didn't automatically help.

(If you are messing with SUID or SGID programs, access() asks a different question; it may be relevant to LBYL, but the code still has to take into account the possibility of failure.)

葵雨 2024-07-17 16:11:18

除了 Python 和 Java 中异常的相对成本之外,请记住它们之间的理念/态度也存在差异。 Java 试图对类型(以及其他一切)非常严格,要求对类/方法签名进行显式、详细的声明。 它假设您在任何时候都应该确切地知道您正在使用什么类型的对象以及它能够做什么。 相比之下,Python 的“鸭子类型”意味着你不确定(也不应该关心)对象的明显类型是什么,你只需要关心当你要求它时它会嘎嘎叫。 在这种宽容的环境中,唯一理智的态度就是假设事情会奏效,但如果不奏效,就要准备好应对后果。 Java 的天然限制性与这种随意的方法不太相符。 (这并不是要贬低方法或语言,而是说这些态度是每种语言习语的一部分,在不同语言之间复制习语往往会导致尴尬和沟通不良......)

In addition to the relative cost of exceptions in Python and Java, keep in mind that there's a difference in philosophy / attitude between them. Java tries to be very strict about types (and everything else), requiring explicit, detailed declarations of class/method signatures. It assumes that you should know, at any point, exactly what type of object you're using and what it is capable of doing. In contrast, Python's "duck typing" means that you don't know for sure (and shouldn't care) what the manifest type of an object is, you only need to care that it quacks when you ask it to. In this kind of permissive environment, the only sane attitude is to presume that things will work, but be ready to deal with the consequences if they don't. Java's natural restrictiveness doesn't fit well with such a casual approach. (This is not intended to disparage either approach or language, but rather to say that these attitudes are part of each language's idiom, and copying idioms between different languages can often lead to awkwardness and poor communication...)

白首有我共你 2024-07-17 16:11:18

Python 中的异常处理比 Java 中的处理效率更高,这至少是您在 Python 中看到这种构造的部分原因。 在 Java 中,以这种方式使用异常的效率较低(就性能而言)。

Exceptions are handled more efficiently in Python than in Java, which is at least partly why you see that construct in Python. In Java, it's more inefficient (in terms of performance) to use exceptions in that way.

听,心雨的声音 2024-07-17 16:11:18

考虑这些代码片段:

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,但由于 isdigitisdecimal 之间的细微差别而失败; 当使用字符串“①²³

Consider these code snippets:

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

They both look correct, right? But one of them isn't.

The former, using LBYL, fails because of a subtle distinction between isdigit and isdecimal; when called with the string "①²³????₅", it will throw an error rather than correctly return the default value.

The later, using EAFTP, results in correct handling, by definition. There is no scope for a behavioural mismatch, because the code that needs the requirement is the code that asserts that requirement.

Using LBYL means taking internal logic and copying them into every call-site. Rather than having one canonical encoding of your requirements, you get a free chance to mess up every single time you call the function.

It's worth noting that EAFTP isn't about exceptions, and Java code especially should not be using exceptions pervasively. It is about giving the right job to the right block of code. As an example, using Optional return values is a perfectly valid way of writing EAFTP code, and is far more effective for ensuring correctness than LBYL.

〆一缕阳光ご 2024-07-17 16:11:18

就我个人而言,我认为这是有惯例支持的,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 块也会产生一些开销

Personally, and I think this is backed up by convention, EAFP is never a good way to go.
You can look at it as an equivalent to the following:

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

as opposed to:

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

Moreover, consider the following:

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

This may look a lot less elegant (and yes this is a crude example - bear with me), but it gives you much greater granularity in handling the error, as opposed to wrapping it all in a try-catch to get that NullPointerException, and then try to figure out where and why you got it.

The way I see it EAFP should never be used, except for rare situations. Also, since you raised the issue: yes, the try-catch block does incur some overhead even if the exception is not thrown.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文