Наборы 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.