Qual é a melhor maneira de executar repetidamente uma função a cada x segundos?


281

Quero executar repetidamente uma função em Python a cada 60 segundos para sempre (assim como um NSTimer no Objective C). Esse código será executado como um daemon e é efetivamente como chamar o script python a cada minuto usando um cron, mas sem exigir que isso seja configurado pelo usuário.

Em esta pergunta sobre um cron implementado em Python , a solução parece efetivamente apenas sleep () para x segundos. Eu não preciso de funcionalidades tão avançadas, então talvez algo assim funcione

while True:
    # Code executed here
    time.sleep(60)

Existem problemas previsíveis com este código?


83
Um ponto pedante, mas pode ser crítico, seu código acima do código não é executado a cada 60 segundos e coloca um intervalo de 60 segundos entre as execuções. Isso só acontece a cada 60 segundos se o código executado não demorar.
Simon

4
também time.sleep(60)pode retornar tanto mais cedo e mais tarde
JFS

5
Ainda estou pensando: há algum problema previsível com esse código?
Banana

1
O "problema previsível" é que você não pode esperar 60 iterações por hora usando apenas time.sleep (60). Portanto, se você estiver anexando um item por iteração e mantendo uma lista de tamanho definido ... a média dessa lista não representará um "período" consistente de tempo; portanto, funções como "média móvel" podem fazer referência a pontos de dados muito antigos, o que distorcerá sua indicação.
Litepresence

2
@Banana Sim, você pode esperar qualquer problema causado porque seu script não é executado EXATAMENTE a cada 60 segundos. Por exemplo. Comecei a fazer algo assim para dividir os fluxos de vídeo e enviá-los, e acabei ficando com sequências de 5 a 10 segundos porque a fila de mídia está em buffer enquanto eu processo dados dentro do loop. Depende dos seus dados. Se a função é algum tipo de vigilância simples que avisa, por exemplo, quando o disco está cheio, você não deve ter problemas com isso.Se você estiver checando os alertas de aviso de uma usina nuclear, pode acabar com uma cidade completamente explodido x #
DGoiko 31/12/18

Respostas:


228

Se o seu programa não tem um ciclo de eventos já, use o sched módulo, que implementa um evento planejador de propósito geral.

import sched, time
s = sched.scheduler(time.time, time.sleep)
def do_something(sc): 
    print("Doing stuff...")
    # do your stuff
    s.enter(60, 1, do_something, (sc,))

s.enter(60, 1, do_something, (s,))
s.run()

Se você já estiver usando uma biblioteca de ciclo de eventos como asyncio, trio, tkinter, PyQt5, gobject, kivy, e muitos outros - basta agendar a tarefa usando métodos da sua biblioteca ciclo de eventos existente, em vez disso.


16
O módulo sched é para funções de agendamento executadas após algum tempo, como você o utiliza para repetir uma chamada de função a cada x segundos sem usar time.sleep ()?
Baishampayan Ghose

2
@Baishampayan: Basta agendar uma nova execução.
Nosklo 23/01/09

3
Então apscheduler em packages.python.org/APScheduler também deve ser mencionado neste momento.
Daniel F

6
nota: esta versão pode derivar. Você poderia usar enterabs()para evitá-lo. Aqui está uma versão não-drifting para comparação .
jfs

8
@JavaSa: porque "faça suas coisas" não é instantâneo e erros time.sleeppodem acumular-se aqui. "executar a cada X segundos" e "executar com um atraso de ~ X segundos repetidamente" não são os mesmos. Veja também este comentário
jfs 25/01

180

Basta travar seu loop de tempo no relógio do sistema. Fácil.

import time
starttime=time.time()
while True:
  print "tick"
  time.sleep(60.0 - ((time.time() - starttime) % 60.0))

22
+1. a sua e a twistedresposta são as únicas respostas que executam uma função a cada xsegundo. O restante executa a função com um atraso de xsegundos após cada chamada.
jfs

13
Se você adicionar um código a isso que demorasse mais de um segundo ... Isso atrasaria o tempo e começaria a ficar para trás. A resposta aceita neste caso está correta ... Qualquer um pode executar um simples comando de impressão e executá-lo a cada segundo sem demora ...
irritado 84

5
Eu prefiro from time import time, sleeppor causa das implicações existenciais;)
Will

14
Funciona fantasticamente. Não há necessidade de subtrair o seu starttimese você começar a sincronizá-lo em um determinado período: time.sleep(60 - time.time() % 60)está funcionando bem para mim. Eu o usei como time.sleep(1200 - time.time() % 1200)e ele me fornece os logs :00 :20 :40exatamente como eu queria.
TemporalWolf

2
@AntonSchigur para evitar desvios após várias iterações. Uma iteração indivíduo pode começar um pouco mais cedo ou mais tarde, dependendo sleep(), timer()precisão e quanto tempo leva para executar o corpo do loop, mas em iterações média ocorrem sempre nos limites do intervalo (mesmo que alguns são ignorados): while keep_doing_it(): sleep(interval - timer() % interval). Compare-o apenas com o local while keep_doing_it(): sleep(interval)onde os erros podem se acumular após várias iterações.
JFS

71

Você pode considerar o Twisted, que é uma biblioteca de rede Python que implementa o Padrão do Reator .

from twisted.internet import task, reactor

timeout = 60.0 # Sixty seconds

def doWork():
    #do work here
    pass

l = task.LoopingCall(doWork)
l.start(timeout) # call every sixty seconds

reactor.run()

Embora "while True: sleep (60)" provavelmente funcione, o Twisted provavelmente já implementa muitos dos recursos que você eventualmente precisará (daemonization, log ou manipulação de exceções, conforme indicado por bobince) e provavelmente será uma solução mais robusta.


Ótima resposta também, muito precisa sem desvio. Gostaria de saber se isso coloca a CPU para dormir também enquanto aguarda a execução da tarefa (também conhecida como não ocupado).
smoothware

1
isso deriva no nível de milissegundos #
Derek Eden

O que significa "desvios no nível de milissegundos"?
Jean-Paul Calderone

67

Se você quiser uma maneira sem bloqueio de executar sua função periodicamente, em vez de um loop infinito de bloqueio, eu usaria um temporizador de threads. Dessa forma, seu código pode continuar em execução e executar outras tarefas e ainda ter sua função chamada a cada n segundos. Eu uso muito essa técnica para imprimir informações de progresso em tarefas longas, com uso intenso de CPU / Disco / Rede.

Aqui está o código que eu postei em uma pergunta semelhante, com o controle start () e stop ():

from threading import Timer

class RepeatedTimer(object):
    def __init__(self, interval, function, *args, **kwargs):
        self._timer     = None
        self.interval   = interval
        self.function   = function
        self.args       = args
        self.kwargs     = kwargs
        self.is_running = False
        self.start()

    def _run(self):
        self.is_running = False
        self.start()
        self.function(*self.args, **self.kwargs)

    def start(self):
        if not self.is_running:
            self._timer = Timer(self.interval, self._run)
            self._timer.start()
            self.is_running = True

    def stop(self):
        self._timer.cancel()
        self.is_running = False

Uso:

from time import sleep

def hello(name):
    print "Hello %s!" % name

print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
    sleep(5) # your long-running job goes here...
finally:
    rt.stop() # better in a try/finally block to make sure the program ends!

Recursos:

  • Somente biblioteca padrão, sem dependências externas
  • start()e stop()é seguro ligar várias vezes, mesmo que o timer já tenha iniciado / parado
  • A função a ser chamada pode ter argumentos posicionais e nomeados
  • Você pode alterar a intervalqualquer momento, entrará em vigor após a próxima execução. Mesmo para args, kwargse até mesmo function!

Essa solução parece flutuar com o tempo; Eu precisava de uma versão que visasse chamar a função a cada n segundos sem desvio. Vou postar uma atualização em uma pergunta separada.
eraoul 5/12/16

Em def _run(self)Eu estou tentando envolver minha cabeça em torno de por que você chamar self.start()antes self.function(). Você pode elaborar? Eu acho que chamando start()primeiro self.is_runningseria sempre Falseassim, então sempre criaríamos um novo tópico.
Rich Episcopo

1
Eu acho que cheguei ao fundo disso. A solução do @ MestreLion executa uma função a cada xsegundo (ou seja, t = 0, t = 1x, t = 2x, t = 3x, ...) onde no código de exemplo dos pôsteres originais executa uma função com um intervalo de x segundos. Além disso, acredito que esta solução tenha um erro se intervalfor menor que o tempo necessário functionpara executar. Nesse caso, self._timerserá substituído na startfunção.
Rich Episcopo

Sim, @RichieEpiscopo, a chamada para .function()after .start()é executar a função em t = 0. E eu não acho que será um problema se functiondemorar mais do que isso interval, mas sim, pode haver algumas condições de corrida no código.
MestreLion 6/02

Esta é a única maneira sem bloqueio que eu poderia ter. Obrigado.
backslashN

35

A maneira mais fácil que acredito ser:

import time

def executeSomething():
    #code here
    time.sleep(60)

while True:
    executeSomething()

Dessa forma, seu código é executado, então ele aguarda 60 segundos e depois é executado novamente, espera, executa, etc ... Não há necessidade de complicar as coisas: D


A palavra-chave True deve estar em maiúscula
Sean Cain

38
Na verdade, essa não é a resposta: time sleep () só pode ser usado para aguardar X segundos após cada execução. Por exemplo, se sua função leva 0,5 segundos para executar e você usa time.sleep (1), significa que sua função é executada a cada 1,5 segundos, e não 1. Você deve usar outros módulos e / ou threads para garantir que algo funcione para os tempos Y em cada X segundo.
usar o seguinte código

1
@kommradHomer: A resposta de Dave Rove demonstra que você pode usar time.sleep()algo em execução a cada X segundos
jfs

2
Na minha opinião o código deve chamar time.sleep()em while Trueloop como:def executeSomething(): print('10 sec left') ; while True: executeSomething(); time.sleep(10)
Leonard Lepadatu

22
import time, traceback

def every(delay, task):
  next_time = time.time() + delay
  while True:
    time.sleep(max(0, next_time - time.time()))
    try:
      task()
    except Exception:
      traceback.print_exc()
      # in production code you might want to have this instead of course:
      # logger.exception("Problem while executing repetitive task.")
    # skip tasks if we are behind schedule:
    next_time += (time.time() - next_time) // delay * delay + delay

def foo():
  print("foo", time.time())

every(5, foo)

Se você quiser fazer isso sem bloquear o código restante, use-o para permitir que ele seja executado em seu próprio encadeamento:

import threading
threading.Thread(target=lambda: every(5, foo)).start()

Esta solução combina vários recursos raramente encontrados combinados nas outras soluções:

  • Tratamento de exceções: Na medida do possível, neste nível, as exceções são tratadas corretamente, ou seja, são registradas para fins de depuração sem interromper o programa.
  • Sem encadeamento: a implementação comum em cadeia (para agendar o próximo evento) que você encontra em muitas respostas é frágil no aspecto de que, se algo der errado dentro do mecanismo de agendamento ( threading.Timerou o que seja), isso encerrará a cadeia. Nenhuma execução adicional ocorrerá, mesmo que o motivo do problema já esteja corrigido. Um loop simples e esperar com um simples sleep()é muito mais robusto em comparação.
  • Sem desvio: minha solução mantém um controle exato dos horários em que deveria funcionar. Não há desvio, dependendo do tempo de execução (como em muitas outras soluções).
  • Ignorando: Minha solução ignorará tarefas se uma execução levar muito tempo (por exemplo, faça X a cada cinco segundos, mas X demore 6 segundos). Esse é o comportamento cron padrão (e por um bom motivo). Muitas outras soluções simplesmente executam a tarefa várias vezes seguidas sem demora. Na maioria dos casos (por exemplo, tarefas de limpeza), isso não é desejado. Se é desejado, basta usar next_time += delayem seu lugar.

2
melhor resposta para não ficar à deriva.
Sebastian Stark

1
@PirateApp Eu faria isso em um segmento diferente. Você pode fazer isso no mesmo encadeamento, mas acaba programando seu próprio sistema de agendamento, que é muito complexo para um comentário.
Alfe

1
No Python, graças ao GIL, acessar variáveis ​​em dois threads é perfeitamente seguro. E a mera leitura em dois threads nunca deve ser um problema (também não em outros ambientes de threads). Somente a gravação de dois threads diferentes em um sistema sem um GIL (por exemplo, em Java, C ++ etc.) precisa de alguma sincronização explícita.
Alfe

1
@ user50473 Sem mais informações, abordaria primeiro a tarefa pelo lado do encadeamento. Um thread lê os dados de vez em quando e depois dorme até a hora de fazê-lo novamente. A solução acima pode ser usada para fazer isso, é claro. Mas eu poderia imaginar várias razões para seguir um caminho diferente. Então boa sorte :)
Alfe 15/10/1919

1
O sono pode ser substituído pelo encadeamento. Evite esperar com tempo limite para ser mais responsivo na saída do aplicativo. stackoverflow.com/questions/29082268/…
themadmax

20

Aqui está uma atualização para o código do MestreLion que evita o desvio ao longo do tempo.

A classe RepeatedTimer aqui chama a função fornecida a cada "intervalo" de segundos, conforme solicitado pelo OP; o agendamento não depende de quanto tempo a função leva para executar. Eu gosto dessa solução, pois ela não possui dependências de bibliotecas externas; isso é apenas python puro.

import threading 
import time

class RepeatedTimer(object):
  def __init__(self, interval, function, *args, **kwargs):
    self._timer = None
    self.interval = interval
    self.function = function
    self.args = args
    self.kwargs = kwargs
    self.is_running = False
    self.next_call = time.time()
    self.start()

  def _run(self):
    self.is_running = False
    self.start()
    self.function(*self.args, **self.kwargs)

  def start(self):
    if not self.is_running:
      self.next_call += self.interval
      self._timer = threading.Timer(self.next_call - time.time(), self._run)
      self._timer.start()
      self.is_running = True

  def stop(self):
    self._timer.cancel()
    self.is_running = False

Exemplo de uso (copiado da resposta do MestreLion):

from time import sleep

def hello(name):
    print "Hello %s!" % name

print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
    sleep(5) # your long-running job goes here...
finally:
    rt.stop() # better in a try/finally block to make sure the program ends!

5

Eu enfrentei um problema semelhante há algum tempo. Pode ser http://cronus.readthedocs.org possa ajudar?

Para a v0.2, o seguinte snippet funciona

import cronus.beat as beat

beat.set_rate(2) # 2 Hz
while beat.true():
    # do some time consuming work here
    beat.sleep() # total loop duration would be 0.5 sec

4

A principal diferença entre isso e cron é que uma exceção mata o daemon para sempre. Você pode querer quebrar com um coletor e registrador de exceções.


4

Uma resposta possível:

import time
t=time.time()

while True:
    if time.time()-t>10:
        #run your task here
        t=time.time()

1
Isso está ocupado esperando um, portanto, muito ruim.
Alfe #

Boa solução para quem procura um temporizador sem bloqueio.
Noel

3

Acabei usando o módulo de agendamento . A API é legal.

import schedule
import time

def job():
    print("I'm working...")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every(5).to(10).minutes.do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

Estou com dificuldades para tentar usar esse módulo em particular, preciso desbloquear o thread principal, verifiquei as perguntas frequentes no site de documentação da programação, mas não entendi muito bem a solução alternativa fornecida. Alguém sabe onde posso encontrar um exemplo de trabalho que não bloqueie o thread principal?
5Daydreams 30/01

1

Eu uso o método Tkinter after (), que não "rouba o jogo" (como o módulo sched que foi apresentado anteriormente), ou seja, permite que outras coisas sejam executadas em paralelo:

import Tkinter

def do_something1():
  global n1
  n1 += 1
  if n1 == 6: # (Optional condition)
    print "* do_something1() is done *"; return
  # Do your stuff here
  # ...
  print "do_something1() "+str(n1)
  tk.after(1000, do_something1)

def do_something2(): 
  global n2
  n2 += 1
  if n2 == 6: # (Optional condition)
    print "* do_something2() is done *"; return
  # Do your stuff here
  # ...
  print "do_something2() "+str(n2)
  tk.after(500, do_something2)

tk = Tkinter.Tk(); 
n1 = 0; n2 = 0
do_something1()
do_something2()
tk.mainloop()

do_something1()e do_something2()pode funcionar em paralelo e em qualquer velocidade de intervalo. Aqui, o segundo será executado duas vezes mais rápido. Observe também que usei um contador simples como condição para finalizar qualquer função. Você pode usar qualquer outra afirmação que desejar ou nenhuma, se quiser executar uma função até que o programa termine (por exemplo, um relógio).


Cuidado com a sua redação: afternão permite que as coisas funcionem paralelamente. O Tkinter é de thread único e pode fazer apenas uma coisa de cada vez. Se algo agendado afterestiver em execução, ele não estará em paralelo com o restante do código. Se ambos do_something1e do_something2estiverem programados para serem executados ao mesmo tempo, eles serão executados sequencialmente, não em paralelo.
Bryan Oakley

@Apostolos tudo o que sua solução faz é usar o mainklo tkinter em vez de o mainloop sched , para que funcione exatamente da mesma maneira, mas permite que as interfaces tkinter continuem respondendo. Se você não estiver usando o tkinter para outras coisas, isso não muda nada em relação à solução de agendamento. Você pode usar duas ou mais funções agendadas com intervalos diferentes na schedsolução e ela funcionará exatamente da mesma forma que a sua.
Nosklo 15/0318

Não, não funciona da mesma maneira. Eu expliquei isso. O que "bloqueia" o programa (ou seja, interrompe o fluxo, você não pode fazer mais nada - nem mesmo iniciar outro trabalho conforme o recomendado) até que ele termine e o outro deixe as mãos livres / livres (ou seja, você pode fazer outras coisas depois que ele começou.Você não precisa esperar até que ele termine.Esta é uma grande diferença.Se você tivesse tentado o método que apresentei, teria visto por si mesmo.Eu tentei o seu.Por que você não experimente o meu também?
Apostolos

1

Aqui está uma versão adaptada ao código do MestreLion. Além da função original, este código:

1) adicione first_interval usado para disparar o timer em um horário específico (o chamador precisa calcular o first_interval e passar)

2) resolva uma condição de corrida no código original. No código original, se o encadeamento de controle falhar ao cancelar o cronômetro em execução ("Interrompa o cronômetro e cancele a execução da ação do cronômetro. Isso funcionará apenas se o cronômetro ainda estiver no estágio de espera".) Citado em https: // docs.python.org/2/library/threading.html ), o cronômetro será executado indefinidamente.

class RepeatedTimer(object):
def __init__(self, first_interval, interval, func, *args, **kwargs):
    self.timer      = None
    self.first_interval = first_interval
    self.interval   = interval
    self.func   = func
    self.args       = args
    self.kwargs     = kwargs
    self.running = False
    self.is_started = False

def first_start(self):
    try:
        # no race-condition here because only control thread will call this method
        # if already started will not start again
        if not self.is_started:
            self.is_started = True
            self.timer = Timer(self.first_interval, self.run)
            self.running = True
            self.timer.start()
    except Exception as e:
        log_print(syslog.LOG_ERR, "timer first_start failed %s %s"%(e.message, traceback.format_exc()))
        raise

def run(self):
    # if not stopped start again
    if self.running:
        self.timer = Timer(self.interval, self.run)
        self.timer.start()
    self.func(*self.args, **self.kwargs)

def stop(self):
    # cancel current timer in case failed it's still OK
    # if already stopped doesn't matter to stop again
    if self.timer:
        self.timer.cancel()
    self.running = False

1

Isso parece muito mais simples do que a solução aceita - há falhas que não estou considerando? Vim aqui à procura de algumas massas cópia simples e muito desapontado.

import threading, time

def print_every_n_seconds(n=2):
    while True:
        print(time.ctime())
        time.sleep(n)

thread = threading.Thread(target=print_every_n_seconds, daemon=True)
thread.start()

Que gera assincronamente.

#Tue Oct 16 17:29:40 2018
#Tue Oct 16 17:29:42 2018
#Tue Oct 16 17:29:44 2018

Existe uma tendência no sentido de que, se a tarefa que está sendo executada leva uma quantidade considerável de tempo, o intervalo se torna 2 segundos + tempo da tarefa; portanto, se você precisar de agendamento preciso, isso não é para você.

Observe que o daemon=Truesinalizador significa que esse encadeamento não impedirá o desligamento do aplicativo. Por exemplo, teve um problema em que pytestseria interrompido indefinidamente após a execução de testes aguardando o término desse passo.


Não, ele imprime apenas a primeira data e depois para ...
Alex Poca

Você tem certeza - eu apenas copio e colo no terminal. Ele retorna imediatamente, mas a impressão continua em segundo plano para mim.
Adam Hughes

Parece que estou perdendo alguma coisa aqui. Eu copiar / colar o código no test.py , e correr com test.py python . Com o Python2.7, preciso remover daemon = True que não é reconhecido e leio várias impressões. Com o Python3.8, ele pára após a primeira impressão e nenhum processo fica ativo após o término. Removendo daemon = True Eu li várias impressões ...
Alex Poca 17/06

hmm estranho - eu estou no python 3.6.10, mas não sei por que isso importa
Adam Hughes

Novamente: Python3.4.2 (Debian GNU / Linux 8 (jessie)), teve que remover daemon = True para que ele pudesse imprimir várias vezes. Com o daemon , recebo um erro de sintaxe. Os testes anteriores com Python2.7 e 3.8 foram no Ubuntu 19.10 Será que o daemon é tratado de maneira diferente de acordo com o sistema operacional?
Alex Poca

0

Eu uso isso para causar 60 eventos por hora, com a maioria dos eventos ocorrendo no mesmo número de segundos após o minuto inteiro:

import math
import time
import random

TICK = 60 # one minute tick size
TICK_TIMING = 59 # execute on 59th second of the tick
TICK_MINIMUM = 30 # minimum catch up tick size when lagging

def set_timing():

    now = time.time()
    elapsed = now - info['begin']
    minutes = math.floor(elapsed/TICK)
    tick_elapsed = now - info['completion_time']
    if (info['tick']+1) > minutes:
        wait = max(0,(TICK_TIMING-(time.time() % TICK)))
        print ('standard wait: %.2f' % wait)
        time.sleep(wait)
    elif tick_elapsed < TICK_MINIMUM:
        wait = TICK_MINIMUM-tick_elapsed
        print ('minimum wait: %.2f' % wait)
        time.sleep(wait)
    else:
        print ('skip set_timing(); no wait')
    drift = ((time.time() - info['begin']) - info['tick']*TICK -
        TICK_TIMING + info['begin']%TICK)
    print ('drift: %.6f' % drift)

info['tick'] = 0
info['begin'] = time.time()
info['completion_time'] = info['begin'] - TICK

while 1:

    set_timing()

    print('hello world')

    #random real world event
    time.sleep(random.random()*TICK_MINIMUM)

    info['tick'] += 1
    info['completion_time'] = time.time()

Dependendo das condições reais, você pode obter tiques de comprimento:

60,60,62,58,60,60,120,30,30,60,60,60,60,60...etc.

mas no final de 60 minutos você terá 60 ticks; e a maioria deles ocorrerá no deslocamento correto para o minuto que você preferir.

No meu sistema, recebo um desvio típico de <1/20 de segundo até que a necessidade de correção surja.

A vantagem deste método é a resolução do desvio do relógio; o que pode causar problemas se você anexar um item por tick e esperar 60 itens anexados por hora. A falta de consideração da deriva pode fazer com que indicações secundárias, como médias móveis, considerem os dados muito profundos no passado, resultando em saída defeituosa.


0

por exemplo, Exibir hora local atual

import datetime
import glib
import logger

def get_local_time():
    current_time = datetime.datetime.now().strftime("%H:%M")
    logger.info("get_local_time(): %s",current_time)
    return str(current_time)

def display_local_time():
    logger.info("Current time is: %s", get_local_time())
    return True

# call every minute
glib.timeout_add(60*1000, display_local_time)

0
    ''' tracking number of times it prints'''
import threading

global timeInterval
count=0
def printit():
  threading.Timer(timeInterval, printit).start()
  print( "Hello, World!")
  global count
  count=count+1
  print(count)
printit

if __name__ == "__main__":
    timeInterval= int(input('Enter Time in Seconds:'))
    printit()

Com base na entrada do usuário, ele repetirá esse método a cada intervalo de tempo.
raviGupta

0

Aqui está outra solução sem o uso de bibliotecas extras.

def delay_until(condition_fn, interval_in_sec, timeout_in_sec):
    """Delay using a boolean callable function.

    `condition_fn` is invoked every `interval_in_sec` until `timeout_in_sec`.
    It can break early if condition is met.

    Args:
        condition_fn     - a callable boolean function
        interval_in_sec  - wait time between calling `condition_fn`
        timeout_in_sec   - maximum time to run

    Returns: None
    """
    start = last_call = time.time()
    while time.time() - start < timeout_in_sec:
        if (time.time() - last_call) > interval_in_sec:
            if condition_fn() is True:
                break
            last_call = time.time()
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.