2012-02-13 15 views
1

私が作成しているゲームのエンティティクラスを定義し始めました。しかし、私は多くのコードを再利用したい。私は、さまざまな機能のクラスを定義し、これらのクラスの機能のいくつかを持つクラスを作成したいと考えています。私が聞いたゲームのエンティティクラスでPythonのコードを再利用する方法

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 

が続いて制御可能な衝突可能、可動、Drawableの、あるPlayerクラスを持っている、唯一の衝突可能であるInvisible Barrier、唯一のDrawableのあるBackgroundなど:たとえば

(コンポジション、(複数の)継承、インターフェースなどを介して)複数のクラスを接続するためのさまざまな方法がありますが、どの状況が最も適切か、そして/またはピジョンソニックであるかはわかりません。

ミックスイン(多重継承の特殊なケース)は私が探しているものです(プレイヤーはコリドー、ムーブブル、ドローラブル、コントロール可能でなければなりません)。 superを使って右の引数を正しいinit関数に渡すのが難しいと思っています。

編集:

私のpython 3.2を使用しています。

+2

は、「オブジェクト」から、しかし短い、常に自分のクラスを継承することを忘れないでください。 Python 2では "オブジェクト"から派生したクラスである新しいスタイルクラスでしか動作しない、非常に多くの便利な機能があります。別の方法として、たくさんの短いクラスを継承する単一のファイルがある場合は、モジュールレベルの '__metaclass __ = type'変数を設定するだけです。オブジェクトから各クラスを継承するのと同じ効果があります。 – jsbueno

+0

これはPython 3.x?私は、私が開発にPython 3.2を使用しているとは言及していませんでした。なぜなら、構文の変更(直接的なものから親への間接的なものへの変更)以外の「スーパー」に関する問題は認識していなかったからです。 – Darthfett

+0

@Darthfett:Py3kでは、クラスはデフォルトで 'object'から継承します。 –

答えて

3

super()を使用して継承を実装するための簡単な方法です。これを機能させるには、常にPlayerのインスタンス(および***クラスを継承する他のクラス)をキーワード引数で作成する必要があります。

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

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クラスを定義することができます。それぞれのベースクラスは、それがkwargsから使用しているものは何でもキーワード引数ストリップと例えば、MROの次__init__()に上の残りの部分を通過します

そして、このようにそれを使用する:あなたはPlayerクラスヨーヨーのための追加のインスタンス変数が必要な場合

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

Uは、たとえば、他のクラスに似た__init__()を定義します「現代」オブジェクトの動作は、それらの上に動作するように

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

私は、これがPythonで行う最も自然な方法であることに同意します。これは、OOPシステムの使用方法です。 – jsbueno

+2

これは、 'Player'インスタンスがそれ自身の引数を親コンストラクタに渡さなければならないとすぐにハッキーになります。それから、 'kwargs.update({...})'が必要になります。いくつかのパラメータの名前が同じであれば、もっと醜いことになります... –

+0

@NiklasB。スーパーコンストラクターを呼び出した後にself *メンバーを使用するのは簡単ですが、本当にそれらを継承する前に行動する必要はありません。 あなたはパラメータ名については正しいです。これは私がそれについて好きでない単一のものです。これは、モジュールのオプションのパラメータを盗む便利な方法ですが、簡単に間違いを犯す方法を提供します。 – Darthfett

4

ミックスインを移動するための方法ですが、あなたはそれらの上に__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 
+1

うわー、私はとても好きです。私のゲームでもいくつかの問題を解決します:)しかし、私は、そのような標準がどのように標準であるのだろうと思います。 '__init__のプライベートバージョン'を定義することは良い習慣として認められていますか?コードではまだ標準の__init__メソッドを使用できます。 'self.init_collidable(...)'の代わりに 'Collidable .__ init __(...)'が得られます。より明確ではなく、より標準的なアプローチを使用していますか?乾杯。 – Morlock

+0

なぜ__init__を呼びたいのですか?これは、適切な議論を正しいクラスに与えるという質問に答えるものですか? ** kwargs引数を使用してこれを解決できますか? – Darthfett

+0

@Darthfett:新しいスタイルのクラス 'super(...).__ init __(...)'は、すべてのメソッドが同じシグネチャを持つ場合にのみ機能します。メソッドが呼び出される順序を指定することはできません。もちろん、他の答えのようなアプローチを使用することもできますが、それを実行することで安全性が失われます(通常はそれほど価値がありません)。 –

関連する問題