158 votes

Accéder aux éléments d'une collection.OrderedDict par index

Disons que j'ai le code suivant :

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

Existe-t-il un moyen d'accéder aux éléments de manière numérotée, par exemple :

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

208voto

Abhijit Points 24122

Si c'est un OrderedDict() vous pouvez facilement accéder aux éléments par indexation en obtenant les tuples de paires (clé,valeur) comme suit

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

Note pour Python 3.X

dict.items retournerait un itérable dict objet de vue plutôt qu'une liste. Nous devons envelopper l'appel dans une liste afin de rendre l'indexation possible.

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

0 votes

Bonne réponse. Les listes sont le TheRightWay(tm) pour effectuer des recherches indexées.

26 votes

Notez que dans la version 3.x, le items renvoie un objet de vue dictionnaire interchangeable plutôt qu'une liste, et ne supporte pas le découpage en tranches ou l'indexation. Il faut donc d'abord le transformer en liste. docs.python.org/3.3/library/stdtypes.html#dict-views

11 votes

La copie d'éléments, de valeurs ou de clés dans des listes peut être assez lente pour les gros dictons. J'ai créé une réécriture de OrderedDict() avec une structure de données interne différente pour les applications qui doivent faire cela très souvent : github.com/niklasf/indexed.py

25voto

GrantJ Points 888

Devez-vous utiliser un OrderedDict ou voulez-vous spécifiquement un type de type map qui soit ordonné d'une manière ou d'une autre avec une indexation positionnelle rapide ? Dans ce dernier cas, envisagez l'un des nombreux types de dict triés de Python (qui ordonne les paires clé-valeur en fonction de l'ordre de tri des clés). Certaines implémentations prennent également en charge l'indexation rapide. Par exemple, le type conteneurs triés Le projet a un SortedDict à cette fin.

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

21voto

Voici un cas particulier si vous voulez que le premièrement (ou proche de celle-ci) dans un OrderedDict, sans créer de liste. (Ceci a été mis à jour pour 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 première fois que vous dites "next()", cela signifie en réalité "first").

Dans mon test informel, next(iter(d.items())) avec un petit OrderedDict est seulement un tout petit peu plus rapide que items()[0] . Avec un OrderedDict de 10 000 entrées, next(iter(d.items())) était environ 200 fois plus rapide que items()[0] .

MAIS si vous enregistrez la liste items() une fois et que vous l'utilisez souvent, cela pourrait être plus rapide. Ou si vous créez un itérateur items() et que vous le parcourez jusqu'à la position souhaitée, cela peut être plus lent.

19voto

刘金国 Points 537

Il est nettement plus efficace d'utiliser IndexedOrderedDict de la indexed paquet.

Suite au commentaire de Niklas, j'ai effectué un benchmark sur OrderedDict y IndexedOrderedDict avec 1000 entrées.

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 est ~100 fois plus rapide pour indexer les éléments à une position spécifique dans ce cas précis.

Les autres solutions énumérées nécessitent une étape supplémentaire. IndexedOrderedDict est un remplacement direct de OrderedDict sauf que c'est indexable.

10voto

Quantum7 Points 615

Ce wiki communautaire tente de rassembler les réponses existantes.

Python 2.7

Dans python 2, le keys() , values() et items() fonctions de OrderedDict renvoyer des listes. Utilisation de values à titre d'exemple, la méthode la plus simple est la suivante

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

Pour les grandes collections où vous ne vous souciez que d'un seul index, vous pouvez éviter de créer la liste complète en utilisant les versions du générateur, iterkeys , itervalues y iteritems :

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

Le site indexé.py Le paquet fournit IndexedOrderedDict qui est conçu pour ce cas d'utilisation et sera l'option la plus rapide.

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

L'utilisation des itervalues peut être considérablement plus rapide pour les grands dictionnaires à accès aléatoire :

$ 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 a les mêmes deux options de base (liste vs générateur), mais les méthodes dict renvoient des générateurs par défaut.

Méthode de la liste :

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

Méthode du générateur :

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

Les dictionnaires de Python 3 sont un ordre de grandeur plus rapide que ceux de Python 2 et ont des accélérations similaires pour l'utilisation de générateurs.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X