popov . dev

Главная

Библиотека

Статьи

Обзор возможност...

Обзор возможностей библиотеки typing в Python

Python, традиционно известный своей динамической типизацией, открыл новую эру ясности кода и предотвращения ошибок с появлением библиотеки typing. Изначально доступный в качестве бэкпорта для версий старше 3.5, typing стал стандартной частью стандартной библиотеки языка в Python 3.5 под названием PEP 484. Это дополнение ознаменовало значительный сдвиг, предоставив разработчикам возможность создавать аннотации к своему коду подсказками по типу, тем самым сочетая гибкость Python с преимуществами статической типизации.

Этот модуль постоянно совершенствовался, в последующих версиях Python добавлялись новые функции и типы.

Подсказки типов: применение статической типизации в Python

До появления Python 3.5 разработчикам, которые хотели включать аннотации к типам в свой код, приходилось полагаться на внешние библиотеки. С появлением модуля typing в Python 3.5 аннотации к типам стали неотъемлемой частью языка, что повысило читаемость и удобство сопровождения кода.

Модуль typing Python оказал глубокое влияние на то, как разработчики создают и понимают код Python. Эта библиотека привносит форму статической проверки типов в язык, исторически известный своей очень динамичной природой. Благодаря включению подсказок по типу в виде функциональных аннотаций и соответствующих комментариев, модуль typing значительно улучшает читаемость и понятность кода. Он также позволяет лучше отслеживать ошибки во время разработки, позволяя средствам проверки типов проверять соответствие кода заданным типам.

Преимущества использования подсказок типа:

  • Улучшенная читаемость и удобство сопровождения кода: подсказки о типах делают код самодокументируемым, поскольку в них явно указаны ожидаемые типы переменных, функций и других элементов кода. Это может облегчить разработчикам понимание и поддержку кода, как для себя, так и для других.
  • Уменьшение количества ошибок: средства проверки типов могут выявлять ошибки на ранних этапах разработки, прежде чем они могут привести к ошибкам в рабочей среде. Это может сэкономить разработчикам время и усилия при отладке и исправлении ошибок.
  • Упрощение адаптации новых разработчиков: подсказки по типам могут помочь новым разработчикам быстрее разобраться в кодовой базе, поскольку они могут видеть ожидаемые типы переменных и функций.
  • Улучшенная поддержка инструментов: многие средства статической проверки типов и интегрированные среды разработки (IDE) могут обеспечить улучшенную поддержку кода с подсказками по типу. Это может включать такие функции, как завершение кода, рефакторинг и проверка типов.

Основные возможности библиотеки typing

Основные подсказки

Подсказки для базовых типов данных просты. Например, целочисленная переменная может быть аннотирована следующим образом:

num: int = 5

Функциональные аннотации

Функции могут быть снабжены аннотациями, указывающими типы их аргументов и тип возвращаемого значения. Например:

def add(x: int, y: int) -> int:
    """Складывает два числа
    Args:
        x: Первое число
        y: Второе число
    Returns:
        Сумма x и y.
    """
    return x + y

result = add(5, 3)
print(result)  # Вывод: 8

# Это вызовет ошибку TypeError, поскольку 3.14 дробь
result = add(5, 3.14)

# Также вызовет ошибку TypeError, потому что "привет" это не число
result = add("привет", 3)

В этом примере функция add с подсказкой типа принимает два целых числа и возвращает целое число. Средство проверки типов может проверить, соответствует ли код этим типам, и выдаст ошибку, если это не так.

Тип Optional

Тип Optional - это мощная функция, указывающая на то, что переменная или возвращаемый тип могут быть заданного типа или None. Это особенно полезно в функциях, где не всегда может быть указан аргумент или возвращаемое значение.

from typing import Optional

def get_name(id: str) -> Optional[str]
    # Возвращает имя человека если найден id, иначе None
    ...

Тип Any

Тип Any используется, когда тип переменной неизвестен и может быть любым. Этот тип, по сути, обходит проверку типов, обеспечивая гибкость при необходимости.

from typing import Any

def some_function(argument: Any) -> Any:
    # Получает и возвращает любой тип
    ...

Тип List

Чтобы определить список, в котором все элементы относятся к определенному типу, используйте тип List из модуля typing, за которым следует тип элементов в квадратных скобках.

from typing import List

# Список целых чисел
numbers: List[int] = [1, 2, 3, 4, 5]

# Список строк
names: List[str] = ["Петя", "Вася", "Игорь"]

Тип Tuple

Кортежи (tuple) могут содержать элементы разных типов. Вы можете указать тип каждого элемента в кортеже, используя тип Tuple, а затем типы элементов в круглых скобках.

from typing import Tuple

# Кортеж, содержащий целое число и строку
person: Tuple[int, str] = (1, "Петя")

# Кортеж со строкой, дробью и логическим значением
item: Tuple[str, float, bool] = ("Книга", 19.99, True)

Если вы хотите указать кортеж, содержащий элементы того же типа, но вам не нужно указывать количество элементов, вам нужно использовать Tuple с типом элемента, за которым следует многоточие. Однако это специфическое использование более точно представлено в Python 3.9 и более поздних версиях с использованием встроенного типа tuple, благодаря PEP 585: Type Hinting Generics In Standard Collections.

В версиях Python до 3.9, где PEP 585 не реализован, вы обычно используете Tuple из модуля typing для кортежей фиксированного размера с различными типами. Если бы вы хотели указать кортеж переменной длины с элементами одного типа, вы бы использовали Tuple[Type, ...].

Для Python 3.8 и более ранних версий (используем typing.Tuple):

from typing import Tuple

# Кортеж с любым названием элементов str
names: Tuple[str, ...] = ("Игорь", "Петя", "Вася")

Для Python 3.9 и более поздних версий (используем стандартный тип tuple):

# Кортеж с любым названием элементов str
names: tuple[str, ...] = ("Игорь", "Петя", "Вася")

Тип Dict (словарь)

Словари сопоставляют ключи со значениями, и вы можете указать типы как ключей, так и значений. Используйте тип Dict из модуля typing, указывая в квадратных скобках тип ключа и значения.

from typing import Dict

# Словарь со строковыми ключами и численными значениями
age_map: Dict[str, int] = {"Игорь": 30, "Вася": 25}

# Словарь с численными ключами
# и списками строк в качестве значений
students_in_classes: Dict[int, List[str]] = {101: ["Игорь", "Вася"], 102: ["Ирина", "Катя"]}

Тип Union

Чтобы использовать Union, нужно указать типы, которые он может принимать в качестве аргументов, в квадратных скобках, разделенных запятыми. Синтаксис выглядит следующим образом:

from typing import Union

# Эта переменная может быть как int, так и str
number_or_string: Union[int, str] = 5

Варианты использования Union

1. Параметры функции: когда функция может принимать аргументы более чем одного типа.

def process_input(value: Union[int, str]) -> Union[float, str]:
    if isinstance(value, str):
        return f"Получена строка: {value}"
    else:
        return float(value)

В этой функции value может быть либо целым числом, либо строкой, и она возвращает либо значение с дробью, либо строку в зависимости от входных данных.

2. Возвращаемые значения: когда функция может возвращать значения разных типов.

from typing import Union

def get_data(key: str) -> Union[dict, list, str]:
    # Представьте себе функцию, которая может возвращать
    # словарь, список или строку на основе заданного ключа
    ...

3. Переменные: для аннотирования переменных, которые могут содержать значения разных типов в течение своего жизненного цикла.

result: Union[int, None] = None

# Позже результату может быть присвоено
# целочисленное значение
result = 10

Union в Python 3.10 и более поздних версиях

Начиная с Python 3.10, с появлением PEP 604, был введен более упрощенный синтаксис для объединений с использованием оператора |, что сделало использование Union менее многословным и более удобным для чтения. Приведенные выше примеры с использованием Union можно переписать с помощью этого нового синтаксиса следующим образом:

# Переменная может быть int или str
number_or_string: int | str = 5

# Пример функции с новым синтаксисом
def process_input(value: int | str) -> float | str:
    if isinstance(value, str):
        return f"Получена строка: {value}"
    else:
        return float(value)

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

Алиасы типов (псевдонимы типов)

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

from typing import List, Tuple, Union, Dict

# Упрощение сложного списка кортежей
Coordinates = List[Tuple[int, int]]
points: Coordinates = [(1, 2), (3, 4)]

# Использование Union для переменной,
# которая может быть как int, так и string
IntOrString = Union[int, str]
data: IntOrString = 42  # Также это может быть строка

# Для словаря со строковыми ключами и значениями,
# которые являются либо целыми числами,
# либо дробными числами
ValueDict = Dict[str, Union[int, float]]
my_dict: ValueDict = {"age": 30, "temperature": 98.6}

Использование typing.NewType

typing.NewType используется для создания различных типов в коде, которые требуют строгой проверки типов. Несмотря на то, что новый тип является эквивалентом исходного типа во время выполнения (это означает, что они одинаковы для интерпретатора Python), средства статической проверки типов рассматривают их как разные типы, что помогает избежать путаницы в сложных кодовых базах.

from typing import NewType

# Создаем новые типы для ID пользователя
# и ID продукта для того чтобы их не путать
UserID = NewType('UserID', int)
ProductID = NewType('ProductID', int)

def get_user_name(user_id: UserID) -> str:
    # Функция требует UserID
    ...

def get_product_name(product_id: ProductID) -> str:
    # Функция требует ProductID
    ...

user_id = UserID(523)
product_id = ProductID(987)

# Теперь эти вызовы защищены от ввода, что предотвращает путаницу
user_name = get_user_name(user_id)
product_name = get_product_name(product_id)

Заключение

Внедрение и эволюция модуля typing в Python ознаменовали значительный сдвиг в сторону повышения качества кода, удобочитаемости и сопровождаемости в сообществе Python. Первоначально представленный в Python 3.5 модуль typing предоставил формальный способ указания типов переменных, параметров функций и возвращаемых значений. С тех пор эта функция развивалась, и в последующих версиях Python появился более сложный и оптимизированный синтаксис для аннотаций типов и подсказок.

Одной из главных вех в эволюции системы типов Python стало введение аннотаций переменных в Python 3.6, которые позволили создавать более краткие и удобочитаемые подсказки по типу. Python 3.8 внес дополнительный вклад, введя, среди прочего, типы TypedDict, Literal и более гибкий тип Union, что повысило ясность аннотаций.

Python 3.9 и более поздние версии продолжили эту тенденцию, представив оператор объединения | в качестве более простой альтернативы typing.Union и создание аннотаций со встроенными типами коллекций (такими как list и dict), которые можно использовать напрямую, без необходимости импорта их из typing. Функция сопоставления с шаблоном в Python 3.10 представила еще более сложные способы использования типов для управления потоком.

Псевдонимы типов и typing.NewType предоставил разработчикам мощные инструменты для создания более удобочитаемого и безопасного кода. Псевдонимы типов упрощают сложные подсказки типов, делая код более понятным и поддерживаемым. NewType, с другой стороны, создает типы, отличные от существующих, обеспечивая более строгую проверку типов и уменьшая количество ошибок за счет предотвращения путаницы типов.

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

Подводя итог, можно сказать, что модуль typing и улучшения в более поздних версиях Python существенно повлияли на то, как разработчики Python пишут и думают о своем коде. Эти функции способствуют написанию более удобного в обслуживании, эффективного и безошибочного кода, подчеркивая эволюцию Python как языка, удовлетворяющего широкому спектру парадигм программирования и потребностей разработчиков.

Комментарии

Для того чтобы оставить свое мнение, необходимо зарегистрироваться на сайте