2012-05-15 7 views
22

Tengo un diccionario, lleno de elementos. Quiero echar un vistazo a un artículo único y arbitrario:Manera pitónica de acceder al elemento arbitrario del diccionario

print "Amongst our dictionary's items are such diverse elements as: %s" % arb(dictionary) 

No me importa qué artículo. No es necesario que sea aleatorio.

Se me ocurren muchas formas de implementar esto, pero todas parecen ser un desperdicio. Me pregunto si alguno de los idiomas preferidos en Python, o (incluso mejor) si me falta uno.

def arb(dictionary): 
# Creates an entire list in memory. Could take a while. 
    return list(dictionary.values())[0] 

def arb(dictionary): 
# Creates an entire interator. An improvement. 
    for item in dictionary.itervalues(): 
     return item 

def arb(dictionary): 
# No iterator, but writes to the dictionary! Twice! 
    key, value = dictionary.popitem() 
    dictionary[key] = value 
    return value 

Estoy en una posición en la que el rendimiento no es lo suficientemente importante que esto es importante (aún), por lo que pueden ser acusados ​​de optimización prematura, pero estoy tratando de mejorar mi estilo de programación Python, por lo que si hay una variante fácilmente comprensible, sería bueno adoptarla.

+7

¿Qué pasa con 'dictionary.itervalues ​​(). Next()'? Eso sería al menos mejor que tu segunda función 'arb'. – srgerg

+0

@sgerg Iba a presentar eso, pero sigue adelante. : D – jamylak

+0

¿Tienen que ser elementos diferentes en la llamada?Todos estos devolverán el mismo artículo ... –

Respuesta

26

Al igual que en su segunda solución, pero un poco más evidente, en mi opinión:

return next(iter(dictionary.values())) 

Esto funciona en Python 2, así como en Pitón 3, pero en Python 2 es más eficiente para hacerlo de esta manera :

return next(dictionary.itervalues()) 
+0

¡Mucho más obvio en mi opinión! – jamylak

+4

Cabe señalar que esto provoca StopIteration si el dict está vacío. – yak

+5

Pero si quiere evitar el 'StopIteration', puede especificar un valor predeterminado, p. 'next (dictionary.itervalues ​​(), None)'. –

1

¿Por qué no utilizar random?

import random 

def arb(dictionary): 
    return random.choice(dictionary.values()) 

Esto hace que sea muy claro que el resultado está destinado a ser puramente arbitraria y no una aplicación de efectos secundarios. Hasta que el rendimiento se convierta en un problema real, siempre ve con claridad sobre la velocidad.

Es una lástima que dict_values ​​no sea compatible con la indexación, sería bueno poder pasar la vista de valor en su lugar.

Actualización: dado que todos están tan obsesionados con el rendimiento, la función anterior toma < 120ms para devolver un valor aleatorio de un dict de 1 millón de elementos. Confiar en un código claro no es el increíble rendimiento que se está logrando.

+2

Donde se ha especificado que el elemento seleccionado no necesita ser aleatorio, esto es una pérdida de tiempo. Una docstring (¡y el nombre!) Es totalmente suficiente para tales observaciones. –

+1

Si el nombre es 'arbitrario' y la acción es iterar a través de las claves, ese nombre no sería claro para mí, así que sí, es necesaria una docstring. Si tiene que escribir un docstring para explicar por qué su código está haciendo algo diferente de lo que parece, tal vez la respuesta sea escribir un código más claro. –

+1

120 ms es completamente inaceptable para una operación como esta. Esto debería terminar en menos de un microsegundo. Lo pones donde antes estabas usando 'd [next (iter (d))]' y tu aplicación podría volverse literalmente un millón de veces más lenta. – user2357112

10

Evitar toda la values/itervalues/viewvalues lío, esto funciona igual de bien en python2 o python3

dictionary[next(iter(dictionary))] 

, alternativamente, si lo prefiere generador de expresiones

next(dictionary[x] for x in dictionary) 
+0

simplemente podría hacer 'next (iter (dictionary.values ​​()))' –

+0

@ Ev.Kounis, pero en python2, eso crea una lista extra. –

+0

'map' también crea una lista en Python 2. – user2357112

2

creo que la pregunta ha sido contestada de manera significativa pero con suerte, esta comparación arrojará algo de luz sobre el código limpio en función del tiempo:

from timeit import timeit 
from random import choice 
A = {x:[y for y in range(100)] for x in range(1000)} 
def test_pop(): 
    k, v= A.popitem() 
    A[k] = v 

def test_iter(): k = next(A.iterkeys()) 

def test_list(): k = choice(A.keys()) 

def test_insert(): A[0] = 0 

if __name__ == '__main__': 
    print('pop', timeit("test_pop()", setup="from __main__ import test_pop", number=10000)) 
    print('iter', timeit("test_iter()", setup="from __main__ import test_iter", number=10000)) 
    print('list', timeit("test_list()", setup="from __main__ import test_list", number=10000)) 
    print('insert', timeit("test_insert()", setup="from __main__ import test_insert", number=10000)) 

Éstos son los resultados:

('pop', 0.0021750926971435547) 
('iter', 0.002003908157348633) 
('list', 0.047267913818359375) 
('insert', 0.0010859966278076172) 

Parece que el uso de iterkeys es marginal más rápido que poping un elemento y volver a insertar pero de 10x más rápido que la creación de la lista y elegir un objeto al azar de la misma.

Cuestiones relacionadas