Python 3. Кортежи

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

Свойства, индексация и срезы

Я уже писал про последовательности и множества в Python 3. Кортеж (tuple) — это упорядоченная неизменяемая последовательность объектов. Простыми словами, это почти то же самое, что список , только после создания его нельзя изменить: нельзя добавить, удалить или заменить элемент. Если нужен другой набор значений, создаётся новый кортеж.

Кортежи в Python 3 записываются в круглых скобках, например так:

Код:

my_tuple = (1, 2, 'три')

В коде выше мы создали кортеж (1, 2, ‘три’) и связали его с переменной my_tuple. Как видите, наш кортеж хранит и числа, и строки. Элементы в кортеже могут быть любого типа, в том числе разных.

Кортеж, так же как и список, нумерует (индексирует) свои элементы:

Индексация позволяет обращаться к объектам по индексу (положительному или отрицательному), а также создавать срезы. Нумерация с начала идёт с нуля, а с конца — с -1.

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

Код:

my_tuple = (1, 2, 3, 'четыре')

print('Тип объекта:', type(my_tuple))
print('Длина:', len(my_tuple))
print('Первый элемент:', my_tuple[0])
print('Последний элемент:', my_tuple[-1])
print('Срез [1:3]:', my_tuple[1:3])
print('Срез [0:3:2]:', my_tuple[0:3:2])     # С шагом 2
print('Срез [::-1]:', my_tuple[::-1])       # Кортеж в обратном порядке

Исполнение:

Тип объекта: <class 'tuple'>
Длина: 4
Первый элемент: 1
Последний элемент: четыре
Срез [1:3]: (2, 3)
Срез [0:3:2]: (1, 3)
Срез [::-1]: ('четыре', 3, 2, 1)

Запомните, когда вы создаёте срез, то указываете, от какого элемента (включительно) и до какого элемента (не включительно) нужно взять значения. Также можно указать шаг среза, например каждый второй элемент или каждый третий. Если указанная граница выходит за пределы кортежа, ошибки не будет: Python просто вернёт то, что есть. Именно поэтому срез [1:4] у кортежа из трёх элементов работает без проблем.

Срез всегда возвращает новый кортеж, а исходный остаётся без изменений.

Как создать кортеж

Кортеж можно создать несколькими способами.

Пустой кортеж создаётся пустыми скобками или функцией tuple():

Код:

empty1 = ()
empty2 = tuple()

print(empty1, type(empty1))
print(empty2, type(empty2))

Исполнение:

() <class 'tuple'>
() <class 'tuple'>

Кортеж из одного элемента создаётся с обязательной запятой. Это самая частая ошибка новичков. Дело в том, что просто скобки Python воспринимает как группировку выражения, а не как кортеж:

Код:

not_tuple = (5)
my_tuple = (5,)

print(type(not_tuple))
print(type(my_tuple))

Исполнение:

<class 'int'>
<class 'tuple'>

Получается, что кортеж создаёт именно запятая, а не скобки. Поэтому скобки можно и вовсе не писать:

Код:

my_tuple = 1, 2, 3

print(my_tuple)
print(type(my_tuple))

Исполнение:

(1, 2, 3)
<class 'tuple'>

Тем не менее я рекомендую всё же использовать скобки: так код читается лучше, и сразу видно, что это кортеж.

Преобразование в кортеж другой последовательности или итерируемого объекта делается функцией tuple():

Код:

print(tuple('abc'))
print(tuple([1, 2, 3]))
print(tuple(range(5)))

Исполнение:

('a', 'b', 'c')
(1, 2, 3)
(0, 1, 2, 3, 4)

Кортеж нельзя изменить

Если попытаться изменить элемент кортежа, Python выдаст ошибку TypeError:

Код:

my_tuple = (1, 2, 'три')
my_tuple[0] = 10

Исполнение:

TypeError: 'tuple' object does not support item assignment

Такая же ошибка будет при попытке удалить элемент (del my_tuple[0]). У кортежей также нет методов append(), remove(), sort() и других методов, которые меняют содержимое.

Работа с кортежами в Python

Точно так же как строки и списки, кортежи поддерживают конкатенацию (объединение) и повторение:

Код:

my_tuple1 = (1, 2, 'три')
my_tuple2 = (4, 5, 'шесть')

print(my_tuple1 + my_tuple2)
print(my_tuple1 * 3)

Исполнение:

(1, 2, 'три', 4, 5, 'шесть')
(1, 2, 'три', 1, 2, 'три', 1, 2, 'три')

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

Код:

my_tuple = (1, 2)
my_tuple += (3,)

print(my_tuple)

Исполнение:

(1, 2, 3)

Также можем проверить, содержится ли элемент в кортеже:

Код:

my_tuple = (1, 2, 'три')

print(1 in my_tuple)
print(5 in my_tuple)

Исполнение:

True
False

У кортежей есть всего два собственных метода. Метод count() подсчитывает, сколько раз элемент встречается в кортеже, а метод index() возвращает индекс первого найденного элемента:

Код:

my_tuple = (1, 2, 5, 2, 7, 1, 2, 4, 0)

print(my_tuple.count(2))
print(my_tuple.index(7))

Исполнение:

3
4

Если элемента в кортеже нет, метод index() вызовет ошибку ValueError, поэтому перед его использованием можно проверить наличие элемента через in.

Для кортежей также работают встроенные функции len(), min(), max(), sum() и sorted(). Обратите внимание, что sorted() всегда возвращает список, а не кортеж:

Код:

numbers = (3, 1, 2)

print(min(numbers))
print(max(numbers))
print(sum(numbers))
print(sorted(numbers))

Исполнение:

1
3
6
[1, 2, 3]

Перебирать кортеж можно в цикле for, как и любую другую последовательность:

Код:

servers = ('pve1', 'pve2', 'pbs1')

for server in servers:
    print(server)

Исполнение:

pve1
pve2
pbs1

Как «изменить» кортеж

Если всё-таки нужно изменить кортеж, его обычно превращают в список, вносят изменения, а затем превращают обратно в кортеж:

Код:

my_tuple = (1, 2, 3)

my_list = list(my_tuple)    # Кортеж -> список
my_list.append(4)           # Меняем список
my_tuple = tuple(my_list)   # Список -> кортеж

print(my_tuple)

Исполнение:

(1, 2, 3, 4)

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

Распаковка кортежей

Одна из самых удобных возможностей кортежей — распаковка. Вы можете разложить элементы кортежа по отдельным переменным за одну строку:

Код:

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

print(a, b, c)

Исполнение:

1 2 3

Количество переменных слева должно совпадать с количеством элементов в кортеже, иначе будет ошибка ValueError. Если нужно забрать только часть элементов, а остальные собрать вместе, используют звёздочку. Остаток при этом попадёт в список:

Код:

my_tuple = (1, 2, 3)
a, *rest = my_tuple

print(a, rest)

Исполнение:

1 [2, 3]

Благодаря распаковке в Python можно поменять значения двух переменных местами без временной переменной. На самом деле в этой записи справа создаётся кортеж, а слева он распаковывается:

Код:

a = 1
b = 2
a, b = b, a

print(a, b)

Исполнение:

2 1

Ещё кортежи используют, когда функция должна вернуть несколько значений. Фактически функция возвращает один кортеж, который можно сразу распаковать:

Код:

def min_max(numbers):
    return min(numbers), max(numbers)

result = min_max([4, 2, 9])
print(result)

lowest, highest = min_max([4, 2, 9])
print(lowest, highest)

Исполнение:

(2, 9)
2 9

Неизменяемость и вложенные объекты

Здесь есть один тонкий момент. Кортеж неизменяемый, но это относится только к самому кортежу: к набору объектов, которые в нём лежат, и к их порядку. Если внутри кортежа находится изменяемый объект, например список, то сам этот объект изменить можно:

Код:

my_tuple = (1, [2, 3])
my_tuple[1].append(4)

print(my_tuple)

Исполнение:

(1, [2, 3, 4])

Кортеж по-прежнему хранит те же самые два объекта, просто один из них (список) поменял своё содержимое. Поэтому, если вам нужна полная неизменяемость, внутри кортежа тоже должны быть неизменяемые объекты: числа, строки, другие кортежи.

Зачем нужны кортежи, если есть списки

Основных причин несколько:

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

Пример использования кортежа в качестве ключа словаря:

Код:

cities = {
    (55.75, 37.62): 'Москва',
    (59.93, 30.34): 'Санкт-Петербург',
}

print(cities[(55.75, 37.62)])

Исполнение:

Москва

Если понравилась статья, подпишись на мой канал в VK или Telegram .