Доступ к элементам в ordereddict

допустим у меня есть следующий код:

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

есть ли способ получить доступ к элементам в нумерованном виде, например:

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

6 ответов


если OrderedDict() вы можете легко получить доступ к элементам по индексу, получая кортежей (ключ,значение) пар следующим образом

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

Примечание для Python 3.X

dict.items вернет iterable dict view object вместо списка. Нам нужно обернуть вызов в список, чтобы сделать индексирование возможным

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

вам нужно использовать OrderedDict или вы специально хотите тип карты, который упорядочен каким-то образом с быстрой позиционной индексацией? Если последнее, то рассмотрим один из многих отсортированных типов dict Python (который упорядочивает пары ключ-значение на основе порядка сортировки ключей). Некоторые реализации также поддерживают быструю индексацию. Например,sortedcontainers есть SortedDict тип только для этой цели.

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

здесь особый случай, если вы хотите первый запись (или близко к ней) в OrderedDict, без создания списка:

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

(первый раз, когда вы говорите " next ()", это действительно означает " первый.")

в моем неофициальном тесте на Python 2.7,iteritems().next() с небольшим OrderedDict только немного быстрее, чем items()[0]. С OrderedDict 10,000 записей,iteritems().next() был примерно в 200 раз быстрее, чем items()[0].

но если вы сохраните элементы () список один раз, а затем использовать список много, что может быть быстрее. Или если вы неоднократно { создаете итератор iteritems() и проходите через него до нужной позиции }, это может быть медленнее.


значительно эффективнее использовать IndexedOrderedDict.

следуя комментарию Никласа, я сделал тест на OrderedDict и IndexedOrderedDict С 1000 записей.

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 раз быстрее в индексировании элементов в определенном положении в этом конкретном случае.


это сообщество wiki пытается собрать существующие ответы.

Python 2.7

в python 2,keys(), values() и items() функции OrderedDict списки возвращения. Используя values в качестве примера, самый простой способ-это

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

для больших коллекций, где вы заботитесь только об одном индексе, вы можете избежать создания полного списка с помощью версий генератора,iterkeys, itervalues и iteritems:

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

indexed.py пакет обеспечивает IndexedOrderedDict, который предназначен для этого случая использования и будет самым быстрым вариантом.

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

использование itervalues может быть значительно быстрее для больших словарей со случайным доступом:

$ 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 имеет те же два основных параметра (list vs generator), но методы dict возвращают генераторы по умолчанию.

список метод:

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

способ генератор:

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

словари Python 3 на порядок быстрее, чем python 2, и имеют аналогичные ускорения для использования генераторов.

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

это новая эра, и словари Python 3.6.1 теперь сохраняют свой порядок. Эти семантики не являются явными, потому что для этого потребуется утверждение BDFL. Но Раймонд Хеттингер-следующая лучшая вещь (и смешнее), и он делает довольно весомые аргументы словари, которые будут заказаны в течение очень долгого времени.

Итак, теперь легко создавать фрагменты словаря:

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

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