回答:
SomeBaseClass.__init__(self)
呼び出すための手段SomeBaseClass
さん__init__
。ながら
super(Child, self).__init__()
インスタンスのメソッド解決順序(MRO)に__init__
続く親クラスから境界を呼び出すことを意味しますChild
。
インスタンスが子のサブクラスである場合、MROの次に来る別の親が存在する可能性があります。
クラスを作成するとき、他のクラスがそれを使用できるようにする必要があります。super()
作成中のクラスを他のクラスが簡単に使用できるようにします。
ボブマーティンが言うように、優れたアーキテクチャでは、意思決定を可能な限り延期することができます。
super()
この種のアーキテクチャを実現できます。
作成したクラスを別のクラスがサブクラス化する場合、他のクラスから継承している可能性もあります。そしてそれらのクラスは__init__
この後に来る__init__
、メソッド解決のためのクラスの順序に基づいて、ます。
なしsuper
あなたはおそらくハードコードは、クラスの親、あなたが書いているでしょう(例はないように)。これは__init__
、MROでnext を呼び出さないことを意味します。したがって、その中でコードを再利用することはできません。
個人使用のために独自のコードを作成している場合、この違いを気にする必要はありません。ただし、他の人にコードを使用してもらいたい場合super
は、使用することで、コードのユーザーの柔軟性が向上します。
これはPython 2および3で機能します。
super(Child, self).__init__()
これはPython 3でのみ機能します。
super().__init__()
スタックフレーム内を上に移動してメソッドの最初の引数を取得し(通常self
はインスタンスメソッドまたはcls
クラスメソッドの場合-他の名前でもかまいません)、引数なしで機能しChild
、フリー変数(名前で検索されます__class__
メソッド内のフリークロージャ変数としてます)。
私は相互互換性のあるの使用方法を示すことを好みsuper
ますが、Python 3のみを使用している場合は、引数なしで呼び出すことができます。
それはあなたに何を与えるのですか?単一継承の場合、質問の例は静的分析の観点からは実質的に同じです。ただし、super
と、前方互換性のある間接層が提供されます。
経験豊富な開発者にとって、上位互換性は非常に重要です。コードを変更しても、最小限の変更でコードが機能し続けるようにする必要があります。変更履歴を見るとき、いつ何が変更されたかを正確に確認したいとします。
単一の継承から始めることもできますが、別の基本クラスを追加する場合は、基本との行を変更するだけで済みます-継承元のクラスで基本が変更された場合(たとえば、ミックスインが追加された場合)、変更しますこのクラスには何もありません。特にPython 2ではsuper
、正しい引数と正しい引数を取得するのが難しい場合があります。super
単一継承で正しく使用していることがわかっている場合は、これによりデバッグが難しくなりません。
他の人々はあなたのコードを使用して、親をメソッド解決に注入することができます:
class SomeBaseClass(object):
def __init__(self):
print('SomeBaseClass.__init__(self) called')
class UnsuperChild(SomeBaseClass):
def __init__(self):
print('UnsuperChild.__init__(self) called')
SomeBaseClass.__init__(self)
class SuperChild(SomeBaseClass):
def __init__(self):
print('SuperChild.__init__(self) called')
super(SuperChild, self).__init__()
オブジェクトに別のクラスを追加し、FooとBarの間にクラスを挿入するとします(テストまたはその他の理由により)。
class InjectMe(SomeBaseClass):
def __init__(self):
print('InjectMe.__init__(self) called')
super(InjectMe, self).__init__()
class UnsuperInjector(UnsuperChild, InjectMe): pass
class SuperInjector(SuperChild, InjectMe): pass
un-super子を使用すると、依存関係の注入に失敗します。これは、使用している子が、自身の後に呼び出されるメソッドをハードコーディングしているためです。
>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called
ただし、を使用super
する子を持つクラスは、依存関係を正しく挿入できます。
>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called
なぜこれが便利なのでしょうか?
Pythonは、C3線形化アルゴリズムを介して複雑な継承ツリーを線形化し、メソッド解決順序(MRO)を作成します。
メソッドをこの順序で検索する必要があります。
親で定義されたメソッドがなしでその順序で次のメソッドを見つけるためにはsuper
、
UnsuperChild
へのアクセス権を持つべきではありませんInjectMe
。「常に使用しない」という結論にならないのはなぜsuper
ですか?ここで何が欠けていますか?
UnsuperChild
んではないへのアクセス権を持っていますInjectMe
。それはにUnsuperInjector
アクセスできますが、InjectMe
継承元のメソッドからそのクラスのメソッドを呼び出すことはできませんUnsuperChild
。
両方の子クラスは、MROで次に来る同じ名前でメソッドを呼び出すことを意図しています。これは、作成時に認識されていなかった別のクラスである可能性があります。
super
親のメソッドをハードコードしていないもの-したがって、そのメソッドの動作が制限されており、サブクラスは呼び出しチェーンに機能を注入できません。
1 とは、 super
より高い柔軟性を持っています。メソッドの呼び出しチェーンを傍受し、機能を注入することができます。
その機能は必要ないかもしれませんが、コードのサブクラスは必要かもしれません。
super
親クラスをハードコーディングする代わりに、常に使用して親クラスを参照します。
あなたが意図しているのは、次の行である親クラスを参照することです。具体的には、子が継承しているのを見ているクラスではありません。
使用しないsuper
と、コードのユーザーに不要な制約が課される可能性があります。
list
インターフェースの実装をもう1つ追加するとdoublylinkedlist
、アプリケーションはそれをスムーズに選択します。config.txt
ロード時に実装を導入およびリンクすることで、サンプルをより構成可能にすることができます。これは正しい例ですか?はいの場合、どのようにコードを関連付けますか?wikiのDIの最初のadvを参照してください。新しい実装はどこで構成可能ですか?あなたのコードで
InjectMe
ます。コメントは議論の対象ではないので、チャットで他の人とさらに議論するか、メインサイトで新しい質問をすることをお勧めします。
__init__
関数には複雑な問題があります。特に、シグネチャが__init__
階層内のクラス間で異なる場合。この側面に焦点を当てた回答を追加しました
私は少し遊んだ super()
呼び出し順序を変更できることを認識していました。
たとえば、次の階層構造があります。
A
/ \
B C
\ /
D
この場合、DのMROは(Python 3のみ)になります。
In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)
super()
メソッドの実行後に呼び出すクラスを作成しましょう。
In [23]: class A(object): # or with Python 3 can define class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: print("I'm from B")
...: super().__init__()
...:
...: class C(A):
...: def __init__(self):
...: print("I'm from C")
...: super().__init__()
...:
...: class D(B, C):
...: def __init__(self):
...: print("I'm from D")
...: super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A
A
/ ⇖
B ⇒ C
⇖ /
D
したがって、解決の順序がMROと同じであることがわかります。しかしsuper()
、メソッドの最初で呼び出すと:
In [21]: class A(object): # or class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: super().__init__() # or super(B, self).__init_()
...: print("I'm from B")
...:
...: class C(A):
...: def __init__(self):
...: super().__init__()
...: print("I'm from C")
...:
...: class D(B, C):
...: def __init__(self):
...: super().__init__()
...: print("I'm from D")
...: d = D()
...:
I'm from A
I'm from C
I'm from B
I'm from D
MROタプルの順序を逆にした、別の順序があります。
A
/ ⇘
B ⇐ C
⇘ /
D
さらに読むには、次の回答をお勧めします。
super()
classmethod、インスタンスメソッド、またはstaticmethodの親のバージョンに解決するために呼び出すときに、現在のクラスのスコープを最初の引数として渡して、解決しようとしている親のスコープを示します。 2番目の引数は、対象のオブジェクトであり、そのスコープを適用しようとしているオブジェクトを示します。
クラス階層を考えてみましょうA
、B
と、C
どこ各クラスには、それに続く1の親である、とa
、b
と、c
それぞれのそれぞれのインスタンス。
super(B, b)
# resolves to the scope of B's parent i.e. A
# and applies that scope to b, as if b was an instance of A
super(C, c)
# resolves to the scope of C's parent i.e. B
# and applies that scope to c
super(B, c)
# resolves to the scope of B's parent i.e. A
# and applies that scope to c
super
静的メソッドでの使用たとえばsuper()
、__new__()
メソッド内から使用する
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
説明:
1- __new__()
呼び出し元のクラスへの参照を最初のパラメーターとして取るのは通常ですが、Pythonではクラスメソッドとしてではなく、静的メソッドとして実装されます。つまり、クラスへの参照は、__new__()
直接呼び出すときに最初の引数として明示的に渡す必要があります。
# if you defined this
class A(object):
def __new__(cls):
pass
# calling this would raise a TypeError due to the missing argument
A.__new__()
# whereas this would be fine
A.__new__(A)
2- super()
親クラスに到達するために呼び出すときにA
、最初の引数として子クラスを渡します。次に、対象のオブジェクトへの参照を渡します。この場合A.__new__(cls)
は、呼び出し時に渡されたクラス参照です。ほとんどの場合、それはたまたま子クラスへの参照でもあります。たとえば、複数世代の継承の場合など、状況によってはそうでない場合があります。
super(A, cls)
原則としてので3- __new__()
staticmethodあり、super(A, cls).__new__
この場合も、insterestのオブジェクトへの参照を含む、明示的にすべての引数を、供給されるstaticmethodとニーズを戻しますcls
。
super(A, cls).__new__(cls, *a, **kw)
4-同じことをせずに super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
super
インスタンスメソッドでの使用例:super()
内部から使用する__init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
説明:
1- __init__
はインスタンスメソッドです。つまり、最初の引数としてインスタンスへの参照を受け取ります。インスタンスから直接呼び出されると、参照は暗黙的に渡されます。つまり、指定する必要はありません。
# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...
# you create an instance
a = A()
# you call `__init__()` from that instance and it works
a.__init__()
# you can also call `__init__()` with the class and explicitly pass the instance
A.__init__(a)
2- super()
内__init__()
で呼び出す場合、最初の引数として子クラスを渡し、2番目の引数として対象のオブジェクトを渡します。これは一般に、子クラスのインスタンスへの参照です。
super(A, self)
3-呼び出しsuper(A, self)
は、スコープを解決してそれself
が親クラスのインスタンスであるかのように適用するプロキシを返します。そのプロキシを呼び出しましょうs
。以来は、__init__()
呼び出しがインスタンスメソッドでs.__init__(...)
暗黙的に参照を渡すself
親の最初の引数として__init__()
。
4- super
インスタンスへの参照を明示的に親のバージョンのに渡す必要なしに同じことを行うには__init__()
。
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
super
クラスメソッドで使用するclass A(object):
@classmethod
def alternate_constructor(cls, *a, **kw):
print "A.alternate_constructor called"
return cls(*a, **kw)
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return super(B, cls).alternate_constructor(*a, **kw)
説明:
1-クラスメソッドはクラスから直接呼び出すことができ、最初のパラメータとしてクラスへの参照を受け取ります。
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- super()
クラスメソッド内で呼び出して、その親のバージョンに解決するとき、現在の子クラスを最初の引数として渡して、解決しようとしている親のスコープを示し、対象のオブジェクトを2番目の引数として渡しますそのスコープを適用するオブジェクトを示します。これは一般に、子クラス自体またはそのサブクラスの1つへの参照です。
super(B, cls_or_subcls)
3-呼び出しsuper(B, cls)
はのスコープに解決され、にA
適用されcls
ます。ので、alternate_constructor()
クラスメソッドであるコールは、super(B, cls).alternate_constructor(...)
暗黙の基準に合格するcls
の最初の引数としてA
ののバージョンalternate_constructor()
super(B, cls).alternate_constructor()
4-を使用せずに同じことを行うにはsuper()
、の非バインドバージョンA.alternate_constructor()
(つまり、関数の明示的なバージョン)への参照を取得する必要があります。これを行うだけでは機能しません。
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return A.alternate_constructor(cls, *a, **kw)
A.alternate_constructor()
メソッドは暗黙的な参照をA
最初の引数として取るため、上記は機能しません。ここcls
で渡されるのは、その2番目の引数です。
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
# first we get a reference to the unbound
# `A.alternate_constructor` function
unbound_func = A.alternate_constructor.im_func
# now we call it and pass our own `cls` as its first argument
return unbound_func(cls, *a, **kw)
多くの素晴らしい答えですが、視覚的な学習者向けです。最初に、superへの引数付きで、次になしで探索しましょう。
画像から緑色で示されている継承チェーンを持つjack
クラスから作成されたインスタンスがあるとしますJack
。呼び出し:
super(Jack, jack).method(...)
jack
(その継承ツリーを特定の順序で)のMRO(メソッド解決順序)を使用し、から検索を開始しJack
ます。なぜ親クラスを提供できるのですか?インスタンスから検索を開始するとjack
すると、インスタンスメソッドが見つかります。全体のポイントは、親メソッドを見つけることです。
スーパーに引数を指定しない場合、最初に渡される引数はのクラスでself
あり、2番目に渡される引数はself
です。これらはPython3で自動的に計算されます。
ただしJack
、のメソッドを使用したくない場合は、渡すのでJack
はなく、から渡しJen
てメソッドの上方検索を開始することができJen
ます。
一度に1つのレイヤーを検索します(深さではなく幅)。たとえばAdam
、Sue
両方に必要なメソッドがある場合、Sue
最初のレイヤーが最初に見つかります。
場合Cain
とSue
の両方が必要な方法で、持っていたCain
のメソッドが最初に呼び出されます。これはコードで次のように対応します:
Class Jen(Cain, Sue):
MROは左から右です。
ここでいくつかの素晴らしい答えがsuper()
ありますが、階層の異なるクラスが異なるシグネチャを持っている場合の使用方法には取り組みません...特に__init__
その部分に答えて効果的に使用できるようにするにsuper()
は、私の答えのsuper()を読んで、協調メソッドの署名を変更することをお勧めします。
このシナリオの解決策は次のとおりです。
- 階層の最上位クラスは、次のようなカスタムクラスから継承する必要があります
SuperObject
。- クラスが異なる引数を取ることができる場合は、常に受け取ったすべての引数をキーワード引数としてスーパー関数に渡し、常に受け入れ
**kwargs
ます。
class SuperObject:
def __init__(self, **kwargs):
print('SuperObject')
mro = type(self).__mro__
assert mro[-1] is object
if mro[-2] is not SuperObject:
raise TypeError(
'all top-level classes in this hierarchy must inherit from SuperObject',
'the last class in the MRO should be SuperObject',
f'mro={[cls.__name__ for cls in mro]}'
)
# super().__init__ is guaranteed to be object.__init__
init = super().__init__
init()
使用例:
class A(SuperObject):
def __init__(self, **kwargs):
print("A")
super(A, self).__init__(**kwargs)
class B(SuperObject):
def __init__(self, **kwargs):
print("B")
super(B, self).__init__(**kwargs)
class C(A):
def __init__(self, age, **kwargs):
print("C",f"age={age}")
super(C, self).__init__(age=age, **kwargs)
class D(B):
def __init__(self, name, **kwargs):
print("D", f"name={name}")
super(D, self).__init__(name=name, **kwargs)
class E(C,D):
def __init__(self, name, age, *args, **kwargs):
print( "E", f"name={name}", f"age={age}")
super(E, self).__init__(name=name, age=age, *args, **kwargs)
E(name='python', age=28)
出力:
E name=python age=28
C age=28
A
D name=python
B
SuperObject
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
これはかなり理解しやすいです。
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
では、使用するとどうなりますsuper(Child,self)
か?
子インスタンスが作成されると、そのMRO(メソッド解決順序)は、継承に基づいて(子、SomeBaseClass、オブジェクト)の順序になります。(SomeBaseClassには、デフォルトオブジェクトを除いて他の親がないと仮定します)
渡すことによってChild, self
、 super
のMROで検索self
インスタンスを、次の子のプロキシオブジェクトを返す、この場合には、それはSomeBaseClassだ、このオブジェクトは、呼び出し__init__
SomeBaseClassの方法を。つまり、の場合super(SomeBaseClass,self)
、super
返されるプロキシオブジェクトは次のようになります。object
多重継承の場合、MROには多くのクラスを含めることができるため、基本的super
にはMROのどこから検索を開始するかを決定できます。
次のコードを検討してください。
class X():
def __init__(self):
print("X")
class Y(X):
def __init__(self):
# X.__init__(self)
super(Y, self).__init__()
print("Y")
class P(X):
def __init__(self):
super(P, self).__init__()
print("P")
class Q(Y, P):
def __init__(self):
super(Q, self).__init__()
print("Q")
Q()
のコンストラクタをY
に変更するとX.__init__
、次のようになります。
X
Y
Q
しかし、を使用するとsuper(Y, self).__init__()
、次のようになります。
X
P
Y
Q
そして、P
またはQ
も、あなたが書くとき、あなたが知らない別のファイルから関与している可能性があるX
とY
。したがって、基本的に、Yのシグネチャはと同じくらい簡単super(Child, self)
ですが、書いているときに何を参照するかはわかりません。だからこそ、superの方が適しているでしょう。class Y(X)
Y(X)