Полиморфизм
Полиморфизм в объектно-ориентированном программировании – это возможность обработки разных типов данных, т. е. принадлежащих к разным классам, с помощью «одной и той же» функции, или метода. На самом деле одинаковым является только имя метода, его исходный код зависит от класса. Кроме того, результаты работы одноименных методов могут существенно различаться. Поэтому в данном контексте под полиморфизмом понимается множество форм одного и того же слова – имени метода.
Например, два разных класса содержат метод total , однако инструкции каждого предусматривают совершенно разные операции. Так в примере ниже в классе T1 – это прибавление 10 к аргументу, в T2 – подсчет длины строки символов. В зависимости от того, к объекту какого класса применяется метод total , выполняются те или иные инструкции.
class T1: def __init__(self): self.n = 10 def total(self, a): return self.n + int(a) class T2: def __init__(self): self.string = 'Hi' def total(self, a): return len(self.string + str(a)) t1 = T1() t2 = T2() print(t1.total(35)) # Вывод: 45 print(t2.total(35)) # Вывод: 4
В предыдущем уроке мы уже наблюдали полиморфизм между классами, связанными наследованием. У каждого может быть свой метод __init__() или square() или какой-нибудь другой. Какой именно из методов square() вызывается, и что он делает, зависит от принадлежности объекта к тому или иному классу.
Однако классы не обязательно должны быть связанны наследованием. Полиморфизм как один из ключевых элементов ООП существует независимо от наследования. Классы могут быть не родственными, но иметь одинаковые методы, как в примере выше.
Полиморфизм дает возможность реализовывать так называемые единые интерфейсы для объектов различных классов. Например, разные классы могут предусматривать различный способ вывода той или иной информации объектов. Однако одинаковое название метода вывода позволит не запутать программу, сделать код более ясным.
В Python среди прочего полиморфизм находит отражение в методах перегрузки операторов. Два из них мы уже рассмотрели. Это __init__ и __del__ , которые вызываются при создании объекта и его удалении. Полиморфизм у методов перегрузки операторов проявляется в том, что независимо от типа объекта, его участие в определенной операции, вызывает метод с конкретным именем. В случае __init__ операцией является создание объекта.
Рассмотрим пример полиморфизма на еще одном методе, который перегружает функцию str , которую автоматически вызывает функция print .
Если вы создадите объект собственного класса, а потом попробуете вывести его на экран, то получите информацию о классе объекта и его адрес в памяти. Такое поведение функции str() по-умолчанию по отношению к пользовательским классам запрограммировано на самом верхнем уровне иерархии, где-то в суперклассе, от которого неявно наследуются все остальные.
class A: def __init__(self, v1, v2): self.field1 = v1 self.field2 = v2 a = A(3, 4) b = str(a) print(a) print(b)
Здесь мы используем переменную b , чтобы показать, что функция print() вызывает str() неявным образом, так как вывод значений обоих переменных одинаков.
Если же мы хотим, чтобы, когда объект передается функции print() , выводилась какая-нибудь другая более полезная информация, то в класс надо добавить специальный метод __str__ . Этот метод должен обязательно возвращать строку, которую будет в свою очередь возвращать функция str() , вызываемая функций print() :
class A: def __init__(self, v1, v2): self.field1 = v1 self.field2 = v2 def __str__(self): s = str(self.field1)+" "+str(self.field2) return s a = A(3, 4) b = str(a) print(a) print(b)
3 4 3 4
Какую именно строку возвращает метод __str__() , дело десятое. Он вполне может строить квадратик из символов:
class Rectangle: def __init__(self, width, height, sign): self.w = int(width) self.h = int(height) self.s = str(sign) def __str__(self): rect = [] # количество строк for i in range(self.h): # знак повторяется w раз rect.append(self.s * self.w) # превращаем список в строку rect = '\n'.join(rect) return rect b = Rectangle(10, 3, '*') print(b)
Практическая работа. Метод перегрузки оператора сложения
В качестве практической работы попробуйте самостоятельно перегрузить оператор сложения. Для его перегрузки используется метод __add__ . Он вызывается, когда объекты класса, имеющего данный метод, фигурируют в операции сложения, причем с левой стороны. Это значит, что в выражении a + b у объекта a должен быть метод __add__ . Объект b может быть чем угодно, но чаще всего он бывает объектом того же класса. Объект b будет автоматически передаваться в метод __add__(self, b) в качестве второго аргумента.
Отметим, в Python также есть правосторонний метод перегрузки сложения – __radd__ .
Согласно полиморфизму ООП, возвращать метод __add__ может что угодно. Может вообще ничего не возвращать, а «молча» вносить изменения в какие-то уже существующие объекты. Допустим, в вашей программе метод перегрузки сложения будет возвращать новый объект того же класса.
Курс с примерами решений практических работ:
pdf-версия
X Скрыть Наверх
Объектно-ориентированное программирование на Python
Полиморфизм для начинающих
Полиморфизм — одна из трех основных парадигм ООП. Если говорить кратко, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового. Для тех, кто не особо сведущ в ООП, это, наверно, звучит сложно. Поэтому рассмотрим применение полиморфизма на примере.
Постановка задачи
Предположим, на сайте нужны три вида публикаций — новости, объявления и статьи. В чем-то они похожи — у всех них есть заголовок и текст, у новостей и объявлений есть дата. В чем-то они разные — у статей есть авторы, у новостей — источники, а у объявлений — дата, после которой оно становится не актуальным.
Самые простые варианты, которые приходят в голову — написать три отдельных класса и работать с ними. Или написать один класс, в которым будут все свойства, присущие всем трем типам публикаций, а задействоваться будут только нужные. Но ведь для разных типов аналогичные по логике методы должны работать по-разному. Делать несколько однотипных методов для разных типов (get_news, get_announcements, get_articles) — это уже совсем неграмотно. Тут нам и поможет полиморфизм.
Абстрактный класс
Грубо говоря, это класс-шаблон. Он реализует функциональность только на том уровне, на котором она известна на данный момент. Производные же классы ее дополняют. Но, пора перейти от теории к практике. Сразу оговорюсь, рассматривается примитивный пример с минимальной функциональностью. Все объяснения — в комментариях в коде.
abstract class Publication
// таблица, в которой хранятся данные по элементу
protected $table ;
// свойства элемента нам неизвестны
protected $properties = array();
// конструктор
public function __construct ( $id )
// обратите внимание, мы не знаем, из какой таблицы нам нужно получить данные
$result = mysql_query ( ‘SELECT * FROM `’ . $this -> table . ‘` WHERE `id` #007700″>. $id . ‘» LIMIT 1’ );
// какие мы получили данные, мы тоже не знаем
$this -> properties = mysql_fetch_assoc ( $result );
>
// метод, одинаковый для любого типа публикаций, возвращает значение свойства
public function get_property ( $name )
if (isset( $this -> properties [ $name ]))
return $this -> properties [ $name ];
// метод, одинаковый для любого типа публикаций, устанавливает значение свойства
public function set_property ( $name , $value )
if (!isset( $this -> properties [ $name ]))
return false ;
$this -> properties [ $name ] = $value ;
// а этот метод должен напечатать публикацию, но мы не знаем, как именно это сделать, и потому объявляем его абстрактным
abstract public function do_print ();
>
Производные классы
Теперь можно перейти к созданию производных классов, которые и реализуют недостающую функциональность.
class News extends Publication
// конструктор класса новостей, производного от класса публикаций
public function __construct ( $id )
// устанавливаем значение таблицы, в которой хранятся данные по новостям
$this -> table = ‘news_table’ ;
// вызываем конструктор родительского класса
parent :: __construct ( $id );
>
// переопределяем абстрактный метод печати
public function do_print ()
echo $this -> properties [ ‘title’ ];
echo ‘
‘ ;
echo $this -> properties [ ‘text’ ];
echo ‘
Источник: ‘ . $this -> properties [ ‘source’ ];
>
>
class Announcement extends Publication
// конструктор класса объявлений, производного от класса публикаций
public function __construct ( $id )
// устанавливаем значение таблицы, в которой хранятся данные по объявлениям
$this -> table = ‘announcements_table’ ;
// вызываем конструктор родительского класса
parent :: __construct ( $id );
>
// переопределяем абстрактный метод печати
public function do_print ()
echo $this -> properties [ ‘title’ ];
echo ‘
Внимание! Объявление действительно до ‘ . $this -> properties [ ‘end_date’ ];
echo ‘
‘ . $this -> properties [ ‘text’ ];
>
>
class Article extends Publication
// конструктор класса статей, производного от класса публикаций
public function __construct ( $id )
// устанавливаем значение таблицы, в которой хранятся данные по статьям
$this -> table = ‘articles_table’ ;
// вызываем конструктор родительского класса
parent :: __construct ( $id );
>
// переопределяем абстрактный метод печати
public function do_print ()
echo $this -> properties [ ‘title’ ];
echo ‘
‘ ;
echo $this -> properties [ ‘text’ ];
echo ‘
© ‘ . $this -> properties [ ‘author’ ];
>
>
Теперь об использовании
Суть в том, что один и тот же код используется для обьектов разных классов.
// наполняем массив публикаций объектами, производными от Publication
$publications [] = new News ( $news_id );
$publications [] = new Announcement ( $announcement_id );
$publications [] = new Article ( $article_id );
foreach ( $publications as $publication ) // если мы работаем с наследниками Publication
if ( $publication instanceof Publication ) // то печатаем данные
$publication -> do_print ();
> else // исключение или обработка ошибки
>
>
Вот и все. Легким движением руки брюки превращаются в элегантные шорты :-).
Основная выгода полиморфизма — легкость, с которой можно создавать новые классы, «ведущие себя» аналогично родственным, что, в свою очередь, позволяет достигнуть расширяемости и модифицируемости. В статье показан всего лишь примитивный пример, но даже в нем видно, насколько использование абстракций может облегчить разработку. Мы можем работать с новостями точно так, как с объявлениями или статьями, при этом нам даже не обязательно знать, с чем именно мы работаем! В реальных, намного более сложных приложениях, эта выгода еще ощутимей.
Немного теории
- Методы, которые требуют переопределения, называются абстрактными. Логично, что если класс содержит хотя бы один абстрактный метод, то он тоже является абстрактным.
- Очевидно, что обьект абстрактного класса невозможно создать, иначе он не был бы абстрактным.
- Производный класс имеет свойства и методы, принадлежащие базовому классу, и, кроме того, может иметь собственные методы и свойства.
- Метод, переопределяемый в производном классе, называется виртуальным. В базовом абстрактном классе об этом методе нет никакой информации.
- Суть абстрагирования в том, чтобы определять метод в том месте, где есть наиболее полная информация о том, как он должен работать.
Полиморфизм
Полиморфизм (polymorphism) — это понятие из объектно-ориентированного программирования, которое позволяет разным сущностям выполнять одни и те же действия. При этом неважно, как эти сущности устроены внутри и чем они различаются.
С греческого языка слово «полиморфизм» означает «многообразие». Термин используют и в других сферах. Скажем, полиморфизм в биологии — способность какого-то вида существовать в разных формах.
Пример полиформности в природе — пчелы: есть рабочие пчелы, матка, трутни. Они разные, но все могут летать независимо от того, что это за пчела. По похожему принципу работает и полиморфизм в ООП.
Освойте профессию
«Fullstack-разработчик на Python»
Профессия / 12 месяцев
Fullstack-разработчик на Python
Создавайте веб-проекты самостоятельно
Fullstack-разработчик на Python
Fullstack-разработчики могут в одиночку сделать IT-проект от архитектуры до интерфейса. Их навыки востребованы у работодателей, особенно в стартапах. Научитесь программировать на Python и JavaScript и создавайте сервисы с нуля.
Например, есть две разных сущности: картинка и видео. И тем, и другим можно поделиться: отправить в личное сообщение другому человеку. Программист может сделать два разных метода — один для картинки, другой для видео. А может воспользоваться полиморфизмом и создать один метод «Отправить» для обеих сущностей.
Такой метод будет называться полиморфным. Плюс этого подхода — разработчик пишет меньше кода и не повторяется.
Чтобы лучше понять, что такое полиморфизм и как он работает, советуем прочитать статью про ООП. Полиморфизм — один из четырех основных принципов этого способа программирования
Для чего нужен полиморфизм
- Облегчает написание кода. Не нужно придумывать десять разных методов: отправить одно, другое, третье. Есть один метод, который можно применять к разным сущностям и не задумываться.
- Позволяет масштабировать решения. Если понадобится отправлять не только видео и картинки, но и текст, это можно будет сделать той же командой.
- Делает код более читаемым. Разработчику не нужно разбираться, что делает десяток методов с похожими названиями. Есть один метод, и по его названию все понятно.
- Помогает точно понимать, чего ждать от разных методов, то есть делает код более предсказуемым. Не может быть такого, что метод «Отправить» вдруг окажется методом не для картинки, а для видео.
С понятием должен быть знаком любой разработчик. Множество языков программирования используют полиморфизм: C, C++, Java, Python и другие. Не все эти языки чисто объектно-ориентированные: некоторые устроены так, что с ними можно работать в разных парадигмах программирования. Так что столкнуться с полиморфизмом может каждый.
Станьте Fullstack-разработчик на Python и найдите стабильную работу
на удаленке
Полиморфизм как принцип ООП
В объектно-ориентированном программировании четыре основных принципа: инкапсуляция, абстракция, наследование и полиморфизм. Это связанные между собой вещи — без одного не работало бы и другое.
Инкапсуляция — это создание сущностей как «вещей в себе». Классы должны работать независимо друг от друга: если какая-то сущность, например, удалится, это не повлияет на принцип работы остальных.
Абстракция — это принцип, когда какие-то общие вещи сводятся до набора абстрактных признаков. То есть мы имеем не абсолютно разные классы «картинка», «видео», «текст», а абстрактный класс «контент».
Наследование — это возможность делать на основе одних сущностей другие. Обычно в качестве «родителя» используются абстрактные сущности, а от них наследуются уже более конкретные. То есть если родитель — «контент», то дети — «картинка», «видео», «текст». Это все подвиды контента и его наследники.
Чтобы реализовать полиморфизм, нужны как минимум абстракция и наследование. Они помогают сделать абстрактный класс, в нем — абстрактный «общий» метод, а потом унаследовать разные реализации этого метода. В итоге название одно, а механика разная в зависимости от подтипа. Сейчас разберемся подробнее.
Что такое полиморфный метод и как его создают
Полиформный — это многообразный: формы различаются, а действие одно и то же. Тот же самый метод «Отправить» из примера выше может быть реализован по-разному для видео и картинки. Но вызывается он одинаково для всех видов контента и выполняет одну и ту же задачу. Вот как это работает.
Сначала программист создает общий класс. Например, «контент». В нем он описывает вещи, общие для всего контента: свойства и методы. Свойства — это признаки, то, что есть у контента: количество лайков, возможность комментирования и так далее. А методы — это действия, то есть команды: контент можно лайкнуть, открыть на отдельной вкладке, репостнуть или отправить в личное сообщение.
У общего класса — абстрактные, общие методы. Контент можно отправить, но как — пока непонятно. Зато уже можно описать, как будет выглядеть эта команда: как она называется, что в нее нужно передать, какое сообщение выдать после этого. Это своего рода каркас будущего конкретного действия.
Затем разработчик создает производные классы. Это наследники общего класса: они более конкретные, у них могут быть дополнительные свойства и методы. Например, видео можно поставить на паузу, а картинку — нет. А еще у них сохраняются абстрактные свойства и методы родителя: их переопределяют, чтобы они тоже работали конкретнее.
У производных классов — свои реализации общих методов. Например, в классе «контент» есть метод «отправить». Он же есть и в производных классах. В классе «картинка» может быть свой код для отправки, а в классе «видео» — свой. Они могут быть похожи или различаться. Но название у них окажется одинаковым, и делать они будут одно и то же.
Можно создавать объекты производных классов и пользоваться их методами. Абстрактные классы существуют как бы в вакууме: нельзя создать объект, который будет принадлежать такому классу. Среди реальных объектов не может быть «просто контента», который не является ни текстом, ни картинкой, ни видео, ни еще чем-то. Соответственно, абстрактные методы никто не будет вызывать. А вот переопределенные методы из производных классов — вполне реальные, ими можно пользоваться в разных ситуациях.
Формы полиморфизма
Существуют разные виды полиморфизма. Вообще-то классификация довольно широкая и начинающему легко в ней запутаться, поэтому мы решили ограничиться только основными формами.
Полиморфизм подтипов. Это полиморфность «по умолчанию»: когда в ООП говорят о полиморфизме, обычно имеют в виду его. Выше мы рассказывали именно про такой тип. Это возможность использовать одни и те же команды, или интерфейсы, для разных сущностей — подтипов.
Параметрический полиморфизм. Его еще называют обобщенным полиморфизмом. В нем для команды не имеет значения, какую сущность ей прислали: для всех возможных классов будет использоваться один код. Такой полиморфизм считается «истинным» и делает код универсальнее, но реализовать его сложнее.
Полиморфизм ad hoc. Этот вид полиморфизма еще называют специализированным. Его иногда противопоставляют параметрическому: идея ad hoc — разный код при одинаковом названии. Часто такой полиморфизм реализуют с помощью перегрузки методов: несколько раз пишут метод с одним и тем же названием, но разным кодом.
Статический и динамический полиморфизм
На более «глубоком», близком к машине уровне полиморфизм можно разделить на две группы — статический и динамический. Разница —в том, когда программа переходит от общего метода к одной из его вариаций.
- Статический — метод переопределяют при компиляции.
- Динамический — при выполнении программы.
Статический полиморфизм реализуют с помощью перегрузки методов, о которой мы рассказывали выше. Динамический — с помощью абстракций. Обычно в объектно-ориентированных языках есть возможность применить оба варианта.
Преимущества полиморфизма
- Код становится аккуратнее: не приходится множить сущности и создавать десяток команд, которые делают одно и то же. Нет «лапши» — бессвязного неструктурированного кода, в котором тяжело разобраться.
- Разработчику удобнее: не нужно каждый раз думать, что делает команда «Отправить» для конкретного вида контента, можно просто написать ее и получить результат.
- Работать с разными сущностями можно одинаково: не обязательно каждый раз узнавать, о каком именно производном классе речь идет на этот раз. Ведь общее полиморфное действие есть для всего.
- Код легче расширять, использовать заново и всячески модифицировать.
Недостатки полиморфизма
- Связь полиморфизма с наследованием порой расценивают как слабое место всей концепции. Если нужен полиформный класс, но для конкретной ситуации не подходит наследование, — реализация может усложниться.
- Не всегда полиморфизм легко реализовать на практике. Поэтому существует довольно много реализаций, которые работают плохо: с багами и необъяснимыми ошибками.
- Полиморфизм может ухудшать производительность кода, делать его более «тяжелым» и медленным. Но тут многое зависит от реализации: скажем, параметрический обычно быстрее, чем ad hoc.
- Новичкам бывает тяжело понять принцип — объяснить полиморфизм простыми словами можно только в связке с другими понятиями ООП. Так что человек на момент изучения уже должен понимать, что такое объектно-ориентированное программирование.
Как начать изучать полиморфизм
Мы советуем начать с основ: сначала разобраться, как работает программирование в целом, потом перейти к принципам ООП и полиморфизму. На всех этапах лучше практиковаться, чтобы закреплять знания, — к тому же на реальных примерах легче понять тот или иной концепт.
Вы можете записаться на наши курсы и начать учиться уже сейчас. Обещаем много реальных задач и интересной практики!
Основные принципы ООП: полиморфизм в программировании
Полиморфизм — один из основных принципов ООП: что это такое, в каких случаях используется, а также наглядный пример полиморфизма в ООП.
Одним из ключевых принципов ООП является полиморфизм — концепция, позволяющая создавать более гибкие, расширяемые и понимаемые программы. В этой статье мы рассмотрим суть полиморфизма, его типы и приведем примеры кода для лучшего понимания данного принципа ООП.
Для чего нужен полиморфизм в программировании?
Полиморфизм в контексте ООП означает, что разные объекты могут реагировать на один и тот же запрос, проявляя разное поведение в зависимости от своего типа. Это позволяет сократить дублирование кода, улучшить читаемость и облегчить расширение программы.
Преимущества принципа полиморфизма
- Гибкость и расширяемость. Полиморфизм позволяет добавлять новые типы объектов и операций без изменения существующего кода. Новые классы, реализующие общий интерфейс, могут быть легко интегрированы в существующую систему.
- Упрощение кода. Полиморфизм способствует уменьшению дублирования кода. Общий интерфейс или абстрактный базовый класс позволяют описать общее поведение, и каждый конкретный класс реализует только свою специфичную логику.
- Читаемость кода. Полиморфизм делает код более интуитивно понимаемым, так как работа с различными объектами происходит через общий интерфейс. Это упрощает восприятие кода другими разработчиками и способствует поддержке программы.
- Расширение функциональности. Добавление новых функций или операций для существующих классов становится проще. Достаточно реализовать необходимые методы в новых классах, которые наследуют общий интерфейс.
- Повторное использование кода. Полиморфизм позволяет использовать одни и те же методы для разных типов данных. Это устраняет необходимость создания аналогичных функций для разных классов.
- Улучшение тестирования. Тестирование становится более удобным, так как можно создать общие тестовые сценарии для всех классов, реализующих один интерфейс. Это способствует повышению качества и надежности программы.
- Абстракция и инкапсуляция. Полиморфизм позволяет абстрагироваться от конкретных реализаций и сосредоточиться на общем поведении объектов. Также он способствует инкапсуляции, разделяя интерфейс от деталей реализации.
- Облегчение командной разработки. Когда разработчики работают над разными частями программы, полиморфизм позволяет им взаимодействовать через общие интерфейсы без необходимости глубокого понимания внутренней реализации друг друга.
Виды полиморфизма в объектно-ориентированном программировании
Разные виды полиморфизма в объектно-ориентированном программировании обеспечивают гибкость и расширяемость кода. Они позволяют обращаться с разными типами данных единообразно, что делает программы более понятными и удобными для разработки и обслуживания программного кода.
10 принципов ООП, о которых стоит знать каждому программисту
1. Полиморфизм подтипов (наследования)
Этот вид полиморфизма основан на наследовании и позволяет объектам дочерних классов использоваться как объекты родительского класса. Это делает код более гибким и облегчает добавление новых типов.
class Shape: def area(self): pass class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height shapes = [Circle(5), Rectangle(4, 6)] for shape in shapes: print("Area:", shape.area())
2. Параметрический полиморфизм (обобщённое программирование)
Параметрический полиморфизм позволяет создавать обобщенные функции и классы, которые могут работать с разными типами данных без знания их конкретной природы.
def print_items(items): for item in items: print(item) numbers = [1, 2, 3] names = ["Alice", "Bob", "Charlie"] print_items(numbers) print_items(names)
3. Полиморфизм в интерфейсах
Интерфейсный полиморфизм позволяет объектам разных классов реализовывать общий интерфейс и предоставлять схожее поведение без явного наследования.
from abc import ABC, abstractmethod class Animal(ABC): @abstractmethod def make_sound(self): pass class Dog(Animal): def make_sound(self): return "Woof!" class Cat(Animal): def make_sound(self): return "Meow!" def animal_sounds(animal): return animal.make_sound() dog = Dog() cat = Cat() print(animal_sounds(dog)) # Вывод: "Woof!" print(animal_sounds(cat)) # Вывод: "Meow!"
Полиморфизм — это суть объектно-ориентированного программирования, позволяющая создавать гибкие и расширяемые программы. Благодаря различным видам полиморфизма, разработчики могут писать более чистый, читаемый и эффективный код. Овладение этим принципом существенно обогатит навыки любого программиста и сделает его программы более элегантными и функциональными.