Переопределить атрибут родительского класса перед вызовом super() в Python 3

#python #class #python-3.x #inheritance #attributes

#python #класс #python-3.x #наследование #атрибуты

Вопрос:

Рассмотрим следующий пример. У Parent класса есть атрибут, который называется attr . Оба Child1 и Child2 имеют атрибут с одинаковым именем. Единственное различие между Child1 и Child2 заключается в том, что Child1 вызовы super() выполняются перед переопределением attr атрибута родителя. Child2 вместо этого, похоже, не переопределяет родительский, attr потому что атрибут определен перед super() вызовом. Есть ли способ Child2 переопределить атрибут родительского класса attr при определении его перед super() вызовом?

 class Parent():

    def __init__(self):
        self.attr = "parent"

class Child1(Parent):

    def __init__(self):
        super().__init__()
        self.attr = "child1"

class Child2(Parent):

    def __init__(self):
        self.attr = "child2"
        super().__init__()

if __name__ == '__main__':
    child1 = Child1()
    print(child1.attr) # "child1"

    child2 = Child2()
    print(child2.attr) # "parent"
  

Ответ №1:

Не существует таких понятий, как «родительский attr « и «дочерний attr «. В этом экземпляре есть только один attr , независимо от того, был ли он установлен из кода в родительском классе, или из кода в дочернем классе, или из кода без класса.

Другими словами, эти примеры дают одинаковые результаты:

Пример 1

 class A:
    def __init__(self):
        self.attr = 1

class B(A):
    pass

b = B()
  

Пример 2

 class A:
    pass

class B(A):
    def __init__(self):
        self.attr = 1

b = B()
  

Пример 3

 class A:
    pass

class B(A):
    pass

b = B()
b.attr = 1
  

Фактический ответ 😉

Итак, разница между Child1 и Child2 заключается в том, что Child1 ‘s __init__ выполняет это:

     self.attr = "parent"
    self.attr = "child1"
  

И Child2 ‘s __init__ эффективно делает это:

     self.attr = "child2"
    self.attr = "parent"
  

Ответ №2:

Нет. super().__init__() это сокращение для вызова суперкласса __init__ , передавая ему текущий экземпляр как self .

Рассмотрим, что происходит при создании экземпляра Child2 . Сначала создается «голый» экземпляр класса. Этот экземпляр должен быть инициализирован. Для этого Python передает этот экземпляр в Child2.__init__ as self . Теперь Child2.__init__ сначала добавляется attr атрибут к этому экземпляру. Но затем он вызывает, super().__init__() который, в данном случае, является сокращением для Parent.__init__(self) . Важно, self является ли экземпляр Child2 экземпляром, который мы инициализируем. Таким образом, Parent.__init__ затем перезаписывается attr атрибут этого экземпляра.

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

Пример, демонстрирующий работу программы:

 class Parent(object):

    def __init__(self):
        print('Here I am, in the parent! My ID is: '   str(id(self)))

class Child(Parent):

    def __init__(self):
        print('Initializing new Child instance with ID: '   str(id(self)))
        super().__init__()
  

Таким образом, что при создании экземпляра Child выводится:

 Initializing new Child instance with ID: 4372063120
Here I am, in the parent! My ID is: 4372063120