Дженерики

Что если мы хотим написать обобщённую функцию, которая принимает на вход итерируемую структуру, то есть структуру, по которой можно итерироваться, и возвращает результат первой итерации?

from typing import TypeVar, Iterable

T = TypeVar("T")

def first(iterable: Iterable[T]) -> T | None:
    for element in iterable:
        return element

print(first(["one", "two"]))  # one
print(first((100, 200)))  # 200

Как видите, типом данных в этой итерируемой структуре iterable могут быть любые данные, а наши type hinting в функции first говорят буквально, что функция принимает на вход итерабельную структуру данных, каждый элемент которой имеет тип T, и функция возвращает тот же тип T. Тип T при этом может быть любым.

Это так называемые дженерики, то есть обобщённые типы.

Причём имя T здесь это пример, он часто используется именно так, T, от Type, но название типа может быть и любым другим.

Помимо дженериков можно сохранять отдельные типы для лучшей читаемости кода и подсказки читателю, что именно за данные здесь хранятся:

from dataclasses import dataclass

Phone = str

@dataclass
class User:
    user_id: int
    phone: Phone

def get_user_phone(user: User) -> Phone:
    return user.phone

Мы уже использовали это в коде приложения погоды, когда задавали псевдоним для градусов Цельсия.