Super python что это
![]()
Функция super() в Python: вызов методов родительского класса
19 марта 2023
Оценки статьи
Еще никто не оценил статью
Функция super() в Python используется для вызова методов родительского класса. В этой статье мы рассмотрим, что такое super() , как он работает, и как его можно использовать.
Что такое super() в Python?
super() — это функция, которая позволяет вызывать методы родительского класса в дочернем классе. Она используется, когда нужно вызвать методы родительского класса в дочернем классе, чтобы избежать дублирования кода и улучшить его читаемость.
Функция super() можно использовать для вызова методов как в методах, так и в конструкторах дочернего класса.
Как работает функция super() в Python?
Когда мы создаем дочерний класс, он наследует все атрибуты и методы родительского класса. Для того, чтобы вызвать метод родительского класса, мы можем использовать функцию super() .
При вызове super() Python ищет родительский класс в иерархии наследования, начиная с текущего класса и переходя к родительским классам в порядке, заданном в списке наследования. После нахождения родительского класса, super() вызывает его методы и передает все аргументы, которые были переданы методу дочернего класса.
Пример использования super() в Python
Рассмотрим пример, который показывает, как использовать super() для вызова метода родительского класса.
class Animal: def __init__(self, name): self.name = name def make_sound(self): print("The animal makes a sound") class Dog(Animal): def __init__(self, name): super().__init__(name) def make_sound(self): super().make_sound() print("The dog barks") my_dog = Dog("Buddy") my_dog.make_sound()
В этом примере у нас есть два класса: Animal и Dog . Класс Dog наследует от Animal .
Мы переопределяем метод make_sound() в классе Dog , но вместо того, чтобы полностью переписывать метод, мы вызываем метод make_sound родительского класса с помощью super() , а затем добавляем к нему свой код.
Результат выполнения этого кода будет:
>>> The animal makes a sound >>> The dog barks
В этом примере мы вызываем метод make_sound() родительского класса с помощью super() , а затем добавляем к нему свой код, чтобы сделать ту же самую вещь, но с дополнительным функционалом.
Функция super() в Python
Функция super() в Python позволяет нам явно ссылаться на родительский класс. Это полезно в случае наследования, когда мы хотим вызывать функции суперкласса. В Python Inheritance подклассы наследуются от суперкласса.
Функция super() позволяет нам неявно ссылаться на суперкласс. Итак, super делает нашу задачу проще и удобнее. Ссылаясь на суперкласс из подкласса, нам не нужно явно писать имя суперкласса.
Пример функции
Сначала просто посмотрите на следующий код, который мы использовали в нашем руководстве по наследованию. В этом примере кода суперклассом был Person, а подклассом – Student. Итак, код показан ниже.
class Person: # initializing the variables name = "" age = 0 # defining constructor def __init__(self, person_name, person_age): self.name = person_name self.age = person_age # defining class methods def show_name(self): print(self.name) def show_age(self): print(self.age) # definition of subclass starts here class Student(Person): studentId = "" def __init__(self, student_name, student_age, student_id): Person.__init__(self, student_name, student_age) self.studentId = student_id def get_id(self): return self.studentId # returns the value of student id # end of subclass definition # Create an object of the superclass person1 = Person("Richard", 23) # call member methods of the objects person1.show_age() # Create an object of the subclass student1 = Student("Max", 22, "102") print(student1.get_id()) student1.show_name()
В приведенном выше примере мы вызвали функцию родительского класса, как:
Person.__init__(self, student_name, student_age)
Мы можем заменить это вызовом функции, как показано ниже:
super().__init__(student_name, student_age)
Результат останется таким же в обоих случаях, как показано на изображении ниже:

Функция для Python 3
Обратите внимание, что приведенный выше синтаксис предназначен для функции super() в python 3. Если вы используете версию Python 2.x, она немного отличается, и вам придется внести следующие изменения:
class Person(object): . super(Student, self).__init__(student_name, student_age)
Первое изменение – сделать объект базовым классом для Person. В версиях Python 2.x необходимо использовать функцию super. В противном случае вы получите следующую ошибку:
Traceback (most recent call last): File "super_example.py", line 40, in student1 = Student("Max", 22, "102") File "super_example.py", line 25, in __init__ super(Student, self).__init__(student_name, student_age) TypeError: must be type, not classobj
Второе изменение синтаксиса самой функции. Как видите, функция в python 3 намного проще в использовании, и синтаксис также выглядит чистым.
Функция super() с многоуровневым наследованием
Как мы уже заявляли ранее, super() позволяет нам неявно ссылаться на суперкласс.
Но в случае многоуровневого наследования, к какому классу оно будет относиться? super() всегда будет ссылаться на непосредственный суперкласс.
Также функция может не только ссылаться на функцию __init __(), но также может вызывать все другие функции суперкласса. Итак, в следующем примере мы увидим это:
class A: def __init__(self): print('Initializing: class A') def sub_method(self, b): print('Printing from class A:', b) class B(A): def __init__(self): print('Initializing: class B') super().__init__() def sub_method(self, b): print('Printing from class B:', b) super().sub_method(b + 1) class C(B): def __init__(self): print('Initializing: class C') super().__init__() def sub_method(self, b): print('Printing from class C:', b) super().sub_method(b + 1) if __name__ == '__main__': c = C() c.sub_method(1)
Давайте посмотрим на результат вышеприведенного примера в python 3 с многоуровневым наследованием.
Initializing: class C Initializing: class B Initializing: class A Printing from class C: 1 Printing from class B: 2 Printing from class A: 3
Итак, из выходных данных мы ясно видим, что сначала была вызвана функция __init __() класса C, затем класса B, а затем класса A. Аналогичная вещь произошла при вызове функции sub_method().
Зачем нужна функция super()?
Если у вас есть предыдущий опыт работы с языком Java, вы должны знать, что базовый класс также вызывается там супер объектом. Итак, эта концепция действительно полезна для кодеров. Однако Python также позволяет программисту использовать имя суперкласса для ссылки на него. И, если ваша программа содержит многоуровневое наследование, эта функция будет вам полезна.
Наследование. Функция 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 при наследовании
© 2024 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Python — о множественном наследовании и функции super() простыми словами
Python позволяет указать для класса несколько родителей. Это называется множественным наследованием.
Например, мы хотим добавить какие-то общие свойства нескольким разным классам. Добавлять эти свойства, через класс-наследник для каждого из классов явно некрасиво, нарушает принцип DRY.
Если речь о чем-то простом, то это можно сделать через декоратор. Но если это что-то более развесистое и прикладное, то напрашивается оформить это как объект, и добавить к нужным классам как mixin. В Python нет специального способа добавлять mixin, это осуществляется через множественное наследование.
Загадка

Platypus.__init__() Mammal.__init__() Bird.__init__() Vertebrate.__init__()
Если хоть один наследник нарушает принципы кооперативного наследования (не вызывает super() ), то метод родителя вообще не будет вызван, хотя вроде бы мы имеем явный вызов этого родителя из другого наследника.
Например, давайте закомментарим вызов super() в классе Bird (строка 8). Вывод изменится следующим образом:
Platypus.__init__() Mammal.__init__() Bird.__init__()
Причина в том, что из Mammal.__init__ вызывается следующий по MRO класс ( Bird ), а вовсе не родитель Mammal ( Vertebrate ). Родителя ранее вызывал Bird , но мы убрали этот вызов.
В коде ниже я добавил аргументы в __init__ , чтобы проиллюстрировать сказанное выше выдаваемой ошибкой.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20class Vertebrate: def __init__(self): print('Vertebrate.__init__()') class Bird(Vertebrate): def __init__(self, beak_length): print('Bird.__init__()') super().__init__() class Mammal(Vertebrate): def __init__(self, hair_length): print('Mammal.__init__()') super().__init__() class Platypus(Mammal, Bird): def __init__(self): print('Platypus.__init__()') super().__init__(1) duckbill = Platypus()Platypus.__init__() Mammal.__init__() . File "animal_class_tree_arguments.py", line 13, in __init__ super().__init__() TypeError: __init__() missing 1 required positional argument: 'beak_length'Ошибка показывает, что в Mammal код super().__init__() пытается вызвать Bird.__init__ .
Примечание - особенности работы super()
Одним из ограничений super() является то, что не получится выполнить операции (binary operations, subscriptions и т.д.) над возвращенным объектом, даже если эти операции реализованы в родителе вызывающего класса с помощью “магических методов”.
Если выполнить операцию над экземпляром класса, то Python найдет нужный для выполнения операции “магический метод” в родителе (в примере ниже - __getitem__ для индексирования с помощью оператора [] ).
Но если попытаться выполнить операцию над объектом, возвращаемым super() , получим ошибку:
class Parent: def __getitem__(self, idx): return 0 class Child(Parent): def index_super(self, idx): return super()[idx] kid = Child() print(f'kid[0]: kid[0]>') print(f'kid.index_super(0): kid.index_super(0)>')kid[0]: 0 . TypeError: 'super' object is not subscriptable