6

super() 関数で取得したインスタンスの型が知りたいです。私は試しprint(super())てみました__print(type(super()))__

class Base:
    def __init__(self):
        pass

class Derive(Base):
    def __init__(self):
        print(super())        
        print(type(super()))        
        super().__init__()

d = Derive()

結果は

<super: <class 'Derive'>, <Derive object>>
<class 'super'>

これらの結果でsuper().__init__()、正しいコンストラクターをどのように呼び出すのか疑問に思っていました。

4

2 に答える 2

4

やりたいことをsuper()直接行うことはできません。代わりに、クラス MRO ( を参照class.__mro__) に移動します。

class Derive(Base):
    def __init__(self):
        mro = type(self).__mro__
        parent = mro[mro.index(__class__) + 1]
        print(parent)

これは、現在の関数が定義されているクラスを参照する__class__魔法のクロージャ変数*です。Derive上記は、ダイヤモンドの継承パターンを生成する場合でも、 を使用して追加のクラスをサブクラス化または混合する場合でも機能し続けます。

デモ:

>>> class Base: pass
... 
>>> class Derive(Base):
...     def __init__(self):
...         mro = type(self).__mro__
...         parent = mro[mro.index(__class__) + 1]
...         print(parent)
... 
>>> Derive()
<class '__main__.Base'>
<__main__.Derive object at 0x10f7476a0>
>>> class Mixin(Base): pass
... 
>>> class Multiple(Derive, Mixin): pass
... 
>>> Multiple()
<class '__main__.Mixin'>
<__main__.Multiple object at 0x10f747ba8>

クラスがとのMultiple両方から継承し、MRO 内の次のクラスが であること注意してくださいDeriveMixinMixin BaseMixin Base

これは何をコピーsuper()します。現在のクラスに関連して、インスタンスの MRO 内の次のクラスを見つけます。


*背景については、Python 3.x の super() マジックはなぜ? を参照してください。

于 2015-08-14T16:07:17.830 に答える
2

あなたのコメントから、super次に呼び出すメソッドをどのように知っているか知りたいです。Super はインスタンスの mro を検査し、それが含まれている現在のクラス メソッドを認識し、次のメソッドを呼び出します。次のデモは Python 2 および 3 で動作し、Python 2 ではメタクラスのおかげで各クラスの名前が出力されるため、その出力を使用します。

まず、印刷をより良くするためのインポートとセットアップ:

import inspect

class Meta(type):
    def __repr__(cls):
        return cls.__name__

次に、スーパー オブジェクト自体に基づいて何が起こっているかを示す関数を定義します。

def next_in_line(supobj):
    print('The instance class: {}'.format(supobj.__self_class__))
    print('in this class\'s method: {}'.format(supobj.__thisclass__))
    mro = inspect.getmro(supobj.__self_class__)
    nextindex = mro.index(supobj.__thisclass__) + 1
    print('super will go to {} next'.format(mro[nextindex]))

最後に、 C3 線形化に関するウィキペディアのエントリの例に基づいてクラス階層を宣言します。十分に複雑な例として、reprPython3 ではメタクラスが機能しないことに注意してください。ただし、属性の割り当てによって壊れることはありません。また、Python 3super(Name, self)と同等の完全な super 呼び出しを使用しており、引き続き機能することにも注意してください。super()

class O(object):
    __metaclass__ = Meta

    def __init__(self):
        next_in_line(super(O, self))
        super(O, self).__init__()

class A(O):
    def __init__(self):
        next_in_line(super(A, self))
        super(A, self).__init__()


class B(O):
    def __init__(self):
        next_in_line(super(B, self))
        super(B, self).__init__()


class C(O):
    def __init__(self):
        next_in_line(super(C, self))
        super(C, self).__init__()


class D(O):
    def __init__(self):
        next_in_line(super(D, self))
        super(D, self).__init__()


class E(O):
    def __init__(self):
        next_in_line(super(E, self))
        super(E, self).__init__()


class K1(A, B, C):
    def __init__(self):
        next_in_line(super(K1, self))
        super(K1, self).__init__()


class K2(D, B, E):
    def __init__(self):
        next_in_line(super(K2, self))
        super(K2, self).__init__()


class K3(D, A):
    def __init__(self):
        next_in_line(super(K3, self))
        super(K3, self).__init__()


class Z(K1, K2, K3):
    def __init__(self):
        next_in_line(super(Z, self))
        super(Z, self).__init__()

Z の mro を出力すると、継承ツリーに適用されたこのアルゴリズムによって定義されたメソッド解決順序が得られます。

>>> print(inspect.getmro(Z))
(Z, K1, K2, K3, D, A, B, C, E, O, <type 'object'>)

Z() を呼び出すときは、関数が mro を使用するため、各メソッドを順番に参照します。

>>> Z()
The instance class: Z
in this class's method: Z
super will go to K1 next
The instance class: Z
in this class's method: K1
super will go to K2 next
The instance class: Z
in this class's method: K2
super will go to K3 next
The instance class: Z
in this class's method: K3
super will go to D next
The instance class: Z
in this class's method: D
super will go to A next
The instance class: Z
in this class's method: A
super will go to B next
The instance class: Z
in this class's method: B
super will go to C next
The instance class: Z
in this class's method: C
super will go to E next
The instance class: Z
in this class's method: E
super will go to O next
The instance class: Z
in this class's method: O
super will go to <type 'object'> next

で停止しobject.__init__ます。上記から、superはインスタンスのクラス、現在のクラスのメソッドを常に認識しており、インスタンス クラスの MRO から次に進むべき場所を推測できることがわかります。


基本クラスの名前を知りたいのですが?

直接ベース (多重継承の場合は複数) のみが必要な場合は__bases__、タプルを返す属性を使用できます。

>>> Derive.__bases__
(<class __main__.Base at 0xffeb517c>,)

>>> Derive.__bases__[0].__name__
'Base'

superMethod Resolution Order (元の呼び出し元のクラスに基づく)を取得するには、inspect モジュールをお勧めします。

>>> import inspect
>>> inspect.getmro(Derive)
(<class __main__.Derive at 0xffeb51dc>, <class __main__.Base at 0xffeb517c>)

スーパーから入手

super().__self_class__インスタンスクラスをsuper().__thisclass__提供し、現在のクラスを提供します。インスタンスの MRO を使用して、次に来るクラスを検索できます。最終的な親でこれを行うことはないと思うので、インデックスエラーをキャッチしていません:

class Base:
    def __init__(self):
        print(super().__self_class__)
        print(super().__thisclass__)


class Derive(Base):
    def __init__(self):
        print(super().__self_class__)
        print(super().__thisclass__)
        mro = inspect.getmro(super().__self_class__)
        nextindex = mro.index(super().__thisclass__) + 1
        print('super will go to {} next'.format(mro[nextindex]))
        super().__init__()


>>> d = Derive()
<class '__main__.Derive'>
<class '__main__.Derive'>
super will go to <class '__main__.Base'> next
<class '__main__.Derive'>
<class '__main__.Base'>
于 2015-08-14T16:10:32.353 に答える