Объясните как работает метод super,а так-же конструкция super().__init__() в python? [закрыт]
Хотите улучшить этот вопрос? Переформулируйте вопрос так, чтобы он был сосредоточен только на одной проблеме.
Закрыт 4 года назад .
Объясните как работает метод super , а так-же конструкция super().__init__() в python? super часто используется в классах. Желательно объяснить как можно проще и на простых примерах.
Отслеживать
66.4k 6 6 золотых знаков 51 51 серебряный знак 112 112 бронзовых знаков
задан 1 июн 2019 в 12:31
366 1 1 золотой знак 3 3 серебряных знака 13 13 бронзовых знаков
1 июн 2019 в 13:05
Отличный ответ но гуглом я умею пользоваться.Читаю и разбираюсь но на данный момент понять не могу, сюда написал затем,что-бы может у кого нибудь получилось объяснить.
1 июн 2019 в 13:35
Raymond Hettinger — Super considered super! youtu.be/EiOglTERPEo
1 июн 2019 в 14:09
Нашёл такой же ролик только с русским переводом youtube.com/watch?v=61UuKJRl2m0
1 июн 2019 в 14:12
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
super() — это функция, которая обращается к классу, от которого наследуется текущий.
Сначала немного кода:
class A: def some_method(self): print('Spam, eggs!!1') class B(A): def some_method(self): print('Hello, World!') x = B() x.some_method()
Hello, World!
Как Вы можете заметить, мы перегрузили метод родительского класса. Но что, если нам потребуется всего лишь немного дополнить его? Как нам дополнить родительский метод, не копируя его полностью и не изобретая велосипедов? Тут нам и нужен super() :
class A: def some_method(self): print('Spam, eggs!!1') class B(A): def some_method(self): super().some_method() print('Hello, World!') x = B() x.some_method()
Spam, eggs!!1 Hello, World!
С помощью super().some_method() мы вызвали родительский метод, а после дополнили свой. Именно для этого чаще всего используется эта функция.
__init__() — это метод инициализации класса, следовательно super().__init__() вызывает метод инициализации из родительского класса. Например, чтобы дополнить его.
Наследование. Функция super() и делегирование
Мы продолжаем изучение темы «наследование». Это занятие я начну с простой, но важной терминологии. Сморите, если у нас имеется некий базовый класс Geom и мы создаем дочерний класс Line, в котором дополнительно прописан метод draw(), то это называется расширением (extended) базового класса:
class Geom: name = 'Geom' class Line(Geom): def draw(self): print("Рисование линии")
Как правило, дочерние создаются именно для расширения функциональности базовых классов. Однако, если в классе Geom также прописать метод draw():
class Geom: name = 'Geom' def draw(self): print("Рисование примитива")
то теперь класс Line лишь переопределяет (overriding) поведение базового класса, не меняя его принцип функционирования. Поэтому, когда говорят о расширении, то подразумевают добавление новых атрибутов в дочерних классах, а при переопределении (обычно методов) – изменение поведения уже существующего функционала.
Функция super() и делегирование
Давайте теперь пропишем инициализатор в базовом классе Geom (метод draw уберем):
class Geom: name = 'Geom' def __init__(self): print("инициализатор Geom")
А ниже создадим экземпляр класса Line:
l = Line()
После запуска программы увидим ожидаемое поведение – был автоматически вызван инициализатор базового класса. В действительности, здесь происходит следующая последовательность вызовов магических методов. Сначала вызывается __call__(), который, в свою очередь, последовательно вызывает метод __new__() для создания экземпляра класса, а затем, метод __init__() для его инициализации. Так вот, все эти методы вызываются из дочернего класса Line. Если какой-либо из них не находится, то поиск продолжается в родительских классах в порядке иерархии наследования. Например, метод __new__() в данном случае будет взят из метакласса type, который неявно вызывается при создании классов (подробнее о метаклассах мы еще будем говорить). А вот метод __init__() мы прописали в классе Geom, поэтому будет вызван именно он. Причем, параметр self в этом методе будет ссылаться на созданный объект класса Line. Об этом мы с вами уже говорили и это следует помнить. Параметр self в методах класса всегда ссылается на объект, из которого метод был вызван.
Отлично, я думаю в целом схема вызова методов в момент создания экземпляров классов, понятна. И в соответствии с ней, если мы определим инициализатор в классе Line, то именно он и должен вызваться. Давайте это сделаем:
class Line(Geom): def __init__(self): print("инициализатор Line") def draw(self): print("Рисование линии")
Запустим программу и теперь видим, что действительно, вызывается именно метод __init__ класса Line. Я перепишу его со следующими параметрами:
def __init__(self, x1, y1, x2, y2): self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2
Это координаты начала и конца линии на плоскости. Соответственно, при создании объектов этого класса, мы теперь должны передавать аргументы:
l = Line(0, 0, 10, 20)
Все работает и никаких проблем у нас нет. Но, давайте теперь добавим еще один класс Rect для прямоугольников:
class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill=None): self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 self.fill = fill def draw(self): print("Рисование прямоугольника")
Смотрите, у нас получилось дублирование кода. Это очень нехорошо. Но мы знаем, как это можно поправить. Давайте общее этих методов вынесем в базовый класс Geom:
class Geom: name = 'Geom' def __init__(self, x1, y1, x2, y2): print(f"инициализатор Geom для ") self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2
В дочернем классе Line уберем инициализатор, т.к. он полностью повторяется в Geom, а класс Rect запишем в виде:
class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill=None): print("инициализатор Rect") self.fill = fill def draw(self): print("Рисование прямоугольника")
Ниже создадим экземпляры обоих классов:
l = Line(0, 0, 10, 20) r = Rect(1, 2, 3, 4)
После запуска увидим следующее:
инициализатор Geom для
инициализатор Rect
О чем это говорит? Для класса Line был вызван инициализатор в базовом классе Geom, а для класса Rect не вызывался – только инициализатор самого класса. И это логично, так как метод __init__() был найден в Rect и дальше цепочка поиска не продолжалась. Но нам же нужно при создании примитивов также вызывать инициализатор и базового класса Geom. Как это сделать? Конечно, мы могли бы явно указать имя базового класса Geom и вызвать через него магический метод __init__() в инициализаторе класса Rect:
def __init__(self, x1, y1, x2, y2, fill=None): Geom.__init__(self, x1, y1, x2, y2) print("инициализатор Rect") self.fill = fill
Но явно указывать имена базовых классов не лучшая практика, так как имена и иерархия наследования могут меняться. Поэтому в Python для обращения к базовому классу используется специальная функция super():
def __init__(self, x1, y1, x2, y2, fill=None): super().__init__(x1, y1, x2, y2) print("инициализатор Rect") self.fill = fill
Она возвращает ссылку на, так называемый, объект-посредник, через который происходит вызов методов базового класса.
Теперь, при запуске программы мы видим, что был вызван инициализатор сначала класса Geom, а затем, для Rect. Такое обращение к переопределенным методам базового класса с помощью функции super() называется делегированием. То есть, мы делегировали вызов инициализатора класса Geom, чтобы он создал в нашем объекте локальные свойства с координатами углов прямоугольника. Причем, вызов метода __init__() базового класса лучше делать в первой же строчке, чтобы он случайно не переопределял какие-либо локальные свойство в дочернем классе. Например, если в базовом __init__() дополнительно прописать:
def __init__(self, x1, y1, x2, y2): print(f"инициализатор Geom для ") self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 self.fill = 0
А в дочернем его вызвать в последнюю очередь:
class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill=None): print("инициализатор Rect") self.fill = fill super().__init__(x1, y1, x2, y2)
то, очевидно, свойство fill будет неявно переопределено при вызове __init__() базового класса:
print(r.__dict__)
Подобные причины и рекомендуют делать вызов инициализатора базового класса в первой же строчке:
class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill=None): super().__init__(x1, y1, x2, y2) print("инициализатор Rect") self.fill = fill
Теперь у нас нет проблем с определением локального свойства fill.
Надеюсь, из этого занятия вы узнали о понятиях расширения классов и переопределения их методов, разобрались с работой функции super(), а также делегированием методов, то есть их вызовом из базового класса через функцию super().
Видео по теме
Концепция ООП простыми словами

#1. Классы и объекты. Атрибуты классов и объектов

#2. Методы классов. Параметр self

#3. Инициализатор __init__ и финализатор __del__

#4. Магический метод __new__. Пример паттерна Singleton

#5. Методы класса (classmethod) и статические методы (staticmethod)

#6. Режимы доступа public, private, protected. Сеттеры и геттеры

#7. Магические методы __setattr__, __getattribute__, __getattr__ и __delattr__

#8. Паттерн Моносостояние

#9. Свойства property. Декоратор @property

#10. Пример использования объектов property

#11. Дескрипторы (data descriptor и non-data descriptor)

#12. Магический метод __call__. Функторы и классы-декораторы

#13. Магические методы __str__, __repr__, __len__, __abs__

#14 Магические методы __add__, __sub__, __mul__, __truediv__

#15. Методы сравнений __eq__, __ne__, __lt__, __gt__ и другие

#16. Магические методы __eq__ и __hash__

#17. Магический метод __bool__ определения правдивости объектов

#18. Магические методы __getitem__, __setitem__ и __delitem__

#19. Магические методы __iter__ и __next__

#20. Наследование в объектно-ориентированном программировании

#21. Функция issubclass(). Наследование от встроенных типов и от object

#22. Наследование. Функция super() и делегирование

#23. Наследование. Атрибуты private и protected

#24. Полиморфизм и абстрактные методы
#25. Множественное наследование

#26. Коллекция __slots__

#27. Как работает __slots__ с property и при наследовании

#28. Введение в обработку исключений. Блоки try / except

#29. Обработка исключений. Блоки finally и else

#30. Распространение исключений (propagation exceptions)

#31. Инструкция raise и пользовательские исключения

#32. Менеджеры контекстов. Оператор with

#33. Вложенные классы

#34. Метаклассы. Объект type

#35. Пользовательские метаклассы. Параметр metaclass

#36. Метаклассы в API ORM Django

#37. Введение в Python Data Classes (часть 1)

#38. Введение в Python Data Classes (часть 2)

#39. Python Data Classes при наследовании
© 2023 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
super() – супер класс в Python
super() – это встроенная функция языка Python. Она возвращает прокси-объект, который делегирует вызовы методов классу-родителю (или собрату) текущего класса (или класса на выбор, если он указан, как параметр).
Основное ее применение и польза – получения доступа из класса наследника к методам класса-родителя в том случае, если наследник переопределил эти методы.
Что такое прокси-объект? Прокси, по-русски, это заместитель. То есть это объект, который по смыслу должен вести себя почти так же, как замещенный объект. Как правило он перенаправляет вызовы своих методов к другому объекту.
Давайте рассмотрим пример наследования. Есть какой-то товар в классе Base с базовой ценой в 10 единиц. Нам понадобилось сделать распродажу и скинуть цену на 20%. Хардкодить – это непрофессионально и негибко:
class Base: def price(self): return 10 class Discount(Base): def price(self): return 8
Гораздо лучше было бы получить цену из родительского класса Base и умножить ее на коэффициент 0.8, что даст 20% скидку. Однако, если мы вызовем self.price() в методе price() мы создадим бесконечную рекурсию, так как это и есть один и тот же метод класса Discount! Тут же нужен метод Base.price() . Тогда его и вызовем по имени класса:
class Discount(Base): def price(self): return Base.price(self) * 0.8
Здесь, надо не забыть указать self при вызове первым параметром явно, чтобы метод был привязан к текущему объекту. Это будет работать, но этот код не лишен изъянов, потому что необходимо явно указывать имя предка. Представьте, если иерархия классов начнет разрастаться? Например, нам нужно будет вставить между этими классами еще один класс, тогда придется редактировать имя класса-родителя в методах Discount:
class Base: def price(self): return 10 class InterFoo(Base): def price(self): return Base.price(self) * 1.1 class Discount(InterFoo): #Тут на помощь приходит super() ! Супер он не потому что, подобно Супермэну, помогает всем людям, а потому что обращается к атрибутам классов стоящих над ним в порядке наследования (кто учил матан, вспомнят понятие супремум).
Будучи вызванным без параметров внутри какого-либо класса, super() вернет прокси-объект, методы которого будут искаться только в классах, стоящих ранее, чем он, в порядке MRO. То есть, это будет как будто бы тот же самый объект, но он будет игнорировать все определения из текущего класса, обращаясь только к родительским:
class Base: def price(self): return 10 class InterFoo(Base): def price(self): return super().price() * 1.1 class Discount(InterFoo): def price(self): return super().price() * 0.8
Для Discount порядок MRO: Discount - InterFoo - Base - object . Вызов super().method() внутри класса Discount будет игнорировать Discount.method() , а будет искать method в InterFoo, затем, если не найдет, то в Base и object.
Когда нельзя забыть super?
Очень часто super вызывается в методе __init__. Метод инициализации класса __init__, как правило задает какие-либо атрибуты экземпляра класса, и если в дочернем классе мы забудем его вызвать, то класс окажется недоинициализированным: при попытке доступа к родительским атрибутам будет ошибка:
class A: def __init__(self): self.x = 10 class B(A): def __init__(self): self.y = self.x + 5 # print(B().y) # ошибка! AttributeError: 'B' object has no attribute 'x' # правильно: class B(A): def __init__(self): super().__init__() #Параметры super
Функция может принимать 2 параметра. super([type [, object]]) . Первый аргумент – это тип, к предкам которого мы хотим обратиться. А второй аргумент – это объект, к которому надо привязаться. Сейчас оба аргумента необязательные. В прошлых версиях Python приходилось их указывать явно:
class A: def __init__(self, x): self.x = x class B(A): def __init__(self, x): super(B, self).__init__(x) # теперь это тоже самое: super().__init__(x)Теперь Python достаточно умен, чтобы самостоятельно подставить в аргументы текущий класс и self для привязки. Но старая форма тоже осталась для особых случаев. Она нужна, если вы используете super() вне класса или хотите явно указать с какого класса хотите начать поиск методов.
Действительно, super() может быть использована вне класса. Пример:
d = Discount() print(super(Discount, d).price())В этом случае объект, полученный из super(), будет вести себя как класс InterFoo (родитель Discount), хотя привязан он к переменной d, которая является экземпляром класса Discount.
Это редко используется, но, вероятно, кому-то будет интересно узнать, что функция super(cls), вызванная только с одним параметром, вернет непривязанный к экземпляру объект. У него нельзя вызывать методы и обращаться к атрибутам. Привязать его можно будет так:
super_d = super(Discount) d = Discount() binded_d = super_d.__get__(d, Discount) # привязка print(binded_d.price()) # 11.0Множественное наследование
В случае множественного наследования super() необязательно указывает на родителя текущего класса, а может указывать и на собрата. Все зависит от структуры наследования и начальной точки вызова метода. Общий принцип остается: поиск начинается с предыдущего класса в списке MRO. Давайте рассмотрим пример ромбовидного наследования. Каждый класс ниже в методе method печатает свое имя. Плюс все, кроме первого, вызывают свой super().method() :
class O: def method(self): print('I am O') class A(O): def method(self): super().method() print('I am A') class B(O): def method(self): super().method() print('I am B') class C(A, B): def method(self): super().method() print('I am C')Если вызвать метод C().method() , то в терминале появится такая распечатка:
# C().method() I am O I am B I am A I am CВидно, что каждый метод вызывается ровно один раз и ровно в порядке MRO. C вызывает родителя A, а A вызывает своего брата B, а B вызывает их общего родителя O. Но! Стоит нам вызвать A().method() , он уже не будет вызывать B().method() , так как класса B нет среди его родителей, он брат, а родитель у класс А только один – это O. А о братьях он и знать не хочет:
# A().method() I am O I am AТаким образом, вызов super() сам автоматически догадывается, к кому обращаться: к родителю или к брату. Все зависит от иерархии класса и начальной точки вызова. Эта фишки снимают с программиста головную боль, связанную с заботой о поддержании цепочки вызовов в иерархии классов.
Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway
Функция super() в Python – как работает в наследовании
Как мы все знаем, Python – это объектно-ориентированный язык программирования. Следовательно, Python следует всем концепциям ООП, и одна из таких концепций – наследование.
Используя концепцию наследования, мы можем ссылаться на родительский класс с помощью функции super() в Python внутри унаследованного или дочернего класса. Функция super(), которую мы используем в дочернем классе, возвращает временно созданный объект суперкласса, который позволяет нам получить доступ ко всем его методам, присутствующим в дочернем классе.
Преимущества функции super()
Ниже приведены преимущества использования функции super() в дочернем классе:
- Нам не нужно запоминать имя родительского класса при использовании функции super(), потому что не нужно указывать имя родительского класса для доступа к методам, присутствующим в нем.
- Мы можем использовать функцию super() с одиночным и множественным наследованием.
- Функция super() в Python реализует возможность повторного использования кода и модульность, поскольку нам нет необходимости переписывать всю функцию снова и снова.
- Функция super() в Python известна как динамическая функция, поскольку все мы знаем, что Python – это язык программирования с динамической типизацией.
Ограничения
Ниже приведены три ограничения, которым мы должны следовать, чтобы использовать функцию super() в программе Python:
- Аргументы задаются в функции super(); и аргументы в функции, которую мы вызвали, должны совпадать.
- Каждое вхождение метода, который мы используем, должно включать ключевое слово super() после его использования.
- Мы должны указать присутствующие класс и методы, на которые ссылается функция super().
Мы можем использовать функцию super() в обоих типах наследования в Python, то есть как в одиночном, так и в множественном наследовании. Разберем использовании функции super() в обоих типах наследования отдельно и на примерах.
Использование функции super() в одиночном наследовании в Python
В этом примере мы будем использовать животных в качестве ссылки для единственного примера наследования.
Кошки, лошади, коровы, собаки и т. д. относятся к классу животных. Все они также имеют некоторые общие характеристики:
- Все они домашние животные.
- У всех четыре ноги и хвост.
- Как часть класса animalia, все они также являются млекопитающими.
Итак, мы можем сказать, что класс кошек, класс лошадей и класс собак являются подклассами класса животных. Это пример одиночного наследования, потому что все подклассы (класс кошек, класс лошадей и класс собак) наследуются только от одного родительского класса, т. е. класса animalia. Теперь посмотрим на следующую программу.
# Define parent class animalia class Animalia: # define construcors for parent animalia class def __init__(self): self.Legs = 4 self.adomestic = True self.atail = True self.amammals = True # define mammal class as child class def aMammal(self): if self.amammals: print("The given animal is a mammal type .") # define domestic class as child class def aDomestic(self): if self.adomestic: print("The given animal is a domestic animal type.") # define dog class class Dog(Animalia): def __init__(self): super().__init__() # using super() function to access class methods def isMammal(self): super().aMammal() # using mammal class # define cat class class Cat(Animalia): def __init__(self): super().__init__() def isMammal(self): super().aDomestic() # using domestic class # define horse class class Horse(Animalia): def __init__(self): super().__init__() def TailandLegs(self): # using tail and legs class if self.atail and self.Legs == 4: print("The given animal has four legs and a tail") # Taking the driver's code for defined classes Tommy = Dog() Tommy.aMammal() Tom = Cat() Tom.aDomestic() Burno = Horse() Burno.TailandLegs()
The given animal is a mammal type. The given animal is a domestic animal type. The given animal has four legs and a tail.
В приведенном выше коде мы определили animalia как родительский класс и унаследовали от него классы домашних животных, имеющих хвост и 4 ноги и класс млекопитающих. После этого мы определили класс cat, horse и dog и использовали в нем функцию super(), с помощью которой в этих классах мы получили доступ к методам класса animalia в классе cat, horse и dog.
Функция super() в множественном наследовании в Python
В этом примере мы будем использовать родительский класс, то есть класс млекопитающих. Затем мы унаследуем классы «Can Fly» и «Can Swim» от класса млекопитающих.
Эти классы будут представлять, может или нет данное млекопитающее летать, плавать. После этого мы определим класс животных, который будет унаследован от классов «Can Fly» и «Can Swim» и вернет нам, что данное животное имеет определенные характеристики или нет.
Итак, мы видим, что используемый здесь класс животных унаследован от нескольких базовых классов и, следовательно, является примером множественного наследования в Python. Теперь посмотрим на следующую программу.
# Define Mammal class as parent class class aMammals(): def __init__(self, name): print(name, "Is a mammal of animalia class") # define can fly as child class class FlyCapable(aMammals): def __init__(self, FlyCapable_name): print(FlyCapable_name, "is not capable of flying") # Calling Parent class Constructor super().__init__(FlyCapable_name) # define can swim as child class class SwimCapable(aMammals): def __init__(self, SwimCapable_name): print(SwimCapable_name, "is not capable of swimming") super().__init__(SwimCapable_name) # Inherit animalia class from both fly and swim class class animalia(FlyCapable, SwimCapable): def __init__(self, name): super().__init__(name) # using super() function # Taking driver Code for animalia class Burno = animalia("Cat")
Cat is not capable of flying Cat is not capable of swimming Cat Is a mammal of animalia class
В приведенном выше коде мы определили млекопитающее как родительский класс. После этого мы унаследовали классы Can Fly и Can Swim от класса млекопитающих. Мы использовали методы как can fly, так и can swim внутри класса animalia с помощью функции super(). Класс animalia унаследован от классов can swim и can fly.

