Допустим, у меня есть следующий код:
import collections
d = collections.OrderedDict()
d['foo'] = 'python'
d['bar'] = 'spam'
Есть ли способ получить доступ к элементам пронумерованным образом, например:
d(0) #foo Output
d(1) #bar Output
Допустим, у меня есть следующий код:
import collections
d = collections.OrderedDict()
d['foo'] = 'python'
d['bar'] = 'spam'
Есть ли способ получить доступ к элементам пронумерованным образом, например:
d(0) #foo Output
d(1) #bar Output
Если его a 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
вернет объект итерируемый объект диктовки вместо списка. Нам нужно перевести вызов в список, чтобы сделать возможной индексацию
>>> items = list(d.items())
>>> items
[('foo', 'python'), ('bar', 'spam')]
>>> items[0]
('foo', 'python')
>>> items[1]
('bar', 'spam')
Нужно ли вам использовать OrderedDict или вам нужен именно такой тип карты, который каким-то образом упорядочен с быстрой позиционной индексацией? Если последний, то рассмотрите один из Python много отсортированных типов dict (который заказывает пары ключ-значение на основе порядка сортировки ключей). Некоторые реализации также поддерживают быструю индексацию. Например, проект 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]
. При заказе Dict из 10 000 записей iteritems().next()
был примерно в 200 раз быстрее, чем items()[0]
.
НО, если вы сохраните список items() один раз, а затем используйте список, который может быть быстрее. Или если вы повторно {создаете итератор iteritems() и переходите к нему в нужную позицию}, это может быть медленнее.
Значительно эффективнее использовать IndexedOrderedDict из indexed
пакета.
Следуя комментариям Никласа, я провел эталонный тест для 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 раз быстрее индексирует элементы в определенной позиции в этом конкретном случае.
Вики сообщества пытается собрать ответы.
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"
Использование больших значений может быть значительно быстрее для больших словарей с произвольным доступом:
$ 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 имеет те же две основные опции (список против генератора), но методы 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]
Примечание. Диктонарное сохранение порядка вставки теперь официально в Python 3.7.
для OrderedDict() вы можете получить доступ к элементам путем индексации, получив кортежи пар (ключ, значение) следующим образом или используя '.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']