Efficient way of having a function only execute once in a loop

At the moment, I'm doing stuff like the following, which is getting tedious:

run_once = 0
while 1:
    if run_once == 0:
        myFunction()
        run_once = 1:

I'm guessing there is some more accepted way of handling this stuff?

What I'm looking for is having a function execute once, on demand. For example, at the press of a certain button. It is an interactive app which has a lot of user controlled switches. Having a junk variable for every switch, just for keeping track of whether it has been run or not, seemed kind of inefficient.


Solution 1:

I would use a decorator on the function to handle keeping track of how many times it runs.

def run_once(f):
    def wrapper(*args, **kwargs):
        if not wrapper.has_run:
            wrapper.has_run = True
            return f(*args, **kwargs)
    wrapper.has_run = False
    return wrapper


@run_once
def my_function(foo, bar):
    return foo+bar

Now my_function will only run once. Other calls to it will return None. Just add an else clause to the if if you want it to return something else. From your example, it doesn't need to return anything ever.

If you don't control the creation of the function, or the function needs to be used normally in other contexts, you can just apply the decorator manually as well.

action = run_once(my_function)
while 1:
    if predicate:
        action()

This will leave my_function available for other uses.

Finally, if you need to only run it once twice, then you can just do

action = run_once(my_function)
action() # run once the first time

action.has_run = False
action() # run once the second time

Solution 2:

Another option is to set the func_code code object for your function to be a code object for a function that does nothing. This should be done at the end of your function body.

For example:

def run_once():  
   # Code for something you only want to execute once
   run_once.func_code = (lambda:None).func_code

Here run_once.func_code = (lambda:None).func_code replaces your function's executable code with the code for lambda:None, so all subsequent calls to run_once() will do nothing.

This technique is less flexible than the decorator approach suggested in the accepted answer, but may be more concise if you only have one function you want to run once.

Solution 3:

Run the function before the loop. Example:

myFunction()
while True:
    # all the other code being executed in your loop

This is the obvious solution. If there's more than meets the eye, the solution may be a bit more complicated.

Solution 4:

I'm assuming this is an action that you want to be performed at most one time, if some conditions are met. Since you won't always perform the action, you can't do it unconditionally outside the loop. Something like lazily retrieving some data (and caching it) if you get a request, but not retrieving it otherwise.

def do_something():
    [x() for x in expensive_operations]
    global action
    action = lambda : None

action = do_something
while True:
    # some sort of complex logic...
    if foo:
        action()

Solution 5:

There are many ways to do what you want; however, do note that it is quite possible that —as described in the question— you don't have to call the function inside the loop.

If you insist in having the function call inside the loop, you can also do:

needs_to_run= expensive_function
while 1:
    …
    if needs_to_run: needs_to_run(); needs_to_run= None
    …