Pythonic способ повторного использования кода в игровых классах Entity - PullRequest
1 голос
/ 13 февраля 2012

Я начинаю определять свои классы сущностей для игры, которую я пишу. Тем не менее, я хочу много повторного использования кода. Я хочу определить классы для различной функциональности, а затем иметь классы, которые «имеют» некоторые из этих классов ».

Например:

class Collidable:
    def handle_collision(other, incident_vector):
        pass

    def __init__(self, shape):
        self.shape = shape

class Movable:
    def update_position(self):
        self.velocity += self.acceleration
        self.position += self.velocity

    def __init__(self, velocity, acceleration):
        self.velocity, self.acceleration = velocity, acceleration

class Drawable:
    def draw(self):
        pass

    def __init__(self, image):
        self.image = image

class Controllable:
    def key_down(self, key):
        pass

    def __init__(self):
        pass

Тогда есть класс Player, который является Collidable, Movable, Drawable, Controllable, Invisible Barrier, который только Collidable, Background, который только Drawable, и т. Д. Я слышал о многих различных способах соединения несколько классов (например, через Composition, (Multiple) Inheritance, Interfaces и т. д.), но я не знаю, какой из них наиболее подходит и / или pythonic для этой ситуации.

Смешанные модули (особый случай множественного наследования) выглядят как то, что я ищу (так как игрок должен БЫТЬ Свертываемым, Подвижным, Рисуемым и Контролируемым), но, попробовав это, я ' m затрудняется использовать super для передачи правильных аргументов в правильные функции инициализации.

Edit:

Я использую Python 3.2.

Ответы [ 2 ]

4 голосов
/ 13 февраля 2012

Mixins - это путь, но вы не хотите звонить __init__ на них:

class CollidableMixin(object):
    #...
    def init_collidable(self, shape):
        self.shape = shape

class MovableMixin(object):
    #...
    def init_movable(self, velocity, acceleration):
        self.velocity, self.acceleration = velocity, acceleration

class DrawableMixin(object):
    #...
    def init_drawable(self, image):
        self.image = image

На мой взгляд, вам не нужен отдельный класс для Controllable, поскольку он просто определяет интерфейс, который должен иметь наследующий класс. Хотя вы часто делаете это в статически типизированных языках, таких как Java, вам не нужно это в Python. Вместо этого вы просто определяете метод key_down и покончите с ним. Это называется "утка".

В примере реализации это будет выглядеть следующим образом:

class Player(CollidableMixin, DrawableMixin, MovableMixin):
    def __init__(self):
        self.init_collidable(...)
        self.init_drawable(...)
        self.init_movable(...)

    def key_down(self, key):
        # ...

objects = []
objects.append(Player())
# ... add some more objects. Later we iterate through that collection,
# not knowing which of them is a player:
for o in objects:
    try:
        o.key_down(...)
    except AttributeError:
        pass
3 голосов
/ 13 февраля 2012

Вот простой способ реализации наследования с использованием super(). Чтобы это работало, вам всегда нужно создавать экземпляры Player (и другие классы, которые наследуются от ваших *** способных классов) с аргументами ключевых слов. Каждый базовый класс обрезает любые аргументы ключевого слова, которые он использует, из kwargs и передает остаток следующему __init__() в mro, например:

class Collidable(object):
    def handle_collision(other, incident_vector):
        pass

    def __init__(self, shape, **kwargs):
        self.shape = shape
        super(Collidable, self).__init__(**kwargs)

class Movable(object):
    def update_position(self):
        self.velocity += self.acceleration
        self.position += self.velocity

    def __init__(self, velocity, acceleration, **kwargs):
        self.velocity, self.acceleration = velocity, acceleration
        super(Movable, self).__init__(**kwargs)

class Drawable(object):
    def draw(self):
        pass

    def __init__(self, image, **kwargs):
        self.image = image
        super(Drawable, self).__init__(**kwargs)

class Controllable(object):
    def key_down(self, key):
        pass

    def __init__(self, **kwargs):
        super(Controllable, self).__init__(**kwargs)

Тогда вы можете определить свой Player класс:

class Player(Collidable, Movable, Drawable, Controllable):
    pass

И используйте это так:

>>> p = Player(shape='circle', velocity=0.0, acceleration=1.0, image='player.png')
>>> p.shape
'circle'
>>> p.velocity
0.0
>>> p.acceleration
1.0

Если вам нужны дополнительные переменные экземпляра для класса Player, вы должны определить __init__() аналогично другим классам, например:

class Player(Collidable, Movable, Drawable, Controllable):
    def __init__(name, **kwargs):
        self.name = name
        super(Player, self).__init__(**kwargs)
...