【问题标题】:what is difference on iterating differently in python dictionaries?在 python 字典中以不同方式迭代有什么区别?
【发布时间】:2014-11-27 05:21:45
【问题描述】:

我有一本像下面这样的字典

G = {0: {1: 1, 4: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 3: 1}, 3: {2: 1, 4: 1}, 4: {0: 1, 3: 1}}

我可以通过两种方式迭代它们的键:

for key in G:
    do stuff

也喜欢

for key in G.keys():
    do stuff

哪一种更好&pythonic的方式? 还有哪个在时间复杂度上更好?

【问题讨论】:

  • 练习:您可以通过key in some_dictkey in some_dict.keys() 检查一个键是否在字典中。 1)它们在渐近复杂度上是否等效? 2)比较python2和python3中(1)的答案。

标签: python python-2.7 optimization iteration time-complexity


【解决方案1】:

for key in G 是最有效的方法,这两种方法都是O(N),但for key in G 避免了不必要地构建列表,就像您使用G.keys() 和额外的函数调用一样。

In [3]: G = {0: {1: 1, 4: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 3: 1}, 3: {2: 1, 4: 1}, 4: {0: 1, 3: 1}}

In [4]: %%timeit 
   ...: for k in G:
   ...:    pass
   ...: 
10000000 loops, best of 3: 171 ns per loop

In [5]: %%timeit
   ...: for k in G.keys():
   ...:     pass
   ...: 
1000000 loops, best of 3: 235 ns per loop

【讨论】:

  • 这只适用于 Python 2.x - 在 Python 3.x 中,keys() 返回一个迭代器。
  • @isedev 问题被标记为 2.7,而在 python 3 中,您仍在使用不必要的函数调用
  • @isedev:这仍然是一个额外的方法调用,以及一个新对象(一个字典视图)的创建。
  • 然而,它不是 O(1)。仍然是 O(N),但固定成本更低。
  • @MartijnPieters,是的,读错了问题是想如果if k in d
【解决方案2】:

两者的顺序大约是相同的 O(N) 但在第二个你必须花一点时间提取 keys

为了更好地理解两者的运行时间:

def test1():    
   G = {0: {1: 1, 4: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 3: 1}, 3: {2: 1, 4: 1}, 4: {0: 1, 3: 1}}
   for key in G:
    key+=1

def test2():    
   G = {0: {1: 1, 4: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 3: 1}, 3: {2: 1, 4: 1}, 4: {0: 1, 3: 1}}
   for key in G.keys():
    key+=1


if __name__ == '__main__':
    import timeit
    print 'test 1 : ',timeit.timeit("test1()", setup="from __main__ import test1")
    print 'test 2 : ',timeit.timeit("test2()", setup="from __main__ import test2")

结果:

test 1 :  0.934647083282
test 2 :  1.02073597908

【讨论】:

  • Python 本身的启动和关闭时间太长,无法正确显示差异。请改用timeit 模块来显示性能差异。
  • 是的,当然,使用time 命令是我的习惯!我将编辑答案!
【解决方案3】:

最常见的是for key in G:,具有上述所有好处。

如果您在循环中操作字典,for key in G.keys(): 很有用,而更有效的第一个变体禁止这样做。通过对字典的操作,我的意思是显式更改字典键(添加/删除项目)!这样做会导致"RuntimeError: dictionary changed size during iteration"。但是您也可以在第一个变体中更改键的关联值,因为它不会更改字典(-length),而是更改键的关联值。

所以没有真正的“更好”,只有两个用例!通常你会使用第一个变体,但如果你想遍历字典并操作它,你会使用第二个。

编辑: 正如 Bakuriu 在 python3 中指出的那样,您必须手动将 G.keys() 转换为列表。我的示例适用于标记版本 2.7。

【讨论】:

  • 您不能在迭代集合时修改它!下一个项目是什么?使用 dict.keys() 您可以遍历键列表,并可以添加或删除字典的元素。
  • 警告:遍历keys() 并修改字典仅适用于python2。在 python3 中,keys() 是一个视图,因此您必须手动将其转换为 list 才能使其正常工作。 @PadraicCunningham 尝试类似:for i, key in enumerate(some_dict): if i % 2 == 0: del some_dict[key]。这里的“修改”是指字典通过添加或删除元素来改变其大小。重新映射同一个键不成问题。
  • @PadraicCunningham:如果添加或删除键,您将收到“RuntimeError: dictionary changed size during iteration”。
  • @PadraicCunningham:你想要什么?也许我不够明确,但您唯一可以更改的是值。您可能不会更改键(的值),因为这意味着添加一个新项目,这会改变大小!我现在明确指出这一点是为了避免歧义。
猜你喜欢
  • 1970-01-01
  • 2011-10-30
  • 2014-06-27
  • 1970-01-01
  • 2011-11-22
  • 2018-01-25
  • 2011-06-15
  • 2014-01-26
  • 1970-01-01
相关资源
最近更新 更多