93

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.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Marcus Ottosson
  • 3,241
  • 4
  • 28
  • 34
  • 12
    If you have to run once, why don't you just call the function -> myFunction() once !!. Please take a look at your code, explain the intent better.! – pyfunc Nov 05 '10 at 05:28
  • 7
    Why can't you leave it outside of the loop? More context please. – EightyEight Nov 05 '10 at 05:31
  • @pyfunc Let me know if the code edit more clearly conveys the issue – Ron May 01 '16 at 09:49
  • Well, it is possible that the loop iterates through experiment parameters for some task, and all functions utilize the parameter except for one. However, I might as well put them in a for loop since they are all implementations for the task. E.g. In an retrieval task, my loop iterates through configurations for the embedding methods I developed but the baseline keyword-matching method always output the same result. This way the code looks cleaner while saves computational resources. – Sean Dec 03 '17 at 08:40

19 Answers19

150

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
aaronasterling
  • 68,820
  • 20
  • 127
  • 125
  • This seems like what I might be looking for, but I am uncertain of the syntax of what your using. What is the @ char for? – Marcus Ottosson Nov 05 '10 at 18:47
  • 4
    @Marcus Ottosson The `@` char makes it a decorator. A comment is too long to explain what that is. Here, have a [link](http://www.artima.com/weblogs/viewpost.jsp?thread=240808). At any rate, you might just want to use the second form that I presented instead where you apply it manually. You should probably still know what a decorator is. – aaronasterling Nov 05 '10 at 19:52
  • 2
    It's worth mentioning that this also works on methods of classes. – Ehtesh Choudhury Feb 27 '14 at 14:42
  • new to decorator.. could someone explain a bit about how the run_once decorator works? if you pass my_function into it, wrapper.has_run is set to false before "return wrapper" and eventually wrapper function is returned and .has_run is set to true and my_function is returned, running it once. Then if I call my_function again later, won't wrapper be returned again as there is always this "wrapper.has_run = False" line before "return wrapper"? Where am I missing? – olala Oct 14 '16 at 16:38
  • @olala: What you don't understand is that the `run_once` decorator itself is only getting run once by the `@run_once` statement right above the `def my_function`, so the `wrapper.has_run = False` only happens **once** in the sample code. From then on, it's the `wrapper()` function created by doing that is what get invoked by calling ` my_function()`—so the flag doesn't get reset. – martineau Nov 29 '18 at 20:56
  • Old answer but still the best one! Congrats! – CFV Aug 25 '19 at 21:42
  • Is this thread-safe? What if two threads running on two cores both are checking `if not wrapper.has_run` when the flag is `False`, and then both threads would execute the function? – zerox Sep 15 '21 at 03:26
  • @aaronasterling This solution is awesome! I'm using in a function, but after I change this function to be asynchronous using `asyncio` library, I'm getting an error. How can I use this decorator with `await asyncio.create_task(my_function)` from this same example from your answer? – Guilherme Matheus Jan 14 '22 at 01:23
  • "Just add an else clause to the if if you want it to return something else." You mean a deindented return after the if clause... – Spartan Jun 04 '22 at 19:41
  • This does not work. I get "Attribute error" X has no attribute "run_once" – Ryan Glenn Jul 10 '22 at 01:26
26

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.

Community
  • 1
  • 1
j b
  • 5,147
  • 5
  • 41
  • 60
6

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.

Rafe Kettler
  • 75,757
  • 21
  • 156
  • 151
  • 6
    This won't cut is because the OP states "For example, at the press of a certain button. It is an interactive app which has a lot of user controlled switches." So the OP wants the function to be runnable once from inside the main run loop. – j b Jul 22 '14 at 13:05
6

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()
Ryan Ginstrom
  • 13,915
  • 5
  • 45
  • 60
  • 1
    This is assuming you'd want to (or could) change the logic of do_something() or myFunction() to suit this particular case. Maybe do_something/myFunction is called elsewhere in a different context. (Of course we won't know till poster clarifies.) – snapshoe Nov 05 '10 at 06:08
  • 3
    This is overly convoluted. Neat trick though. My main gripe, and it is I believe a serious one, is that `do_something` needs to know that it's going to be aliased to `action` which seems to me to violate some sort of separation of concerns. I reiterate though, neat trick. – aaronasterling Nov 05 '10 at 07:42
  • 3
    -1. This is messy and in larger code bases can hurt readability. Global variables, the *name* `action`, A function getting called every time (as opposed to the original question where it's called just once). – Noufal Ibrahim Nov 05 '10 at 09:11
  • 2
    `action();action = lambda:None` achieves a similar result without the extra function – John La Rooy Nov 07 '10 at 04:42
  • @gnibbler True, but I'm thinking of a more complex function, where `action` could be called under various circumstances. That said, I like the decorator variant. – Ryan Ginstrom Nov 07 '10 at 05:31
6

I've thought of another—slightly unusual, but very effective—way to do this that doesn't require decorator functions or classes. Instead it just uses a mutable keyword argument, which ought to work in most versions of Python. Most of the time these are something to be avoided since normally you wouldn't want a default argument value to change from call-to-call—but that ability can be leveraged in this case and used as a cheap storage mechanism. Here's how that would work:

def my_function1(_has_run=[]):
    if _has_run: return
    print("my_function1 doing stuff")
    _has_run.append(1)

def my_function2(_has_run=[]):
    if _has_run: return
    print("my_function2 doing some other stuff")
    _has_run.append(1)

for i in range(10):
    my_function1()
    my_function2()

print('----')
my_function1(_has_run=[])  # Force it to run.

Output:

my_function1 doing stuff
my_function2 doing some other stuff
----
my_function1 doing stuff

This could be simplified a little further by doing what @gnibbler suggested in his answer and using an iterator (which were introduced in Python 2.2):

from itertools import count

def my_function3(_count=count()):
    if next(_count): return
    print("my_function3 doing something")

for i in range(10):
    my_function3()

print('----')
my_function3(_count=count())  # Force it to run.

Output:

my_function3 doing something
----
my_function3 doing something
martineau
  • 119,623
  • 25
  • 170
  • 301
  • This is a brilliant answer! Great way to use a disadvantage of the Python language to your advantage! The only problem is this ensures globally once execution of the function, it does not solve the problem in the original question. – Sarang Mar 12 '23 at 07:12
  • This would be the most reasonable way to do it, but probably just go ahead and use the @once decorator in funcy. – Robert Moskal May 06 '23 at 20:24
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
    …
tzot
  • 92,761
  • 29
  • 141
  • 204
  • +1 I'm tempted to delete my answer but I've been doing a little too much of that recently. – aaronasterling Nov 05 '10 at 12:10
  • @aaron: you definitely shouldn't delete your answer, whatever the ratio of your deleted / remaining answers :) – tzot Nov 05 '10 at 13:30
  • 1
    explicit `if needs_to_run is not None` and proper formatting might be appropriate here. `needs_to_run, run_once = None, needs_to_run; run_once()` could protect from exceptions, make less likely multiple invocations in response to user keystrokes. – jfs Nov 06 '10 at 17:45
3

Here's an answer that doesn't involve reassignment of functions, yet still prevents the need for that ugly "is first" check.

__missing__ is supported by Python 2.5 and above.

def do_once_varname1():
    print 'performing varname1'
    return 'only done once for varname1'
def do_once_varname2():
    print 'performing varname2'
    return 'only done once for varname2'

class cdict(dict):
    def __missing__(self,key):
        val=self['do_once_'+key]()
        self[key]=val
        return val

cache_dict=cdict(do_once_varname1=do_once_varname1,do_once_varname2=do_once_varname2)

if __name__=='__main__':
    print cache_dict['varname1'] # causes 2 prints
    print cache_dict['varname2'] # causes 2 prints
    print cache_dict['varname1'] # just 1 print
    print cache_dict['varname2'] # just 1 print

Output:

performing varname1
only done once for varname1
performing varname2
only done once for varname2
only done once for varname1
only done once for varname2
martineau
  • 119,623
  • 25
  • 170
  • 301
parity3
  • 643
  • 9
  • 18
3

One object-oriented approach and make your function a class, aka as a "functor", whose instances automatically keep track of whether they've been run or not when each instance is created.

Since your updated question indicates you may need many of them, I've updated my answer to deal with that by using a class factory pattern. This is a bit unusual, and it may have been down-voted for that reason (although we'll never know for sure because they never left a comment). It could also be done with a metaclass, but it's not much simpler.

def RunOnceFactory():
    class RunOnceBase(object): # abstract base class
        _shared_state = {} # shared state of all instances (borg pattern)
        has_run = False
        def __init__(self, *args, **kwargs):
            self.__dict__ = self._shared_state
            if not self.has_run:
                self.stuff_done_once(*args, **kwargs)
                self.has_run = True
    return RunOnceBase

if __name__ == '__main__':
    class MyFunction1(RunOnceFactory()):
        def stuff_done_once(self, *args, **kwargs):
            print("MyFunction1.stuff_done_once() called")

    class MyFunction2(RunOnceFactory()):
        def stuff_done_once(self, *args, **kwargs):
            print("MyFunction2.stuff_done_once() called")

    for _ in range(10):
        MyFunction1()  # will only call its stuff_done_once() method once
        MyFunction2()  # ditto

Output:

MyFunction1.stuff_done_once() called
MyFunction2.stuff_done_once() called

Note: You could make a function/class able to do stuff again by adding a reset() method to its subclass that reset the shared has_run attribute. It's also possible to pass regular and keyword arguments to the stuff_done_once() method when the functor is created and the method is called, if desired.

And, yes, it would be applicable given the information you added to your question.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • This one seems pretty good too. Would you mind reading my updated question to see if it might be a good solution? – Marcus Ottosson Nov 05 '10 at 18:49
  • @MarcusOttosson: Sorry for the belated reply. If I understand the additional information in your updated question, then yes, I think it would work fine. – martineau Jan 31 '13 at 02:43
2

Assuming there is some reason why myFunction() can't be called before the loop

from itertools import count
for i in count():
    if i==0:
        myFunction()
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • Very clever idea -- but it sounds like the OP wants something that is an aspect of the function and in effect for any call to it, rather than being externally controlled from one point like this. – martineau Nov 07 '10 at 00:08
2

You can also use one of the standard library functools.lru_cache or functools.cache decorators in front of the function:

from functools import lru_cache

@lru_cache
def expensive_function():
   return None

https://docs.python.org/3/library/functools.html

smac89
  • 39,374
  • 15
  • 132
  • 179
charlax
  • 25,125
  • 19
  • 60
  • 71
1

Here's an explicit way to code this up, where the state of which functions have been called is kept locally (so global state is avoided). I don't much like the non-explicit forms suggested in other answers: it's too surprising to see f() and for this not to mean that f() gets called.

This works by using dict.pop which looks up a key in a dict, removes the key from the dict, and takes a default value to use in case the key isn't found.

def do_nothing(*args, *kwargs):
    pass

# A list of all the functions you want to run just once.
actions = [
    my_function,
    other_function
]
actions = dict((action, action) for action in actions)

while True:
    if some_condition:
        actions.pop(my_function, do_nothing)()
    if some_other_condition:
        actions.pop(other_function, do_nothing)()
1

You have all those 'junk variables' outside of your mainline while True loop. To make the code easier to read those variables can be brought inside the loop, right next to where they are used. You can also set up a variable naming convention for these program control switches. So for example:

#                                  # _already_done checkpoint logic
    try:
        ran_this_user_request_already_done
    except:
        this_user_request()
        ran_this_user_request_already_done = 1

Note that on the first execution of this code the variable ran_this_user_request_already_done is not defined until after this_user_request() is called.

CopyPasteIt
  • 532
  • 1
  • 8
  • 22
1

I use cached_property decorator from functools to run just once and save the value. Example from the official documentation https://docs.python.org/3/library/functools.html

class DataSet:

    def __init__(self, sequence_of_numbers):
        self._data = tuple(sequence_of_numbers)

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)
smac89
  • 39,374
  • 15
  • 132
  • 179
Shaan
  • 141
  • 7
0

If I understand the updated question correctly, something like this should work

def function1():
    print "function1 called"

def function2():
    print "function2 called"

def function3():
    print "function3 called"

called_functions = set()
while True:
    n = raw_input("choose a function: 1,2 or 3 ")
    func = {"1": function1,
            "2": function2,
            "3": function3}.get(n)

    if func in called_functions:
        print "That function has already been called"
    else:
        called_functions.add(func)
        func()
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • That would stop the interactivity. See, this app monitors my tablet input and processes it through various expressions and basic math and finally outputs it on screen. I think that what I am looking for is not very unique in any way, cause all that I'm trying to do is, say, at the press of B, add 2 to whatever variable so that the equation changes. Or, alter the color of one of the bars representing some data on screen. I am starting to thing that there might not be another way of doing it than what I am currently doing though.. – Marcus Ottosson Nov 07 '10 at 04:31
  • So, to clarify, it needs to not only run Once and then never be able to run again. Just needs to do it's thing inside the loop, and then stop doing it. Like, add 2 to my equation, then continue doing whatever you were doing before, but with the new user inputted data. Easy stuff, I would think :) – Marcus Ottosson Nov 07 '10 at 04:35
  • @Marcus, I'm not suggesting that _you_ should use `raw_input()`. It's just there so you can run this program and get an idea of how it works. Sounds to me that your app would work better with callbacks though – John La Rooy Nov 07 '10 at 04:38
0

A simple function you can reuse in many places in your code (based on the other answers here):

def firstrun(keyword, _keys=[]):
    """Returns True only the first time it's called with each keyword."""
    if keyword in _keys:
        return False
    else:
        _keys.append(keyword)
        return True

or equivalently (if you like to rely on other libraries):

from collections import defaultdict
from itertools import count
def firstrun(keyword, _keys=defaultdict(count)):
    """Returns True only the first time it's called with each keyword."""
    return not _keys[keyword].next()

Sample usage:

for i in range(20):
    if firstrun('house'):
        build_house() # runs only once
if firstrun(42): # True
    print 'This will print.'
if firstrun(42): # False
    print 'This will never print.'
Joooeey
  • 3,394
  • 1
  • 35
  • 49
0

I've taken a more flexible approach inspired by functools.partial function:

DO_ONCE_MEMORY = []

def do_once(id, func, *args, **kwargs):
    if id not in DO_ONCE_MEMORY:
        DO_ONCE_MEMORY.append(id)
        return func(*args, **kwargs)
    else:
        return None

With this approach you are able to have more complex and explicit interactions:

do_once('foobar', print, "first try")
do_once('foo', print, "first try")
do_once('bar', print, "second try")
# first try 
# second try 

The exciting part about this approach it can be used anywhere and does not require factories - it's just a small memory tracker.

Granitosaurus
  • 20,530
  • 5
  • 57
  • 82
0

Depending on the situation, an alternative to the decorator could be the following:

from itertools import chain, repeat

func_iter = chain((myFunction,), repeat(lambda *args, **kwds: None))
while True:
    next(func_iter)()

The idea is based on iterators, which yield the function once (or using repeat(muFunction, n) n-times), and then endlessly the lambda doing nothing.

The main advantage is that you don't need a decorator which sometimes complicates things, here everything happens in a single (to my mind) readable line. The disadvantage is that you have an ugly next in your code.

Performance wise there seems to be not much of a difference, on my machine both approaches have an overhead of around 130 ns.

DerWeh
  • 1,721
  • 1
  • 15
  • 26
-1

If the condition check needs to happen only once you are in the loop, having a flag signaling that you have already run the function helps. In this case you used a counter, a boolean variable would work just as fine.

signal = False
count = 0 
def callme(): 
     print "I am being called"  
while count < 2: 
     if signal == False : 
         callme()
         signal = True
     count +=1
Graeme Perrow
  • 56,086
  • 21
  • 82
  • 121
J K
  • 1,597
  • 1
  • 12
  • 12
-2

I'm not sure that I understood your problem, but I think you can divide loop. On the part of the function and the part without it and save the two loops.

Asar
  • 65
  • 3