3

i have

for i in xrange(repeat):
  #does my stuff
  time.sleep(10)

BUT, i don't want it to sleep if it's the last run. Meaning, if there's no more iteration, don't sleep

How do I gracefully handle this?

user1008636
  • 2,989
  • 11
  • 31
  • 45

4 Answers4

5
case of 1x repetition: doStuff
case of 2x repetition: doStuff, sleep, doStuff
case of 3x repetition: doStuff, sleep, doStuff, sleep, doStuff
...

What you seem to want is to do a bunch of things, then sleep between each of them.

Furthermore, the delay is independent of the nature of the tasks.


Solution 1 -- Sleep if it isn't the last

The easiest way is to special-case the for loop:

for i in range(numTimesToRepeat):
    if i>0:
        time.sleep(lengthToSleep)
    doStuff()

You could also do the following, but it's much less elegant because you repeat doStuff():

doStuff()
for i in range(numTimesToRepeat-1):
    time.sleep(lengthToSleep)
    doStuff()

Solution 2 -- While loop with break

One of the most straightforward ways to solve these issues is to break out of a while loop. It's dirty and somewhat non-functional-programming, but it's very much how a human might naturally think of the problem:

numTimesToRepeat = ...
while True:
    doStuff()                     # we did stuff
    numTimesToRepeat -= 1         # repeat one less time because we just did it
    if numTimesToRepeat==0:       # do we repeat again?
        break                     #   no -> done!
    else:
        time.sleep(lengthToSleep) #   yes -> sleep before repeating

While loops are annoying because if you make a programming error, your program will infinite-loop.


Solution 3 -- Detect if last

The above methods do something if it's not the first iteration of the loop. However, "detecting the last iteration of the loop" is how you tried to approach the problem. It is a bit more annoying, but you can do it like this: https://stackoverflow.com/a/6090673/711085 and use it like this:

for i,task,isLast in enumerateLast(tasks):
    task()
    if not isLast:
        time.sleep(lengthToSleep)

Less elegant is to reverse the list and do enumerate():

for untilLast,task in reversed(enumerate(reversed(tasks))):
    task()
    if untilLast>0:
        time.sleep(lengthToSleep)

(very small irrelevant minutiae: do note that in the reversed(enumerate(reversed(iterable))) case, if tasks were a generator, it would generate every single task to do, enumerate them in reverse order, then do them: this can cause lag, and also prevents you from creating an infinite task generator)


Task abstraction

No matter which way you do it, you may wish to abstract the concept of having tasks by creating callbacks, into which you insert a delay between each, sort of like a ''.join.

def callAndWaitBetween(tasks):
    for ..task.. in enumerate?(tasks):
        #... some solution to this problem...
        task()

For reference, if the delay depended on the tasks, then your tasks should return how long to wait.

Community
  • 1
  • 1
ninjagecko
  • 88,546
  • 24
  • 137
  • 145
  • For anyone looking at this and wanting something that works with a possibly infinite iterator, you might try peeking ahead (i.e. with a copy of your iterator using itertools.tee) to see if you're at the end. Note that if the generator has blocking operations, there will be a lag before you can consume things, since you need to wait for a future event to yield before you can tell if it's the end of the iterator or not! – btown Mar 30 '15 at 21:39
  • btown: ah interesting if willing to risk overshooting the delay time... you could take a snapshot of the current time, get the iterator's .next(), subtract that from the current time to get how long the iterator took, and subtract that from the fixed delay, so your delay will remain fixed – ninjagecko Mar 31 '15 at 04:14
2

try this:

for i in xrange(repeat):
  if i<repeat:
    time.sleep(10)
schacki
  • 9,401
  • 5
  • 29
  • 32
2

If all you do is sleeping, you can do

for i in range(repeat)[:-1]:
  time.sleep(10)

But if you actually need to perform some type of action, then:

sleep = [lambda: time.sleep(20) for i in range(repeat)[:-1]] + [lambda: None]

for i in xrange(repeat):
  # do stuff
  sleep.pop(0)()

If you really have a lot of iteration, use itertools to create a generator instead of a list for sleep.

Bite code
  • 578,959
  • 113
  • 301
  • 329
0

A while loop might work

while repeat > 0:
    #do stuff
    if repeat > 1:
        time.sleep(10)
    repeat -= 1

Just set repeat directly to the number of times the loops is to be repeated instead of using xrange.

underbar
  • 588
  • 3
  • 15