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
метод, не Field
s.
С 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