Как вызвать метод из класса 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()
Нужно явно вызывать конструктор базового класса
class A(object): def __init__(self, x=5): print('A.__init__') self.x = x class B(A): def __init__(self, y=2): print('B.__init__') self.y = y k = B(7) # B.__init__ print('k.y =', k.y) # k.y = 7 #print('k.x =', k.x) # AttributeError: 'B' object has no attribute 'x'
Видно, что без явного вызова конструктора класса А не вызывается A.__init__ и не создается поле x класса А.
Вызовем конструктор явно.
Конструктор базового класса стоит вызывать раньше, чем иницилизировать поля класса-наследника, потому что поля наследника могут зависеть (быть сделаны из) полей экземпляра базового класса.
class A(object): def __init__(self, x=5): print('A.__init__') self.x = x class B(A): def __init__(self, y=2): print('B.__init__') super().__init__(y/2) self.y = y k = B(7) # B.__init__ # A.__init__ print('k.y =', k.y) # k.y = 7 print('k.x =', k.x) # k.x = 3.5
super() или прямое обращение к классу?
Метод класса можно вызвать, используя синтаксис вызова через имя класса:
class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): Base.__init__(self) print('A.__init__') k = A() # Base.__init__ # A.__init__
Все работает. Но при дальнейшем развитии классов могут начаться проблемы:
class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): Base.__init__(self) print('A.__init__') class B(Base): def __init__(self): Base.__init__(self) print('B.__init__') class C(A, B): def __init__(self): A.__init__(self) B.__init__(self) print('C.__init__') x = C() # Base.__init__ # A.__init__ # Base.__init__ - второй вызов # B.__init__ # C.__init__
Видно, что конструктор Base.__init__ вызывается дважды. Иногда это недопустимо (считаем количество созданных экземпляров класса, увеличивая в конструкторе счетчик на 1; выдаем очередное auto id какому-то нашему объекту, например, номер пропуска или паспорта или номер заказа).
То же самое через super():
- вызов конструктора Base.__init__ происходит только 1 раз.
- вызваны конструкторы всех базовых классов.
- порядок вызова конструкторов для классов А и В не определен.
class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): super().__init__() print('A.__init__') class B(Base): def __init__(self): super().__init__() print('B.__init__') class C(A, B): def __init__(self): super().__init__() print('C.__init__') x = C() # Base.__init__ # B.__init__ - вызваны конструкторы обоих базовых классов # A.__init__ - порядок вызова # C.__init__
Как это работает?
Для реализации наследования питон ищет вызванный атрибут начиная с первого класса до последнего. Этот список создается слиянием (merge sort) списков базовых классов:
- дети проверяются раньше родителей.
- если родителей несколько, то проверяем в том порядке, в котором они перечислены.
- если подходят несколько классов, то выбираем первого родителя.
При вызове super() продолжается поиск, начиная со следующего имени в MRO. Пока каждый переопределенный метод вызывает super() и вызывает его только один раз, будет перебран весь список MRO и каждый метод будет вызван только один раз.
Не забываем вызывать метод суперкласса
А если где-то не вызван метод суперкласса?
class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): #super().__init__() - НЕ вызываем super() print('A.__init__') class B(Base): def __init__(self): super().__init__() print('B.__init__') class C(A, B): def __init__(self): super().__init__() print('C.__init__') x = C() # A.__init__ # C.__init__ print(C.__mro__) # (, , , , )
Заметим, что хотя в B.__init__ есть вызов super(), то до вызова B.__init__ не доходит.
- Вызываем у объекта класса С метод __init__.
- Ищем его в mro и находим С.__init__. Выполняем его.
- В этом методе вызов super() — ищем метод __init__ далее по списку от найденного.
- Находим A.__init__. Выполняем его. В нем нет никаких super() — дальнейший поиск по mro прекращается.
Нет метода в своем базовом классе, есть у родителя моего сиблинга
Определим класс, который пытается вызвать метод, которого нет в базовом классе:
class A(object): def spam(self): print('A.spam') super().spam() x = A() x.spam()
получим, как и ожидалось:
A.spam Traceback (most recent call last): File "examples/oop_super_3.py", line 7, in x.spam() File "examples/oop_super_3.py", line 4, in spam super().spam() AttributeError: 'super' object has no attribute 'spam'
Определим метод spam в классе В. Класс С, наследник А и В, вызывает метод A.spam(), который вызывает B.spam — класс В не связан с классом А.
class A(object): def spam(self): print('A.spam') super().spam() class B(object): def spam(self): print('B.spam') class C(A, B): pass y = C() y.spam() print(C.__mro__)
A.spam B.spam (, , , )
Для объекта класса С вызвали метод spam(). Ищем его в MRO. Находим A.spam() и вызываем. Далее для super() из A.spam() идем дальше от найденного по списку mro и находим B.spam().
Отметим, что при другом порядке описания родителей class C(B, A) , вызывается метод B.spam() у которого нет super():
B.spam (, , , )
Вызываем метод spam для объекта класса С. В С его нет, ищем дальше в В. Находим. Вызваем. Далее super() нет и дальнейший поиск не производится.
Чтобы не было таких сюрпризов при переопределении методов придерживайтесь правил:
- все методы в иерархии с одинаковым именем имеют одинаковую сигнатуру вызова (количество аргументов и их имена для именованных аргументов).
- реализуйте метод в самом базовом классе, чтобы цепочка вызовов закончилась хоть каким
Обращение к дедушке
Игнорируем родителя
Если у нас есть 3 одинаковых метода foo(self) в наследуемых классах А, В(А), C(B), и нужно из C.foo() вызвать сразу A.foo() минуя B.foo(), то наши классы неправильно сконструированы (почему нужно игнорировать В? может, нужно было наследовать С от А, а не от В?). Нужен рефакторинг.
Но можно всегда вызвать метод по имени класса:
class A(object): def spam(self): print('A.spam') class B(A): def spam(self): print('B.spam') class C(B): def spam(self): A.spam(self) print('C.spam') y = C() y.spam() print(C.__mro__)
A.spam C.spam (, , , )
Метод определен только у дедушки
Если в В такого метода нет, и из C.foo() нужно вызвать A.foo() (или в базовом классе выше по иерархии), вызываем super().foo() и больше не думаем, у какого пра-пра-пра-дедушки реализован этот метод.
Просто воспользуйтесь super() для поиска по mro.
class A(object): def spam(self): print('A.spam') class B(A): pass class C(B): def spam(self): super().spam() print('C.spam') y = C() y.spam() print(C.__mro__)
A.spam C.spam (, , , )
super().super() не работает
Или мы ищем какого-то родителя в mro, или точно указываем из какого класса нужно вызвать метод.
Литература
- Документация по python
- Python’s super() considered super!
- Python Cookbook, chapter 8.7 Calling a Method on a Parent Class
Методы классов. Параметр self
Мы продолжаем изучать ООП языка Python. Как я говорил на первом занятии, класс может содержать свойства (данные) и методы (функции). Благодаря методам внутри класса можно реализовывать самые разные алгоритмы, то есть методы – это действия. Именно поэтому, в названиях методов используют глаголы, например:
set_value, get_param, start, stop, и т.п.
В то время как именами свойств (данных) выступают существительные:
color, size, x, y, и т.п.
Рекомендуется придерживаться этого простого правила.
Давайте, для примера объявим метод set_coords в классе Point, который будет просто выводить в консоль сообщение «вызов метода set_coords»:
class Point: color = 'red' circle = 2 def set_coords(self): print("вызов метода set_coords")
Здесь сразу бросается в глаза вот этот параметр self, который автоматически прописывает интегрированная среда. Зачем он здесь, если мы пока ничего не собираемся передавать этому методу? Давайте его уберем! Пока никаких проблем не возникло. Мало того, мы можем его вызвать из класса Point:
Point.set_coords()
и все будет работать без ошибок. Здесь мы видим, как вызываются методы класса. Все довольно очевидно. Записываем имя класса (Point), и через точку указываем имя метода. В конце обязательно прописываем круглые скобки, так как это оператор вызова функций. И, так как метод – это функция класса, то для вызова метода используется тот же оператор, что и для вызова функций.
В результате, мы получили класс, в котором два свойства и один метод. Далее, создадим экземпляр этого класса:
pt = Point()
И, как мы с вами говорили, через объект pt можно обращаться ко всем атрибутам класса Point, в том числе и к методу set_coords:
pt.set_coords
Этот атрибут ссылается на объект-функцию, которую мы определили в классе Point. Попробуем ее вызвать:
pt.set_coords()
Видим ошибку, что в метод set_coords при вызове передается один аргумент, а он у нас определен без параметров. Дело в том, что когда мы вызываем методы класса через его объекты, то интерпретатор Python автоматически добавляет первым аргументом ссылку на объект, из которого этот метод вызывается.

Поэтому, если мы хотим внутри класса определить метод, который можно было бы вызывать из его экземпляров, то дополнительно прописывается первый параметр, обычно, с именем self:
class Point: color = 'red' circle = 2 def set_coords(self): print("вызов метода set_coords " + str(self))
Еще раз, параметр self будет ссылаться на экземпляр класса, из которого вызывается метод. Зачем это надо? Сейчас узнаете. После этого дополнения мы уже не сможем вызвать данный метод через класс без указания первого аргумента:
Point.set_coords()
но можем через его объекты:
pt.set_coords()
То есть, когда метод вызывается через класс, то Python автоматически не подставляет никаких аргументов. А когда вызов идет через экземпляры класса, то первый аргумент – это всегда ссылка на экземпляр. Данный момент нужно знать и помнить.
Но мы все же можем вызвать метод set_coords и через класс, если явно передадим ссылку на объект pt, следующим образом:
Point.set_coords(pt)
Именно это на автомате делает Python, когда вызов осуществляется через объекты классов.
Так зачем понадобилось такое поведение? Дело в том, что метод класса – это тоже его атрибут и когда создаются экземпляры класса, то метод становится общим для всех объектов и не копируется в них. Фактически, только благодаря параметру self мы «знаем» какой объект вызвал данный метод и можем организовать с ним обратную связь.
Например, пусть метод set_coords задает координаты точек для текущего объекта. Тогда, мы пропишем в нем два дополнительных параметра и через self в самом экземпляре класса создадим (либо переопределим) два свойства:
class Point: color = 'red' circle = 2 def set_coords(self, x, y): self.x = x self.y = y
В результате, при вызове метода:
pt.set_coords(1, 2) print(pt.__dict__)
в объекте pt будут созданы два свойства x, y со значениями 1 и 2. Вот для чего нужен этот параметр self. Если в программе создать еще один объект:
pt2 = Point()
и через него вызвать тот же самый метод:
pt2.set_coords(10, 20) print(pt2.__dict__)
То увидим, что свойства x, y со значениями 10 и 20 были созданы только в нем (в его пространстве имен) и никак не связаны с координатами другого объекта pt или классом Point. То есть, через self мы работаем с конкретным объектом, из которого был вызван данный метод.
Конечно, в классах мы можем прописывать произвольное количество методов. Например, определим еще один, который будет возвращать координаты точки в виде кортежа значений:
class Point: color = 'red' circle = 2 def set_coords(self, x, y): self.x = x self.y = y def get_coords(self): return (self.x, self.y)
И ниже в программе можем вызвать его:
print(pt.get_coords())
Интересно, что так как имя метода – это атрибут класса, то мы можем обратиться к нему через знакомую нам уже функцию:
res = getattr(pt, 'get_coords') print(res)
Видим, что это ссылка на объект-функцию. А раз так, то ничто нам не мешает ее здесь вызывать:
print(res())
Конечно, так делают очень редко. Обычно используют синтаксис через точку. Я привел это, чтобы еще раз подчеркнуть, что имена методов – это те же самые атрибуты, просто они ведут не на данные, а на функции. Во всем остальном они схожи с атрибутами-данными класса.
Заключение
Итак, на этом занятии вы должны были узнать, как определяются простые методы класса, за что отвечает параметр self и как происходит обращение к методам и их вызов. Если все это понятно, то смело переходите к следующему занятию, где мы продолжим эту тему.
Видео по теме

Концепция ООП простыми словами

#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 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта
Как вызвать метод класса python?
Метод класса можно вызвать как функцию, указав его название через точку после названия класса:
class MyClass: def my_method(a, b): return a + b MyClass.my_method # MyClass.my_method(3, 5) # 8
