Python中何时使用if和elif?

38

如果我有一个包含多个条件语句的函数,其中每个分支都会执行并从函数中返回。那么我应该使用多个if语句还是if/elif/else语句呢?例如,假设我有一个函数:

def example(x):
    if x > 0:
        return 'positive'
    if x < 0:
        return 'negative'
    return 'zero'

哪种写法更好:

def example(x):
    if x > 0:
        return 'positive'
    elif x < 0:
        return 'negative'
    else:
        return 'zero'

两者具有相同的结果,但其中一个是否更有效或被认为更符合惯用语?

编辑:

有几个人说在第一个示例中,两个if语句总是会被评估,但我并不这样认为

例如,如果我运行以下代码:

l = [1,2,3]

def test(a):
    if a > 0:
        return a
    if a > 2:
        l.append(4)

test(5)

l仍将等于[1,2,3]


1
这个链接提供了一个完美的解释。它是关于Python中if-else语句的内容。 - Sander Van der Zeeuw
2
@SanderVanderZeeuw 我不明白那如何回答我的问题。我知道if/elif/else的工作原理,但如果选项是互斥的并且它们从一个函数返回,那么两个结果没有区别。这两个语句编译成相同的东西吗?我更喜欢第一种方式,但我担心它可能会让其他人阅读我的代码感到困惑或烦恼,因为它可能不符合惯用法。 - Sean Geoffrey Pietz
1
它们在功能上可能是相同的,如果它们都返回,但对于其他人阅读代码来说,你的意图就不那么清晰了,如果将来更改了代码,就会有更多的错误可能性。在这种情况下,是的,两者都是有效的并且做同样的事情。后者绝对是更好的选择,因为它更易读。 - Gareth Latty
@SeanGeoffreyPietz它们做相同的事情,但我认为从我看到的所有代码中,使用elif更方便(否则你可能会被连续使用太多if语句所迷惑) - Sander Van der Zeeuw
正如其他帖子中所说的那样,其中包括return语句的if..else阶梯是终止的,因此它比我们可能想要允许落入下一个if块的情况更不普遍。请参见我的答案。 - smci
7个回答

32

我会将我的评论扩展为答案。

在所有情况都返回的情况下,它们确实是等效的。在选择它们之间时变得重要的是哪个更可读。

你后面的例子使用了elif结构来明确说明这些情况是互斥的,而不是依靠它们隐含的返回值。这使得该信息更加明显,因此代码更易于阅读,且更少出错。

比如说,有人决定再增加一个情况:

def example(x):
    if x > 0:
        return 'positive'
    if x == -15:
        print("special case!")
    if x < 0:
        return 'negative'
    return 'zero'
突然间,如果用户意图让该情况互斥,就会出现潜在的错误(显然,这在示例中没有多少意义,但在更现实的情况下可能会出现)。如果使用elif并将行为展示给添加代码的人员,他们在添加时看到的级别上就可以消除这种歧义。
如果我遇到您的第一个代码示例,我可能会认为选择使用if而不是elif意味着这些情况是互斥的,因此可能会使用更改x的值来更改哪些if执行(显然在这种情况下,意图是明确且互斥的,但是再次强调,我们正在谈论不太明显的情况-保持一致是好的,因此即使在简单的示例中,当它很明显时,最好坚持一种方式) 。

19

看这个以了解区别:

>>> a = 2
>>> if a > 1: a = a+1
...
>>> if a > 2: a = a+1
...
>>> a
4
对抗
>>> a = 2
>>> if a > 1: a = a+1
... elif a > 2: a = a+1
...
>>> a
3

第一个案例相当于具有空的else语句的两个不同的if(或者想象成else: pass);在第二个案例中,elif是第一个if语句的一部分。


3
如果正如原帖作者所述,所有案件都会返回,那么这仍然没有任何影响。 - Gareth Latty

5
在某些情况下,正确的语义需要使用elif。当条件不是互斥的时候才需要这样做:
if x == 0:   result = 0
elif y == 0: result = None
else:        result = x / y

在某些情况下,这种方式是高效的,因为解释器不需要检查所有条件,这就是您的示例中的情况。如果x为负数,那么为什么要检查正数情况?在这种情况下,elif也使代码更易读,因为它清楚地表明只有一个分支将被执行。


1
你确定我的第一个例子总是检查两种情况吗?我想象一旦第一个if语句评估为true,它会在执行任何后续代码之前返回“positive”。 - Sean Geoffrey Pietz
@SeanGeoffreyPietz 我以为我们在谈论更一般的 ifelif 的用法... 但是,如果其中一个 if 中有 return,其他的就不会执行。 - Dunno
@SeanGeoffreyPietz,你说得对,我没有看到return语句,所以考虑一个没有return语句的例子。在你的例子中,这样做很好,可以增加清晰度。 - perreal
1
这有点不相关,但在你的例子中,你应该交换第一个和第二个条件语句的顺序,这样你就可以写成 if y == 0: result = None; elif x == 0 .... 我并不是想挑剔,但我觉得我可能也应该指出来 :) - Sean Geoffrey Pietz

2
一般情况下(例如您的示例),您应始终使用 if..elif 梯度来明确显示条件是相互排斥的。这可以防止歧义、错误等问题。
我能想到的唯一不使用 elif 而使用 if 的原因是,来自前面的 if 语句(或之前的 elif 语句)的操作可能已更改条件,因此可能不再相互排斥。所以它不再是一个梯度,只是单独连接的 if(..elif..else) 块。(在分隔的块之间留出空行,以保持良好的风格,并防止有人意外地认为它应该是 elif 并进行“修复”)
这里有一个松散的例子,只是为了证明这一点:
if total_cost>=10:
    if give_shopper_a_random_discount():
        print 'You have won a discount'
        total_cost -= discount
    candidate_prime = True

if total_cost<10:
    print 'Spend more than $10 to enter our draw for a random discount'

你可以看到如果第一个if块应用了折扣,那么我们也会执行第二个if块,这将导致输出一条信息,这可能会令人困惑,因为原始总额已经大于等于10。在这种情况下,使用“elif”可以避免这种情况发生。但是,在其他场景中,我们可能希望第二个if块运行,即使对于该情况也是如此。
if total_cost<10:
    <some other action we should always take regardless of original undiscounted total_cost>

1
关于您的问题中的编辑部分,当您说:“有几个人说在第一个例子中两个if语句总是被评估,但我觉得不是这样”,您提供了以下示例:
l = [1,2,3]

def test(a):
    if a > 0:
        return a
    if a > 2:
        l.append(4)

test(5)

在这种情况下,确实列表l仍将等于[1,2,3],仅因为您正在返回运行该块的结果,因为return语句导致退出函数,如果使用elifreturn语句,将得到相同的结果。
现在尝试使用print语句而不是return语句,您会看到第二个if语句将正常执行,并且使用append确实将4附加到列表l中。
那么...如果第一个if语句更改了在第二个if语句中进行评估的任何内容的值,会怎样呢?
是的,这是另一种情况。例如,假设您有一个变量x并使用if语句来评估实际更改了x值的代码块。 现在,如果您使用另一个评估相同变量xif语句,将是错误的,因为您认为x的值与其初始值相同,而实际上它在第一个if执行后已更改。因此,您的代码将是错误的。
这种情况经常发生,有时甚至希望明确更改它。如果这是您希望代码行为的方式,则应使用多个if,这样可以很好地完成工作。否则请使用elif
在我的示例中,第一个if块被执行并更改了x的值,这导致第二个if评估不同的x(因为其值已更改)。
那就需要用到elif,它可以避免这种情况发生,这是使用它的主要好处。 使用elif而不是多个if的另一个次要好处是避免混淆和更好的代码可读性。

1

Consider this For someone looking for a easy way:

>>> a = ['fb.com', 'tw.com', 'cat.com']
>>> for i in a:
...      if 'fb' in i:
...          pass
...      if 'tw' in i:
...          pass
...      else:
...          print(i)

output:

fb.com

cat.com

>>> a = ['fb.com', 'tw.com', 'cat.com']
>>> for i in a:
...      if 'fb' in i:
...          pass
...      elif 'tw' in i:
...          pass
...      else:
...          print(i)

Output:

cat.com

'If'检查第一个条件,然后搜索elif或else,而使用elif,在if之后,它会继续检查所有elif条件,最后进入else。


0

elif 更高效,而且很合乎逻辑:使用 if 时程序必须每次评估每个逻辑表达式。但是,在 elif 中,情况并非总是如此。然而,在您的示例中,这种改进非常非常小,可能根本无法注意到,因为评估 x > 0 是其中最便宜的操作之一。

在处理 elif 时,考虑最佳顺序也是一个好主意。考虑以下示例:

if (x-3)**3+(x+1)**2-6*x+4 > 0:
    #do something 1
elif x < 0:
    #do something 2

在这里,程序每次都必须评估丑陋的表达式!但是,如果我们改变顺序:

if x < 0:
   #do something 2
elif (x-3)**3+(x+1)**2-6*x+4 > 0:
   #do something 1

现在程序将首先检查 x 是否小于 0(简单而便宜),只有当它不是时,才会评估更复杂的表达式(顺便说一下,这段代码没有太多意义,只是一个随机示例)

另外,就像 perreal 说的那样。


海报提供的情况是所有案例都从函数返回,因此没有来自任何其他案例的额外计算。 - Gareth Latty
@Lattyware 像我在另一个回答下所说的,我认为我们正在谈论更一般的 ifelif 的用法... - Dunno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接