Python, Anulando un método de clase heredado

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.


Solución rápida y sucia

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] 

Usando propiedades

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