Một người bạn và tôi đã chơi đùa với một số đồ chơi và đi qua số this tutorial for building games bằng cách sử dụng pygame. Chúng tôi thực sự thích cách nó phá vỡ trò chơi thành một hệ thống mô hình xem-bộ điều khiển với các sự kiện như một sự thay đổi giữa, nhưng mã làm cho việc sử dụng nặng sử dụng số isinstance
kiểm tra cho hệ thống sự kiện.Cách gõ vịt bằng Python để xử lý sự kiện MVC trong pygame
Ví dụ:
class CPUSpinnerController:
...
def Notify(self, event):
if isinstance(event, QuitEvent):
self.keepGoing = 0
Điều này dẫn đến một số mã cực kỳ unpythonic. Có ai có bất cứ đề nghị về cách này có thể được cải thiện? Hoặc một phương pháp thay thế để thực hiện MVC?
Đây là mã tôi viết dựa trên câu trả lời @ Mark-Hildreth (làm cách nào để liên kết người dùng?) Có ai khác có đề xuất hay không? Tôi sẽ để cái này mở thêm một ngày nữa trước khi chọn một giải pháp.
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()
Đây là một bản dựng khác sử dụng các ví dụ từ @Paul - đơn giản một cách ấn tượng!
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()
Btw, thông số 'tên' của bạn trong' Sự kiện .__ init__' là không cần thiết. Tên của lớp đã được Python lưu trữ. In 'QuitEvent .__ name__' để xem. :) Ngoài ra, nếu bạn có một cá thể đối tượng, bạn có thể làm 'obj .__ class __.__ name__' để lấy chuỗi tên lớp của nó. –
Tất cả điều đó có vẻ ổn. Nhưng đừng quên loại bỏ người nghe từ người quản lý sự kiện trước (hoặc khi) đối tượng RunController của bạn bị phá hủy! Ngoài ra, tôi không thấy bất kỳ vấn đề gì. Tôi vẫn nghĩ bạn nên tạo WeakBoundMethod bên trong RunController__init__ thay vì bên trong Eventmanager.add. EventManager nên bất khả tri đối với loại người nghe mà nó nhận được. –
Re: * 'thực hiện điều này thực sự làm bất cứ điều gì?' * - Không, nhưng tôi muốn nêu rõ trong các hàm '__init__' thuộc tính của lớp đó. Dòng đó làm cho nó rõ ràng rằng self._listeners là một dict có các đối tượng như các khóa và danh sách dưới dạng các giá trị. –