通过索引访问 collections.OrderedDict 中的项目

Posted

技术标签:

【中文标题】通过索引访问 collections.OrderedDict 中的项目【英文标题】:Accessing items in an collections.OrderedDict by index 【发布时间】:2012-04-20 22:28:42 【问题描述】:

假设我有以下代码:

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

有没有办法以编号方式访问项目,例如:

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

【问题讨论】:

只有值才做od.value(),只有键做od.keys(),两者的元组都做od.items() 【参考方案1】:

使用indexed 包中的IndexedOrderedDict 效率显着提高。

根据 Niklas 的评论,我对 OrderedDictIndexedOrderedDict 进行了 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 倍。

列出的其他解决方案需要额外的步骤。 IndexedOrderedDictOrderedDict 的直接替代品,但它是可索引的。

【讨论】:

不错!不幸的是,还没有在 Anaconda 中。 @Konstantin 包的实际名称是 indexed.py。尝试安装indexed.py 而不是indexed 链接已损坏。在PyPI 上调用indexed【参考方案2】:

如果您安装了pandas,您可以将有序字典转换为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

【讨论】:

【参考方案3】:

如果您要处理事先知道的固定数量的键,请改用 Python 的内置 namedtuples。一个可能的用例是当您想要存储一些常量数据并通过索引和指定键在整个程序中访问它时。

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

【讨论】:

【参考方案4】:

如果您希望在 OrderedDict 中的 first 条目(或接近它)而不创建列表,这是一种特殊情况。 (这已更新到 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()”时,真正的意思是“第一”)

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

但是如果您保存 items() 列表一次然后经常使用该列表,那可能会更快。或者,如果您反复创建一个 items() 迭代器并逐步将其定位到您想要的位置,这可能会更慢。

【讨论】:

Python 3 OrderedDicts 没有iteritems() 方法,因此您需要执行以下操作才能获得第一项:next(iter(d.items())) 在 Python 3 中 d.items() 好像不是迭代器,所以前面的 iter 不会有帮助吧?它仍然会返回完整的列表:( 更新:我错了,iter(d.items()) 返回 odict_iterator 并在 IRC #python 上向我确认这不会复制列表。 @Nathan Osman,感谢您的推动。最近我终于更新到 Python 3 了!【参考方案5】:

对于 OrderedDict(),您可以通过按如下方式获取 (key,value) 对的元组或使用 '.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']

【讨论】:

【参考方案6】:

这是一个新时代,Python 3.6.1 字典现在保留了它们的顺序。这些语义并不明确,因为这需要 BDFL 批准。但 Raymond Hettinger 是第二好的(而且更有趣),他创建了一个 pretty strong case,表示字典将被订购很长时间。

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

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

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

注意:字典插入顺序保存现在是official in Python 3.7。

【讨论】:

【参考方案7】:

此社区 wiki 试图收集现有答案。

Python 2.7

在 python 2 中,OrderedDictkeys()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

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

【讨论】:

【参考方案8】:

如果它是OrderedDict(),您可以通过如下方式获取 (key,value) 对的元组通过索引轻松访问元素

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

【讨论】:

请注意,在 3.x 中,items 方法返回可交互的字典视图对象而不是列表,并且不支持切片或索引。所以你必须先把它变成一个列表。 docs.python.org/3.3/library/stdtypes.html#dict-views 对于大型字典来说,将项目、值或键复制到列表中可能会很慢。我为必须经常这样做的应用程序创建了一个具有不同内部数据结构的 OrderedDict() 重写:github.com/niklasf/indexed.py @PeterDeGlopper 我如何把它变成一个列表? @Dejel - 使用构造函数:list(d.items()) 如果只访问一项,可以避免list(d.items())的内存开销,使用next(islice(d.items(), 1))获取('bar', 'spam')【参考方案9】:

您是否必须使用 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'

【讨论】:

您也可以将SortedDict 与键功能一起使用以避免比较。喜欢:SortedDict(lambda key: 0, ...)。键随后将未排序,但将保持稳定的顺序并且可索引。 我只想要列表/可迭代的值。如何做到这一点?好的,一个是od.values(),只有值是od.value(),只有键是od.keys(),两者的元组是od.items()

以上是关于通过索引访问 collections.OrderedDict 中的项目的主要内容,如果未能解决你的问题,请参考以下文章

通过索引对类模板成员的编译时访问

Oracle数据访问和索引的使用

通过 Children[] 索引访问边界内的元素

通过索引访问 collections.OrderedDict 中的项目

通过数字索引访问 Dictionary.Keys 键

如何通过索引 C# 访问对象 [关闭]