Должен ли __init __() вызывать родительский класс __init __()?

Я использую, что в Objective-C у меня есть эта конструкция:

- (void)init {
    if (self = [super init]) {
        // init class
    }
    return self;
}

Должен ли Python также вызвать реализацию родительского класса для __init__?

class NewClass(SomeOtherClass):
    def __init__(self):
        SomeOtherClass.__init__(self)
        # init class

Является ли это также true/false для __new__() и __del__()?

Изменить: Там очень похожий вопрос: Наследование и переопределение __init__ в Python

Ответ 1

В Python вызывать суперкласс ' __init__ необязательно. Если вы вызываете его, тогда также необязательно использовать super или явно указывать имя суперкласса:

object.__init__(self)

В случае объекта, вызов супер-метода не является строго обязательным, поскольку супер-метод пуст. То же самое для __del__.

С другой стороны, для __new__ вы действительно должны вызывать метод super и использовать его возвращение как вновь созданный объект - если вы явно не хотите возвращать что-то другое.

Ответ 2

Если вам нужно что-то из super __init__, которое будет сделано в дополнение к тому, что делается в текущем классе __init__,, вы должны называть его самостоятельно, так как это не произойдет автоматически. Но если вам не нужно ничего от супер __init__,, не нужно его вызывать. Пример:

>>> class C(object):
    def __init__(self):
        self.b = 1


>>> class D(C):
    def __init__(self):
        super().__init__() # in Python 2 use super(D, self).__init__()
        self.a = 1


>>> class E(C):
    def __init__(self):
        self.a = 1


>>> d = D()
>>> d.a
1
>>> d.b  # This works because of the call to super init
1
>>> e = E()
>>> e.a
1
>>> e.b  # This is going to fail since nothing in E initializes b...
Traceback (most recent call last):
  File "<pyshell#70>", line 1, in <module>
    e.b  # This is going to fail since nothing in E initializes b...
AttributeError: 'E' object has no attribute 'b'

__del__ - это то же самое (но будьте осторожны, полагаясь на __del__ для финализации - рассмотрите возможность делать это с помощью инструкции with).

Я редко использую __new__. Я выполняю всю инициализацию в __init__.

Ответ 3

На Anon отвечу:
"Если вам нужно, чтобы что-то из super __init__ было сделано в дополнение к тому, что делается в текущем классе __init__, вы должны вызвать это сами, так как это не произойдет автоматически"

Это невероятно: он формулирует прямо противоположное принципу наследования.


Дело не в том, что "что-то из super __init__ (...) не произойдет автоматически", а в том, что это произойдет автоматически, но этого не происходит, потому что базовый класс __init__ переопределяется определением производного класс __init__

Итак, ПОЧЕМУ определяется производный класс ' __init__, так как он переопределяет то, на что направлен, когда кто-то прибегает к наследованию??

Это потому, что нужно определить что-то, что НЕ сделано в базовом классе ' __init__, и единственная возможность получить это - поместить его выполнение в функцию производного класса' __init__.
Другими словами, нужно что-то в базовом классе ' __init__ в дополнение к тому, что было бы автоматически сделано в base-classe' __init__ если бы этот последний не был переопределен.
НЕ наоборот.


Тогда проблема заключается в том, что требуемые инструкции, присутствующие в базовом классе ' __init__, больше не активируются в момент создания экземпляра. Чтобы компенсировать эту инактивацию, требуется что-то особенное: явный вызов базового класса ' __init__, чтобы ПРОДОЛЖИТЬ, НЕ ДОБАВИТЬ инициализацию, выполняемую базовым классом' __init__. Это именно то, что сказано в официальном документе:

Переопределяющий метод в производном классе может фактически хотеть расширить, а не просто заменить метод базового класса с тем же именем. Существует простой способ прямого вызова метода базового класса: просто вызовите BaseClassName.methodname(self, arguments).
http://docs.python.org/tutorial/classes.html#inheritance

Вот и вся история

  • когда цель состоит в том, чтобы сохранить инициализацию, выполняемую базовым классом, то есть чистое наследование, ничего особенного не требуется, нужно просто избегать определения функции __init__ в производном классе

  • когда целью является ЗАМЕНА инициализации, выполняемой базовым классом, в производном классе должен быть определен __init__

  • когда целью является добавление процессов к инициализации, выполняемой базовым классом, должен быть определен производный класс __init__, включающий явный вызов базового класса __init__


На посту Анона меня удивляет не только то, что он выражает противоречие с теорией наследования, но и то, что мимо прошли 5 парней, которые проголосовали, не повредив волос, и, кроме того, никто не реагировал за 2 года тема, интересная тема которой должна читаться относительно часто.

Ответ 4

Изменить: (после изменения кода)
Мы не можем сказать вам, нужно ли вам или нет позвонить вашему родителю __init__ (или любой другой функции). Очевидно, что наследование будет работать без такого вызова. Все зависит от логики вашего кода: например, если все ваши __init__ выполняются в родительском классе, вы можете просто пропустить общий класс __init__.

рассмотрим следующий пример:

>>> class A:
    def __init__(self, val):
        self.a = val


>>> class B(A):
    pass

>>> class C(A):
    def __init__(self, val):
        A.__init__(self, val)
        self.a += val


>>> A(4).a
4
>>> B(5).a
5
>>> C(6).a
12

Ответ 5

Нет жесткого и быстрого правила. Документация для класса должна указывать, должны ли подклассы вызывать метод суперкласса. Иногда вы хотите полностью заменить поведение суперкласса и в других случаях увеличить его - то есть вызвать свой собственный код до и/или после вызова суперкласса.

Обновление: Эта же основная логика применяется к любому вызову метода. Конструкторам иногда требуется особое внимание (поскольку они часто устанавливают состояние, которое определяет поведение) и деструкторы, потому что они параллельные конструкторы (например, при распределении ресурсов, например, соединения с базой данных). Но то же самое можно применить, скажем, к методу render() виджета.

Дальнейшее обновление: Что такое OPP? Вы имеете в виду ООП? Нет. Подкласс часто должен знать что-то о дизайне суперкласса. Не внутренние детали реализации, а основной контракт, который имеет суперкласс с его клиентами (с использованием классов). Это никоим образом не нарушает принципы ООП. Вот почему protected является допустимым понятием в ООП вообще (хотя, конечно, не на Python).

Ответ 6

ИМО, вы должны это назвать. Если ваш суперкласс object, вы не должны этого делать, но в других случаях я считаю исключительным не называть его. Как уже было сказано другими, очень удобно, если ваш класс даже не должен переопределять __init__ сам, например, когда он не имеет (дополнительного) внутреннего состояния для инициализации.

Ответ 7

Да, вы всегда должны явно называть базовый класс __init__ хорошей практикой кодирования. Если вы забудете это сделать, это может вызвать тонкие проблемы или ошибки во время выполнения. Это верно, даже если __init__ не принимает никаких параметров. Это не похоже на другие языки, где компилятор будет неявно вызывать конструктор базового класса для вас. Python этого не делает!

Основная причина постоянного вызова базового класса _init__ заключается в том, что базовый класс обычно может создавать переменную-член и инициализировать их значениями по умолчанию. Поэтому, если вы не вызовете init базового класса, ни один из этого кода не будет выполнен, и вы получите базовый класс, который не имеет переменных-членов.

Пример:

class Base:
  def __init__(self):
    print('base init')

class Derived1(Base):
  def __init__(self):
    print('derived1 init')

class Derived2(Base):
  def __init__(self):
    super(Derived2, self).__init__()
    print('derived2 init')

print('Creating Derived1...')
d1 = Derived1()
print('Creating Derived2...')
d2 = Derived2()

Это печатает..

print('Creating Derived1...')
d1 = Derived1()
print('Creating Derived2...')
d2 = Derived2()

Запустите этот код.