ordereddict python 3
Accediendo a los artículos en un orderdict (6)
¿Tiene que usar un OrderedDict o desea específicamente un tipo de mapa ordenado de alguna manera con indexación posicional rápida? En este último caso, considere uno de los muchos tipos de dictados ordenados de Python (que ordena pares clave-valor basados en el orden de clasificación de claves). Algunas implementaciones también admiten indexación rápida. Por ejemplo, el proyecto sortedcontainers tiene un tipo SortedDict 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''
Digamos que tengo el siguiente código:
import collections
d = collections.OrderedDict()
d[''foo''] = ''python''
d[''bar''] = ''spam''
¿Hay alguna manera de que pueda acceder a los elementos de una manera numerada, como:
d(0) #foo''s Output
d(1) #bar''s Output
Aquí hay un caso especial si quiere 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 "next ()", realmente significa "primero").
En mi prueba informal en Python 2.7, iteritems().next()
con un pequeño OrderedDict es solo un poco más rápido que items()[0]
. Con un OrderedDict de 10,000 entradas, iteritems().next()
era 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, eso podría ser más rápido. O si repetidamente {crea un iterador iteritems () y lo desplaza a la posición que desea}, podría ser más lento.
Es mucho más eficiente usar IndexedOrderedDict .
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 los elementos de indexación en una posición específica en este caso específico.
Es una nueva era y con Python 3.6.1, los diccionarios conservan su orden. Esta semántica no es explícita porque requeriría la aprobación de BDFL. Pero Raymond Hettinger es la mejor opción (y más divertida) y defiende 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]
Si es un OrderedDict()
, puede acceder fácilmente a los elementos indexando obteniendo las tuplas de los 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.items
devolvería un objeto dict.items
dict view en lugar de una lista. Necesitamos incluir la llamada en 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'')
Esta wiki de la comunidad intenta recopilar respuestas existentes.
Python 2.7
En python 2, las funciones keys()
, values()
y items()
de OrderedDict
devuelven listas. Usando values
como ejemplo, la forma más simple es
d.values()[0] # "python"
d.values()[1] # "spam"
Para colecciones grandes donde solo le importa un solo índice, puede evitar crear la lista completa usando las versiones del 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 pypi.python.org/pypi/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 estos servicios puede ser considerablemente más rápido para los 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 lista:
list(d.values())[0] # "python"
list(d.values())[1] # "spam"
Método del generador:
import itertools
next(itertools.islice(d.values(), 0, 1)) # "python"
next(itertools.islice(d.values(), 1, 2)) # "spam"
Los diccionarios Python 3 son un orden de magnitud más rápidos que los de 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 |
+--------+-----------+----------------+---------+