Tengo dos clases, Field
y Background
. Se ven un poco así:
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 )
Este error está apuntando al campo de buildField()
:
"TypeError: buildField() takes exactly 2 arguments (1 given)."
Esperaba que primero se llamara a fondo init (). Para pasar “a, b” a los campos init (), Field para asignar a y b, luego para asignar una lista con tres 0 en el campo. Luego, para que inicie Background () continúe, luego llame a su propio buildField () y anule self.field con una lista que contenga c.
Parece que no entiendo completamente a super (), sin embargo, no pude encontrar una solución a mi problema después de ver problemas de herencia similares en la web y por aquí.
Esperaba un comportamiento como c ++ donde una clase puede anular un método que fue heredado. ¿Cómo puedo lograr esto o algo similar.
La mayoría de los problemas que encontré relacionados con esto eran personas que usaban guiones bajos. Mi experiencia con la herencia con super está utilizando la clase heredada init () para pasar diferentes variables a la súper clase. Nada que implique sobrescribir nada.
Desde una perspectiva de C ++, podría haber dos conceptos erróneos aquí.
Primero, reemplazar un método con una firma diferente no lo sobrecarga como en C ++. Si uno de sus objetos en segundo plano intenta llamar a buildField sin argumentos, no se llamará a la versión original de Field, ya que se ha ocultado por completo.
El segundo problema es que si un método definido en la superclase llama a buildField, se llamará a la versión de la subclase. En Python, todos los métodos están enlazados dinámicamente, como un método virtual
C ++.
__init__
esperaba que el __init__
de Field estuviera tratando con un objeto que tenía un método buildField sin argumentos. Usaste el método con un objeto que tiene un método buildField tomando un argumento.
Lo que pasa con super
es que no cambia el tipo de objeto, por lo que no debes cambiar la firma de ningún método que los métodos de superclase puedan llamar.
Esperaba que se llamara a fondo init (). Para pasar “a, b” a los campos init (), Field para asignar a y b
Hasta ahora tan bueno.
luego, para asignar una lista con tres 0 en el campo.
Ah. Aquí es donde obtenemos el error.
self.field = self.buildField()
A pesar de que esta línea ocurre dentro de Field.__init__
, self
es una instancia de Background
. así que self.buildField
encuentra el método buildField
, no Field
‘s.
Como Background.buildField
espera 2 argumentos en lugar de 1,
self.field = self.buildField()
plantea un error.
Entonces, ¿cómo le decimos a Python que llame Field
método buildField
Field
lugar de al de Background
?
El propósito de la manipulación de nombres (nombrar un atributo con guiones bajos) es resolver este problema exacto.
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)
El nombre del método __buildField
se ” __buildField
” para _Field__buildField
dentro del Field
así que dentro del Field.__init__
,
self.field = self.__buildField()
llama a self._Field__buildField()
, que es Field
método __buildField
Field
. Mientras que de manera similar,
self.field = self.__buildField(c)
dentro de Background.__init__
llama __buildField
método __buildField
.
Esperaba que se llamara a fondo init ()
En realidad, el Background init()
se está llamando.
Pero eche un vistazo a su clase de fondo …
class Background( Field ): def __init__( self, a, b, c ): super(Background, self).__init__( a, b ) self.field = self.buildField( c )
Por lo tanto, la primera statement de __init__
es invocar el método de inicio de super class(Field)
… y pasar el self
como argumento … Ahora este self
es en realidad una referencia de la Background class
.
Ahora en tu clase de campo: –
class Field( object ): def __init__( self, a, b ): print self.__class__ // Prints `` self.a = a self.b = b self.field = self.buildField()
Su método buildField()
está invocando el de la clase de fondo. Esto se debe a que el self
aquí es una instancia de la clase de Background
__init__
método __init__
, de la clase de Background
.
Es por eso que está recibiendo error ..
El error “TypeError: buildField () toma exactamente 2 argumentos (1 dado).
Como no estás pasando ningún valor … Entonces, solo el valor pasado es el self
implícito.
El super(Background, self).__init__( a, b )
invocará:
def __init__( self, a, b ): self.a = a self.b = b self.field = self.buildField()
en el Field
. Sin embargo, self
aquí se refiere a la instancia de background
, y self.buildField()
de hecho está llamando a buildField()
de Background
, por lo que recibe ese error.
Parece ser que tu código debería estar mejor escrito como:
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 )
Si no puede permitir que el constructor base termine, esto indica que el diseño es defectuoso.
Por lo tanto, es mucho mejor separar buildField()
para que pertenezca a la clase mediante el uso de decorador de classmethod
clase o staticmethod
, si tiene que llamar a estos métodos en su constructor.
Sin embargo, si el constructor de su clase base no invoca ningún método de instancia desde dentro, entonces puede sobrescribir con seguridad cualquier método de esta clase base.
Se habla de Overriding
pero a mí me parece que chaining constructors or (methods)
Y también suena como propiedades de sobre escritura :
Dejame explicar:
Una propiedad denominada campo se inicializará como [0,0,0]
. @property
decoradores de @property
ven mejor.
Luego, la clase de Background
sobrescribe esta propiedad.
No conozco su lógica de negocios, pero a veces, el hecho de pasar por __init__
método __init__
de __init__
me dio más control:
#!/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]
Tiene más syntax limpia.
#!/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