2

だから私はゲームを作っています、そしてすべてのオブジェクトはこのようなもののように見える1つのGameObjectクラスから派生しています。

class GameObject(pygame.sprite.DirtySprite):
    actions = dict()

    def __init__(self):
        pygame.sprite.DirtySprite.__init__(self)
        self.rect  = None
        self.state = None

    def update(self):
        if callable(self.__class__.actions[self.state]):
        #If this key has a function for its element...
            self.__class__.actions[self.state](self)

今、私は継承に関する別の問題に直面しています。以下のクラスと、それから派生した2つのクラスを観察してください。

class Bullet(gameobject.GameObject):
    FRAME  = pygame.Rect(23, 5, 5, 5)
    STATES = config.Enum('IDLE', 'FIRED', 'MOVING', 'RESET')

    def __init__(self):
        gameobject.GameObject.__init__(self)
        self.image = config.SPRITES.subsurface(self.__class__.FRAME)
        self.rect  = self.__class__.START_POS.copy()
        self.state = self.__class__.STATES.IDLE

    actions = {
               STATES.IDLE   : None        ,
               STATES.FIRED  : start_moving,
               STATES.MOVING : move        ,
               STATES.RESET  : reset       ,
              }

class ShipBullet(bullet.Bullet):
    SPEED     = -8
    START_POS = pygame.Rect('something')

    def __init__(self):
        super(self.__class__, self).__init__()
        self.add(ingame.PLAYER)

class EnemyBullet(bullet.Bullet):
    SPEED     = 2
    START_POS = pygame.Rect('something else')

    def __init__(self):
        super(self.__class__, self).__init__()
        self.add(ingame.ENEMIES)

Bullet.actionsを除く(静的メンバー、気を付けてください)のすべての要素はNone、内に保持されている関数ですBulletBullet単独で作成することを意図したものではありません。これがC++の場合、抽象クラスになります。つまり、Bulletサブクラスはすべてのフレームを検索しBullet.actionsて、状態(移動しているのか、撃たれたばかりなのかなど)に応じて、次に何をするかを決定します。ただし、の要素はBullet.actions独自Bulletメソッドであるため、そのサブクラスは、独自の拡張バージョン(親メソッドを呼び出す)ではなく、それらを実行しています。メモリ使用量の理由から、このコールバックのdictを複製する必要はありません。だから、私はこれを尋ねます。サブクラスのインスタンスに、コールバックメソッドでいっぱいの親ディクショナリを調べて、存在する場合は独自のバージョンを実行し、存在しない場合は親のバージョンを実行するにはどうすればよいですか?

4

5 に答える 5

2

BoppreHの答えを拡張するgetattrと、次のようなクラスデコレータを使用して、クラス作成時にアクティビティdictに適切なメソッドを入力することで、ルックアップを取り除くことができます。

def generateActions(cls):
    cls.actions = {}
    for a, f in cls.genactions.items():
        cls.actions[a] = getattr(cls, f) if f else lambda *_: None
    return cls

actionsアクションに指定された値がである場合、は何もしないラムダで満たされていることに注意してください。つまり、でそのステートメントをNone削除できます。if callable(...)update

ここで、クラスにデコレータを追加するだけです。

@generateActions
class Bullet(gameobject.GameObject):
    FRAME  = pygame.Rect(23, 5, 5, 5)
    STATES = config.Enum('IDLE', 'FIRED', 'MOVING', 'RESET')

    genactions = {
           STATES.IDLE   :  None         ,
           STATES.FIRED  : 'start_moving',
           STATES.MOVING : 'move'        ,
           STATES.RESET  : 'reset'       ,
          }
    ...

@generateActions
class ShipBullet(bullet.Bullet):
    ...
于 2012-11-25T15:05:39.993 に答える
2

考えられる解決策の1つは、直接参照の代わりに関数の名前を保存し、を使用getattrして正しい参照を取得することです。

actions = {
           STATES.IDLE   : None          ,
           STATES.FIRED  : 'start_moving',
           STATES.MOVING : 'move'        ,
           STATES.RESET  : 'reset'       ,
          }

[...]

def update(self):
    method_name = self.__class__.actions[self.state]
    if method_name and callable(getattr(self, method_name)):
        getattr(self, method_name)(self)

スピードアップのために、オブジェクトを初期化するときにこのテーブルを事前に計算できます。

class Bullet(gameobject.GameObject):

    FRAME  = pygame.Rect(23, 5, 5, 5)
    STATES = config.Enum('IDLE', 'FIRED', 'MOVING', 'RESET')

    action_names = {
                     STATES.IDLE   : None          ,
                     STATES.FIRED  : 'start_moving',
                     STATES.MOVING : 'move'        ,
                     STATES.RESET  : 'reset'       ,
                    }

    def __init__(self):
        gameobject.GameObject.__init__(self)
        self.image = config.SPRITES.subsurface(self.__class__.FRAME)
        self.rect  = self.__class__.START_POS.copy()
        self.state = self.__class__.STATES.IDLE

        # Update actions table using getattr, so we get the correct
        # method for subclasses.
        self.actions = {}
        for state, method_name in self.action_names.items():
            if method_name and callable(getattr(self, method_name)):
                self.actions[state] = getattr(self, method_name)
            else:
                self.actions[state] = lambda self: None


    def update(self):
        self.actions[self.state]()

のコードはを__init__使用しているため、他のクラスgetattrに配置して拡張するだけでよいことに注意してください。Bullet.__init__すでにスーパーコンストラクターを呼び出しているので、拡張クラスを変更したり、注釈を付けたりする必要はありません。

于 2012-11-22T04:08:27.617 に答える
2

継承にPython組み込みメカニズムを使用してみませんか?

インスタンス関数actionsは、派生クラスでも同じですB。呼び出されるとインスタンスを取得しself、インスタンス自体で関数を呼び出すようなものです。Pythonの継承メカニズムはB、メソッドが存在する場合、または実装にフォールバックする場合に、メソッドを呼び出しますA

編集: l4mpiは、これにより毎回マップが作成されることを指摘したので、action_mapを属性に変更しました。

class A():
    def actions(self, action):
        if not hasattr(self, "actions_map"):
            self.actions_map = {
                   "IDLE"   : self.idle,
                   "FIRED"  : self.fired,
                   "MOVING" : self.move,
                   "RESET"  : self.reset,
                  }
        return self.actions_map[action]

    def idle(self):
        print "A idle"
        pass

    def fired(self):
        print "A fired"

    def move(self):
        print "A move"

    def reset(self):
        print "A reset"

class B(A):       
    def fired(self):
        print "B fired"


a = A()
b = B()

a.actions("FIRED")()
b.actions("FIRED")()
b.actions("MOVING")()

>> A fired
>> B fired
>> A move
于 2012-11-25T12:04:26.203 に答える
0

ゲームオブジェクトの型クラスを定義することを検討してください。

これが私の解決策です。ここで説明している点とは関係のないことはすべて簡略化しています。

class GameObjectClass(type):
    """A metaclass for all game objects"""

    @staticmethod
    def find(key, bases, dict):
        """Find a member in the class dict or any of the bases"""
        if key in dict:
            return dict[key]
        for b in bases:
            attr = getattr(b, key, None)
            if attr is not None:
                return attr
        return None

    def __new__(mcs, name, bases, dict):
        actions = GameObjectClass.find('actions', bases, dict)
        actionsResolved = {}
        for key, methodname in actions.items():
            if methodname is None:
                actionsResolved[key] = None
            else:
                actionsResolved[key] = GameObjectClass.find(methodname, bases, dict)
        dict['actionsResolved'] = actionsResolved
        return type.__new__(mcs, name, bases, dict)

class GameObject(object):

    # This class and all its subclasses will have
    # GameObjectClass for a metaclass
    __metaclass__ = GameObjectClass
    actions = dict()

    def __init__(self):
        self.state = None

    def update(self):
        if callable(self.__class__.actionsResolved[self.state]):
            self.__class__.actionsResolved[self.state](self)

class Bullet(GameObject):
    STATES = config.Enum('IDLE', 'FIRED', 'MOVING', 'RESET')
    def __init__(self):
        super(Bullet, self).__init__()
        self.state = self.__class__.STATES.IDLE
    # Here, strings are used. They will be resolved to
    # references to actual methods (actionsResolved),
    # and this resolution will happen only once
    # (when the game object class is defined)
    actions = {
        STATES.IDLE: None,
        STATES.FIRED: 'start_moving',
        STATES.MOVING: 'move',
        STATES.RESET: 'reset'
    }
    def start_moving(self):
        print "Bullet.start_moving"
    def move(self):
        print "Bullet.move"
    def reset(self):
        print "Bullet.reset"

class ShipBullet(Bullet):
    # This one will be correctly used for the FIRED state
    def start_moving(self):
        print "ShipBullet.start_moving"
于 2012-11-26T16:05:34.070 に答える
0

たぶん私はあなたが何をしたいのかよく理解していませんでした..

私が理解しているように、あなたは主に関数を説明するクラス(A)と、主にプロパティを説明するクラス(B)を持っています。

クラスBのインスタンスからクラスAのメソッドを呼び出したいですか?

このようなことをしてみませんか:

class Bullet(gameobject.GameObject):
    ...

class ShipBullet(bullet.Bullet):
    ABC = bullet.Bullet

    def somefunc(self):
        somekey = 5
        self.ABC.actions[somekey](self, *a, **kw)
        # or
        super(self.__class__, self).actions[somekey](self, *a, **kw)
        # or
        bullet.Bullet.actions[somekey](self, *a, **kw)

次のようなアクション定義でインスタンスにrefを追加する必要があります

def move(self, to_x, to_y): #as classic method
    ....
# or
def move(whom, to_x, to_y): #as "free-function"
    ....
于 2012-11-27T12:47:48.557 に答える