# Is there a Pythonic way of skipping if statements in a for loop to make my code run faster?

I'm writing a script in Python that essentially rolls a dice and checks whether the die roll exceeds a number `x`. I want to repeat this process `n` times and get the probability that the die roll exceeds the number `x`. e.g.

``````Count = 0
for _ in itertools.repeat(None, Iterations):
x = 3
die_roll = rnd.randint(1,6)
if die_roll > x:
Count += 1
Probability_of_exceed = Count / Iterations
``````

I want to modify both the die roll and x based on user input. This user input will select different routines to modify the script e.g. `"Andy's_Routine"` might change `x` to `4`. Currently I implement this using if statements in the for loop to check which routines are active, then applying them e.g.

``````Count = 0
for _ in itertools.repeat(None, Iterations):
x = 3

if "Andy's_Routine" in Active_Routines:
x = 4

die_roll = rnd.randint(1,6)
if "Bill's_Routine" in Active_Routines:
die_roll += 1
if "Chloe's_Routine" in Active_Routines:
# do something
pass

if "Person_10^5's_Routine" in Active_Routines:
# do something else
pass

if die_roll > x:
Count += 1
Probability_of_exceed = Count / Iterations
``````

In practice the routines are not so simple that they can be generalised, they might add an extra output for example. The routines can be and are concurrently implemented. The problem is that there could be thousands of different routines, such that each loop will spend the majority of its time checking the if statements, slowing down the program.

Is there a better way of structuring the code that checks which routines are in use only once, and then modifies the iteration somehow?

You're asking two things here - you want your code to be more Pythonic, and you want it to run faster.

The first one is easier to answer: make `Active_Routines` a list of functions instead of a list of strings, and call the functions from the list. Since these functions may need to change the local state (`x` and `die_roll`), you will need to pass them the state as parameters, and let them return a new state. The refactor might look like this:

``````def Andy(x, die_roll):
return (4, die_roll)

def Bill(x, die_roll):
return (x, die_roll + 1)

def Chloe(x, die_roll):
# do something
return (x, die_roll)

Active_Routines = [Andy, Bill, Chloe]

Count = 0
for i in range(Iterations):
x = 3
die_roll = rnd.randint(1,6)

for routine in Active_Routines:
x, die_roll = routine(x, die_roll)

if die_roll > x:
Count += 1

Probability_of_exceed = Count / Iterations
``````

The second one is harder to answer. This refactoring now makes a lot of function calls instead of checking `if` conditions; so there could be fewer missed branch predictions, but more function call overhead. You would have to benchmark it (e.g. using the timeit library) to be sure. However, at least this code should be easier to maintain.

To apply Pythonic Way to your code, use the PEP8 Code Style Guide.

So:

• `Count` should be`count` (use of capital letters only for classes);

• `for ...:` statements with the determinate number of iteration can use list for iterate in `for i in i_list:` or `range()` function for create list from int `for i in range(max_count)`;

• inside `for` may use `continue` (or `break` to finish loop) to skip the iteration after applying the changes OR use the single statement `if ...: elif ...: elif ...: else:`, where you do not need to check one value for many checks.

`@dataclasses` usage:

``````from random import randint
from dataclasses import dataclass

@dataclass
class Condition:
x: int = 3
die_roll: int = randint(1,6)

active_routine_list = ["Andy's_Routine", "Chloe's_Routine"]

iterations = 5
count = 0
for _ in range(iterations):
condition = Condition()
print('from:', condition.die_roll)

def _stack_conditions(routine_name):
routine_conditions = {
"Andy's_Routine": Condition(x=4, die_roll=condition.die_roll),
"Chloe's_Routine": Condition(x=condition.x, die_roll=condition.die_roll + 1)
}
return routine_conditions[routine_name]

for routine_name in active_routine_list:
condition = _stack_conditions(routine_name)

# Check this after one of the checks in the previous statement are applying
if condition.die_roll > condition.x:
count += 1

probability_of_exceed = count / iterations
``````

Timeit: `[0.006040813000254275, 0.07721800600029383, 0.0213634470001125, 0.014464111999586748, 0.016983135000373295]`

`eval` usage: NOTE: it's not a good practice, but like a use case

``````from random import randint

active_routine_list = ["Andy's_Routine", "Chloe's_Routine"]

iterations = 5
count = 0
for _ in range(iterations):
x = 3
die_roll = randint(1,6)

active_routines_action = {
# Name : [x, die_roll]
"Andy's_Routine": {'x': '4'},
"Bill's_Routine": {'die_roll': 'die_roll + 1'},
"Chloe's_Routine": {'x':'x + 5'},
"Person_10^5's_Routine": {}
}
# Apply routine actions related to active_routine_list
for active_routine in active_routine_list:
if active_routines_action[active_routine].get('x'):
x = eval(active_routines_action[active_routine]['x'])
if active_routines_action[active_routine].get('die_roll'):
die_roll = eval(active_routines_action[active_routine]['die_roll'])

# Check this after one of the checks in the previous statement are applying
if die_roll > x:
count += 1

probability_of_exceed = count / iterations

``````

Timeit: `[0.0015670310003770282, 0.05414528299979793, 0.002308889999767416, 0.0019655290002447146, 0.0020383940000101575]`

``````from random import randint

count = 0
for _ in range(iterations):
x = 3
die_roll = randint(1,6)

# One statement for one check
if "Andy's_Routine" in active_routine_list:
x = 4
if "Bill's_Routine" in active_routine_list:
die_roll += 1
if "Chloe's_Routine" in active_routine_list:
# do something
pass
if "Person_10^5's_Routine" in active_routine_list:
# do something else
pass

# Check this after one of the checks in the previous statement are applying
if die_roll > x:
count += 1

probability_of_exceed = count / iterations

``````

`if` usage timeit: `[0.000327431999721739, 0.00041720400031408644, 0.0003407909998713876, 0.00031981899974198313, 0.00032971699965855805]`