2012-05-15 13 views
22

Ho un dizionario, pieno di elementi. Voglio dare un'occhiata a un singolo oggetto arbitrario:Modo Pythonic per accedere all'elemento arbitrario dal dizionario

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

Non mi interessa quale elemento. Non è necessario che sia casuale.

Posso pensare a molti modi di implementarlo, ma sembrano tutti sprechi. Mi chiedo se alcuni siano idiomi preferiti in Python, o (ancora meglio) se ne manchi 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 

io sono in una posizione in cui la prestazione non è abbastanza fondamentale che questo importa (ancora), così posso essere accusato di ottimizzazione prematura, ma sto cercando di migliorare il mio stile di codifica Python, quindi se c'è una variante facilmente comprensibile, sarebbe bene adottarla.

+7

Che dire di 'dictionary.itervalues ​​(). Next()'? Sarebbe almeno meglio della tua seconda funzione "arb". – srgerg

+0

@sgerg Stavo per presentarlo ma tu vai avanti. : D – jamylak

+0

Devono essere diversi per tutta la chiamata?Tutto ciò restituirà lo stesso elemento ... –

risposta

26

simile alla vostra seconda soluzione, ma un po 'più evidente, a mio parere:

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

Questo funziona in Python 2, così come in Python 3, ma in Python 2 è più efficiente di fare in questo modo :

return next(dictionary.itervalues()) 
+0

Molto più ovvio secondo me! – jamylak

+4

Va notato che ciò solleva StopIteration se il dict è vuoto. – yak

+5

Ma se si desidera evitare "StopIteration', è possibile specificare un valore predefinito, ad es. 'next (dictionary.itervalues ​​(), None)'. –

1

Perché non utilizzare random?

import random 

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

Ciò rende molto chiaro che il risultato è destinato a essere puramente arbitraria e non un'implementazione effetto collaterale. Fino a quando le prestazioni diventano un problema reale, vai sempre con chiarezza sulla velocità.

È un peccato che dict_value non supporti l'indicizzazione, sarebbe bello poter passare invece nella vista valore.

Aggiornamento: dal momento che tutti sono così ossessionati dalle prestazioni, la funzione sopra riportata richiede < 120 ms per restituire un valore casuale da un ditt di 1 milione di elementi. Affidarsi a un codice chiaro non è il sorprendente successo di prestazioni che si sta facendo.

+2

Dove è stato specificato che l'elemento selezionato non deve essere casuale, questa è una perdita di tempo. Una docstring (e il nome!) È del tutto sufficiente per tali osservazioni. –

+1

Se il nome è "arbitrario" e l'azione è quella di iterare attraverso i tasti, quel nome non sarebbe chiaro per me, quindi sì, una docstring è necessaria. Se devi scrivere una docstring per spiegare perché il tuo codice sta facendo qualcosa di diverso da quello che appare, forse la risposta è scrivere un codice più chiaro. –

+1

120 ms è assolutamente inaccettabile per un'operazione come questa. Questo dovrebbe finire in meno di un microsecondo. Inseritelo dove state usando in precedenza 'd [next (iter (d))]' e la vostra applicazione potrebbe ottenere letteralmente un milione di volte più lentamente. – user2357112

10

Evitando l'intero values/itervalues/viewvalues pasticcio, questo funziona altrettanto bene in python2 o python3

dictionary[next(iter(dictionary))] 

in alternativa, se si preferisce generatore di espressioni

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

si potrebbe semplicemente anche fare 'next (iter (dictionary.values ​​()))' –

+0

@ Ev.Kounis, ma in python2, che crea una lista aggiuntiva. –

+0

'map' crea anche un elenco in Python 2. – user2357112

2

Credo che il problema è stato significativamente risposto ma si spera che questo confronto farà luce sul codice pulito contro il time trade off:

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)) 

Ecco i risultati:

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

Sembra che l'utilizzo di iterkeys è solo marginale, più veloce poping un elemento e ri-inserimento, ma 10x più veloce quindi la creazione della lista e la scelta di un oggetto a caso da esso.

Problemi correlati