# Введение в типы Python { #python-types-intro }

Python поддерживает необязательные «подсказки типов» (их также называют «аннотациями типов»).

Эти **«подсказки типов»** или аннотации — это специальный синтаксис, позволяющий объявлять <dfn title="например: str, int, float, bool">тип</dfn> переменной.

Объявляя типы для ваших переменных, редакторы кода и инструменты смогут лучше вас поддерживать.

Это всего лишь **краткое руководство / напоминание** о подсказках типов в Python. Оно охватывает только минимум, необходимый для их использования с **FastAPI**... что на самом деле очень мало.

**FastAPI** целиком основан на этих подсказках типов — они дают ему множество преимуществ и выгод.

Но даже если вы никогда не используете **FastAPI**, вам будет полезно немного узнать о них.

/// note | Примечание

Если вы являетесь экспертом в Python и уже знаете всё о подсказках типов, переходите к следующей главе.

///

## Мотивация { #motivation }

Давайте начнем с простого примера:

{* ../../docs_src/python_types/tutorial001_py310.py *}

Вызов этой программы выводит:

```
John Doe
```

Функция делает следующее:

* Принимает `first_name` и `last_name`.
* Преобразует первую букву каждого значения в верхний регистр с помощью `title()`.
* <dfn title="Соединяет их в одно. Содержимое одного — сразу после другого.">Соединяет</dfn> их пробелом посередине.

{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}

### Отредактируем пример { #edit-it }

Это очень простая программа.

А теперь представьте, что вы пишете её с нуля.

В какой-то момент вы бы начали определение функции, у вас были бы готовы параметры...

Но затем нужно вызвать «тот метод, который делает первую букву заглавной».

Это был `upper`? Или `uppercase`? `first_uppercase`? `capitalize`?

Тогда вы пробуете старого друга программиста — автозавершение редактора кода.

Вы вводите первый параметр функции, `first_name`, затем точку (`.`) и нажимаете `Ctrl+Space`, чтобы вызвать автозавершение.

Но, к сожалению, ничего полезного не находится:

<img src="/img/python-types/image01.png">

### Добавим типы { #add-types }

Давайте изменим одну строку из предыдущей версии.

Мы поменяем ровно этот фрагмент — параметры функции — с:

```Python
    first_name, last_name
```

на:

```Python
    first_name: str, last_name: str
```

Вот и всё.

Это и есть «подсказки типов»:

{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}

Это не то же самое, что объявление значений по умолчанию, как, например:

```Python
    first_name="john", last_name="doe"
```

Это другая вещь.

Здесь мы используем двоеточия (`:`), а не знак равенства (`=`).

И добавление подсказок типов обычно не меняет поведение программы по сравнению с вариантом без них.

Но теперь представьте, что вы снова посередине написания этой функции, только уже с подсказками типов.

В тот же момент вы пробуете вызвать автозавершение с помощью `Ctrl+Space` — и видите:

<img src="/img/python-types/image02.png">

С этим вы можете прокручивать варианты, пока не найдёте тот самый:

<img src="/img/python-types/image03.png">

## Больше мотивации { #more-motivation }

Посмотрите на эту функцию — у неё уже есть подсказки типов:

{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}

Так как редактор кода знает типы переменных, вы получаете не только автозавершение, но и проверки ошибок:

<img src="/img/python-types/image04.png">

Теперь вы знаете, что нужно исправить — преобразовать `age` в строку с помощью `str(age)`:

{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}

## Объявление типов { #declaring-types }

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

Это также основное место, где вы будете использовать их с **FastAPI**.

### Простые типы { #simple-types }

Вы можете объявлять все стандартные типы Python, не только `str`.

Можно использовать, например:

* `int`
* `float`
* `bool`
* `bytes`

{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}

### Модуль `typing` { #typing-module }

Для некоторых дополнительных сценариев может понадобиться импортировать что-то из стандартного модуля `typing`. Например, когда вы хотите объявить, что что-то имеет «любой тип», можно использовать `Any` из `typing`:

```python
from typing import Any


def some_function(data: Any):
    print(data)
```

### Generic-типы { #generic-types }

Некоторые типы могут принимать «параметры типов» в квадратных скобках, чтобы определить их внутренние типы. Например, «список строк» объявляется как `list[str]`.

Такие типы, которые принимают параметры типов, называются **Generic-типами** или **Generics**.

Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):

* `list`
* `tuple`
* `set`
* `dict`

#### List { #list }

Например, давайте определим переменную как `list` из `str`.

Объявите переменную с тем же синтаксисом двоеточия (`:`).

В качестве типа укажите `list`.

Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:

{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}

/// info | Информация

Эти внутренние типы в квадратных скобках называются «параметрами типов».

В данном случае `str` — это параметр типа, передаваемый в `list`.

///

Это означает: «переменная `items` — это `list`, и каждый элемент этого списка — `str`».

Таким образом, ваш редактор кода сможет помогать даже при обработке элементов списка:

<img src="/img/python-types/image05.png">

Без типов добиться этого почти невозможно.

Обратите внимание, что переменная `item` — один из элементов списка `items`.

И всё же редактор кода знает, что это `str`, и даёт соответствующую поддержку.

#### Tuple и Set { #tuple-and-set }

Аналогично вы бы объявили `tuple` и `set`:

{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}

Это означает:

* Переменная `items_t` — это `tuple` из 3 элементов: `int`, ещё один `int` и `str`.
* Переменная `items_s` — это `set`, и каждый элемент имеет тип `bytes`.

#### Dict { #dict }

Чтобы определить `dict`, вы передаёте 2 параметра типов, разделённые запятой.

Первый параметр типа — для ключей `dict`.

Второй параметр типа — для значений `dict`:

{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}

Это означает:

* Переменная `prices` — это `dict`:
    * Ключи этого `dict` имеют тип `str` (скажем, название каждой позиции).
    * Значения этого `dict` имеют тип `float` (скажем, цена каждой позиции).

#### Union { #union }

Вы можете объявить, что переменная может быть **одним из нескольких типов**, например, `int` или `str`.

Чтобы это определить, используйте <dfn title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</dfn> для разделения обоих типов.

Это называется «объединение» (union), потому что переменная может быть чем угодно из объединения этих двух множеств типов.

```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
```

Это означает, что `item` может быть `int` или `str`.

#### Возможно `None` { #possibly-none }

Вы можете объявить, что значение может иметь определённый тип, например `str`, но также может быть и `None`.

//// tab | Python 3.10+

```Python hl_lines="1"
{!> ../../docs_src/python_types/tutorial009_py310.py!}
```

////

Использование `str | None` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.

### Классы как типы { #classes-as-types }

Вы также можете объявлять класс как тип переменной.

Допустим, у вас есть класс `Person` с именем:

{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}

Тогда вы можете объявить переменную типа `Person`:

{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}

И снова вы получите полную поддержку редактора кода:

<img src="/img/python-types/image06.png">

Обратите внимание, что это означает: «`one_person` — это **экземпляр** класса `Person`».

Это не означает: «`one_person` — это **класс** с именем `Person`».

## Pydantic-модели { #pydantic-models }

[Pydantic](https://docs.pydantic.dev/) — это библиотека Python для валидации данных.

Вы объявляете «форму» данных как классы с атрибутами.

И у каждого атрибута есть тип.

Затем вы создаёте экземпляр этого класса с некоторыми значениями — он провалидирует значения, преобразует их к соответствующему типу (если это применимо) и вернёт вам объект со всеми данными.

И вы получите полную поддержку редактора кода для этого результирующего объекта.

Пример из официальной документации Pydantic:

{* ../../docs_src/python_types/tutorial011_py310.py *}

/// info | Информация

Чтобы узнать больше о [Pydantic, ознакомьтесь с его документацией](https://docs.pydantic.dev/).

///

**FastAPI** целиком основан на Pydantic.

Вы увидите намного больше всего этого на практике в [Учебник - Руководство пользователя](tutorial/index.md).

## Подсказки типов с аннотациями метаданных { #type-hints-with-metadata-annotations }

В Python также есть возможность добавлять **дополнительные <dfn title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</dfn>** к подсказкам типов с помощью `Annotated`.

Вы можете импортировать `Annotated` из `typing`.

{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}

Сам Python ничего не делает с `Annotated`. А для редакторов кода и других инструментов тип по-прежнему `str`.

Но вы можете использовать это место в `Annotated`, чтобы передать **FastAPI** дополнительные метаданные о том, как вы хотите, чтобы ваше приложение себя вело.

Важно помнить, что **первый параметр типа**, который вы передаёте в `Annotated`, — это **фактический тип**. Всё остальное — просто метаданные для других инструментов.

Пока вам достаточно знать, что `Annotated` существует и это — стандартный Python. 😎

Позже вы увидите, насколько это **мощно**.

/// tip | Совет

Тот факт, что это **стандартный Python**, означает, что вы по-прежнему получите **лучший возможный разработческий опыт** в вашем редакторе кода, с инструментами для анализа и рефакторинга кода и т.д. ✨

А ещё ваш код будет очень совместим со множеством других инструментов и библиотек Python. 🚀

///

## Аннотации типов в **FastAPI** { #type-hints-in-fastapi }

**FastAPI** использует эти подсказки типов для выполнения нескольких задач.

С **FastAPI** вы объявляете параметры с подсказками типов и получаете:

* **Поддержку редактора кода**.
* **Проверки типов**.

...и **FastAPI** использует эти же объявления для:

* **Определения требований**: из path-параметров пути запроса, query-параметров, HTTP-заголовков, тел запросов, зависимостей и т.д.
* **Преобразования данных**: из HTTP-запроса к требуемому типу.
* **Валидации данных**: приходящих с каждого HTTP-запроса:
    * Генерации **автоматических ошибок**, возвращаемых клиенту, когда данные некорректны.
* **Документирования** API с использованием OpenAPI:
    * что затем используется пользовательскими интерфейсами автоматической интерактивной документации.

Всё это может звучать абстрактно. Не волнуйтесь. Вы увидите всё это в действии в [Учебник - Руководство пользователя](tutorial/index.md).

Важно то, что, используя стандартные типы Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.), **FastAPI** сделает за вас большую часть работы.

/// info | Информация

Если вы уже прошли всё руководство и вернулись, чтобы узнать больше о типах, хорошим ресурсом будет [«шпаргалка» от `mypy`](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).

///
