52

私には2つのクラスがFieldありBackgroundます。彼らは少しこのように見えます:

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)."

私はBackgroundinit ()が最初に呼び出されることを期待していました。「a、b」をFields init()に渡すには、Fieldでaとbを割り当て、次に3つの0を含むリストをfieldに割り当てます。次に、Backgroundのinit()を続行するには、独自のbuildField()を呼び出し、cを含むリストでself.fieldをオーバーライドします。

super()を完全には理解していないようですが、Webやこのあたりで同様の継承の問題を調べた後、問題の解決策を見つけることができませんでした。

クラスが継承されたメソッドをオーバーライドできるc++のような動作を期待していました。どうすればこれまたは類似のものを達成できますか。

これに関連して私が見つけたほとんどの問題は、二重アンダースコアを使用している人々でした。スーパーでの継承に関する私の経験では、継承されたクラスinit()を使用して、さまざまな変数をスーパークラスに渡すだけです。何も上書きする必要はありません。

4

5 に答える 5

52

私はBackgroundinit()が呼び出されることを期待していました。「a、b」をフィールドinit()に渡すには、フィールドにaとbを割り当てます。

ここまでは順調ですね。

次に、3つの0を含むリストをフィールドに割り当てます。

ああ。ここでエラーが発生します。

    self.field = self.buildField()

この行は内Field.__init__selfありますが、はのインスタンスですBackground。したがって、'sではなく' sメソッドself.buildFieldを検索します。BackgroundbuildFieldField

Background.buildField1ではなく2つの引数が必要なので

self.field = self.buildField()

エラーが発生します。


では、Pythonに'sの代わりにField'sメソッドを呼び出すように指示するにはどうすればよいでしょうか。buildFieldBackground

名前マングリング(二重アンダースコアで属性に名前を付ける)の目的は、この正確な問題を解決することです。

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)

メソッド名はinside__buildFieldに「マングル」されているので、inside 、_Field__buildFieldFieldField.__init__

    self.field = self.__buildField()

を呼び出しますself._Field__buildField()。これはField__buildFieldメソッドです。同様に、

    self.field = self.__buildField(c)

内部Background.__init__呼び出しBackground__buildFieldメソッド。

于 2012-10-07T00:35:03.947 に答える
29

C ++の観点からすると、ここには2つの誤解があるかもしれません。

まず、同じ名前で異なるシグネチャを持つメソッドは、C++のようにオーバーロードしません。背景オブジェクトの1つが引数なしでbuildFieldを呼び出そうとすると、Fieldの元のバージョンは呼び出されず、完全に非表示になります。

2番目の問題は、スーパークラスで定義されたメソッドがbuildFieldを呼び出すと、サブクラスバージョンが呼び出されることです。Pythonでは、C ++メソッドのように、すべてのvirtualメソッドが動的にバインドされます。

Field__init__は、引数を取らないbuildFieldメソッドを持つオブジェクトを処理することが期待されています。1つの引数を取るbuildFieldメソッドを持つオブジェクトでメソッドを使用しました。

オブジェクトのsuperタイプは変更されないため、スーパークラスのメソッドが呼び出す可能性のあるメソッドのシグネチャを変更しないでください。

于 2012-10-07T00:32:32.217 に答える
18

Background init()が呼び出されることを期待していました

実はBackground init()呼ばれています。

ただし、Backgroundクラスを見てください。

class Background( Field ):
    def __init__( self, a, b, c ):
        super(Background, self).__init__( a, b )
        self.field = self.buildField( c )

したがって、の最初のステートメントは、 initメソッド__init__を呼び出してas引数を渡すことです。これで、これは実際には..の参照になります。super class(Field)selfselfBackground class

今あなたのフィールドクラスに:-

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__init__Background

そのため、エラーが発生します。

エラー「TypeError:buildField()は正確に2つの引数を取ります(1つ指定)。

値を渡していないので..したがって、渡された値のみが暗黙的selfです。

于 2012-10-07T00:17:54.673 に答える
5

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()はを呼び出しているため、このエラーが発生します。buildField()Background


あなたのコードは次のように書くほうがよいようです。

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 )

基本コンストラクターを終了させることができない場合は、設計に欠陥があることを示します。

したがって、コンストラクタでこれらのメソッド呼び出す必要がある場合は、デコレータまたはbuildField()を使用してクラスに属するように分離することをお勧めします。classmethodstaticmethod

ただし、基本クラスコンストラクターが内部からインスタンスメソッドを呼び出さない場合は、この基本クラスの任意のメソッドを安全に上書きできます。

于 2012-10-07T00:31:03.033 に答える
1

Overriding話されていますが、私には聞こえますchaining constructors or (methods)

また、プロパティを上書きしているように聞こえます。

説明させてください:

  • fieldという名前のプロパティは、として初期化され[0,0,0]ます。 @propertyデコレータはより適切に見えます。

  • 次に、Backgroundクラスはこのプロパティを上書きします。


迅速で汚い解決策

私はあなたのビジネスロジックを知りませんが、時々スーパークラスの__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
于 2016-03-27T12:58:46.197 に答える