在 Python 3.6+ 中高效地按位置访问字典项目

Accessing dictionary items by position in Python 3.6+ efficiently

我理解字典是 ,作为 3.6 中的实现细节和 3.7+ 中的官方。

鉴于它们是有序的,似乎很奇怪没有方法可以按插入顺序检索字典的 ith 项。可用的 only solutions 似乎具有 O(n) 复杂性,或者:

  1. 通过 O(n) 过程转换为列表,然后使用 list.__getitem__.
  2. enumerate 循环中的字典项和 return 达到所需索引时的值。同样,时间复杂度为 O(n)。

由于从 list 中获取一个项目具有 O(1) 的复杂度,有没有办法用字典实现相同的复杂度?使用常规 dictcollections.OrderedDict 都可以。

如果不可能,是否存在阻止这种方法的结构性原因,或者这只是尚未考虑/实施的功能?

对于 OrderedDict 它本质上是 O(n) 因为排序记录在 linked list.

对于内置的dict,有一个向量(一个连续的数组)而不是一个链表,但最后几乎是一样的东西:向量包含一些"dummies",特殊的内部值这意味着 "no key has been stored here yet" 或 "a key used to be stored here but no longer"。这使得,例如,删除一个密钥非常便宜(只需用一个虚拟值覆盖密钥)。

但是如果不在其之上添加辅助数据结构,就无法跳过假人而不一次一个地跳过它们。因为 Python 使用一种开放寻址的形式来解决冲突,并将负载因子保持在 2/3 以下,所以向量的条目中至少有三分之一 虚拟对象。 the_vector[i] 可以在 O(1) 时间内访问,但实际上与第 i 个非虚拟条目没有可预测的关系。

根据 ,结构性原因导致您无法在 O(1) 时间内按位置访问字典项目。

如果您正在寻找按键 位置的 O(1) 查找,则值得考虑替代方案。 NumPy / Pandas 等第 3 方库提供此类功能,高效 尤其是 对于不需要指针的数字数组。

使用 Pandas,您可以构建一个带有唯一标签的 "dictionary-like" 系列,通过 "label" 或位置提供 O(1) 查找。你牺牲的是删除标签时的性能,这会产生 O(n) 成本,很像 list.

import pandas as pd

s = pd.Series(list(range(n)))

# O(n) item deletion
del s[i]
s.drop(i)
s.pop(i)

# O(1) lookup by label
s.loc[i]
s.at[i]
s.get(i)
s[i]

# O(1) lookup by position
s.iloc[i]
s.iat[i]

pd.Series 绝不是 dict 的直接替代品。例如,如果该系列主要用作映射,则不会阻止重复键,并且会导致问题。但是,如果数据存储在连续的内存块中,如上例所示,您可能会看到显着的性能改进。

另请参阅:

  1. What are the advantages of NumPy over regular Python lists?.
  2. What is the performance impact of non-unique indexes in pandas?