Вызвать конструктор родителя python

Обновлено: 28.03.2024

В объектно-ориентированном программировании конструктором класса называют метод, который автоматически вызывается при создании объектов. Его также можно назвать конструктором объектов класса. Имя такого метода обычно регламентируется синтаксисом конкретного языка программирования. Так в Java имя конструктора класса совпадает с именем самого класса. В Python же роль конструктора играет метод __init__().

В Python наличие пар знаков подчеркивания спереди и сзади в имени метода говорит о том, что он принадлежит к группе методов перегрузки операторов. Если подобные методы определены в классе, то объекты могут участвовать в таких операциях как сложение, вычитание, вызываться как функции и др.

При этом методы перегрузки операторов не надо вызывать по имени. Вызовом для них является сам факт участия объекта в определенной операции. В случае конструктора класса – это операция создания объекта. Так как объект создается в момент вызова класса по имени, то в этот момент вызывается метод __init__().

Необходимость конструкторов связана с тем, что нередко объекты должны иметь собственные свойства сразу. Пусть имеется класс Person, объекты которого обязательно должны иметь имя и фамилию. Если класс будет описан подобным образом

то создание объекта возможно без полей. Для установки имени и фамилии метод set_name() нужно вызывать отдельно:

В свою очередь, конструктор класса не позволит создать объект без обязательных полей:

Здесь при вызове класса в круглых скобках передаются значения, которые будут присвоены параметрам метода __init__(). Первый его параметр – self – ссылка на сам только что созданный объект.

Теперь, если мы попытаемся создать объект, не передав ничего в конструктор, то будет возбуждено исключение, и объект не будет создан:

Однако бывает, что надо допустить создание объекта, даже если никакие данные в конструктор не передаются. В таком случае параметрам конструктора класса задаются значения по умолчанию:

Если класс вызывается без значений в скобках, то для параметров будут использованы их значения по умолчанию. Однако поля width и height будут у всех объектов.

Кроме того, конструктору вовсе не обязательно принимать какие-либо параметры, не считая self. Значения полям могут назначаться как угодно. Также не обязательно, чтобы в конструкторе происходила установка атрибутов объекта. Там может быть, например, код, который порождает создание объектов других классов.

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

В Python создать несколько методов __init__() в классе можно, однако "рабочим" останется только последний. Он переопределит ранее определенные. Поэтому в Python в классах используется только один конструктор, а изменчивость количества передаваемых аргументов настраивается через назначение значений по-умолчанию.

Практическая работа. Конструктор и деструктор

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

В языке программирования Python объект уничтожается, когда исчезают все связанные с ним переменные или им присваивается другое значение, в результате чего связь со старым объектом теряется. Удалить переменную можно с помощью команды языка del.

В классах Python функцию деструктора выполняет метод __del__().

Напишите программу по следующему описанию:

Есть класс Person, конструктор которого принимает три параметра (не учитывая self) – имя, фамилию и квалификацию специалиста. Квалификация имеет значение заданное по умолчанию, равное единице.

У класса Person есть метод, который возвращает строку, включающую в себя всю информацию о сотруднике.

Класс Person содержит деструктор, который выводит на экран фразу "До свидания, мистер …" (вместо троеточия должны выводиться имя и фамилия объекта).

В основной ветке программы создайте три объекта класса Person. Посмотрите информацию о сотрудниках и увольте самое слабое звено.

В конце программы добавьте функцию input(), чтобы скрипт не завершился сам, пока не будет нажат Enter. Иначе вы сразу увидите как удаляются все объекты при завершении работы программы.

В Python деструктор используется редко, так как интерпретатор и без него хорошо убирает "мусор".

Курс с примерами решений практических работ:
android-приложение, pdf-версия

Надо вызвать метод базового класса из метода, который переопределен в производном классе.

Из конструктора дочернего класса нужно явно вызывать конструктор родительского класса.

Обращение к базовому классу происходит с помощью super()

Нужно явно вызывать конструктор базового класса

Видно, что без явного вызова конструктора класса А не вызывается A.__init__ и не создается поле x класса А.

Вызовем конструктор явно.

Конструктор базового класса стоит вызывать раньше, чем иницилизировать поля класса-наследника, потому что поля наследника могут зависеть (быть сделаны из) полей экземпляра базового класса.

super() или прямое обращение к классу?

Метод класса можно вызвать, используя синтаксис вызова через имя класса:

Все работает. Но при дальнейшем развитии классов могут начаться проблемы:

Видно, что конструктор Base.__init__ вызывается дважды. Иногда это недопустимо (считаем количество созданных экземпляров класса, увеличивая в конструкторе счетчик на 1; выдаем очередное auto id какому-то нашему объекту, например, номер пропуска или паспорта или номер заказа).

То же самое через super():

  • вызов конструктора Base.__init__ происходит только 1 раз.
  • вызваны конструкторы всех базовых классов.
  • порядок вызова конструкторов для классов А и В не определен.

Как это работает?

Для реализации наследования питон ищет вызванный атрибут начиная с первого класса до последнего. Этот список создается слиянием (merge sort) списков базовых классов:

  • дети проверяются раньше родителей.
  • если родителей несколько, то проверяем в том порядке, в котором они перечислены.
  • если подходят несколько классов, то выбираем первого родителя.

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

Не забываем вызывать метод суперкласса

А если где-то не вызван метод суперкласса?

Заметим, что хотя в B.__init__ есть вызов super(), то до вызова B.__init__ не доходит.

  • Вызываем у объекта класса С метод __init__.
  • Ищем его в mro и находим С.__init__. Выполняем его.
  • В этом методе вызов super() - ищем метод __init__ далее по списку от найденного.
  • Находим A.__init__. Выполняем его. В нем нет никаких super() - дальнейший поиск по mro прекращается.

Нет метода в своем базовом классе, есть у родителя моего сиблинга

Определим класс, который пытается вызвать метод, которого нет в базовом классе:

получим, как и ожидалось:

Определим метод spam в классе В. Класс С, наследник А и В, вызывает метод A.spam(), который вызывает B.spam - класс В не связан с классом А.

Для объекта класса С вызвали метод spam(). Ищем его в MRO. Находим A.spam() и вызываем. Далее для super() из A.spam() идем дальше от найденного по списку mro и находим B.spam().

Отметим, что при другом порядке описания родителей class C(B, A) , вызывается метод B.spam() у которого нет super():

Вызываем метод spam для объекта класса С. В С его нет, ищем дальше в В. Находим. Вызваем. Далее super() нет и дальнейший поиск не производится.

Чтобы не было таких сюрпризов при переопределении методов придерживайтесь правил:

  • все методы в иерархии с одинаковым именем имеют одинаковую сигнатуру вызова (количество аргументов и их имена для именованных аргументов).
  • реализуйте метод в самом базовом классе, чтобы цепочка вызовов закончилась хоть каким

Обращение к дедушке

Игнорируем родителя

Если у нас есть 3 одинаковых метода foo(self) в наследуемых классах А, В(А), C(B), и нужно из C.foo() вызвать сразу A.foo() минуя B.foo(), то наши классы неправильно сконструированы (почему нужно игнорировать В? может, нужно было наследовать С от А, а не от В?). Нужен рефакторинг.

Но можно всегда вызвать метод по имени класса:

Метод определен только у дедушки

Если в В такого метода нет, и из C.foo() нужно вызвать A.foo() (или в базовом классе выше по иерархии), вызываем super().foo() и больше не думаем, у какого пра-пра-пра-дедушки реализован этот метод.

Просто воспользуйтесь super() для поиска по mro.

super().super() не работает

Или мы ищем какого-то родителя в mro, или точно указываем из какого класса нужно вызвать метод.

In all other languages I've worked with the super constructor is invoked implicitly. How does one invoke it in Python? I would expect super(self) but this doesn't work.

you should emphasize that an answer that doesn't use the Derived Class name is what you want. e.g. (pseudocode): super().__init__(args. )

you should be accepting Aidan Gomez's answer. It would save us a lot of time, since it has an answer in both python 2 and 3.

@Mike I think there's still value in an answer that lists the Python 2 way, because there's a lot of old Python 2 code floating around out there, and some of the people who wind up at this question probably won't otherwise know how to make sense of it. (And despite it being EOL, many people do still write code in Python 2, either because they don't know better or because some organizational requirement has forced it on them.)

I have changed the accepted answer to @Aiden Gomez's answer. Though Ignacio was correct, @Aidan's was the most appropriate as of today given Python 3's changes to super()

7 Answers 7

You can now choose to sort by Trending, which boosts votes that have happened recently, helping to surface more up-to-date answers.

Trending is based off of the highest score sort and falls back to it if no posts are trending.

In line with the other answers, there are multiple ways to call super class methods (including the constructor), however in Python-3.x the process has been simplified:

Python-3.x

Python-2.x

In python 2.x, you have to call the slightly more verbose version super(, self) , which is equivalent to super() as per the docs.

super() returns a parent-like object in new-style classes:

just of curiosity why does super(B,self) require both B and self to be mentioned? isn't this redundant? shouldn't self contain a reference to B already?

With respect to the documentation of super() , you should be able to write super().__init__() wothout arguments.

With Python 2.x old-style classes it would be this:

@kdbanman: This will work with new-style classes, but one of the reasons to use new-style classes is to not have to do it this way. You can use super and not have to directly name the class you're inheriting from.

One way is to call A's constructor and pass self as an argument, like so:

The advantage of this style is that it's very clear. It call A's initialiser. The downside is that it doesn't handle diamond-shaped inheritance very well, since you may end up calling the shared base class's initialiser twice.

Another way is to use super(), as others have shown. For single-inheritance, it does basically the same thing as letting you call the parent's initialiser.

However, super() is quite a bit more complicated under-the-hood and can sometimes be counter-intuitive in multiple inheritance situations. On the plus side, super() can be used to handle diamond-shaped inheritance. If you want to know the nitty-gritty of what super() does, the best explanation I've found for how super() works is here (though I'm not necessarily endorsing that article's opinions).

при создании простой иерархии объектов в Python я хотел бы иметь возможность вызывать методы родительского класса из производного класса. В Perl и Java есть ключевое слово для этого ( super ). В Perl я мог бы сделать следующее:

в python кажется,что я должен явно назвать родительский класс из дочернего. В приведенном выше примере я должен был бы сделать что-то вроде Foo::frotz() .

Это не кажется правильным, так как такое поведение затрудняет создавать глубокие иерархии. Если детям нужно знать, какой класс определил унаследованный метод, то создается всевозможная информационная боль.

является ли это фактическим ограничением в python, пробелом в моем понимании или обоими?

да, но только с новый-стиль-классы. Используйте super() функция:

Python также имеет супер а также:

super(type[, object-or-type])

возвращает прокси-объект, который делегирует вызовы методов родителя или отпрыска класс тип. Это полезно для доступа к унаследованным методам, которые были переопределены в классе. Порядок поиска совпадает с порядком, используемым getattr (), за исключением того, что сам тип пропускается.

будет просто отлично, будет ли определен непосредственный родительский класс frotz сам или унаследовал его. super нужен только для правильной поддержки несколько наследование (и то он работает только если каждый класс использует его правильно). В общем, AnyClass.whatever собирается искать whatever на AnyClass 'предки, если AnyClass не определяет / переопределяет его, и это справедливо для "дочернего класса, вызывающего метод родителя", как и для любого другого случая!

в Python 3 есть другой и более простой синтаксис для вызова родительского метода. Если Foo класс наследует от Bar , затем из Bar.__init__ можно вызвать из Foo С помощью super().__init__() :

пример использования super ():

во многих ответах объясняется, как вызвать метод из родительского элемента, который был переопределен в дочернем элементе.

"Как вы вызвать метод родительского класса из дочернего класса?"

также может означать:

" Как вы называете унаследованные методы?"

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

например, в python 3:

да, это может быть довольно очевидно, но я чувствую, что, не указывая на это, люди могут оставить этот поток с впечатлением, что вам нужно прыгать через смешные обручи только для доступа к унаследованным методам в python. Тем более, что этот вопрос высоко ценится в поисках "как получить доступ к методу родительского класса в Python", а OP написан с точки зрения кого-то нового для python.

в Python тоже есть super (). Это немного шатко, из - за классов старого и нового стиля Python, но довольно часто используется, например, в конструкторах:

Я бы рекомендовал использовать CLASS.__bases__ что-то вроде этого!--3-->

Если вы не знаете, сколько аргументов вы можете получить, и хотите передать их все ребенку:

в python также есть super ().

пример того, как метод суперкласса вызывается из метода подкласса

этот пример аналогичен описанному выше.Однако есть одно отличие, что super не имеет никаких аргументов, переданных ему.Этот приведенный выше код является исполняемым в версии python 3.4.

в Python 2 мне не очень повезло с super (). Я использовал ответ из jimifiki на эту нить как ссылаться на родительский метод в python?. Затем я добавил к нему свой собственный маленький поворот, который, я думаю, является улучшением удобства использования (особенно если у вас длинные имена классов).

Наследование – важная составляющая объектно-ориентированного программирования. Так или иначе мы уже сталкивались с ним, ведь объекты наследуют атрибуты своих классов. Однако обычно под наследованием в ООП понимается наличие классов и подклассов. Также их называют супер- или надклассами и классами, а также родительскими и дочерними классами.

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

Простое наследование методов родительского класса

В качестве примера рассмотрим два класса столов. Класс Table – родительский по отношению к DeskTable (письменные столы). Независимо от своего типа все столы имеют длину, ширину и высоту. Пусть для письменных столов также важна площадь поверхности. Общее вынесем в класс, частное – в подкласс.

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

В данном случае у класса DeskTable нет своего конструктора, поэтому он наследует его от родителя. При создании объектов передавать аргументы необходимо в обоих случаях. Попытка вызова DeskTable с пустыми скобками приведет к ошибке.

С другой стороны, экземпляры надкласса Table, согласно неким родственным связям, не наследуют метод square своего подкласса.

В этом смысле терминология "родительский и дочерний класс" не совсем верна. Наследование в ООП – это скорее аналог систематизации и классификации наподобие той, что есть в живой природе. Все млекопитающие имеют четырехкамерное сердце, но только носороги – рог.

Полное переопределение метода надкласса

Рассмотрим вариант программы с "цепочкой наследования". Пусть дочерний по отношению к Table класс DeskTable в свою очередь выступит родительским по отношению к ComputerTable (компьютерные столы):

Допустим, по задумке разработчиков рабочая поверхность компьютерного стола может вычисляться за вычетом площади, которую занимает монитор. В результате метод square в ComputerTable имеет отличия.

Определив в дочернем классе метод, одноименный методу родительского, мы тем самым переопределяем метод родительского класса. При вызове square на экземпляры ComputerTable будет вызываться метод из этого класса, а не из родительского класса DeskTable.

В то же время ComputerTable наследует конструктор класса от своей "бабушки" – класса Table.

Дополнение, оно же расширение, метода

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

Пусть имеется подкласс кухонных столов, для которых необходимо еще одно поле – количество посадочных мест. Из-за этой детали мы вынуждены в дочернем классе переопределить конструктор родительского.

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

Здесь в теле конструктора KitchenTable мы вызываем метод __init__ через объект-класс Table, а не через объект-экземпляр. Вспомним, что в таких случаях метод вызывается как обычная функция (объект, к которому применяется метод, не передается в качестве первого аргумента). Поэтому в конструктор надкласса мы "вручную" передаем текущий экземпляр (self), записывая его перед остальными аргументами.

У кода выше есть небольшой недостаток. Нам ничего не мешает (при условии совпадения количества параметров) вызвать конструктор другого класса, а не только родительского, указав его имя вместо Table. Кроме того, имя надкласса может измениться, и тогда есть риск неправильных обращений к нему из дочерних классов.

В Python c целью улучшения так называемой обслуживаемости кода можно использовать встроенную в язык функцию super. Наиболее распространенным вариантом ее применения является вызов метода родительского класса из метода подкласса:

В данном случае аргумент self в скобках вызываемого родительского метода указывать явно не требуется.

Параметры со значениями по умолчанию у родительского класса

Рассмотрим случай, когда родительский класс имеет параметры со значениями по умолчанию, а дочерний – нет:

При таком определении классов можно создать экземпляр от Table без передачи аргументов для конструктора:

Можем ли мы создать экземпляр от KitchenTable, передав значение только для параметра p ? Например, вот так:

Возможно ли, что p будет присвоено число 10, а l , w и h получат по единице от родительского класса? Невозможно, будет выброшено исключение по причине несоответствия количества переданных аргументов количеству требуемых конструктором:

Когда создается объект от дочернего класса, сначала вызывается его конструктор, если он есть. Интерпретатор еще не знает, что в теле этого конструктора будет вызван конструктор родительского класса. Ведь это не обязательно. Значит, если все параметры дочернего конструктора не имеют значений по умолчанию, при построении объекта все значения должны передаваться.

Поэтому, если требуется допустить создание объектов от дочернего класса без передачи аргументов, придется назначить значения по умолчанию также в конструкторе дочернего класса.

Параметр p , которого нет у родительского класса, мы делаем последним не просто так. Бывает, объекты разных родственных классов создаются или обрабатываются в одном цикле, то есть по одному алгоритму. При этом у них должны быть одинаковые "интерфейсы", то есть одинаковое количество передаваемых в конструктор аргументов.

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

Другой вариант – отказаться от конструктора в дочернем классе, а значение для поля places устанавливать отдельным вызовом метода:

Здесь у всех кухонных столов по-умолчанию будет 4 места. Если мы хотим изменить значение поля places, можем вызвать метод set_places(). Хотя в случае Python можем сделать это напрямую, присвоив полю. При этом у экземпляра появится собственное поле places.

Поэтому метод set_places() в общем-то не нужен.

В любом случае произвольное количество мест будет устанавливаться не в конструкторе, а отдельно. Если все же требуется указывать места при создании объекта, это можно сделать и в конструкторе родителя:

С помощью функции isinstance() проверяется, что создаваемый объект имеет тип KitchenTable. Если это так, то у него появляется поле places.

Мы не используем параметр p со значением по умолчанию в заголовке конструктора потому, что, если объектам других родственных классов он не нужен, не происходило путаницы и сложностей с документированием кода.

Практическая работа

Разработайте программу по следующему описанию.

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

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

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

Отправьте одного из солдат первого героя следовать за ним. Выведите на экран идентификационные номера этих двух юнитов.

Курс с примерами решений практических работ:
android-приложение, pdf-версия

Читайте также: