Перейти до змісту

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

🌐 Переклад ШІ та людьми

Цей переклад виконано ШІ під керівництвом людей. 🤝

Можливі помилки через неправильне розуміння початкового змісту або неприродні формулювання тощо. 🤖

Ви можете покращити цей переклад, допомігши нам краще спрямовувати AI 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.

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

  • int
  • float
  • bool
  • bytes
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 строк» буде оголошений як list[str].

Ці типи, які можуть приймати параметри типів, називаються generic типами або generics.

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

  • list
  • tuple
  • set
  • dict

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 and 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 використовує ті самі оголошення для:

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

Все це може здатися абстрактним. Не хвилюйтеся. Ви побачите все це в дії в Навчальний посібник - Посібник користувача.

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

Інформація

Якщо ви вже пройшли весь навчальний посібник і повернулися, щоб дізнатися більше про типи, ось хороший ресурс: «шпаргалка» від mypy.