2016-02-25 10 views
11

私は多相継承の例があるExpert Python Programmingを読んでいます。本の著者は説明しましたが、私はそれを理解できませんでしたので、別の見方をしたいと思います。オブジェクトはPythonで2回作成されました

この例では、オブジェクトBが2回作成されています。

私に直感的な説明を教えてください。

In [1]: class A(object): 
    ...:  def __init__(self): 
    ...:   print "A" 
    ...:   super(A, self).__init__() 

In [2]: class B(object): 
    ...:  def __init__(self): 
    ...:   print "B" 
    ...:   super(B, self).__init__() 

In [3]: class C(A,B): 
    ...:  def __init__(self): 
    ...:   print "C" 
    ...:   A.__init__(self) 
    ...:   B.__init__(self) 

In [4]: print "MRO:", [x.__name__ for x in C.__mro__] 
MRO: ['C', 'A', 'B', 'object'] 

In [5]: C() 
C 
A 
B 
B 
Out[5]: <__main__.C at 0x3efceb8> 

本の著者は言った:

これは、このようにsuper(A, self).__init__()コールBのコンストラクタ

作り、原因 Cのインスタンスで作られてA.__init__(self)コールに起こります私はそのアイデアを得ていないのですか?A.__init__(self)コールはsuper(A, self).__init__()コールBのコンストラクタを呼び出します

+0

なぜあなたは 'スーパーを使用していない。ここでは代わりにA.doit(self)

class C(A,B): def doit_explain(self): print "C", self # calls B.doit() super(A, self).doit() print "Back to C" # calls A.doit() (and super in A also calls B.doit()) super(C, self).doit() print "Back to C" # and just B.doit() B.doit(self) 

私が直接super(A, self).doit()を使用して、それがまたB.doit()呼び出しの結果は、ここに出力されます'Cのinitメソッドでは? –

+0

私はオブジェクトがPythonでどのように作成されているかを理解したいと思います。 – Bryan

+3

'super'呼び出しを '親クラスのメソッドを呼び出す'ではなく 'MROの次のメソッドを呼び出す'と考えると、この動作は理にかなっています。この場合、Bが2回表示されるのは、superがBのinitを(Aのinitによって)呼び出されるように既に設定しているためです。したがって、CからB.initを明示的に呼び出すと、2回目の呼び出しが発生します。 –

答えて

8

は、「次の行」を意味し、行はmro['C', 'A', 'B', 'object']です。したがって、Aの次の行はBです。

mroは、C3 linearizationというアルゴリズムに従って計算されます。 super()を使用すると、Pythonはこの順番になります。あなたがクラスAを書くとき、あなたはまだどのクラスが次の行になるのか分かりません。複数の継承を持つクラスCを作成してプログラムを実行した後で、Aの次は何かを知ることができます。

ご例えば、それは意味:

C()は、それがA__init__()を呼び出すには、C__init__()を呼び出します。今、Asuper()を使用し、Bがmroにあるので、__init__()Bと呼びます。次に、のC__init__()Bと再度呼び出します。 __init__()super()を呼び出す

は異なるMROを作成し、B__init__()に二重のコールを避けることができます。

from __future__ import print_function 

class A(object): 
    def __init__(self): 
     print("A") 
     super(A, self).__init__() 

class B(object): 
    def __init__(self): 
     print("B") 
     super(B, self).__init__() 

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

用途:

>>> C.mro() 
[__main__.C, __main__.A, __main__.B, object] 
>> C() 
C 
A 
B 
+1

私はまだそれを得ることができないより多くを説明できます – danidee

+0

CのMROは '['C'、 'A'、 'B'、 'object']'です。 CのMRO( 'A .__ init __()'が呼び出されたところ)は、次の行で 'B 'であるので' print' B''を呼び出しますAのスーパー()と 'B .__ init __()' –

1

のは、コードを少し変更するだけの行動が__init__に関連する一般的ではないことを確認するdoit__init__を交換してみましょう。

のも正確に何が起こるかを見るために多くの出力を追加してみましょう:

class A(object): 
    def doit(self): 
     print "A", self, super(A, self) 
     super(A, self).doit() 

class B(object): 
    def doit(self): 
     print "B", self, super(B, self) 

class C(A,B): 
    def doit(self): 
     print "C", self 
     A.doit(self) 
     B.doit(self) 

print "MRO:", [x.__name__ for x in C.__mro__] 
#MRO: ['C', 'A', 'B', 'object'] 

C().doit() 

これが出力されます:あなたが見

C <__main__.C object at ...> 
A <__main__.C object at ...> <super: <class 'A'>, <C object>> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 

selfはどこにでも実際にCオブジェクトなので、あなたがA.doitを打ったときのことを実際には<super: <class 'A'>, <C object>>です。 MROのリストからAA

そして、MROの次のクラスの後に次の(スーパー)クラスのdoitメソッドを呼び出すオブジェクトCため

:に変換

Bであるため、B.doit()と呼びます。

チェックも、このコード:

C <__main__.C object at ...> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 
Back to C 
A <__main__.C object at ...> <super: <class 'A'>, <C object>> 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 
Back to C 
B <__main__.C object at ...> <super: <class 'B'>, <C object>> 
関連する問題