What is the best way to repeatedly execute a function every x seconds?

后端 未结 18 2682
不知归路
不知归路 2020-11-21 06:04

I want to repeatedly execute a function in Python every 60 seconds forever (just like an NSTimer in Objective C). This code will run as a daemon and is effectively like call

相关标签:
18条回答
  • 2020-11-21 06:18

    I ended up using the schedule module. The API is nice.

    import schedule
    import time
    
    def job():
        print("I'm working...")
    
    schedule.every(10).minutes.do(job)
    schedule.every().hour.do(job)
    schedule.every().day.at("10:30").do(job)
    schedule.every(5).to(10).minutes.do(job)
    schedule.every().monday.do(job)
    schedule.every().wednesday.at("13:15").do(job)
    schedule.every().minute.at(":17").do(job)
    
    while True:
        schedule.run_pending()
        time.sleep(1)
    
    0 讨论(0)
  • 2020-11-21 06:20

    If your program doesn't have a event loop already, use the sched module, which implements a general purpose event scheduler.

    import sched, time
    s = sched.scheduler(time.time, time.sleep)
    def do_something(sc): 
        print("Doing stuff...")
        # do your stuff
        s.enter(60, 1, do_something, (sc,))
    
    s.enter(60, 1, do_something, (s,))
    s.run()
    

    If you're already using an event loop library like asyncio, trio, tkinter, PyQt5, gobject, kivy, and many others - just schedule the task using your existing event loop library's methods, instead.

    0 讨论(0)
  • 2020-11-21 06:20

    I use Tkinter after() method, which doesn't "steal the game" (like the sched module that was presented earlier), i.e. it allows other things to run in parallel:

    import Tkinter
    
    def do_something1():
      global n1
      n1 += 1
      if n1 == 6: # (Optional condition)
        print "* do_something1() is done *"; return
      # Do your stuff here
      # ...
      print "do_something1() "+str(n1)
      tk.after(1000, do_something1)
    
    def do_something2(): 
      global n2
      n2 += 1
      if n2 == 6: # (Optional condition)
        print "* do_something2() is done *"; return
      # Do your stuff here
      # ...
      print "do_something2() "+str(n2)
      tk.after(500, do_something2)
    
    tk = Tkinter.Tk(); 
    n1 = 0; n2 = 0
    do_something1()
    do_something2()
    tk.mainloop()
    

    do_something1() and do_something2() can run in parallel and in whatever interval speed. Here, the 2nd one will be executed twice as fast.Note also that I have used a simple counter as a condition to terminate either function. You can use whatever other contition you like or none if you what a function to run until the program terminates (e.g. a clock).

    0 讨论(0)
  • 2020-11-21 06:24

    If you want a non-blocking way to execute your function periodically, instead of a blocking infinite loop I'd use a threaded timer. This way your code can keep running and perform other tasks and still have your function called every n seconds. I use this technique a lot for printing progress info on long, CPU/Disk/Network intensive tasks.

    Here's the code I've posted in a similar question, with start() and stop() control:

    from threading import Timer
    
    class RepeatedTimer(object):
        def __init__(self, interval, function, *args, **kwargs):
            self._timer     = None
            self.interval   = interval
            self.function   = function
            self.args       = args
            self.kwargs     = kwargs
            self.is_running = False
            self.start()
    
        def _run(self):
            self.is_running = False
            self.start()
            self.function(*self.args, **self.kwargs)
    
        def start(self):
            if not self.is_running:
                self._timer = Timer(self.interval, self._run)
                self._timer.start()
                self.is_running = True
    
        def stop(self):
            self._timer.cancel()
            self.is_running = False
    

    Usage:

    from time import sleep
    
    def hello(name):
        print "Hello %s!" % name
    
    print "starting..."
    rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
    try:
        sleep(5) # your long-running job goes here...
    finally:
        rt.stop() # better in a try/finally block to make sure the program ends!
    

    Features:

    • Standard library only, no external dependencies
    • start() and stop() are safe to call multiple times even if the timer has already started/stopped
    • function to be called can have positional and named arguments
    • You can change interval anytime, it will be effective after next run. Same for args, kwargs and even function!
    0 讨论(0)
  • 2020-11-21 06:25

    You might want to consider Twisted which is a Python networking library that implements the Reactor Pattern.

    from twisted.internet import task, reactor
    
    timeout = 60.0 # Sixty seconds
    
    def doWork():
        #do work here
        pass
    
    l = task.LoopingCall(doWork)
    l.start(timeout) # call every sixty seconds
    
    reactor.run()
    

    While "while True: sleep(60)" will probably work Twisted probably already implements many of the features that you will eventually need (daemonization, logging or exception handling as pointed out by bobince) and will probably be a more robust solution

    0 讨论(0)
  • 2020-11-21 06:27

    This seems much simpler than accepted solution - does it have shortcomings I'm not considering? Came here looking for some dead-simple copy pasta.

    import threading, time
    
    def print_every_n_seconds(n=2):
        while True:
            print(time.ctime())
            time.sleep(n)
        
    thread = threading.Thread(target=print_every_n_seconds, daemon=True)
    thread.start()
    
    

    Which asynchronously outputs.

    #Tue Oct 16 17:29:40 2018
    #Tue Oct 16 17:29:42 2018
    #Tue Oct 16 17:29:44 2018
    

    This does have drift. If the task being run takes appreciable amount of time, then the interval becomes 2 seconds + task time, so if you need precise scheduling then this is not for you.**

    Note the daemon=True flag means this thread won't block the app from shutting down. For example, had issue where pytest would hang indefinitely after running tests waiting for this thead to cease.

    0 讨论(0)
提交回复
热议问题