User09061968
User09061968

Reputation: 43

Better way to write if-elif-elif-elif-else?

How can I improve (reduce the writing) for this if-else block to run a method based on an option entered? I've tried looking at SO and various other areas for solutions but didn't find something fit my problem.

def funcA(arg1, arg2):
    ...

def funcB(arg1):
    ...

def funcC():
    ...

def funcD():
    ...

def funcE():
    ...

if option == 1:
   funcA(arg1, arg2)
elif option == 2:
   funcB(arg1)
elif option == 3:
   funcC()
elif option == 4:
   funcD()
elif option == 5:
   funcE()
else:
   pass

Apologies if the code is not clear.

Upvotes: 1

Views: 532

Answers (5)

Alain T.
Alain T.

Reputation: 42139

You can use a helper function fo imitate a switch/case statement and avoid the ugly repetitions (and corresponding maintenance hassles):

def switch(value): yield lambda *match: value in match

example usage:

for case in switch(option):
    if   case(1): funcA(arg1, arg2)
    elif case(2): funcB(arg1)
    elif case(3): funcC()
    elif case(4): funcD()
    elif case(5): funcE()

Upvotes: 0

Martin Kleiven
Martin Kleiven

Reputation: 7363

You could use a dictionary as a dispatch-table, and namedtuples to store a function & argument pairing.

from collections import namedtuple

def funcA(arg1, arg2):
    print(f'I am funcA and you gave me {arg1} and {arg2}')
def funcB(arg):
    print(f'I am funcB and you gave me {arg}')
def funcC():
    print(f'I am funcC')
def funcD():
    print(f'I am funcD')
def funcE():
    print(f'I am funcE')

OptionPairing = namedtuple('OptionPairing', ['f', 'args'])

dispatch = {
  1: OptionPairing(funcA, [True, False]),
  2: OptionPairing(funcB, [True]),
  3: OptionPairing(funcC, []),
  4: OptionPairing(funcD, []),
  5: OptionPairing(funcE, [])
}

for i in range(1, 6):
    choice = dispatch[i]
    x = choice.f(*choice.args)

Running this, then, gives the following results:

I am funcA and you gave me True and False
I am funcB and you gave me True
I am funcC
I am funcC
I am funcE

If you want to specify yourself what arguments to pass to your function, simply this will suffice:

dispatch = {
  1: funcA,
  2: funcB,
  3: funcC,
  4: funcD,
  5: funcE,
}

Then call your function like so:

dispatch[option](args)

Making sure to pass the arguments args, as a list, since the number of arguments is variadic.

Upvotes: 2

napuzba
napuzba

Reputation: 6298

You can use a dictionary of the functions and their args :

actions = {
  1: [ func1 , [arg1,arg2] ]
  2: [ func2 , [arg1] ]
  3: [ func3 , []]
  4: [ func4 , []]
  5: [ func5 , []]
}

def doAction( actions , option ):
    if option in actions:
        func, args = actions[option]
        func(*args)

Upvotes: 0

urban
urban

Reputation: 5702

It is common in python to do a switch-like struct with a dictionary as you said. However in your case the if/else looks ok I think since the following is a bit obfuscated:

option = 2

def funcA(arg1, arg2):
    print("A")

def funcB(arg1):
    print("B")

def funcC():
    print("C")

def funcD():
    print("D")

def funcE():
    print("E")

func, args = {
    1: (funcA, ("arg1", "arg2")),
    2: (funcB, ("arg1",)),
    3: (funcC, ()),
    4: (funcD, ()),
    5: (funcE, ()),
}.get(option, (None, (),))

if func:
    func(*args)

It really depends on how many functions and args you have but I would suggest for a few functions to stick to if/else

Upvotes: 0

Ben Carley
Ben Carley

Reputation: 280

You could have a dictionary approach?

funcs = {
    1: funcA,
    2: funcB,
    ...
}

func = funcs.get(option)
if func is None:
    return 

print(f"Running func: {func.__name__}")
func(*args, **kwargs)

Upvotes: 1

Related Questions