我有一个大的dict src(最多1M个项目),我想取N个(典型值为N=10K-20K)项目,将它们存储在一个新的dict dst中,只保留src中剩余的项目.取哪N项并不重要.我正在寻找在Python 3.6或3.7上实现这一点的最快方法.

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

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

# Taking items 1 by 1 (~0.0059s)
dst = {}
while len(dst) < 20000:
    item = src.popitem()
    dst[item[0]] = item[1]

还有更好的吗?即使是微薄的yield 也不错.

推荐答案

这要快一点:

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

与其他版本相比,使用Netwave的testcase:

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

它需要 for each 项调用Python到C的函数.

Python-3.x相关问答推荐

根据收件箱内部的值以行降序的特定顺序重新排序列

使用PANAS根据另两个列表中的值对一个列表中的字符串值进行分组

是否有必要使用Threads()中的args显式地将共享变量传递给Python中的线程函数或直接访问它?

向前/向后移动导致移动行的数据不可见

如何定义部署用 Python 编写的 Firestore 第二代函数的区域/位置?

我无法直接在 VSCode 中运行该程序,但可以使用 VScode 中的终端运行它

在不使用 split 函数的情况下从字符串中分割逗号(','),句号('.')和空格(' '),将字符串的单词附加到列表中

如何提高 snowpark 程序的性能?

三重奏:为什么频道被记录为使用async with,而不是with?

使用正则表达式捕获组解析地址

正则表达式从文本文件中捕获包含制表符/空格和子字符串的部分字符串

python 3中的SQLAlchemy ER图

解包时是否可以指定默认值?

pysftp vs. Paramiko

Tensorflow:ImportError:libcudnn.so.7:无法打开共享对象文件:没有这样的文件或目录

Asyncio RuntimeError:事件循环已关闭

matplotlib - 模块sip没有属性setapi

在 macbook pro M1 上安装 Tensorflow 时出现zsh:非法硬件指令 python

通过字典有效地替换Pandas 系列中的值

Beautifulsoup 的单元测试失败