Переменные можно аннотировать типами, которые представляют собой коллекции
numbers: list = [] # переменная numbers хранит список
languages: dict = {} # переменная languages хранит словарь
temperature: tuple = (1, 2, 3) # переменная temperature хранит кортеж
letters: set = set('hello') # переменная letters хранит множество
Такая аннотация допускается, никаких проблем у вас не будет при использовании любой версии Python. Проблемы начнутся, если вы захотите указать аннотацию для элементов коллекции. Например, для numbers логично указать не просто список, а список целых или вещественных чисел. Для такой аннотации вам понадобится модуль 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
. Его необходимо импортировать из модуля typing
from typing import Union
C помощью объекта 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
Давайте вновь взглянем с вами на функцию 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
из модуля 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
) , либо строкой