Surprised to not find a solution using a generator for timing. I just designed this one for my own purposes.
This solution: single threaded, no object instantiation each period, uses generator for times, rock solid on timing down to precision of the time
module (unlike several of the solutions I've tried from stack exchange).
Note: for Python 2.x, replace next(g)
below with g.next()
.
import time
def do_every(period,f,*args):
def g_tick():
t = time.time()
while True:
t += period
yield max(t - time.time(),0)
g = g_tick()
while True:
time.sleep(next(g))
f(*args)
def hello(s):
print('hello {} ({:.4f})'.format(s,time.time()))
time.sleep(.3)
do_every(1,hello,'foo')
Results in, for example:
hello foo (1421705487.5811)
hello foo (1421705488.5811)
hello foo (1421705489.5809)
hello foo (1421705490.5830)
hello foo (1421705491.5803)
hello foo (1421705492.5808)
hello foo (1421705493.5811)
hello foo (1421705494.5811)
hello foo (1421705495.5810)
hello foo (1421705496.5811)
hello foo (1421705497.5810)
hello foo (1421705498.5810)
hello foo (1421705499.5809)
hello foo (1421705500.5811)
hello foo (1421705501.5811)
hello foo (1421705502.5811)
hello foo (1421705503.5810)
Note that this example includes a simulation of the cpu doing something else for .3 seconds each period. If you changed it to be random each time it wouldn't matter. The max in the yield
line serves to protect sleep
from negative numbers in case the function being called takes longer than the period specified. In that case it would execute immediately and make up the lost time in the timing of the next execution.