Accediendo a los elementos en una colección.OrdenedDict por índice

Digamos que tengo el siguiente código:

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

¿Hay alguna forma de acceder a los elementos de forma numerada, como:

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

Si es un OrderedDict() , puede acceder fácilmente a los elementos al indexar obteniendo las tuplas de (clave, valor) pares de la siguiente manera

 >>> 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 para Python 3.X

dict.items devolvería un objeto de vista de dict iterable en lugar de una lista. Necesitamos incluir la llamada en una lista para que la indexación sea posible

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

¿Tiene que usar un OrderedDict o desea específicamente un tipo de mapa que esté ordenado de alguna manera con una rápida indexación de posición? Si es lo último, entonces considere uno de los muchos tipos de dict ordenados de Python (que ordena pares clave-valor en función del orden de clave). Algunas implementaciones también admiten la indexación rápida. Por ejemplo, el proyecto sortedcontainers tiene un tipo SortedDict solo para este propósito.

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

Este es un caso especial si desea la primera entrada (o cerca de ella) en un OrderedDict, sin crear una lista:

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

(La primera vez que dices “siguiente ()”, realmente significa “primero”).

En mi prueba informal en Python 2.7, iteritems().next() con un pequeño OrderedDict es solo un poquito más rápido que los items()[0] . Con un OrderedDict de 10,000 entradas, iteritems().next() fue 200 veces más rápido que los items()[0] .

PERO si guarda la lista de elementos () una vez y luego la usa mucho, podría ser más rápido. O si repetidamente {crea un iterador iteritems () y avanza hasta la posición que desea}, esto podría ser más lento.

Es mucho más eficiente usar IndexedOrderedDict del paquete indexed .

Siguiendo el comentario de Niklas, he hecho un punto de referencia en OrderedDict e IndexedOrderedDict con 1000 entradas.

 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 es ~ 100 veces más rápido en la indexación de elementos en una posición específica en este caso específico.

Esta wiki de la comunidad intenta recostackr las respuestas existentes.

Python 2.7

En Python 2, las keys() , los values() y los items() funcionan en las listas de devolución de OrderedDict . Usando values como ejemplo, la forma más sencilla es

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

Para colecciones grandes donde solo le importa un índice único, puede evitar crear la lista completa utilizando las versiones de generador, iterkeys , itervalues e iteritems :

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

El paquete indexed.py proporciona IndexedOrderedDict , que está diseñado para este caso de uso y será la opción más rápida.

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

El uso de itervalues ​​puede ser considerablemente más rápido para diccionarios grandes con acceso aleatorio:

 $ 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 tiene las mismas dos opciones básicas (lista vs generador), pero los métodos dict devuelven los generadores de forma predeterminada.

Método de lista:

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

Método generador:

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

Los diccionarios de Python 3 son un orden de magnitud más rápido que python 2 y tienen aceleraciones similares para usar generadores.

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

Es una nueva era y con los diccionarios Python 3.6.1 ahora conservan su orden. Estas semánticas no son explícitas porque eso requeriría la aprobación de BDFL. Pero Raymond Hettinger es la mejor cosa siguiente (y más divertida) y cree que los diccionarios se ordenarán durante mucho tiempo.

Así que ahora es fácil crear porciones de un diccionario:

 test_dict = { 'first': 1, 'second': 2, 'third': 3, 'fourth': 4 } list(test_dict.items())[:2] 

Nota: la preservación del orden de inserción es ahora oficial en Python 3.7 .

para OrderedDict () puede acceder a los elementos al indexar obteniendo las tuplas de (clave, valor) pares de la siguiente manera 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']