最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python實(shí)現(xiàn)Event回調(diào)機(jī)制的方法

 更新時間:2019年02月13日 10:46:51   作者:tab_space  
今天小編就為大家分享一篇Python實(shí)現(xiàn)Event回調(diào)機(jī)制的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

0.背景

在游戲的UI中,往往會出現(xiàn)這樣的情況:

在某個戰(zhàn)斗副本中獲得了某個道具A,那么當(dāng)進(jìn)入主界面的時候,你會看到你的背包UI上有個小紅點(diǎn)(意思是有新道具),點(diǎn)擊進(jìn)入背包后,發(fā)現(xiàn)新增了道具A,顯示個數(shù)為1,并且在下個界面中有個使用的按鈕由灰色不可使用變成橙色的可使用狀態(tài)

Python Event回調(diào)機(jī)制

圖1. 事件觸發(fā)說明圖

其中這里是由道具獲得這個事件,觸發(fā)了上述的三個行為。如果使用顯示調(diào)用行為,會使得代碼難擴(kuò)展,易出錯,邏輯混亂等問題,如果使用Event回調(diào)機(jī)制,就會變得十分方便。

其實(shí)Event回調(diào)機(jī)制就是觀察者模式,如下圖:

Python Event回調(diào)機(jī)制

圖2. 觀察者模式

在C#中存在(delegate & event)的語義來實(shí)現(xiàn)Event回調(diào)機(jī)制:具體使用如下:

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,可以在注冊事件的回調(diào)時,帶入一個參數(shù)callback,在注冊函數(shù)實(shí)體內(nèi),存在一個list將callback添加進(jìn)去,形如:

def register_callback(self, cb):
 self.callbacks.append(cb)

但是這樣是一個最為普遍的做法,既然是Python,這里我們有更Pythonic的做法,而且相比于上述的觀察者模式,它的做法更加簡潔,使用更加方便,接下來我們來解析一下Python實(shí)現(xiàn)Event callback的步驟。

1. UML類圖

上述案例中,是針對游戲客戶端UI的案例。所以我們呈現(xiàn)出的UML圖也是與UI相關(guān)。如圖3所示,它顯示了Python中實(shí)現(xiàn)Event回調(diào)的機(jī)制。

Python Event回調(diào)機(jī)制

圖3. UML關(guān)系圖

如上圖所示,此機(jī)制主要由三個類及他們的實(shí)例(instance)組成:UIBase, UIScene, UIDataEvent。

1 . UIBase: 所有UIScene的基類,其實(shí)例有scene_id變量,包含兩個必要的方法, __init__ 是初始化方法,init_data_listeners方法是將實(shí)例中的某些方法, 例如ui_updata_func中包含的UIDataEvent實(shí)例(所有的UIDataEvent實(shí)例都是單例)遍歷,并把ui_update_func注冊在每一個UIDataEvent實(shí)例中。

2 . UIScene: 場景類,管理某個場景的UI渲染。在其實(shí)例中,存在某些方法,例如ui_update_func需要在某些UIDataEvent實(shí)例觸發(fā)時候,也被同時觸發(fā)調(diào)用。ui_update_func在Python中一個bound method object, 它會擁有一個特殊的屬性events,即所有需要觸發(fā)此方法的UIDataEvent實(shí)例集合。這個通過裝飾器(decorator)來實(shí)現(xiàn),即圖中的:

“ui_update_func” is a Python object which add a amount of UIDataEvent instances by Python decorator named “data_listener”

3 . UIDataEvent: 事件類,該類有個類變量_events, 記錄了所有的UIDataEvent實(shí)例,每一個UIDataEvent實(shí)例都是單例,而且都有一個名字,和一個回調(diào)方法集合_callbacks, 里面的每一個方法都是在本事件觸發(fā)后需要回調(diào)的方法。實(shí)例還有個__iadd__方法,將需要回調(diào)的函數(shù)cb注冊進(jìn)去。__call__事件觸發(fā)是實(shí)際觸發(fā)的函數(shù)。

2. 代碼

上一步講述了三個類之間的聯(lián)系與各自的作用,此步展示代碼實(shí)現(xiàn)相關(guān)功能。

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):
  """為所有標(biāo)有@data_listener的成員函數(shù)注冊事件監(jiān)聽器"""
  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

其實(shí)源碼的意思就是,在dir(object)的value中找,找到能夠滿足predicate(value) == True的value,然后將(key, value)收集,進(jìn)行排序后返回。

放在代碼的意思是:

  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就是屬性對象,這里其實(shí)就是包含事件的函數(shù)對象,然后遍歷listener中的每一個UIDataEvent實(shí)例,并將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這個事件觸發(fā)之后,需要回調(diào)的函數(shù),上述代碼中寫了三個函數(shù)。注意需要在函數(shù)上加上裝飾器@data_listener(OnItemAdded),這樣此函數(shù)就會添加一個特殊的屬性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):
  """清空所有事件上的所有監(jiān)聽器,在銷毀一個界面的時候調(diào)用"""
  for event in cls._events:
   event._cb = []

OnItemAdded = UIDataEvent('OnItemAdded')

data_listener裝飾器其實(shí)就是聲明一個特殊的events屬性,并將所有在UIScene中填寫的UIDataEvent實(shí)例元組集合賦值給它。

__iadd__是將參數(shù)cb添加到實(shí)例的變量中_callbacks中,此方法在UIBase的init_data_listeners中使用。

__call__是當(dāng)UIDataEvent實(shí)例自調(diào)用時,例如OnItemAdded(item),實(shí)際調(diào)用的函數(shù),在函數(shù)體里,會回調(diào)_callbacks中的每個方法,這也就是Event回調(diào)機(jī)制的核心部分,相當(dāng)于觀察者模式的notify方法

最后新建一個OnItemAdded事件。

c) client.py

創(chuàng)建上述幾個類之后,使用Event回調(diào)就非常簡單了,代碼如下:

# -*- coding: utf-8 -*-
from UIScene import UIScene
from UIDataNotifier import *

OnItemAdded('liu_xin_biao') #新道具流星鏢獲得事件發(fā)生了

輸出:

ui_render_avaliable_use_btn
ui_render_new_tool: liu_xin_biao
ui_render_red_point

3.使用方法

1. 在本模塊內(nèi)增加一個事件定義,并在注釋中寫明事件的參數(shù)及意義。

如果要監(jiān)聽一個事件,請仔細(xì)閱讀相關(guān)注釋。

2. 在ui類最頂端import需要的事件及data_listener。

3. 在需要響應(yīng)該事件的方法(監(jiān)聽器方法)前增加裝飾器@data_listener,參數(shù)內(nèi)列出要監(jiān)聽的所有事件。

如:

@data_listener(OnEventA, OnEventB)
def my_listener_method(arg1):
 ...

注意保持監(jiān)聽器方法的參數(shù)個數(shù)及意義與事件觸發(fā)的地方一致。

4. 在邏輯代碼中適當(dāng)?shù)奈恢脤κ录M(jìn)行觸發(fā)。如OnEventA(arg1, ...)

注意:并不是所有與UI的交互都必須使用事件,事件機(jī)制是為了方便多對多的交互。比如背包物品改變事件,有多個UI都會監(jiān)聽背包物品的變化,而有多種邏輯都會導(dǎo)致背包物品變化,這時使用事件就比較方便。

4. 總結(jié)

本文主要講述了如何使用Python實(shí)現(xiàn)Event回調(diào)機(jī)制,上述的示例代碼參考我的[github-EventCallBack] (https://github.com/csdz/SnapToSnap/tree/master/EventCallBack)。

以上這篇Python實(shí)現(xiàn)Event回調(diào)機(jī)制的方法就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • python多進(jìn)程使用及線程池的使用方法代碼詳解

    python多進(jìn)程使用及線程池的使用方法代碼詳解

    這篇文章主要介紹了python多進(jìn)程使用及線程池的使用方法代碼詳解,需要的朋友可以參考下
    2018-10-10
  • 利用Python統(tǒng)計(jì)每天敲了多少次鍵盤

    利用Python統(tǒng)計(jì)每天敲了多少次鍵盤

    每到年末各大App都會給你來一次年度總結(jié),最近突發(fā)奇想,鍵盤是每天必備的工具,為啥不給鍵盤也來個工作總結(jié),本文就來用Python統(tǒng)計(jì)一下每天敲了多少次鍵盤吧
    2024-04-04
  • python os.path.isfile 的使用誤區(qū)詳解

    python os.path.isfile 的使用誤區(qū)詳解

    今天小編就為大家分享一篇python os.path.isfile 的使用誤區(qū)詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • Python3.6 Schedule模塊定時任務(wù)(實(shí)例講解)

    Python3.6 Schedule模塊定時任務(wù)(實(shí)例講解)

    下面小編就為大家?guī)硪黄狿ython3.6 Schedule模塊定時任務(wù)(實(shí)例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • 關(guān)于Python字符串顯示u...的解決方式

    關(guān)于Python字符串顯示u...的解決方式

    這篇文章主要介紹了關(guān)于Python字符串顯示u...的解決方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-03-03
  • Python學(xué)習(xí)之while 循環(huán)語句

    Python學(xué)習(xí)之while 循環(huán)語句

    這篇文章主要給大家介紹了關(guān)于Python中while循環(huán)語句的相關(guān)資料,使用while循環(huán)語句可以解決程序中需要重復(fù)執(zhí)行的操作,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-10-10
  • 13個Pandas實(shí)用技巧,助你提高開發(fā)效率

    13個Pandas實(shí)用技巧,助你提高開發(fā)效率

    這篇文章主要介紹了13個Pandas實(shí)用技巧,幫助你提高python開發(fā)的效率,感興趣的朋友可以了解下
    2020-08-08
  • python中opencv?直方圖處理

    python中opencv?直方圖處理

    這篇文章主要介紹了python中opencv?直方圖處理,直方圖從圖像內(nèi)部灰度級的角度對圖像進(jìn)行表述,直方圖是圖像內(nèi)灰度值的統(tǒng)計(jì)特性與圖像灰度值之間的函數(shù),直方圖統(tǒng)計(jì)圖像內(nèi)各個灰度級出現(xiàn)的次數(shù),更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-06-06
  • Python標(biāo)準(zhǔn)庫re的使用舉例(正則化匹配)

    Python標(biāo)準(zhǔn)庫re的使用舉例(正則化匹配)

    正則表達(dá)式re是內(nèi)置函數(shù),通過一定的匹配規(guī)則獲取指定的數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于Python標(biāo)準(zhǔn)庫re的使用舉例,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • Django-rest-framework中過濾器的定制實(shí)例

    Django-rest-framework中過濾器的定制實(shí)例

    這篇文章主要介紹了Django-rest-framework中過濾器的定制實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04

最新評論

平和县| 长顺县| 揭东县| 那坡县| 紫金县| 类乌齐县| 广德县| 当阳市| 吉水县| 乌海市| 容城县| 平果县| 宁海县| 景德镇市| 长治县| 北流市| 保亭| 广德县| 淳安县| 云南省| 贵州省| 聊城市| 应用必备| 夏邑县| 谢通门县| 家居| 体育| 新兴县| 奈曼旗| 治县。| 舞钢市| 邯郸县| 荆门市| 岢岚县| 和硕县| 平塘县| 保德县| 灵山县| 梧州市| 扎囊县| 张家界市|