Python: Полиморфизм
Теория: Параметрический полиморфизм
Полиморфизм играет важную роль в программировании. Он обеспечивает универсальность и повторное использование кода, что упрощает разработку и поддержку программного обеспечения. Также он позволяет объектам и функциям работать с данными разных типов.
В языках программирования существуют разные виды полиморфизма. В императивных языках, например, Java и C++, есть полиморфизм подтипов — интерфейсы, и параметрический полиморфизм — дженерики.
В функциональных языках, например, Haskell и Lisp, существует параметрический полиморфизм и высший-родовой полиморфизм — функторы, монады. Эти виды полиморфизма позволяют создавать гибкие и масштабируемые структуры данных и функции.
Применение параметрического полиморфизма
Параметрический полиморфизм — это вид полиморфизма, который позволяет функциям работать независимо от используемых ими типов данных. Это достигается за счет использования параметров типа, которые представляют собой переменные, используемые для указания одного или нескольких типов данных.
Получается, что одна и та же функция или структура данных может быть использована с различными типами данных. Это делает код более универсальным и повторно используемым.
Параметрический полиморфизм имеет множество преимуществ. Одно из них — увеличение повторного использования кода. Вместо написания функции для каждого типа данных мы можем написать одну функцию, которая работает с любым типом данных. Это также делает код более универсальным и масштабируемым.
Рассмотрим, как это работает на примерах из языков с динамической и статической типизацией.
Параметрический полиморфизм в языках с динамической типизацией
Рассмотрим следующий пример Python кода:
Здесь используется функция chain из модуля itertools, которая позволяет объединять несколько итерируемых объектов в один итератор. Она принимает произвольное количество аргументов и возвращает один итератор, который состоит из всех элементов, переданных итерируемых объектов.
Также дополнительно используется явное преобразование в list, потому что chain возвращает собственный тип объекта — itertools.chain объект.
Теперь реализуем данную функцию:
В примере функция my_chain принимает произвольное количество итерируемых объектов и объединяет их элементы в один список. Внутри функции используются два вложенных цикла for для прохода по всем переданным итерируемым объектам и их элементам. Затем каждый элемент добавляется в результирующий список result.
Если рассмотреть код функции my_chain, можно заметить, что никакие операции над данными внутри коллекций не выполняются. Данные перекладываются из одной коллекции в другую, но не изменяются.
Новая функция my_chain(), как и стандартная функция chain() из модуля itertools, может работать с коллекциями, которые содержат любые типы данных.
Для разработчиков, которые всегда писали на языках с динамической типизацией, это кажется естественным. В языках со статической типизацией все не так.
Параметрический полиморфизм в языках со статической типизацией
Ниже пример определения массивов в Java:
Для массивов нужно указывать тип данных, которые ожидаются внутри. Для первого массива — это int, для второго — String. Нельзя создать массив и не указать тип его значений. То же самое касается функций, которые обрабатывают массивы:
Обратите внимание на сигнатуру метода int[] chain_(int[] arr1, int[] arr2). В отличие от варианта на Python здесь указано, что входными параметрами являются массивы чисел. То есть для массива строк эта функция работать не будет. Не будет она работать и для остальных типов данных.
Это означает, что нам придется реализовывать подобную функцию для каждого типа при том, что алгоритм внутри идентичен.
Здесь нам пригодится параметрический полиморфизм.
Статическим языкам приходится вводить в язык специальные конструкции, которые позволяют описывать подобные алгоритмы безотносительно типа параметра. В некоторых языках их называют шаблонами или дженериками:
В этом примере метод chain_ использует параметрический полиморфизм для работы с массивами любого типа. <T> — параметр типа, который может быть заменен любым типом класса при вызове метода chain_.
Благодаря параметрическому полиморфизму мы можем использовать одну и ту же функцию для работы с массивами разных типов данных.
Параметрический полиморфизм в языках со статической типизацией позволяет создавать универсальные алгоритмы и функции, которые могут работать с различными типами данных. Вместо того чтобы создавать отдельные версии функции для каждого типа, мы можем использовать одну обобщенную функцию, которая принимает параметр типа и может быть применена к разным типам данных.
Это упрощает разработку и обслуживание кода, поскольку мы можем использовать одну и ту же функцию для различных типов данных. При этом мы избегаем дублирование кода и улучшаем повторное использование. Также это делает код более гибким и масштабируемым, так как мы можем легко изменять типы данных, с которыми работает функция. При этом не нужно изменять саму функцию.
Параметрический полиморфизм в статически типизированных языках программирования позволяет создавать более гибкий и переиспользуемый код.
Выводы
Параметрический полиморфизм дает возможность писать алгоритмы, которые идентично обрабатывают данные разных типов. Это уменьшает количество кода и вероятность его поломки. Иногда за это приходится платить сложностью решения. Но для большинства типичных операций сложность растет не сильно. Это видно и по коду выше.
В динамических языках для реализации обобщенных алгоритмов параметрический полиморфизм не нужен. Коллекция может содержать любые типы данных в любой момент времени. Благодаря этому не требуется вводить дополнительные языковые конструкции и изучать новые концепции.

.png)
