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
- 浅谈Python3中strip()、lstrip()、rstrip()用法详解(浅谈Python3中strip、lstrip、rstrip用法详解)
- python实现最简单的游戏(20行python代码的入门级小游戏的详解)
- python与php(解决Python3 被PHP程序调用执行返回乱码的问题)
- python读取文件的正确方法(强悍的Python读取大文件的解决方案)
- python字符串找一个最大字符(Python查找最长不包含重复字符的子字符串算法示例)
- 微信昵称python(Python 微信之获取好友昵称并制作wordcloud的实例)
- python虚拟环境和包使用教程(在win10和linux上分别安装Python虚拟环境的方法步骤)
- python静态方法和类方法的区别(Python实例方法、类方法、静态方法的区别与作用详解)
- pythonrequest包设置编码(解决python3中的requests解析中文页面出现乱码问题)
- python3.x base64怎么加密解密(python3.x实现base64加密和解密)
- python中导入模块的命令(Python3 导入上级目录中的模块实例)
- python时间类的实现(Python日期时间Time模块实例详解)
- python3的循环怎么用(对Python3 goto 语句的使用方法详解)
- python创建字典的代码(Python创建字典的八种方式)
- python3.7手册中文版(Python3.4解释器用法简单示例)
- python获取txt文件内容(使用python读取.text文件特定行的数据方法)
- 感冒要吃什么药(猫咪感冒要吃什么药)
- 下雪会怎样(下雪怎样画)
- 白蓝色穿搭(白蓝色衣服配什么裤子)
- 天空是什么颜色(天空是什么颜色的英语)
- 高马尾扎发(高马尾扎发教程视频)
- 这里输入关键词(请手动输入关键词)
热门推荐
- dedecms后台关键词(DedeCms防止垃圾文章群发、垃圾评论和注册的方法)
- thinkphp伪静态实例(thinkPHP+mysql+ajax实现的仿百度一下即时搜索效果详解)
- html5课程入门(萌新的HTML5 入门指南)
- python怎么导入beautifulsoup元素(python使用BeautifulSoup与正则表达式爬取时光网不同地区top100电影并对比)
- css3做出效果(深入解读CSS3中transform变换模型的渲染)
- 微信小程序图片加特效(微信小程序实现可实时改变转速的css3旋转动画实例代码)
- linux安装nacos教程(Nacos配置MySQL8的方法)
- mysql千万数据如何优化(MySQL千万级数据的表如何优化)
- docker容器root密码(docker用root进入容器的操作)
- 百度智能云安全教程(百度云服务器安全组开放端口配置图文教程)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9