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 列表理解及使用方法
Oct 27 Python
python reduce 函数使用详解
Dec 05 Python
python处理csv中的空值方法
Jun 22 Python
对python中数组的del,remove,pop区别详解
Nov 07 Python
Python实现的各种常见分布算法示例
Dec 13 Python
如何解决tensorflow恢复模型的特定值时出错
Feb 06 Python
Python读写操作csv和excle文件代码实例
Mar 16 Python
浅谈Python程序的错误:变量未定义
Jun 02 Python
Python-openCV开运算实例
Jul 05 Python
PyCharm 2020.1版安装破解注册码永久激活(激活到2089年)
Sep 24 Python
numpy数据类型dtype转换实现
Apr 24 Python
Python如何让字典保持有序排列
Apr 29 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
PHP+javascript模拟Matrix画面
2006/10/09 PHP
php curl常见错误:SSL错误、bool(false)
2011/12/28 PHP
php数组去重的函数代码
2013/02/03 PHP
php计算整个mysql数据库大小的方法
2015/06/19 PHP
基于php实现随机合并数组并排序(原排序)
2015/11/26 PHP
Zend Framework入门之环境配置及第一个Hello World示例(附demo源码下载)
2016/03/21 PHP
浅谈php fopen下载远程文件的函数
2016/11/18 PHP
微信企业转账之入口类分装php代码
2018/10/01 PHP
wordpress自定义标签云与随机获取标签的方法详解
2019/03/22 PHP
Thinkphp 框架扩展之行为扩展原理与实现方法分析
2020/04/23 PHP
javascript控制frame,iframe的src属性代码
2009/12/31 Javascript
jQuery表格行换色的三种实现方法
2011/06/27 Javascript
Web跨浏览器进程通信(Web跨域)
2013/04/17 Javascript
jquery 实现密码框的显示与隐藏示例代码
2013/09/18 Javascript
Mac/Windows下如何安装Node.js
2013/11/22 Javascript
JS模态窗口返回值兼容问题的完美解决方法
2016/05/28 Javascript
D3.js封装文本实现自动换行和旋转平移等功能
2016/10/14 Javascript
jQuery 插件封装的方法
2016/11/16 Javascript
ES6字符串模板,剩余参数,默认参数功能与用法示例
2017/04/06 Javascript
JavaScript实现写入文件到本地的方法【基于FileSaver.js插件】
2018/03/15 Javascript
[04:32]玩具屠夫中文语音节选
2020/08/23 DOTA
解决python3 urllib中urlopen报错的问题
2017/03/25 Python
Python Tkinter模块实现时钟功能应用示例
2018/07/23 Python
win8下python3.4安装和环境配置图文教程
2018/07/31 Python
python用opencv批量截取图像指定区域的方法
2019/01/24 Python
通过Django Admin+HttpRunner1.5.6实现简易接口测试平台
2020/11/11 Python
python实现录制全屏和选择区域录屏功能
2021/02/05 Python
HTML块级标签汇总(小篇)
2016/07/13 HTML / CSS
司机岗位职责
2013/11/15 职场文书
大学军训感言600字
2014/02/25 职场文书
土地转让协议书范本
2014/04/15 职场文书
小学毕业典礼演讲稿
2014/09/09 职场文书
传承焦裕禄精神思想汇报2014
2014/09/10 职场文书
2015圣诞节贺卡寄语
2015/03/24 职场文书
详解Java实践之抽象工厂模式
2021/06/18 Java/Android
mysql创建存储过程及函数详解
2021/12/04 MySQL