Acceso a elementos en una colección. Pedido ordenado por índice


142

Digamos que tengo el siguiente código:

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

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

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

Respuestas:


181

Si OrderedDict()es así, puede acceder fácilmente a los elementos indexando obteniendo las tuplas de pares (clave, valor) 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.itemsdevolvería un objeto de vista dict iterable en lugar de una lista. Necesitamos ajustar la llamada a una lista para hacer posible la indexación.

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

21
Tenga en cuenta que en 3.x el itemsmétodo devuelve un objeto de vista de diccionario interable en lugar de una lista, y no admite la división o indexación. Entonces primero deberías convertirlo en una lista. docs.python.org/3.3/library/stdtypes.html#dict-views
Peter DeGlopper

8
Copiar elementos, valores o claves en listas puede ser bastante lento para grandes dictonarios. Creé una reescritura de OrderedDict () con una estructura de datos interna diferente para aplicaciones que tienen que hacer esto muy a menudo: github.com/niklasf/indexed.py
Niklas

1
@PeterDeGlopper ¿cómo lo convierto en una lista?
Dejell el

1
@Dejel - usa el constructor:list(d.items())
Peter DeGlopper

9
Si solo accede a un elemento, puede evitar la sobrecarga de memoria list(d.items())utilizando next(islice(d.items(), 1))para obtener('bar', 'spam')
Quantum7

24

¿Tiene que usar un OrderedDict o desea específicamente un tipo de mapa que esté ordenado de alguna manera con indexación posicional rápida? Si es lo último, considere uno de los muchos tipos de dict ordenados de Python (que ordena los pares clave-valor en función del orden de clasificación de las claves). Algunas implementaciones también admiten 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'

1
También puede usar SortedDictcon una función clave para evitar comparaciones. Al igual que: SortedDict(lambda key: 0, ...). Las claves estarán sin clasificar pero permanecerán en un orden estable y son indexables.
GrantJ

19

Aquí hay un caso especial si desea la primera entrada (o cerca de ella) en un OrderedDict, sin crear una lista. (Esto se ha actualizado 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 primera vez que dices "siguiente ()", realmente significa "primero").

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

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


10
Python 3 OrderedDicts no tienen un iteritems()método, por lo que tendrá que hacer lo siguiente con el fin de obtener el primer punto: next(iter(d.items())).
Nathan Osman

En Python 3 d.items()no parece ser un iterador, por lo tanto, ¿por delante no ayudará? Todavía devolverá la lista completa :(
askol

1
Actualización: estaba equivocado, iter (d.items ()) regresa odict_iteratory me confirmaron en IRC #python que esto no hace una copia de la lista.
askol

@Nathan Osman, gracias por el empujón. ¡Finalmente me actualicé a Python 3 recientemente!
SteveWitham Duplicar el

14

Es dramáticamente más eficiente usar IndexedOrderedDict del indexedpaquete.

Siguiendo el comentario de Niklas, hice 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 elementos de indexación en una posición específica en este caso específico.


¡Agradable! Aún no en Anaconda por desgracia.
Konstantin

1
@ Konstantin El nombre real del paquete es indexed.py . Intenta instalar en indexed.pylugar de indexed.
Sven Haile

9

Este wiki comunitario intenta recopilar respuestas existentes.

Python 2.7

En Python 2, las keys(), values()y items()las funciones de OrderedDictlas listas de retorno. Usando valuescomo ejemplo, la forma más simple 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 del generador iterkeys, itervaluesy 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 generadores por defecto.

Método de la 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  |
+--------+-----------+----------------+---------+

7

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 segunda mejor opción (y más divertida) y argumenta que los diccionarios se ordenarán por mucho tiempo.

Así que ahora es fácil crear secciones 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 dictonario ahora es oficial en Python 3.7 .


0

para OrderedDict () puede acceder a los elementos indexando obteniendo las tuplas de pares (clave, valor) 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']
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.