J'ai deux classes, Field
et Background
. Ils ressemblent un peu à ceci:
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 )
Cette erreur pointe vers buildField()
de Field:
"TypeError: buildField() takes exactly 2 arguments (1 given)."
Je m'attendais à ce que Background init () soit appelé en premier. Pour passer "a, b" aux champs init (), champ pour assigner a et b puis pour assigner une liste avec trois 0 dedans au champ. Ensuite, pour que Background's init () continue, pour ensuite appeler son propre buildField () et remplacer self.field par une liste contenant c.
Il semble que je ne comprenne pas parfaitement super (), mais je n'ai pas pu trouver de solution à mon problème après avoir examiné des problèmes d'héritage similaires sur le Web et ici.
Je m'attendais à un comportement comme c ++ où une classe peut remplacer une méthode héritée. Comment puis-je y parvenir ou quelque chose de similaire.
La plupart des problèmes que j'ai rencontrés à ce sujet étaient des personnes utilisant des doubles soulignements. Mon expérience avec l'héritage avec super utilise la classe héritée init () pour simplement passer différentes variables à la super classe. Rien impliquant d'écraser quoi que ce soit.
Du point de vue C++, il pourrait y avoir deux idées fausses ici.
Tout d'abord, une méthode avec le même nom et une signature différente ne la surcharge pas comme en C++. Si l'un de vos objets Background essaie d'appeler buildField sans argument, la version d'origine de Field ne sera pas appelée - elle a été complètement masquée.
Le deuxième problème est que si une méthode définie dans la superclasse appelle buildField, la version de la sous-classe sera appelée. En python, les méthodes toutes sont liées dynamiquement, comme une méthode C++ virtual
.
Des champs __init__
s'attendait à avoir affaire à un objet qui avait une méthode buildField ne prenant aucun argument. Vous avez utilisé la méthode avec un objet qui a une méthode buildField prenant un argument.
Le problème avec super
est qu'il ne change pas le type de l'objet, vous ne devez donc pas modifier la signature des méthodes que les méthodes de la superclasse pourraient appeler.
Je m'attendais à ce que Background init () soit appelé. Pour passer "a, b" aux champs init (), Champ pour affecter a et b
Jusqu'ici tout va bien.
puis pour affecter une liste avec trois 0 dans le champ.
Ah. C'est là que nous obtenons l'erreur.
self.field = self.buildField()
Même si cette ligne apparaît dans Field.__init__
, self
est une instance de Background
. donc self.buildField
trouve la méthode Background
buildField
, pas Field
.
Puisque Background.buildField
Attend 2 arguments au lieu de 1,
self.field = self.buildField()
déclenche une erreur.
Alors, comment pouvons-nous dire à Python d'appeler la méthode Field
de buildField
au lieu de Background
?
Le but de name mangling (nommer un attribut avec des soulignements doubles) est de résoudre ce problème exact.
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)
Le nom de la méthode __buildField
Est "modifié" en _Field__buildField
Dans Field
donc dans Field.__init__
,
self.field = self.__buildField()
appelle self._Field__buildField()
, qui est la méthode __buildField
de Field
. De même,
self.field = self.__buildField(c)
dans Background.__init__
appelle la méthode __buildField
de Background
.
Je m'attendais à ce que Background init () soit appelé
En fait, Background init()
est appelée ..
Mais jetez un oeil à votre classe d'arrière-plan ..
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.field = self.buildField( c )
Ainsi, la première instruction de __init__
Invoque la méthode super class(Field)
init .. et passe le self
comme argument .. Maintenant, ce self
est en fait un référence de Background class
..
Maintenant dans votre classe Field: -
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()
Votre méthode buildField()
appelle en fait celle de la classe Background. En effet, la self
est ici une instance de la classe Background
(Essayez d'imprimer self.__class__
dans votre méthode __init__
de Field class
) .. Comme vous l'avez passée en invoquant la méthode __init__
, à partir de la classe Background
..
C'est pourquoi vous obtenez une erreur ..
L'erreur "TypeError: buildField () prend exactement 2 arguments (1 donné).
Comme vous ne transmettez aucune valeur. Ainsi, seule la valeur transmise est le self
implicite.
La super(Background, self).__init__( a, b )
invoquera:
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
dans Field
. Cependant, self
fait ici référence à l'instance background
, et self.buildField()
appelle en fait buildField()
de Background
, c'est pourquoi vous obtenez cette erreur.
Il semble que votre code devrait être mieux écrit comme:
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 vous ne pouvez pas permettre au constructeur de base de terminer, cela signale que la conception est défectueuse.
Il est donc préférable de séparer buildField()
pour appartenir à la classe en utilisant classmethod
décorateur ou staticmethod
, si vous devez appeler ces méthodes dans votre constructeur.
Cependant, si votre constructeur de classe de base n'invoque aucune méthode d'instance de l'intérieur, vous pouvez alors remplacer en toute sécurité n'importe quelle méthode de cette classe de base.
Overriding
dont on parle mais ça me semble chaining constructors or (methods)
Et cela ressemble aussi à écraser propriétés:
Laissez-moi expliquer:
Une propriété nommée champ sera initialisée comme [0,0,0]
. @property
les décorateurs sont plus en forme.
Ensuite, Background
class écraser cette propriété.
Je ne connais pas votre logique métier mais parfois en contournant le __init__
la méthode m'a donné plus de contrôle:
#!/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]
A une syntaxe plus propre.
#!/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