python中return 类的实例(Python实现Event回调机制的方法)
python中return 类的实例
Python实现Event回调机制的方法0.背景
在游戏的ui中,往往会出现这样的情况:
在某个战斗副本中获得了某个道具a,那么当进入主界面的时候,你会看到你的背包ui上有个小红点(意思是有新道具),点击进入背包后,发现新增了道具a,显示个数为1,并且在下个界面中有个使用的按钮由灰色不可使用变成橙色的可使用状态
图1. 事件触发说明图
其中这里是由道具获得这个事件,触发了上述的三个行为。如果使用显示调用行为,会使得代码难扩展,易出错,逻辑混乱等问题,如果使用event回调机制,就会变得十分方便。
其实event回调机制就是观察者模式,如下图:
图2. 观察者模式
在c#中存在(delegate & event)的语义来实现event回调机制:具体使用如下:
|
public delegate void newtoolgotevent(); public class toolbag { event newtoolgotevent newtoolgothandler; void start() { newtoolgothandler + = renderredpoint; newtoolgothandler + = rendernewtool; newtoolgothandler + = renderavaliableusebtn; } void renderredpoint() { / / todo } void rendernewtool() { / / todo } void renderavaliableusebtn() { / / todo } void eventhappened() { newtoolgothandler(); / / usage, fill args if necessary } } |
如果在python,可以在注册事件的回调时,带入一个参数callback,在注册函数实体内,存在一个list将callback添加进去,形如:
|
def register_callback( self , cb): self .callbacks.append(cb) |
但是这样是一个最为普遍的做法,既然是python,这里我们有更pythonic的做法,而且相比于上述的观察者模式,它的做法更加简洁,使用更加方便,接下来我们来解析一下python实现event callback的步骤。
1. uml类图
上述案例中,是针对游戏客户端ui的案例。所以我们呈现出的uml图也是与ui相关。如图3所示,它显示了python中实现event回调的机制。
图3. uml关系图
如上图所示,此机制主要由三个类及他们的实例(instance)组成:uibase, uiscene, uidataevent。
1 . uibase: 所有uiscene的基类,其实例有scene_id变量,包含两个必要的方法, __init__ 是初始化方法,init_data_listeners方法是将实例中的某些方法, 例如ui_updata_func中包含的uidataevent实例(所有的uidataevent实例都是单例)遍历,并把ui_update_func注册在每一个uidataevent实例中。
2 . uiscene: 场景类,管理某个场景的ui渲染。在其实例中,存在某些方法,例如ui_update_func需要在某些uidataevent实例触发时候,也被同时触发调用。ui_update_func在python中一个bound method object, 它会拥有一个特殊的属性events,即所有需要触发此方法的uidataevent实例集合。这个通过装饰器(decorator)来实现,即图中的:
“ui_update_func” is a python object which add a amount of uidataevent instances by python decorator named “data_listener”
3 . uidataevent: 事件类,该类有个类变量_events, 记录了所有的uidataevent实例,每一个uidataevent实例都是单例,而且都有一个名字,和一个回调方法集合_callbacks, 里面的每一个方法都是在本事件触发后需要回调的方法。实例还有个__iadd__方法,将需要回调的函数cb注册进去。__call__事件触发是实际触发的函数。
2. 代码
上一步讲述了三个类之间的联系与各自的作用,此步展示代码实现相关功能。
a) uibase.py
首先列出来的是uibase的类,除了上述的__init__与init_data_listeners方法,还多了destroy方法
|
# -*- coding: utf-8 -*- from uidatanotifier import uidataevent import inspect class uibase( object ): def __init__( self , in_scene_id): self . id = in_scene_id self .init_data_listeners() def init_data_listeners( self ): """为所有标有@data_listener的成员函数注册事件监听器""" for listener_name, listener in inspect.getmembers( self , lambda f: hasattr (f, 'events' )): for event in listener.events: event + = listener def destroy( self ): print '%s.destroy' % self .__class__.__name__ uidataevent.clear() |
init_data_listener比较难理解,我们看一下built-in的inspect.getmembers的源码:
|
def getmembers( object , predicate = none): """return all members of an object as (name, value) pairs sorted by name. optionally, only return members that satisfy a given predicate.""" results = [] for key in dir ( object ): try : value = getattr ( object , key) except attributeerror: continue if not predicate or predicate(value): results.append((key, value)) results.sort() return results |
其实源码的意思就是,在dir(object)的value中找,找到能够满足predicate(value) == true的value,然后将(key, value)收集,进行排序后返回。
放在代码的意思是:
|
for listener_name, listener in inspect.getmembers( self , lambda f: hasattr (f, 'events' )): for event in listener.events: event + = listener |
在dir(scene)中找,找到value中存在名叫events的属性, 返回得到是一个list,每个list的元素是一个二元tuple: (key, value),其中key,即listener_name是dir(scene)的属性名,而value, 即listener就是属性对象,这里其实就是包含事件的函数对象,然后遍历listener中的每一个uidataevent实例,并将listener注册到event中(+= ==> __iadd__ )
b) uiscene.py
uiscene的代码如下:
|
# -*- coding: utf-8 -*- from uidatanotifier import * from uibase import uibase class uiscene(uibase): def __init__( self , in_scene_id): super (uiscene, self ).__init__(in_scene_id) @data_listener (onitemadded) def ui_render_red_point( self , item): print 'ui_render_red_point' @data_listener (onitemadded) def ui_render_new_tool( self , item): print 'ui_render_new_tool: ' + item @data_listener (onitemadded) def ui_render_avaliable_use_btn( self , item): print 'ui_render_avaliable_use_btn' bag_ui_scene = uiscene( 123 ) |
在uiscene中只是要填写对于onitemadded这个事件触发之后,需要回调的函数,上述代码中写了三个函数。注意需要在函数上加上装饰器@data_listener(onitemadded),这样此函数就会添加一个特殊的属性events,具体装饰器的代码见uidatanotifier.py。
最后新建一个bag_ui_scene的scene。
c) uidatanotifier.py
uidatanotifier.py代码如下:
|
# -*- coding: utf-8 -*- import sys def data_listener( * events): def wrapped_f(f): f.events = events return f return wrapped_f class uidataevent( object ): _events = [] def __init__( self , name): self ._name = name self ._callbacks = [] uidataevent._events.append( self ) def __iadd__( self , cb): self ._callbacks.append(cb) return self def __call__( self , * args, * * kwargs): for cb in self ._callbacks: try : cb( * args, * * kwargs) except : ex = sys.exc_info() print "uidatanotifier cb error, function:" , cb.__name__, ex def __repr__( self ): return 'uidataevent %s' % self ._name @classmethod def clear( cls ): """清空所有事件上的所有监听器,在销毁一个界面的时候调用""" for event in cls ._events: event._cb = [] onitemadded = uidataevent( 'onitemadded' ) |
data_listener装饰器其实就是声明一个特殊的events属性,并将所有在uiscene中填写的uidataevent实例元组集合赋值给它。
__iadd__是将参数cb添加到实例的变量中_callbacks中,此方法在uibase的init_data_listeners中使用。
__call__是当uidataevent实例自调用时,例如onitemadded(item),实际调用的函数,在函数体里,会回调_callbacks中的每个方法,这也就是event回调机制的核心部分,相当于观察者模式的notify方法
最后新建一个onitemadded事件。
c) client.py
创建上述几个类之后,使用event回调就非常简单了,代码如下:
|
# -*- coding: utf-8 -*- from uiscene import uiscene from uidatanotifier import * onitemadded( 'liu_xin_biao' ) #新道具流星镖获得事件发生了 |
输出:
|
ui_render_avaliable_use_btn ui_render_new_tool: liu_xin_biao ui_render_red_point |
3.使用方法
1. 在本模块内增加一个事件定义,并在注释中写明事件的参数及意义。
如果要监听一个事件,请仔细阅读相关注释。
2. 在ui类最顶端import需要的事件及data_listener。
3. 在需要响应该事件的方法(监听器方法)前增加装饰器@data_listener,参数内列出要监听的所有事件。
如:
|
@data_listener (oneventa, oneventb) def my_listener_method(arg1): ... |
注意保持监听器方法的参数个数及意义与事件触发的地方一致。
4. 在逻辑代码中适当的位置对事件进行触发。如oneventa(arg1, ...)
注意:并不是所有与ui的交互都必须使用事件,事件机制是为了方便多对多的交互。比如背包物品改变事件,有多个ui都会监听背包物品的变化,而有多种逻辑都会导致背包物品变化,这时使用事件就比较方便。
4. 总结
本文主要讲述了如何使用python实现event回调机制,上述的示例代码参考我的[github-eventcallback] (https://github.com/csdz/snaptosnap/tree/master/eventcallback)。
以上这篇python实现event回调机制的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持开心学习网。
原文链接:https://blog.csdn.net/tab_space/article/details/52188991
- python中字符串截取规则(Python中的字符串切片截取字符串的详解)
- python3.8爬虫需要的包(python爬取基于m3u8协议的ts文件并合并)
- python读写文件实验心得(Python文件读写常见用法总结)
- python发送钉钉消息(钉钉群自定义机器人消息Python封装的实例)
- python opencv图像合并(Python3+OpenCV2实现图像的几何变换平移、镜像、缩放、旋转、仿射)
- python3第三方库手册(使用python3构建文件传输的方法)
- python 从入门到实践笔记(python基础梳理一推荐)
- python静态方法和类方法的区别(Python实例方法、类方法、静态方法的区别与作用详解)
- 能自动点赞的小程序(python实现QQ空间自动点赞功能)
- python字符串找一个最大字符(Python查找最长不包含重复字符的子字符串算法示例)
- pythonyield使用场景(Yii框架核心组件类实例详解)
- pythonpandas操作拆分excel(Python使用pandas和xlsxwriter读写xlsx文件的方法示例)
- 怎么用python实现链表(Python3实现的判断回文链表算法示例)
- python绘折线图数据(python2.7使用plotly绘制本地散点图和折线图)
- python中的迭代器详解(Python通过for循环理解迭代器和生成器实例详解)
- pythonopt用法(Python语言检测模块langid和langdetect的使用实例)
- 王伦狭隘,晁盖霸道,宋江奸诈骨头软,只有鲁智深才适合当寨主(王伦狭隘晁盖霸道)
- 他是梁山最早的头目,江湖人称 旱地忽律 ,宋江几乎将其遗忘(他是梁山最早的头目)
- 梁山创始人杜迁,为何不受宋江待见,只排名83位(梁山创始人杜迁)
- 法国面包(法国面包法棍)
- 微信(微信分身)
- 双十二(双十二和双十一哪个划算)
热门推荐
- vue购物车简单项目(vue实现简单购物车案例)
- WEB API 中get、post、put,delete请求方式
- html5video怎么优化(html5 移动端视频video的android兼容去除播放控件、全屏)
- laravel如何使用预处理(Laravel项目中timeAgo字段语言转换的改善方法示例)
- laravel完成权限验证(laravel实现Auth认证,登录、注册后的页面回跳方法)
- 如何查看本机docker的安装目录(使用rpm安装指定版本docker1.12.6的详细步骤)
- php新建文件夹代码(php新建文件的方法实例)
- MYSQL中GROUP BY不包含所有的非聚合字段时的注意事项
- 云服务器如何选供应商(如何选择云服务器运营商?)
- 怎么用css设计边框(单元素利用css实现多重边框效果示例代码)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9