7
class A(object):
    def __init__(self, a, b, c):
        #super(A, self).__init__()
        super(self.__class__, self).__init__()


class B(A):
    def __init__(self, b, c):
        print super(B, self)
        print super(self.__class__, self)
        #super(B, self).__init__(1, b, c)
        super(self.__class__, self).__init__(1, b, c)

class C(B):
    def __init__(self, c):
        #super(C, self).__init__(2, c)
        super(self.__class__, self).__init__(2, c)
C(3)

上記のコードでは、コメントアウトされた__init__呼び出しは、スーパークラスの初期化を行うための一般的に受け入れられている「スマート」な方法のようです。ただし、クラス階層が変更される可能性がある場合は、最近までコメントなしのフォームを使用していました。

B上記の階層でのスーパーコンストラクターの呼び出しでは、これB.__init__が再度呼び出されますが、self.__class__実際には、私がいつも想定していたものCではないようです。B

BPython-2.xで、現在のクラス( in in )に名前を付けずにスーパーコンストラクターを呼び出すときに、(すべての親クラスを正しい順序で初期化することに関して)適切なMROを維持できる方法はありsuper(B, self).__init__(1, b, c)ますか?

4

4 に答える 4

3

簡単な答え:いいえ、__init__Python2.xでは正しい親クラスの正しい引数を使用して暗黙的に権利を呼び出す方法はありません。

ちなみに、ここに示されているコードは正しくありません:super()を使用する場合。__init__、次に、階層内のすべてのクラスの__init__メソッドに同じシグネチャが必要です。そうしないと、多重継承を使用する新しいサブクラスを導入した場合に、コードが機能しなくなる可能性があります。

問題の詳細な説明(写真付き)については、 http://fuhm.net/super-harmful/を参照してください。

于 2010-05-03T15:26:05.707 に答える
1

おそらくあなたが探しているのはメタクラスですか?

class metawrap(type):
    def __new__(mcs,name, bases, dict):
        dict['bases'] = bases
        return type.__new__(mcs,name,bases,dict)

class A(object):
    def __init__(self):
        pass
    def test(self):
        print "I am class A"

class B(A):
    __metaclass__ = metawrap
    def __init__(self):
        pass
    def test(self):
        par = super(self.bases[0],self)
        par.__thisclass__.test(self)
foo = B()
foo.test()

「私はクラスAです」と印刷します

メタクラスが行うことは、(オブジェクトではなく)Bクラスの最初の作成をオーバーライドし、各Bオブジェクトの組み込みディクショナリにBのすべてのベースクラスを見つけることができるベース配列が含まれていることを確認することです。

于 2010-03-01T10:32:09.350 に答える
1

あなたのコードはメソッド解決の順序とは何の関係もありません。メソッドの解決は、例の場合とは異なり、多重継承の場合に行われます。self.__class__これは、メソッドが定義されているクラスと実際には同じクラスであり、これが間違っていると想定しているため、コードは単純に間違っています。

>>> class A(object):
...     def __init__(self):
...         print self.__class__
... 
>>> 
>>> class B(A):
...     def __init__(self):
...         A.__init__(self)
... 
>>> B()
<class '__main__.B'>
<__main__.B object at 0x1bcfed0>
>>> A()
<class '__main__.A'>
<__main__.A object at 0x1bcff90>
>>> 

したがって、いつ電話する必要があるか:

super(B, self).__init__(1, b, c)

あなたは確かに呼んでいます:

# super(self.__class__, self).__init__(1, b, c)
super(C, self).__init__(1, b, c)

編集:質問にもっとよく答えようとしています。

class A(object):
    def __init__(self, a):
        for cls in self.__class__.mro():
            if cls is not object:
                cls._init(self, a)
    def _init(self, a):
        print 'A._init'
        self.a = a

class B(A):
    def _init(self, a):
        print 'B._init'

class C(A):
    def _init(self, a):
        print 'C._init'

class D(B, C):
    def _init(self, a):
        print 'D._init'


d = D(3)
print d.a

プリント:

D._init
B._init
C._init
A._init
3

テンプレートパターンの修正バージョン)。

現在、親のメソッドは実際には暗黙的に呼び出されますが、コードが読みにくく、ゲインが低いため、明示的が暗黙的よりも優れているpythonzenに同意する必要があります。ただし、すべての_initメソッドが同じパラメーターを持っていることに注意してください。親を完全に忘れることはできません。そうすることはお勧めしません。

単一継承の場合、より良いアプローチは、を呼び出さずに親のメソッドを明示的に呼び出すことsuperです。そうすることで、現在のクラスに名前を付ける必要はありませんが、それでも親のクラスが誰であるかを気にする必要があります。

良い読み物は次のとおりです:how-does-pythons-super-do-the-right-thingとその質問で提案されたリンク、特にPythonのSuperは気の利いたものですが、使用することはできません

階層が変更される可能性がある場合は、設計が不適切であり、そのコードを使用しているすべての部分に影響を与えるため、推奨されるべきではありません。

編集2

別の例が思い浮かびますが、これはメタクラスを使用しています。Urwidライブラリは、メタクラスを使用して属性をクラスに格納する__superため、その属性にアクセスするだけで済みます。

元:

>>> class MetaSuper(type):
...     """adding .__super"""
...     def __init__(cls, name, bases, d):
...         super(MetaSuper, cls).__init__(name, bases, d)
...         if hasattr(cls, "_%s__super" % name):
...             raise AttributeError, "Class has same name as one of its super classes"
...         setattr(cls, "_%s__super" % name, super(cls))
... 
>>> class A:
...  __metaclass__ = MetaSuper
...  def __init__(self, a):
...   self.a = a
...   print 'A.__init__'
... 
>>> class B(A):
...  def __init__(self, a):
...   print 'B.__init__'
...   self.__super.__init__(a)
... 
>>> b = B(42)
B.__init__
A.__init__
>>> b.a
42
>>> 
于 2010-04-30T08:24:05.337 に答える
0

私の知る限り、以下は一般的に行われていません。しかし、それは機能しているようです。

特定のクラス定義のメソッドは、常に二重アンダースコア属性をマングルして、それらが定義されているクラスの名前を含めます。したがって、インスタンスが参照できる名前マングル形式でクラスへの参照を隠しておく場合は、次を使用できます。への呼び出しでそれsuper

基本クラスに実装することにより、オブジェクト自体の参照を隠しておく例__new__

def mangle(cls, name):
    if not name.startswith('__'):
        raise ValueError('name must start with double underscore')
    return '_%s%s' % (cls.__name__, name)

class ClassStasher(object):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for c in cls.mro():
            setattr(obj, mangle(c, '__class'), c)
        return obj

class A(ClassStasher):
    def __init__(self):
        print 'init in A', self.__class
        super(self.__class, self).__init__()

class B(A):
    def __init__(self):
        print 'init in B', self.__class
        super(self.__class, self).__init__()

class C(A):
    def __init__(self):
        print 'init in C', self.__class
        super(self.__class, self).__init__()

class D(B, C):
    def __init__(self):
        print 'init in D', self.__class
        super(self.__class, self).__init__()


d = D()    
print d

そして、同様のことを行いますが、メタクラスを使用__classし、クラスオブジェクト自体の参照を隠します。

class ClassStasherType(type):
    def __init__(cls, name, bases, attributes):
        setattr(cls, mangle(cls, '__class'), cls)

class ClassStasher(object):
    __metaclass__ = ClassStasherType

class A_meta(ClassStasher):
    def __init__(self):
        print 'init in A_meta', self.__class
        super(self.__class, self).__init__()

class B_meta(A_meta):
    def __init__(self):
        print 'init in B_meta', self.__class
        super(self.__class, self).__init__()

class C_meta(A_meta):
    def __init__(self):
        print 'init in C_meta', self.__class
        super(self.__class, self).__init__()

class D_meta(B_meta, C_meta):
    def __init__(self):
        print 'init in D_meta', self.__class
        super(self.__class, self).__init__()


d = D_meta()    
print d

これをすべて一緒に、1つのソースファイルとして実行します。

% python /tmp/junk.py
init in D <class '__main__.D'>
init in B <class '__main__.B'>
init in C <class '__main__.C'>
init in A <class '__main__.A'>
<__main__.D object at 0x1004a4a50>
init in D_meta <class '__main__.D_meta'>
init in B_meta <class '__main__.B_meta'>
init in C_meta <class '__main__.C_meta'>
init in A_meta <class '__main__.A_meta'>
<__main__.D_meta object at 0x1004a4bd0>
于 2010-05-04T17:06:39.613 に答える