親クラスの初期化子を暗黙的に呼び出す
-
23-09-2019 - |
質問
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
私がいつも思っていたように。
Python-2.x で、現在のクラス ( B
で super(B, self).__init__(1, b, c)
)?
解決
短い答え:いいえ、暗黙的にはPython 2.xで右親クラスの右引数と右__init__
呼び出すための方法はありません。
なお、ここで示すようにコードが正しくありません:あなたはスーパー()__init__
を使用する場合は、階層内のすべてのクラスは、その__init__
方法で同じシグネチャを持つ必要があります。あなたは多重継承を使用する新しいサブクラスを導入した場合にそれ以外の場合は、あなたのコードは動作を停止することができます。
の詳細な説明は http://fuhm.net/super-harmful/するを参照してください。 (写真付き)問題ます。
他のヒント
コードはメソッドの解決順序とは何の関係もありません。メソッドの解決は、あなたの例の場合ではない多重継承の場合に行われます。あなたのコードは単に間違っている、と仮定しているからです 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
(修正版 テンプレートパターン).
現在、親のメソッドは実際には暗黙的に呼び出されていますが、コードが読みにくく、ゲインが低いため、暗黙的よりも明示的な方が優れているというPython zenに同意する必要があります。しかし、すべてに注意してください _init
メソッドには同じパラメータがあるため、親のことを完全に忘れることはできませんし、そうすることはお勧めしません。
単一継承の場合、より良いアプローチは、親のメソッドを呼び出さずに明示的に呼び出すことです。 super
. 。そうする必要はありません 現在のクラスに名前を付けます, 、それでも、親のクラスが誰であるかを気にする必要があります。
良い読み物は次のとおりです。 Python はどのようにしてスーパー 正しいことを行うのか その質問で提案されているリンク、特に Python の Super は気の利いたものですが、使えません
階層が変更される可能性がある場合は、悪い設計の兆候であり、そのコードを使用しているすべての部分に影響を与えるため、推奨すべきではありません。
編集2
別の例が思い浮かびますが、これはメタクラスを使用しています。ウルウィド図書館 メタクラスを使用する 属性を保存するには、 __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
>>>
おそらく、あなたが探しているもののメタクラスのですか?
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のためのすべてのbaseclasses
を見つけることができる塩基配列が含まれていることを確認しますされますは、私の知る限りでは、以下が一般的に行われていません。しかし、それは動作するようには思えない。
常にマングルダブルアンダースコアは、それらがで定義されているクラスの名前が含まれるように属性を指定したクラス定義のメソッド。だから、あなたはインスタンスがそれを見ることができ、名前マングル形でクラスへの参照を隠しておく場合には、あなたは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>