LBYL vs EAFP in Java?

2022-08-31 13:14:52

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

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(它比Permission更像Ask Forgiveness):

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

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

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

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

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

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


答案 2

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