私はPythonのオブジェクト指向プログラミングについてはかなり新しいので、特に多重継承に関してはsuper()
関数(新しいスタイルクラス)を理解するのが困難です。
たとえば、次のようなものがあるとします。
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
Third()
クラスは両方のコンストラクタメソッドを継承するのでしょうか。そうであれば、どれがsuper()で実行されるのでしょうか、そしてそれはなぜですか?
そして、あなたが他のものを走らせたいならば、どうですか?私はそれがPythonのメソッド解決の順序( _ mro _ )と関係があることを知っています。
これは、Guido自身のブログ投稿 Method Resolution Order (以前の2回の試行を含む)で、妥当な範囲の詳細で詳しく説明されています。
あなたの例では、Third()
はFirst.__init__
を呼び出します。 Pythonは、クラスの親の各属性を左から右の順にリストしながら探します。この場合、私たちは__init__
を探しています。だから、あなたが定義すれば
class Third(First, Second):
...
PythonはFirst
を見ることから始めます、そしてFirst
がその属性を持っていなければ、それはSecond
を見るでしょう。
継承がパスを横切って開始するとき(例えばFirst
がSecond
から継承される場合)、この状況はより複雑になります。詳細については上記のリンクをお読みください。ただし、簡単に言うと、Pythonは各クラスが継承リストに表示される順序を子クラス自体から順に維持しようとします。
だから、たとえば、あなたが持っていた場合:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First):
def __init__(self):
print "third"
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"
mROは[Fourth, Second, Third, First].
になります
ところで、Pythonが首尾一貫したメソッド解決の順序を見つけられない場合、ユーザーを驚かせるかもしれない振る舞いに戻るのではなく、例外を発生させるでしょう。
あいまいなMROの例を追加するように編集しました。
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
print "third"
Third
のMROは[First, Second]
または[Second, First]
のどちらであるべきですか?明白な期待はありません、そしてPythonはエラーを発生させるでしょう:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
編集: 上記の例にはsuper()
呼び出しがないと主張する人が何人かいるので説明しておきましょう。例の要点はMROがどのように構成されているかを示すことです。それらは ではありません "first\nsecond\third"または何でも印刷することを意図しています。もちろん、super()
呼び出しを追加し、何が起こるのかを見て、Pythonの継承モデルをより深く理解することができます。しかし、ここでの私の目標は、それを単純にして、MROがどのように構築されているのかを示すことです。そしてそれは私が説明したように作られています:
>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)
あなたのコードと他の答えはすべてバグがあります。協調的サブクラス化が機能するために必要な最初の2つのクラスには、super()
呼び出しがありません。
これがコードの修正版です。
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
super()
呼び出しは各ステップでMRO内の次のメソッドを見つけます、それがFirstとSecondもそれを持っていなければならない理由です、そうでなければ実行はSecond.__init__()
の終わりで停止します。
これは私が得るものです:
>>> Third()
second
first
third
Pythonの多重継承階層でsuper()を使用する方法を読み始めたとき、すぐには得られなかったため、 lifelessによる答え を詳しく説明したかったのです。
理解する必要があるのは、super(MyClass, self).__init__()
が、使用されるMethod Resolution Ordering(MRO)アルゴリズムに従ってnext__init__
メソッドを提供することです完全な継承階層のコンテキストで。
この最後の部分を理解することは非常に重要です。例をもう一度考えてみましょう。
#!/usr/bin/env python2
class First(object):
def __init__(self):
print "First(): entering"
super(First, self).__init__()
print "First(): exiting"
class Second(object):
def __init__(self):
print "Second(): entering"
super(Second, self).__init__()
print "Second(): exiting"
class Third(First, Second):
def __init__(self):
print "Third(): entering"
super(Third, self).__init__()
print "Third(): exiting"
メソッド解決順序に関するこの記事によると Guido van Rossumにより、__init__
を解決する順序が(Python 2.3より前に)計算されます。右へのトラバース」:
Third --> First --> object --> Second --> object
最後のものを除くすべての重複を削除すると、次のようになります。
Third --> First --> Second --> object
したがって、Third
クラスのインスタンスをインスタンス化するとどうなるかを追跡しましょう。 x = Third()
。
Third.__init__
が実行されます。Third(): entering
を出力しますsuper(Third, self).__init__()
が実行され、MROは呼び出されたFirst.__init__
を返します。First.__init__
が実行されます。First(): entering
を出力しますsuper(First, self).__init__()
が実行され、MROは呼び出されたSecond.__init__
を返します。Second.__init__
が実行されます。Second(): entering
を出力しますsuper(Second, self).__init__()
が実行され、MROは呼び出されたobject.__init__
を返します。object.__init__
が実行されます(コード内にprintステートメントはありません)Second.__init__
に戻り、Second(): exiting
を出力しますFirst.__init__
に戻り、First(): exiting
を出力しますThird.__init__
に戻り、Third(): exiting
を出力しますこれにより、Third()のインスタンス化の結果が次のようになります。
Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
MROアルゴリズムはPython 2.3以降から改善されて、複雑なケースでうまく機能しますが、「深さ優先左から右へのトラバーサル」+「最後に期待される重複の除去」を使用すると思いますほとんどの場合に機能します(そうでない場合はコメントしてください)。 Guidoのブログ投稿を必ず読んでください!
これは Diamond Problem として知られています。このページにはPythonのエントリがありますが、要するにPythonはスーパークラスのメソッドを左から右に呼び出します。
これは、初期化のために異なる変数を持つ複数の継承を持ち、同じ関数呼び出しを持つ複数のMixInsを持つという問題を解決した方法です。渡された** kwargsに変数を明示的に追加し、スーパーコールのエンドポイントになるMixInインターフェイスを追加する必要がありました。
ここでA
は拡張可能な基本クラスで、B
とC
はどちらも関数f
を提供するMixInクラスです。 A
とB
はどちらも、それぞれの__init__
にパラメーターv
を必要とし、C
はw
を必要とします。関数f
は1つのパラメータy
を取ります。 Q
は3つすべてのクラスから継承します。 MixInF
はB
とC
のミックスインインターフェースです。
class A(object):
def __init__(self, v, *args, **kwargs):
print "A:init:v[{0}]".format(v)
kwargs['v']=v
super(A, self).__init__(*args, **kwargs)
self.v = v
class MixInF(object):
def __init__(self, *args, **kwargs):
print "IObject:init"
def f(self, y):
print "IObject:y[{0}]".format(y)
class B(MixInF):
def __init__(self, v, *args, **kwargs):
print "B:init:v[{0}]".format(v)
kwargs['v']=v
super(B, self).__init__(*args, **kwargs)
self.v = v
def f(self, y):
print "B:f:v[{0}]:y[{1}]".format(self.v, y)
super(B, self).f(y)
class C(MixInF):
def __init__(self, w, *args, **kwargs):
print "C:init:w[{0}]".format(w)
kwargs['w']=w
super(C, self).__init__(*args, **kwargs)
self.w = w
def f(self, y):
print "C:f:w[{0}]:y[{1}]".format(self.w, y)
super(C, self).f(y)
class Q(C,B,A):
def __init__(self, v, w):
super(Q, self).__init__(v=v, w=w)
def f(self, y):
print "Q:f:y[{0}]".format(y)
super(Q, self).f(y)
私はこれがsuper()
の質問に直接答えないことを理解しています、しかし私はそれが共有するのに十分に関連があると思います。
継承した各クラスを直接呼び出す方法もあります。
class First(object):
def __init__(self):
print '1'
class Second(object):
def __init__(self):
print '2'
class Third(First, Second):
def __init__(self):
Second.__init__(self)
First
の__init__()
が呼び出されないと確信しているので、このようにすると、それぞれ手動で呼び出す必要があります。
すべてがobject
から派生していると仮定して(そうでない場合はあなたがあなた自身のものです)、Pythonはクラス継承ツリーに基づいてメソッド解決順序(MRO)を計算します。 MROは3つの特性を満たします。
そのような順序付けが存在しない場合、Pythonはエラーを起こします。これの内部の働きはクラスの先祖のC3 Linerizationです。それについてのすべてをここに読んでください: https://www.python.org/download/releases/2.3/mro/
したがって、以下の両方の例では、次のようになります。
メソッドが呼び出されると、MRO内でそのメソッドが最初に現れるのは、呼び出されるものです。そのメソッドを実装していないクラスはすべてスキップされます。そのメソッド内でsuper
を呼び出すと、MRO内でそのメソッドが次に出現する場所が呼び出されます。したがって、クラスを継承する順序とメソッド内のsuper
の呼び出し位置の両方が重要になります。
super
を付けるclass Parent(object):
def __init__(self):
super(Parent, self).__init__()
print "parent"
class Left(Parent):
def __init__(self):
super(Left, self).__init__()
print "left"
class Right(Parent):
def __init__(self):
super(Right, self).__init__()
print "right"
class Child(Left, Right):
def __init__(self):
super(Child, self).__init__()
print "child"
Child()
出力:
parent
right
left
child
super
があるclass Parent(object):
def __init__(self):
print "parent"
super(Parent, self).__init__()
class Left(Parent):
def __init__(self):
print "left"
super(Left, self).__init__()
class Right(Parent):
def __init__(self):
print "right"
super(Right, self).__init__()
class Child(Left, Right):
def __init__(self):
print "child"
super(Child, self).__init__()
Child()
出力:
child
left
right
parent
@ calfzhou's comment について、通常どおり**kwargs
を使用できます。
class A(object):
def __init__(self, a, *args, **kwargs):
print("A", a)
class B(A):
def __init__(self, b, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
print("B", b)
class A1(A):
def __init__(self, a1, *args, **kwargs):
super(A1, self).__init__(*args, **kwargs)
print("A1", a1)
class B1(A1, B):
def __init__(self, b1, *args, **kwargs):
super(B1, self).__init__(*args, **kwargs)
print("B1", b1)
B1(a1=6, b1=5, b="hello", a=None)
結果:
A None
B hello
A1 6
B1 5
それらを位置的に使用することもできます。
B1(5, 6, b="hello", a=None)
しかし、あなたはMROを覚えていなければなりません、それは本当に混乱します。
ちょっと面倒かもしれませんが、メソッドをオーバーライドするときに*args
と**kwargs
を使うのを忘れてしまう人たちがいることに気づきました。
まだカバーされていない別のポイントはクラスの初期化のためのパラメータを渡すことです。 super
の行き先はサブクラスに依存するので、パラメータを渡す唯一の良い方法はそれらをまとめてパックすることです。それから、異なる意味を持つ同じパラメータ名を持たないように注意してください。
例:
class A(object):
def __init__(self, **kwargs):
print('A.__init__')
super().__init__()
class B(A):
def __init__(self, **kwargs):
print('B.__init__ {}'.format(kwargs['x']))
super().__init__(**kwargs)
class C(A):
def __init__(self, **kwargs):
print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
super().__init__(**kwargs)
class D(B, C): # MRO=D, B, C, A
def __init__(self):
print('D.__init__')
super().__init__(a=1, b=2, x=3)
print(D.mro())
D()
を与えます:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__
スーパークラス__init__
を直接呼び出してパラメータを直接指定するのは魅力的ですが、スーパークラス内でsuper
呼び出しがある場合やMROが変更され、実装によってはクラスAが複数回呼び出される場合は失敗します。
結論としては、協調継承と初期化のためのスーパーおよび特定のパラメータは、うまく協調していません。
class First(object):
def __init__(self, a):
print "first", a
super(First, self).__init__(20)
class Second(object):
def __init__(self, a):
print "second", a
super(Second, self).__init__()
class Third(First, Second):
def __init__(self):
super(Third, self).__init__(10)
print "that's it"
t = Third()
出力は
first 10
second 20
that's it
Third()を呼び出すと、Thirdで定義されている init が検索されます。そして、そのルーチン内でsuperを呼び出すと、Firstで定義されている init が呼び出されます。 MRO = [1、2]。これで、Firstで定義されているsuper in init はMROの検索を続け、Secondで定義されているfind init を呼び出し、デフォルトのobject init を呼び出すことになります。この例が概念を明確にすることを願っています。
あなたがファーストからスーパーを呼ばないならば。チェーンが停止し、次のような出力が得られます。
first 10
that's it
私は に加えたいのですが - @Visionscaperが言うこと
Third --> First --> object --> Second --> object
この場合、インタプリタは、オブジェクトクラスが重複しているので除外するのではなく、Secondが階層サブセットの先頭位置に表示され、末尾位置に表示されないためです。オブジェクトは末尾位置にのみ現れるため、優先順位を決定するためのC3アルゴリズムでの強い位置とは見なされません。
クラスCの線形化(mro)L(C)は、
Linearised Mergeは、順序が重要であるため、末尾ではなくリストの先頭として表示される共通のクラスを選択することによって行われます(以下で明らかになります)。
Thirdの線形化は、次のように計算できます。
L(O) := [O] // the linearization(mro) of O(object), because O has no parents
L(First) := [First] + merge(L(O), [O])
= [First] + merge([O], [O])
= [First, O]
// Similarly,
L(Second) := [Second, O]
L(Third) := [Third] + merge(L(First), L(Second), [First, Second])
= [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2,
= [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
= [Third, First, Second] + merge([O], [O])
= [Third, First, Second, O]
したがって、次のコードのsuper()実装の場合:
class First(object):
def __init__(self):
super(First, self).__init__()
print "first"
class Second(object):
def __init__(self):
super(Second, self).__init__()
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
このメソッドがどのように解決されるかは明らかになります
Third.__init__() ---> First.__init__() ---> Second.__init__() --->
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"
誤解しない限り、学ぶために私はsuper()と呼ばれるものを組み込み関数として学んでいます。 super()関数を呼び出すことで、継承が親と '兄弟'を通過するのを助け、より明確に見えるようにすることができます。私はまだ初心者ですが、python2.7でこのsuper()を使用した経験を共有したいと思います。
このページのコメントを読み終えたら、Method Resolution Order(MRO)というメソッドがあなたが書いた関数であることが聞こえます。MROは、深さ - 左 - 右 - 右スキームを使用して検索と実行を行います。あなたはそれについてもっと研究をすることができます。
Super()関数を追加することによって
super(First, self).__init__() #example for class First.
複数のインスタンスと 'family'をsuper()でつなぐことができます。それらのそれぞれとすべての人を追加することによってです。そして、それはメソッドを実行し、それらを通過し、あなたが見逃していないことを確認します!しかし、前後にそれらを追加することはあなたが学習pythonthehardway演習44を終えたかどうかあなたが知っている違いを生むでしょう。楽しみを始めましょう!
下記の例では、コピー&ペーストして実行してみてください。
class First(object):
def __init__(self):
print("first")
class Second(First):
def __init__(self):
print("second (before)")
super(Second, self).__init__()
print("second (after)")
class Third(First):
def __init__(self):
print("third (before)")
super(Third, self).__init__()
print("third (after)")
class Fourth(First):
def __init__(self):
print("fourth (before)")
super(Fourth, self).__init__()
print("fourth (after)")
class Fifth(Second, Third, Fourth):
def __init__(self):
print("fifth (before)")
super(Fifth, self).__init__()
print("fifth (after)")
Fifth()
どのように動作しますか? five()のインスタンスは次のようになります。各ステップは、スーパー関数が追加されたクラスからクラスへと進みます。
1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)
親が見つかり、それは3番目と4番目に続きます!
5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)
これで、super()を持つすべてのクラスがアクセスされました。親クラスが見つかって実行されたので、今度は継承内の関数のボックスを解除してコードを完成させます。
9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed
上記のプログラムの結果:
fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)
私にとってはsuper()を追加することで、Pythonがどのようにコーディングを実行するのかをより明確にし、継承が意図したメソッドにアクセスできることを確認することができます。
まだ追加できるものがあるかもしれません。Django rest_frameworkとデコレーターを使用した小さな例です。これは、暗黙の質問に対する答えを提供します:「とにかくこれが欲しいのですか?」
前述のように、Django rest_frameworkを使用しており、汎用ビューを使用しています。データベース内のオブジェクトのタイプごとに、GETおよびPOSTを提供する1つのビュークラスがあります。オブジェクトのリスト、および個々のオブジェクトのGET、PUT、DELETEを提供する他のビュークラス用。
次に、Djangoのlogin_requiredで装飾したいPOST、PUT、およびDELETEです。これが両方のクラスに影響を与えることに注意してください。ただし、いずれかのクラスのすべてのメソッドではありません。
ソリューションは複数の継承を経ることができます。
from Django.utils.decorators import method_decorator
from Django.contrib.auth.decorators import login_required
class LoginToPost:
@method_decorator(login_required)
def post(self, arg, *args, **kwargs):
super().post(arg, *args, **kwargs)
他の方法についても同様です。
具象クラスの継承リストでは、LoginToPost
の前にListCreateAPIView
を、LoginToPutOrDelete
の前にRetrieveUpdateDestroyAPIView
を追加します。私の具体的なクラスのget
は装飾されていません。