Python字典popitem无限循环潜力

时间:2013-12-06 22:45:48

标签: python dictionary

是否可以采用以下形式的算法:

while dict:
  k, v = dict.popitem()
  ... #arbitrary program statements
  ...
  if ...: #reinsert key in some cases
    dict[k] = v

无限循环?换句话说,字典/ popitem的实现是否保证如果我弹出一个项目,决定它应该重新插入,并重新执行循环,那么同样的项目将再次弹出?

2 个答案:

答案 0 :(得分:2)

当然,这是可能的。但请注意,这取决于精确的实现 python版本。

dict.popitem()将在字典哈希表中弹出第一个键值对。由于哈希冲突,重新插入相同的密钥并不能保证它会再次出现在同一个槽中。

使用整数键可以在CPython 2.7中轻松演示:

>>> d = {1: 1, 7: 7}
>>> d.popitem()
(1, 1)
>>> d[1] = 1
>>> d.popitem()
(7, 7)

在CPython中(对于3.3之前的版本),一个小字典从哈希表中的几个插槽开始(随着字典增长,动态创建更多空间),17发生哈希到同一个插槽。

我第一次从字典中弹出一个项目时,返回了1,但重新插入1现在将7作为第一个键,而是返回它。

(Python 3.3引入了随机哈希种子,使得哈希冲突无法预测)。

但是,当然,如果只是因为它是字典中的最后一个键,完全可能会一次又一次地插入和删除一个键:

>>> d = {1: 1}
>>> d.popitem()
(1, 1)
>>> d[1] = 1
>>> d.popitem()
(1, 1)

你无法轻易预测这将会发生。

请注意,无论接下来弹出什么键,如果您继续将键重新插入字典,您的数据集都不会缩小,最终会以无限循环结束。如果该循环每次处理一个键或重复一系列键都无关紧要。

也许您正在寻找队列?有一个有用且高效的collections.deque() double-ended queue实现可用:

from collections import deque

to_process = deque(d.items())  # initialize with key-values
while to_process:
    k, v = to_process.popleft()

    # do work with item

    if somecondition:
        to_process.append((k, v))

你仍然可以得到一个无限循环(例如,如果你的if语句总是重新插入相同的键值对),但至少你可以保证你第一次看到在开始循环之前,所有键值对。

答案 1 :(得分:-2)

我认为行为未定义。相反,迭代copy.deepcopy(dict _)。

EG:

for key, value in copy.deepcopy(dict_).items():
    do_something_with_dict(dict_)

# do_something_with_dict can change keys or values however it wants, without worrying
# that the for loop will get messed up