Python: あなたは知らない超

高洛峰
高洛峰オリジナル
2016-11-15 15:14:371182ブラウズ

super()入門

クラス継承ではメソッドを再定義すると親クラスの同名のメソッドをオーバーライドしますが、場合によっては親クラスの機能も同時に実現したい場合があります。この場合、親クラスのメソッドを呼び出す必要があります。これは、次のように super を使用して実行できます。

class Animal(object):
    def __init__(self, name):
        self.name = name
    def greet(self):
        print 'Hello, I am %s.' % self.name

class Dog(Animal):
    def greet(self):
        super(Dog, self).greet()   # Python3 可使用 super().greet()
        print 'WangWang...'

上記では、Animal が親クラスで、Dog がサブクラスであると再定義しました。 Dog クラスのgreetメソッドを使用して親クラスの関数を実装すると同時に、親クラスのメソッドを呼び出しています。次の使用法を参照してください:

>>> dog = Dog('dog')
>>> dog.greet()
Hello, I am dog.
WangWang..

super の最も一般的な使用法の 1 つは次のとおりです。

class Base(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

class A(Base):
    def __init__(self, a, b, c):
        super(A, self).__init__(a, b)  # Python3 可使用 super().__init__(a, b)
        self.c = c

super() の詳細を見てみましょう

上記の使用法を読んだ後、super の使用は非常に簡単だと思うかもしれません。親クラスを取得して、親クラスのメソッドを呼び出すだけです。実際、上記のケースでは、super によって取得されたクラスがたまたま親クラスですが、他のケースでは、実際には Super が親クラスと実質的な関係を持っているとは限りません。

多重継承を伴うもう少し複雑な例を見てみましょう。コードは次のとおりです:

class Base(object):
    def __init__(self):
        print "enter Base"
        print "leave Base"

class A(Base):
    def __init__(self):
        print "enter A"
        super(A, self).__init__()
        print "leave A"

class B(Base):
    def __init__(self):
        print "enter B"
        super(B, self).__init__()
        print "leave B"

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

そのうち、Base が親クラスで、A と B は Base から継承し、C は A と B から継承します。関係は次のとおりです:

      Base
      /  \
     /    \
    A      B
     \    /
      \  /
       C

さて、使用法を見てみましょう:

>>> c = C()
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C

super が「親クラスのメソッドを呼び出す」ことを意味すると思うなら、おそらく enter A の次の文がなぜそうではないのか不思議に思っているでしょう。 Base と入力しますが、B と入力します。その理由は、スーパーが親クラスと実質的な関係を持たないためです。次に、スーパーがどのように機能するかを理解しましょう。

MRO リスト

実際、Python は定義したクラスごとに、クラスの継承順序を表すメソッド解決順序 (MRO) リストを計算します。次のメソッドを使用してメソッド クラス MRO リストを取得できます。
>>> C.mro()   # or C.__mro__ or C().__class__.mro()
[__main__.C, __main__.A, __main__.B, __main__.Base, object]

では、この MRO リストの順序は、C3 線形化アルゴリズムによってどのように決定されるのでしょうか? ここでは、このアルゴリズムについて詳しく説明しません。一般的に、MRO リストについては、次のとおりです。クラスはすべての親クラスの MRO リストをマージし、次の 3 つの原則に従います:

サブクラスは常に親クラスの前にあります

複数の親クラスがある場合、それらはそれらに従ってリストに配置されますorder in がチェックされています

次のクラスに正当な選択肢が 2 つある場合、最初の親クラスが選択されます

超原則

超は次のように動作します:

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

ここで、cls はクラスを表し、inst はインスタンスを表します。上記のコードは 2 つのことを行います:

inst の MRO リストを取得します

現在の MRO リストで cls のインデックスを見つけて、その次のクラス (mro[index + 1]) を返します

super( cls, inst)、Python は inst の MRO リストで cls の次のクラスを検索します。

さて、前の例に戻りましょう。

まずクラス C の __init__ メソッドを見てみましょう:

super(C, self).__init__()

ここでの self は C の現在のインスタンス、self.__class__.mro() 結果は次のようになります:

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

C の次のクラスが A であることがわかります。 A の __init__ に到達したら、A と入力すると、次のコード行が実行されます:

super(A, self).__init__()

ここでの self も現在の C のインスタンスであることに注意してください。MRO リストは上記と同じです。 MRO 内の A の次のクラスを検索し、それが B であることが判明したため、B の __init__ にジャンプします。このとき、Enter Base の代わりに Enter B が表示されます。

プロセス全体は比較的明確です。重要なのは、スーパーが親クラスのメソッドを呼び出すことを当然のことと考えるのではなく、スーパーがどのように機能するかを理解することです。

概要

実際、super は親クラスと実質的な関係はありません。

super(cls, inst) は、inst の MRO リスト内の cls の次のクラスを取得します。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。