一个朋友和我一直玩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是一个字典,它具有对象作为键和列表作为值。 –