2015-08-10 3 views
0

У меня есть следующий тип кода, но он медленный, потому что report() вызывается очень часто.Throttle вызов функции в python

import time 
import random 

def report(values): 
    open('report.html', 'w').write(str(values)) 

values = [] 

for i in range(10000): 
    # some computation 
    r = random.random()/100. 
    values.append(r) 
    time.sleep(r) 
    # report on the current status, but this should not slow things down 
    report(values) 

В этом иллюстративном примере коды, я хотел бы доклад будет уточненным (в большинстве 10s старого), так что я хотел бы придушить эту функцию.

Я могу разблокировать отчет, записать текущую временную метку и дождаться этого периода и проверить с помощью метки времени с общей памятью, если отчет был вызван тем временем. Если да, прекратите, если нет, напишите отчет.

Есть ли более элегантный способ сделать это в Python?

+0

Использование многопоточности с общей очереди? – jonrsharpe

+2

Я предполагаю, что это медленно, потому что вы каждый раз открываете файл (который также должен быть закрыт). Если вы сохраните файл открытым (передайте его в функцию отчета или создайте класс 'reporter'), это может занять некоторое время. – Holloway

+0

Вы собираетесь переписывать файл каждый раз? – MattH

ответ

0

Вот декоратор, который примет аргумент о том, как долго защищать внутреннюю функцию, создавая исключение, если вызвано слишком рано.

import time 
from functools import partial, wraps 

class TooSoon(Exception): 
    """Can't be called so soon""" 
    pass 

class CoolDownDecorator(object): 
    def __init__(self,func,interval): 
    self.func = func 
    self.interval = interval 
    self.last_run = 0 
    def __get__(self,obj,objtype=None): 
    if obj is None: 
     return self.func 
    return partial(self,obj) 
    def __call__(self,*args,**kwargs): 
    now = time.time() 
    if now - self.last_run < self.interval: 
     raise TooSoon("Call after {0} seconds".format(self.last_run + self.interval - now)) 
    else: 
     self.last_run = now 
     return self.func(*args,**kwargs) 

def CoolDown(interval): 
    def applyDecorator(func): 
    decorator = CoolDownDecorator(func=func,interval=interval) 
    return wraps(func)(decorator) 
    return applyDecorator 

Тогда:

>>> @CoolDown(10) 
... def demo(): 
... print "demo called" 
... 
>>> 
>>> for i in range(12): 
... try: 
...  demo() 
... except TooSoon, exc: 
...  print exc 
... time.sleep(1) 
... 
demo called 
Call after 8.99891519547 seconds 
Call after 7.99776816368 seconds 
Call after 6.99661898613 seconds 
Call after 5.99548196793 seconds 
Call after 4.9943420887 seconds 
Call after 3.99319410324 seconds 
Call after 2.99203896523 seconds 
Call after 1.99091005325 seconds 
Call after 0.990563154221 seconds 
demo called 
Call after 8.99888515472 seconds 
+0

Проблема с этим подходом в контексте OP состоит в том, что последние N секунд данных никогда не будут записаны в файл. –

+0

Хорошая точка. Тем не менее, вы можете сохранить не оформленную версию функции и назвать ее окончательной записью. – MattH

+0

У этого решения есть проблема, что если продолжительность вычисления (time.sleep здесь) случайна (например, между 0,1 с и 100 с), demo() может не вызываться в течение длительного времени, намного дольше 10 секунд. – j13r