Полиморфизм

Модуль 1: Python Core
9 уровень , 7 лекция
Открыта

8.1 Полиморфизм

Полиморфизм — одна из основных концепций объектно-ориентированного программирования (ООП), которая позволяет объектам разных классов использовать один и тот же интерфейс.

В Python полиморфизм достигается через динамическую типизацию и наследование. Важным аспектом полиморфизма является перегрузка методов и замещение методов базового класса методами класса-наследника.

Основные концепции полиморфизма:

Единый интерфейс для различных объектов:

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

Динамическая типизация:

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

Наследование:

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

Примеры полиморфизма:

Простой пример полиморфизма: все три класса имеют метод с именем move(). Это значит, что мы можем написать код, который будет одновременно работать с этими объектами.


class Car:
    def move(self):
        pass
        
class Human:
    def move(self):
        pass
        
class Bird:
    def move(self):
        print("Кар!")
        
car = Car()
human = Human()
bird = Bird()
        
for it in [car, human, bird]:
    it.move()

В данном случае «общим интерфейсом» является имя метода, ну или его сигнатура, если метод имеет параметры.

8.2 Переопределение методов

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


class Employee:
    def get_salary(self):
        return 1000
        
class FullTimeEmployee(Employee):
    def get_salary(self):
        return 5000
        
class PartTimeEmployee(Employee):
    def get_salary(self):
        return 3000
        
class Intern(Employee):
    pass
        
def print_salary(employee):
    print(employee.get_salary())
        
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
        
for employee in employees:
    print_salary(employee)

Этот пример похож на предыдущий, но имеет важное отличие — наши классы не обязаны иметь метод get_salary, ведь он всегда есть у базового класса. Теперь «общим интерфейсом» всех классов является не просто метод get_salary(), а именно класс Employee со всеми его методами и атрибутами.

8.3 Вызов методов класса-наследника

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

Мы вызываем метод print_salary(), который есть только у базового класса Employee, а он уже из базового класса вызывает другой метод базового класса — get_salary(). Так какие зарплаты будут выведены на экран?


class Employee:
    def print_salary(self):
        salary = self.get_salary()
        print(salary)
        
    def get_salary(self):
        return 1000
        
class FullTimeEmployee(Employee):
    def get_salary(self):
        return 5000
        
class PartTimeEmployee(Employee):
    def get_salary(self):
        return 3000
        
class Intern(Employee):
    pass
        
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
        
for employee in employees:
    employee.print_salary()

Важно! Прочитайте то, что написано ниже — это важно.

Если мы вызовем метод print_salary(), например, у класса FullTimeEmployee, то вызовется метод базового класса, так как у самого класса такой метод не объявлен.

Но вот этот метод print_salary() будет вызывать метод get_salary() у объекта self, который имеет тип FullTimeEmployee, а не Employee. Поэтому вызовется именно метод get_salary() из класса FullTimeEmployee!

Более того, класс FullTimeEmployee может вызвать метод get_salary() базового класса для своих нужд. Например, мы хотим рассчитывать зарплату в процентах от базовой ставки:


class FullTimeEmployee(Employee):
    def get_salary(self):
        base_salary = super().get_salary()
        return base_salary * 5  
      
  
      
  
      
  
# 500%
class PartTimeEmployee(Employee): def get_salary(self): base_salary = super().get_salary() return base_salary * 3 # 300%

8.4 Перегрузка методов

Перегрузка методов (method overloading) в Python — это способность создавать несколько методов с одинаковым именем, но разными параметрами. Однако, в чистом виде перегрузка методов не поддерживается в Python, как это делается в других языках (например, C++ или Java).

В Python можно имитировать перегрузку методов через использование аргументов по умолчанию, *args и **kwargs.

Примеры перегрузки методов

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


class Example:
    def display(self, a=None, b=None):
        if a is not None and b is not None: print(a, b)
        elif a is not None: print(a)
        else: print("No arguments")
        

obj = Example()
obj.display(1, 2)  # Output: 1 2
obj.display(1)  # Output: 1
obj.display()  # Output: No arguments

Также можно учесть тип переданных данных — просто сделать проверку типа:


class Example:
    def mod(self, a, b):
        if type(a) == int and type(b) == int: print(a % b)
        elif type(a) == float or type(b) == float: print(round(a / b))
        else: print("Help instruction: a and b should be int or float")
        

obj = Example()
obj.mod(5, 2)  # Output: 1
obj.mod(5.0, 2)  # Output: 2
obj.mod("5", 2)  # Output: Help instruction: a and b should be int or float

Если переданы неправильные типы данных, можно вывести инструкцию или даже ссылку на документацию в интернете — это тоже очень популярное решение.

2
Задача
Модуль 1: Python Core, 9 уровень, 7 лекция
Недоступна
Полиморфизм.
Полиморфизм.
2
Задача
Модуль 1: Python Core, 9 уровень, 7 лекция
Недоступна
Животный мир.
Животный мир.
Комментарии (11)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Анатолий Уровень 29
3 февраля 2026
❤️
SWK Уровень 26
7 апреля 2025
Кто найдёт хотя бы 3 отличия второй задачи ("Животный мир") от задачи "Рычащие" из предыдущей темы?
Анатолий Уровень 29
3 февраля 2026
- Cat - make_sound - "Ууууууу!"
SWK Уровень 26
7 апреля 2025
Кто найдёт хотя бы 2 отличия первой задачи ("Полиморфизм") от задачи "Фигуры" из темы "Наследование"? (15 уровень, 6 лекция)
SWK Уровень 26
7 апреля 2025
А почему бы нижеприведённый пример из лекции не отформатировать нормально?

class FullTimeEmployee(Employee):
    def get_salary(self):
        base_salary = super().get_salary()
        return base_salary * 5

  

    # 500%
  


class PartTimeEmployee(Employee):
    def get_salary(self):
        base_salary = super().get_salary()
        return base_salary * 3  # 300%
Dmitry Ryabov Уровень 23
4 февраля 2025
В общем получается, исходя из этой лекции, что в питоне нет перегрузки методов. Можно лишь её попытаться имитировать.
Anonymous #2923722 Уровень 21
5 февраля 2025
после с++ так и есть
Олексій Єрмак Уровень 25
9 февраля 2025
Может сделают в будущем перегрузку в питоне, например, как в джаве, язык развивается и с каждой новой версие появляется что-то новое.
SerGo Уровень 25
23 февраля 2025
Можно эмулировать перегрузку с помощью аргументов по умолчанию, *args, **kwargs или декораторов
SWK Уровень 26
7 апреля 2025
В пайтоне динамическая типизация, поэтому понятие "перегрузка методов" к нему, вообще, слабо применимо. Передаёшь типы какие угодно в любом количестве - ну анализируй, что пришло, и действуй соответственно.
Vasyl Tverdokhlib Уровень 22
17 августа 2025
можно сделать, если засунуть под капот строгую типизацию, что бы var:int выдавала ошибку при неправильном присвоении, но тогда теряется весь смысл языка