Что делает 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(type, object-or-type)
Параметры:
- type — необязательно, тип, от которого начинается поиск объекта-посредника
- object-or-type — необязательно, тип или объект, определяет порядок разрешения метода для поиска
Возвращаемое значение:
- объект-посредник, делегирующий вызовы методов родителю или собрату класса.
Описание:
Функция super() , возвращает объект-посредник, который делегирует вызовы метода родительскому или родственному классу, указанного type типа. Это полезно для доступа к унаследованным методам, которые были переопределены в классе.
object-or-type определяет порядок разрешения метода __mro__ для поиска. Поиск начинается с класса, сразу после указанного типа. Например, если __mro__ — это D -> B -> C -> A -> object , а значение type=B , то super() выполняет поиск объекта C -> A -> object .
- Если object-or-type не указан, то возвращается несвязанный объект-посредник.
- Если object-or-type является объектом (экземпляром), то будет получен посредник, для которого isinstance(obj, type) возвращает True .
- Если object-or-type является типом (классом), то будет получен посредник, для которого issubclass(subtype, type) возвращает True .
Используя функцию super() с обоими аргументами, можно точно определить объекты и сделать соответствующие ссылки (смотрите самый последний пример). Без аргументов функция super() работает только внутри определения класса, а необходимые детали для идентификации класса и доступа к текущему экземпляру для методов заполняет компилятор.
В дополнение к поиску методов, функция super() также работает при поиске атрибутов. Одним из вариантов использования этого является вызов дескрипторов в родительском или родственном классе.
Обратите внимание, что super() реализована как часть процесса привязки для явного поиска по атрибутам, таких как super().__getitem__(name) . Это достигается путем реализации собственного метода __getattribute__() для поиска классов в предсказуемом порядке, который поддерживает множественное наследование __mro__ . Функция super() не предназначена для неявных поисков с использованием инструкций или операторов, таких как super()[name] .
Типичные случаи использования super() .
Функция super() используется для обращения к родительским классам в иерархиях с единичным наследованием, чтобы явно не указывать их имена, что упрощает поддержку кода в дальнейшем.
Совсем простой пример для понимания о чем речь:
>>> class A: . def some_method(self): . print('some_method A') . >>> class B(A): . def some_method(self): . print('some_method B') . >>> x = B() >>> x.some_method() # some_method B
Здесь перегружен метод родительского класса. Но что если необходимо только дополнить родительский метод (изменить его поведение), не копируя его полностью? Тут и нужна функция super() :
class A: def some_method(self): print('some_method A') class B(A): def some_method(self): # вызываем метод родительского класса super().some_method() # Добавляем свое поведение print('some_method B') >>> x = B() >>> x.some_method() # some_method A # some_method B
Более практичный пример.
class LoggingDict(dict): def __setitem__(self, key, value): logging.info('Setting %r to %r' % (key, value)) super().__setitem__(key, value)
Класс LoggingDict имеет все те же возможности, что и его родитель dict , плюс расширяет метод __setitem__ для логирования каждого обновлении ключа. После внесения записи в журнал метод использует super() для делегирования работы по фактическому обновлению словаря с помощью пары ключ/значение.
До появления super() нужно было жестко связывать вызов с dict.__setitem__(self, key, value) . Функция super() лучше, т. к. это вычисляемая косвенная ссылка.
Одним из преимуществ косвенности является то, что не нужно указывать класс делегата по имени. Чтобы переключить базовый класс на какое-либо другое сопоставление (вместо dict ), то ссылка super() автоматически переключится на новый родительский класс.
# используем новый базовый # класс SomeOtherMapping class LoggingDict(SomeOtherMapping): def __setitem__(self, key, value): logging.info('Setting %r to %r' % (key, value)) # никаких изменений не требуется super().__setitem__(key, value)
В дополнение к изоляции изменений, у вычисляемой косвенности есть еще одно важное преимущество, которое может быть незнакомо людям, пришедшим из статических языков. Так как косвенность вычисляется во время выполнения, то есть свобода влиять на вычисление, чтобы косвенность указывала на какой-то другой класс.
Расчет зависит как от класса, в котором вызывается super() , так и от «дерева предков» экземпляра. Первый компонент определяется исходным кодом этого класса, в котором вызывается super() . В примере, функция super() вызывается в методе LoggingDict.__setitem__ . Этот компонент фиксирован. Второй компонент, более интересный и носит переменный характер (можно создавать новые подклассы с богатым деревом предков).
Воспользуемся этим поведением для создания упорядоченного словаря журналирования LoggingOD , не изменяя существующие классы:
class LoggingOD(LoggingDict, collections.OrderedDict): pass
И это все, исходный код не изменялся. «Дерево-предок» для нового класса: LoggingOD , LoggingDict , OrderedDict , dict , object . Важным результатом является то, что OrderedDict был вставлен после LoggingDict и перед dict ! Это означает, что вызов super() в LoggingDict.__setitem__ теперь отправляет обновление ключа/значения в OrderedDict , а не в dict .
Задумайтесь как удобно, был создан подкласс LoggingOD , единственная логика которого состоит в том, чтобы правильно составить два существующих класса и управлять «порядком их поиска«.
Порядок поиска или дерево предков — это официально известно как порядок разрешения методов или MRO. Легко просмотреть MRO, распечатав атрибут __mro__ :
>>> pprint(LoggingOD.__mro__) # (, # , # , # , # )
Практические советы.
Функция super() занимается делегированием вызовов методов некоторому классу в дереве предков экземпляра. Чтобы переупорядочиваемые вызовы методов работали, классы должны разрабатываться совместно. При этом возникают три легко решаемые практические задачи:
- Метод, вызываемый super() , должен существовать.
- Вызывающий и вызываемый должны иметь совпадающую подпись аргументов.
- Каждое вхождение метода должно использовать super() .
Рассмотрим стратегии получения аргументов вызывающего объекта, чтобы они соответствовали сигнатуре вызываемого метода. Это немного сложнее, чем традиционные вызовы методов, когда вызываемый объект известен заранее. При использовании super() вызываемый объект неизвестен во время написания класса (т.к. подкласс, написанный позже, может ввести новые классы в «MRO»).
Один из подходов состоит в том, чтобы придерживаться фиксированной подписи, используя позиционные аргументы. Это хорошо работает с такими методами, как __setitem__ , которые имеют фиксированную сигнатуру из двух аргументов, ключа и значения. Этот метод показан в примере выше LoggingDict , где __setitem__ имеет одинаковую подпись как в LoggingDict , так и в dict .
Более гибкий подход состоит в том, чтобы каждый метод в дереве предков был разработан совместно. Другими словами, должен принимать ключевые аргументы и словарь ключевых аргументов **kwargs , удалять любые аргументы, которые ему нужны, и перенаправлять оставшиеся аргументы с помощью **kwargs , в конечном итоге оставляя словарь пустым для последнего вызова в цепочке.
Каждый уровень удаляет ключевые аргументы, которые ему нужны, чтобы окончательный пустой словарь можно было отправить методу, который вообще не ожидает аргументов (например, у метода object.__init__ нет аргументов):
# test.py class Shape: def __init__(self, shapename, **kwds): self.shapename = shapename super().__init__(**kwds) class ColoredShape(Shape): def __init__(self, color, **kwds): self.color = color super().__init__(**kwds) # $ python -i test.py >>> cs = ColoredShape(color='red', shapename='circle')
С сигнатурой разобрались, а как убедиться, что целевой метод существует?
В приведенном выше примере показан простейший случай. Известно, что у любого объекта в Python есть метод object.__init__ , и этот объект всегда является последним классом в цепочке MRO, поэтому любая последовательность вызовов super().__init__ гарантированно заканчивается вызовом метода object.__init__ . Другими словами, есть гарантия того, что цель вызова super().__init__() существует и не вызовет ошибку AttributeError .
Для случаев, когда объект не имеет интересующего метода (например, метода .draw() ), необходимо написать некий класс Root , который гарантированно будет вызываться перед object . Ответственность класса Root состоит в том, чтобы просто съесть вызов метода без переадресации вызова с помощью super() .
Также метод Root.draw() может использовать утверждение, которое будет гарантировать, что он не маскирует какой-либо другой метод .draw() позже в цепочке. Это может произойти, если подкласс ошибочно включает класс, который имеет метод .draw() , но не наследуется от Root :
# test.py class Root: def draw(self): # цепочка делегирования остановится здесь assert not hasattr(super(), 'draw') class Shape(Root): def __init__(self, shapename, **kwds): self.shapename = shapename super().__init__(**kwds) def draw(self): print('Рисование. Установка формы на:', self.shapename) super().draw() class ColoredShape(Shape): def __init__(self, color, **kwds): self.color = color super().__init__(**kwds) def draw(self): print('Рисование. Установка цвета на:', self.color) super().draw() # $ python -i test.py >>> cs = ColoredShape(color='blue', shapename='square') >>> cs.draw() # Рисование. Установка цвета на: blue # Рисование. Установка формы на: square
Если подклассы внедряют другие классы в MRO, эти другие классы также должны наследоваться от Root , чтобы ни один путь поиска вызова .draw() не мог достичь object , не будучи остановленным Root.draw() . Это должно быть четко задокументировано, чтобы тот, кто пишет новые взаимодействующие классы, знал, что это подкласс от Root . Это ограничение мало чем отличается от собственного требования Python, согласно которому все новые исключения должны наследоваться от BaseException .
Методы, показанные выше, гарантируют, что функция super() вызывает метод, о существовании которого известно, и что подпись передаваемых аргументов будет правильной. Осталось только гарантировать непрерывность цепочки делегирования. Этого легко добиться, если классы разрабатываются совместно — просто добавим вызов super() к каждому методу в цепочке.
Примеры получения доступа к унаследованным методам.
Использование функции super() c единичным наследовании:
class Computer(): def __init__(self, computer, ram, ssd): self.computer = computer self.ram = ram self.ssd = ssd # Если создать дочерний класс `Laptop`, то будет доступ # к свойству базового класса благодаря функции super(). class Laptop(Computer): def __init__(self, computer, ram, ssd, model): super().__init__(computer, ram, ssd) self.model = model lenovo = Laptop('lenovo', 2, 512, 'l420') print('This computer is:', lenovo.computer) print('This computer has ram of', lenovo.ram) print('This computer has ssd of', lenovo.ssd) print('This computer has this model:', lenovo.model) # Вывод # This computer is: lenovo # This computer has ram of 2 # This computer has ssd of 512 # This computer has this model: l420
В следующем примере класс Rectangle является суперклассом, а Square является подклассом, поскольку методы Square наследуются от Rectangle , то мы можем вызвать метод __init __() суперкласса ( Rectangle.__ init __() ) из класса Square используя функцию super() . Далее просто пользоваться методами родителя, не написав ни строчки кода. В данном случае квадрат — это частный случай прямоугольника.
class Rectangle: def __init__(self, length, width): self.length = length self.width = width def area(self): return self.length * self.width def perimeter(self): return 2 * self.length + 2 * self.width class Square(Rectangle): def __init__(self, length): # Для квадрата просто нужно передать один параметр length. # При вызове 'super().__init__()' установим атрибуты 'length' и 'width'. super().__init__(length, length) # Класс 'Square' явно не реализует метод 'area()' и # будет использовать его из суперкласса 'Rectangle' sqr = Square(4) print("Area of Square is:", sqr.area()) # Area of Square is: 16 rect = Rectangle(2, 4) print("Area of Rectangle is:", rect.area()) # Area of Rectangle is: 8
И наконец пример работы функции super() при использовании совместного множественного наследования в динамическом окружении:
class A: def __init__(self): print('Initializing: class A') def sub_method(self, b): print('sub_method from class A:', b) class B(A): def __init__(self): print('Initializing: class B') super().__init__() def sub_method(self, b): print('sub_method from class B:', b) super().sub_method(b + 1) class X(B): def __init__(self): print('Initializing: class X') super().__init__() def sub_method(self, b): print('sub_method from class X:', b) super().sub_method(b + 1) class Y(X): def __init__(self): print('Initializing: class Y') # super() с параметрами super(X, self).__init__() def sub_method(self, b): print('sub_method from class Y:', b) super().sub_method(b + 1) x = X() x.sub_method(1) print('Обратите внимание как происходит инициализация') print('классов при указании аргументов в функции super()') y = Y() y.sub_method(5) # Вывод # Initializing: class X # Initializing: class B # Initializing: class A # sub_method from class X: 1 # sub_method from class B: 2 # sub_method from class A: 3 # обратите внимание как происходит инициализация # классов при указании аргументов в функции super() # Initializing: class Y # Initializing: class B # Initializing: class A # sub_method from class Y: 5 # sub_method from class X: 6 # sub_method from class B: 7 # sub_method from class A: 8
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Функция abs(), абсолютное значение числа
- Функция all(), все элементы True
- Функция any(), хотя бы один элемент True
- Функция ascii(), преобразует строку в ASCII
- Функция bin(), число в двоичную строку
- Класс bool(), логическое значение объекта
- Функция breakpoint(), отладчик кода
- Класс bytearray(), преобразует в массив байтов
- Класс bytes(), преобразует в строку байтов
- Функция callable(), проверяет можно ли вызвать объект
- Функция chr(), число в символ Юникода
- Класс classmethod, делает функцию методом класса
- Функция compile() компилирует блок кода Python
- Класс complex(), преобразует в комплексное число
- Функция delattr(), удаляет атрибут объекта
- Класс dict() создает словарь
- Функция dir(), все атрибуты объекта
- Функция divmod(), делит числа с остатком
- Функция enumerate(), счетчик элементов последовательности
- Функция eval(), выполняет строку-выражение с кодом
- Функция exec(), выполняет блок кода
- Функция filter(), фильтрует список по условию
- Класс float(), преобразует в вещественное число
- Функция format(), форматирует значение переменной
- Класс frozenset(), преобразует в неизменяемое множество
- Функция getattr(), значение атрибута по имени
- Функция globals(), переменные глобальной области
- Функция hasattr(), наличие атрибута объекта
- Функция hash(), хэш-значение объекта
- Функция help(), справка по любому объекту
- Функция hex(), число в шестнадцатеричную строку
- Функция id(), идентификатор объекта
- Функция input(), ввод данных с клавиатуры
- Класс int(), преобразует в тип int
- Функция isinstance(), принадлежность экземпляра к классу
- Функция issubclass(), проверяет наследование класса
- Функция iter(), создает итератор
- Функция len(), количество элементов объекта
- Класс list(), преобразовывает в список
- Функция locals(), переменные локальной области
- Функция map(), обработка последовательности без цикла
- Функция max(), максимальное значение элемента
- Класс memoryview(), ссылка на буфер обмена
- Функция min(), минимальное значение элемента
- Функция next(), следующий элемент итератора
- Класс object(), возвращает безликий объект
- Функция oct(), число в восьмеричную строку
- Функция open(), открывает файл на чтение/запись
- Функция ord(), число символа Unicode
- Функция pow(), возводит число в степень
- Функция print(), печатает объект
- Класс property(), метод класса как свойство
- Класс range(), генерирует арифметические последовательности
- Функция repr(), описание объекта
- Функция reversed(), разворачивает последовательность
- Функция round(), округляет число
- Класс set(), создает или преобразовывает в множество
- Функция setattr(), создает атрибут объекта
- Класс slice(), шаблон среза
- Функция sorted(), выполняет сортировку
- Декоратор staticmethod(), метод класса в статический метод
- Класс str(), преобразует объект в строку
- Функция sum(), сумма последовательности
- Функция super(), доступ к унаследованным методам
- Класс tuple(), создает или преобразует в кортеж
- Класс type(), возвращает тип объекта
- Функция vars(), словарь переменных объекта
- Функция zip(), объединить элементы в список кортежей
- Функция __import__(), находит и импортирует модуль
- Функция aiter(), создает асинхронный итератор
- Функция anext(), следующий элемент асинхронного итератора
Наследование. Функция 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 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Объясните как работает метод super,а так-же конструкция super().__init__() в python? [закрыт]
Хотите улучшить этот вопрос? Переформулируйте вопрос так, чтобы он был сосредоточен только на одной проблеме.
Закрыт 4 года назад .
Объясните как работает метод super , а так-же конструкция super().__init__() в python? super часто используется в классах. Желательно объяснить как можно проще и на простых примерах.
Отслеживать
66.6k 6 6 золотых знаков 54 54 серебряных знака 113 113 бронзовых знаков
задан 1 июн 2019 в 12:31
366 1 1 золотой знак 4 4 серебряных знака 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__() вызывает метод инициализации из родительского класса. Например, чтобы дополнить его.