在 python 中限制函数调用

Throttle a function call in python

我有以下类型的代码,但是速度很慢,因为 report() 经常被调用。

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)

在这个说明性代码示例中,我希望报告是最新的(最多 10 秒),所以我想限制该功能。

我可以分叉报告,写入当前时间戳,等待那个时间段,如果同时调用了报告,则使用共享内存时间戳进行检查。如果是,终止,如果不是,写报告。

在Python中有更优雅的方法吗?

这是一个装饰器,它会接受保护内部函数多长时间的参数,如果过早调用则会引发异常。

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

这里是 Python3.

中使用闭包限制函数的示例
import time

def get_current_time_milli():
    return int(round(time.time() * 1000))

def mycallbackfunction():
    time.sleep(0.1)  #mocking some work
    print ("in callback function...")


'''
Throttle a function call using closures.
Don't call the callback function until last invokation is more than 100ms ago.
Only works with python 3. 
Caveat: python 2 we cannot rebind nonlocal variable inside the closure. 

'''

def debouncer(callback, throttle_time_limit=100):

    last_millis = get_current_time_milli()  

    def throttle():
        nonlocal last_millis
        curr_millis = get_current_time_milli()
        if (curr_millis - last_millis) > throttle_time_limit:
            last_millis = get_current_time_milli()
            callback()

return throttle    

# 
myclosure_function = debouncer(mycallbackfunction, 100)

# we are calling myclosure_function 20 times, but only few times, the callback is getting executed. 

# some event triggers this call repeatedly. 
for i in range(20):
    print('calling my closure', myclosure_function(), get_current_time_milli())