Зачем нужна коллекция, которую нельзя менять
Со списками вы уже работали: значения в квадратных скобках можно свободно добавлять, удалять, переписывать. Но представьте другую ситуацию: вы храните дату рождения человека, координаты города на карте или цвет в формате красный-зелёный-синий. Эти данные не должны меняться посреди программы — если кто-то случайно перезапишет один из трёх компонентов цвета, вы получите совсем другой цвет и не сразу поймёте, откуда взялась ошибка.
Для таких данных в Python есть отдельный тип коллекции — такой же упорядоченный, как список, но с одним важным отличием: после создания его нельзя изменить.
(), элементы разделяются запятыми. Слово встречается почти в каждой библиотеке Python, поэтому знать термин обязательно.Как создать кортеж
Проще всего перечислить значения через запятую в круглых скобках — так же, как список, только со скобками другой формы.
color = (255, 128, 0)
print(color)
print(type(color))
Разберём построчно.
color = (255, 128, 0)— три числа в скобках через запятую превращаются в один кортеж и сохраняются в переменнойcolor.print(color)— напечатает кортеж целиком:(255, 128, 0).print(type(color))— знакомая функцияtype()покажет<class 'tuple'>: перед нами кортеж, а не список.
Круглые скобки на самом деле необязательны — кортеж создаёт сама запятая. Это называют упаковкой: несколько значений через запятую Python молча упаковывает в один кортеж.
point = 10, 20
print(point)
print(type(point))
point = 10, 20— скобок нет, но запятая между числами уже говорит Python: собери из них кортеж.print(point)— выведет(10, 20), Python сам добавил скобки при печати.print(type(point))— снова<class 'tuple'>, хотя скобки мы не писали.
Есть и функция tuple() — она превращает в кортеж другую коллекцию, например список:
numbers = tuple([1, 2, 3])
print(numbers)
tuple([1, 2, 3])— на входе список, на выходе кортеж с теми же элементами в том же порядке:(1, 2, 3).
Если написать
x = (5), вы получите не кортеж, а обычное число 5 — Python решит, что скобки здесь просто группируют выражение, как в математике. Чтобы Python понял, что вы хотите кортеж из одного элемента, после значения нужно поставить запятую.
x = (5)
print(type(x))
y = (5,)
print(type(y))
x получит тип int, а y — tuple с единственным элементом. Разница в одной запятой, но она полностью меняет тип значения. Запомните это правило: одноэлементный кортеж — это всегда (значение,).
Неизменяемость: что будет, если попробовать изменить
Официальный термин для главного свойства кортежа — иммутабельность (от английского immutable, «неизменяемый»). Это значит, что после создания кортежа ни один его элемент нельзя ни заменить, ни удалить, ни добавить новый.
rgb = (255, 128, 0)
rgb[0] = 100
rgb = (255, 128, 0)— создаём кортеж из трёх чисел.rgb[0] = 100— пытаемся присвоить новое значение первому элементу, обратившись к нему по индексу, как в списке.
Эта строка не выполнится — программа остановится с ошибкой TypeError: 'tuple' object does not support item assignment. Дословно: «объект типа tuple не поддерживает присваивание по элементу». Это не сбой программы, а сознательная защита: вы сами объявили эти данные постоянными, и Python honestly её соблюдает.
Зачем вообще может понадобиться коллекция, которую нельзя менять? Кортеж защищает данные от случайной перезаписи где-то в середине большой программы, работает чуть быстрее списка, потому что Python не готовит его к изменениям, и делает код понятнее: увидев кортеж, любой программист сразу знает — эти значения зафиксированы.
Кортеж и список похожи — оба хранят несколько значений по порядку. Разница в том, можно ли их менять после создания:
| Свойство | Список | Кортеж |
|---|---|---|
| Скобки | [ ] |
( ) |
| Можно изменять после создания | Да | Нет |
| Методы | Много: append, insert, remove, sort и другие | Два: count и index |
| Когда использовать | Данные будут меняться: корзина, список задач | Данные фиксированы: координаты, дата, цвет |
Чтение элементов и два метода кортежа
Читать значения из кортежа можно точно так же, как из списка: индексы, срезы, отрицательные индексы для чтения с конца — здесь ничего нового, все правила из темы про списки работают и тут.
student = ("Алина", 19, "ФИТ", 4.8)
print(student[0])
print(student[-1])
print(student[1:3])
print(len(student))
print("ФИТ" in student)
student[0]— первый элемент по индексу 0, выведетАлина.student[-1]— последний элемент с конца, выведет4.8.student[1:3]— срез с индекса 1 до индекса 3 (не включая его), выведет(19, 'ФИТ')— срез кортежа тоже кортеж.len(student)— вернёт4, в кортеже четыре элемента."ФИТ" in student— проверка на вхождение, вернётTrue.
А методов у кортежа всего два — осознанное решение авторов Python: раз кортеж нельзя менять, ему незачем иметь методы вроде append или remove.
scores = (5, 3, 4, 5, 5, 2, 4)
print(scores.count(5))
print(scores.index(3))
scores.count(5)— методcount()считает, сколько раз значение встречается в кортеже; пятёрка встречается трижды, выведет3.scores.index(3)— методindex()возвращает индекс первого вхождения значения; тройка стоит на позиции 1, выведет1. Если бы искомого значения в кортеже не было, Python выбросил бы ошибкуValueError.
Распаковка: раскладываем кортеж по переменным
Допустим, у вас есть кортеж с тремя значениями, и каждое нужно положить в свою переменную. Можно писать name = student[0], age = student[1], faculty = student[2] — три строки с индексами. А можно одной строкой: перечислить имена переменных через запятую слева от знака равенства, и Python сам разложит значения по местам.
student = ("Алина", 19, "ФИТ")
name, age, faculty = student
print(name)
print(age)
print(faculty)
student = ("Алина", 19, "ФИТ")— кортеж из трёх значений.name, age, faculty = student— слева три имени через запятую, справа кортеж из трёх элементов. Python берёт элементы по порядку: первый элемент кортежа уходит вname, второй — вage, третий — вfaculty.- Дальше
name,ageиfaculty— обычные отдельные переменные, никакого кортежа в них уже нет.
ValueError.
a, b = (1, 2, 3)
a, b, c = (1, 2)
В первой строке слева две переменные, а справа три значения — лишнему деться некуда, будет ValueError: too many values to unpack. Во второй строке наоборот — переменных больше, чем значений, будет ValueError: not enough values to unpack.
Распаковка работает не только с кортежами, но и с любой упорядоченной коллекцией — например, со списком, потому что механизм один и тот же: элементы по порядку раскладываются по переменным. a, b, c = [10, 20, 30] сработает точно так же, как с кортежем.
Оператор * — когда часть элементов не важна поштучно
Бывает нужно взять только первый элемент, а всё остальное сложить в один список, не считая заранее, сколько там элементов. Для этого перед одной из переменных ставят звёздочку *. Она говорит Python: «всё, что не досталось остальным переменным, собери сюда одним списком».
numbers = (1, 2, 3, 4, 5)
first, *rest = numbers
print(first)
print(rest)
first, *rest = numbers—firstбез звёздочки получает ровно один элемент, первый. Звёздочка передrestозначает: собери туда всё, что осталось.print(first)— выведет1.print(rest)— выведет[2, 3, 4, 5]. Даже если исходный кортеж, звёздочка всегда собирает остаток именно в список, а не в кортеж.
Звёздочку можно поставить и в середине, и последней переменной — Python сам вычислит, сколько элементов отдать каждой обычной переменной, а остаток отдаст переменной со звёздочкой:
records = (2019, 2020, 2021, 2022, 2023)
first_year, *middle_years, last_year = records
print(first_year)
print(middle_years)
print(last_year)
first_yearбез звёздочки первым по счёту — получает первый элемент:2019.*middle_yearsв середине — заберёт всё, что осталось после того, какfirst_yearиlast_yearполучили по одному элементу:[2020, 2021, 2022].last_yearбез звёздочки последним — получает последний элемент:2023.
*a, *b = (1, 2, 3) Python не поймёт — это синтаксическая ошибка, а не ошибка выполнения: программа не запустится вообще.Обмен значений переменных местами
Представьте, что нужно поменять местами значения двух переменных. В большинстве языков программирования для этого заводят третью, временную переменную, чтобы ничего не потерять при перезаписи. В Python это делается одной строкой — благодаря той же упаковке и распаковке, которые мы только что разобрали.
a = 10
b = 20
a, b = b, a
print(a)
print(b)
a = 10иb = 20— исходные значения.a, b = b, a— ключевая строка. Справа Python сначала полностью вычисляет выражениеb, a: берёт текущие значения переменных (20 и 10) и упаковывает их в новый кортеж(20, 10). Только после этого начинается присваивание слева: кортеж распаковывается вaиb.print(a)иprint(b)— выведут20и10: значения поменялись местами.
Главное здесь — порядок действий: вся правая часть вычисляется до того, как хоть одна переменная слева поменяет значение, поэтому старые значения не «теряются» и временная переменная не нужна.
Распаковка прямо в заголовке цикла for
Когда в списке лежат кортежи — например, товары в формате «название, цена, количество» — распаковку можно делать прямо в заголовке цикла for, без отдельной строки присваивания.
products = [
("Молоко", 89.90, 2),
("Хлеб", 45.50, 3),
]
total = 0
for name, price, qty in products:
total += price * qty
print(total)
products— список, где каждый элемент — кортеж из трёх значений.for name, price, qty in products:— на каждом шаге цикла Python берёт очередной кортеж изproductsи сразу распаковывает его в три переменные:name,priceиqty. Без распаковки пришлось бы писатьitem[0],item[1],item[2]внутри цикла — так гораздо понятнее.total += price * qty— на каждом шаге к общей сумме прибавляется стоимость одной позиции.print(total)— после цикла выведет итоговую сумму по всем товарам.
Это тот же приём, что и при переборе пар «индекс, значение»: каждый шаг цикла отдаёт готовый кортеж, и вы сразу раскладываете его на переменные по тому же правилу распаковки.
Частые ошибки
size = (42) — это число, а не кортеж, нужна запятая: size = (42,). Программа не упадёт сразу, а поведёт себя не так, как вы ожидали: дальше size[0] вызовет TypeError, потому что у числа нет индексов.point = (1, 2), затем point[0] = 5 — TypeError. Если данные должны меняться, кортеж — неправильный выбор, нужен список.a, b = (1, 2, 3) — ValueError: too many values to unpack. Заранее считайте элементы или используйте звёздочку для сбора остатка.*a, *b = (1, 2, 3, 4) — синтаксическая ошибка ещё до запуска: звёздочка в присваивании может быть только одна.Что важно запомнить
- Кортеж — упорядоченная неизменяемая коллекция:
(1, 2, 3). Скобки создают упаковку, но реально кортеж создаёт запятая — скобки можно и не писать. - Одноэлементный кортеж — всегда с запятой:
(5,). Без запятой это просто число в скобках. - Изменить кортеж нельзя: любая попытка присвоить, добавить или удалить элемент завершится
TypeError. - Читать из кортежа можно так же, как из списка: индексы, отрицательные индексы, срезы,
len(),in. - У кортежа всего два метода:
count()считает вхождения значения,index()находит индекс первого вхождения. - Распаковка —
a, b, c = коллекция: раскладывает элементы по переменным одной строкой. Количество переменных и элементов должно совпадать, иначеValueError. - Звёздочка
*restсобирает всё, что не досталось обычным переменным, в список — даже если элемент один или их нет вовсе. В одном присваивании она может стоять только один раз. - Обмен переменных
a, b = b, aработает без временной переменной: правая часть целиком собирается в кортеж, и только потом распаковывается. - Распаковку можно делать прямо в заголовке цикла
for, когда перебираете коллекцию кортежей — это избавляет от обращения по индексам внутри цикла.
Проверьте себя
5 вопросов
Работа с кортежами
Даны переменные name, age и grade с данными одного студента.
- Соберите из них кортеж
studentв порядке (имя, возраст, оценка). - Получите из
studentобратно те же значения по индексам в переменныеname2,age2,grade2.
Дальше даны переменные a и b. Поменяйте их значения местами без временной переменной и сохраните результат в те же имена a и b.