Accesso agli elementi in un collection.OrderedDict per indice


142

Diciamo che ho il seguente codice:

import collections
d = collections.OrderedDict()
d['foo'] = 'python'
d['bar'] = 'spam'

Esiste un modo per accedere agli articoli in modo numerato, come:

d(0) #foo's Output
d(1) #bar's Output

Risposte:


181

Se è un OrderedDict(), puoi accedere facilmente agli elementi indicizzando ottenendo le tuple di coppie (chiave, valore) come segue

>>> import collections
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'
>>> d.items()
[('foo', 'python'), ('bar', 'spam')]
>>> d.items()[0]
('foo', 'python')
>>> d.items()[1]
('bar', 'spam')

Nota per Python 3.X

dict.itemsrestituirebbe un oggetto iterabile vista dict piuttosto che un elenco. Dobbiamo includere la chiamata in un elenco per rendere possibile l'indicizzazione

>>> items = list(d.items())
>>> items
[('foo', 'python'), ('bar', 'spam')]
>>> items[0]
('foo', 'python')
>>> items[1]
('bar', 'spam')

21
Si noti che in 3.x il itemsmetodo restituisce un oggetto visualizzabile del dizionario anziché un elenco e non supporta lo slicing o l'indicizzazione. Quindi dovresti prima trasformarlo in un elenco. docs.python.org/3.3/library/stdtypes.html#dict-views
Peter DeGlopper

8
La copia di elementi, valori o chiavi in ​​elenchi può essere piuttosto lenta per i grandi dittonari. Ho creato una riscrittura di OrderedDict () con una diversa struttura di dati interna per le applicazioni che devono farlo molto spesso: github.com/niklasf/indexed.py
Niklas,

1
@PeterDeGlopper come posso trasformarlo in un elenco?
Dejell,

1
@Dejel - usa il costruttore:list(d.items())
Peter DeGlopper

9
Se si accede a un solo elemento, è possibile evitare l'overhead della memoria list(d.items())utilizzando next(islice(d.items(), 1))per ottenere('bar', 'spam')
Quantum7

24

Devi usare un OrderedDict o vuoi specificamente un tipo simile a una mappa che sia ordinato in qualche modo con una rapida indicizzazione della posizione? Se quest'ultimo, considera uno dei molti tipi di dict ordinati di Python (che ordina le coppie chiave-valore in base all'ordinamento delle chiavi). Alcune implementazioni supportano anche l'indicizzazione rapida. Ad esempio, il progetto sortcontainers ha un tipo SortedDict proprio per questo scopo.

>>> from sortedcontainers import SortedDict
>>> sd = SortedDict()
>>> sd['foo'] = 'python'
>>> sd['bar'] = 'spam'
>>> print sd.iloc[0] # Note that 'bar' comes before 'foo' in sort order.
'bar'
>>> # If you want the value, then simple do a key lookup:
>>> print sd[sd.iloc[1]]
'python'

1
È inoltre possibile utilizzare SortedDictuna funzione chiave per evitare confronti. Come: SortedDict(lambda key: 0, ...). Le chiavi non saranno quindi ordinate ma rimarranno in un ordine stabile e sono indicizzabili.
GrantJ

19

Ecco un caso speciale se si desidera la prima voce (o vicino ad essa) in un OrderedDict, senza creare un elenco. (Questo è stato aggiornato a Python 3):

>>> from collections import OrderedDict
>>> 
>>> d = OrderedDict()
>>> d["foo"] = "one"
>>> d["bar"] = "two"
>>> d["baz"] = "three"
>>> next(iter(d.items()))
('foo', 'one')
>>> next(iter(d.values()))
'one'

(La prima volta che dici "next ()", significa davvero "first".)

Nel mio test informale, next(iter(d.items()))con un piccolo OrderedDict è solo un po 'più veloce di items()[0]. Con un OrderedDict di 10.000 voci, next(iter(d.items()))era circa 200 volte più veloce di items()[0].

MA se salvi una volta l'elenco degli oggetti () e poi usi molto l'elenco, questo potrebbe essere più veloce. Oppure se {crei ripetutamente un iteratore items () e lo passi nella posizione desiderata}, ciò potrebbe essere più lento.


10
Python 3 OrderedDicts non hanno un iteritems()metodo, quindi è necessario effettuare le seguenti operazioni al fine di ottenere il primo elemento: next(iter(d.items())).
Nathan Osman,

In Python 3 d.items()non sembra essere un iteratore, quindi iter davanti non sarà d'aiuto? Restituirà comunque l'elenco completo :(
chiede il

1
Aggiornamento: ho sbagliato, iter (d.items ()) ritorna odict_iteratore mi è stato confermato su IRC #python che questo non crea una copia dell'elenco.
chiede il

@ Nathan Osman, grazie per la spinta. Mi sono finalmente aggiornato su Python 3 di recente!
SteveWitham

14

È notevolmente più efficiente utilizzare IndexedOrderedDict dalindexed pacchetto.

Dopo il commento di Niklas, ho fatto un benchmark su OrderedDict e IndexedOrderedDict con 1000 voci.

In [1]: from numpy import *
In [2]: from indexed import IndexedOrderedDict
In [3]: id=IndexedOrderedDict(zip(arange(1000),random.random(1000)))
In [4]: timeit id.keys()[56]
1000000 loops, best of 3: 969 ns per loop

In [8]: from collections import OrderedDict
In [9]: od=OrderedDict(zip(arange(1000),random.random(1000)))
In [10]: timeit od.keys()[56]
10000 loops, best of 3: 104 µs per loop

IndexedOrderedDict è ~ 100 volte più veloce nell'indicizzazione di elementi in una posizione specifica in questo caso specifico.


Bello! Purtroppo non ancora ad Anaconda.
Konstantin,

1
@Konstantin Il nome effettivo del pacchetto è indexed.py . Prova a installare indexed.pyinvece di indexed.
Sven Haile,

9

Questa wiki della comunità tenta di raccogliere risposte esistenti.

Python 2.7

In Python 2, i keys(), values()e le items()funzioni delle OrderedDictliste di ritorno. Usando valuescome esempio, il modo più semplice è

d.values()[0]  # "python"
d.values()[1]  # "spam"

Per le grandi collezioni di cui si preoccupano solo di un unico indice, si può evitare di creare l'elenco completo utilizzando le versioni del generatore, iterkeys, itervaluese iteritems:

import itertools
next(itertools.islice(d.itervalues(), 0, 1))  # "python"
next(itertools.islice(d.itervalues(), 1, 2))  # "spam"

Il pacchetto indexed.py fornisce IndexedOrderedDict, che è progettato per questo caso d'uso e sarà l'opzione più veloce.

from indexed import IndexedOrderedDict
d = IndexedOrderedDict({'foo':'python','bar':'spam'})
d.values()[0]  # "python"
d.values()[1]  # "spam"

L'uso di itervalues ​​può essere notevolmente più veloce per dizionari di grandi dimensioni con accesso casuale:

$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 1000;   d = OrderedDict({i:i for i in range(size)})'  'i = randint(0, size-1); d.values()[i:i+1]'
1000 loops, best of 3: 259 usec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 10000;  d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i:i+1]'
100 loops, best of 3: 2.3 msec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 100000; d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i:i+1]'
10 loops, best of 3: 24.5 msec per loop

$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 1000;   d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
10000 loops, best of 3: 118 usec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 10000;  d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
1000 loops, best of 3: 1.26 msec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 100000; d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
100 loops, best of 3: 10.9 msec per loop

$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 1000;   d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.19 usec per loop
$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 10000;  d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.24 usec per loop
$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 100000; d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.61 usec per loop

+--------+-----------+----------------+---------+
|  size  | list (ms) | generator (ms) | indexed |
+--------+-----------+----------------+---------+
|   1000 | .259      | .118           | .00219  |
|  10000 | 2.3       | 1.26           | .00224  |
| 100000 | 24.5      | 10.9           | .00261  |
+--------+-----------+----------------+---------+

Python 3.6

Python 3 ha le stesse due opzioni di base (elenco vs generatore), ma i metodi dict restituiscono generatori per impostazione predefinita.

Metodo elenco:

list(d.values())[0]  # "python"
list(d.values())[1]  # "spam"

Metodo del generatore:

import itertools
next(itertools.islice(d.values(), 0, 1))  # "python"
next(itertools.islice(d.values(), 1, 2))  # "spam"

I dizionari Python 3 sono un ordine di grandezza più veloce di Python 2 e hanno velocità simili per l'uso dei generatori.

+--------+-----------+----------------+---------+
|  size  | list (ms) | generator (ms) | indexed |
+--------+-----------+----------------+---------+
|   1000 | .0316     | .0165          | .00262  |
|  10000 | .288      | .166           | .00294  |
| 100000 | 3.53      | 1.48           | .00332  |
+--------+-----------+----------------+---------+

7

È una nuova era e con i dizionari Python 3.6.1 ora mantengono il loro ordine. Queste semantiche non sono esplicite perché ciò richiederebbe l'approvazione BDFL. Ma Raymond Hettinger è la prossima cosa migliore (e più divertente) e lui fa un caso piuttosto forte che dizionari verrà ordinato per un tempo molto lungo.

Quindi ora è facile creare sezioni di un dizionario:

test_dict = {
                'first':  1,
                'second': 2,
                'third':  3,
                'fourth': 4
            }

list(test_dict.items())[:2]

Nota: la conservazione dell'ordine di inserimento Dictonary è ora ufficiale in Python 3.7 .


0

per OrderedDict () puoi accedere agli elementi indicizzando ottenendo le tuple delle coppie (chiave, valore) come segue o usando '.values ​​()'

>>> import collections
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'
>>> d.items()
[('foo', 'python'), ('bar', 'spam')]
>>>d.values()
odict_values(['python','spam'])
>>>list(d.values())
['python','spam']
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.