Python, переопределение метода унаследованного класса

у меня есть два класса, 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)."

Я ожидал фоне init () вызывается первым. Чтобы передать" a, b " полям init (), поле для присвоения a и b, а затем назначить список с тремя 0 в поле. Тогда для фона init () чтобы продолжить, затем вызвать собственное buildField () и переопределить личность.поле со списком, содержащим гр.

кажется, я не полностью понимаю super (), однако я не смог найти решение моей проблемы после просмотра аналогичных проблем наследования в интернете и здесь.

Я ожидал поведения, как c++ , где класс может переопределить метод, который был унаследован. Как я могу достичь этого или чего-то подобного.

большинство проблем, которые я нашел, связанных с этим, были люди, использующие двойные подчеркивания. Мой опыт с наследования с super использует унаследованный класс init () просто передать различные переменные в супер класс. Ничего, связанного с переписыванием.

5 ответов


исходя из перспективы C++, здесь могут быть два неправильных представления.

во-первых, переопределение метода с другой сигнатурой не перегружает его, как в C++. Если один из ваших фоновых объектов пытается вызвать buildField без аргументов, исходная версия из поля не будет вызвана-она была полностью скрыта.

вторая проблема заключается в том, что если метод, определенный в суперклассе, вызывает buildField, будет вызвана версия подкласса. в Python, все методы связаны динамически, как C++ virtual метод.

поля __init__ ожидается, что будет иметь дело с объектом, у которого метод buildField не принимает аргументов. Вы использовали метод с объектом, который имеет метод buildField, принимающий один аргумент.

С super заключается в том, что он не изменяет тип объекта, поэтому вы не должны изменять подпись любых методов, которые могут вызывать методы суперкласса.


я ожидал, что будет вызван Background init (). Чтобы передать" a, b " полям init (), поле для присвоения a и b

пока все хорошо.

затем назначить список с тремя 0-х в его поле.

Ах. Здесь мы получаем ошибку.

    self.field = self.buildField()

хотя эта строка происходит внутри Field.__init__, self пример Background. так что self.buildField находит Background ' s buildField метод, не Fields.

С Background.buildField ожидает 2 аргумента вместо 1,

self.field = self.buildField()

выдает ошибку.


Итак, как мы скажем Python позвонить Field ' s buildField вместо 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 "искалечен" до _Field__buildField внутри Field так внутри Field.__init__,

    self.field = self.__buildField()

звонки self._Field__buildField(), которая составляет Field ' s __buildField метод. Как и то,

    self.field = self.__buildField(c)

внутри Background.__init__ звонки Background ' s __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 в качестве аргумента.. Теперь это!--5--> на самом деле ссылка Background 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() способ вызова в фоновый класс.. Это потому, что self вот экземпляр Background класс(для печати self.__class__ в своем __init__ метод Field class).. Как вы прошли его при вызове __init__ С Background класса..

вот почему вы получаете ошибку..

ошибка " 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() фактически вызывает buildField() of 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() чтобы принадлежать к классу, используя classmethod декоратор или staticmethod, если обязательно вызовите эти методы в своем конструкторе.

однако, если конструктор базового класса не вызывает метод экземпляра изнутри, вы можете безопасно перезаписать любой метод этого базового класса.


Overriding говорят, но это звучит как для меня chaining constructors or (methods)

а также это звучит как более-писать!--12--> свойства:

поясню:

  • свойство с именем поле будет инициализирован как [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