3

私が最善の方法で何かをしているのかどうかについての質問...

Pythonで、(少なくとも)次のようなクラス階層を作成したいと思います。

class Actor
  class Mover(Actor)
  class Attacker(Actor)
    class Human(Mover, Attacker)

しかし、私は、以下のように、およびサブクラスActorのそれぞれから、初期化したい特定の属性を持っているという事実にぶつかります。MoverAttacker

class Actor:
    _world = None
    def __init__(self, world):
        self._world = world

class Mover(Actor):
    _speed = 0
    def __init__(self, world, speed):
        Actor.__init__(self, world)
        self._speed = speed

class Attacker(Actor):
    _range = 0
    def __init__(self, world, range):
        Actor.__init__(self, world)
        self._range = range

その後、これに対する最初のアプローチを採用し、スーパークラスのコンストラクターを使用するという点で常に持っているものに従うと、明らかにActorコンストラクターを2回呼び出すことになりますが、問題はありませんが、プログラマーはうずきを感じて「 dむしろそれをよりクリーンな方法で行います。

class Human(Mover, Attacker):
    def __init__(self, world, speed, range):
        Mover.__init__(self, world, speed)
        Attacker.__init__(self, world, range)

たとえば、コンストラクターを呼び出すことしかできず、単に'sを明示的に初期化することしかできませんでしたが、これは、の初期化コードを複製するため、はるかに悪いアプローチとして私に飛びつきます。MoverHuman_rangeAttacker

私が言ったように、_world属性を2回設定することは大したことではないことを私は知っていますが、もっと集中的なことが起こった場合Actor.__init__、この状況は心配になると想像できます。Pythonでこの構造を実装するためのより良い方法を誰かが提案できますか?

4

1 に答える 1

5

ここにあるのはダイヤモンド継承と呼ばれます。Pythonオブジェクトモデルは、 C3線形化を使用するメソッド解決順序アルゴリズムを介してこれを解決します。実際には、あなたがしなければならないのは、使用してパススルーすることです(そしてPython 2では、から継承します):super**kwargsobject

class Actor(object):    # in Python 3, class Actor:
    _world = None
    def __init__(self, world):
        self._world = world

class Mover(Actor):
    _speed = 0
    def __init__(self, speed, **kwargs):
        super(Mover, self).__init__(**kwargs)    # in Python 3, super().__init__(**kwargs)
        self._speed = speed

class Attacker(Actor):
    _range = 0
    def __init__(self, range, **kwargs):
        super(Attacker, self).__init__(**kwargs) # in Python 3, super().__init__(**kwargs)
        self._range = range

class Human(Mover, Attacker):
    def __init__(self, **kwargs):
        super(Human, self).__init__(**kwargs)    # in Python 3, super().__init__(**kwargs)

Humanここで、kwargsスタイルで構築する必要があることに注意してください。

human = Human(world=world, range=range, speed=speed)

ここで実際に何が起こりますか?呼び出しをインストルメント化すると、次のようになります(簡潔__init__にするためにクラスの名前を変更します)。A, B, C, D

  • D.__init__呼び出しB.__init__
    • B.__init__呼び出しC.__init__
      • C.__init__呼び出しA.__init__
        • A.__init__呼び出しobject.__init__

何が起こっているのかというと、メソッド解決順序の次のknowssuper(B, self)のインスタンスで呼び出されるため、直接ではなくに移動します。MROを見て確認できます。DCCA

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

理解を深めるには、Pythonのsuper()を読んでください。

superそれは絶対に魔法ではないことに注意してください。Python自体でそれが何をするかを概算することができます(ここではsuper(cls, obj)機能のためだけに、クロージャーを使用して__getattribute__循環性をバイパスします):

def super(cls, obj):
    mro = type(obj).__mro__
    parent = mro[mro.index(cls) + 1]
    class proxy(object):
        def __getattribute__(self, name):
            return getattr(parent, name).__get__(obj)
    return proxy()
于 2012-08-13T10:07:59.480 に答える