superと言えばsuperみんなは簡単だと思うかもしれませんね。だって、親クラスのメソッドを呼び出すために使うだけじゃないですか。もし本当にそんなに簡単なら、この記事も存在しなかったでしょう。詳しく説明しますので、お聞きください。
約束
始める前に、この記事で使用するPythonのバージョンを決めておきましょう。デフォルトはPython 3です。つまり、この記事で定義するクラスはすべて新しいスタイルのクラスです。Python 2を使っている場合は、必ず継承してください。object:
# 默认, Python 3
class A:
pass
# Python 2
class A(object):
pass
Python 3とPython 2のもう一つの違いは、Python 3では直接使用できることですsuper().xxxの代わりにsuper(Class, self).xxx :
# 默认,Python 3
class B(A):
def add(self, x):
super().add(x)
# Python 2
class B(A):
def add(self, x):
super(B, self).add(x)
したがって、Python 2を使っている場合は、この記事のsuper()を置き換えてくださいsuepr(Class, self) 。
他にもPython 2と互換性のない状況があれば、記事中で注記します。
単一継承
単一継承ではsuperみんなが思っているように、主に親クラスのメソッドを呼び出すために使われます。
class A:
def __init__(self):
self.n = 2
def add(self, m):
print('self is {0} @A.add'.format(self))
self.n += m
class B(A):
def __init__(self):
self.n = 3
def add(self, m):
print('self is {0} @B.add'.format(self))
super().add(m)
self.n += 3
以下のコードを実行した後、 b.nの値はいくつになると思いますか?
b = B() b.add(2) print(b.n)
実行結果は以下のとおりです:
self is <__main__.B object at 0x106c49b38> @B.add self is <__main__.B object at 0x106c49b38> @A.add 8
この結果は2つのことを示しています:
- 1、super().add(m)確かに親クラスAのaddメソッドを呼び出しています。
- 2、super().add(m)親クラスのメソッドを呼び出すdef add(self, m)とき、このとき親クラス内のselfは親クラスのインスタンスではなく子クラスのインスタンスです。したがってb.add(2)の後の結果は5ではなく4 。
この結果はあなたの予想と同じでしょうか?次に多重継承の例を見てみましょう。
多重継承
今回はもう一つのclass Cともう一つのclass D:
class C(A):
def __init__(self):
self.n = 4
def add(self, m):
print('self is {0} @C.add'.format(self))
super().add(m)
self.n += 4
class D(B, C):
def __init__(self):
self.n = 5
def add(self, m):
print('self is {0} @D.add'.format(self))
super().add(m)
self.n += 5
今度は以下のコードは何を出力するでしょうか?
d = D() d.add(2) print(d.n)
今回の出力は以下のとおりです:
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @C.add self is <__main__.D object at 0x10ce10e48> @A.add 19
あなたの予想は当たりましたか?おそらく上記のコードの出力が次のようになると考えていたでしょう:
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @A.add 15
なぜ予想と違うのでしょうか?次に一緒にsuperの謎を見ていきましょう。
superはクラス
私たちがsuper()を呼び出すとき、実際にはsuperクラスをインスタンス化しています。見間違いではありません、superはクラスであり、キーワードでも関数でも他のデータ構造でもありません:
>>> class A: pass ... >>> s = super(A) >>> type(s) <class 'super'> >>>
ほとんどの場合、superには2つの非常に重要な情報が含まれています: MROと、MRO内のクラスです。以下のようにsuper呼び出すとき:
super(a_type, obj)
MROとはtype(obj)のMROを指し、MRO内のそのクラスはa_type、同時にisinstance(obj, a_type) == True 。
このように呼び出すとき:
super(type1, type2)
MROはtype2のMRO, MRO内のそのクラスはtype1、同時にissubclass(type2, type1) == True 。
では、super()は実際に何をするのでしょうか?簡単に言うと、MROと、MRO内のクラスを提供します。C , super()は、MRO 中 Cの後のクラスからメソッドを探すオブジェクトを返します。
つまり、検索方法は通常のメソッドのようにすべてのMROクラスから探すのではなく、MROの tail から探します。
例を挙げると、MRO:
[A, B, C, D, E, object]
以下の呼び出し:
super(C, A).foo()
superはCの後からのみ検索します。つまり、D 或 E 或 object内でのみfooメソッドを検索します。
多重継承におけるsuperの動作方法
再び前述の
d = D() d.add(2) print(d.n)
に戻ります。今ならなぜ出力が
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @C.add self is <__main__.D object at 0x10ce10e48> @A.add 19
になるのか、少しはわかったのではないでしょうか ;)
以下で具体的に分析してみましょう:
DのMROは:[D, B, C, A, object] 。 注記:を使用してD.mro()(Python 2ではD.__mro__) 確認できますDのMRO情報)
-
詳細なコード分析は以下のとおりです:
class A: def __init__(self): self.n = 2 def add(self, m): # 第四步 # 来自 D.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @A.add'.format(self)) self.n += m # d.n == 7 class B(A): def __init__(self): self.n = 3 def add(self, m): # 第二步 # 来自 D.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @B.add'.format(self)) # 等价于 suepr(B, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 B 之后的 [C, A, object] 中查找 add 方法 super().add(m) # 第六步 # d.n = 11 self.n += 3 # d.n = 14 class C(A): def __init__(self): self.n = 4 def add(self, m): # 第三步 # 来自 B.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @C.add'.format(self)) # 等价于 suepr(C, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 C 之后的 [A, object] 中查找 add 方法 super().add(m) # 第五步 # d.n = 7 self.n += 4 # d.n = 11 class D(B, C): def __init__(self): self.n = 5 def add(self, m): # 第一步 print('self is {0} @D.add'.format(self)) # 等价于 super(D, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 D 之后的 [B, C, A, object] 中查找 add 方法 super().add(m) # 第七步 # d.n = 14 self.n += 5 # self.n = 19 d = D() d.add(2) print(d.n)呼び出しプロセスの図は以下のとおりです:
D.mro() == [D, B, C, A, object] d = D() d.n == 5 d.add(2) class D(B, C): class B(A): class C(A): class A: def add(self, m): def add(self, m): def add(self, m): def add(self, m): super().add(m) 1.---> super().add(m) 2.---> super().add(m) 3.---> self.n += m self.n += 5 <------6. self.n += 3 <----5. self.n += 4 <----4. <--| (14+5=19) (11+3=14) (7+4=11) (5+2=7)
これで d.add(2) の後に d.n の値が 19 になる理由がわかりましたね ;)
記事の出典:https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html