Python, nadpisanie dziedziczonej metody klasowej

Mam dwie klasy, Field i Background. Wyglądają trochę tak:

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 )

Ten błąd wskazuje na pole buildField():

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

Oczekiwałem, że Background init () zostanie wywołany jako pierwszy. Aby przekazać "a, b" do pól init(), pole przypisać a i b, a następnie przypisać listę z trzema 0 do pola. Następnie, aby init tła był kontynuowany, aby następnie wywołać własną buildField() i nadpisać self.pole z listą zawierającą c.

It wygląda na to, że nie do końca rozumiem super (), jednak nie byłem w stanie znaleźć rozwiązania mojego problemu po przyjrzeniu się podobnym problemom dziedziczenia w Internecie i wokół tutaj.

Spodziewałem się zachowania takiego jak c++, gdzie klasa może nadpisać metodę, która została odziedziczona. Jak mogę to osiągnąć lub coś podobnego.

Większość problemów, które znalazłem związane z tym były osoby używające podwójnego podkreślenia. Moje doświadczenie z dziedziczeniem z super polega na używaniu odziedziczonej klasy init (), aby po prostu przejść różne zmienne do super klasy. Nic, co wiązałoby się z nadpisywaniem czegokolwiek.

Author: Pianosaurus, 2012-10-07

5 answers

Patrząc z perspektywy C++, mogą tu być dwa błędne przekonania.

Po pierwsze, nadpisanie metody z innym podpisem nie przeciąża jej jak w C++. Jeśli jeden z Twoich obiektów tła spróbuje wywołać buildField bez żadnych argumentów, Oryginalna wersja z pola nie zostanie wywołana -- została całkowicie ukryta.

Druga sprawa polega na tym, że jeśli metoda zdefiniowana w superklasie wywoła buildField, to zostanie wywołana wersja podklasy. W Pythonie, wszystkie metody są związane dynamicznie, podobnie jak metoda C++ virtual.

Field ' s __init__ ma do czynienia z obiektem, którego metoda buildField nie przyjmuje żadnych argumentów. Użyłeś metody z obiektem, który posiada metodę buildField przyjmującą jeden argument.

Rzecz z super jest taka, że nie zmienia typu obiektu, więc nie powinieneś zmieniać podpisu żadnych metod, które metody klasy nadrzędnej mogą wywoływać.

 17
Author: Ian Clelland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-07 00:37:49

Spodziewałem się wywołania Background INIT (). Aby przekazać "a, b" do pól init (), pole do przypisania a i B

Jak na razie dobrze.

Następnie przypisać listę z trzema 0 ' s w polu.

Ah. Tutaj pojawia się błąd.
    self.field = self.buildField()

Mimo że linia ta występuje w Field.__init__, self jest instancją Background. więc self.buildField znajduje Background metodę buildField, a nie Field.

Ponieważ Background.buildField oczekuje 2 argumentów zamiast 1,

self.field = self.buildField()

Podnosi błąd.


Więc jak powiemy Pythonowi, aby wywołał metodę Field's buildField zamiast Background's?

Celem Wymaganie nazwy (nazwanie atrybutu z podwójnymi podkreślnikami) jest rozwiązanie tego problemu.

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)

Nazwa metody __buildField jest "zniekształcona" do _Field__buildField wewnątrz Field więc wewnątrz Field.__init__,

    self.field = self.__buildField()

Wywołania self._Field__buildField(), czyli Field ' S __buildField metoda. Podczas gdy podobnie,

    self.field = self.__buildField(c)

Inside Background.__init__ calls Background ' s __buildField metoda.

 37
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-15 00:38:15

Oczekiwałem, że wywołanie Background INIT () zostanie wywołane

Właściwie Background init() jest wywoływany..

Ale spójrz na swoją klasę..
class Background( Field ):
    def __init__( self, a, b, c ):
        super(Background, self).__init__( a, b )
        self.field = self.buildField( c )

Zatem pierwsze twierdzenie {[3] } jest wywołaniem metody init super class(Field).. i podanie self jako argumentu.. Teraz to {[5] } jest w rzeczywistości odniesieniem Background class..

Teraz na zajęciach terenowych: -

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

Twoja metoda buildField() faktycznie wywołuje tę W Tle klasy.. Jest to spowodowane tym, że self jest instancją klasy Background (spróbuj wydrukować self.__class__ w swojej metodzie __init__ z Field class).. Gdy minąłeś ją podczas wywoływania metody __init__, z klasy Background..

Dlatego dostajesz błąd..

Błąd " TypeError: buildField () pobiera dokładnie 2 argumenty (1 given).

Ponieważ nie przekazujesz żadnej wartości.. Tak więc jedyną przekazaną wartością jest implicit self.

 15
Author: Rohit Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-07 00:24:45

super(Background, self).__init__( a, b ) wywoła:

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

W Field. Jednakże self odnosi się tutaj do instancji background, a self.buildField() w rzeczywistości wywołuje buildField() z Background, dlatego pojawia się ten błąd.


Wydaje się, że Twój kod powinien być lepiej napisany jako:

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 )

Jeśli nie możesz pozwolić konstruktorowi bazowemu na ukończenie, oznacza to, że projekt jest wadliwy.

Dlatego o wiele lepiej jest oddzielić buildField() należeć do klasy używając classmethod dekorator lub staticmethod, jeśli musisz wywołać te metody w swoim konstruktorze.

Jednakże, jeśli konstruktor klasy bazowej nie wywoła żadnej metody instancji z wewnątrz, możesz bezpiecznie zastąpić dowolną metodę tej klasy bazowej.

 2
Author: K Z,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-07 00:54:40

Overriding mówi się o tym, ale to brzmi jak dla mnie chaining constructors or (methods)

A także brzmi jak nadpisanie właściwości:

Pozwól mi wyjaśnić:

  • Właściwość o nazwie pole {[12] } zostanie zainicjalizowana jako [0,0,0]. @property dekoratorzy wyglądają lepiej.

  • Następnie Background klasa nadpisuje tę właściwość.


Szybkie i brudne rozwiązanie

Nie znam Twojej logiki biznesowej, ale czasami mijając super metoda klasy __init__ dała mi większą kontrolę:

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

Używanie właściwości

Ma bardziej czystą składnię.

#!/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
Author: guneysus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-27 12:58:46