web-dev-qa-db-fra.com

Héritage de classe Python: AttributeError: l'objet '[SubClass]' n'a pas d'attribut 'xxx'

J'ai la classe de base et la sous-classe suivantes:

class Event(object):
    def __init__(self, sr1=None, foobar=None):
        self.sr1 = sr1
        self.foobar = foobar
        self.state = STATE_NON_EVENT

# Event class wrappers to provide syntatic sugar
class TypeTwoEvent(Event):
    def __init__(self, level=None):
        self.sr1 = level
        self.state = STATE_EVENT_TWO

Plus loin dans mon code, j'inspecte une instance d'une classe TypeTwoEvent, en recherchant un champ que je connais qui existe dans la classe de base - je m'attendais à ce que sa valeur par défaut soit Aucune. Cependant, mon code soulève l'exception suivante:

AttributeError: l'objet 'TypeTwoEvent' n'a pas d'attribut 'foobar'

J'avais l'impression que les champs de la classe de base seraient hérités par la sous-classe et que la création d'une instance d'une sous-classe instancierait la classe de base (et invoquerait donc son constructeur) ...

Qu'est-ce que j'oublie ici?. Pourquoi TypeTwoEvent ne possède-t-il pas d'attribut foobar - lorsque la classe de base à partir de laquelle elle est dérivée a un attribut foobar?

18

Votre sous-classe devrait être:

class TypeTwoEvent(Event):

    def __init__(self, level=None, *args, **kwargs):
        super(TypeTwoEvent, self).__init__(*args, **kwargs)
        self.sr1 = level
        self.state = STATE_EVENT_TWO

Comme vous substituez la méthode __init__, vous devez donc appeler la méthode parent si vous souhaitez que le comportement parent se produise.

Rappelez-vous, __init__ n'est pas une méthode spéciale, malgré son nom étrange. C'est juste la méthode appelée automatiquement après la création de l'objet. Sinon, c'est une méthode ordinaire, et les règles d'héritage ordinaires s'appliquent.

super(ClassName, self).__init__(arguments, that, goes, to, parents)

est la syntaxe pour appeler la version parente de la méthode.

Pour *args et **kwargs, cela garantit simplement que nous capturons tous les arguments supplémentaires passés à __init__ et les transmettons à la méthode parent, car la signature de votre méthode enfant ne l’a pas fait et que le parent a besoin de ces arguments pour fonctionner.

19
e-satis

Vous remplacez le constructeur (__init__) de la classe parente. Pour l'étendre, vous devez appeler explicitement le constructeur du parent avec un appel super() .

class TypeTwoEvent(Event):
    def __init__(self, level=None, **kwargs):
        # the super call to set the attributes in the parent class
        super(TypeTwoEvent, self).__init__(**kwargs)
        # now, extend other attributes
        self.sr1 = level
        self.state = STATE_EVENT_TWO

Notez que l'appel super est not toujours en haut de la méthode __init__ dans votre sous-classe. Son emplacement dépend de votre situation et de votre logique.

3
Praveen Gollakota

Lorsque l'instance est créée, sa méthode __init__ est appelée. Dans ce cas, il s'agit de TypeTwoEvent.__init__. Les méthodes de superclasse ne seront pas appelées automatiquement car cela créerait une grande confusion.

Vous devriez appeler Event.__init__(self, ...) à partir de TypeTwoEvent.__init__ (ou utiliser super, mais si vous ne le connaissez pas, lisez-le d'abord pour que vous sachiez ce que vous faites).

2
Chris Morgan

Vous devez appeler la méthode __init__ de la classe de base à partir de la méthode __init__ de la classe héritée.

Voir ici pour savoir comment faire cela.

1
alan

Cela me pose également problème, mais j’ai mis super().__init__() en bas de ma classe dérivée et c’est pourquoi cela ne fonctionne pas. Parce que j'essaie d'utiliser des attributs qui ne sont pas initialisés.

0
М.Б.