Программа курса:
Аннотации при помощи модуля typing
Переменные можно аннотировать типами, которые представляют собой коллекции
numbers: list = [] # переменная numbers хранит список
languages: dict = {} # переменная languages хранит словарь
temperature: tuple = (1, 2, 3) # переменная temperature хранит кортеж
letters: set = set('hello') # переменная letters хранит множествоТакая аннотация допускается, никаких проблем у вас не будет при использовании любой версии Python. Проблемы начнутся, если вы захотите указать аннотацию для элементов коллекции. Например, для numbers логично указать не просто список, а список целых или вещественных чисел. Для такой аннотации вам понадобится модуль typing
Модуль typing
Встроенный модуль typing позволяет создавать аннотации в более сложных случаях, например, если вы хотите проаннотировать переменную сразу несколькими типами или указать аннотацию для элементов коллекции.
Модуль typing широко применялся вплоть до версии python3.9 пока не появился стандарт . До версии python3.9 модуль typing был единственным способом проаннотировать составные объекты
В модуле хранится множество объектов, которые позволят вам выполнить любую аннотацию.
Аннотация коллекций
Вернемся к нашему примеру
numbers: list = [] # переменная numbers хранит список
languages: dict = {} # переменная languages хранит словарь
temperature: tuple = (1, 2, 3) # переменная temperature хранит кортеж
letters: set = set('hello') # переменная letters хранит множествоВ модуле typing для каждого типа данных python имеется соответствующий объект. Называться он будет также как встроенный тип данных, только имя начинается с заглавной буквы. Значит, за встроенный тип данных list в модуле typing отвечает объект List.
Для использования любого объекта из модуля typing его необходимо сперва импортировать.
from typing import List, Dict, Tuple, SetВ примере выше мы из модуля typing сразу импортировали четыре объекта: List, Dict, Tuple, Set. Они перечисляются через запятую после инструкции from typing import в любом порядке
Давайте перепишем данный код при помощи модуля typing
from typing import List, Dict, Tuple, Set
numbers: List = [1.1, 3.0, 4.5]
languages: Dict = {}
temperature: Tuple = (1, 2, 3)
letters: Set = set('hello')Теперь мы можем указать для каждой коллекции, какой тип данных должен быть у ее элементов. Вот как это делается:
from typing import List, Set
numbers: List[float] = [1.1, 3.0, 4.5]
letters: Set[str] = set('hello')Взгляните на аннотацию
numbers: List[float]здесь в квадратных скобках указывается, что в качестве элементов списка ожидаются вещественные числа.
По аннотации переменной letters мы понимаем, что в ней хранится множество строк.
letters: Set[str]Аннотация кортежей и словарей выполняется чуть иначе, рассмотрим их отдельно далее в этой лекции.
Объект Union
Во всех примерах аннотации до этого момента мы указывали только один единственный тип данных для переменной или параметра. Но представьте, что у вас есть переменная, которая может принимать значения сразу нескольких типов данных, например, быть целым или вещественным числом, а также сохранять логический тип. Если у вас возникает такая ситуация, вам на помощь может прийти объект Union. Его необходимо импортировать из модуля typing
from typing import UnionC помощью объекта Union можно указывать сразу несколько типов данных, которые могут быть сохранены в переменную. Для этого необходимо в квадратных скобках перечислить через запятую необходимые типы данных. Ниже представлен пример использования Union
param: Union[int, float, bool]При такой аннотации в переменной param могут храниться вещественные и целые числа, а также логические значения True и False.
Вот пример использования объекта при аннотации параметров функции и возвращаемого значения
from typing import Union
def add_numbers(a: Union[int, float],
b: Union[int, float]) -> Union[int, float]:
return a + b
print(add_numbers.__annotations__)
print(add_numbers(10, 25.7))
print(add_numbers(10, 25))В примере выше мы говорим, что входные параметры и возвращаемый результат могут быть целыми и вещественными числами.
Но если подумать, то операцию сложения можно производить также и над списками, строками, кортежами, типом bool. Если добавлять все перечисленные типы в аннотацию, получится очень громоздкая запись, которая не умещается в одну строку. Вот пример реализации такой функции, которая выполняет сложение с произвольным объектом
from typing import Union
def add(a: Union[int, float, str, list, bool],
b: Union[int, float, str, list, bool])\
-> Union[int, float, str, list, bool]:
return a + b
print(add.__annotations__)
print(add(10, 25.7))
print(add('hello', 'world'))
print(add([1, 2], [3, 4]))
print(add(True,True))В случаях, когда в аннотацию входят практически все встроенные объекты, можно подумать об использовании объекта Any.
Union полезна в версиях python до 3.10, поскольку в этой версии появилась короткая запись данной функции через вертикальную черту:
param: int| float| str
Объект Optional
Давайте вновь взглянем с вами на функцию append_to_list.
def append_to_list(value, my_list=None):
if my_list is None:
my_list = []
my_list.append(value)
return my_listМы разбирали ее на уроке «». Здесь вам стоит обратить внимание на параметр my_list. По умолчанию он принимает значение None, но в него также может быть передан список. Следовательно, всего имеется два возможных типа данных у параметра my_list. Его можно проаннотировать при помощи объекта Union
def append_to_list(value, my_list: Union[list, None] = None):
if my_list is None:
my_list = []
my_list.append(value)
return my_listЛибо можно воспользоваться специальным объектом Optional , который специально создан для аннотирования указанного типа и значения None. Не забудьте перед использованием импортировать объект Optional.
from typing import OptionalВот взгляните на примеры аннотации
from typing import Optional
num: Optional[int] = None
word: Optional[str] = NoneПеременная num из примера выше может теперь хранить в себе целые числа и также значение None. Переменная wordпроаннотирована строковым типом и еще пустым значением None. Обязательно после Optional тип указывается в квадратных скобках.
Инструкция Optional[int] эквивалента записи Union[None, int], а запись Optional[str] равнозначна записи Union[None, str].
Если не указать Optional у переменной word, то при попытке сохранить значение None IDE будет выдавать предупреждение о недопустимом типе данных.
Познакомившись с объектом Optional, давайте перепишем функцию append_to_list.
from typing import Optional
def append_to_list(value, my_list: Optional[list] = None):
if my_list is None:
my_list = []
my_list.append(value)
return my_list
Объект Any
Например, вы хотите указать при помощи аннотаций, что в переменной можно сохранить любой тип данных. В этом вам поможет объект Any из модуля typing
from typing import Any
value: Any
value = 10
print(value)
value = [1, 2, 3]
print(value)
value = {'hi': 'привет'}
print(value)Все в той же функции append_to_list у нас имеется параметр value, значение которого добавляется в список.
from typing import Optional
def append_to_list(value, my_list: Optional[list] = None):
if my_list is None:
my_list = []
my_list.append(value)
return my_listВ принципе, мы можем добавить совершенно любой тип данных в список, поэтому лучше проаннотировать данный параметр объектом Any и заодно указать, что функция возвращает список из любых элементов. Вот как это сделать
from typing import Optional, Any, List
def append_to_list(value: Any, my_list: Optional[list] = None) -> List[Any]:
if my_list is None:
my_list = []
my_list.append(value)
return my_list
Аннотация элементов кортежа
Помните, как мы аннотировали элементы списка?
from typing import List
numbers: List[float] = [1.1, 3.0, 4.5]Тут мы указали тип float для всех элементов списка. С кортежами такая схема не пройдет, потому что они, в отличие от списков, часто используются для разнотипных элементов.
Для кортежа в квадратных скобках нужно указывать тип каждого элемента по отдельности
from typing import Tuple
words: Tuple[str, int] = ("hello", 300)Если же планируется использовать кортеж аналогично списку: хранить неизвестное количество однотипных элементов, можно воспользоваться многоточием (...).
from typing import Tuple
words: Tuple[str, ...] = ("hello", "world", '!')При такой схеме мы указываем сразу всем элементам кортежа один тип данных str.
Аннотация словарей
Чтобы добавить аннотацию типа для словаря, необходимо использовать объект Dict модуля typing, и далее указать тип ключа и тип значения следующим образом:
from typing import Dict
Dict[key_type, value_type]Например, в словаре person и ключ, и значение представлены в виде строки:
person = { "first_name": "Джон", "last_name": "Доу"}Вы можете аннотировать его следующим образом:
from typing import Dict
person: Dict[str, str] = { "first_name": "John", "last_name": "Doe"}Тип Dict указывает, что ключи словаря person имеют тип str, а значения имеют тип str.
Давайте взглянем на более сложный пример
from typing import Dict, Optional, Union
def foo(bar: Dict[Union[str, int], Optional[str]]) -> bool:
return TrueЗдесь функция foo принимает один аргумент bar, он должен являться словарем, у которого ключи могут быть либо строкой либо целым числом, а значения могут быть либо пустыми (тип None) , либо строкой