Python что быстрее list или tuple
Перейти к содержимому

Python что быстрее list или tuple

  • автор:

Python для начинающих: какая разница между tuple, list и set?

Язык программирования Python предоставляет четыре встроенных типа данных для хранения коллекций из объектов. Все они наделены различными свойствами и характеристиками: list (список), tuple (кортеж), set (множество) и dictionary (словарь).

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

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

Зачем вообще выбирать?

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

Может стог сена — это список? Как насчет кортежа? Почему бы не использовать множества всегда? На какие предостережения следует обратить внимание?

Отличия между списком, кортежем и множеством

  • Отличие 1: дубликаты.
    Говоря проще, List и Tuple в Python как двойняшки разного пола, а тип данных Set для них как двоюродный брат. В отличие от списков или кортежей, множество не содержит дубликатов. Другими словами, элементы множества всегда уникальны. Получается, что множество удобно удаляет дубликаты, словно создано именно для этого.
set_example = 
# fruit_set =
#
  • Отличие 2: упорядоченность.
    Наверняка вы слышали утверждение “множества и словари в Python не упорядочены”, но на сегодняшний день — это лишь половина правды в зависимости от того, какой версией Python вы пользуетесь. До Python версии 3.6 словари и множества действительно не сохраняли порядок элементов, но начиная с Python 3.7, dictionary и set официально упорядочены по времени добавления элементов. А вот list и tuple — это всегда упорядоченные последовательности объектов.
# Пример тогда ещё неупорядоченного множества в Python версии 3.5fruit_size = <> 
>>> fruit_size['��'] = 12
>>> fruit_size['��'] = 16
>>> fruit_size['��'] = 20
>>> fruit_size
  • Отличие 3: индексация.
    Что списки, что кортежи — оба поддерживают индексацию и срезы, а вот множества — нет.
fruit_list = ['��', '��', '��']
fruit_list[1]
# '��'
animal_tuple = ('��', '��', '��')
animal_tuple[2]
# '��'
vehicle_set =
vehicle_set[0]
# TypeError: 'set' object is not subscriptable

Когда выбирать список, а когда — кортеж?

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

a_tuple = tuple(range(1000))
a_list = list(range(1000))
a_tuple.__sizeof__() # 8024 байта
a_list.__sizeof__() # 9088 байт
  • Список подходит, если:
  1. Последовательность планируется изменять.
  2. Планируется постепенно добавлять новые элементы в последовательность или удалять старые.
  • Кортеж подходит, если:
  1. Последовательность НЕ планируется изменять.
  2. Все, что нужно от последовательности — это возможность поочередно перебирать постоянный набор элементов.
  3. Нужна последовательность элементов для ее назначения в качестве ключа словаря. Поскольку списки — это изменяемый тип данных, их нельзя применять в качестве ключей словаря.
  4. Важна скорость выполнения операций с последовательностью: из-за отсутствия возможности изменения, кортежи работают куда быстрее списков.

Когда выбирать множества?

Базовая структура типа данных “множество” — это хеш-таблица (Hash Table). Поэтому множества очень быстро справляются с проверкой элементов на вхождение, например содержится ли объект x в последовательности a_set .

Идея заключается в том, что поиск элемента в хэш-таблице — это операция O(1), то есть операция с постоянным временем выполнения.

Получается, всегда надо использовать множество?

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

Выводы

“Преждевременная оптимизация — корень всех зол”.

Итак, самое главное, что вам стоит запомнить по поводу списков, кортежей и множеств.

  1. Если необходимо хранить дубликаты, то выбирайте список или кортеж.
  2. Если НЕ планируется изменять последовательность после ее создания, то выбирайте кортеж, а не список.
  3. Если НЕ нужно хранить дубликаты, то воспользуйтесь множеством, так как они значительно быстрее определяют наличие объекта в последовательности.

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

Главное — помнить о похожих чертах и особенностях встроенных типов данных Python.

  • Скрейпинг PDF с нуля на Python: библиотеки tabula-py и Pandas
  • Как вычислить миллионное число Фибоначчи на Python
  • 3 важных рекомендации Django-программистам

Чем отличаются list, tuple и set? Зачем они нужны?

List (список), tuple (кортеж), set (множество) — это встроенные структуры данных языка python. Каждая из них имеет свои возможности и ограничения. Это позволяет выбрать наиболее подходящий способ хранения информации в программе.

List (список)

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

Создание списка
>>> my_list = [] # Создание пустого списка с помощью литерала списка >>> my_list = list() # Создание пустого списка с помощью встроенной функции >>> >>> my_list = [1,2,['a','b'],4,5] # Инициализация списка >>> >>> my_list = list('hello world') # Создание списка из итерируемого объекта >>> my_list ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] >>> >>> my_list = [x for x in range(10)] # Генератор списков в действии >>> my_list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
Доступные методы
  • my_list.append(x) — добавляет x в конец списка
  • my_list.clear() — очищает список
  • my_list.copy() — возвращает копию списка my_list
  • my_list.count(x) — возвращает кол-во элементов со значением x
  • my_list.extend(x) — добавляет элементы списка x к концу списка my_list
  • my_list.index(x,start,end) — возвращает индекс первого найденного x, можно задать промежуток для поиска (опционально)
  • my_list.insert(index, x) — вставляет x на заданную позицию
  • my_list.pop(index) — возвращает элемент с указанным индексом и удаляет его, если индекс не указан — возвращается и удаляется последний элемент
  • my_list.remove(x) — удаляет первый элемент со значением x
  • my_list.reverse() — инвертирует порядок элементов в списке
  • my_list.sort(key=x) сортирует список на основе функции x
В каких случаях использовать?

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

Tuple (кортёж)

Кортеж — это неизменяемый и более быстрый аналог списка. Он защищает хранимые данные от непреднамеренных изменений и может использоваться в качестве ключа в словарях (словарь — ассоциативный массив в python).

Создание кортежа.
>>> my_tuple = () # Создание кортежа с помощью литерала >>> my_tuple = tuple() # Создание кортежа с помощью встроенной функции >>> >>> my_tuple = (1,2,['a','b'],4,5) # Инициализация кортежа >>> >>> my_tuple = tuple('hello world') # Создание кортежа из итерируемого объекта >>> my_tuple ('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd') >>> >>> my_tuple = tuple(2**x for x in [0, 1, 2, 3]) # Генератор кортежей >>> my_tuple (1, 2, 4, 8) 
Доступные методы
  • my_tuple.count(x) — возвращает кол-во элементов со значением x
  • my_tuple.index(x,start,end) — возвращает индекс первого найденного x, можно задать промежуток для поиска (опционально)
В каких случаях использовать?

Для хранения данных вместо списка (если они не предполагают изменений).

Set (множество)

Множество — это набор уникальных элементов в случайном порядке (неупорядоченный список). Множества примечательны тем, что операция проверки “принадлежит ли объект множеству” происходит значительно быстрее аналогичных операций в других структурах данных.

Создание множества
>>> my_something = > # . Попытка создать множество при помощи литерала даст нам словарь >>> type(my_something) class 'dict'> >>> >>> my_set = set() # Создание при помощи встроенной функции >>> >>> my_set = 1,2,3,4,5> # Инициализация множества >>> >>> my_set = set('hello world') # Создания множества из итерируемого объекта >>> my_set 'r', 'o', 'e', 'h', 'd', 'w', 'l', ' '> >>> >>> my_set = x for x in range(10)> # Генератор множеств >>> my_set 0, 1, 2, 3, 4, 5, 6, 7, 8, 9> 
Доступные методы
  • my_set.add(x) — добавляет x во множество
  • my_set.difference(x) — возвращает множество элементов my_set, которые не входят во множество x
  • my_set.difference_update(x) — удаляет из множества my_set все элементы, которые входят во множество x
  • my_set.discard(x) — удаляет элемент x из my_set
  • my_set.intersection(x) — возвращает элементы общие для множеств my_set и x
  • my_set.intersection_update(x) — удаляет из множества my_set элементы, которых нет во множестве x
  • my_set.isdisjoint(x) — возвращает true если my_set и x не содержат одинаковых значений
  • my_set.issubset(x) — возвращает true если все элементы my_set входят во множество x
  • my_set.issuperset(x) — возвращает true если все элементы x входят во множество my_set
  • my_set.pop() — возвращает и удаляет первый (на данный момент) элемент множества
  • my_set.remove(x) — удаляет x из множества
  • my_set.symmetric_difference(x) — возвращает все элементы из x и my_set, которые встречаются только в одном из множеств
  • my_set.symmetric_difference_update(x) — обновляет исходное множество таким образом, что оно будет состоять из всех элементов x и my_set, которые встречаются только в одном из множеств
  • my_set.union(x) — возвращает новое множество, состоящее из всех элементов x и my_set
  • my_set.update(x) — добавляет в my_set все элементы x
В каких случаях использовать?

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

Попробуйте бесплатные уроки по Python

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

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.

Списки (List) vs Кортежи (Typle)

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

Различия в представлении

Представление списков и кортежей отличается незначительно. Списки обычно заключаются в квадратную скобку ё [] , а элементы — через запятую. Кортежи заключаются в круглую скобку () , а элементы разделяются запятой. Скобки использовать необязательно, и такие типы кортежей называются упаковкой кортежей.

Рассмотрим следующий пример.

list1 = ['JavaTpoint', 1, 2, 54.30, ] print(type(list)) tuple1 = ('JavaTpoint',5,8,31.9,[1,2,3]) print(type(tuple1)) 

В приведенной выше программе мы определили переменную list1 , которая содержит список данных разного типа с индексом от 0 до 4 . Мы определили еще одну переменную tuple1 , которая содержит кортеж данных разного типа. Она заключена в рамку () .

Изменяемые списки и неизменяемые кортежи в Python

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

a = ["Peter","Joseph","Mathew","Ricky"] print(a) 
['Peter', 'Joseph', 'Mathew', 'Ricky'] 

Теперь мы меняем 0-й элемент индекса «Peter» на «Samson».

a[0] = "Samson" print(a) 
['Samson', 'Joseph', 'Mathew', 'Ricky'] 

Теперь мы создаем кортеж и делаем то же самое.

a = (10,20,"JavaTpoint",30,40) print(a) 
(10, 20, 'JavaTpoint', 30, 40) 
a[0] = 50 
TypeError Traceback (most recent call last) in ----> 1 a[0] = 50 TypeError: 'tuple' object does not support item assignment 

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

Отладка кортежей и списков в Python

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

a = [6,9,4,3,7,0,1] # Copying address of a in b b = a a[3] = "JavaToint" print(a) 
[6, 9, 4, 'JavaToint', 7, 0, 1] 

В приведенном выше коде мы сделали b = a ; здесь мы не копируем объект списка из b в a . b ссылается на адрес списка a . Это означает, что если мы сделаем изменение в b , то это отразится так же, как и в списке a , и это облегчает отладку. Но это сложно для серьезного проекта, где объекты Python могут иметь множество ссылок.

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

Поддержка функций у кортежей и списков в Python

Кортежи поддерживают меньше операций, чем списки. Встроенный dir(object) используется для получения всех поддерживаемых функций для списка и кортежа.

Функциии списков (List)

dir(list) 
['__add__','__class__','__contains__','__delattr__','__delitem__','__dir_, '__doc__','__eq__','__format__', '__get__','__getattribute__','__getitem_' '__gt__','__hash__','__iadd__','__imul__','__init__','__init_subclass__''__iter__','__le__','__len__','__lt__','__mul__', '__ne__','__new__', '__reduce__', '__reduce_ex__','__repr__','__reversed__','__rmul__','__setattr__','__setitem__','__sizeof__','__str__','__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] 

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

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index'] 

Расходование памяти у списков (List) и у кортежей (Tuple) в Python

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

Tuple = (1,2,3,4,5,6,7,8,9,0,5485,87525,955,3343,53234,6423,623456,234535) List = [1,2,3,4,5,6,7,8,9,0,78,34,43,32,43,55,54,212,642,533,43434,54532 ] print('Tuple size =', Tuple.__sizeof__()) # Tuple size = 52 print('List size =', List.__sizeof__()) 
Tuple size = 168 List size = 216 

В некоторых случаях списки могут показаться более полезными, чем кортежи. Но кортежи являются важными структурами данных в Python. Кортежи обычно используются для неизменяемых данных, или можно сказать, что данные в кортежах «защищены от записи». Кортежи передают интерпретатору Python указание на то, что данные не должны меняться в будущем. Мы можем использовать кортеж так же, как и словарь, не используя ключи для хранения данных, например:

list1 = [(101, "Mike", 24),(102, 'Hussey', 26),(103, 'David', 27),(104, 'Warner', 29)] 

Кортежи могут использовать ключи словаря, поскольку они хэшируемы и неизменяемы, в то время как списки не могут использовать ключи словаря.

dict = #valid dictionary dict = #Invalid dictionary 

Оптимизации, используемые в 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 не будет опубликован. Обязательные поля помечены *