是否扩展了一个字典列表,而不是迭代密钥?

时间:2018-04-06 21:22:40

标签: python performance

当帮助我的同事解决问题时,我看到了一些我不知道python的事情。与其他方式相比,我很好奇性能和时间复杂度的堆积,最好的方法是为了性能。

我的同事做了什么促使这个问题:

list_of_keys = []
test_dict = {'foo': 1, 'bar': [1, 2, 3, 4, 5]}
list_of_keys.extend(test_dict)

print(list_of keys)
  

[' foo',' bar']

与我见过的其他例子相比:

list_of_keys = []
test_dict = {'foo': 1, 'bar': [1, 2, 3, 4, 5]}
for i in test_dict.keys():
    list_of_keys.append(i)

keys = list(test_dict)

为了简单地附加键,这些中的哪一个被证明是最有益的和最pythonic的。哪一个产生最佳性能?

2 个答案:

答案 0 :(得分:3)

the docs解释时,s.extend(t)

  

使用s的内容扩展t(大部分内容与s[len(s):len(s)] = t相同)

好的,所以不清楚它是否应该比在循环中调用append更快或更慢。但它更快一点 - 循环发生在C而不是Python中,它可以使用一些特殊的优化代码添加到列表中,因为它知道你没有同时触摸列表。

更重要的是,它更简单,更易读,更容易出错。

至于从空列表开始然后扩展它(或附加到它),没有充分的理由这样做。如果您已经有一个包含某些值的列表,并且想要添加dict键,那么请使用extend。但是,如果您只想创建密钥列表,只需执行list(d)

至于d.keys()d,根本没有任何区别。无论是迭代dict还是dict_keys视图,您都会获得完全相同的迭代值,即使使用完全相同的dict_keyiterator也是如此。对keys()的额外调用确实使事情变得有点慢,但这是一个固定的成本,而不是每个元素一次,所以除非你的字母很小,否则你不会看到任何显着的差异。

那么,在这种情况下,哪一个看起来更具可读性。一般来说,你想要循环d.keys()的唯一原因是当你想要明确表示你正在迭代dict的键时,但是周围的代码d并不明显。是dict

除此之外,您还询问了复杂性。

所有这些解决方案都具有相同的(线性)复杂性,因为它们在封面下都做同样的事情:对于字典中的每个键,将其附加到列表的末尾。这是每个键的一步,并且每个步骤的复杂性是分摊的常量(因为Python列表以指数方式扩展),因此标题时间为O(N),其中N是字典的长度。

答案 1 :(得分:1)

在@thebjorn提到模块之后。似乎调用extend是最快的

为了便于阅读和清洁,list()似乎是最为pythonic的。

最有益的似乎取决于用例。但是,或多或少这样做是多余的,如评论中所述。这是从一个错误中发现的,我很好奇。

timeit.timeit("for i in {'foo': 1, 'bar': [1, 2, 3, 4, 5]}.keys():[].append(i)", number=1000000)
0.6147394659928977

timeit.timeit("[].extend({'foo': 1, 'bar': [1, 2, 3, 4, 5]})", number=1000000)
0.36140396299015265

timeit.timeit("list({'foo': 1, 'bar': [1, 2, 3, 4, 5]})", number=1000000)
0.4726199270080542