Наследование класса Python: AttributeError: объект '[SubClass]' не имеет атрибута 'xxx'

У меня есть следующий базовый класс и подкласс:

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

Далее в моем коде я проверяю экземпляр класса TypeTwoEvent, проверяю, существует ли поле, которое, как я знаю, существует в базовом классе - я ожидал, что его значение по умолчанию будет равным None. Тем не менее, мой код вызывает следующее исключение:

AttributeError: у объекта "TypeTwoEvent" нет атрибута "foobar"

У меня сложилось впечатление, что поля базового класса будут унаследованы подклассом и что создание экземпляра подкласса будет создавать экземпляр базового класса (и, таким образом, вызывать его конструктор)...

Что мне здесь не хватает? Почему TypeTwoEvent не имеет атрибута foobar - когда базовый класс, из которого он получен, имеет атрибут foobar?

Ответы

Ответ 1

Ваш подкласс должен быть:

class TypeTwoEvent(Event):

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

Поскольку вы переопределяете метод __init__, вам нужно вызвать родительский метод, если вы хотите, чтобы родительское поведение имело место.

Помните, __init__ не является специальным методом, несмотря на его странное имя. Это просто метод, который автоматически вызывается после создания объекта. В противном случае это обычный метод, и применяются обычные правила наследования.

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

это синтаксис для вызова родительской версии метода.

Для *args и **kwargs он просто гарантирует, что мы перехватим все дополнительные аргументы, переданные в __init__ и передадим его родительскому методу, так как подпись вашего дочернего метода не сделала этого, и родительский нужен для работы этих аргументов.

Ответ 2

Вы переопределяете конструктор (__init__) родительского класса. Чтобы расширить его, вам нужно явно вызвать конструктор родителя с вызовом 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

Обратите внимание, что вызов super не всегда находится в верхней части метода __init__ в вашем подклассе. Его местоположение зависит от вашей ситуации и логики.

Ответ 3

Когда экземпляр создан, вызывается его метод __init__. В этом случае это TypeTwoEvent.__init__. Методы суперкласса не будут вызываться автоматически, потому что это будет очень запутанным.

Вы должны вызвать Event.__init__(self, ...) из TypeTwoEvent.__init__ (или использовать super, но если вы не знакомы с ним, сначала прочитайте его, чтобы вы знали, что делаете).

Ответ 4

Вам нужно вызвать метод __init__ базового класса из метода __init__ унаследованного класса.

Смотрите здесь, как это сделать.

Ответ 5

У меня также была проблема с этим, но я поместил super().__init__() в нижней части моего производного класса и почему он не работает. Потому что я пытаюсь использовать атрибуты, которые не инициализированы.