python事件驱动event实现详解


Posted in Python onNovember 21, 2018

所有的计算机程序都可以大致分为两类:脚本型(单次运行)和连续运行型(直到用户主动退出)。

脚本型:脚本型的程序包括最早的批处理文件以及使用Python做交易策略回测等等,这类程序的特点是在用户启动后会按照编程时设计好的步骤一步步运行,所有步骤运行完后自动退出。

连续运行型:连续运行型的程序包含了操作系统和绝大部分我们日常使用的软件等等,这类程序启动后会处于一个无限循环中连续运行,直到用户主动退出时才会结束。

一、连续运行型程序

我们要开发的交易系统就是属于连续运行型程序,而这种程序根据其计算逻辑的运行机制不同,又可以粗略的分为时间驱动和事件驱动两种。

1.1 时间驱动

时间驱动的程序逻辑相对容易设计,简单来说就是让电脑每隔一段时间自动做一些事情。这个事情本身可以很复杂、包括很多步骤,但这些步骤都是线性的,按照顺序一步步执行下来。

from time import sleep
def demo():
 print('BB')
while True:
 demo()
 sleep(1.0)

时间驱动的程序本质上就是每隔一段时间固定运行一次脚本。尽管脚本自身可以很长、包含非常多的步骤,但是我们可以看出这种程序的运行机制相对比较简单、容易理解。

时间驱动的程序在量化交易方面还存在一些其他的缺点:如浪费CPU的计算资源、实现异步逻辑复杂度高等等。

1.2 事件驱动

与时间驱动对应的就是事件驱动的程序:当某个新的事件被推送到程序中时,程序立即调用和这个事件相对应的处理函数进行相关的操作。

举个例子:

有些人喜欢的某个公众号,然后去关注这个公众号,哪天这个公众号发布了篇新的文章,没多久订阅者就会在微信里收到这个公众号推送的新消息,如果感兴趣就打开来阅读。

上面公众号例子可以翻译为,监听器(订阅者)监听了(关注了)事件源(公众号),当事件源的发送事件时(公众号发布文章),所有监听该事件的监听器(订阅者)都会接收到消息并作出响应(阅读文章)。

  • 公众号为事件源
  • 订阅者为事件监听器
  • 订阅者关注公众号,相当于监听器监听了事件源
  • 公众号发布文章这个动作为发送事件
  • 订阅者收到事件后,做出阅读文章的响应动作

事件驱动主要包含以下元素和操作函数:

1.2.1 元素

  • 事件源
  • 事件监听器
  • 事件对象

1.2.2 操作函数

  • 监听动作
  • 发送事件
  • 调用监听器响应函数

现在用python实现来实现上述的业务逻辑,先看流程图:

python事件驱动event实现详解

1.2.3 EventManager事件管理类代码如下:

# -*- coding: utf-8 -*-
"""
Created on Tue Nov 13 13:51:31 2018

@author: 18665
"""
# 系统模块
from queue import Queue, Empty
from threading import *
########################################################################
class EventManager:
 #----------------------------------------------------------------------
 def __init__(self):
  """初始化事件管理器"""
  # 事件对象列表
  self.__eventQueue = Queue()
  # 事件管理器开关
  self.__active = False
  # 事件处理线程
  self.__thread = Thread(target = self.__Run)
  self.count = 0
  # 这里的__handlers是一个字典,用来保存对应的事件的响应函数
  # 其中每个键对应的值是一个列表,列表中保存了对该事件监听的响应函数,一对多
  self.__handlers = {}
 #----------------------------------------------------------------------
 def __Run(self):
  """引擎运行"""
  print('{}_run'.format(self.count))
  while self.__active == True:
   try:
    # 获取事件的阻塞时间设为1秒
    event = self.__eventQueue.get(block = True, timeout = 1) 
    self.__EventProcess(event)
   except Empty:
    pass
   self.count += 1
 #----------------------------------------------------------------------
 def __EventProcess(self, event):
  """处理事件"""
  print('{}_EventProcess'.format(self.count))
  # 检查是否存在对该事件进行监听的处理函数
  if event.type_ in self.__handlers:
   # 若存在,则按顺序将事件传递给处理函数执行
   for handler in self.__handlers[event.type_]:
    handler(event)
  self.count += 1
 #----------------------------------------------------------------------
 def Start(self):
  """启动"""
  print('{}_Start'.format(self.count))
  # 将事件管理器设为启动
  self.__active = True
  # 启动事件处理线程
  self.__thread.start()
  self.count += 1
 #----------------------------------------------------------------------
 def Stop(self):
  """停止"""
  print('{}_Stop'.format(self.count))
  # 将事件管理器设为停止
  self.__active = False
  # 等待事件处理线程退出
  self.__thread.join()
  self.count += 1
 #----------------------------------------------------------------------
 def AddEventListener(self, type_, handler):
  """绑定事件和监听器处理函数"""
  print('{}_AddEventListener'.format(self.count))
  # 尝试获取该事件类型对应的处理函数列表,若无则创建
  try:
   handlerList = self.__handlers[type_]
  except KeyError:
   handlerList = []
 self.__handlers[type_] = handlerList
  # 若要注册的处理器不在该事件的处理器列表中,则注册该事件
  if handler not in handlerList:
   handlerList.append(handler)
  print(self.__handlers)
  self.count += 1
 #----------------------------------------------------------------------
 def RemoveEventListener(self, type_, handler):
  """移除监听器的处理函数"""
  print('{}_RemoveEventListener'.format(self.count))
  try:
   handlerList = self.handlers[type_]
   # 如果该函数存在于列表中,则移除
   if handler in handlerList:
    handlerList.remove(handler)
   # 如果函数列表为空,则从引擎中移除该事件类型
   if not handlerList:
    del self.handlers[type_]
  except KeyError:
   pass
  self.count += 1
 #----------------------------------------------------------------------
 def SendEvent(self, event):
  """发送事件,向事件队列中存入事件"""
  print('{}_SendEvent'.format(self.count))
  self.__eventQueue.put(event)
  self.count += 1
########################################################################
"""事件对象"""
class Event:
 def __init__(self, type_=None):
  self.type_ = type_  # 事件类型
  self.dict = {}   # 字典用于保存具体的事件数据

1.2.4 测试代码

# -*- coding: utf-8 -*-
"""
Created on Tue Nov 13 13:50:45 2018

@author: 18665
"""

# encoding: UTF-8
import sys
from datetime import datetime
from threading import *
#sys.path.append('D:\\works\\TestFile')
#print(sys.path)
from eventManager import *

#事件名称 新文章
EVENT_ARTICAL = "Event_Artical"

#事件源 公众号
class PublicAccounts:
 def __init__(self,eventManager):
  self.__eventManager = eventManager

 def WriteNewArtical(self):
  #事件对象,写了新文章
  event = Event(type_=EVENT_ARTICAL)
  event.dict["artical"] = u'如何写出更优雅的代码\n'
  
  #发送事件
  self.__eventManager.SendEvent(event)
  print(u'公众号发送新文章\n')

#监听器 订阅者
class Listener:
 def __init__(self,username):
  self.__username = username

 #监听器的处理函数 读文章
 def ReadArtical(self,event):
  print(u'%s 收到新文章' % self.__username)
  print(u'正在阅读新文章内容:%s' % event.dict["artical"])

"""测试函数"""
#--------------------------------------------------------------------
def test():
 # 实例化监听器
 listner1 = Listener("thinkroom") #订阅者1
 listner2 = Listener("steve")  #订阅者2
 # 实例化事件操作函数
 eventManager = EventManager()

 #绑定事件和监听器响应函数(新文章)
 eventManager.AddEventListener(EVENT_ARTICAL, listner1.ReadArtical)
 eventManager.AddEventListener(EVENT_ARTICAL, listner2.ReadArtical)
 # 启动事件管理器,# 启动事件处理线程
 eventManager.Start()

 publicAcc = PublicAccounts(eventManager)
 timer = Timer(2, publicAcc.WriteNewArtical)
 timer.start()

if __name__ == '__main__':
 test()

通过eventManager可以实现事件触发的逻辑,当事件触发时,推送事件到线程里运行。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用Mixin设计模式进行Python编程的方法讲解
Jun 21 Python
浅谈Django自定义模板标签template_tags的用处
Dec 20 Python
python编程实现随机生成多个椭圆实例代码
Jan 03 Python
Python实现PS图像明亮度调整效果示例
Jan 23 Python
Python及Django框架生成二维码的方法分析
Jan 31 Python
Python WEB应用部署的实现方法
Jan 02 Python
详解python算法之冒泡排序
Mar 05 Python
python的等深分箱实例
Nov 22 Python
Flask框架搭建虚拟环境的步骤分析
Dec 21 Python
解决pycharm debug时界面下方不出现step等按钮及变量值的问题
Jun 09 Python
pycharm 使用tab跳出正在编辑的括号(){}{}等问题
Feb 26 Python
python双向链表实例详解
May 25 Python
python程序封装为win32服务的方法
Mar 07 #Python
pygame游戏之旅 添加icon和bgm音效的方法
Nov 21 #Python
pygame游戏之旅 添加游戏暂停功能
Nov 21 #Python
使用50行Python代码从零开始实现一个AI平衡小游戏
Nov 21 #Python
pygame游戏之旅 调用按钮实现游戏开始功能
Nov 21 #Python
pygame游戏之旅 按钮上添加文字的方法
Nov 21 #Python
Face++ API实现手势识别系统设计
Nov 21 #Python
You might like
大师制作的中短波矿石收音机
2020/04/02 无线电
PHP产生随机字符串函数
2006/12/06 PHP
全面解析PHP面向对象的三大特征
2017/06/10 PHP
php中isset与empty函数的困惑与用法分析
2019/07/05 PHP
理解Javascript闭包
2013/11/01 Javascript
js/jquery获取文本框输入焦点的方法
2014/03/04 Javascript
jQuery实现连续动画效果实例分析
2015/10/09 Javascript
解决JavaScript数字精度丢失问题的方法
2015/12/03 Javascript
jQuery实现的超简单点赞效果实例分析
2015/12/31 Javascript
基于Node.js实现nodemailer邮件发送
2016/01/26 Javascript
JS结合bootstrap实现基本的增删改查功能
2016/07/22 Javascript
原生js实现放大镜
2017/02/20 Javascript
js编写简单的计时器功能
2017/07/15 Javascript
Vue.js项目部署到服务器的详细步骤
2017/07/17 Javascript
对Vue table 动态表格td可编辑的方法详解
2018/08/28 Javascript
基于element-ui的rules中正则表达式
2018/09/04 Javascript
解决vue里a标签值解析变量,跳转页面,前面加默认域名端口的问题
2020/07/22 Javascript
jquery+ajax实现异步上传文件显示进度条
2020/08/17 jQuery
[01:10]DOTA2英雄背景故事第四期之混沌法则混沌骑士
2020/07/16 DOTA
python和shell实现的校验IP地址合法性脚本分享
2014/10/23 Python
Python批量重命名同一文件夹下文件的方法
2015/05/25 Python
python实现查找两个字符串中相同字符并输出的方法
2015/07/11 Python
Python网络编程中urllib2模块的用法总结
2016/07/12 Python
python+opencv实现霍夫变换检测直线
2020/10/23 Python
利用Python检测URL状态
2019/07/31 Python
使用PyTorch训练一个图像分类器实例
2020/01/08 Python
Selenium webdriver添加cookie实现过程详解
2020/08/12 Python
Pandas中两个dataframe的交集和差集的示例代码
2020/12/13 Python
使用 CSS3 中@media 实现网页自适应的示例代码
2020/03/24 HTML / CSS
英国定做窗帘和纺织品面料一站式商店:Dekoria
2018/08/29 全球购物
人事专员工作职责
2014/02/22 职场文书
财务科科长岗位职责
2014/03/10 职场文书
暖通工程师岗位职责
2014/06/12 职场文书
2014年餐厅服务员工作总结
2014/11/18 职场文书
好好学习保证书
2015/02/26 职场文书
Mysql分析设计表主键为何不用uuid
2022/03/31 MySQL