从大词典中弹出N个项目的最快方法

时间:2019-03-16 17:00:33

标签: python python-3.x performance dictionary optimization

我有一个大的字典class UsersController < ApplicationController def new role, model = request.path.split("/")[1, 2] # ["", "admin", "user"] @user = model.classify.constantize.new(role: role) end end (最多1百万个项目),我想取N(典型值为N = 10K-20K)项,并将它们存储在新的字典src中并将其余的项目仅保留在dst中。拿走N个项目都没关系。我正在寻找在Python 3.6或3.7上实现此目标的最快方法。

到目前为止我发现的最快方法:

src

还有什么更好的吗?即使是微不足道的收益也不错。

3 个答案:

答案 0 :(得分:19)

dict内部进行简单的理解即可:

dict(src.popitem() for _ in range(20000))

这里有timing tests

setup = """
src = {i: i ** 3 for i in range(1000000)}

def method_1(d):
  dst = {}
  while len(dst) < 20000:
      item = d.popitem()
      dst[item[0]] = item[1]
  return dst

def method_2(d):
  return dict(d.popitem() for _ in range(20000))
"""
import timeit
print("Method 1: ", timeit.timeit('method_1(src)', setup=setup, number=1))

print("Method 2: ", timeit.timeit('method_2(src)', setup=setup, number=1))

结果:

Method 1:  0.007701821999944514
Method 2:  0.004668198998842854

答案 1 :(得分:9)

这还快一点:

from itertools import islice
def method_4(d):
    result = dict(islice(d.items(), 20000))
    for k in result: del d[k]
    return result

与其他版本相比,使用Netwave的测试用例:

Method 1:  0.004459443036466837  # original
Method 2:  0.0034434819826856256 # Netwave
Method 3:  0.002602717955596745  # chepner
Method 4:  0.001974945073015988  # this answer

额外的提速似乎来自避免C和Python函数之间的转换。从反汇编中,我们可以注意到dict实例化发生在C端,只有3个来自Python的函数调用。循环使用DELETE_SUBSCR操作码而不需要函数调用:

>>> dis.dis(method_4)
  2           0 LOAD_GLOBAL              0 (dict)
              2 LOAD_GLOBAL              1 (islice)
              4 LOAD_FAST                0 (d)
              6 LOAD_ATTR                2 (items)
              8 CALL_FUNCTION            0
             10 LOAD_CONST               1 (20000)
             12 CALL_FUNCTION            2
             14 CALL_FUNCTION            1
             16 STORE_FAST               1 (result)

  3          18 SETUP_LOOP              18 (to 38)
             20 LOAD_FAST                1 (result)
             22 GET_ITER
        >>   24 FOR_ITER                10 (to 36)
             26 STORE_FAST               2 (k)
             28 LOAD_FAST                0 (d)
             30 LOAD_FAST                2 (k)
             32 DELETE_SUBSCR
             34 JUMP_ABSOLUTE           24
        >>   36 POP_BLOCK

  4     >>   38 LOAD_FAST                1 (result)
             40 RETURN_VALUE

method_2中的迭代器相比:

>>> dis.dis(d.popitem() for _ in range(20000))
  1           0 LOAD_FAST                0 (.0)
        >>    2 FOR_ITER                14 (to 18)
              4 STORE_FAST               1 (_)
              6 LOAD_GLOBAL              0 (d)
              8 LOAD_ATTR                1 (popitem)
             10 CALL_FUNCTION            0
             12 YIELD_VALUE
             14 POP_TOP
             16 JUMP_ABSOLUTE            2
        >>   18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

每个项目都需要一个Python到C函数的调用。

答案 2 :(得分:3)

我发现使用字典理解可以更快地(-10%的速度)使用range来产生并解压缩键和值的循环

dst = {key:value for key,value in (src.popitem() for _ in range(20000))}

在我的机器上:

your code: 0.00899505615234375
my code:   0.007996797561645508

大约快了12%,还不错,但不如Netwave simpler answer那样开箱

如果您要在流程中转换键或值,则此方法很有用。