对 C 的标注是否可以预估 Python 字典的容量?

Can a callout to C presize a Python dict's capacity?

作为处理将容纳数千万或数亿个键的字典的优化,我真的非常想预先调整其容量……但似乎没有 Pythonic 的方法可以做到这一点。

用Cython或者C callout直接调用CPython的内部函数,比如dictresize() or _PyDict__NewPresized(),来实现这个是否实用?

通常,这是个坏主意,因为这意味着您的代码取决于 python 语言的实现。所以每次更新 CPython 时它都可能会中断。 但是,如果您提前知道键(这可能不会扩展到 python 的其他实现):

keys = {'red', 'green', 'blue', 'yellow', 'orange', 'pink', 'black'}
d = dict.fromkeys(keys)

这个字典dwill have the correct size right away,因为dict.fromkeys()提前分配了space

这取决于你所说的实用。这当然很简单;你可以打电话 _PyDict_NewPresized(howevermany)。哎呀,你甚至可以从 Python:

>>> import ctypes
>>> import sys
>>> ctypes.pythonapi._PyDict_NewPresized.restype = ctypes.py_object
>>> d = ctypes.pythonapi._PyDict_NewPresized(100)
>>> sys.getsizeof(d)
1676
>>> sys.getsizeof({})
140
>>> len(d)
0

如您所见,dict 已预先调整大小,但没有任何元素。像这样依赖 CPython 实现细节是否实用取决于您。

经过一夜的黑客攻击,我想出了以下不依赖任何模块的解决方案。它允许您为最多 2**31-1 (=2,147,483,647) 个元素的任意数量的空间初始化一个字典。

def bigdict(size):
    bytecode = '\x91%c%ci%c%cS'%((size>>16)&0xff,(size>>24)&0xff,size&0xff,(size>>8)&0xff)
    return eval(bigdict.func_code.__class__( 0, 0, 1, 64, bytecode, (), (), (), "317070", '<module>', 1, '', (), ()))

举例说明:

In [95]: print sys.getsizeof({})
280

In [96]: print sys.getsizeof(bigdict(0))
280

In [97]: print sys.getsizeof(bigdict(1))
280

In [98]: print sys.getsizeof(bigdict(100))
3352

In [99]: print sys.getsizeof(bigdict(2**29-1))
12884902168

In [100]: print bigdict(2**29-1)
{}

这是我见过的最慢的空字典。最后一条命令花了很长时间才完成。