Tuples / Кортежи в Python

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

В этом руководстве вы познакомитесь с кортежами Python в подробностях:

  • Узнаете, как их создавать. Увидите, что значит неизменяемый тип на примерах
  • Разберетесь, чем кортежи в Python отличаются от списков
  • Познакомитесь с операциями: срезами, конкатенацией, умножением и так далее
  • Увидите встроенные функции
  • Научитесь присваивать сразу несколько значений кортежами

Кортеж Python

Эта структура данных используется для хранения последовательности упорядоченных и неизменяемых элементов.

Кортежи создают с помощью круглых скобок (). Для создания нужно написать следующее:

cake = ('c','a','k','e') 
print(type(cake))
<class 'tuple'>

Примечание: type() — это встроенная функция для проверки типа данных переданного параметра.

Кортежи могут включать однородные и разнородные значения. Но после объявления их уже нельзя будет поменять:

mixed_type = ('C',0,0,'K','I','E')

for i in mixed_type:
    print(i,":",type(i))
C : <class 'str'>
0 : <class 'int'>
0 : <class 'int'>
K : <class 'str'>
I : <class 'str'>
E : <class 'str'>
# Попробуйте изменить 0 на «O»
mixed_type[1] = "O"
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-16-dec28c299a95> in <module>()
----> 1 mixed_type[1] = 'O'  # Попробуйте изменить 0 на «O»


TypeError: 'tuple' object does not support item assignment

Последняя ошибка появилась из-за попытки поменять значения внутри кортежа.

Кортежи можно создавать и вот так:

numbers_tuple = 1,2,3,4,5
print(type(numbers_tuple))
<class 'tuple'>

Кортежи против списков

Как вы могли заметить, кортежи очень похожи на списки. По сути, они являются неизменяемыми списками. Это значит, что после создания кортежа хранимые в нем значения нельзя удалять или менять. Добавлять новые также нельзя:

numbers_tuple = (1,2,3,4,5)
numbers_list = [1,2,3,4,5]

# Добавим число в кортеж
numbers_tuple.append(6)
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-26-e48876d745ce> in <module>()
      3 
      4 # Добавим число в кортеж
----> 5 numbers_tuple.append(6)


AttributeError: 'tuple' object has no attribute 'append'

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

# Добавим число в список
numbers_list.append(6)
numbers_list.append(7)
numbers_list.append(8)

# Удалить число из списка
numbers_list.remove(7)
print(numbers_list)
[1, 2, 3, 4, 5, 6, 8]

Но зачем использовать этот тип данных, если он неизменяемый?

Кортежи не только предоставляют доступ только для чтения к элементам, но и работают быстрее списков. Рассмотрим в качестве примера следующий код:

>>> import timeit
>>> timeit.timeit('x=(1,2,3,4,5,6,7,8,9)', number=100000)
0.0018976779974764213
>>> timeit.timeit('x=[1,2,3,4,5,6,7,8,9]', number=100000)
0.019868606992531568

Какую роль играет неизменяемость в случае с кортежами?

Согласно официальной документации Python неизменяемый — это «объект с фиксированным значением», но в отношении кортежей «значение» — это чересчур размытый термин. Лучше использовать id. id определяет расположения объекта в памяти.

Рассмотрим подробнее:

# Кортеж 'n_tuple' со списком в качестве одного из его элементов.
n_tuple = (1, 1, [3,4])

# Элементы с одинаковым значением имеют одинаковый идентификатор.
id(n_tuple[0]) == id(n_tuple[1])
True
# Элементы с разным значением имеют разные идентификаторы.
id(n_tuple[0]) == id(n_tuple[2])
False
print(id(n_tuple[0]), id(n_tuple[2]))
4297148528, 4359711048
n_tuple.append(5)
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-40-3cd388e024ff> in <module>()
----> 1 n_tuple.append(5)


AttributeError: 'tuple' object has no attribute 'append'

Добавить элемент в кортеж нельзя, поэтому появляется последняя ошибка AttributeError. Вот почему эта структура данных неизменна. Но всегда можно сделать вот так:

n_tuple[2].append(5)
n_tuple
(1, 1, [3, 4, 5])

Это позволяет изменять оригинальный кортеж? Куда в таком случае делась их неизменяемость?

Суть в том, что id списка в кортеже не меняется несмотря на добавленный в него элемент 5.

id(n_tuple[2])
4359711048

Теперь вы знаете следующее:

Некоторые кортежи (которые содержат только неизменяемые объекты: строки и так далее) — неизменяемые, а другие (содержащие изменяемые типы, например, списки) изменяемые. Но это очень обсуждаемая тема среди программистов на Python и необходимы кое-какие познания, чтобы полностью понять ее. В целом же кортежи неизменяемые.

  • Вы не можете добавлять в них новые элементы. У этого типа нет методов append() или extend()
  • Удалять элементы тоже нельзя, также из-за неизменяемости. Методов remove() и pop() здесь нет
  • Искать элементы в кортеже можно, потому что этот процесс его не меняет
  • Разрешено использовать оператор in для проверки наличия элемента в кортеже

Так что если вы планируете использовать постоянный набор значений для перебора, используйте кортеж вместо списка. Он будет работать быстрее. Плюс, это безопаснее, ведь такой тип данных защищен от записи.

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

Стандартные операции с кортежами

Python предоставляет несколько способов для управления кортежами. Рассмотрим их на примерах.

Срезы

Значение индекса первого элемента в кортеже — 0. По аналогии со списками эти значения можно использовать с квадратными скобками [] для получения доступа к кортежам:

numbers = (0,1,2,3,4,5)
numbers[0]
0

Можно использовать и отрицательные значения:

numbers[-1]
5

Индексы позволяют получать отдельные элементы, а с помощью срезов становятся доступны и подмножества. Для этого нужно использовать диапазоны индексов:

[Начальный индекст (включен):Конечный индекс (исключен):Частота]

Частота в данном случае является опциональным параметром, а его значение по умолчанию равно 1.

# Элемент с индексом 4 исключен
numbers[1:4]
(1, 2, 3)
# Это возвращает все элементы в кортеже
numbers[:]
(0, 1, 2, 3, 4, 5)
# Частота = 2
numbers[::2]
(0, 2, 4)

Совет: значение частоты может быть и негативным, чтобы развернуть кортеж.

numbers[::-1]
(5, 4, 3, 2, 1, 0)

Объединение кортежей

Можно объединять кортежи для создания нового объекта. Операция объединения выполняет конкатенацию двух кортежей.

x = (1,2,3,4)
y = (5,6,7,8)

# Объединение двух кортежей для формирования нового кортежа
z = x + y 
print(z)
(1, 2, 3, 4, 5, 6, 7, 8)
y = [5,6,7,8]
z = x + y
print(z)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-55-d442c6414a4c> in <module>()
      1 y = [5,6,7,8]
----> 2 z = x + y
      3 print(z)


TypeError: can only concatenate tuple (not "list") to tuple

Разрешается объединять только определенные типы данных. Так, попытка соединить кортеж и список закончится ошибкой.


Умножение кортежей

Операция умножения приводит к тому, что кортеж повторяется несколько раз.

x = (1,2,3,4)
z = x*2
print(z)
(1, 2, 3, 4, 1, 2, 3, 4)

Функции кортежей

В отличие от списков у кортежей нет методов, таких как append(), remove(), extend(), insert() или pop() опять-таки из-за их неизменяемости. Но есть другие:

count() и len()

count() возвращает количество повторений элемента в кортеже.

a = [1,2,3,4,5,5]
a.count(5)
2

len() — длину кортежа:

a = (1,2,3,4,5)
print(len(a))
5

any()

Функцию any() можно использовать, чтобы определить являются ли элементы кортежа итерируемыми. Если да, то она вернет True.

a = (1,)
print(any(a))
True

Обратите внимание на запятую (,) в объявлении кортежа a. Если ее не указать при создании объекта с одним элементом? Python предположит, что вы по ошибке добавили лишнюю пару скобок (это ни на что не влияет), но тип данных в таком случае — это не кортеж. Поэтому важно не забывать использовать запятую при объявлении кортежа с одним элементом.

И снова к функции any. В булевом контексте значение элемента не имеет значения. Пустой кортеж вернет False, а кортеж с хотя бы одним элементом — True.

b = ()
print(any(b))
False

Функция может быть полезной, если кортеж вызывается? и нужно удостовериться, что он не пустой.

tuple()

Функция tuple() используется для конвертации данных в кортеж. Например, так можно превратить список в кортеж.

a_list = [1,2,3,4,5]
b_tuple = tuple(a_list)
print(type(b_tuple))
<class 'tuple'>

min() и max()

Функция max()q возвращает самый большой элемент последовательности, а min() — самый маленький. Возьмем следующий пример:

print(max(a))
print(min(a))
5
A

Эти функции можно использовать и для кортежей со строками.

# Строка «Apple» автоматически преобразуется в последовательность символов.
a = ('Apple') 
print(max(a))
p

sum()

С помощью этой функции можно вернуть сумму элементов в кортеже. Работает только с числовыми значениями.

sum(a)
28

sorted()

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

a = (6,7,4,2,1,5,3)
sorted(a)
[1, 2, 3, 4, 5, 6, 7]

Но важно отметить, что возвращаемый тип — список, а не кортеж. При этом последовательность в оригинальном объекте неизменна, а сам он остается кортежем.

Присваивание несколько кортежей

Кортежи можно использовать для присваивания нескольких значений одновременно. Вот так:

a = (1,2,3)
(one,two,three) = a
print(one)
1

a — это кортеж из трех элементов и (one, two, three) — кортеж трех переменных. Присваивание (one, two, three) кортежу a присваивает каждое значение a каждой переменной: one, two и three по очереди. Это удобно, если нужно присвоить определенному количеству переменных значений в кортеже.

Выводы

Теперь вы знаете, что такое кортежи, как их создавать, какие самые распространенные операции, и как ими можно управлять. Также — распространенные методы структур Python. А в качестве бонуса научились присваивать нескольким переменным разные значения.