TwoPointOH
TwoPointOH

Reputation: 41

Printing function every second?

My goal is to be able to run a function and print the result every second. I want it to look something like this:

printing:

"At {seconds} you have {value}." // Where the value comes from another function we can also assume that the value goes up by something constant, but is calculated by another function.

And to continue printing this on new lines for each whole second. I have come across two problems so far.

1) I am unable to write a function that prints a value every second. I've tried using threading and time, but I haven't been able to get it work.

2) If I set a sleep timer for 1 second, it must take several milliseconds (or way smaller time measurements) to then calculate the function before it prints the above line. I figured that after several hundred/thousand calculations, my time and values would no longer be accurate.

So far, I have borrowed this from another thread:

 def update(i):
  threading.Timer(1, update, [i]).start()
  sys.stdout.write(str(i)+'\r')
  sys.stdout.flush()
  print(i)
  i += 1

This function doesn't work how I imagined. I thought that it would up the value of i by one each time, and put that value back in the update() function. It just keeps printing out the same value each time. Honestly, I'm not sure why, and I tried going line by line to figure it out, but alas I cannot.

Thanks

-2.0

Upvotes: 2

Views: 4785

Answers (2)

fferri
fferri

Reputation: 18940

Move threading.Timer(1, update, [i]).start() to the end of the function, and it will print incrementing values correctly.

If you want to have an accurate frequency of 1 Hz in the long run, store last execution time (time.time()) in an additional argument, and compensate the interval passed to Timer, e.g.:

def update(i, last_time=time.time()-1):
    sys.stdout.write(str(i)+'\r')
    sys.stdout.flush()
    print(i)
    i += 1
    cur_time = time.time()
    threading.Timer(2-(cur_time-last_time), update, [i, cur_time]).start()

Even if you have some local drift (due the the scheduling of threads which is not always instantaneous), it will recover in the next step(s).

You can try this yourself by putting a time.sleep(random.random()*0.6) after i += 1.

Upvotes: 2

dlask
dlask

Reputation: 8982

Avoid cumulative error by calculating the expected moments precisely but calling the sleep function with the actual remaining time till the end of the current interval.

import datetime
import time

tstep = datetime.timedelta(seconds=1)
tnext = datetime.datetime.now() + tstep
while True:
    print "something"
    tdiff = tnext - datetime.datetime.now()
    time.sleep(tdiff.total_seconds())
    tnext = tnext + tstep

If your processing time stays below 1 second, this procedure correctly compensates it.

Upvotes: 0

Related Questions