Python Forum

Full Version: which is better for many (el)ifs in a loop?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
i have many ifs or elifs in the body of a loop. which is better for such a situation?

style A:
   while or for some loop:
        if condition1:
            action1
            continue
        if condition2:
            action2
            continue
        if condition3:
            action3
            continue
        if condition4:
            action4
            continue
or ...
style B:
   while or for some loop:
        if condition1:
            action1
        elif condition2:
            action2
        elif condition3:
            action3
        elif condition4:
            action4
as you can see, both styles result in the same condition tests and selected action based on those tests. when you have a situation like this that may be very long, which is the better way in Python? does it vary by version of Python? does it vary by number of different conditions and actions? does it vary by anything else? or would your answer be the same for all these cases?

i did a test of something like this in C a long time ago with maximum optimization and examined the compiled result and got essentially identical machine code. both i don't know how Python would deal with these cases or what i should even consider important.
The elifs, obviously. Unless you write the rest of your code like this:

def fibonacci(n):
    pass
    numbers = [1, 1]
    pass
    for number in range(n - 2):
        pass
        numbers.append(sum(numbers[-2:]))
    pass
    return numbers
The only time you have to use if if if structure is graphical such as you want to check if the user is moving on an X and Y axis individually or simultaneously like a joystick. Otherwise you would always use the if elif elif structure.

if you have a lot of if elif elif's you can minimize them by using a dictionary.

something = 'something'

if something == 'this':
    the_thing = 1
elif something == 'that':
    the_thing = 2
elif something == 'there':
    the_thing = 3
else:
    the_thing = 4
something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

the_thing = options.get(something, 4)
something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}

if something in options:
    the_thing = options[something]
else:
    the_thing = 4
from collections import defaultdict

something = 'something'
options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3})

the_thing = options[something]
it is not obvious to me, obviously (because i asked). i do have the general tendency to code with elifs where i can. but the ifs+continues way gives an opportunity to insert code along the way to do things that change how subsequent conditions or actions behave, such as making a string be lower case to make subsequent conditions test a string in a case insensitive way, or diagnose possible bugs. so, sometimes i do it the other way. but i am curious how the Python community prefers it. the big advantage i see in the elifs way is readability.
(Dec-11-2018, 02:52 AM)Skaperen Wrote: [ -> ]the big advantage i see in the elifs way is readability.
all ifs would be your code checks all conditions, whereas elif checks each condition and abandons the rest when one is followed. If you do not need to check each condition, you could be slowing the program down by checking every condition (depending on what each condition is doing and how many if conditions you actually have).

(Dec-11-2018, 02:52 AM)Skaperen Wrote: [ -> ]ifs+continues way gives an opportunity to insert code along the way to do things that change how subsequent conditions or actions behave
I always use elif unless i am moving a character diagonally. Can you submit a code example of your if if if structure and its required use?
the only time i have ever needed if if if is either that case of checking all conditions (like your graphical case although i have encountered a non-graphical one) or a case of doing something in the midst of the sequence of checkss (like conversion of something being checked, for the checks that follow). the midst case, not being a check everything case, does have continue at the end of each check body.

i'd probably have to look around in older Pike or C code for real examples.
The entire use-case is handled by the if/elif construct. It might just personal preference, but I think the only time you should use continue is if there's no other way to accomplish that particular goal.

But hey, we don't have to guess!
>>> def ifelif(value):
...   while value > 0:
...     if value == 1:
...       value += 1
...     elif value == 2:
...       value -= 1
...     elif value == 3:
...       value -= 4
...   return value
...
>>> def ifcont(value):
...   while value > 0:
...     if value == 1:
...       value += 1
...       continue
...     if value == 2:
...       value -= 1
...       continue
...     if value == 3:
...       value -= 4
...   return value
...
>>> import dis
>>> dis.dis(ifelif)
  2           0 SETUP_LOOP              64 (to 66)
        >>    2 LOAD_FAST                0 (value)
              4 LOAD_CONST               1 (0)
              6 COMPARE_OP               4 (>)
              8 POP_JUMP_IF_FALSE       64

  3          10 LOAD_FAST                0 (value)
             12 LOAD_CONST               2 (1)
             14 COMPARE_OP               2 (==)
             16 POP_JUMP_IF_FALSE       28

  4          18 LOAD_FAST                0 (value)
             20 LOAD_CONST               2 (1)
             22 INPLACE_ADD
             24 STORE_FAST               0 (value)
             26 JUMP_ABSOLUTE            2

  5     >>   28 LOAD_FAST                0 (value)
             30 LOAD_CONST               3 (2)
             32 COMPARE_OP               2 (==)
             34 POP_JUMP_IF_FALSE       46

  6          36 LOAD_FAST                0 (value)
             38 LOAD_CONST               2 (1)
             40 INPLACE_SUBTRACT
             42 STORE_FAST               0 (value)
             44 JUMP_ABSOLUTE            2

  7     >>   46 LOAD_FAST                0 (value)
             48 LOAD_CONST               4 (3)
             50 COMPARE_OP               2 (==)
             52 POP_JUMP_IF_FALSE        2

  8          54 LOAD_FAST                0 (value)
             56 LOAD_CONST               5 (4)
             58 INPLACE_SUBTRACT
             60 STORE_FAST               0 (value)
             62 JUMP_ABSOLUTE            2
        >>   64 POP_BLOCK

  9     >>   66 LOAD_FAST                0 (value)
             68 RETURN_VALUE
>>> dis.dis(ifcont)
  2           0 SETUP_LOOP              64 (to 66)
        >>    2 LOAD_FAST                0 (value)
              4 LOAD_CONST               1 (0)
              6 COMPARE_OP               4 (>)
              8 POP_JUMP_IF_FALSE       64

  3          10 LOAD_FAST                0 (value)
             12 LOAD_CONST               2 (1)
             14 COMPARE_OP               2 (==)
             16 POP_JUMP_IF_FALSE       28

  4          18 LOAD_FAST                0 (value)
             20 LOAD_CONST               2 (1)
             22 INPLACE_ADD
             24 STORE_FAST               0 (value)

  5          26 JUMP_ABSOLUTE            2

  6     >>   28 LOAD_FAST                0 (value)
             30 LOAD_CONST               3 (2)
             32 COMPARE_OP               2 (==)
             34 POP_JUMP_IF_FALSE       46

  7          36 LOAD_FAST                0 (value)
             38 LOAD_CONST               2 (1)
             40 INPLACE_SUBTRACT
             42 STORE_FAST               0 (value)

  8          44 JUMP_ABSOLUTE            2

  9     >>   46 LOAD_FAST                0 (value)
             48 LOAD_CONST               4 (3)
             50 COMPARE_OP               2 (==)
             52 POP_JUMP_IF_FALSE        2

 10          54 LOAD_FAST                0 (value)
             56 LOAD_CONST               5 (4)
             58 INPLACE_SUBTRACT
             60 STORE_FAST               0 (value)
             62 JUMP_ABSOLUTE            2
        >>   64 POP_BLOCK

 11     >>   66 LOAD_FAST                0 (value)
             68 RETURN_VALUE
>>>
Both methods result in 68 bytecode operations. And, aside from whitespace, those operations look identical to me. So the only difference is whether or not you feel like typing continue all over the place.
(Dec-13-2018, 11:09 PM)nilamo Wrote: [ -> ]So the only difference is whether or not you feel like typing continue all over the place.
its really weird to add

It also may appear to others like noobie code. If someone came on the forum and put that, that would be one thing i would tell them not to do.

(Dec-13-2018, 08:15 PM)Skaperen Wrote: [ -> ]or a case of doing something in the midst of the sequence of checkss (like conversion of something being checked, for the checks that follow).
I feel like this should not be required. But i would have to see an example of what you mean exactly. I think there might be an alternative method.
(Dec-13-2018, 11:57 PM)metulburr Wrote: [ -> ]
(Dec-13-2018, 08:15 PM)Skaperen Wrote: [ -> ]or a case of doing something in the midst of the sequence of checkss (like conversion of something being checked, for the checks that follow).
I feel like this should not be required. But i would have to see an example of what you mean exactly. I think there might be an alternative method.
the one way i can think of (now ... i should go back and recode it) is to have groups of if/elif and the mod code between them. but that is a limited case since the fall-through goes to the mod, not the end. i'll go look for that code and see what i can do with it when i have a big enough block of certain time.