It is necessary to make a cycle that will function for a minute with an interval of 1 second.

What is the most effective way? Couldn't think of anything better than this:

from time import sleep seconds_left = 60 while seconds_left > 0: do_something() seconds_left -= 1 sleep(1) 

    4 answers 4

    You can use range(n) to repeat something n times in Python:

     for _ in range(60): do_something() time.sleep(1) 

    Is this a “correct” and “effective” way? It depends on the specific task.

    The code is simple and straightforward: call the do_something() function 60 times, each next call occurs no earlier than a second after the previous call ends. At the end wait a second before completing the cycle.

    Imagine do_something() requires half a second on average, then the cycle takes a minute and a half or more. Whether this is good depends on the task.

    If you want calls to occur on the border of every second:

     for _ in range(60): do_something() time.sleep(1 - time.time() % 1) 

    With accuracy up to the activity of other processes, threads (features of process planners / threads in the operating system, features of GIL implementing the selected version of Python interpreter), each call to do_something() except the first one occurs close to the time when time.time() returns integer values.

    For example, if time.time() returns X.3 seconds, time.sleep() will sleep at least 0.7 seconds (if the signal handler does not throw an exception), then the next call to do_something() will occur in X + 1 seconds by system time (if it is not much jumped while we slept). In this case, the do_something() calls can be more evenly distributed and the loop will end almost exactly in a minute according to time.time() (if the execution time of do_something() less than a second). Whether this is good depends on the task.

    If you do not want to run do_something() , if more than a minute has passed according to the selected timer, you can use the explicit condition:

     deadline = time.monotonic() + 60 while time.monotonic() < deadline: do_something() time.sleep(1) 

    Things that need to be taken into account depending on the task:

    • What is the desired behavior, if do_something() can run for more than a second: skip the iteration, run in a separate thread, thread / process pool?
    • What is the desired behavior if the system time jumps (because someone closed the laptop lid during a cycle or hibernation of the system for any other reason, or the mobile OS shipped your process to save energy over time, or the code inside the VM — large jumps are possible). Do you want to continue the cycle as if nothing happened when the system wakes up, or cancel subsequent calls (outdated), or make the remaining calls as quickly as possible, without a pause or even all at once (deadline passed)?

    For example, if the time changes during the execution of the print_some_times() from the @ReinRaus response , what do you think will happen? Is this guaranteed behavior by the documentation of the sched module (will it change between different implementations / versions of Python)? Is this important in your particular case? (I think the clarity of a simple sleep() loop can be especially attractive at this point).

    As a variation on the theme, you can periodically call a function in the background thread or use the capabilities of various event loops . In more complex cases, if you do not have special preferences, you can use the apscheduler module to embed the scheduler in your application . In some cases, it makes sense to use the system scheduler (for large intervals) such as cron and Windows Task Scheduler.

    It is useful to know the reasons why the call_repeatedly() function was removed from the PEP 3156 - Asynchronous IO Support Rebooted: the "asyncio" Module :

    Note: A previous version of this method is defined by the method named call_repeatedly() , which is called a callback at regular intervals. This has been withdrawn. On the one hand, it is easy to emulate using a callback that reschedules itself using call_later() ; it is also easy to write a message (a toplevel function in the module, see below). There are many traps and pitfalls. It is impossible to offer an app.

       import sched, time s = sched.scheduler(time.time, time.sleep) def print_time(): print ("From print_time", time.time()) time.sleep(.2) def print_some_times(): print ( time.time() ) for i in range( 60 ): s.enter(i, 1, print_time, () ) s.run() print ( time.time() ) print_some_times() 

      The advantage of this approach is that despite the long execution of the print_time function (0.2 seconds), the function starts with very high accuracy (I have millisecond accuracy):

       1476319996.9365706 From print_time 1476319996.9434264 From print_time 1476319997.9441211 ... From print_time 1476320014.944284 From print_time 1476320015.9442484 1476320016.1564507 

        We write the decorator to do functions with "lag"

         def make_delayed(delay_in_seconds): """ Decorator with parameter for making functions launched with minimal delay between calls """ if delay_in_seconds <= 0.: raise ValueError("Non-positive delay: {}".format(delay_in_seconds)) class CallDelayer: def __init__(self): self.call_event = threading.Event() self.last_call_time = time.time() def launch_with_delay(self, function): def launched_with_delay(*args, **kwargs): self.last_call_time += delay_in_seconds function(*args, **kwargs) wait_sec = self.last_call_time - time.time() self.call_event.wait(wait_sec) print(wait_sec) return launched_with_delay call_delayer = CallDelayer() return call_delayer.launch_with_delay 

        then we decorate our do_something and just call the required number of times

         do_something_delayed = make_delayed(1.)(do_something) for i in range(60): do_something_delayed() 

        the advantage of this approach is high accuracy, the use of standard library tools, versatility, the downside may be that decorators are not to everyone’s liking

          If you need to perform at intervals (between the end of one call and the beginning of another) for 1 minute, then the cycle body is correct, but the condition is not very:

           import time TIME_TO_LOOP = 60 start = time.time() while time.time() < start + TIME_TO_LOOP: do_something() time.sleep(1) 
          • Your way is good because it takes into account the iteration time. In other cases, if the iteration is performed, for example, 0.5 seconds, then the total time will be 90 seconds. - Max
          • @Max my answer explicitly contains a loop with a time limit and explicitly mentions an example with half a second. I understand that my answer is great (to read for a long time): but there are many possible options — and the point is that in different tasks different options are more “correct” (which is said at the very beginning of my answer). This response ignores many likely situations. - jfs
          • @jfs later I reread your answer too and noticed this. Just put Like. - Max
          • @Max if you have any comments or something is not clear in the answer, feel free to leave comments. Maybe it was worth time.time() difference between time.time() and time.monotonic() ? Or, for example, is it clear why , and how time.sleep(1 - time.time() % 1) construction is used? - jfs
          • @Max if you look closely at the condition, the cycle, then there is no counter in it, but there is a time limit. I carefully read the condition of the problem - the interval is 1 second, the execution time is 60 seconds. Of course, you can provide half a million other conditions, but in the formulation of the original problem of those conditions are not. - ChCh