在 super class 的 init 和 subclass 的 init 中自动组合字典?

Combining dict in super class's init and subclass's init automatically?

我正在创建一个事件系统,它使用以下 class 事件:

class Event(set):
    def __init__(self, name, iterable=()):
        super().__init__(iterable)
        self.name = name

    def __iadd__(self, listener):
        self.add(listener)
        return self

    def __isub__(self, listener):
        self.remove(listener)
        return self

    def fire(self, **eargs):
        for listener in self:
            listener(**eargs)

现在我正在尝试创建某种字典,它会自动在其 __init__ 中创建事件,如下所示:

class EventDict(dict):
    def __init__(self, prefix, *event_names):
        super().__init__({
            name: Event('%s.%s' % (prefix, name))
            for name in event_names
        })

这是一个用法示例:

class Player:
    def __init__(self, name):
        self._name = name
        self.events = EventDict('Player', 'change_name')

    @property
    def name(self):
        returns self._name

    @name.setter
    def name(self, value):
        old_name = self.name
        self.name = value
        self.events['change_name'].fire(player=self, old_name=old_name)

现在我面临的问题是subclassing。 如果我要 subclass 我的 Player class 也包含 health 属性,我不能使用相同的方式创建事件字典,因为它会覆盖现有的,我无法再访问 change_name

所以我正在尝试找到一种方法来做这样的事情(理想的解决方案):

class Player:
    events = EventDict('Player', 'change_name')

class Player2(Player):
    events = EventDict('Player2', 'attack', 'kill')

p2 = Player2()
p2.events['change_name'] += my_event_listener  # Still access Player class's events

这样的事情可能吗?


我知道我能做到:

class Player2(Player):
    def __init__(self, name):
        super().__init__()
        self.events.update(...)

但不一样:P

认为你想要的是:

class Player:

    EVENTS = ('change_name',)

    def __init__(self, name):
        self._name = name
        self.events = EventDict(
            self.__class__.__name__, 
            *self.EVENTS,
        )

    ...

那么你在Player2中所需要的是:

class Player2(Player):

    EVENTS = Player.EVENTS + ('attack', 'kill')

继承的 __init__ 将正常工作。

停止使用 EventDict。 class 本身有自己的 dict,它支持这样的继承。

class Player:
    def __init__(self, name):
        self._name = name
        self.change_name_event = Event('Player.change_name')

class Player2(Player):
    def __init__(self, name):
        super().__init__(name)
        self.attack_event = Event('Player2.attack')
        self.kill_event = Event('Player2.kill')

无论如何都会添加子class中的所有事件。

我注意到你可能想让它们明显地表明它们是事件,所以我在字段名称中添加了 'event',但如果你不想,则不需要.

如果您希望前缀始终相同,则可以将字符串从 'Player.change_name' 更改为 self.__class__.__name__ + '.change_name'。这样,它总是得到对象的 actual class 是什么。这是@jonrsharpe 的解决方案试图达到的目的的一部分。

如果你想让其他人可以动态添加更多事件,他们可以简单地做一行 playerObj.my_new_event = Event('Player.my_new_event') 你可以在Player class 让他们的生活更轻松:

def add_event(self, event_name):
    setattr(self, event_name, Event(self.__class__.__name__ + '.' + event_name)