Problema: precisamos de executar uma acção sujeita a limites temporais, não mais que n
execuções por segundo são permitidas. Chamemos a isto throttle.
Como boa prática a função de throttle deve estar separada da acção f a executar. Uma abordagem funcional é perfeita: f = throttle(count, timeframe, f)
, tal que a função retornada por throttle()
tenha o mesmo inteface que f
.
Isto é precisamente o que fazem os decoradores em python.
Eis a minha implementação desta funcionalidade.
import datetime
def throttle(count, timeframe): # timeframe in seconds
def work(fn):
# state is lasttimemarker, remaining count
state = [datetime.datetime.now(), count]
def __work(*args, **kwargs):
elapsed = datetime.datetime.now() - state[0]
ntimeframes = int(elapsed.total_seconds() // timeframe)
if ntimeframes > 0: ## just an optimization
state[1] += count * ntimeframes
state[0] += datetime.timedelta(seconds=timeframe) * ntimeframes
if state[1] <= 0:
wait_time = timeframe - elapsed.total_seconds() % timeframe
time.sleep(wait_time)
return __work(*args, **kwargs)
state[1] -= 1
rv = fn(*args, **kwargs)
return rv
return __work
return work
####
import time
# exec 3 times on each 5 seconds timeframe
@throttle(3, 5)
def printtime():
print time.ctime(time.time())
for i in range(10): printtime()
Sat Jun 28 17:16:08 2014
Sat Jun 28 17:16:08 2014
Sat Jun 28 17:16:08 2014
Sat Jun 28 17:16:13 2014
Sat Jun 28 17:16:13 2014
Sat Jun 28 17:16:13 2014
Sat Jun 28 17:16:18 2014
Sat Jun 28 17:16:18 2014
Sat Jun 28 17:16:18 2014
Sat Jun 28 17:16:23 2014