访问集合中的项

假设我有以下代码:

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

有没有办法我可以用编号的方式访问这些项目,比如:

d(0) #foo's Output
d(1) #bar's Output
230508 次浏览

如果它是一个 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')

Python3.X 的注意事项

dict.items将返回一个 可迭代的 dict 视图对象而不是一个列表。为了使索引成为可能,我们需要将调用包装到一个列表中

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

您必须使用 OrderedDect 还是特别想要一个类似于地图的类型,它以某种方式排序,并具有快速的位置索引?如果是后者,那么考虑 Python 的许多排序 dict 类型之一(它根据键排序顺序排列键-值对)。一些实现还支持快速索引。例如,分类容器项目就有一个 分类词典类型用于此目的。

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

如果您希望 OrderedDect 中有 第一条目(或接近它) ,而不需要创建列表,那么这里有一个特例。(已更新为 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'

(当你第一次说“ next ()”时,它的意思是“第一次”。)

在我的非正式测试中,带有一个小 OrderedDect 的 next(iter(d.items()))只比 items()[0]快一点点。OrderedDect 有10,000个条目,next(iter(d.items()))items()[0]快200倍。

但是 如果您保存了 item ()列表一次,然后大量使用该列表,那么可能会更快。或者如果您重复{创建一个 item ()迭代器并逐步通过它到达您想要的位置} ,那可能会慢一些。

indexed包中使用 IndexedOrderedDect效率显著提高。

根据尼克拉斯的评论,我已经做了一个基准的 命令IndexedOrderedDect与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

IndexedOrderedDect 在此特定情况下,在特定位置对元素进行索引的速度要快约100倍。

上面列出的其他解决方案需要额外的步骤。

这是一个新的时代,Python 3.6.1的字典现在保持了它们的顺序。这些语义并不明确,因为这需要 BDFL 的批准。但是雷蒙德 · 海廷格(Raymond Hettinger)是仅次于他的最好的作品(也更有趣) ,他制作了一个 很有力的证据,字典的订购时间将会非常长。

所以现在很容易创建字典的片段:

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


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

注: 字典插入顺序保存现在是 Python 3.7的官方版本

这个社区 wiki 试图收集现有的答案。

Python 2.7

在 python2中,OrderedDict返回列表的 keys()values()items()函数。以 values为例,最简单的方法是

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

对于只关心单个索引的大型集合,可以避免使用生成器版本 iterkeysitervaluesiteritems创建完整列表:

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

Python3具有相同的两个基本选项(列表与生成器) ,但在默认情况下,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"

Python3字典比 python2快一数量级,在使用生成器方面也有类似的加速。

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

对于 OrderedDect () ,您可以通过索引来访问元素,方法是获取(key,value)对的元组,如下所示或使用’。价值()’

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

如果您正在处理预先知道的 固定键数,那么应该使用 Python 的内置 命名元组。一个可能的用例是当您想要存储一些常量数据并通过索引和指定键在整个程序中访问它时。

import collections
ordered_keys = ['foo', 'bar']
D = collections.namedtuple('D', ordered_keys)
d = D(foo='python', bar='spam')

索引访问:

d[0] # result: python
d[1] # result: spam

通过指定键进行访问:

d.foo # result: python
d.bar # result: spam

或者更好:

getattr(d, 'foo') # result: python
getattr(d, 'bar') # result: spam

如果您已经安装了 pandas,您可以将所订购的字典转换为熊猫 Series。这将允许随机访问字典元素。

>>> import collections
>>> import pandas as pd
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'


>>> s = pd.Series(d)


>>> s['bar']
spam
>>> s.iloc[1]
spam
>>> s.index[1]
bar