¿Cuál es la "forma [...] obvia" de agregar todos los elementos de un iterable a uno existente set
?
¿Cuál es la "forma [...] obvia" de agregar todos los elementos de un iterable a uno existente set
?
Respuestas:
Puede agregar elementos de list
a a set
como este:
>>> 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
constructor toma un iterable como argumento.
{1, 2, 3}
en Python 3, mientras que estaba set([1, 2, 3])
en Python 2.
Para el beneficio de cualquier persona que pueda creer, por ejemplo, que hacer aset.add()
un ciclo tendría un rendimiento competitivo aset.update()
, aquí hay un ejemplo de cómo puede poner a prueba sus creencias rápidamente antes de hacerlo público:
>\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
Parece que el costo por artículo del enfoque de bucle es TRES veces mayor que el del update
enfoque.
El uso |= set()
cuesta aproximadamente 1.5 update
veces lo que hace, pero la mitad de lo que hace agregar cada elemento individual en un ciclo.
Puede usar la función set () para convertir un iterable en un conjunto, y luego usar el operador de actualización de conjunto estándar (| =) para agregar los valores únicos de su nuevo conjunto al existente.
>>> a = { 1, 2, 3 }
>>> b = ( 3, 4, 5 )
>>> a |= set(b)
>>> a
set([1, 2, 3, 4, 5])
.update
tiene el beneficio de que el argumento puede ser iterable, no necesariamente un conjunto, a diferencia del RHS del |=
operador en su ejemplo.
|
para unión, &
intersección y ^
para obtener elementos que están en uno u otro pero no en ambos. Pero en un lenguaje de tipo dinámico donde a veces es difícil leer el código y conocer los tipos de objetos que vuelan, me siento reacio a usar estos operadores. Alguien que no los reconoce (o tal vez ni siquiera se da cuenta de que Python permite operadores como estos) podría confundirse y pensar que están ocurriendo algunas operaciones lógicas o bit a bit extrañas. Sería bueno si estos operadores trabajaran también en otros iterables ...
.update()
y agregué elementos individuales en un bucle. Descubrí que .update()
fue más rápido.
Solo una actualización rápida, tiempos 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)
los resultados son:
one_by_one 10.184448844986036
cast_to_list_and_back 7.969255169969983
update_set 2.212590195937082
Usa la comprensión de la lista.
Cortocircuito en la creación de iterables usando una lista, por ejemplo :)
>>> 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])
>>>
[Editar: se perdió la parte de la pregunta establecida]
for item in items:
extant_set.add(item)
Para que conste, creo que la afirmación de que "debería haber una, y preferiblemente solo una, forma obvia de hacerlo". es falso Supone que muchas personas de mentalidad técnica hacen, que todos piensan igual. Lo que es obvio para una persona no es tan obvio para otra.
Yo diría que mi solución propuesta es claramente legible y hace lo que usted pide. No creo que haya ningún éxito en el rendimiento involucrado, aunque admito que podría estar perdiendo algo. Pero a pesar de todo eso, puede que no sea obvio y preferible para otro desarrollador.
aset.update(iterable)
repite a velocidad C, mientras que se for item in iterable: aset.add(item)
repite a velocidad de Python, con una búsqueda de método y una llamada de método (¡¡¡aarrgghh !!) por elemento.