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 相关文章推荐
python实现批量转换文件编码(批转换编码示例)
Jan 23 Python
跟老齐学Python之赋值,简单也不简单
Sep 24 Python
编写Python脚本使得web页面上的代码高亮显示
Apr 24 Python
在scrapy中使用phantomJS实现异步爬取的方法
Dec 17 Python
Python提取特定时间段内数据的方法实例
Apr 01 Python
Python属性和内建属性实例解析
Jan 14 Python
利用PyQt中的QThread类实现多线程
Feb 18 Python
Python多线程通信queue队列用法实例分析
Mar 24 Python
Python接收手机短信的代码整理
Aug 02 Python
手把手教你配置JupyterLab 环境的实现
Feb 02 Python
python基础学习之递归函数知识总结
May 26 Python
python高温预警数据获取实例
Jul 23 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
DC《神奇女侠2》因疫情推迟上映 温子仁新恐怖片《恶性》撤档
2020/04/09 欧美动漫
如何做到多笔资料的同步
2006/10/09 PHP
php下使用无限生命期Session的方法
2007/03/16 PHP
php学习之 认清变量的作用范围
2010/01/26 PHP
PHP+mysql实现的三级联动菜单功能示例
2019/02/15 PHP
thinkphp5框架结合mysql实现微信登录和自定义分享链接与图文功能示例
2019/08/13 PHP
javascript淡入淡出效果的实现思路
2012/03/31 Javascript
jquery实现div阴影效果示例代码
2013/09/16 Javascript
JQuery中$.ajax()方法参数详解及应用
2013/12/12 Javascript
js日期联动示例
2014/05/02 Javascript
使用jquery.upload.js实现异步上传示例代码
2014/07/29 Javascript
使用jquery制作弹出框效果
2015/04/03 Javascript
js只执行1次的函数示例
2016/07/20 Javascript
浅谈 Vue v-model指令的实现原理
2017/06/08 Javascript
vue回到顶部监听滚动事件详解
2019/08/02 Javascript
基于Vue的侧边目录组件的实现
2020/02/05 Javascript
[40:06]DOTA2亚洲邀请赛 4.3 突围赛 Liquid vs VGJ.T 第一场
2018/04/04 DOTA
[01:04:14]VP vs TNC 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
Python2.7基于淘宝接口获取IP地址所在地理位置的方法【测试可用】
2017/06/07 Python
利用Python找出序列中出现最多的元素示例代码
2017/12/08 Python
Python3多线程爬虫实例讲解代码
2018/01/05 Python
python3之模块psutil系统性能信息使用
2018/05/30 Python
基于Python的OCR实现示例
2020/04/03 Python
Python插件机制实现详解
2020/05/04 Python
副总经理工作职责
2013/11/28 职场文书
材料专业大学毕业生自荐书
2014/07/02 职场文书
2014政府领导班子对照检查材料思想汇报(3篇)
2014/09/26 职场文书
学院党委班子四风问题自查报告及整改措施
2014/10/25 职场文书
2014年库房工作总结
2014/11/26 职场文书
小兵张嘎电影观后感
2015/06/03 职场文书
单独二胎证明
2015/06/24 职场文书
2016保送生自荐信范文
2016/01/29 职场文书
让人感觉高大上的讲话稿怎么写?
2019/07/08 职场文书
JS创建或填充任意长度数组的小技巧汇总
2021/10/24 Javascript
Python机器学习应用之基于线性判别模型的分类篇详解
2022/01/18 Python
mysql 乱码 字符集latin1转UTF8
2022/04/19 MySQL