Set di Python vs Elenchi


187

In Python, quale struttura di dati è più efficiente / veloce? Supponendo che l'ordine non sia importante per me e controllerei comunque i duplicati, un Python è più lento di un elenco Python?

Risposte:


231

Dipende da cosa hai intenzione di farne.

Gli insiemi sono significativamente più veloci quando si tratta di determinare se un oggetto è presente nell'insieme (come in x in s), ma sono più lenti degli elenchi quando si tratta di iterare sul loro contenuto.

Puoi usare il modulo timeit per vedere quale è più veloce per la tua situazione.


4
Per il tuo punto: "I set sono significativamente più veloci", qual è l'implementazione sottostante che lo rende più veloce?
Scambio eccessivo del

I linguaggi di scripting amano nascondere le implementazioni sottostanti, ma questa apparente semplicità non è sempre una buona cosa, è necessaria una certa consapevolezza della "struttura dei dati" quando si progetta un software.
Christophe Roussy,

4
Set non è significativamente più lento dell'elenco durante l'iterazione.
Omerfarukdogan,

39
Gli insiemi e gli elenchi hanno entrambi un'iterazione temporale lineare. Dire che uno è "più lento" dell'altro è fuorviante e ha confuso i nuovi programmatori che leggono questa risposta.
habnabit

@habnabit se stai dicendo che entrambi hanno un'iterazione temporale lineare. Questo significa che hanno lo stesso tempo di iterazione? Qual è la differenza allora?
Mohammed Noureldin

153

Gli elenchi sono leggermente più veloci degli insiemi quando si desidera semplicemente scorrere i valori.

Gli insiemi, tuttavia, sono significativamente più veloci degli elenchi se si desidera verificare se un elemento è contenuto al suo interno. Tuttavia possono contenere solo oggetti unici.

Si scopre che le tuple si comportano quasi nello stesso modo delle liste, tranne per la loro immutabilità.

iterazione

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

Determina se è presente un oggetto

>>> 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
Ho scoperto che (Set di inizializzazione -> 5.5300979614257812) (Elenco di inizializzazione -> 1.8846848011016846) (Tupla di inizializzazione -> 1.8730108737945557) Articoli di dimensioni 10.000 sul mio core Intel i5 quad core con 12 GB di RAM. Questo dovrebbe essere preso in considerazione anche.
ThePracticalOne

4
Ho aggiornato il codice per rimuovere la creazione dell'oggetto ora. La fase di installazione dei cicli timeit viene chiamata una sola volta ( docs.python.org/2/library/timeit.html#timeit.Timer.timeit ).
Ellis Percival,

7

Elenco delle prestazioni:

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

Imposta le prestazioni:

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

Potresti considerare le Tuple come simili agli elenchi ma non modificabili. Occupano un po 'meno memoria e sono più veloci ad accedervi. Non sono così flessibili ma sono più efficienti delle liste. Il loro uso normale è di servire come chiavi del dizionario.

Gli insiemi sono anche strutture sequenziali ma con due differenze rispetto a liste e tuple. Sebbene gli insiemi abbiano un ordine, quell'ordine è arbitrario e non sotto il controllo del programmatore. La seconda differenza è che gli elementi in un set devono essere unici.

setper definizione. [ pitone | wiki ].

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

4
Prima di tutto, è necessario aggiornare al setcollegamento del tipo incorporato ( docs.python.org/2/library/stdtypes.html#set ) non alla setslibreria obsoleta . In secondo luogo, "I set sono anche strutture di sequenza", leggi quanto segue dal link di tipo incorporato: "Essendo una raccolta non ordinata, i set non registrano la posizione dell'elemento o l'ordine di inserimento. Di conseguenza, i set non supportano indicizzazione, divisione o altro comportamento simile a una sequenza ".
Seaux,

7
rangenon lo è list. rangeè una classe speciale con __contains__metodo magico personalizzato .
Ryne Wang

@RyneWang questo è vero, ma solo per Python3. Nella gamma Python2 restituisce un elenco normale (ecco perché esistono cose orribili come xrange)
Manoel Vilela,

7

Setvince a causa di controlli "contiene" quasi istantanei: https://en.wikipedia.org/wiki/Hash_table

Implementazione elenco : di solito un array, basso livello vicino al metal, buono per iterazione e accesso casuale per indice elemento.

Imposta implementazione: https://en.wikipedia.org/wiki/Hash_table , non scorre su un elenco, ma trova l'elemento calcolando un hash dalla chiave, quindi dipende dalla natura degli elementi chiave e dall'hash funzione. Simile a ciò che viene utilizzato per dict. Sospetto che listpotrebbe essere più veloce se hai pochissimi elementi (<5), maggiore è il numero di elementi, migliore setsarà il rendimento per un controllo del contenuto. È inoltre veloce per l'aggiunta e la rimozione di elementi. Inoltre, tieni sempre presente che la costruzione di un set ha un costo!

NOTA : se l' listopzione è già ordinata, la ricerca listpotrebbe essere abbastanza veloce, ma nei casi normali a setè più veloce e più semplice per contenere i controlli.


8
Vicino al metallo? Cosa significa questo anche nel contesto di Python? In che modo un elenco è più vicino al metal di un set?
roganjosh,

@roganjosh, python funziona ancora su una macchina e alcune implementazioni come list come 'array' sono più vicine a ciò che l'hardware è bravo: stackoverflow.com/questions/176011/… , ma dipende sempre da ciò che vuoi ottenere, è bene sapere qualcosa sulle implementazioni, non solo sulle astrazioni.
Christophe Roussy,

2

tl; dr

Le strutture dati (DS) sono importanti perché vengono utilizzate per eseguire operazioni sui dati che implicano sostanzialmente: prendere un input , elaborarlo e restituire l'output .

Alcune strutture di dati sono più utili di altre in alcuni casi particolari. Pertanto, è abbastanza ingiusto chiedere quale (DS) sia più efficiente / veloce. È come chiedere quale strumento sia più efficiente tra un coltello e una forchetta. Voglio dire, tutto dipende dalla situazione.

elenchi

Un elenco è una sequenza mutabile , generalmente utilizzata per archiviare raccolte di elementi omogenei .

Imposta

Un oggetto set è una raccolta non ordinata di oggetti hash distinti . È comunemente usato per testare l'appartenenza, rimuovere i duplicati da una sequenza e calcolare operazioni matematiche come intersezione, unione, differenza e differenza simmetrica.

uso

Da alcune delle risposte, è chiaro che un elenco è molto più veloce di un set quando scorre i valori. D'altra parte, un set è più veloce di un elenco quando si verifica se un elemento è contenuto al suo interno. Pertanto, l'unica cosa che puoi dire è che un elenco è meglio di un set per alcune operazioni particolari e viceversa.


2

Ero interessato ai risultati quando controllavo, con CPython, se un valore è uno di un piccolo numero di letterali. setvince in Python 3 vs tuple, liste 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))

Produzione:

tuple
4.735646052286029
list
4.7308746771886945
set
3.5755991376936436
or
4.687681658193469

Da 3 a 5 letterali, setvince ancora con un ampio margine e ordiventa il più lento.

In Python 2, setè sempre il più lento. orè il più veloce da 2 a 3 letterali tuplee listsono più veloci con 4 o più letterali. Non riuscivo a distinguere la velocità del tuplevs list.

Quando i valori da testare venivano memorizzati nella cache in una variabile globale fuori dalla funzione, anziché creare il valore letterale all'interno del loop, setvinceva ogni volta, anche in Python 2.

Questi risultati si applicano a CPython a 64 bit su un Core i7.


0

Vorrei raccomandare un'implementazione Set in cui il caso d'uso è limitato al riferimento o cercare l'esistenza e l'implementazione Tuple in cui il caso d'uso richiede l'esecuzione dell'iterazione. Un elenco è un'implementazione di basso livello e richiede un notevole sovraccarico di memoria.


1
In effetti, la corretta distinzione tra quando usare Set e quando usare Tuple è davvero della massima importanza. Non sarei preoccupato per i sovraccarichi di memoria coinvolti, impronte se non sto scrivendo un'API di livello inferiore.

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)

Output dopo aver confrontato 10 iterazioni per tutti e 3: confronto


0

Gli insiemi sono più veloci, più ottieni più funzioni con gli insiemi, ad esempio diciamo che hai due insiemi:

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

Possiamo facilmente unire due set:

set3 = set1.union(set2)

Scopri cosa è comune in entrambi:

set3 = set1.intersection(set2)

Scopri cosa c'è di diverso in entrambi:

set3 = set1.difference(set2)

E altro ancora! Provali, sono divertenti! Inoltre, se devi lavorare su valori diversi in 2 elenchi o valori comuni in 2 elenchi, preferisco convertire i tuoi elenchi in set e molti programmatori lo fanno in questo modo. Spero che ti aiuti :-)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.