June 8, 2020

Кортежи

До этого момента мы встречались только с примитивными типами — строка, число, булев тип. Ещё нам знакомы функции и модули с пакетами (да, в Python модули и пакеты, это тоже значения специальных встроенных типов). Но часто в программировании приходится создавать более сложные значения, чем числа и строки, и манипулировать такими значениями. Именно поэтому во многих языках программирования есть составные типы. Значение составного типа может состоять как из значений примитивных типов, так и из значений других составных типов, таким образом составные типы позволяют описывать сущности произвольной сложности — и шахматные доски, и космические ракеты!

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

Кортежи

Кортеж — это несколько значений, записанных через запятую. Да, всё очень просто! Вот несколько примеров:

rgb_colour = (255, 127, 64)
name_and_age = ('Bob', 42)
three_booleans = (True, False, True)
two_pairs_of_numbers = ((1, 2), (3, 4))

Определять кортежи очень просто, сложности могут возникнуть только с кортежами, содержащими ровно один элемент. Если мы просто укажем значение в скобках, то Python подумает, что мы хотим посчитать арифметическое выражение со скобками:

not_a_tuple = (42)  # 42

Чтобы сказать пайтону, что мы хотим создать именно кортеж, нужно поставить после элемента кортежа запятую:

tuple = (42,)  # (42,)

Да, форма записи довольно необычная, но вы привыкнете :)

Возврат нескольких значений из функции

Кортежи очень полезны, когда нам нужно вернуть из функции сразу несколько значений. Так, функция, которая принимает два аргумента-числа и возвращает одновременно результат деления нацело и остаток от деления, будет выглядеть так:

def div_mod(a, b):
    quotient = a // b
    modulo = a % b
    return (quotient, modulo)

div_mod(13, 4)  # (3, 1)

Получение элементов кортежа по индексу

Выше мы только создавали кортежи. Теперь научимся их разбирать! В простейшем случае достаточно обратиться к элементу кортежа по индексу:

name_and_age = ('Bob', 42)

name_and_age[0]  # 'Bob'
name_and_age[1]  # 42

Также у кортежа есть длина, которую, как и для строки, можно получить с помощью функции len:

tuple = (42,)  # (42,)
len(tuple)     # 1
pair = (1, 2)  # (1, 2)
len(pair)      # 2

Деструктуризация

Обращение по индексу, это не самый удобный способ работы с кортежами. Дело в том, что кортежи часто содержат значения разных типов, и помнить, по какому индексу что лежит — очень непросто. Но есть способ лучше! Как мы кортеж собираем, так его можно и разобрать:

name_and_age = ('Bob', 42)

(name, age) = name_and_age
name  # 'Bob'
age   # 42

Именно таким способом принято получать и сразу разбирать значения, которые возвращает функция (если таковая возвращает несколько значений, конечно):

(quotient, modulo) = div_mod(13, 4)

Соответственно кортеж из одного элемента нужно разбирать так:

(a,) = (42,)
a  # 42

Если же после имени переменной не поставить запятую, то синтаксической ошибки не будет, но в переменную a кортеж запишется целиком, т.е. ничего не распакуется. Всегда помните о запятых!

Кортежи, множественное присваивание и обмен значениями

Благодаря тому, что кортежи легко собирать и разбирать, в Python удобно делать такие вещи, как множественное присваивание. Смотрите:

(a, b, c) = (1, 2, 3)
a  # 1
b  # 2
c  # 3

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

a = 100
b = 'foo'

(a, b) = (b, a)
a  # 'foo'
b  # 100

Cтрочку (a, b) = (b, a) нужно понимать как "присвоить в а и b значения из кортежа, состоящего из значений переменных b и a".