2

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

manlio
  • 18,345
  • 14
  • 76
  • 126
TwoPointOH
  • 41
  • 3
  • 7
  • related: [How to run a function periodically in python](http://stackoverflow.com/a/26609843/4279) – jfs Jul 04 '15 at 18:05
  • related: [Trying to simulate constant byte rate. Confusion with time.sleep results](http://stackoverflow.com/questions/26595419/trying-to-simulate-constant-byte-rate-confusion-with-time-sleep-results#comment41808839_26595419) – jfs Jul 04 '15 at 18:06
  • in this [blog entry](http://sahandsaba.com/understanding-asyncio-node-js-python-3-4.html) is the description how to do something similar with the new asyncio. maybe that helps. – hiro protagonist Jul 04 '15 at 18:06

2 Answers2

2

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.

fferri
  • 18,285
  • 5
  • 46
  • 95
  • When I run this code, it prints out the correct number, but it prints it twice. Example: 00, 11, 22, 33, 44, 55, 66, 77, 88, 99, 1010, 1111, etc. I'm tried manipulating the code, but I don't understand why this is happening. It seems to be printing the 'i' value two times per line. – TwoPointOH Jul 05 '15 at 17:40
  • that's because it's writing twice: once with `print` and once with `sys.stdout.write` – fferri Jul 05 '15 at 17:41
  • I've tried removing either the `print` or the `sys.stdout.write` statement, but then it begins to print off two numbers per action. It will print 1 and then 2 on separate lines (at the same time), then 3 and 4, then 5 and 6... etc. I only have one print statement, so I am not sure why this is happening. It also appears to print every 2 seconds, so the timing is right, but I am trying to have it print one number per line. – TwoPointOH Jul 05 '15 at 18:00
0

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.

dlask
  • 8,776
  • 1
  • 26
  • 30