If vs. Elif in Python, which is better?

Viewed 1344

I am reading Grokking Algorithms, which seems to be a highly recommended book. I am looking at the first algorithm for "binary search" and the guy uses two "ifs" instead of a "if" and "elif". Would using two "ifs" be better or faster?

def binary_search(list, item):
    low = 0
    high = len(list) - 1

    while low <= high:
        mid = (low + high)
        guess = list[mid]
        if guess == item:
            return mid
        if guess > item:
            high = mid - 1
        else:
            low = mid + 1
    return None

my_list = [1,3,5,7,9]
3 Answers

example1

>>> def foo():
    a = 10
    if a == 10:
        print("condition1")
    elif a == 10:
        print("condition2")
    else:
        print(0)    
>>> foo()
condition1
>>> 

elif is guaranteed not to run when if is true.

example2

def foo():
    a = 10
    if a == 10:
        print("condition1")
    if a == 10:
        print("condition2")
    else:
        print(0)
>>> foo()
condition1
condition2
>>> 

example3 modify if statement of example2.

if a == 10:
    print("condition1")
    return a

output

>>> foo()
condition1
10

So, in your case adding a return in first if statement has similar operation like an if-elif block. the (return a) is preventing the second if statement to be executed in example3.

You use multiple ifs, when there's a chance that all the ifs could be simultaneously true and you wish to execute the statements within them. Whereas you use if, elifs and else when you know that only the statements within a single block will get executed. Let's see examples to make it very clear.

Here, the condition for all the if statements are checked and the last three conditions are satisfied so the statements within them gets executed.

x = 105
if (x%2):
    print("Multiple of 2")
if (x%3):
    print("Multiple of 3")
if (x%5):
    print("Multiple of 5")
if (x%7):
    print("Multiple of 7")

Whereas in this case the conditions are only checked till a True condition is found, once a True condition is found, all the remaining conditional statements are skipped. So, here only the statement within the second conditional statement gets executed. (But, Note even though only the statement within one conditional statement is executed, there were two checks)

if (x%2):
    print("Multiple of 2")
elif (x%3):
    print("Multiple of 3")
elif (x%5):
    print("Multiple of 5")
else:
    print("Multiple of 7")

So, basically both these statements have different use cases, although if you are sure that only one out of multiple conditional statements will get executed, using if, elifs and else is better. But here since you have a return statement with the if loop, things actually behave differently.

As other answers mentioned, an elif is not necessary after an if that does a return. But they didn't cover the performance impact. I thought it would be interesting to test.

Turns out, it can be a tad faster to use if/elif/else instead of separate if's.

from timeit import timeit
import random


def count_items_ifelifelse(items, threshold):
    below, on, above = 0, 0, 0
    for item in items:
        if item > threshold:
            above += 1
        elif item < threshold:
            below += 1
        else:
            on += 1
    return below, on, above


def count_items_ififif(items, threshold):
    below, on, above = 0, 0, 0
    for item in items:
        if item > threshold:
            above += 1
        if item < threshold:
            below += 1
        if item == threshold:
            on += 1
    return below, on, above


def generate_items_and_threshold(count=100_000):
    """List of reproducible random numbers to test with. Set threshold at half"""
    items = list(range(count))
    random.Random(123).shuffle(items)
    threshold = count // 2
    return items, threshold


def run_test():
    t1 = timeit(
        "count_items_ifelifelse(i, t)",
        setup="from __main__ import count_items_ifelifelse, generate_items_and_threshold; i, t = generate_items_and_threshold()",
        number=1000,
    )
    print("count_items_ifelifelse: {:.2f}".format(t1))
    t2 = timeit(
        "count_items_ififif(i, t)",
        setup="from __main__ import count_items_ififif, generate_items_and_threshold; i, t = generate_items_and_threshold()",
        number=1000,
    )
    print("count_items_ififif: {:.2f}".format(t2))


if __name__ == "__main__":
    run_test()

This outputs (Python 3.8.2, Windows):

count_items_ifelifelse: 6.69
count_items_ififif: 8.71

Roughly 20% faster, because of more unnecessary if evaluations each loop. The other side of it is that for if/elif/else the performance will vary based on how the data is sorted, and whether it checks the most occurring condition first.

Related