I'm relatively new to Python so I don't know how difficult or easy this is to solve, but I'm trying to make a function that can measure time without blocking other code from executing while doing so. Here's what I have:
import time
def tick(secs):
start = time.time()
while True:
end = time.time()
elapsed = end - start
if elapsed >= secs:
return elapsed
break
input("what time is it?: ")
print(f"one: {round(tick(1))}")
print(f"two: {round(tick(2))}")
print(f"three: {round(tick(3))}")
print(f"four: {round(tick(4))}")
print(f"five: {round(tick(5))}")
The input blocks the timer from starting until it gets input, and the tick()'s after dont run simultaneously. Thus running one at a time like, wait 1 second then wait 2 seconds instead of wait 5 seconds (to be clear I want all timers that are started to run at the same time others are, so the 5 second timer would start at the same time the 1 second one does), thank you for your time and please let me know if you have a solution for this.
Not exactly sure what you are asking for, but how does this look:
import time
start=time.time()
input("what time is it?: ")
time.sleep(1)
print(time.time()-start)
time.sleep(2)
print(time.time()-start)
time.sleep(3)
print(time.time()-start)
time.sleep(4)
print(time.time()-start)
time.sleep(5)
print(time.time()-start)
The tick's are not running simultanously because your are first waiting for 1 second, then again you are waiting for 2, then again for 3, etc.
A simple thing to do is have a list of time intervals you want to "pause" at, in your case [1, 2, 3, 4, 5] which are sorted numerically. You will then keep track of the current index by checking elapsed >= secs and if it succedds you will increment it by one. Here's a glance
import time
def tick(tocks: list):
"""Tocks is a list of the time intervals which you want to be
notified at when are reached, all of them are going to run in parallel.
"""
tocks = sorted(tocks)
current = 0 # we are at index 0 which is the lowest interval
start = time.time()
while current < len(tocks): # while we have not reached the last interval
end = time.time()
elapsed = end - start
if elapsed >= tocks[current]: # if the current interval has passed check for the next
print(f"Tock: {tocks[current]}")
current += 1
This function can then be called like this
tick([1, 2, 3, 4, 5])
This will print 1 to 5 seconds at the same time. Here is the output
Tock: 1 # at 1 sec
Tock: 2 # at 2 sec
Tock: 3 # at 3 sec
Tock: 4 # .....
Tock: 5
You can imagine that this may have some minor flaws if you choose really close numbers like [0.5, 0.50001, 0.50002] and because the difference is so small 0.0001 seconds may not actually pass.
You could also try multithreading as has been noted, but it will be a very CPU-intensive (imagine wanting to count to 100 and you have to open 100 threads) task for something very simple.
Related
Complete newbie here so bare with me. I've got a number of devices that report status updates to a singular location, and as more sites have been added, drift with time.sleep(x) is becoming more noticeable, and with as many sites connected now it has completely doubles the sleep time between iterations.
import time
...
def client_list():
sites=pandas.read_csv('sites')
return sites['Site']
def logs(site):
time.sleep(x)
if os.path.isfile(os.path.join(f'{site}/target/', 'hit')):
stamp = time.strftime('%Y-%m-%d,%H:%M:%S')
log = open(f"{site}/log", 'a')
log.write(f",{stamp},{site},hit\n")
log.close()
os.remove(f"{site}/target/hit")
else:
stamp = time.strftime('%Y-%m-%d,%H:%M:%S')
log = open(f"{site}/log", 'a')
log.write(f",{stamp},{site},miss\n")
log.close()
...
if __name__ == '__main__':
while True:
try:
client_list()
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.map(logs, client_list())
...
I did try adding calculations for drift with this:
from datetime import datetime, timedelta
def logs(site):
first_called=datetime.now()
num_calls=1
drift=timedelta()
time_period=timedelta(seconds=5)
while 1:
time.sleep(n-drift.microseconds/1000000.0)
current_time = datetime.now()
num_calls += 1
difference = current_time - first_called
drift = difference - time_period* num_calls
if os.path.isfile(os.path.join(f'{site}/target/', 'hit')):
...
It ends up with a duplicate entries in the log, and the process still drifts.
Is there a better way to schedule the function to run every x seconds and account for the drift in start times?
Create a variable equal to the desired system time at the next interval. Increment that variable by 5 seconds each time through the loop. Calculate the sleep time so that the sleep will end at the desired time. The timings will not be perfect because sleep intervals are not super precise, but errors will not accumulate. Your logs function will look something like this:
def logs(site):
next_time = time.time() + 5.0
while 1:
time.sleep(time.time() - next_time)
next_time += 5.0
if os.path.isfile(os.path.join(f'{site}/target/', 'hit')):
# do something that takes a while
So I managed to find another route that doesn't drift. The other method still drifted over time. By capturing the current time and seeing if it is divisible by x (5 in the example below) I was able to keep the time from deviating.
def timer(t1,t2)
return True if t1 % t2 == 0 else False
def logs(site):
while 1:
try:
if timer(round(time.time(), 0), 5.0):
if os.path.isfile(os.path.join(f'{site}/target/', 'hit')):
# do something that takes a while
time.sleep(1) ''' this kept it from running again immediately if the process was shorter than 1 second. '''
...
I have a question on how I am able to set the timer so that every time it exits the loop it sets the time back to 2 seconds. The problem is that the first time the sound works after 2 seconds, the next times it is executed immediately. Thank you very much in advance for any advice.
This is my code:
time = 2
while time > 0:
timer = datetime.timedelta(seconds=time)
time -= 1
duration = 1000
freq = 440
winsound.Beep(freq, duration)
I am not sure if you meant that, but for me it seems like you just want to wait 2 seconds before executing the next steps. You can do that like so:
import time
while True:
time.sleep(2) # waits 2 seconds
winsound.Beep(440, 1000)
Anyways I don't recommend you to use a plain infinite loop, without a break statement. Therefore I recommend you to add one, like down below.
import time
while True:
time.sleep(2) # waits 2 seconds
winsound.Beep(440, 1000)
if True: # break on a specific statment
break
Edit: As CrazyChucky mentioned in the comments, this approach should work fine in most of the cases, but it can end up being more than two seconds sometimes. Therefore you should work with timedeltas or take a look at scheduler.
To be more accurate as possible use:
import time
timer = 0
step = 2
t0 = time.time()
while True:
timer = time.time() - t0
wait = step - timer
time.sleep(wait)
print(time.time())
winsound.Beep(freq, duration)
t0 = time.time()
This script take in count the execution time of script lines for your computer.
You just have to reinitialize the time at the end of the loop
time = 2
while True:
timer = datetime.timedelta(seconds=time)
time -= 1
duration = 1000
freq = 440
if time == 0:
time = 2
break
winsound.Beep(freq, duration)
I'm making a program that runs something for the amount of minutes the user alots (it's an idle game in beta). I put on a timer for one minute and noticed that the program ran over the minute by a couple of seconds-- Not very noticable, but I was wondering if this is because of how long a loop takes to execute? This is my code:
import time
foreverloop = True
automodeOn = False
idleSec = 0
idleMin = 0
pages = 0
pps = 0
while foreverloop:
if automodeOn == False:
msg = input("BTCG Command >> ")
if msg == 'auto':
autotime = input("How long would you like to go idle for? Answer in minutes.")
automodeOn = True
elif msg == 'autoMORE':
pps += .5
else:
pages += pps
print("You have auto-read",pps,"pages.")
idleSec += 1
if idleSec == 60:
idleSec = 0
idleMin += 1
if idleMin == int(autotime):
print("Idle mode turning off.")
automodeOn = False
time.sleep(1)
You could measure the time it takes for a number of lines of code to execute by measuring the start time:
start = time.time()
before any number of lines you'd like to measure the time, then at the end adding:
end = time.time()
the time elapse is then calculated as their subtraction:
elapsed_time = end-start
I suggest that you read about code complexity, the most popular of which is the Big O notation.
edit: as denoted in a comment, timeit is the better option if you're looking to precisely measure the time it takes for a certain line or function to execute, the main difference between the 2 approaches is that timeit is made specifically for this purpose and as part of this takes as a parameter a variable number indicating the number of times the specified code is run before determining how long it takes on average to run.
Instead of making the program wait in adittion to the time it takes to execute, I would use time.time() to get the system's current UNIX time in seconds as a float and only continue if a certain time has passed:
import time
time_begin = time.time()
wait_time = 60 # seconds to wait
while time.time() < time_begin + wait_time:
# do logic
print("Time passed:", time.time() - time_begin)
time.sleep(1) # can be whatever
print(wait_time, "seconds has passed!")
I have a Python 3 script that will run an infinite loop, 24/7. It makes a website connection and goes to a certain webpage to download a file.
Each iteration will take about a minute, but I need the script to start each iteration at exact times, about 5 minutes apart. For instance: at 1 minute after the hour, 6 minutes after the hour, 11 minutes after the hour, etc. I need the script to sleep between iterations until the exact time to start again. Just wondering if there's a clean way to do this?
EDIT: Based on answers given so far, I think I need to re-phrase my question. Basically, what is needed is to calculate the time between the time the last iteration ended and when I want the next iteration to begin. In other words, I need the difference between now, the current time, and the next time the minute of the time is either 01, 06, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56 and the seconds of the time is 00. Then I can time.sleep() the script for that amount of time.
Check out the requests library for making web connections and put the script in a cron job to have it run every 5 min.
Well, I believe they call this a "kludge". I posted the below with a bunch of comments and print lines left in for clarification and to see how this works. In any event, this seems to be working pretty well. Each loop will begin (except the first loop) at either ?1:30 and ?6:30, exactly 5 minutes apart and within just a few milliseconds of when the loop is due to start. Of course, it will need to be edited if you decide to use it but, maybe this is a helpful start.
##### import modules
import time
import datetime
from datetime import datetime
from datetime import timedelta
##### create list of possible loop start times; this here creates start times every 5 minutes, starting the loops at 01:30, 06:30, 11:30, etc., after the hour
dt_cur = datetime.now()
dt_4_list = datetime.strptime(str(dt_cur.year) + '-' + str(dt_cur.month) + '-' + str(dt_cur.day) + ' ' + str(dt_cur.hour) + ':1:30', '%Y-%m-%d %H:%M:%S')
ls_dt = [dt_4_list]
for i in range(288):
dt_4_list = dt_4_list + timedelta(minutes = 5)
ls_dt.append(dt_4_list)
##### begin the loop
loop_timer = 0
while loop_timer < 275:
if loop_timer > 0:
##### check if the loop started before the exact time due to time.sleep() not being exactly accurate
time_check = datetime.now().strftime("%S.%f")
time_check = float(time_check)
loop_time_check = 0
while time_check < 30 and loop_time_check < 100:
time_check = datetime.now().strftime("%S.%f")
time_check = float(time_check)
print(time_check, '- WARNING: time_check < 30; loop =', loop_time_check + 1, flush=True)
time.sleep(.025)
loop_time_check += 1
if loop_time_check == 100:
print(time_check, '- WARNING: exiting script early; time_check < 30 100 times', flush=True)
raise SystemExit()
########################################################################################################################
##### start doing stuff here ##########################################################################################
dt_cur = datetime.now()
print(dt_cur, flush=True)
for i in range(5): ##### this is where your own script does it's work before resting until the next loop
print(i, flush=True)
time.sleep(1)
##### end doing stuff here #############################################################################################
########################################################################################################################
##### create the current datetime var
dt_cur = datetime.now()
print(dt_cur, flush=True)
##### create the dt_next_loop time var, increment the loop
for d in ls_dt:
if d > dt_cur:
dt_next_loop = d
break
loop_timer += 1
##### get the seconds to sleep the script but make it about 5 seconds short of the actual time to begin the next loop
sleep_secs = (dt_next_loop - dt_cur) - timedelta(seconds=5)
sleep_secs = timedelta.total_seconds(sleep_secs)
time.sleep(sleep_secs)
##### sleep the script about 5 seconds more before the actual time to begin the next loop (sleep.time() is not precisely accurate
##### over minutes and doing this again 5 seconds before the time to begin the next loop will ensure more accuracy)
sleep_secs = datetime.now().strftime("%S.%f")
sleep_secs = 30 - float(sleep_secs)
time.sleep(sleep_secs)
First time poster here, wondering what the best way is to check the times a function is returning a value in a certain period of time. For example: x is returning 7 times a value in 10 seconds. I have a measure_time function which returns the time it takes to tap a certain pattern of keys. In the while loop i'm checking the elapsed time and inside the while i want to check if the measure_time function returns something. Any suggestions on where i go wrong?
start = time.time()
time.clock()
elapsed = 0
runs = 0
while elapsed < seconds:
elapsed = time.time() - start
if self.measure_time():
runs += 1