una programacion poo polimorfismo otra orientada objetos metodos metodo llamar instanciar herencia como clases clase python inheritance methods super overwrite

programacion - Python, anulando un método de clase heredado



poo python 3 (5)

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 a buildField() Field:

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

Esperaba que init init () se llamara primero. Para pasar "a, b" a Fields init (), Field para asignar a y b para asignar una lista con tres 0 en el campo. Luego, para que init () continúe, llame a su propia buildField () y anule self.field con una lista que contenga c.

Parece que no entiendo totalmente 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 ++ en el que una clase puede anular un método heredado. ¿Cómo puedo lograr esto o algo similar?

La mayoría de los problemas que encontré relacionados con esto fueron personas que usan doble guión bajo. Mi experiencia con la herencia con super está usando la clase heredada init () para simplemente pasar diferentes variables a la superclase. Nada que implique sobrescribir nada.


Esperaba que se llamara Background init ()

En realidad, se está llamando a Background init() .

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 )

Entonces, la primera afirmación de __init__ es invocando el método de inicio de __init__ super class(Field) ... y pasando al self como argumento. Ahora este self es en realidad una referencia de la Background class de Background class ...

Ahora en tu clase de campo: -

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

Tu método buildField() está invocando el de la clase Background. Esto es porque, el self aquí es una instancia de la clase Background (prueba a imprimir self.__class__ en tu método __init__ de la Field class ). Al pasarlo mientras invocaste el método __init__ , de la clase de Background ...

Es por eso que estás recibiendo un error ...

El error "TypeError: buildField () toma exactamente 2 argumentos (1 dado).

Como no está pasando ningún valor ... Entonces, solo el valor pasado es el self implícito.


Esperaba que se llamara Background init (). Para pasar "a, b" a Campos init (), Campo para asignar a y b

Hasta aquí todo bien.

luego asignar una lista con tres 0 en el campo.

Ah. Aquí es donde obtenemos el error.

self.field = self.buildField()

Aunque 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.

Dado que Background.buildField espera 2 argumentos en lugar de 1,

self.field = self.buildField()

provoca un error

Entonces, ¿cómo le decimos a Python que llame Field método buildField Field lugar de Background ?

El propósito del mangle de nombres (nombrar un atributo con dobles 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 está "destrozado" en _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 . Mientras que de manera similar,

self.field = self.__buildField(c)

dentro de Background.__init__ se __buildField método __buildField .


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() está de hecho llamando a buildField() de Background , por lo que obtienes ese error.

Parece ser que su código debería escribirse mejor 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 finalice, indica que el diseño es defectuoso.

Por lo tanto, es mucho mejor separar buildField() para pertenecer a la clase utilizando classmethod decorator 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 adentro, puede sobrescribir con seguridad cualquier método de esta clase base.


Se habla de Overriding pero me parece que chaining constructors or (methods)

Y también suena como sobreescribir las propiedades:

Dejame explicar:

  • Una propiedad llamada campo se inicializará como [0,0,0] . @property decoradores @property ven mejor.

  • Luego, la clase de Background sobrescribe esta propiedad.

Solución rápida y sucia

No conozco la lógica de su negocio, pero a veces __init__ método __init__ la __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 una sintaxis más 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


Viniendo desde una perspectiva de C ++, puede haber dos conceptos erróneos aquí.

En primer lugar, anular un método con una firma diferente no lo sobrecarga como en C ++. Si uno de tus objetos de fondo intenta llamar a buildField sin argumentos, no se invocará la versión original de Field, 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 vinculados dinámicamente, como un método virtual C ++.

La __init__ de __init__ esperaba estar 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 ocurre con el super es que no cambia el tipo de objeto, por lo que no debe cambiar la firma de ningún método que puedan llamar los métodos de la superclase.