自动结合超类的init和子类的init中的dict?

时间:2015-06-18 13:21:56

标签: python python-3.x dictionary superclass

我正在创建一个事件系统,它将以下类用于事件:

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)

现在我正在尝试创建某种类型的dict,它会自动在__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)

现在我面临的问题是子类化。 如果我将我的Player类子类化为包含health属性,我不能使用相同的方式创建事件dict,因为它会覆盖现有的dict而我无法访问{ {1}}。

所以我试图找到一种方法,我可以做这样的事情(理想的解决方案):

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

但它不一样:P

2 个答案:

答案 0 :(得分:1)

认为你想要的是:

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__可以正常工作。

答案 1 :(得分:0)

停止使用EventDict。 该类本身有自己的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')

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

我注意到,也许你想明确表示他们是事件,所以我在字段的名称中添加了“event”,但如果你不想这样做,则不需要。

如果你想要它以使前缀始终相同,那么你就可以将字符串从'Player.change_name'更改为self.__class__.__name__ + '.change_name'。这样,它总是得到对象的实际类。这是@ jonrsharpe解决方案试图获得的一部分。

如果你想让别人可以动态添加更多事件,他们可以简单地执行像playerObj.my_new_event = Event('Player.my_new_event') 这样的行,你可以在Player中提供一个不错的方法让他们的生活更轻松的课程:

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