Ich habe zwei Klassen, Field und Background. Sie sehen ein bisschen so aus:

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 )

Dieser Fehler zeigt auf Field's buildField():

"TypeError: buildField() takes exactly 2 arguments (1 given)."

Ich habe erwartet, dass Hintergrund init () zuerst aufgerufen wird. Um "a, b" an Felder init () zu übergeben, Feld, um a und b zuzuweisen, und dann, um dem Feld eine Liste mit drei Nullen zuzuweisen. Damit das init () von Background fortgesetzt werden kann, rufen Sie dann sein eigenes buildField () auf und überschreiben Sie self.field mit einer Liste, die c enthält.

Es scheint, dass ich super () nicht vollständig verstehe, aber ich konnte keine Lösung für mein Problem finden, nachdem ich mir ähnliche Vererbungsprobleme im Web und hier angesehen hatte.

Ich habe ein Verhalten wie c ++ erwartet, bei dem eine Klasse eine vererbte Methode überschreiben kann. Wie kann ich dies oder ähnliches erreichen?

Die meisten Probleme, die ich im Zusammenhang damit fand, waren Personen, die doppelte Unterstriche verwendeten. Meine Erfahrung mit der Vererbung mit Super besteht darin, die geerbte Klasse init () zu verwenden, um einfach verschiedene Variablen an die Superklasse zu übergeben. Nichts mit Überschreiben.

48
d00nut 7 Okt. 2012 im 04:11

5 Antworten

Beste Antwort

Aus C ++ - Sicht kann es hier zwei Missverständnisse geben.

Erstens überlädt eine Methode mit demselben Namen und unterschiedlicher Signatur sie nicht wie in C ++. Wenn eines Ihrer Hintergrundobjekte versucht, buildField ohne Argumente aufzurufen, wird die Originalversion von Field nicht aufgerufen - sie wurde vollständig ausgeblendet.

Das zweite Problem ist, dass die Unterklassenversion aufgerufen wird, wenn eine in der Oberklasse definierte Methode buildField aufruft. In Python werden alle Methoden wie eine C ++ virtual -Methode dynamisch gebunden.

Von Field __init__ wird erwartet, dass es sich um ein Objekt handelt, für das eine buildField-Methode keine Argumente akzeptiert. Sie haben die Methode mit einem Objekt verwendet, für das eine buildField-Methode ein Argument enthält.

Die Sache mit super ist, dass es den Typ des Objekts nicht ändert, daher sollten Sie die Signatur von Methoden, die die Methoden der Oberklasse aufrufen könnten, nicht ändern.

25
Matt 22 Aug. 2019 im 15:02

Ich habe erwartet, dass Background init () aufgerufen wird

Tatsächlich wird Background init() aufgerufen.

Aber werfen Sie einen Blick auf Ihre Hintergrundklasse.

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

Die erste Anweisung von __init__ ruft also die super class(Field) init-Methode auf und übergibt die self als Argument. Nun ist diese self tatsächlich eine Referenz von {{X4 }} ..

Jetzt in Ihrer Feldklasse: -

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()

Ihre buildField() -Methode ruft tatsächlich die in der Hintergrundklasse auf. Dies liegt daran, dass die self hier eine Instanz der Background -Klasse ist (Versuchen Sie, self.__class__ in Ihrer {{ X4}} Methode von Field class) .. Wie Sie es beim Aufrufen der __init__ Methode aus der Background Klasse übergeben haben ..

Deshalb wird ein Fehler angezeigt.

Der Fehler "TypeError: buildField () akzeptiert genau 2 Argumente (1 angegeben).

Da Sie keinen Wert übergeben, ist nur der implizite Wert self übergeben.

18
Rohit Jain 7 Okt. 2012 im 00:24

Ich habe erwartet, dass Background init () aufgerufen wird. Um "a, b" an Fields init () zu übergeben, Field, um a und b zuzuweisen

So weit, ist es gut.

dann, um dem Feld eine Liste mit drei Nullen zuzuweisen.

Ah. Hier bekommen wir den Fehler.

    self.field = self.buildField()

Obwohl diese Zeile in Field.__init__ vorkommt, ist self eine Instanz von Background. self.buildField findet also die buildField Methode von Background, nicht die von Field.

Da Background.buildField 2 Argumente anstelle von 1 erwartet,

self.field = self.buildField()

Löst einen Fehler aus.


Wie können wir Python anweisen, die buildField -Methode von Field anstelle der von Background aufzurufen?

Der Zweck von Namensmangel (Benennung eines Attributs mit doppelten Unterstrichen) besteht darin, genau dieses Problem zu lösen.

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)

Der Methodenname __buildField wird innerhalb von Field auf _Field__buildField "entstellt", also innerhalb von Field.__init__.

    self.field = self.__buildField()

Ruft self._Field__buildField() auf, die Field Methode von Field. Während ähnlich,

    self.field = self.__buildField(c)

Innerhalb von Background.__init__ wird die __buildField -Methode von Background aufgerufen.

45
unutbu 15 Feb. 2015 im 00:38

Das super(Background, self).__init__( a, b ) ruft Folgendes auf:

def __init__( self, a, b ):
    self.a = a
    self.b = b
    self.field = self.buildField()

In Field. self bezieht sich hier jedoch auf die background Instanz, und self.buildField() ruft tatsächlich buildField() von Background auf, weshalb Sie diesen Fehler erhalten.


Es scheint, dass Ihr Code besser geschrieben werden sollte als:

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 )

Wenn Sie nicht zulassen können, dass der Basiskonstruktor fertig ist, signalisiert dies, dass das Design fehlerhaft ist.

Es ist daher viel besser, buildField() zu trennen, um zur Klasse zu gehören, indem Sie {{ X1}} Dekorateur oder staticmethod, wenn Sie müssen diese Methoden in Ihrem Konstruktor aufrufen.

Wenn Ihr Basisklassenkonstruktor jedoch keine Instanzmethode von innen aufruft, können Sie jede Methode dieser Basisklasse sicher überschreiben.

3
K Z 7 Okt. 2012 im 00:54

Es wird über Overriding gesprochen, aber es klingt für mich wie chaining constructors or (methods)

Und es klingt auch nach Überschreiben Eigenschaften:

Lassen Sie mich erklären:

  • Eine Eigenschaft mit dem Namen Feld wird als [0,0,0] initialisiert. @property Dekorateure sehen besser aus.

  • Dann überschreibt Background Klasse diese Eigenschaft . .


Schnelle und schmutzige Lösung

Ich kenne Ihre Geschäftslogik nicht, aber manchmal gab mir die Umgehung der __init__ -Methode der Superklasse mehr Kontrolle:

#!/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]

Eigenschaften verwenden

Hat eine sauberere Syntax.

#!/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
1
guneysus 27 März 2016 im 12:58