# Вступ до типів Python

Python підтримує додаткові "підказки типу" ("type hints") (також звані "анотаціями типу" ("type annotations")).

Ці **"type hints"** є спеціальним синтаксисом, що дозволяє оголошувати <abbr title="наприклад: str, int, float, bool">тип</abbr> змінної.

За допомогою оголошення типів для ваших змінних, редактори та інструменти можуть надати вам кращу підтримку.

Це просто **швидкий посібник / нагадування** про анотації типів у Python. Він покриває лише мінімум, необхідний щоб використовувати їх з **FastAPI**... що насправді дуже мало.

**FastAPI** повністю базується на цих анотаціях типів, вони дають йому багато переваг.

Але навіть якщо ви ніколи не використаєте **FastAPI**, вам буде корисно дізнатись трохи про них.

!!! note
    Якщо ви експерт у Python і ви вже знаєте усе про анотації типів - перейдіть до наступного розділу.

## Мотивація

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

```Python
{!../../../docs_src/python_types/tutorial001.py!}
```

Виклик цієї програми виводить:

```
John Doe
```

Функція виконує наступне:

* Бере `first_name` та `last_name`.
* Конвертує кожну літеру кожного слова у верхній регістр за допомогою `title()`.
* <abbr title="З’єднує їх, як одне ціле. З вмістом один за одним.">Конкатенує</abbr> їх разом із пробілом по середині.

```Python hl_lines="2"
{!../../../docs_src/python_types/tutorial001.py!}
```

### Редагуйте це

Це дуже проста програма.

Але тепер уявіть, що ви писали це з нуля.

У певний момент ви розпочали б визначення функції, у вас були б готові параметри...

Але тоді вам потрібно викликати "той метод, який переводить першу літеру у верхній регістр".

Це буде `upper`? Чи `uppercase`? `first_uppercase`? `capitalize`?

Тоді ви спробуєте давнього друга програміста - автозаповнення редактора коду.

Ви надрукуєте перший параметр функції, `first_name`, тоді крапку (`.`), а тоді натиснете `Ctrl+Space`, щоб запустити автозаповнення.

Але, на жаль, ви не отримаєте нічого корисного:

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

### Додайте типи

Давайте змінимо один рядок з попередньої версії.

Ми змінимо саме цей фрагмент, параметри функції, з:

```Python
    first_name, last_name
```

на:

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

Ось і все.

Це "type hints":

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

Це не те саме, що оголошення значень за замовчуванням, як це було б з:

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

Це зовсім інше.

Ми використовуємо двокрапку (`:`), не дорівнює (`=`).

І додавання анотації типу зазвичай не змінює того, що сталось би без них.

Але тепер, уявіть що ви посеред процесу створення функції, але з анотаціями типів.

В цей же момент, ви спробуєте викликати автозаповнення з допомогою `Ctrl+Space` і побачите:

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

Разом з цим, ви можете прокручувати, переглядати опції, допоки ви не знайдете одну, що звучить схоже:

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

## Більше мотивації

Перевірте цю функцію, вона вже має анотацію типу:

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

Оскільки редактор знає типи змінних, ви не тільки отримаєте автозаповнення, ви також отримаєте перевірку помилок:

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

Тепер ви знаєте, щоб виправити це, вам потрібно перетворити `age` у строку з допомогою `str(age)`:

```Python hl_lines="2"
{!../../../docs_src/python_types/tutorial004.py!}
```

## Оголошення типів

Щойно ви побачили основне місце для оголошення анотацій типу. Як параметри функції.

Це також основне місце, де ви б їх використовували у **FastAPI**.

### Прості типи

Ви можете оголошувати усі стандартні типи у Python, не тільки `str`.

Ви можете використовувати, наприклад:

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

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

### Generic-типи з параметрами типів

Існують деякі структури даних, які можуть містити інші значення, наприклад `dict`, `list`, `set` та `tuple`. І внутрішні значення також можуть мати свій тип.

Ці типи, які мають внутрішні типи, називаються "**generic**" типами. І оголосити їх можна навіть із внутрішніми типами.

Щоб оголосити ці типи та внутрішні типи, ви можете використовувати стандартний модуль Python `typing`. Він існує спеціально для підтримки анотацій типів.

#### Новіші версії Python

Синтаксис із використанням `typing` **сумісний** з усіма версіями, від Python 3.6 до останніх, включаючи Python 3.9, Python 3.10 тощо.

У міру розвитку Python **новіші версії** мають покращену підтримку анотацій типів і в багатьох випадках вам навіть не потрібно буде імпортувати та використовувати модуль `typing` для оголошення анотацій типу.

Якщо ви можете вибрати новішу версію Python для свого проекту, ви зможете скористатися цією додатковою простотою. Дивіться кілька прикладів нижче.

#### List (список)

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

=== "Python 3.8 і вище"

    З модуля `typing`, імпортуємо `List` (з великої літери `L`):

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

    Оголосимо змінну з тим самим синтаксисом двокрапки (`:`).

    Як тип вкажемо `List`, який ви імпортували з `typing`.

    Оскільки список є типом, який містить деякі внутрішні типи, ви поміщаєте їх у квадратні дужки:

    ```Python hl_lines="4"
    {!> ../../../docs_src/python_types/tutorial006.py!}
    ```

=== "Python 3.9 і вище"

    Оголосимо змінну з тим самим синтаксисом двокрапки (`:`).

    Як тип вкажемо `list`.

    Оскільки список є типом, який містить деякі внутрішні типи, ви поміщаєте їх у квадратні дужки:

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

!!! info
    Ці внутрішні типи в квадратних дужках називаються "параметрами типу".

    У цьому випадку, `str` це параметр типу переданий у `List` (або `list` у Python 3.9 і вище).

Це означає: "змінна `items` це `list`, і кожен з елементів у цьому списку - `str`".

!!! tip
    Якщо ви використовуєте Python 3.9 і вище, вам не потрібно імпортувати `List` з `typing`, ви можете використовувати натомість тип `list`.

Зробивши це, ваш редактор може надати підтримку навіть під час обробки елементів зі списку:

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

Без типів цього майже неможливо досягти.

Зверніть увагу, що змінна `item` є одним із елементів у списку `items`.

І все ж редактор знає, що це `str`, і надає підтримку для цього.

#### Tuple and Set (кортеж та набір)

Ви повинні зробити те ж саме, щоб оголосити `tuple` і `set`:

=== "Python 3.8 і вище"

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

=== "Python 3.9 і вище"

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

Це означає:

* Змінна `items_t` це `tuple` з 3 елементами, `int`, ще `int`, та `str`.
* Змінна `items_s` це `set`, і кожен його елемент типу `bytes`.

#### Dict (словник)

Щоб оголосити `dict`, вам потрібно передати 2 параметри типу, розділені комами.

Перший параметр типу для ключа у `dict`.

Другий параметр типу для значення у `dict`:

=== "Python 3.8 і вище"

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

=== "Python 3.9 і вище"

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

Це означає:

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

#### Union (об'єднання)

Ви можете оголосити, що змінна може бути будь-яким із **кількох типів**, наприклад, `int` або `str`.

У Python 3.6 і вище (включаючи Python 3.10) ви можете використовувати тип `Union` з `typing` і вставляти в квадратні дужки можливі типи, які можна прийняти.

У Python 3.10 також є **альтернативний синтаксис**, у якому ви можете розділити можливі типи за допомогою <abbr title='також називають «побітовим "або" оператором», але це значення тут не актуальне'>вертикальної смуги (`|`)</abbr>.

=== "Python 3.8 і вище"

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

=== "Python 3.10 і вище"

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

В обох випадках це означає, що `item` може бути `int` або `str`.

#### Possibly `None` (Optional)

Ви можете оголосити, що значення може мати тип, наприклад `str`, але також може бути `None`.

У Python 3.6 і вище (включаючи Python 3.10) ви можете оголосити його, імпортувавши та використовуючи `Optional` з модуля `typing`.

```Python hl_lines="1  4"
{!../../../docs_src/python_types/tutorial009.py!}
```

Використання `Optional[str]` замість просто `str` дозволить редактору допомогти вам виявити помилки, коли ви могли б вважати, що значенням завжди є `str`, хоча насправді воно також може бути `None`.

`Optional[Something]` насправді є скороченням для `Union[Something, None]`, вони еквівалентні.

Це також означає, що в Python 3.10 ви можете використовувати `Something | None`:

=== "Python 3.8 і вище"

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

=== "Python 3.8 і вище - альтернатива"

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

=== "Python 3.10 і вище"

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

#### Generic типи

Ці типи, які приймають параметри типу у квадратних дужках, називаються **Generic types** or **Generics**, наприклад:

=== "Python 3.8 і вище"

    * `List`
    * `Tuple`
    * `Set`
    * `Dict`
    * `Union`
    * `Optional`
    * ...та інші.

=== "Python 3.9 і вище"

    Ви можете використовувати ті самі вбудовані типи, як generic (з квадратними дужками та типами всередині):

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

    І те саме, що й у Python 3.8, із модуля `typing`:

    * `Union`
    * `Optional`
    * ...та інші.

=== "Python 3.10 і вище"

    Ви можете використовувати ті самі вбудовані типи, як generic (з квадратними дужками та типами всередині):

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

    І те саме, що й у Python 3.8, із модуля `typing`:

    * `Union`
    * `Optional` (так само як у Python 3.8)
    * ...та інші.

    У Python 3.10, як альтернатива використанню `Union` та `Optional`, ви можете використовувати <abbr title='також називають «побітовим "або" оператором», але це значення тут не актуальне'>вертикальну смугу (`|`)</abbr> щоб оголосити об'єднання типів.

### Класи як типи

Ви також можете оголосити клас як тип змінної.

Скажімо, у вас є клас `Person` з імʼям:

```Python hl_lines="1-3"
{!../../../docs_src/python_types/tutorial010.py!}
```

Потім ви можете оголосити змінну типу `Person`:

```Python hl_lines="6"
{!../../../docs_src/python_types/tutorial010.py!}
```

І знову ж таки, ви отримуєте всю підтримку редактора:

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

## Pydantic моделі

<a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic</a> це бібліотека Python для валідації даних.

Ви оголошуєте «форму» даних як класи з атрибутами.

І кожен атрибут має тип.

Потім ви створюєте екземпляр цього класу з деякими значеннями, і він перевірить ці значення, перетворить їх у відповідний тип (якщо є потреба) і надасть вам об’єкт з усіма даними.

І ви отримуєте всю підтримку редактора з цим отриманим об’єктом.

Приклад з документації Pydantic:

=== "Python 3.8 і вище"

    ```Python
    {!> ../../../docs_src/python_types/tutorial011.py!}
    ```

=== "Python 3.9 і вище"

    ```Python
    {!> ../../../docs_src/python_types/tutorial011_py39.py!}
    ```

=== "Python 3.10 і вище"

    ```Python
    {!> ../../../docs_src/python_types/tutorial011_py310.py!}
    ```

!!! info
    Щоб дізнатись більше про <a href="https://docs.pydantic.dev/" class="external-link" target="_blank">Pydantic, перегляньте його документацію</a>.

**FastAPI** повністю базується на Pydantic.

Ви побачите набагато більше цього всього на практиці в [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.

## Анотації типів у **FastAPI**

**FastAPI** використовує ці підказки для виконання кількох речей.

З **FastAPI** ви оголошуєте параметри з підказками типу, і отримуєте:

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

...і **FastAPI** використовує ті самі оголошення для:

* **Визначення вимог**: з параметрів шляху запиту, параметрів запиту, заголовків, тіл, залежностей тощо.
* **Перетворення даних**: із запиту в необхідний тип.
* **Перевірка даних**: що надходять від кожного запиту:
    * Генерування **автоматичних помилок**, що повертаються клієнту, коли дані недійсні.
* **Документування** API за допомогою OpenAPI:
    * який потім використовується для автоматичної інтерактивної документації користувальницьких інтерфейсів.

Все це може здатися абстрактним. Не хвилюйтеся. Ви побачите все це в дії в [Туторіал - Посібник користувача](tutorial/index.md){.internal-link target=_blank}.

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

!!! info
    Якщо ви вже пройшли весь навчальний посібник і повернулися, щоб дізнатися більше про типи, ось хороший ресурс <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">"шпаргалка" від `mypy`</a>.
