Python の super のメリットとデメリット

※英語の読める人はこのエントリよりも Python's Super is nifty, but you can't use it を読んで下さい

super を使うメリットは、コンストラクタのようなクラス階層全体にまたがるメソッドチェーンを、ダイヤモンド型の多重継承を行った場合でも実現できることである。

super を使わない場合

# no super style
class A(object):
    def __init__(self):
        print 'A',

class B(A):
    def __init__(self):
        print 'B',
        A.__init__(self)

class C(A):
    def __init__(self):
        print 'C',
        A.__init__(self)

class D(B, C):
    def __init__(self):
        print 'D',
        B.__init__(self)
        C.__init__(self)

d = D() # D B A C A

のように、A.__init__() が2回呼び出されてしまう。

superを使うと、

# super style
class A(object):
    def __init__(self):
        print 'A',

class B(A):
    def __init__(self):
        print 'B',
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print 'C',
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print 'D',
        super(D, self).__init__()

d = D() # D B C A

のような順序 (= mro: method resolution order) で __init__() チェーンが実行される。A.__init__() が二回呼び出されることが無い。

しかし、 super を使うことによるデメリットも存在する。例えば上の例で、 A.__init__() が self 以外の引数を取った場合、class C が super(C, self).__init__(nanika) してしまうと、引数はA ではなくて B に渡されてしまう。 super() を使うと、特定のクラスのメソッドが呼ばれる保証が無いので、メソッドチェーン全体に対して「引数はselfだけ」の様な制限を加えなければならない。

しかも、 super() を使うならメソッドチェーン全体にわたって super() で統一しないと、一部にでも Klass.method(self, args) のような形が混じっていると、メソッドチェーンの一部が実行されない等の不具合が生じる。

no super style でも ダイヤモンド継承ができないことはない。

一つの方法は、ダイヤモンド継承の頂点になるクラスを、 __init__() を複数回実行しても大丈夫なように設計することである。実際、 new style class の object は __init__ を複数回呼んでも大丈夫なので、上の例の class B と class C が object を継承しているようなダイヤモンド継承なら問題は起こらない。

もう一つの方法は、ダイヤモンド継承の片方を __init__() 不要にすることだ。この方法の典型例が mix-in になる。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。