Quelle est la "manière [...] la plus évidente" d'ajouter tous les éléments d'un itérable à un existant set
?
Quelle est la "manière [...] la plus évidente" d'ajouter tous les éléments d'un itérable à un existant set
?
Réponses:
Vous pouvez ajouter des éléments de a list
à un set
comme ceci:
>>> 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
constructeur prend un itérable comme argument.
{1, 2, 3}
en Python 3 alors qu'elle était set([1, 2, 3])
en Python 2.
Pour le bénéfice de tous ceux qui pourraient croire, par exemple, que faire aset.add()
en boucle aurait des performances compétitives avec le faire aset.update()
, voici un exemple de la façon dont vous pouvez tester rapidement vos croyances avant de devenir public:
>\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
On dirait que le coût par élément de l'approche en boucle est plus de TROIS fois celui de l' update
approche.
L'utilisation |= set()
coûte environ 1,5 fois ce que update
fait mais la moitié de ce que fait l'ajout de chaque élément individuel dans une boucle.
Vous pouvez utiliser la fonction set () pour convertir un itérable en un ensemble, puis utiliser l'opérateur de mise à jour de l'ensemble standard (| =) pour ajouter les valeurs uniques de votre nouvel ensemble à l'existant.
>>> a = { 1, 2, 3 }
>>> b = ( 3, 4, 5 )
>>> a |= set(b)
>>> a
set([1, 2, 3, 4, 5])
.update
a l'avantage que l'argument peut être n'importe quel itérable - pas nécessairement un ensemble - contrairement au RHS de l' |=
opérateur dans votre exemple.
|
pour l'union, &
pour l'intersection et ^
pour obtenir des éléments qui sont dans l'un ou l'autre mais pas les deux. Mais dans un langage typé dynamiquement où il est parfois difficile de lire le code et de connaître les types d'objets qui volent, j'hésite à utiliser ces opérateurs. Quelqu'un qui ne les reconnaît pas (ou ne se rend peut-être pas compte que Python autorise des opérateurs comme ceux-ci) pourrait être confus et penser que des opérations binaires ou logiques étranges sont en cours. Ce serait bien si ces opérateurs travaillaient aussi sur d'autres itérables ...
.update()
et ajouter des éléments individuels dans une boucle. J'ai trouvé que .update()
c'était plus rapide. J'ai ajouté mes résultats à cette réponse existante: stackoverflow.com/a/4046249/901641
Juste une mise à jour rapide, timings utilisant 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)
les résultats sont:
one_by_one 10.184448844986036
cast_to_list_and_back 7.969255169969983
update_set 2.212590195937082
Utilisez la compréhension de liste.
Court-circuiter la création d'itérable à l'aide d'une liste par exemple :)
>>> 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])
>>>
[Modifier: a manqué la partie définie de la question]
for item in items:
extant_set.add(item)
Pour mémoire, je pense que l'affirmation selon laquelle "il devrait y avoir une - et de préférence une seule - façon évidente de le faire." est faux. Cela suppose que de nombreuses personnes à l'esprit technique font, que tout le monde pense de la même manière. Ce qui est évident pour une personne ne l'est pas pour une autre.
Je dirais que ma solution proposée est clairement lisible et fait ce que vous demandez. Je ne pense pas qu'il y ait de succès en termes de performance - même si j'admets que je manque peut-être quelque chose. Mais malgré tout cela, cela n'est peut-être pas évident et préférable à un autre développeur.
aset.update(iterable)
boucle à la vitesse C alors que les for item in iterable: aset.add(item)
boucles à la vitesse Python, avec une recherche de méthode et un appel de méthode (aarrgghh !!) par élément.