Какие виды полиморфизма применяются к функциям в Python

Ответ

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

1. Ad-hoc полиморфизм (Перегрузка)

Это возможность определять несколько версий функции для разных типов аргументов. В Python это реализуется не через одинаковые имена функций (как в C++ или Java), а с помощью декоратора @singledispatch из модуля functools.

Пример: Создадим функцию format_value, которая по-разному форматирует целые числа и строки.

from functools import singledispatch

@singledispatch
def format_value(value):
    """Базовая реализация по умолчанию."""
    return str(value)

@format_value.register(int)
def _(value):
    """Реализация для целых чисел (int)."""
    return f"Integer: {value}"

@format_value.register(str)
def _(value):
    """Реализация для строк (str)."""
    return f'String: "{value}"'

print(format_value(123))         # Вывод: Integer: 123
print(format_value("hello"))     # Вывод: String: "hello"
print(format_value([1, 2, 3]))  # Вывод: [1, 2, 3] (используется базовая реализация)

2. Параметрический полиморфизм (через утиную типизацию)

Это способность функции выполняться одинаково для любого типа данных, который поддерживает необходимые ей операции. В Python это достигается за счет утиной типизации (duck typing): "если что-то крякает как утка и плавает как утка, то это утка". Тип объекта не важен, важны лишь его методы и атрибуты.

Пример: Встроенная функция len() работает с любым объектом, у которого есть метод __len__().

# len() работает со строкой, списком и словарем
print(len("Python"))      # 6
print(len([1, 2, 3, 4]))  # 4
print(len({"a": 1, "b": 2})) # 2

# Создадим свой класс с методом __len__
class Order:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)

my_order = Order(["apple", "banana", "orange"])
print(len(my_order))      # 3, len() успешно работает с нашим объектом

Ответ 18+ 🔞

А, ну вот, опять про этот ваш полиморфизм, блядь. Сидят, умные такие, терминами кидаются. А на деле всё просто, как три копейки, ёпта.

Смотри, есть у тебя функция. И вот эта самая функция, хитрая жопа такая, может работать не с одним типом данных, а с кучей разных. Это и есть полиморфизм, если по-простому. В Питоне он бывает двух основных видов, и первый — это как бы «перегрузка», только по-питонячьи, блядь.

1. Полиморфизм «на выбор» (Ad-hoc)

В других языках ты можешь десять функций с одним именем написать, только аргументы разные. В Питоне так нельзя, он тебе сразу скажет: «Иди нахуй, имя уже занято». Но выход есть, ёпта! Декоратор @singledispatch из functools. Он как швейцар в дорогом клубе: смотрит на тип гостя (аргумента) и пускает его к нужной реализации.

Вот смотри, как это выглядит. Делаем функцию format_value, которая по-разному будет выёбываться с числами и строками.

from functools import singledispatch

@singledispatch
def format_value(value):
    """Базовая реализация по умолчанию."""
    return str(value)

@format_value.register(int)
def _(value):
    """Реализация для целых чисел (int)."""
    return f"Integer: {value}"

@format_value.register(str)
def _(value):
    """Реализация для строк (str)."""
    return f'String: "{value}"'

print(format_value(123))         # Вывод: Integer: 123
print(format_value("hello"))     # Вывод: String: "hello"
print(format_value([1, 2, 3]))  # Вывод: [1, 2, 3] (используется базовая реализация)

Видишь? Кинул ей число — она тебе «Integer» вывела. Кинул строку — «String». А кинул список, на который регистрации нет, — она просто в строку превратила, по дефолту. Удобно, блядь, не надо городить кучу if isinstance(...).

2. Полиморфизм «по-утиному» (Параметрический)

А вот это, блядь, моя любимая тема. Утиная типизация! Принцип простой, как палка: «Если что-то крякает как утка и плавает как утка, то это утка». Нам похуй на тип объекта, нам важно, чтобы у него был нужный метод. Если есть — работаем. Нет — получи по ебалу с AttributeError.

Классический пример — встроенная функция len(). Ей абсолютно насрать, что ты ей передаёшь: строку, список, свой класс. Лишь бы у объекта был метод __len__(). Она его вызовет и всё.

# len() работает со строкой, списком и словарем
print(len("Python"))      # 6
print(len([1, 2, 3, 4]))  # 4
print(len({"a": 1, "b": 2})) # 2

# Создадим свой класс с методом __len__
class Order:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)

my_order = Order(["apple", "banana", "orange"])
print(len(my_order))      # 3, len() успешно работает с нашим объектом

Вот и вся магия, ёпта. Не нужно никого наследовать от какого-то общего абстрактного «Измеримого». Просто сделал метод __len__ — и ты уже утка в глазах функции len(). Пиздец как гибко и просто. Это и есть сила Питона, блядь, в этом вся его прелесть.