Clean way to variable check + continue in while loop

Viewed 184

I have a relatively large main() function where I need to check a variable several times at different places in the function and then decide if I should continue the loop accordingly, like so:

def main():
    while True:
        ...
        if foo.check():
            reset()
            continue
        ...

Where foo is working as a time keeper of sorts, therefore I need to check it at certain intervals. These 3 lines appear about 3 to 5 times within the function.

I hate how dirty this is, so is there a cleaner way to do this?

3 Answers

You haven't specified enough information. I have two questions:

  1. Is the call foo.check() idempotent meaning it always returns the same value and has no side-effects?
  2. Is there a path through the code where you can reach the nth call to foo.check() in the same block governed by the continue statement without first calling the n-1th occurrence?

If, for example, the answer to the answer is yes and the second question were no, then you could remove all but the first occurrence of the call to foo.check() because the return value is clearly False or else you would never reach the second occurence.

If the answer to the first question is yes and the second is yes, then if the call to foo_check() is expensive, I might consider up front setting:

check_result = foo.check()

and then replacing every call to foo_check() with check_result. But ultimately you still need to do all the checks. But in all cases you can create a function check_and_reset:

def check_and_reset():
    if foo_check():
        reset()
        return True
    return False

Then your code becomes:

if check_and_reset(): continue

I have two suggestions:

Suggestion 1: Create another function and call wherever you want.

Suggestion 2: Make it as a one-liner

if foo.check():reset();continue

If it is just a complex way to control some timing while running a set of tasks, I'd suggest to introduce a loop over the tasks. You can even easily pass partial results around:

def task_a():
    ...

def task_c(some_input):
    ...

tasks = [lambda x: task_a(), task_c]
last_stage_output = None

while True:
    reset()
    for task in tasks:
        if not foo.check():
            break 
        last_stage_output = task(last_stage_output)

This way, you make it clear that it is just a series of tasks to be done, it's simple to add, remove of reshuffle them, and the timing logic is concentrated in a single point.

Related