38

If I have a function with multiple conditional statements where every branch gets executed returns from the function. Should I use multiple if statements, or if/elif/else? For example, say I have a function:

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

Is it better to write:

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

Both have the same outcome, but is one more efficient or considered more idiomatic than the other?

Edit:

A couple of people have said that in the first example both if statements are always evaluated, which doesn't seem to be the case to me

for example if I run the code:

l = [1,2,3]

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

test(5)

l will still equal [1,2,3]

Matze
  • 5,100
  • 6
  • 46
  • 69
Sean Geoffrey Pietz
  • 1,120
  • 2
  • 12
  • 17
  • 1
    http://www.tutorialspoint.com/python/python_if_else.htm this holds a perfect explanation to that – Sander Van der Zeeuw Apr 01 '14 at 10:15
  • 2
    @SanderVanderZeeuw I don't see how that answers my question. I understand how if/elif/else work, but if the options are mutually exclusive and they return from a function there is no difference in the two outcomes. Do these two statements compile to the same thing? I prefer the first way, but I'm worried it might confuse or bother other people reading my code as it may not be idiomatic. – Sean Geoffrey Pietz Apr 01 '14 at 10:20
  • 1
    They may be the same functionally if they all return, but it is vastly less clear what your intention is for someone else reading the code, and if it gets changed in the future, there is more possibility for error. In this case, yes, both are valid and do the same thing. The latter is definitely the better option as it's more readable. – Gareth Latty Apr 01 '14 at 10:24
  • @SeanGeoffreyPietz They do the same thing but i think that from all the code i saw, its more convenient to use elif (otherwise you can get confused by to many if statements in a row – Sander Van der Zeeuw Apr 01 '14 at 10:25
  • As the other posters are saying here, an `if..else` ladder where the actions include `return` statements is terminal, so it's a less general case than the one where we might want to allow falling-through into the next `if`-block. See my answer. – smci Apr 01 '14 at 10:37

7 Answers7

32

I'll expand out my comment to an answer.

In the case that all cases return, these are indeed equivalent. What becomes important in choosing between them is then what is more readable.

Your latter example uses the elif structure to explicitly state that the cases are mutually exclusive, rather than relying on the fact they are implicitly from the returns. This makes that information more obvious, and therefore the code easier to read, and less prone to errors.

Say, for example, someone decides there is another case:

def example(x):
    if x > 0:
        return 'positive'
    if x == -15:
        print("special case!")
    if x < 0:
        return 'negative'
    return 'zero'

Suddenly, there is a potential bug if the user intended that case to be mutually exclusive (obviously, this doesn't make much sense given the example, but potentially could in a more realistic case). This ambiguity is removed if elifs are used and the behaviour is made visible to the person adding code at the level they are likely to be looking at when they add it.

If I were to come across your first code example, I would probably assume that the choice to use ifs rather than elifs implied the cases were not mutually exclusive, and so things like changing the value of x might be used to change which ifs execute (obviously in this case the intention is obvious and mutually exclusive, but again, we are talking about less obvious cases - and consistency is good, so even in a simple example when it is obvious, it's best to stick to one way).

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
19

Check this out to understand the difference:

>>> a = 2
>>> if a > 1: a = a+1
...
>>> if a > 2: a = a+1
...
>>> a
4

versus

>>> a = 2
>>> if a > 1: a = a+1
... elif a > 2: a = a+1
...
>>> a
3

The first case is equivalent to two distinct if's with empty else statements (or imagine else: pass); in the second case elif is part of the first if statement.

marco
  • 806
  • 1
  • 7
  • 17
4

In some cases, elif is required for correct semantics. This is the case when the conditions are not mutually exclusive:

if x == 0:   result = 0
elif y == 0: result = None
else:        result = x / y

In some cases it is efficient because the interpreter doesn't need to check all conditions, which is the case in your example. If x is negative then why do you check the positive case? An elif in this case also makes code more readable as it clearly shows only a single branch will be executed.

perreal
  • 94,503
  • 21
  • 155
  • 181
  • 1
    Are you sure my first example always checks both cases? I would imagine that once the first if statement evaluates as true it returns 'positive' without executing any of the subsequent code – Sean Geoffrey Pietz Apr 01 '14 at 10:23
  • @SeanGeoffreyPietz I thought we're talking about more general use of `if`s and `elif`s... but yes, if there's `return` in one of the `if`s, the other ones won't execute – Dunno Apr 01 '14 at 10:25
  • @SeanGeoffreyPietz, you are right, I didn't see the return, so consider an example without a return statement. In your example it is good for clarity. – perreal Apr 01 '14 at 10:25
  • 1
    This is somewhat irrelevant, but in your example you would want to switch the order of the first two conditionals so you have if y == 0: result = None; elif x == 0 .... Not trying to be petty, but figured i may as well point that out :) – Sean Geoffrey Pietz Apr 01 '14 at 12:21
2

In general (e.g. your example), you would always use an if..elif ladder to explicitly show the conditions are mutually-exclusive. It prevents ambiguity, bugs etc.

The only reason I can think of that you might ever not use elif and use if instead would be if the actions from the body of the preceding if statement (or previous elif statements) might have changed the condition so as to potentially make it no longer mutually exclusive. So it's no longer really a ladder, just separate concatenated if(..elif..else) blocks. (Leave an empty line between the separate blocks, for good style, and to prevent someone accidentally thinking it should have been elif and 'fixing' it)

Here's a contrived example, just to prove the point:

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'

You can see it's possible to hit both conditions, if the first if-block applies the discount, so then we also execute the second, which prints a message which would be confusing since our original total had been >=10.

An elif here would prevent that scenario. But there could be other scenarios where we want the second block to run, even for that scenario.

if total_cost<10:
    <some other action we should always take regardless of original undiscounted total_cost>
smci
  • 32,567
  • 20
  • 113
  • 146
1

In regards to the edit portion of your question when you said: "A couple of people have said that in the first example both if statements are always evaluated, which doesn't seem to be the case to me"

And then you provided this example:

l = [1,2,3]

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

test(5)

Yes indeed the list l will still equal [1,2,3] in this case, ONLY because you're RETURNING the result of running the block, because the return statement leads to exiting the function, which would result in the same thing if you used elif with the return statement.

Now try to use the print statement instead of the return one, you'll see that the 2nd if statement will execute just fine, and that 4 will indeed be appended to the list l using append.

Well.. now what if the first ifstatement changes the value of whatever is being evaluated in the 2nd if statement?

And yes that's another situation. For instance, say you have a variable x and you used if statement to evaluate a block of code that actually changed the x value. Now, if you use another if statement that evaluates the same variable x will be wrong since you're considering x value to be the same as its initial one, while in fact it was changed after the first if was executed. Therefore your code will be wrong.

It happens pretty often, and sometimes you even want it explicitly to be changed. If that's how you want your code to behave, then yes you should use multiple if's which does the job well. Otherwise stick to elif.

In my example, the 1st if block is executed and changed the value of x, which lead to have the 2nd if evaluates a different x (since its value was changed).

That's where elif comes in handy to prevent such thing from happening, which is the primary benefit of using it. The other secondary good benefit of using elif instead of multiple if's is to avoid confusion and better code readability.

Fouad Boukredine
  • 1,495
  • 14
  • 18
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

And

>>> 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' checks for the first condition then searches for the elif or else, whereas using elif, after if, it goes on checking for all the elif condition and lastly going to else.

Ujjwal Singh Baghel
  • 393
  • 2
  • 6
  • 23
0

elif is a bit more efficient, and it's quite logical: with ifs the program has to evaluate each logical expression every time. In elifs though, it's not always so. However, in your example, this improvement would be very, very small, probably unnoticeable, as evaluating x > 0 is one of the cheapest operations.

When working with elifs it's also a good idea to think about the best order. Consider this example:

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

Here the program will have to evaluate the ugly expression every time! However, if we change the order:

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

Now the program will first check if x < 0 (cheap and simple) and only if it isn't, will it evaluate the more complicated expression (btw, this code doesn't make much sense, it's just a random example)

Also, what perreal said.

Dunno
  • 3,632
  • 3
  • 28
  • 43
  • The case given by the poster is that the cases all return from the function, so there is no extra computation from any other cases. – Gareth Latty Apr 01 '14 at 10:27
  • @Lattyware like I said under the other answer, I thought we're talking about more general use of `if`s and `elif`s... – Dunno Apr 01 '14 at 10:28