В чем отличие списка от кортежа
Перейти к содержимому

В чем отличие списка от кортежа

  • автор:

Различия между списками и кортежами в Python

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

Пример проблемы

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

list_numbers = [1, 2, 3, 4, 5] tuple_numbers = (1, 2, 3, 4, 5)

Оба варианта выглядят похоже и ведут себя одинаково при выводе на экран или при выполнении цикла.

for number in list_numbers: print(number) for number in tuple_numbers: print(number)

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

Различия между списками и кортежами

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

list_numbers.append(6) # Добавляем элемент в список list_numbers[0] = 0 # Изменяем первый элемент списка

В то время как кортежи являются неизменяемыми, то есть после создания кортежа его нельзя изменить. Попытка сделать это приведет к ошибке.

tuple_numbers.append(6) # Вызовет ошибку tuple_numbers[0] = 0 # Вызовет ошибку

Преимущества и недостатки списков и кортежей

Списки:

  • Преимущества: списки изменяемы, что делает их гибкими в использовании.
  • Недостатки: из-за их изменчивости списки занимают больше памяти, чем кортежи.

Кортежи:

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

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

Python List vs. Tuple

difference between list and tuple in python

В этой статье будет рассмотрена разница между списком и кортежем (List vs. Tuple).

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

Важные различия между List и Tuple в Python

List (список) Tuple (Кортеж)
1 Списки изменяемы Кортежи неизменны
2 Последствия итераций отнимают много времени Последствия итераций сравнительно быстрее
3 Список лучше подходит для выполнения операций, таких как вставка и удаление. Тип данных Tuple подходит для доступа к элементам
4 Списки потребляют больше памяти Кортеж потребляет меньше памяти по сравнению со списком
5 Списки имеют несколько встроенных методов. Tuple не имеет большого количества встроенных методов.
6 Более вероятны непредвиденные изменения и ошибки В кортеже это трудно осуществить.

Список против кортежа

Проверим, являются ли кортежи неизменяемыми, а список изменяемым
Здесь мы собираемся сравнить тест изменчивости списка и кортежа.

# Creating a List with # the use of Numbers # code to test that tuples are mutable List = [1, 2, 4, 4, 3, 3, 3, 6, 5] print("Original list ", List) List[3] = 77 print("Example to show mutablity ", List)

Результат выполнения кода:

Исходный список [1, 2, 4, 4, 3, 3, 3, 6, 5] Пример для демонстрации изменчивости [1, 2, 4, 77, 3, 3, 3, 6, 5]

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

tuple1 = (0, 1, 2, 3) tuple1[0] = 4 print(tuple1)

Результат выполнения кода:

Traceback (most recent call last): File "tuple_example.py", line 3, in tuple1[0]=4 TypeError: 'tuple' object does not support item assignment

Проверка эффективности работы кортежей с памятью

Поскольку кортежи хранятся в одном блоке памяти, им не требуется дополнительное пространство для новых объектов, в то время как списки размещаются в двух блоках: первый фиксированный со всей информацией об объектах Python, а второй блок переменного размера для данных.

import sys a_list = [] a_tuple = () a_list = ["Lemons", "And", "Apples"] a_tuple = ("Lemons", "And", "Apples") print(sys.getsizeof(a_list)) print(sys.getsizeof(a_tuple))

Результат выполнения кода:

120 64

Проверка скорости итераций в Tuple по сравнению с List

import sys, platform import time l=list(range(100000001)) t=tuple(range(100000001)) start = time.time_ns() for i in range(len(t)): a = t[i] end = time.time_ns() print("Total lookup time for Tuple: ", end - start) start = time.time_ns() for i in range(len(l)): a = l[i] end = time.time_ns() print("Total lookup time for LIST: ", end - start)

Результат выполнения кода:

Общее время поиска для Tuple: 7038208700 Общее время поиска для LIST: 19646516700

Отличия кортежа от списка в Python

Кортеж и список относятся к типу последовательностей. Однако у них есть несколько существенных различий.

1) Кортеж — неизменяемый, список — изменяемый

Меняем список

Код ниже изменяет первый элемент списка.

fruits = ['яблоко', 'апельсин', 'банан'] fruits[0] = 'клубника' print(fruits)

Вывод

['клубника', 'апельсин', 'банан']

Как вы видите, элемент списка успешно изменился.

Не можем изменить кортеж

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

fruits = ('яблоко', 'апельсин', 'банан') fruits[0] = 'клубника' # Ошибка TypeError: 'tuple' object does not support item assignment

Кортеж можно пересоздать, если нужны изменения

В Python нельзя изменить элемент кортежа, но вы можете задать новый кортеж и ссылаться на него. Например, так:

fruits = ('яблоко', 'апельсин', 'банан') fruits = ('клубника', 'апельсин', 'банан')

В этом примере Python создает новый кортеж и привязывает переменную fruits к нему.

Если вы изучите адреса памяти объектов кортежа, то увидите, что переменная fruits ссылается на другой адрес памяти после присваивания:

fruits = ('яблоко', 'апельсин', 'банан') print(hex(id(fruits))) fruits = ('клубника', 'апельсин', 'банан') print(hex(id(fruits)))

Вывод

0x1c018286e00
0x1c018286e40

2) Кортежи занимают меньше памяти, чем списки

Список — неизменяемая последовательность. Это означает, что в него можно добавлять новые элементы. Из-за этого Python приходится выделять для списка больше памяти, чем нужно. Это называется перераспределением. Избыточное выделение повышает производительность при расширении списка, но увеличивает объект памяти.

Кортеж — неизменяемая последовательность, поэтому количество его элементов фиксировано. Это позволяет Python просто выделить достаточное количество памяти для хранения элементов, заданных при создании.

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

Давайте докажем это с помощью функции getsizeof() из модуля sys .

from sys import getsizeof fruits = ['яблоко', 'апельсин', 'банан'] print(f'Размер памяти для списка — байтов.') fruits = ('яблоко', 'апельсин', 'банан') print(f'Размер памяти для списка — байтов.') 

Вывод

Размер памяти для списка — 80 байтов.
Размер памяти для кортежа — 64 байтов.

3) Копирование кортежа быстрее, чем списка

Когда вы копируете список, Python создает новый список. Рассмотрим этот процесс на следующем примере:

fruit_list = ['яблоко', 'апельсин', 'банан'] fruit_list2 = list(fruit_list) print(id(fruit_list) == id(fruit_list2)) # Вывод: False 

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

fruit_tuple = ('яблоко', 'апельсин', 'банан') fruit_tuple2 = tuple(fruit_tuple) print(id(fruit_tuple) == id(fruit_tuple2)) # Вывод: True

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

Давайте проверим это на следующем примере. Сравним время, необходимое для копирования списка и кортежа 1 миллион раз:

from timeit import timeit times = 1_000_000 t1 = timeit("list(['яблоко', 'апельсин', 'банан'])", number=times) print(f'Время для копирования списка раз: ') t2 = timeit("tuple(('яблоко', 'апельсин', 'банан'))", number=times) print(f'Время для копирования кортежа раз: ') diff = "".format((t2 - t1)/t1) print(f'Разница: ')

Вывод

Время для копирования списка 1000000 раз: 0.12854695800000115
Время для копирования кортежа 1000000 раз: 0.03695795800000212
Разница: -71%

Что нужно запомнить

  • Кортеж неизменяем, список — изменяем.
  • Кортеж занимает меньше памяти, чем список.
  • Копирование кортежа происходит немного быстрее, чем копирование списка.
  • Используйте кортеж, если вам нужен список, который вы не собираетесь изменять.

СodeСhick.io — простой и эффективный способ изучения программирования.

2023 © ООО «Алгоритмы и практика»

Оптимизации, используемые в Python: список и кортеж

В Python, есть два похожих типа — список (list) и кортеж (tuple). Самая известная разница между ними состоит в том, что кортежи неизменяемы.

Вы не можете изменить объекты в tuple:

>>> a = (1,2,3) >>> a[0] = 10 Traceback (most recent call last): File "", line 1, in TypeError: 'tuple' object does not support item assignment 

Но вы можете модифицировать изменяемые объекты внутри кортежа:

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

Внутри CPython (стандартного интерпретатора), список и кортеж реализованы как лист из указателей (ссылок) на Python объекты, т.е. физически они не хранят объекты рядом с друг другом. Когда вы удаляете объект из списка происходит удаление ссылки на этот объект. Если на объект ещё кто-то ссылается, то он продолжит находиться в памяти.

Кортежи

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

Вы можете не замечать, но вы используете кортежи когда:

  • работаете с аргументами или параметрами (они хранятся как кортежи)
  • возвращаете две или более переменных из функции
  • итерируете ключи-значения в словаре
  • используете форматирование строк
>>> import gc >>> def type_stats(type_obj): . count = 0 . for obj in gc.get_objects(): . if type(obj) == type_obj: . count += 1 . return count . >>> type_stats(tuple) 3136 >>> type_stats(list) 659 >>> import pandas >>> type_stats(tuple) 6953 >>> type_stats(list) 2455 
Пустые списки vs пустые кортежи

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

>>> a = () >>> b = () >>> a is b True >>> id(a) 4409020488 >>> id(b) 4409020488 >>> # В CPython, функция id возвращает адрес в памяти. 

Но это не работает со списками, ведь они могут быть изменены:

>>> a = [] >>> b = [] >>> a is b False >>> id(a) 4465566920 >>> id(b) 4465370632 
Оптимизация выделения памяти для кортежей

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

Этот список разделен на 20 групп, где каждая группа представляет из себя список кортежей размера n, где n от 0 до 20. Каждая группа может хранить до 2 000 свободных кортежей. Первая группа хранит только один элемент и представляет из себя список из одного пустого кортежа.

>>> a = (1,2,3) >>> id(a) 4427578104 >>> del a >>> b = (1,2,4) >>> id(b) 4427578104 

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

Оптимизация выделения памяти для списков

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

>>> a = [] >>> id(a) 4465566792 >>> del a >>> b = [] >>> id(b) 4465566792 
Изменение размера списка

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

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

Паттерн роста размера списка выглядит примерно так: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88,…

Для примера, если вы хотите добавить новый элемент в список с 8 элементами, то свободных ячеек в нём уже не будет и Python сразу расширит его размер до 16 ячеек, где 9 из них будут заняты и видны пользователю.

Формула выбора размера написанная на Python:

>>> def get_new_size(n_items): . new_size = n_items + (n_items // 2 ** 3) . if n_items < 9: . new_size += 3 . else: . new_size += 6 . . return new_size . >>> get_new_size(9) 16 
Скорость

Если сравнивать эти два типа по скорости, то в среднем по больнице, кортежи слегка быстрее списков. У Raymond Hettinger есть отличное объяснение разницы в скорости на stackoverflow.

P.S.: Я являюсь автором этой статьи, можете задавать любые вопросы.

Добавить комментарий

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