Что такое полиморфизм?
В программировании полиморфизм это способность одного фрагмента кода, например вызова функции или имени метода, работать со значениями разных типов, где каждый тип задаёт своё поведение. Вызов shape.area() выполняет для круга другое вычисление, чем для квадрата.
Обновлено 24 сентября 2026 г.
В Python len("hello") возвращает 5, len([4, 8, 15]) возвращает 3, а len({"a": 1}) возвращает 1. Это одно имя функции для трёх типов, и каждый тип считает по-своему: строка считает символы, список элементы, словарь ключи. Слово пришло из греческого: poly, много, и morph, форма. Один вызов, много форм поведения.
Как работает полиморфизм
Код вызывает метод по имени, например shape.area(). Когда эта строка выполняется, язык смотрит на фактический объект и находит area в классе самого объекта. Такой поиск называется динамической диспетчеризацией, и именно он позволяет одной строке выполнять разную работу для разных объектов:
Circle 3.14
Square 9
Circle 12.57
Python ищет метод сначала в классе объекта, потом в его родительских классах в определённом порядке, который называется порядком разрешения методов (MRO). Если бы у Square не было своего area(), поиск дошёл бы до Shape.area() и выбросил бы NotImplementedError, сигнал о том, что каждый подкласс должен реализовать этот метод.
Цикл никогда не проверяет, какая фигура у него в руках. Чтобы добавить треугольник, ты пишешь класс Triangle с собственным area(), и цикл работает без изменений. Без полиморфизма циклу понадобилась бы ветка if isinstance(...) для каждой фигуры, и её пришлось бы править всякий раз, когда появляется новая.
Виды полиморфизма
| Вид | Другие названия | Как выглядит | Когда выбирается версия |
|---|---|---|---|
| Полиморфизм подтипов | Динамический полиморфизм, переопределение методов | Подкласс заменяет метод родителя | Во время работы программы |
| Ad hoc | Перегрузка | Одно имя, несколько версий для разных типов параметров; сюда же перегрузка операторов | Во время компиляции в Java и C++ |
| Параметрический | Обобщения (generics), шаблоны | Один фрагмент кода, который работает с любым типом: List<T> в Java, vector<T> в C++ | Во время компиляции |
Когда в объектно-ориентированном программировании говорят «полиморфизм» без уточнений, почти всегда имеют в виду первую строку: полиморфизм подтипов через переопределение.
В Python есть все три, каждый на свой лад. Переопределение работает, как в примере с фигурами. Операторы вроде + это ad hoc полиморфизм. Функции вроде max() и sorted() параметрические по духу: они принимают список чисел, строк или дат, лишь бы элементы можно было сравнивать, а аннотации типов вроде list[int] или TypeVar позволяют указать это явно.
Статический и динамический полиморфизм в Java
В Java есть оба вида, и на собеседованиях часто спрашивают о разнице:
class Animal { String sound() { return "..."; } }
class Dog extends Animal {
@Override String sound() { return "Woof"; }
}
Animal pet = new Dog();
pet.sound(); // "Woof": chosen at run time from the object
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
add(2, 3); // int version, chosen by the compiler
add(2.5, 1.0); // double version
Переменная pet имеет тип Animal, но выполняется версия Dog, потому что выбор зависит от объекта, а не от переменной. Два метода add это перегрузка: компилятор выбирает один из них по типам аргументов ещё до запуска программы. Интерфейсы дают то же поведение во время выполнения без общего кода: любой класс, реализующий Comparable, можно отсортировать с помощью Collections.sort. C++ работает так же с одним отличием: метод получает динамическую диспетчеризацию, только если он объявлен как virtual. Смотри полиморфизм в Java, перегрузку методов и виртуальные функции C++.
Полиморфизм в Python: утиная типизация и операторы
Python не требует общего родительского класса. Если у объекта есть метод, который вызывает код, всё работает: «если что-то ходит как утка и крякает как утка, это утка». Операторы тоже полиморфны. Запись a + b вызывает a.__add__(b), поэтому твои собственные классы могут поддерживать +, len() и print():
3 abcd [1, 2]
Vector(11, 22)
5 3 2
Наследование это один из способов получить полиморфизм, а не обязательное условие. Vector ни от чего особенного не наследуется, но len() и + обращаются с ним как со встроенным типом. Документация по наследованию в Python показывает путь через подклассы.
Частые ошибки
Ожидание, что Python перегружает функции по сигнатуре. Второй def с тем же именем молча заменяет первый:
Hi Ana Silva
Traceback (most recent call last):
...
TypeError: greet() missing 1 required positional argument: 'last'
Вместо этого используй аргумент по умолчанию: def greet(first, last="").
Забытый virtual в C++. Без него вызов метода через указатель на базовый класс выполняет базовую версию, даже если объект это подкласс.
Переопределения, которые меняют смысл. Если area() в одном подклассе возвращает периметр, ломается каждый вызывающий код, который доверяет area(). Переопределение должно выполнять обещание, данное родителем; это правило известно как принцип подстановки Лисков.
Проверки типов повсюду. Цепочка веток if type(x) == ... обычно говорит о том, что у каждого типа должен быть свой метод.
Что читать дальше
Полиморфизм работает вместе с инкапсуляцией, которая защищает данные каждого объекта, и абстракцией, которая задаёт общий интерфейс, реализуемый типами. Потренируйся в переопределении по документации по классам Python или попробуй курс Java, где перегрузка и переопределение встречаются бок о бок.
Часто задаваемые вопросы
Какой пример полиморфизма самый простой?
+ в Python: 1 + 2 складывает числа, "ab" + "cd" соединяет строки, а [1] + [2] соединяет списки. Один и тот же символ работает с тремя типами, и каждый тип сам решает, что он означает. В объектно-ориентированном коде классический пример это список фигур, где каждая отвечает на area() по своей формуле.Чем полиморфизм отличается от наследования?
Что такое полиморфизм в биологии?
Поддерживает ли Python перегрузку методов?
def с тем же именем заменяет первый. В коде на Python тот же эффект получают с помощью аргументов по умолчанию, *args или functools.singledispatch, который выбирает функцию по типу первого аргумента.Зачем нужен полиморфизм?
shape.draw() для каждой фигуры, не нужны правки, когда ты добавляешь класс треугольника; пишется только новый класс. Без полиморфизма в этом цикле появлялась бы новая ветка if для каждого типа.