Qual è il "modo [...] ovvio" per aggiungere tutti gli elementi di un iterabile a un esistente set
?
Qual è il "modo [...] ovvio" per aggiungere tutti gli elementi di un iterabile a un esistente set
?
Risposte:
Puoi aggiungere elementi di list
a a set
come questo:
>>> foo = set(range(0, 4))
>>> foo
set([0, 1, 2, 3])
>>> foo.update(range(2, 6))
>>> foo
set([0, 1, 2, 3, 4, 5])
set
costruttore prende un iterabile come argomento.
{1, 2, 3}
in Python 3 mentre era set([1, 2, 3])
in Python 2.
A beneficio di chiunque possa credere, ad esempio, che fare aset.add()
in un ciclo avrebbe prestazioni competitive rispetto al fare aset.update()
, ecco un esempio di come è possibile verificare rapidamente le proprie convinzioni prima di diventare pubbliche:
>\python27\python -mtimeit -s"it=xrange(10000);a=set(xrange(100))" "a.update(it)"
1000 loops, best of 3: 294 usec per loop
>\python27\python -mtimeit -s"it=xrange(10000);a=set(xrange(100))" "for i in it:a.add(i)"
1000 loops, best of 3: 950 usec per loop
>\python27\python -mtimeit -s"it=xrange(10000);a=set(xrange(100))" "a |= set(it)"
1000 loops, best of 3: 458 usec per loop
>\python27\python -mtimeit -s"it=xrange(20000);a=set(xrange(100))" "a.update(it)"
1000 loops, best of 3: 598 usec per loop
>\python27\python -mtimeit -s"it=xrange(20000);a=set(xrange(100))" "for i in it:a.add(i)"
1000 loops, best of 3: 1.89 msec per loop
>\python27\python -mtimeit -s"it=xrange(20000);a=set(xrange(100))" "a |= set(it)"
1000 loops, best of 3: 891 usec per loop
Sembra che il costo per articolo dell'approccio ad anello sia oltre TRE volte quello update
dell'approccio.
L'utilizzo |= set()
costa circa 1,5 volte ciò che update
fa, ma la metà di ciò che fa l'aggiunta di ogni singolo elemento in un ciclo.
È possibile utilizzare la funzione set () per convertire un iterabile in un set, quindi utilizzare l'operatore di aggiornamento set standard (| =) per aggiungere i valori univoci dal nuovo set a quello esistente.
>>> a = { 1, 2, 3 }
>>> b = ( 3, 4, 5 )
>>> a |= set(b)
>>> a
set([1, 2, 3, 4, 5])
.update
ha il vantaggio che l'argomento può essere qualsiasi iterabile, non necessariamente un insieme, a differenza dell'RHS |=
dell'operatore nel tuo esempio.
|
per l'unione, &
per l'intersezione e ^
per ottenere elementi che si trovano nell'uno o nell'altro ma non in entrambi. Ma in un linguaggio tipizzato in modo dinamico in cui a volte è difficile leggere il codice e conoscere i tipi di oggetti che volano in giro, mi sento titubante a usare questi operatori. Qualcuno che non li riconosce (o forse non si rende nemmeno conto che Python consente agli operatori come questi) potrebbe essere confuso e pensare che siano in corso delle strane operazioni bit per bit o logiche. Sarebbe bello se anche questi operatori lavorassero su altri iterabili ...
.update()
e aggiungi singoli elementi in un ciclo. Ho scoperto che .update()
era più veloce. Ho aggiunto i miei risultati a questa risposta esistente: stackoverflow.com/a/4046249/901641
Solo un rapido aggiornamento, i tempi usando Python 3:
#!/usr/local/bin python3
from timeit import Timer
a = set(range(1, 100000))
b = list(range(50000, 150000))
def one_by_one(s, l):
for i in l:
s.add(i)
def cast_to_list_and_back(s, l):
s = set(list(s) + l)
def update_set(s,l):
s.update(l)
i risultati sono:
one_by_one 10.184448844986036
cast_to_list_and_back 7.969255169969983
update_set 2.212590195937082
Usa la comprensione dell'elenco.
Cortocircuito la creazione di iterabile utilizzando un elenco, ad esempio :)
>>> x = [1, 2, 3, 4]
>>>
>>> k = x.__iter__()
>>> k
<listiterator object at 0x100517490>
>>> l = [y for y in k]
>>> l
[1, 2, 3, 4]
>>>
>>> z = Set([1,2])
>>> z.update(l)
>>> z
set([1, 2, 3, 4])
>>>
[Modifica: mancata la parte impostata della domanda]
for item in items:
extant_set.add(item)
Per la cronaca, penso l'affermazione che "Dovrebbe esserci uno - e preferibilmente solo un - modo obsoleto di farlo". è falso. Presuppone che molte persone di mentalità tecnica fanno, che tutti pensano allo stesso modo. Ciò che è ovvio per una persona non è così evidente per un'altra.
Direi che la mia soluzione proposta è chiaramente leggibile e fa quello che chiedi. Non credo che ci siano hit di performance coinvolti, anche se ammetto che potrei perdere qualcosa. Ma nonostante tutto, potrebbe non essere ovvio e preferibile a un altro sviluppatore.
aset.update(iterable)
scorre alla velocità C mentre for item in iterable: aset.add(item)
scorre alla velocità Python, con una ricerca del metodo e una chiamata del metodo (aarrgghh !!) per articolo.