11

在一个关于 Python 声明的问题的回答中(由S.Lott 撰写)try...else

实际上,即使在 if 语句中,else: 也可能以非常糟糕的方式被滥用,从而产生很难找到的错误。[...]

三思而后行:。这通常是一个问题。避免它,除非在 if 语句中,甚至考虑记录 else 条件以使其明确。

这是一个广泛持有的观点吗?被else 认为是有害的?

当然,您可以用它编写令人困惑的代码,但任何其他语言结构都是如此。在我看来,即使是 Python 也是for...else一件非常方便的事情(对于try...else.

4

13 回答 13

31

S.Lott 显然已经看到了一些糟糕的代码。我们不是吗?我不认为 else 有害,尽管我已经看到它用于编写糟糕的代码。在这些情况下,所有周围的代码也很糟糕,那为什么还要责怪糟糕的其他代码呢?

于 2009-05-14T21:04:53.727 回答
15

不,它没有害处,这是必要的。

应该始终有一个包罗万象的声明。所有开关都应该有一个默认值。ML 语言中的所有模式匹配都应该有一个默认值。

在一系列 if 语句之后不可能推理出什么是真实的论点是生活中的事实。计算机是最大的有限状态机,在每种情况下列举每种可能性是愚蠢的。

如果你真的害怕未知错误在 else 语句中被忽视,那么在那里引发异常真的那么难吗?

于 2009-05-14T21:06:57.730 回答
7

说 else 被认为是有害的有点像说变量或类是有害的。哎呀,这甚至就像说 goto 是有害的。当然,事情可能会被滥用。但是在某些时候,你只需要相信程序员是成年人并且足够聪明,不要这样做。

归根结底是:如果您愿意不使用某些东西,因为 SO 上的答案或博客文章甚至 Dijkstra 的著名论文告诉您不要使用,您需要考虑编程是否适合您.

于 2009-05-14T21:09:25.370 回答
7

我不会说它是有害的,但有时 else 语句会给你带来麻烦。例如,如果您需要根据一个输入值进行一些处理,并且只有两个有效的输入值。只有检查一个可能会引入一个错误。例如:

The only valid inputs are 1 and 2:

if(input == 1)
{
   //do processing
   ...
}
else
{
   //do processing 
   ...
}

在这种情况下,使用 else 将允许处理除 1 之外的所有值,而应该只处理值 1 和 2。

于 2009-05-14T21:09:31.220 回答
6

对我来说,某些流行语言结构本质上不好的整个概念是完全错误的。甚至goto有它的位置。我见过 Walter Bright 和 Linus Torvalds 等人使用它的可读性强、可维护的代码。只教程序员可读性很重要并使用常识比武断地声明某些构造“有害”要好得多。

于 2009-05-14T21:08:48.227 回答
4

如果你写:

if foo:
    # ...
elif bar:
    # ...
# ...

那么读者可能会想知道:如果两者foo都不bar是真的呢?也许您知道,根据您对代码的理解,必须是fooor 或bar. 我希望看到:

if foo:
    # ...
else:
    # at this point, we know that bar is true.
    # ...
# ...

或者:

if foo:
    # ...
else:
    assert bar
    # ...
# ...

这让读者清楚地知道您希望控制如何流动,而无需读者对来自何处foobar来自何处有深入的了解。

(在最初的情况下,你仍然可以写一个评论来解释正在发生的事情,但我想我会想知道:“为什么不只使用一个else:子句?”)

我认为重点不是你不应该使用else:; 相反,一个else:子句可以让您编写不清楚的代码,您应该尝试识别何时发生这种情况并添加一些注释以帮助任何读者。

这对于编程语言中的大多数事情都是正确的,真的:-)

于 2009-05-14T23:02:38.857 回答
3

Else在记录有关代码的假设时最有用。它确保您已经考虑了 if 语句的两面。

始终在每个 if 语句中使用 else 子句甚至是“代码完成”中的推荐做法。

于 2009-05-14T21:11:31.620 回答
3

Au contraire... 在我看来,每个 if 都必须有一个 else。诚然,您可以做愚蠢的事情,但如果您足够努力,您可以滥用任何构造。您知道“真正的程序员可以用每种语言编写 FORTRAN”的说法。

我经常做的是将 else 部分写成评论,描述为什么没有什么可做的。

于 2009-05-14T21:12:24.443 回答
2

else首先在 Python 中包含语句 (of )的基本原理try...else是只捕获您真正想要的异常。通常当你有一个try...except块时,有一些代码可能会引发异常,然后还有一些代码只有在前面的代码成功时才应该运行。如果没有else块,您必须将所有代码放入try块中:

try:
    something_that_might_raise_error()
    do_this_only_if_that_was_ok()
except ValueError:
    # whatever

问题是,如果do_this_only_if_that_was_ok()引发 aValueError怎么办?except当你可能不想要它时,它会被声明抓住。这就是else块的目的:

try:
    something_that_might_raise_error()
except ValueError:
    # whatever
else:
    do_this_only_if_that_was_ok()

我想这在某种程度上是一个见仁见智的问题,但我个人认为这是一个好主意,尽管我很少使用它。当我使用它时,感觉非常合适(此外,我认为它有助于澄清代码流)

于 2009-05-14T21:12:23.227 回答
1

在我看来,对于存在默认场景或副作用的任何语言和任何流程控制语句,该场景需要具有相同的考虑水平。if 或 switch 或 while 中的逻辑仅与条件 if(x) while(x) 或 for(...) 一样好。因此,该陈述无害,但其条件的逻辑是有害的。

因此,作为开发人员,我们有责任用广泛的 else in mind 进行编码。太多的开发人员将其视为“如果不是上述”,实际上它可以忽略所有常识,因为其中唯一的逻辑是对前面逻辑的否定,这通常是不完整的。(算法设计错误本身)

然后,我不认为“其他”比 for() 循环中的“其他”或糟糕的内存管理更有害。这都是关于算法的。如果您的自动机在其范围和可能的分支上是完整的,并且所有内容都是具体的并且可以理解,那么就不会有危险。危险在于人们误用了表达式背后的逻辑,而没有意识到广泛逻辑的影响。计算机很愚蠢,它们按照操作员的指示行事(理论上)

我确实认为trycatch是危险的,因为它可以否定对未知数量的代码的处理。在raise之上的分支可能包含一个 bug,由raise本身突出显示。这可能是不明显的。这就像将一组顺序指令变成错误处理的树或图,其中每个组件都依赖于父级中的分支。奇怪的。请注意,我爱C。

于 2009-05-14T21:12:52.617 回答
1

在 C 家族语言中遇到了一个所谓的“悬空 else”问题,如下所示:

if (a==4)
if (b==2)
printf("here!");
else
printf("which one");

这个无辜的代码可以通过两种方式来理解:

if (a==4)
    if (b==2)
        printf("here!");
    else
        printf("which one");

或者

if (a==4)
    if (b==2)
        printf("here!");
else
    printf("which one");

问题是“else”是“悬空的”,可以迷惑else的所有者。当然编译器不会造成这种混淆,但它对凡人有效。

感谢 Python,我们不能在 Python 中遇到悬空的 else 问题,因为我们必须编写

if a==4:
    if b==2:
        print "here!"
else:
    print "which one"

或者

if a==4:
    if b==2:
        print "here!"
    else:
        print "which one"

所以人的眼睛会捕捉到它。而且,不,我不认为“else”是有害的,它和“if”一样有害。

于 2009-05-15T22:11:10.813 回答
0

在假设难以推理的示例中,可以显式编写,但 else 仍然是必需的。例如

if a < 10:       
    # condition stated explicitly   
elif a > 10 and b < 10:       
    # condition confusing but at least explicit   
else:       
    # Exactly what is true here?       
    # Can be hard to reason out what condition is true

可以写

if a < 10:       
    # condition stated explicitly   
elif a > 10 and b < 10:       
    # condition confusing but at least explicit   
elif a > 10 and b >=10:
    # else condition
else:   
    # Handle edge case with error?
于 2009-05-14T21:11:30.327 回答
0

我认为关键try...except...else在于使用它来创建不一致的状态而不是修复它是一个简单的错误。不是不惜一切代价避免它,但它可能会适得其反。

考虑:

try:
    file = open('somefile','r')
except IOError:
    logger.error("File not found!")
else:
    # Some file operations
    file.close()
# Some code that no longer explicitly references 'file'

说上面的块阻止代码尝试访问不存在的文件或用户没有权限的目录,并且说所有内容都被封装,因为它在一个try...except...else块中,这将是非常好的。但实际上,上面形式的很多代码真的应该是这样的:

try:
    file = open('somefile','r')
except IOError:
    logger.error("File not found!")
    return False
# Some file operations
file.close()
# Some code that no longer explicitly references 'file'

你经常自欺欺人地说,因为file不再在范围内引用,所以在块之后继续编码是可以的,但在许多情况下,会出现一些不合适的地方。或者,稍后可能会在else块中创建一个未在块中创建的变量except

这就是我区分if...elsefrom 的方式try...except...else。在这两种情况下,在大多数情况下,必须使块并行(一个中设置的变量和状态应该设置在另一个中),但在后一种情况下,编码人员通常不这样做,可能是因为它不可能或不相关。在这种情况下,返回调用者通常比尝试继续解决您认为在最佳情况下将拥有的东西更有意义。

于 2009-05-14T21:50:38.703 回答