Python - threading - 函数防抖(debounce)

文章目录

  • 1 概念
  • 2 装饰器
      • 2.1 函数工厂
      • 2.2 装饰器简介
  • 3 函数防抖

1 概念

函数防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。
常见的如实时检索框,每次输入字符都会导致推荐的结果改变,但是实际上只需要输入完成后的文本。如果不设置防抖,会每次输入一个字符就搜索一次数据库,导致性能浪费。

2 装饰器

2.1 函数工厂

假设有以下需求:给qt中3个列表的currentRowChanged绑定三个变量的设置,可以通过函数工厂的方式只写一个函数完成三个操作

def row_change(list_idx):
	def set_param(row_idx):
		param[list_idx] = row_idx
	return set_param

# 代码片段
param = [1, 3, 2]
listwidget_0.currentRowChanged.connect(row_change(0))
listwidget_1.currentRowChanged.connect(row_change(1))
listwidget_2.currentRowChanged.connect(row_change(2))

2.2 装饰器简介

当把上面例子中的list_idx换成函数作为输入,就得到了装饰器。假设需要计算函数运行时间,但是又不想修改函数的内容,可以用到装饰器

import time

def timeit(func):
	def decorator(*args, **kwargs):
		start = time.time()
		func(*args, **kwargs)
		end = time.time()
		print('finished in {} seconds'.format(end-start))
	return decorator

def test(x):
	while 1:
		x += 1
		if x > 10000:
			break

为了实现函数计时,共有两种写法

decorated_test = timeit(func=test)
print(decorated_test)
decorated_test(2)
# 输出
# .decorator at 0x000001F44858C1F0>
# finished in 0.002038717269897461 seconds

第一种最容易理解,将test函数作为timeit函数的输入,timeit函数返回decorator函数,所以decorated_test是decorator,调用decorated_test(2)实际上是调用decorator(2)

@timeit
def test(x):
	for i in range(10000):
		x += 1
		
print(test)	
test(2)

# 输出
# .decorator at 0x000001F44858C1F0>
# finished in 0.002038717269897461 seconds

第二种在test函数定义时加了一句@timeit,然后在运行时也不需要调用timeit函数,直接用test(2)就可以完成。但是实际上这个和第一种方法是等效的,print(test)可以看出虽然名字是test,但实际上函数还是timeit中的decorator函数

3 函数防抖

在上述例子中,timeit即为装饰器,可以通过在需要装饰的函数前加@timeit装饰函数,装饰器可以为函数,也可以是类,还可以是类的方法,这里介绍的函数防抖就是用的类的方法作为装饰器。

import threading

class Debounce:
    def __init__(self, interval):
        self.interval = interval
        self.debounced = None

    def __call__(self, func):
        def decorator(*args, **kwargs):
            if self.debounced is not None:
                self.debounced.cancel()
            self.debounced = threading.Timer(self.interval, func, args, kwargs)
            self.debounced.start()
        return decorator

@Debounce(0.01)
def test(x,y,z):
    print(x,y,z)

for i in range(10):
    for j in range(10):
        for k in range(10):
            test(i,j,k)
# 输出
# 9 9 9

此处,在@Debounce后还加了参数,实际上是@的一个实例化的类,此时调用test(i,j,k)等效于

Debounce(0.01)(i,j,k)

调用的是Debounce的__call__方法,其中这个类的interval是0.01。在每次__call__时,会取消前面的print(x,y,z)线程,并创建一个0.01秒后执行的print(x,y,z)线程。因此,在0.01秒内调用的下一次test函数会取消前面的还未执行的线程,直到最后一次调用test(9,9,9)时,创建线程print(9,9,9),因为后面没有再调用test,因此不会取消这次线程,最终成功执行。

你可能感兴趣的:(python,开发语言,性能优化)