'Avoiding nested if else statement ladder in Python
It's hard to find answers to this problem when you don't know exactly how to describe it...
What is the most idiomatic way to deal with a fairly deep (but fixed) nested set of tests that you want to run sequentially but terminate as soon as the first one comes up with a successful result?
Instead of the following
Option 1: (results in way too much indentation)
def make_decision():
results = ... some code or function that returns a list or None
if results:
decision = random.choice(results)
else:
results = ... other code or function
if results:
decision = random.choice(results)
else:
results = ... other code or function
if results:
decision = random.choice(results)
else:
results = ... other code or function
if results:
decision = random.choice(results)
...etc.
else:
decision = None
print(decision)
return decision
Option 2
Another option is to return from the function early but I'm not sure it's good practice to have so many returns scattered about and in this case I would prefer to do one final thing at the end (e.g. print(decision)) before returning:
def make_decision():
results = ... some code or function that returns a list or None
if results:
return random.choice(results)
results = ... other code or function
if results:
return random.choice(results)
results = ... other code or function
if results:
return random.choice(results)
...etc.
return None
Option 3
Finally I could make each test a separate function as described here and call them iteratively, provided each test function has the same set of arguments.
def test1(args):
...
def test2(args):
...
def test3(args):
...
def make_decision():
decision = None
for test in [test1, test2, test3, ...]:
results = test(args)
if results:
decision = random.choice(results)
break
print(decision)
return decision
This looks the best but I hadn't planned to make a function for every test and some tests can be done with the same function but with different arguments and some are just one-liners whereas others are multiple lines. So then would I have to build a list of functions and arguments before starting the loop? Or make a list of partial functions?
Any better suggestions welcome (before I go ahead with option 3 above)
UPDATE 2018-07-21:
A future potential option
Unbeknown to me as I was wrestling with this problem, PEP 572 was approved (and Mr van Rossum resigned as a consequence). If this PEP is implemented the following solution will also be possible I think:
def make_decision():
if (results := ... some code or function) is not None:
decision = random.choice(results)
elif (results := ... some code or function) is not None:
decision = random.choice(results)
...etc.
else:
decision = None
return decision
Solution 1:[1]
You can choose any of the options, it all depends on your preference. On Option 2, I don't think it's a bad idea to have many returns since they are enclosed in conditional code. They will only be executed if that condition is True.
On Option 1, you can decide to switch to elif results: instead of:
if results:
# some code
else:
if results:
# some code
In your code it almost seem like you're checking for same results which looks like only one if block will execute. You should be checking against some value e.g. if results == something.
Finally, Option 3 looks cleaner. And to wrap up, pick the one you feel most comfortable with.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
