一個朋友和我一直玩pygame一些和使用pygame碰到this tutorial for building games。我們真的很喜歡它是如何將遊戲轉化爲模型 - 視圖 - 控制器系統的,其中事件作爲中介,但是代碼使重複使用isinstance
檢查事件系統。Python鴨子打字的pygame MVC事件處理
實施例:
class CPUSpinnerController:
...
def Notify(self, event):
if isinstance(event, QuitEvent):
self.keepGoing = 0
這導致一些極其unpythonic代碼。有沒有人有任何建議如何可以改善?或者實現MVC的另一種方法?
這是一段代碼我寫基於@馬克 - 希爾德雷思答案(我怎麼鏈接的用戶?)沒有任何人有什麼好的建議?在選擇一個解決方案之前,我打算再開放一天左右。
class EventManager:
def __init__(self):
from weakref import WeakKeyDictionary
self.listeners = WeakKeyDictionary()
def add(self, listener):
self.listeners[ listener ] = 1
def remove(self, listener):
del self.listeners[ listener ]
def post(self, event):
print "post event %s" % event.name
for listener in self.listeners.keys():
listener.notify(event)
class Listener:
def __init__(self, event_mgr=None):
if event_mgr is not None:
event_mgr.add(self)
def notify(self, event):
event(self)
class Event:
def __init__(self, name="Generic Event"):
self.name = name
def __call__(self, controller):
pass
class QuitEvent(Event):
def __init__(self):
Event.__init__(self, "Quit")
def __call__(self, listener):
listener.exit(self)
class RunController(Listener):
def __init__(self, event_mgr):
Listener.__init__(self, event_mgr)
self.running = True
self.event_mgr = event_mgr
def exit(self, event):
print "exit called"
self.running = False
def run(self):
print "run called"
while self.running:
event = QuitEvent()
self.event_mgr.post(event)
em = EventManager()
run = RunController(em)
run.run()
這是另一個使用@Paul示例的版本 - 非常簡單!
class WeakBoundMethod:
def __init__(self, meth):
import weakref
self._self = weakref.ref(meth.__self__)
self._func = meth.__func__
def __call__(self, *args, **kwargs):
self._func(self._self(), *args, **kwargs)
class EventManager:
def __init__(self):
# does this actually do anything?
self._listeners = { None : [ None ] }
def add(self, eventClass, listener):
print "add %s" % eventClass.__name__
key = eventClass.__name__
if (hasattr(listener, '__self__') and
hasattr(listener, '__func__')):
listener = WeakBoundMethod(listener)
try:
self._listeners[key].append(listener)
except KeyError:
# why did you not need this in your code?
self._listeners[key] = [listener]
print "add count %s" % len(self._listeners[key])
def remove(self, eventClass, listener):
key = eventClass.__name__
self._listeners[key].remove(listener)
def post(self, event):
eventClass = event.__class__
key = eventClass.__name__
print "post event %s (keys %s)" % (
key, len(self._listeners[key]))
for listener in self._listeners[key]:
listener(event)
class Event:
pass
class QuitEvent(Event):
pass
class RunController:
def __init__(self, event_mgr):
event_mgr.add(QuitEvent, self.exit)
self.running = True
self.event_mgr = event_mgr
def exit(self, event):
print "exit called"
self.running = False
def run(self):
print "run called"
while self.running:
event = QuitEvent()
self.event_mgr.post(event)
em = EventManager()
run = RunController(em)
run.run()
順便說一句,'Event.__ init__'中的'name'參數是不必要的。該類的名稱已由Python存儲。打印'QuitEvent .__ name__'可以看到。 :)另外,如果你有一個對象實例,你可以使用'obj .__ class __.__ name__'來獲得它的類的名字字符串。 –
一切似乎都沒錯。但是不要忘記在你的RunController對象被破壞之前(或者什麼時候)從事件管理器中移除偵聽器!除此之外,我沒有看到任何問題。我仍然認爲你應該在RunController__init__裏面創建WeakBoundMethod,而不是在Eventmanager.add裏面。事件管理器應該不知道它收到的監聽者的類型。 –
回覆:*'這實際上是否做了什麼?'* - 不,但我想在我的'__init__'函數中清楚地說明這個類有哪些屬性。該行清楚表明self._listeners是一個字典,它具有對象作爲鍵和列表作爲值。 –