Field
とBackground
の2つのクラスがあります。彼らはこのように少し見えます:
_class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
def buildField( self ):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.field = self.buildField( c )
def buildField( self, c ):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background( a, b, c )
_
このエラーは、フィールドのbuildField()
を指しています:
_"TypeError: buildField() takes exactly 2 arguments (1 given)."
_
Background init()が最初に呼び出されることを期待していました。 "a、b"をFields init()に渡すには、Fieldでaとbを割り当て、次に3つの0を含むリストをfieldに割り当てます。次に、Backgroundのinit()を続行し、独自のbuildField()を呼び出して、cを含むリストでself.fieldをオーバーライドします。
私はsuper()を完全には理解していないようですが、Webやこの周辺で同様の継承の問題を見た後、私の問題の解決策を見つけることができませんでした。
クラスが継承されたメソッドをオーバーライドできるc ++のような動作を期待していました。どうすればこれまたは同様のことを達成できますか?.
これに関連して見つかったほとんどの問題は、二重アンダースコアを使用している人々でした。スーパーでの継承の私の経験は、継承されたクラスinit()を使用して、スーパークラスに異なる変数を渡すだけです。何も上書きする必要はありません。
C++の観点から見ると、ここには2つの誤解があるかもしれません。
まず、同じ名前で異なるシグネチャを持つメソッドは、C++のようにオーバーロードしません。 Backgroundオブジェクトの1つが引数なしでbuildFieldを呼び出そうとした場合、Fieldからの元のバージョンは呼び出されません。完全に非表示になっています。
2番目の問題は、スーパークラスで定義されたメソッドがbuildFieldを呼び出すと、サブクラスバージョンが呼び出されることです。 Pythonでは、C++ virtual
メソッドのように、allメソッドが動的にバインドされます。
フィールドの__init__
は、引数を取らないbuildFieldメソッドを持つオブジェクトを処理することを期待されていました。 1つの引数を取るbuildFieldメソッドを持つオブジェクトでメソッドを使用しました。
super
の利点は、オブジェクトのタイプを変更しないため、スーパークラスのメソッドが呼び出す可能性のあるメソッドのシグネチャを変更しないことです。
Background init()が呼び出されることを期待していました。 "a、b"をFields init()に渡し、Fieldでaとbを割り当てます
ここまでは順調ですね。
次に、フィールドに3つの0を含むリストを割り当てます。
あ。ここでエラーが発生します。
self.field = self.buildField()
この行はField.__init__
内で発生しますが、self
はBackground
のインスタンスです。したがって、self.buildField
は、Background
ではなく、buildField
のField
メソッドを検出します。
Background.buildField
は1ではなく2つの引数を予期するため、
self.field = self.buildField()
エラーを発生させます。
それでは、どのようにPythonにField
の代わりにbuildField
のBackground
メソッドを呼び出すように指示しますか?
name mangling (二重アンダースコアで属性に名前を付ける)の目的は、この正確な問題を解決することです。
class Field(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.field = self.__buildField()
def __buildField(self):
field = [0,0,0]
return field
class Background(Field):
def __init__(self, a, b, c):
super(Background, self).__init__(a, b)
self.field = self.__buildField(c)
def __buildField(self, c):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background(a, b, c)
メソッド名__buildField
はField
内で_Field__buildField
に「マングル」されているため、Field.__init__
内では、
self.field = self.__buildField()
self._Field__buildField()
を呼び出します。これは、Field
の__buildField
メソッドです。同様に、
self.field = self.__buildField(c)
Background.__init__
の内部でBackground
の__buildField
メソッドを呼び出します。
Background init()が呼び出されることを期待していました
実際、Background init()
が呼び出されています。
しかしバックグラウンドクラスを見てください。
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.field = self.buildField( c )
したがって、__init__
の最初のステートメントは、super class(Field)
initメソッドを呼び出して、self
を引数として渡します。このself
は、実際にはBackground class
..の参照です。
Fieldクラスで:-
class Field( object ):
def __init__( self, a, b ):
print self.__class__ // Prints `<class '__main__.Background'>`
self.a = a
self.b = b
self.field = self.buildField()
buildField()
メソッドは、実際にBackgroundクラスのメソッドを呼び出しています。これは、ここのself
がBackground
クラスのインスタンスであるためです(self.__class__
の__init__
メソッドでField class
メソッドを印刷してみてください) ).. Background
クラスから__init__
メソッドを呼び出して渡したとき。
だからエラーが発生します。
エラー「TypeError:buildField()は、正確に2つの引数(1つ指定)を取ります。
値を渡していないので、渡された値のみが暗黙のself
です。
super(Background, self).__init__( a, b )
は以下を呼び出します:
_def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
_
Field
にあります。ただし、ここでself
はbackground
インスタンスを指し、self.buildField()
は実際にBackground
のbuildField()
を呼び出しているため、そのエラーが発生します。
あなたのコードは次のように書かれるべきだと思われます:
_class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = Field.buildField()
@classmethod
def buildField(cls):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__(a, b)
self.field = Background.buildField(c)
@classmethod
def buildField(cls,c):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background( a, b, c )
_
ベースコンストラクターの終了を許可できない場合、デザインに欠陥があることを通知します。
したがって、 classmethod
decorator または staticmethod
を使用して、クラスに属するbuildField()
を分離することをお勧めします。 コンストラクターでこれらのメソッドを呼び出す必要がある場合.
ただし、基本クラスコンストラクターが内部からインスタンスメソッドを呼び出さない場合、この基本クラスのメソッドを安全に上書きできます。
Overriding
について話しますが、私には聞こえますchaining constructors or (methods)
また、overwriting propertiesのようにも聞こえます:
説明させてください:
フィールドという名前のプロパティは、[0,0,0]
として初期化されます。 @property
デコレータはより適切に見えます。
次に、Background
class over-writeこのプロパティ。
私はあなたのビジネスロジックを知りませんが、時々スーパークラスの__init__
メソッドをバイパスすることで、より多くの制御ができました:
#!/usr/bin/env python
class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
def buildField( self ):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
# super(Background, self).__init__( a, b )
# Unfortunately you should repeat or move initializing a and b
# properties here
self.a = a
self.b = b
self.field = self.buildField( c )
def buildField( self, c ):
# You can access super class methods
assert super(Background, self).buildField() == [0,0,0]
field = [c]
return field
a, b, c = 0, 1, 2
bg = Background(a,b,c)
assert bg.field == [2]
より簡潔な構文があります。
#!/usr/bin/env python
class Field( object ):
@property
def field(self):
return [0,0,0]
def __init__( self, a, b ):
self.a = a
self.b = b
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.c = c
assert (self.a, self.b, self.c) == (0,1,2) # We assigned a and b in
# super class's __init__ method
assert super(Background, self).field == [0,0,0]
assert self.field == [2]
@property
def field(self):
return [self.c]
a, b, c = 0, 1, 2
background = Background( a, b, c )
print background.field