【发布时间】:2020-04-28 09:31:11
【问题描述】:
如何在 python 中编写一个 debounce 装饰器,它不仅在调用的函数上而且在函数参数/使用的函数参数组合上去抖动?
去抖动意味着在给定的时间范围内抑制对函数的调用,假设您在 1 秒内调用函数 100 次,但您只想让函数每 10 秒运行一次,去抖动修饰的函数将运行一次函数如果没有进行新的函数调用,则在最后一次函数调用后 10 秒。在这里,我要问的是如何使用特定的函数参数来消除函数调用的抖动。
一个例子可能是去抖动一个人对象的昂贵更新,例如:
@debounce(seconds=10)
def update_person(person_id):
# time consuming, expensive op
print('>>Updated person {}'.format(person_id))
然后在函数上去抖动 - 包括函数参数:
update_person(person_id=144)
update_person(person_id=144)
update_person(person_id=144)
>>Updated person 144
update_person(person_id=144)
update_person(person_id=355)
>>Updated person 144
>>Updated person 355
因此,调用具有相同 person_id 的函数 update_person 将被抑制(去抖动),直到 10 秒的去抖动间隔过去,而没有对具有相同 person_id 的函数的新调用。
有一些 debounce 装饰器,但没有一个包含函数参数,例如:https://gist.github.com/walkermatt/2871026
我已经通过函数和参数做了一个类似的油门装饰器:
def throttle(s, keep=60):
def decorate(f):
caller = {}
def wrapped(*args, **kwargs):
nonlocal caller
called_args = '{}'.format(*args)
t_ = time.time()
if caller.get(called_args, None) is None or t_ - caller.get(called_args, 0) >= s:
result = f(*args, **kwargs)
caller = {key: val for key, val in caller.items() if t_ - val > keep}
caller[called_args] = t_
return result
# Keep only calls > keep
caller = {key: val for key, val in caller.items() if t_ - val > keep}
caller[called_args] = t_
return wrapped
return decorate
主要的要点是它将函数参数保存在调用者[caller_args]中
另见油门和去抖动的区别:http://demo.nimius.net/debounce_throttle/
更新:
在对上述节流装饰器和要点中的 threading.Timer 示例进行一些修改之后,我实际上认为这应该可以工作:
from threading import Timer
from inspect import signature
import time
def debounce(wait):
def decorator(fn):
sig = signature(fn)
caller = {}
def debounced(*args, **kwargs):
nonlocal caller
try:
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
called_args = fn.__name__ + str(dict(bound_args.arguments))
except:
called_args = ''
t_ = time.time()
def call_it(key):
try:
# always remove on call
caller.pop(key)
except:
pass
fn(*args, **kwargs)
try:
# Always try to cancel timer
caller[called_args].cancel()
except:
pass
caller[called_args] = Timer(wait, call_it, [called_args])
caller[called_args].start()
return debounced
return decorator
【问题讨论】:
-
在此上下文中定义“去抖动”;预期的输入和输出?此外,您的输入和输出取决于时间,因此您可能需要提供它。
-
到目前为止,您为实现 debounce 装饰器做了哪些尝试?您已经证明您知道如何编写装饰器,并且去抖动算法并不是特别复杂。链接的要点确实显示了带参数的去抖动装饰器。您需要哪些具体问题的帮助?
-
我仍然对 debouce 在这里的含义知之甚少,但我确实在你的代码中看到了一些我理解的奇怪之处:
'{}'.format(*args)表达式几乎肯定不会做你想要它做的事情.我想它相当于str(args[0])。如果您遇到复杂的参数处理,您可能想使用inspect.Signature,重新发明它会非常乏味。 -
@Blckknght;油门 - 让第一个呼叫通过抑制跟随,去抖动 - 抑制除最后一个呼叫之外的所有呼叫。节流和去抖动都在给定的时间间隔内。
标签: python python-3.x debounce