Наборы Python против списков


187

В Python, какая структура данных является более эффективной / быстрой? Предполагая, что порядок не важен для меня, и я все равно буду проверять наличие дубликатов, является ли набор Python более медленным, чем список Python?

Ответы:


231

Это зависит от того, что вы собираетесь с этим делать.

Наборы значительно быстрее, когда дело доходит до определения наличия объекта в наборе (как в x in s ), но медленнее, чем списки, когда дело доходит до перебора их содержимого.

Вы можете использовать модуль timeit, чтобы увидеть, что быстрее для вашей ситуации.


4
Для вашей точки зрения: «Наборы значительно быстрее», какая базовая реализация делает это быстрее?
— сверхобмена

Языки сценариев любят скрывать базовые реализации, но эта кажущаяся простота не всегда хорошая вещь, вам нужно некоторое понимание «структуры данных» при разработке части программного обеспечения.
— Кристоф Русси

4
Установить не значительно медленнее, чем список во время итерации.
— омерфарукдоган

39
Наборы и списки имеют линейную итерацию времени. Сказать, что один «медленнее», чем другой, вводит в заблуждение и вводит в заблуждение новых программистов, которые читают этот ответ.
— января

@habnabit, если вы говорите, что они оба имеют линейную итерацию времени. Значит ли это, что у них одинаковое время итерации? Какая тогда разница?
— Мухаммед Нурельдин

153

Списки немного быстрее, чем наборы, когда вы просто хотите перебрать значения.

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

Оказывается, кортежи работают почти так же, как списки, за исключением их неизменности.

Итерация

>>> def iter_test(iterable):
...     for i in iterable:
...         pass
...
>>> from timeit import timeit
>>> timeit(
...     "iter_test(iterable)",
...     setup="from __main__ import iter_test; iterable = set(range(10000))",
...     number=100000)
12.666952133178711
>>> timeit(
...     "iter_test(iterable)",
...     setup="from __main__ import iter_test; iterable = list(range(10000))",
...     number=100000)
9.917098999023438
>>> timeit(
...     "iter_test(iterable)",
...     setup="from __main__ import iter_test; iterable = tuple(range(10000))",
...     number=100000)
9.865639209747314

Определить, присутствует ли объект

>>> def in_test(iterable):
...     for i in range(1000):
...         if i in iterable:
...             pass
...
>>> from timeit import timeit
>>> timeit(
...     "in_test(iterable)",
...     setup="from __main__ import in_test; iterable = set(range(1000))",
...     number=10000)
0.5591847896575928
>>> timeit(
...     "in_test(iterable)",
...     setup="from __main__ import in_test; iterable = list(range(1000))",
...     number=10000)
50.18339991569519
>>> timeit(
...     "in_test(iterable)",
...     setup="from __main__ import in_test; iterable = tuple(range(1000))",
...     number=10000)
51.597304821014404

6
Я обнаружил, что (Инициализирующий набор -> 5.5300979614257812) (Инициализирующий список -> 1.8846848011016846) (Инициализирующий кортеж -> 1.8730108737945557) Элементы размером 10 000 на моем четырехъядерном процессоре Intel Core i5 с 12 ГБ ОЗУ. Это следует учитывать также.
— ThePracticalOne

4
Я обновил код, чтобы удалить объект сейчас. Фаза установки циклов timeit вызывается только один раз ( docs.python.org/2/library/timeit.html#timeit.Timer.timeit ).
— Эллис Персиваль

7

Список производительности:

>>> import timeit
>>> timeit.timeit(stmt='10**6 in a', setup='a = range(10**6)', number=100000)
0.008128150348026608

Установить производительность:

>>> timeit.timeit(stmt='10**6 in a', setup='a = set(range(10**6))', number=100000)
0.005674857488571661

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

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

setпо определению. [ питон | вики ].

>>> x = set([1, 1, 2, 2, 3, 3])
>>> x
{1, 2, 3}

4
Прежде всего, вам следует обновить setссылку встроенного типа ( docs.python.org/2/library/stdtypes.html#set ), а не устаревшую setsбиблиотеку. Во-вторых, «Наборы также являются структурами последовательностей», считайте следующее из ссылки встроенного типа: «Будучи неупорядоченной коллекцией, наборы не записывают положение элемента или порядок вставки. Соответственно, наборы не поддерживают индексацию, нарезку или другие последовательное поведение. "
— Seaux

7
rangeне list. rangeэто специальный класс с пользовательским __contains__магическим методом.
— Райн Ван

@RyneWang это правда, но только для Python3. В Python2 диапазон возвращает нормальный список (вот почему существуют такие ужасные вещи, как xrange)
— Маноэль Вилела

7

Setвыигрывает из-за почти мгновенных проверок "содержит": https://en.wikipedia.org/wiki/Hash_table

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

Реализация набора : https://en.wikipedia.org/wiki/Hash_table , он не выполняет итерацию по списку, но находит элемент, вычисляя хеш-код из ключа, поэтому он зависит от природы ключевых элементов и хеш-функции. функция. Подобно тому, что используется для dict. Я подозреваю, что listможет быть быстрее, если у вас очень мало элементов (<5), чем больше число элементов, тем лучшеset будет проверка содержимого. Это также быстро для добавления и удаления элементов. Также всегда помните, что создание набора имеет свою стоимость!

ПРИМЕЧАНИЕ . Если объект listуже отсортирован, поиск listможет быть довольно быстрым, но в обычных случаях setон быстрее и проще для проверок содержимого.


8
Близко к металлу? Что это вообще значит в контексте Python? Как список ближе к металлу, чем набор?
— roganjosh

@roganjosh, python по-прежнему работает на компьютере, и некоторые реализации, такие как list как 'array', ближе к тому, что хорошо с аппаратным обеспечением: stackoverflow.com/questions/176011/… , но это всегда зависит от того, чего вы хотите достичь, это Полезно знать немного о реализациях, а не только об абстракциях.
— Кристоф Русси

2

ТЛ; др

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

Некоторые структуры данных более полезны, чем другие в некоторых конкретных случаях. Поэтому довольно несправедливо спрашивать, какой (DS) является более эффективным / быстрым. Это все равно, что спросить, какой инструмент более эффективен между ножом и вилкой. Я имею в виду, все зависит от ситуации.

Списки

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

наборы

Заданный объект - это неупорядоченная коллекция различных хешируемых объектов. . Он обычно используется для проверки членства, удаления дубликатов из последовательности и вычисления математических операций, таких как пересечение, объединение, разность и симметричная разность.

использование

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


2

Меня интересовали результаты при проверке с помощью CPython, является ли значение одним из небольшого числа литералов. setвыигрывает в Python 3 против tuple, listи or:

from timeit import timeit

def in_test1():
  for i in range(1000):
    if i in (314, 628):
      pass

def in_test2():
  for i in range(1000):
    if i in [314, 628]:
      pass

def in_test3():
  for i in range(1000):
    if i in {314, 628}:
      pass

def in_test4():
  for i in range(1000):
    if i == 314 or i == 628:
      pass

print("tuple")
print(timeit("in_test1()", setup="from __main__ import in_test1", number=100000))
print("list")
print(timeit("in_test2()", setup="from __main__ import in_test2", number=100000))
print("set")
print(timeit("in_test3()", setup="from __main__ import in_test3", number=100000))
print("or")
print(timeit("in_test4()", setup="from __main__ import in_test4", number=100000))

Вывод:

tuple
4.735646052286029
list
4.7308746771886945
set
3.5755991376936436
or
4.687681658193469

От 3 до 5 литералов setвсе еще выигрывает с большим отрывом и orстановится самым медленным.

В Python 2 setвсегда самый медленный. orявляется самым быстрым для 2 до 3 литер, а tupleи listбыстрее с 4 или более литералов. Я не мог отличить скорость tupleпротив list.

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

Эти результаты применимы к 64-битному CPython на Core i7.


0

Я бы порекомендовал реализацию Set, где вариант использования ограничен ссылками или поиском существования, и реализацию Tuple, где вариант использования требует от вас выполнения итерации. Список является низкоуровневой реализацией и требует значительных накладных расходов памяти.


1
Действительно, правильное различие между тем, когда использовать наборы и когда использовать Tuple, действительно имеет огромное значение. Меня не будут беспокоить связанные с этим накладные расходы памяти, следы, если я не пишу сценарий API более низкого уровня.

0
from datetime import datetime
listA = range(10000000)
setA = set(listA)
tupA = tuple(listA)
#Source Code

def calc(data, type):
start = datetime.now()
if data in type:
print ""
end = datetime.now()
print end-start

calc(9999, listA)
calc(9999, tupA)
calc(9999, setA)

Вывод после сравнения 10 итераций для всех 3: Сравнение


0

Наборы работают быстрее, но вы получаете больше функций с наборами, например, допустим, у вас есть два набора:

set1 = {"Harry Potter", "James Bond", "Iron Man"}
set2 = {"Captain America", "Black Widow", "Hulk", "Harry Potter", "James Bond"}

Мы можем легко объединить два набора:

set3 = set1.union(set2)

Узнайте, что общего в обоих:

set3 = set1.intersection(set2)

Узнайте, что отличается в обоих:

set3 = set1.difference(set2)

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.