Supongamos lo siguiente:
>>> s = set([1, 2, 3])
¿Cómo obtengo un valor (cualquier valor) s
sin hacerlo s.pop()
? Quiero dejar el elemento en el conjunto hasta que esté seguro de que puedo eliminarlo, algo de lo que solo puedo estar seguro después de una llamada asincrónica a otro host.
Rápido y sucio:
>>> elem = s.pop()
>>> s.add(elem)
¿Pero sabes de una mejor manera? Idealmente en tiempo constante.
union
etc., sin tomar elementos de él. Por ejemplo,next(iter({3,2,1}))
siempre regresa,1
así que si pensabas que esto devolvería un elemento aleatorio, no lo haría. Entonces, ¿tal vez solo estás usando la estructura de datos incorrecta? ¿Cuál es el caso de uso?Respuestas:
Dos opciones que no requieren copiar todo el conjunto:
O...
Pero, en general, los conjuntos no admiten indexación o segmentación.
fuente
iter(s).next()
no es asqueroso pero genial. Completamente general para tomar elementos arbitrarios de cualquier objeto iterable. Sin embargo, puede elegir si desea tener cuidado si la colección está vacía.next(iter(your_list or []), None)
para manejar conjuntos de Ninguno y conjuntos vacíosEl código mínimo sería:
Obviamente, esto crearía una nueva lista que contiene cada miembro del conjunto, por lo que no es genial si su conjunto es muy grande.
fuente
next(iter(s))
solo superalist(s)[0]
en tres caracteres y, por lo demás, es dramáticamente superior en complejidad de tiempo y espacio. Entonces, aunque la afirmación del "código mínimo" es trivialmente cierta, también es trivialmente cierto que este es el peor enfoque posible. Incluso eliminar manualmente y luego volver a agregar el elemento eliminado al conjunto original es superior a "construir un contenedor completamente nuevo solo para extraer el primer elemento", que es evidentemente una locura. Lo que más me preocupa es que 38 Stackoverflowers realmente votaron por esto. Solo sé que veré esto en el código de producción.min(s)
usa incluso menos caracteres y es tan terrible e ineficiente como esto.min(s)
es un poco más rápido quenext(iter(s))
para los conjuntos de tamaño 1, y llegué a esta respuesta buscando específicamente extraer un único elemento de los conjuntos en caso especial de tamaño 1.Me preguntaba cómo funcionarán las funciones para diferentes conjuntos, así que hice un punto de referencia:
Este gráfico muestra claramente que algunos enfoques (
RandomSample
,SetUnpacking
yListIndex
) dependen del tamaño del conjunto y deben evitarse en el caso general (al menos si el rendimiento puede ser importante). Como ya se mostró en las otras respuestas, la forma más rápida esForLoop
.Sin embargo, siempre que se utilice uno de los enfoques de tiempo constante, la diferencia de rendimiento será insignificante.
iteration_utilities
(Descargo de responsabilidad: soy el autor) contiene una función conveniente para este caso de usofirst
:También lo incluí en el punto de referencia anterior. Puede competir con las otras dos soluciones "rápidas", pero la diferencia no es mucho de ninguna manera.
fuente
tl; dr
for first_item in muh_set: break
sigue siendo el enfoque óptimo en Python 3.x. Te maldigo, Guido.haces esto
Bienvenido a otro conjunto de temporizaciones de Python 3.x, extrapolado de wr. 's excelente respuesta específica 2.x-Python . A diferencia de la respuesta específica de Python 3.x igualmente útil de AChampion , los tiempos a continuación también presentan soluciones atípicas sugeridas anteriormente, que incluyen:
list(s)[0]
, La nueva solución basada en secuencias de John .random.sample(s, 1)
, dF. La solución ecléctica basada en RNG .Fragmentos de código para gran alegría
Enciende, sintoniza, cronometra:
Tiempos intemporales rápidamente obsoletos
¡Mirad! Ordenado por fragmentos más rápidos a más lentos:
Faceplants para toda la familia
Como era de esperar, la iteración manual sigue siendo al menos el doble de rápida que la próxima solución más rápida. Aunque la brecha ha disminuido desde los días Bad Old Python 2.x (en los que la iteración manual fue al menos cuatro veces más rápida), decepciona al fanático de PEP 20 en mí que la solución más detallada es la mejor. Al menos convertir un conjunto en una lista solo para extraer el primer elemento del conjunto es tan horrible como se esperaba. Gracias Guido, que su luz continúe guiándonos.
Sorprendentemente, la solución basada en RNG es absolutamente horrible. La conversión de la lista es mala, pero
random
realmente toma el pastel de salsa horrible. Esto en cuanto al Dios del número aleatorio .Solo desearía que los amorfos
set.get_first()
ya nos hicieran PEP un método. Si estás leyendo esto, ellos: "Por favor. Haz algo".fuente
next(iter(s))
es dos veces más lento quefor x in s: break
enCPython
es un poco extraño. Quiero decir que esCPython
. Será aproximadamente 50-100 veces (o algo así) más lento que C o Haskell haciendo lo mismo (la mayor parte del tiempo, especialmente en iteración, sin eliminación de llamadas de cola y sin optimizaciones de ningún tipo). Perder algunos microsegundos no hace una diferencia real. ¿No te parece? Y también está PyPyPara proporcionar algunas cifras de tiempo detrás de los diferentes enfoques, considere el siguiente código. El get () es mi adición personalizada al setobject.c de Python, siendo solo un pop () sin eliminar el elemento.
El resultado es:
Esto significa que la solución for / break es la más rápida (a veces más rápida que la solución get () personalizada).
fuente
for x in s
también? "Se crea un iterador para el resultado de laexpression_list
".s.remove()
a la mezcla de lositer
ejemplos tantofor
yiter
voy catastróficamente mal.Como desea un elemento aleatorio, esto también funcionará:
La documentación no parece mencionar el rendimiento de
random.sample
. De una prueba empírica realmente rápida con una gran lista y un gran conjunto, parece ser un tiempo constante para una lista pero no para el conjunto. Además, la iteración sobre un conjunto no es aleatoria; el orden es indefinido pero predecible:Si la aleatoriedad es importante y necesita un montón de elementos en tiempo constante (conjuntos grandes),
random.sample
primero usaría y convertiría a una lista:fuente
choice()
, porque Python intentará indexar tu conjunto y eso no funciona.Aparentemente la forma más compacta (6 símbolos) aunque muy lenta para obtener un elemento establecido (hecho posible por PEP 3132 ):
Con Python 3.5+ también puede usar esta expresión de 7 símbolos (gracias a PEP 448 ):
Ambas opciones son aproximadamente 1000 veces más lentas en mi máquina que el método for-loop.
fuente
Yo uso una función de utilidad que escribí. Su nombre es algo engañoso porque implica que podría ser un elemento aleatorio o algo así.
fuente
Siguiendo @wr. post, obtengo resultados similares (para Python3.5)
Salida:
Sin embargo, al cambiar el conjunto subyacente (por ejemplo, llamar a
remove()
) las cosas van mal para los ejemplos iterables (for
,iter
):Resultados en:
fuente
Lo que suelo hacer para colecciones pequeñas es crear un tipo de método analizador / convertidor como este
Entonces puedo usar la nueva lista y acceder por número de índice
Como lista, tendrá todos los otros métodos con los que puede necesitar trabajar
fuente
list
lugar de crear un método convertidor?¿Qué tal
s.copy().pop()
? No lo he cronometrado, pero debería funcionar y es simple. Sin embargo, funciona mejor para conjuntos pequeños, ya que copia todo el conjunto.fuente
Otra opción es usar un diccionario con valores que no le interesan. P.ej,
Puede tratar las teclas como un conjunto, excepto que son solo una matriz:
Un efecto secundario de esta elección es que su código será compatible con
set
versiones anteriores de Python anteriores. Tal vez no sea la mejor respuesta, pero es otra opción.Editar: Incluso puede hacer algo como esto para ocultar el hecho de que usó un dict en lugar de una matriz o conjunto:
fuente