Supposons ce qui suit:
>>> s = set([1, 2, 3])
Comment puis-je obtenir une valeur (n'importe quelle valeur) s
sans le faire s.pop()
? Je veux laisser l'élément dans l'ensemble jusqu'à ce que je sois sûr de pouvoir le supprimer - quelque chose dont je ne peux être sûr qu'après un appel asynchrone à un autre hôte.
Rapide et sale:
>>> elem = s.pop()
>>> s.add(elem)
Mais connaissez-vous un meilleur moyen? Idéalement en temps constant.
union
etc sans en retirer des éléments. Par exemple,next(iter({3,2,1}))
renvoie toujours1
donc si vous pensiez que cela retournerait un élément aléatoire - ce ne serait pas. Alors peut-être utilisez-vous simplement la mauvaise structure de données? Quel est le cas d'utilisation?Réponses:
Deux options qui ne nécessitent pas de copier l'ensemble complet:
Ou...
Mais en général, les ensembles ne prennent pas en charge l'indexation ou le découpage.
la source
iter(s).next()
n'est pas grossier mais super. Complètement général pour prendre un élément arbitraire de tout objet itérable. Votre choix si vous voulez être prudent si la collection est vide.next(iter(your_list or []), None)
pour gérer les ensembles Aucun et les ensembles videsLe moins de code serait:
Évidemment, cela créerait une nouvelle liste qui contient chaque membre de l'ensemble, donc pas génial si votre ensemble est très grand.
la source
next(iter(s))
ne dépasse quelist(s)[0]
de trois caractères et est par ailleurs considérablement supérieur en termes de complexité temporelle et spatiale. Ainsi, alors que la revendication du "moindre code" est trivialement vraie, il est également trivialement vrai que c'est la pire approche possible. Même supprimer manuellement puis rajouter l'élément supprimé à l'ensemble d'origine est supérieur à «construire un tout nouveau conteneur juste pour extraire le premier élément», ce qui est manifestement fou. Ce qui me préoccupe le plus, c'est que 38 Stackoverflowers ont voté en faveur de cela. Je sais juste que je verrai cela dans le code de production.min(s)
encore moins de caractères tout en étant aussi terrible et inefficace que cela.min(s)
est légèrement plus rapide quenext(iter(s))
pour les ensembles de taille 1, et je suis venu à cette réponse en recherchant spécifiquement un cas spécial en extrayant le seul élément des ensembles de taille 1.Je me demandais comment les fonctions fonctionneraient pour différents ensembles, alors j'ai fait un test de performance:
Ce graphique montre clairement que certaines approches (
RandomSample
,SetUnpacking
etListIndex
) dépendent de la taille de l'ensemble et doivent être évitées dans le cas général (au moins si les performances peuvent être importantes). Comme l'ont déjà montré les autres réponses, le moyen le plus rapide estForLoop
.Cependant, tant qu'une des approches à temps constant est utilisée, la différence de performances sera négligeable.
iteration_utilities
(Avertissement: je suis l'auteur) contient une fonction pratique pour ce cas d'utilisationfirst
::Je l'ai également inclus dans l'indice de référence ci-dessus. Il peut rivaliser avec les deux autres solutions «rapides», mais la différence n'est pas grande dans les deux cas.
la source
tl; dr
for first_item in muh_set: break
reste l'approche optimale dans Python 3.x. Je te maudis, Guido.tu fais ça
Bienvenue dans un autre ensemble de timings Python 3.x, extrapolé à partir de wr. est une excellente réponse spécifique à Python 2.x . Contrairement à AChampion réponse spécifique à Python 3.x tout aussi utile d' , les délais ci-dessous temporisent également les solutions aberrantes suggérées ci-dessus - y compris:
list(s)[0]
, La nouvelle solution basée sur des séquences de John .random.sample(s, 1)
, dF. la solution éclectique à base de RNG .Extraits de code pour Great Joy
Allumez, syntonisez, chronométrez:
Timings intemporels rapidement obsolètes
Voir! Ordonné par extraits les plus rapides aux plus lents:
Plantes faciales pour toute la famille
Sans surprise, l' itération manuelle reste au moins deux fois plus rapide que la solution la plus rapide suivante. Bien que l'écart ait diminué depuis les jours Bad Old Python 2.x (au cours desquels l'itération manuelle était au moins quatre fois plus rapide), il déçoit en moi le fanatique PEP 20 que la solution la plus verbeuse est la meilleure. Au moins, convertir un ensemble en liste juste pour extraire le premier élément de l'ensemble est aussi horrible que prévu.Merci Guido, que sa lumière continue de nous guider.
Étonnamment, la solution basée sur RNG est absolument horrible. La conversion de liste est mauvaise, mais prend
random
vraiment le gâteau de sauce horrible. Voilà pour le Dieu du nombre aléatoire .Je souhaite juste aux amorphes qu'ils auraient PEP une
set.get_first()
méthode pour nous déjà. Si vous lisez ceci, ils: "S'il vous plaît. Faites quelque chose."la source
next(iter(s))
est deux fois plus lent quefor x in s: break
dansCPython
est un peu étrange. Je veux dire que ouiCPython
. Ce sera environ 50-100 fois (ou quelque chose comme ça) plus lent que C ou Haskell faisant la même chose (pour la plupart du temps, surtout en itération, pas d'élimination des appels de queue et aucune optimisation que ce soit). La perte de quelques microsecondes ne fait pas vraiment de différence. Tu ne crois pas? Et il y a aussi PyPyPour fournir des chiffres de synchronisation derrière les différentes approches, considérez le code suivant. Le get () est mon ajout personnalisé au setobject.c de Python, étant juste un pop () sans supprimer l'élément.
La sortie est:
Cela signifie que la solution for / break est la plus rapide (parfois plus rapide que la solution get () personnalisée).
la source
for x in s
aussi? "Un itérateur est créé pour le résultat de laexpression_list
."s.remove()
au mélange lesiter
exemples à la foisfor
et queiter
ça va terriblement mal.Puisque vous voulez un élément aléatoire, cela fonctionnera également:
La documentation ne semble pas mentionner les performances de
random.sample
. À partir d'un test empirique très rapide avec une liste énorme et un ensemble énorme, il semble qu'il soit temps constant pour une liste mais pas pour l'ensemble. De plus, l'itération sur un ensemble n'est pas aléatoire; l'ordre est indéfini mais prévisible:Si le caractère aléatoire est important et que vous avez besoin d'un tas d'éléments en temps constant (grands ensembles), j'utiliserais
random.sample
et convertirais d'abord en liste:la source
choice()
, car Python essaiera d'indexer votre ensemble et cela ne fonctionne pas.Apparemment le moyen le plus compact (6 symboles) mais très lent pour obtenir un élément set (rendu possible par PEP 3132 ):
Avec Python 3.5+, vous pouvez également utiliser cette expression à 7 symboles (grâce à PEP 448 ):
Les deux options sont environ 1000 fois plus lentes sur ma machine que la méthode for-loop.
la source
J'utilise une fonction utilitaire que j'ai écrite. Son nom est quelque peu trompeur car il implique en quelque sorte qu'il pourrait s'agir d'un élément aléatoire ou quelque chose comme ça.
la source
Après @wr. post, j'obtiens des résultats similaires (pour Python3.5)
Production:
Cependant, lors du changement de l'ensemble sous-jacent (par exemple l'appel à
remove()
), les choses vont mal pour les exemples itérables (for
,iter
):Résulte en:
la source
Ce que je fais habituellement pour les petites collections, c'est de créer une sorte de méthode analyseur / convertisseur comme celle-ci
Ensuite, je peux utiliser la nouvelle liste et accéder par numéro d'index
En tant que liste, vous aurez toutes les autres méthodes avec lesquelles vous devrez peut-être travailler
la source
list
au lieu de créer une méthode de conversion?Et alors
s.copy().pop()
? Je ne l'ai pas chronométré, mais ça devrait marcher et c'est simple. Cependant, cela fonctionne mieux pour les petits ensembles, car il copie l'ensemble entier.la source
Une autre option consiste à utiliser un dictionnaire avec des valeurs qui ne vous intéressent pas. Par exemple,
Vous pouvez traiter les clés comme un ensemble, sauf qu'elles ne sont qu'un tableau:
Un effet secondaire de ce choix est que votre code sera rétrocompatible avec les anciennes
set
versions antérieures de Python. Ce n'est peut-être pas la meilleure réponse, mais c'est une autre option.Edit: Vous pouvez même faire quelque chose comme ça pour cacher le fait que vous avez utilisé un dict au lieu d'un tableau ou d'un ensemble:
la source