Typing.overload: примеры (PYTHON)

Использование typing.overload для перегрузки функций в Python
Раздел: Типизация, Аннотации типов
typing.overload: callable

Функция typing.overload в Python

Декоратор typing.overload позволяет определять несколько сигнатур для одной функции или метода. Он используется для указания, что функция может принимать различные комбинации типов аргументов и возвращать разные типы в зависимости от входных данных. Этот декоратор не изменяет поведение функции во время выполнения, но предоставляет информацию для статических анализаторов типов и систем подсказок.

Основное применение - документирование сложных перегрузок функций в модулях, написанных на чистом Python, особенно когда логика выбора реализации основана на типах или количестве аргументов.

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

Сигнатура использования выглядит как последовательность декорированных @overload определений с разными типами аргументов, за которыми следует одно недекорированное определение с реализацией. Возвращаемые значения указываются через аннотацию -> для каждого варианта перегрузки.

Основные примеры использования

Пример перегрузки функции для обработки разных типов аргументов:

from typing import overload, Union

@overload
def process(value: int) -> str: ...

@overload
def process(value: str) -> int: ...

def process(value: Union[int, str]) -> Union[str, int]:
    if isinstance(value, int):
        return str(value)
    return len(value)

print(process(42))     # Возвращает строку
print(process("hello")) # Возвращает целое число
'42'
5

Пример с различным количеством аргументов:

from typing import overload

@overload
def multiply(x: int) -> int: ...

@overload
def multiply(x: int, y: int) -> int: ...

def multiply(x: int, y: int = 1) -> int:
    return x * y

print(multiply(5))
print(multiply(5, 3))
5
15

Похожие возможности в Python

Одиночные аннотации типов - базовый способ указания типов для аргументов и возвращаемого значения. Подходит для простых функций с одной сигнатурой.

Union и Optional типы - позволяют указывать несколько допустимых типов для одного аргумента. Используются когда функция принимает разные типы, но имеет одинаковую логику обработки.

Protocol и структурная типизация - определяют интерфейсы на основе наличия методов. Применяются для создания гибких контрактов между компонентами.

Функции с использованием isinstance - проверка типов во время выполнения. Подходит когда логика функции существенно отличается для различных типов входных данных.

Аналоги в других языках программирования

TypeScript поддерживает перегрузку функций через несколько объявлений с одной реализацией:

function process(value: number): string;
function process(value: string): number;
function process(value: any): any {
    if (typeof value === 'number') {
        return value.toString();
    }
    return value.length;
}

Java реализует перегрузку методов с разными параметрами в одной классе:

public class Processor {
    public String process(Integer value) {
        return value.toString();
    }
    public Integer process(String value) {
        return value.length();
    }
}

C# поддерживает перегрузку методов с разными сигнатурами:

public class Processor {
    public string Process(int value) => value.ToString();
    public int Process(string value) => value.Length;
}

Отличие Python в том, что @overload существует только для статической типизации и не влияет на выполнение кода.

Типичные ошибки использования

Отсутствие финальной реализации после декорированных overload определений:

from typing import overload

@overload
def example(x: int) -> str: ...

# Ошибка: нет реализации функции
example(10)
TypeError: 'function' object is not callable

Несоответствие сигнатур перегрузок и реализации:

from typing import overload

@overload
def test(x: int) -> str: ...

@overload
def test(x: str) -> int: ...

# Реализация не соответствует сигнатурам перегрузок
def test(x: int) -> str:
    return str(x)

# Вызов с строковым аргументом вызовет ошибку

Использование overload как обычного декоратора выполнения:

@overload
def calculate(x: int) -> int:
    return x * 2  # Код никогда не выполнится

Изменения в последних версиях

В Python 3.11 улучшена поддержка перегрузок для generic-классов и методов. Добавлена лучшая интеграция с протоколами и callback-типами.

В Python 3.10 улучшена обработка Union типов в перегрузках и повышена точность вывода типов при использовании статических анализаторов.

Начиная с Python 3.8, typing модуль получил более стабильный API, а перегрузки стали лучше обрабатываться различными инструментами проверки типов.

Расширенные примеры применения

Перегрузка с использованием generic-типов:

Пример python
from typing import overload, TypeVar, Sequence, List

T = TypeVar('T')

@overload
def first(seq: Sequence[T]) -> T: ...

@overload
def first(seq: Sequence[T], default: T) -> T: ...

def first(seq: Sequence[T], default: T = None) -> T:
    try:
        return seq[0]
    except IndexError:
        return default

print(first([1, 2, 3]))
print(first([], 0))
1
0

Перегрузка для методов класса с различными условиями:

Пример python
from typing import overload

class Converter:
    @overload
    def convert(self, value: int, base: int) -> str: ...
    
    @overload
    def convert(self, value: str) -> int: ...
    
    def convert(self, value, base=10):
        if isinstance(value, int):
            return format(value, f'0{base}')
        return int(value)

conv = Converter()
print(conv.convert(255, 16))
print(conv.convert("FF", 16))
'ff'
255

Комбинирование перегрузок с аргументами по ключу:

Пример python
from typing import overload, Literal

@overload
def create_element(
    type: Literal["button"], 
    text: str
) -> str: ...

@overload
def create_element(
    type: Literal["input"], 
    placeholder: str
) -> str: ...

def create_element(**kwargs):
    if "text" in kwargs:
        return f"<button>{kwargs['text']}</button>"
    return f"<input placeholder='{kwargs['placeholder']}'>"

print(create_element(type="button", text="Click"))
print(create_element(type="input", placeholder="Enter text"))
<button>Click</button>
<input placeholder='Enter text'>

питон typing.overload function comments

En
Typing.overload Decorator for creating overloaded functions