最近一个项目,需要实现自定义事件,发现python对于事件好像没有一个非常标准的封装,或者是原语,下面是自己去实现的一个类似事件的方法,逻辑上参考了.net实现事件的方法,也就是特殊的委托。
1. python回调函数
如果对于python回调函数比较熟悉,可以跳过。
讲实话,论坛上基本上说的什么回调函数,只不过是把函数当做参数传递,这种方式也能称之为“回调”函数吗,可能是我浅薄了。。。
def funcA(a, b):
return a + b
def funcB(a, b, c, func):
return func(a, b) + c
if __name__ == '__main__':
# 将funcA当做参数,传入funcB
res = funcB(1, 2, 3, funcA)
print(res)
说一下这个不能称之为回调函数的原因。回调函数应该是指一个函数指针,当我们调用这个函数指针的时候,相当于调用了指针所指向的函数。按上面的示例,一般将funcA称为回调函数,主函数的funcA相当于是函数指针,指向这个回调函数,并将这个“指针”当做参数传给了funcB。这样理解和操作是ok的,但是如果我在调用funcB的时候,并不知道这个指针指向的是哪个回调函数,我只知道这个函数需要的形参和返回值,那该如何操作。如果是c或者c++,可以声明函数指针,然后使用这个函数指针传进去,这个指针指向的是谁可能是由别的函数指定的,那这样的操作python该如何完成。
上面这一大段文字可能理解起来比较绕,没关系,这个和下面的自定义事件逻辑非常像。
2. python自定义事件
事件概述
如果熟悉.NET的话,应该非常清楚事件的机制,我做自定义事件也是参照这一逻辑处理的,可以自行跳过这一小节。
事件的拥有者:事件的发送者
事件的订阅者:事件的接受和处理
事件关系:事件发生后会不会得到响应,得到谁的响应
举个例子:
老师上课点名,点到小明的名字,小明答:到!其他人都没有吱声,其他课堂的人,包括也有一个叫小明的也没有吱声。
上面老师就是事件的拥有者,发出了点名的事件,所有课堂上的同学都订阅了这个事件。这个事件带有一个参数为姓名,姓名的值为小明,于是小明响应这个事件,并喊了一声到!其他课堂的人由于没有订阅这个事件,所以不会动作。
python实现自定义事件
现在有这样一个业务场景。开一个线程循环监视一个变量,当这个变量变化的时候,发出事件,报告这个变量变化了,同时携带一个参数,参数就是这个变量变化的值,之间的监听者响应这个事件。
import time
import threading
test = 18
class MyEvent:
onVarChanged = []
@staticmethod
def raiseEvent(*args):
for fun in MyEvent.onVarChanged:
fun(*args)
def reportVarChange():
currentVar = test
while True:
time.sleep(0.01)
if test != currentVar:
# test变量发生了改变,激发事假
MyEvent.raiseEvent(test)
currentVar = test
def ChangeProcessor(newVar):
print("changed to:", newVar)
if __name__ == '__main__':
# 订阅事件,并指定处理函数
MyEvent.onVarChanged.append(ChangeProcessor)
t = threading.Thread(target=reportVarChange)
t.start()
time.sleep(1)
test = 20
time.sleep(1)
test = 80
time.sleep(1)
熟悉.NET的人对这个框架可能就比较熟悉了,MyEvent类相当于是声明的委托,MyEvent.onVarChanged相当于实现委托的多播。主函数中对onVarChanged列表添加函数指针。当事件发生,MyEvent.raiseEvent函数被调用,所有onVarChanged中函数的指针都被调用。
这样的话,就可以做个EventBase的事件基类,所有自定义事件都继承这个类形成规范。
class EventBase:
eventHandler = []
@staticmethod
def raiseEvent(obj, *args):
for fun in EventBase.eventHandler:
fun(obj, *args)
其中raiseEvent参数obj就是发出事件的对象,*args就是事件参数