Полиморфизм - третий основной принцип объектно-ориентированного программирования, который позволяет объектам разных типов использовать один и тот же интерфейс. Слово "полиморфизм" происходит от греческого "много форм" и означает способность принимать множество форм.
Python поддерживает полиморфизм "утиной типизации" - если объект ведет себя как утка (имеет нужные методы), значит это утка:
# Базовый класс
class Животное:
def __init__(self, имя):
self.имя = имя
def издать_звук(self):
pass # Абстрактный метод
def описание(self):
return f"{self.имя} - животное"
# Подклассы
class Собака(Животное):
def издать_звук(self):
return f"{self.имя} говорит: Гав-гав!"
def описание(self):
return f"{self.имя} - собака"
class Кошка(Животное):
def издать_звук(self):
return f"{self.имя} говорит: Мяу-мяу!"
def описание(self):
return f"{self.имя} - кошка"
class Птица(Животное):
def издать_звук(self):
return f"{self.имя} говорит: Чирик-чирик!"
def описание(self):
return f"{self.имя} - птица"
# Функция, использующая полиморфизм
def показать_животное(животное):
print(животное.описание())
print(животное.издать_звук())
print()
# Использование полиморфизма
животные = [
Собака("Бобик"),
Кошка("Мурка"),
Птица("Чижик")
]
for животное in животные:
показать_животное(животное)
Использование абстрактных классов для обеспечения полиморфного поведения:
from abc import ABC, abstractmethod
# Абстрактный базовый класс
class Фигура(ABC):
def __init__(self, название):
self.название = название
@abstractmethod
def площадь(self):
pass
@abstractmethod
def периметр(self):
pass
def описание(self):
return f"Это {self.название}"
# Конкретные реализации
class Прямоугольник(Фигура):
def __init__(self, ширина, высота):
super().__init__("прямоугольник")
self.ширина = ширина
self.высота = высота
def площадь(self):
return self.ширина * self.высота
def периметр(self):
return 2 * (self.ширина + self.высота)
class Круг(Фигура):
def __init__(self, радиус):
super().__init__("круг")
self.радиус = радиус
def площадь(self):
import math
return math.pi * self.радиус ** 2
def периметр(self):
import math
return 2 * math.pi * self.радиус
class Треугольник(Фигура):
def __init__(self, сторона_a, сторона_b, сторона_c):
super().__init__("треугольник")
self.сторона_a = сторона_a
self.сторона_b = сторона_b
self.сторона_c = сторона_c
def площадь(self):
# Формула Герона
p = self.периметр() / 2
return (p * (p - self.сторона_a) * (p - self.сторона_b) * (p - self.сторона_c)) ** 0.5
def периметр(self):
return self.сторона_a + self.сторона_b + self.сторона_c
# Функция, работающая с любыми фигурами
def анализировать_фигуру(фигура):
print(фигура.описание())
print(f"Площадь: {фигура.площадь():.2f}")
print(f"Периметр: {фигура.периметр():.2f}")
print()
# Использование полиморфизма
фигуры = [
Прямоугольник(5, 3),
Круг(4),
Треугольник(3, 4, 5)
]
for фигура in фигуры:
анализировать_фигуру(фигура)
Функции в Python могут работать с объектами разных типов благодаря полиморфизму:
# Функции, демонстрирующие полиморфизм
def длина_объекта(объект):
return len(объект)
def итерировать_объект(объект):
результат = []
for элемент in объект:
результат.append(элемент)
return результат
def строковое_представление(объект):
return str(объект)
# Разные типы объектов
объекты = [
"строка",
[1, 2, 3, 4],
{"ключ": "значение"},
(1, 2, 3),
{1, 2, 3}
]
for объект in объекты:
print(f"Объект: {объект}")
print(f"Тип: {type(объект).__name__}")
print(f"Длина: {длина_объекта(объект)}")
print(f"Элементы: {итерировать_объект(объект)}")
print(f"Строковое представление: {строковое_представление(объект)}")
print()
Python позволяет переопределять поведение операторов для пользовательских классов:
class Вектор:
def __init__(self, x, y):
self.x = x
self.y = y
# Сложение векторов
def __add__(self, другой):
if isinstance(другой, Вектор):
return Вектор(self.x + другой.x, self.y + другой.y)
return NotImplemented
# Умножение вектора на число
def __mul__(self, скаляр):
if isinstance(скаляр, (int, float)):
return Вектор(self.x * скаляр, self.y * скаляр)
return NotImplemented
# Умножение числа на вектор
def __rmul__(self, скаляр):
return self.__mul__(скаляр)
# Строковое представление
def __str__(self):
return f"Вектор({self.x}, {self.y})"
# Представление для отладки
def __repr__(self):
return f"Вектор({self.x}, {self.y})"
# Сравнение векторов
def __eq__(self, другой):
if isinstance(другой, Вектор):
return self.x == другой.x and self.y == другой.y
return False
# Длина вектора
def __abs__(self):
import math
return math.sqrt(self.x ** 2 + self.y ** 2)
# Использование перегруженных операторов
вектор1 = Вектор(3, 4)
вектор2 = Вектор(1, 2)
print(f"Вектор 1: {вектор1}")
print(f"Вектор 2: {вектор2}")
print(f"Сумма: {вектор1 + вектор2}")
print(f"Умножение на 3: {вектор1 * 3}")
print(f"Умножение 2 на вектор: {2 * вектор2}")
print(f"Равны ли векторы: {вектор1 == вектор2}")
print(f"Длина вектора 1: {abs(вектор1):.2f}")
Исключения в Python также демонстрируют полиморфное поведение:
# Функция, которая может вызывать разные исключения
def обработать_данные(данные):
try:
if isinstance(данные, str):
if not данные:
raise ValueError("Строка не должна быть пустой")
return len(данные)
elif isinstance(данные, list):
if not данные:
raise IndexError("Список не должен быть пустым")
return sum(данные)
elif isinstance(данные, dict):
if not данные:
raise KeyError("Словарь не должен быть пустым")
return len(данные)
else:
raise TypeError(f"Неподдерживаемый тип данных: {type(данные)}")
except (ValueError, IndexError, KeyError, TypeError) as e:
print(f"Ошибка: {e}")
return None
# Тестирование с разными типами данных
тестовые_данные = [
"Привет, мир!",
"",
[1, 2, 3, 4, 5],
[],
{"ключ1": "значение1", "ключ2": "значение2"},
{},
42
]
for данные in тестовые_данные:
print(f"Обработка: {данные} (тип: {type(данные).__name__})")
результат = обработать_данные(данные)
if результат is not None:
print(f"Результат: {результат}")
print()
Рассмотрим практические примеры использования полиморфизма:
from abc import ABC, abstractmethod
# Абстрактный базовый класс для платежных систем
class ПлатежнаяСистема(ABC):
def __init__(self, имя):
self.имя = имя
@abstractmethod
def обработать_платеж(self, сумма):
pass
@abstractmethod
def получить_комиссию(self, сумма):
pass
# Конкретные реализации
class КредитнаяКарта(ПлатежнаяСистема):
def __init__(self, имя, номер_карты):
super().__init__(имя)
self.номер_карты = номер_карты
def обработать_платеж(self, сумма):
комиссия = self.получить_комиссию(сумма)
итоговая_сумма = сумма + комиссия
return f"Платеж {сумма} через кредитную карту обработан. Комиссия: {комиссия}. Итого: {итоговая_сумма}"
def получить_комиссию(self, сумма):
return сумма * 0.02 # 2% комиссия
class PayPal(ПлатежнаяСистема):
def __init__(self, имя, email):
super().__init__(имя)
self.email = email
def обработать_платеж(self, сумма):
комиссия = self.получить_комиссию(сумма)
итоговая_сумма = сумма + комиссия
return f"Платеж {сумма} через PayPal обработан. Комиссия: {комиссия}. Итого: {итоговая_сумма}"
def получить_комиссию(self, сумма):
return сумма * 0.03 # 3% комиссия
class БанковскийПеревод(ПлатежнаяСистема):
def __init__(self, имя, номер_счета):
super().__init__(имя)
self.номер_счета = номер_счета
def обработать_платеж(self, сумма):
комиссия = self.получить_комиссию(сумма)
итоговая_сумма = сумма + комиссия
return f"Платеж {сумма} банковским переводом обработан. Комиссия: {комиссия}. Итого: {итоговая_сумма}"
def получить_комиссию(self, сумма):
return max(50, сумма * 0.01) # Минимум 50 или 1%
# Система обработки платежей
class ПлатежныйПроцессор:
def __init__(self):
self.платежные_системы = []
def добавить_систему(self, система):
self.платежные_системы.append(система)
def обработать_платежи(self, сумма):
print(f"Обработка платежа на сумму {сумма} через все доступные системы:")
for система in self.платежные_системы:
print(система.обработать_платеж(сумма))
print()
# Использование полиморфизма в системе платежей
процессор = ПлатежныйПроцессор()
процессор.добавить_систему(КредитнаяКарта("Visa", "****1234"))
процессор.добавить_систему(PayPal("PayPal", "user@example.com"))
процессор.добавить_систему(БанковскийПеревод("SWIFT", "SWIFT123456789"))
процессор.обработать_платежи(1000)
Задание 1:
from abc import ABC, abstractmethod
class Медиа(ABC):
def __init__(self, название, длительность=0):
self.название = название
self.длительность = длительность
@abstractmethod
def воспроизвести(self):
pass
@abstractmethod
def пауза(self):
pass
@abstractmethod
def остановить(self):
pass
class Аудио(Медиа):
def __init__(self, название, исполнитель, длительность):
super().__init__(название, длительность)
self.исполнитель = исполнитель
def воспроизвести(self):
return f"Воспроизводится аудио '{self.название}' от {self.исполнитель}"
def пауза(self):
return f"Аудио '{self.название}' поставлено на паузу"
def остановить(self):
return f"Аудио '{self.название}' остановлено"
class Видео(Медиа):
def __init__(self, название, режиссер, длительность):
super().__init__(название, длительность)
self.режиссер = режиссер
def воспроизвести(self):
return f"Воспроизводится видео '{self.название}' от режиссера {self.режиссер}"
def пауза(self):
return f"Видео '{self.название}' поставлено на паузу"
def остановить(self):
return f"Видео '{self.название}' остановлено"
class Изображение(Медиа):
def __init__(self, название, художник):
super().__init__(название)
self.художник = художник
def воспроизвести(self):
return f"Отображается изображение '{self.название}' от художника {self.художник}"
def пауза(self):
return "Изображения не поддерживают паузу"
def остановить(self):
return f"Изображение '{self.название}' скрыто"
# Пример использования
медиа_файлы = [
Аудио("Bohemian Rhapsody", "Queen", 354),
Видео("Inception", "Christopher Nolan", 8880),
Изображение("Mona Lisa", "Leonardo da Vinci")
]
for медиа in медиа_файлы:
print(медиа.воспроизвести())
print(медиа.пауза())
print(медиа.остановить())
print()
Задание 2:
from abc import ABC, abstractmethod
import math
class ГеометрическоеТело(ABC):
def __init__(self, название):
self.название = название
@abstractmethod
def объем(self):
pass
@abstractmethod
def площадь_поверхности(self):
pass
class Куб(ГеометрическоеТело):
def __init__(self, сторона):
super().__init__("куб")
self.сторона = сторона
def объем(self):
return self.сторона ** 3
def площадь_поверхности(self):
return 6 * self.сторона ** 2
class Шар(ГеометрическоеТело):
def __init__(self, радиус):
super().__init__("шар")
self.радиус = радиус
def объем(self):
return (4/3) * math.pi * self.радиус ** 3
def площадь_поверхности(self):
return 4 * math.pi * self.радиус ** 2
class Цилиндр(ГеометрическоеТело):
def __init__(self, радиус, высота):
super().__init__("цилиндр")
self.радиус = радиус
self.высота = высота
def объем(self):
return math.pi * self.радиус ** 2 * self.высота
def площадь_поверхности(self):
return 2 * math.pi * self.радиус * (self.радиус + self.высота)
# Функция для анализа тел
def анализировать_тело(тело):
print(f"Анализ {тела.название}а:")
print(f"Объем: {тело.объем():.2f}")
print(f"Площадь поверхности: {тело.площадь_поверхности():.2f}")
print()
# Пример использования
тела = [
Куб(3),
Шар(2),
Цилиндр(2, 5)
]
for тело in тела:
анализировать_тело(тело)
Задание 3:
from abc import ABC, abstractmethod
class ТранспортноеСредство(ABC):
def __init__(self, марка, модель):
self.марка = марка
self.модель = модель
@abstractmethod
def двигаться(self):
pass
@abstractmethod
def максимальная_скорость(self):
pass
def информация(self):
return f"{self.марка} {self.модель}"
class Автомобиль(ТранспортноеСредство):
def __init__(self, марка, модель, тип_двигателя):
super().__init__(марка, модель)
self.тип_двигателя = тип_двигателя
def двигаться(self):
return f"{self.информация()} едет по дороге"
def максимальная_скорость(self):
return 200 # км/ч
class Самолет(ТранспортноеСредство):
def __init__(self, марка, модель, тип_крыла):
super().__init__(марка, модель)
self.тип_крыла = тип_крыла
def двигаться(self):
return f"{self.информация()} летит в воздухе"
def максимальная_скорость(self):
return 900 # км/ч
class Корабль(ТранспортноеСредство):
def __init__(self, марка, модель, тип_корпуса):
super().__init__(марка, модель)
self.тип_корпуса = тип_корпуса
def двигаться(self):
return f"{self.информация()} плывет по воде"
def максимальная_скорость(self):
return 50 # узлов
# Функция для демонстрации полиморфизма
def демонстрация_движения(транспорт):
print(транспорт.двигаться())
print(f"Максимальная скорость: {транспорт.максимальная_скорость()} км/ч")
print()
# Пример использования
транспортные_средства = [
Автомобиль("Toyota", "Camry", "бензиновый"),
Самолет("Boeing", "747", "крыло переменной стреловидности"),
Корабль("Queen Mary 2", "лайнер", "стальной")
]
for транспорт in транспортные_средства:
демонстрация_движения(транспорт)
Задание 4:
from abc import ABC, abstractmethod
import math
class Фигура(ABC):
def __init__(self, название):
self.название = название
@abstractmethod
def площадь(self):
pass
def __str__(self):
return f"{self.название} (площадь: {self.площадь():.2f})"
# Перегрузка оператора сложения
def __add__(self, другая):
if isinstance(другая, Фигура):
общая_площадь = self.площадь() + другая.площадь()
return СоставнаяФигура([self, другая], общая_площадь)
return NotImplemented
# Перегрузка операторов сравнения
def __lt__(self, другая):
if isinstance(другая, Фигура):
return self.площадь() < другая.площадь()
return NotImplemented
def __le__(self, другая):
if isinstance(другая, Фигура):
return self.площадь() <= другая.площадь()
return NotImplemented
def __eq__(self, другая):
if isinstance(другая, Фигура):
return self.площадь() == другая.площадь()
return NotImplemented
def __ne__(self, другая):
if isinstance(другая, Фигура):
return self.площадь() != другая.площадь()
return NotImplemented
def __gt__(self, другая):
if isinstance(другая, Фигура):
return self.площадь() > другая.площадь()
return NotImplemented
def __ge__(self, другая):
if isinstance(другая, Фигура):
return self.площадь() >= другая.площадь()
return NotImplemented
class Прямоугольник(Фигура):
def __init__(self, ширина, высота):
super().__init__("прямоугольник")
self.ширина = ширина
self.высота = высота
def площадь(self):
return self.ширина * self.высота
class Круг(Фигура):
def __init__(self, радиус):
super().__init__("круг")
self.радиус = радиус
def площадь(self):
return math.pi * self.радиус ** 2
class СоставнаяФигура(Фигура):
def __init__(self, фигуры, общая_площадь):
super().__init__("составная фигура")
self.фигуры = фигуры
self._общая_площадь = общая_площадь
def площадь(self):
return self._общая_площадь
# Пример использования
прямоугольник = Прямоугольник(4, 5)
круг = Круг(3)
print(f"Фигура 1: {прямоугольник}")
print(f"Фигура 2: {круг}")
# Сложение фигур
составная = прямоугольник + круг
print(f"Составная фигура: {составная}")
# Сравнение фигур
print(f"Прямоугольник < Круг: {прямоугольник < круг}")
print(f"Прямоугольник > Круг: {прямоугольник > круг}")
print(f"Прямоугольник == Круг: {прямоугольник == круг}")
# Сортировка фигур по площади
фигуры = [Прямоугольник(3, 4), Круг(2), Прямоугольник(5, 2), Круг(3)]
print("\nФигуры до сортировки:")
for фигура in фигуры:
print(фигура)
print("\nФигуры после сортировки по площади:")
фигуры.sort()
for фигура in фигуры:
print(фигура)