Zestawy Python vs. Listy


187

Która struktura danych w Pythonie jest bardziej wydajna / szybsza? Zakładając, że kolejność nie jest dla mnie ważna, a mimo to sprawdzałbym duplikaty, czy zestaw Python jest wolniejszy niż lista Python?

Odpowiedzi:


231

To zależy od tego, co zamierzasz z tym zrobić.

Zestawy są znacznie szybsze, jeśli chodzi o ustalenie, czy obiekt jest obecny w zestawie (jak w x in s), ale są wolniejsze niż listy, jeśli chodzi o iterację ich zawartości.

Możesz użyć modułu timeit, aby zobaczyć, który jest szybszy w twojej sytuacji.


4
Jeśli chodzi o twój punkt: „Zestawy są znacznie szybsze”, jaka jest podstawowa implementacja, która sprawia, że ​​jest szybszy?
— przeeksponowanie

Języki skryptowe lubią ukrywać podstawowe implementacje, ale ta pozorna prostota nie zawsze jest dobrą rzeczą, potrzebujesz pewnej świadomości „struktury danych” podczas projektowania oprogramowania.
— Christophe Roussy

4
Zestaw nie jest znacznie wolniejszy niż lista podczas iteracji.
— omerfarukdogan

39
Zestawy i listy mają liniową iterację czasu. Powiedzenie, że jedno jest „wolniejsze” niż drugie, jest mylące i dezorientuje nowych programistów, którzy czytają tę odpowiedź.
— habnabit

@ habnabit, jeśli mówisz, że oba mają liniową iterację czasu. Czy to oznacza, że ​​mają ten sam czas iteracji? Jaka jest zatem różnica?
— Mohammed Noureldin

153

Listy są nieco szybsze niż zestawy, gdy chcesz po prostu iterować po wartościach.

Zestawy są jednak znacznie szybsze niż listy, jeśli chcesz sprawdzić, czy element jest w nim zawarty. Mogą jednak zawierać tylko unikalne przedmioty.

Okazuje się, że krotki działają prawie dokładnie tak samo jak listy, z wyjątkiem ich niezmienności.

Iteracja

>>> 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

Sprawdź, czy obiekt jest obecny

>>> 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
Odkryłem, że (zestaw inicjujący -> 5.5300979614257812) (lista inicjująca -> 1.8846848011016846) (krotka inicjująca -> 1.8730108737945557) Przedmioty o wielkości 10.000 na moim czterordzeniowym rdzeniu Intel Core i5 z 12 GB pamięci RAM. Należy to również wziąć pod uwagę.
— ThePracticalOne

4
Zaktualizowałem kod, aby usunąć teraz tworzenie obiektu. Faza konfiguracji pętli timeit jest wywoływana tylko raz ( docs.python.org/2/library/timeit.html#timeit.Timer.timeit ).
— Ellis Percival

7

Wydajność listy:

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

Ustaw wydajność:

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

Możesz rozważyć stosowanie Tuple, ponieważ są one podobne do list, ale nie można ich modyfikować. Zajmują nieco mniej pamięci i są szybciej dostępne. Nie są tak elastyczne, ale są bardziej wydajne niż listy. Zwykle służą jako klucze słownikowe.

Zbiory są również strukturami sekwencji, ale z dwiema różnicami od list i krotek. Chociaż zestawy mają kolejność, kolejność ta jest dowolna i nie podlega kontroli programisty. Druga różnica polega na tym, że elementy zestawu muszą być unikalne.

setzgodnie z definicją. [ python | wiki ].

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

4
Po pierwsze, należy zaktualizować do setwbudowanego łącza typu ( docs.python.org/2/library/stdtypes.html#set ), a nie przestarzałej setsbiblioteki. Po drugie, „Zestawy są również strukturami sekwencji”, przeczytaj następujące informacje z wbudowanego łącza typu: „Będąc kolekcją nieuporządkowaną, zestawy nie rejestrują pozycji elementu ani kolejności wstawiania. W związku z tym zestawy nie obsługują indeksowania, dzielenia ani innych zachowanie podobne do sekwencji ”.
— Seaux

7
rangenie jest list. rangeto specjalna klasa z niestandardową __contains__metodą magiczną.
— Ryne Wang

@RyneWang to prawda, ale tylko w przypadku Python3. W zakresie Python2 zwraca normalną listę (dlatego istnieją takie okropne rzeczy jak xrange)
— Manoel Vilela,

7

Setwygrywa dzięki prawie natychmiastowym czekom „zawiera”: https://en.wikipedia.org/wiki/Hash_table

Implementacja listy : zwykle tablica, niski poziom blisko metalu, dobry do iteracji i losowy dostęp według indeksu elementów.

Implementacja zestawu : https://en.wikipedia.org/wiki/Hash_table , nie iteruje się na liście, ale znajduje element, obliczając skrót z klucza, więc zależy to od natury kluczowych elementów i skrótu funkcjonować. Podobne do tego, co jest używane do dyktowania. Podejrzewam, że listmoże być szybszy, jeśli masz bardzo mało elementów (<5), im większy element, tym lepsza setwydajność przy sprawdzaniu zawartości. Jest również szybki do dodawania i usuwania elementów. Pamiętaj też, że zbudowanie zestawu ma swój koszt!

UWAGA : Jeśli listjest już posortowane, wyszukiwanie listmoże być dość szybkie, ale w zwykłych przypadkach a setjest szybsze i prostsze w przypadku sprawdzania zawartości.


8
Blisko do metalu? Co to w ogóle oznacza w kontekście Pythona? Jak lista jest bliżej metalu niż zestawu?
— roganjosh

@roganjosh, python nadal działa na komputerze, a niektóre implementacje, takie jak lista jako „tablica”, są bliższe temu, co sprzęt jest dobry: stackoverflow.com/questions/176011/... , ale zawsze zależy to od tego, co chcesz osiągnąć, to dobrze jest wiedzieć trochę o implementacjach, a nie tylko abstrakcjach.
— Christophe Roussy

2

tl; dr

Struktury danych (DS) są ważne, ponieważ służą do wykonywania operacji na danych, co w zasadzie implikuje: weź trochę danych wejściowych , przetworz je i zwróć dane wyjściowe .

Niektóre struktury danych są bardziej przydatne niż inne w niektórych szczególnych przypadkach. Dlatego niesprawiedliwe jest pytanie, które (DS) jest bardziej wydajne / szybkie. To jak pytanie, które narzędzie jest bardziej wydajne między nożem a widelcem. Mam na myśli, że wszystko zależy od sytuacji.

Listy

Lista jest zmienną sekwencją , zwykle używaną do przechowywania kolekcji jednorodnych przedmiotów .

Zestawy

Ustawiony obiekt to nieuporządkowana kolekcja różnych obiektów możliwych do skrótu . Jest powszechnie używany do testowania członkostwa, usuwania duplikatów z sekwencji i obliczania operacji matematycznych, takich jak przecięcie, połączenie, różnica i różnica symetryczna.

Stosowanie

Z niektórych odpowiedzi jasno wynika, że ​​lista jest znacznie szybsza niż zestaw podczas iteracji po wartościach. Z drugiej strony zestaw jest szybszy niż lista podczas sprawdzania, czy element jest w nim zawarty. Dlatego jedyną rzeczą, którą możesz powiedzieć, jest to, że lista jest lepsza niż zestaw dla niektórych konkretnych operacji i na odwrót.


2

Byłem zainteresowany wynikami podczas sprawdzania za pomocą CPython, czy wartość jest jedną z niewielkiej liczby literałów. setwygrywa Pythonie 3 vs tuple, listi 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))

Wynik:

tuple
4.735646052286029
list
4.7308746771886945
set
3.5755991376936436
or
4.687681658193469

Dla 3 do 5 literałów setnadal wygrywa z szerokim marginesem i orstaje się najwolniejszy.

W Pythonie 2 setjest zawsze najwolniejszy. orjest najszybszy dla 2 do 3 literałów tuplei listjest szybszy z 4 lub więcej literałami. Nie mogłem odróżnić prędkość tuplevs list.

Gdy wartości do testowania były buforowane w zmiennej globalnej poza funkcją, zamiast tworzyć literał w pętli, setwygrywało za każdym razem, nawet w Pythonie 2.

Te wyniki dotyczą 64-bitowego CPython na Core i7.


0

Poleciłbym implementację Set, w której przypadek użycia ogranicza się do odwoływania się lub wyszukiwania istnienia, oraz implementację Tuple, w której przypadek użycia wymaga wykonania iteracji. Lista jest implementacją niskiego poziomu i wymaga znacznego obciążenia pamięci.


1
Rzeczywiście, właściwe rozróżnienie między tym, kiedy używać zestawów, a kiedy używać Tuple, ma naprawdę ogromne znaczenie. Nie martwiłbym się związanymi z tym kosztami pamięci i śladami, chyba że piszę skrypt API niższego poziomu.

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)

Wyjście po porównaniu 10 iteracji dla wszystkich 3: Porównanie


0

Zestawy są szybsze, ponadto dostajesz więcej funkcji dzięki zestawom, na przykład powiedzmy, że masz dwa zestawy:

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

Możemy łatwo połączyć dwa zestawy:

set3 = set1.union(set2)

Dowiedz się, co jest wspólne w obu:

set3 = set1.intersection(set2)

Dowiedz się, co różni się w obu:

set3 = set1.difference(set2)

I wiele więcej! Wypróbuj je, są fajne! Co więcej, jeśli musisz pracować nad różnymi wartościami z 2 list lub wspólnymi wartościami z 2 list, wolę przekonwertować twoje listy na zestawy, a wielu programistów robi to w ten sposób. Mam nadzieję, że to ci pomoże :-)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.