Введение в типы Python¶
🌐 Перевод выполнен с помощью ИИ и людей
Этот перевод был сделан ИИ под руководством людей. 🤝
В нем могут быть ошибки из-за неправильного понимания оригинального смысла или неестественности и т. д. 🤖
Вы можете улучшить этот перевод, помогая нам лучше направлять ИИ LLM.
Python поддерживает необязательные «подсказки типов» (их также называют «аннотациями типов»).
Эти «подсказки типов» или аннотации — это специальный синтаксис, позволяющий объявлять тип переменной.
Объявляя типы для ваших переменных, редакторы кода и инструменты смогут лучше вас поддерживать.
Это всего лишь краткое руководство / напоминание о подсказках типов в Python. Оно охватывает только минимум, необходимый для их использования с FastAPI... что на самом деле очень мало.
FastAPI целиком основан на этих подсказках типов — они дают ему множество преимуществ и выгод.
Но даже если вы никогда не используете FastAPI, вам будет полезно немного узнать о них.
Примечание
Если вы являетесь экспертом в Python и уже знаете всё о подсказках типов, переходите к следующей главе.
Мотивация¶
Давайте начнем с простого примера:
def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
🤓 Other versions and variants
def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
Вызов этой программы выводит:
John Doe
Функция делает следующее:
- Принимает
first_nameиlast_name. - Преобразует первую букву каждого значения в верхний регистр с помощью
title(). - Соединяет их пробелом посередине.
def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
🤓 Other versions and variants
def get_full_name(first_name, last_name):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
Отредактируем пример¶
Это очень простая программа.
А теперь представьте, что вы пишете её с нуля.
В какой-то момент вы бы начали определение функции, у вас были бы готовы параметры...
Но затем нужно вызвать «тот метод, который делает первую букву заглавной».
Это был upper? Или uppercase? first_uppercase? capitalize?
Тогда вы пробуете старого друга программиста — автозавершение редактора кода.
Вы вводите первый параметр функции, first_name, затем точку (.) и нажимаете Ctrl+Space, чтобы вызвать автозавершение.
Но, к сожалению, ничего полезного не находится:

Добавим типы¶
Давайте изменим одну строку из предыдущей версии.
Мы поменяем ровно этот фрагмент — параметры функции — с:
first_name, last_name
на:
first_name: str, last_name: str
Вот и всё.
Это и есть «подсказки типов»:
def get_full_name(first_name: str, last_name: str):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
🤓 Other versions and variants
def get_full_name(first_name: str, last_name: str):
full_name = first_name.title() + " " + last_name.title()
return full_name
print(get_full_name("john", "doe"))
Это не то же самое, что объявление значений по умолчанию, как, например:
first_name="john", last_name="doe"
Это другая вещь.
Здесь мы используем двоеточия (:), а не знак равенства (=).
И добавление подсказок типов обычно не меняет поведение программы по сравнению с вариантом без них.
Но теперь представьте, что вы снова посередине написания этой функции, только уже с подсказками типов.
В тот же момент вы пробуете вызвать автозавершение с помощью Ctrl+Space — и видите:

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

Больше мотивации¶
Посмотрите на эту функцию — у неё уже есть подсказки типов:
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + age
return name_with_age
🤓 Other versions and variants
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + age
return name_with_age
Так как редактор кода знает типы переменных, вы получаете не только автозавершение, но и проверки ошибок:

Теперь вы знаете, что нужно исправить — преобразовать age в строку с помощью str(age):
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + str(age)
return name_with_age
🤓 Other versions and variants
def get_name_with_age(name: str, age: int):
name_with_age = name + " is this old: " + str(age)
return name_with_age
Объявление типов¶
Вы только что увидели основное место, где объявляют подсказки типов — параметры функции.
Это также основное место, где вы будете использовать их с FastAPI.
Простые типы¶
Вы можете объявлять все стандартные типы Python, не только str.
Можно использовать, например:
intfloatboolbytes
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_e
🤓 Other versions and variants
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_e
Модуль typing¶
Для некоторых дополнительных сценариев может понадобиться импортировать что-то из стандартного модуля typing. Например, когда вы хотите объявить, что что-то имеет «любой тип», можно использовать Any из typing:
from typing import Any
def some_function(data: Any):
print(data)
Generic-типы¶
Некоторые типы могут принимать «параметры типов» в квадратных скобках, чтобы определить их внутренние типы. Например, «список строк» объявляется как list[str].
Такие типы, которые принимают параметры типов, называются Generic-типами или Generics.
Вы можете использовать те же встроенные типы как generics (с квадратными скобками и типами внутри):
listtuplesetdict
List¶
Например, давайте определим переменную как list из str.
Объявите переменную с тем же синтаксисом двоеточия (:).
В качестве типа укажите list.
Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
def process_items(items: list[str]):
for item in items:
print(item)
🤓 Other versions and variants
def process_items(items: list[str]):
for item in items:
print(item)
Информация
Эти внутренние типы в квадратных скобках называются «параметрами типов».
В данном случае str — это параметр типа, передаваемый в list.
Это означает: «переменная items — это list, и каждый элемент этого списка — str».
Таким образом, ваш редактор кода сможет помогать даже при обработке элементов списка:

Без типов добиться этого почти невозможно.
Обратите внимание, что переменная item — один из элементов списка items.
И всё же редактор кода знает, что это str, и даёт соответствующую поддержку.
Tuple и Set¶
Аналогично вы бы объявили tuple и set:
def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
return items_t, items_s
🤓 Other versions and variants
def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
return items_t, items_s
Это означает:
- Переменная
items_t— этоtupleиз 3 элементов:int, ещё одинintиstr. - Переменная
items_s— этоset, и каждый элемент имеет типbytes.
Dict¶
Чтобы определить dict, вы передаёте 2 параметра типов, разделённые запятой.
Первый параметр типа — для ключей dict.
Второй параметр типа — для значений dict:
def process_items(prices: dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
🤓 Other versions and variants
def process_items(prices: dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
Это означает:
- Переменная
prices— этоdict:- Ключи этого
dictимеют типstr(скажем, название каждой позиции). - Значения этого
dictимеют типfloat(скажем, цена каждой позиции).
- Ключи этого
Union¶
Вы можете объявить, что переменная может быть одним из нескольких типов, например, int или str.
Чтобы это определить, используйте вертикальную черту (|) для разделения обоих типов.
Это называется «объединение» (union), потому что переменная может быть чем угодно из объединения этих двух множеств типов.
def process_item(item: int | str):
print(item)
Это означает, что item может быть int или str.
Возможно None¶
Вы можете объявить, что значение может иметь определённый тип, например str, но также может быть и None.
def say_hi(name: str | None = None):
if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")
Использование str | None вместо просто str позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда str, хотя на самом деле оно может быть и None.
Классы как типы¶
Вы также можете объявлять класс как тип переменной.
Допустим, у вас есть класс Person с именем:
class Person:
def __init__(self, name: str):
self.name = name
def get_person_name(one_person: Person):
return one_person.name
🤓 Other versions and variants
class Person:
def __init__(self, name: str):
self.name = name
def get_person_name(one_person: Person):
return one_person.name
Тогда вы можете объявить переменную типа Person:
class Person:
def __init__(self, name: str):
self.name = name
def get_person_name(one_person: Person):
return one_person.name
🤓 Other versions and variants
class Person:
def __init__(self, name: str):
self.name = name
def get_person_name(one_person: Person):
return one_person.name
И снова вы получите полную поддержку редактора кода:

Обратите внимание, что это означает: «one_person — это экземпляр класса Person».
Это не означает: «one_person — это класс с именем Person».
Pydantic-модели¶
Pydantic — это библиотека Python для валидации данных.
Вы объявляете «форму» данных как классы с атрибутами.
И у каждого атрибута есть тип.
Затем вы создаёте экземпляр этого класса с некоторыми значениями — он провалидирует значения, преобразует их к соответствующему типу (если это применимо) и вернёт вам объект со всеми данными.
И вы получите полную поддержку редактора кода для этого результирующего объекта.
Пример из официальной документации Pydantic:
from datetime import datetime
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str = "John Doe"
signup_ts: datetime | None = None
friends: list[int] = []
external_data = {
"id": "123",
"signup_ts": "2017-06-01 12:22",
"friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123
Информация
Чтобы узнать больше о Pydantic, ознакомьтесь с его документацией.
FastAPI целиком основан на Pydantic.
Вы увидите намного больше всего этого на практике в Учебник - Руководство пользователя.
Подсказки типов с аннотациями метаданных¶
В Python также есть возможность добавлять дополнительные метаданные к подсказкам типов с помощью Annotated.
Вы можете импортировать Annotated из typing.
from typing import Annotated
def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
return f"Hello {name}"
🤓 Other versions and variants
from typing import Annotated
def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
return f"Hello {name}"
Сам Python ничего не делает с Annotated. А для редакторов кода и других инструментов тип по-прежнему str.
Но вы можете использовать это место в Annotated, чтобы передать FastAPI дополнительные метаданные о том, как вы хотите, чтобы ваше приложение себя вело.
Важно помнить, что первый параметр типа, который вы передаёте в Annotated, — это фактический тип. Всё остальное — просто метаданные для других инструментов.
Пока вам достаточно знать, что Annotated существует и это — стандартный Python. 😎
Позже вы увидите, насколько это мощно.
Совет
Тот факт, что это стандартный Python, означает, что вы по-прежнему получите лучший возможный разработческий опыт в вашем редакторе кода, с инструментами для анализа и рефакторинга кода и т.д. ✨
А ещё ваш код будет очень совместим со множеством других инструментов и библиотек Python. 🚀
Аннотации типов в FastAPI¶
FastAPI использует эти подсказки типов для выполнения нескольких задач.
С FastAPI вы объявляете параметры с подсказками типов и получаете:
- Поддержку редактора кода.
- Проверки типов.
...и FastAPI использует эти же объявления для:
- Определения требований: из path-параметров пути запроса, query-параметров, HTTP-заголовков, тел запросов, зависимостей и т.д.
- Преобразования данных: из HTTP-запроса к требуемому типу.
- Валидации данных: приходящих с каждого HTTP-запроса:
- Генерации автоматических ошибок, возвращаемых клиенту, когда данные некорректны.
- Документирования API с использованием OpenAPI:
- что затем используется пользовательскими интерфейсами автоматической интерактивной документации.
Всё это может звучать абстрактно. Не волнуйтесь. Вы увидите всё это в действии в Учебник - Руководство пользователя.
Важно то, что, используя стандартные типы Python в одном месте (вместо добавления дополнительных классов, декораторов и т.д.), FastAPI сделает за вас большую часть работы.
Информация
Если вы уже прошли всё руководство и вернулись, чтобы узнать больше о типах, хорошим ресурсом будет «шпаргалка» от mypy.