字典大小随着增加一个元素而减小

纳特卡斯爵士:

我跑了这个:

import sys

diii = {'key1':1,'key2':2,'key3':1,'key4':2,'key5':1,'key6':2,'key7':1}
print sys.getsizeof(diii)
# output: 1048

diii = {'key1':1,'key2':2,'key3':1,'key4':2,'key5':1,'key6':2,'key7':1,'key8':2}
print sys.getsizeof(diii)
# output: 664  

在这里问之前,我重新启动了python shell并在线进行了尝试,并得到了相同的结果。
我认为一本包含更多元素的字典将提供与输出相同或更多的字节,而不是包含少一个元素的字典。

知道我在做什么错吗?

克拉索夫斯基:

先前的答案已经提到您不必担心,因此我将深入探讨一些技术细节。很长,但是请忍受我。

TLDR:这与调整大小的算法有关。每次调整大小都会分配2**i内存,其中2**i > requested_size; 2**i >= 8,但是如果填充了2/3的插槽,则每次插入都会进一步调整基础表的大小,但这一次是new_size = old_size * 4这样,您的第一个字典最终分配了32个单元格,而第二个字典仅分配了16个单元格(因为它的初始大小更大)。

:正如@snakecharmerb在评论中指出的那样,这取决于字典的创建方式。为了简洁起见,让我向您推荐这篇优秀的博客文章,该文章解释了Python字节码和CPython实现级别上dict()构造函数和dict文字之间的区别{}

让我们从8键的神奇数字开始。事实证明,这是为dictobject.h头文件中的Python 2.7实现预定义的常量-Python字典的最小大小:

/* PyDict_MINSIZE is the minimum size of a dictionary.  This many slots are
 * allocated directly in the dict object (in the ma_smalltable member).
 * It must be a power of 2, and at least 4.  8 allows dicts with no more
 * than 5 active entries to live in ma_smalltable (and so avoid an
 * additional malloc); instrumentation suggested this suffices for the
 * majority of dicts (consisting mostly of usually-small instance dicts and
 * usually-small dicts created to pass keyword arguments).
 */
#define PyDict_MINSIZE 8

因此,在特定的Python实现之间可能有所不同,但让我们假设我们都使用相同的CPython版本。但是,大小为8的dict预计将仅包含5个元素;不必担心,因为这种特定的优化对我们而言似乎并不重要。

现在,当您使用dict文字创建字典时{},CPython会采用快捷方式(与调用dict构造函数时的显式创建相比)。字节码操作简化了一点BUILD_MAP,它导致调用该_PyDict_NewPresized函数,函数将构造一个我们已经预先知道其大小的字典:

/* Create a new dictionary pre-sized to hold an estimated number of elements.
   Underestimates are okay because the dictionary will resize as necessary.
   Overestimates just mean the dictionary will be more sparse than usual.
*/

PyObject *
_PyDict_NewPresized(Py_ssize_t minused)
{
    PyObject *op = PyDict_New();

    if (minused>5 && op != NULL && dictresize((PyDictObject *)op, minused) == -1) {
        Py_DECREF(op);
        return NULL;
    }
    return op;
}

该函数调用普通的dict构造函数(PyDict_New),并要求重新调整新创建的dict的大小-但前提是希望它容纳5个以上的元素。这是由于一项优化,它允许Python通过将数据保存在预先分配的“小表”中来加速某些事情,而无需调用昂贵的内存分配和取消分配功能。

然后,dictresize将尝试确定新字典的最小大小。它还将使用魔术数8-作为起点,并迭代乘以2,直到找到最小大小大于请求的大小。对于第一个字典,它只是8,但是,对于第二个字典(以及由dict文字少于15个键的所有字典),它是16。

现在,在该dictresize函数中,前者具有一个特殊情况new_size == 8,即small,这意味着进行上述优化(使用“小表”减少内存操作)。但是,由于不需要调整新创建的dict的大小(例如,到目前为止尚未删除任何元素,因此表是“干净的”),因此实际上什么也没有发生。

相反,当时new_size != 8,遵循重新分配哈希表的常规过程。最后,分配一个新表来存储“大”字典。尽管这是直观的(较大的dict有较大的表),但这似乎尚未使我们前进到所观察到的行为-但是,请再忍受我一下。

一旦有了预分配的字典,STORE_MAP optcodes就会告诉解释器插入连续的键值对。这是通过dict_set_item_by_hash_or_entry功能实现的-重要的是-如果已经使用了超过2/3的插槽,则每次增加大小(即成功插入)后都会调整字典的大小。大小将增加x4(在我们的示例中,对于较大的字典,仅增加x2)。

因此,当您使用7个元素创建字典时会发生以下情况:

# note 2/3 = 0.(6)
BUILD_MAP   # initial_size = 8, filled = 0
STORE_MAP   # 'key_1' ratio_filled = 1/8 = 0.125, not resizing
STORE_MAP   # 'key_2' ratio_filled = 2/8 = 0.250, not resizing
STORE_MAP   # 'key_3' ratio_filled = 3/8 = 0.375, not resizing
STORE_MAP   # 'key_4' ratio_filled = 4/8 = 0.500, not resizing
STORE_MAP   # 'key_5' ratio_filled = 5/8 = 0.625, not resizing
STORE_MAP   # 'key_6' ratio_filled = 6/8 = 0.750, RESIZING! new_size = 8*4 = 32
STORE_MAP   # 'key_7' ratio_filled = 7/32 = 0.21875

最后,您得到的哈希表中共有32个元素的dict。

但是,当添加八个元素时,初始大小将是其两倍大(16),因此我们将永远不会调整大小,因为ratio_filled > 2/3将永远无法满足条件

这就是为什么在第二种情况下最终使用较小的表的原因。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

将元素大小减小一个因子,然后以相同的因子增加到原始大小

我需要一个数学函数,其中y随着x的增加而减小得更快

SASS 函数随着字体大小的增加而减小行高

随着浏览器宽度减小,增加元素填充

添加第一个元素后,字典中的“快速设置”值不会增加

如何随着另一个div的高度增加自动增加div的高度?

根据另一个RV滚动来增加和减小RV高度

如何创建两个flex容器,一个随着内容的增加而增加,另一个不增加?

Python-如何随着时间的流逝增加一个值

随着时间的推移增加一个值时从变量中添加/减去

为什么第一个元素与最后一个元素之间的距离减小了?

创建一个处理程序以增加字体大小

Python:字典列表,如果存在,则增加一个字典值,如果不增加新字典

字典-获取已知元素的下一个元素

无法使用javascript中的上一个元素同级增加最后一个元素

Laravel 在 HTML 元素中增加一个变量

当一个元素增加时,增加所有元素的高度(等高列)

在一个循环中将三个元素增加一个数字

从字典的每个键中随机采样一个元素

缺少 If 语句的字典原因的一个元素

向字典添加另一个元素

如何打印一个键为整数的字典元素?

python截断字典以获取最后一个元素

只保留嵌套字典的第一个元素

从字典的列表中弹出一个元素

增加/减小页面上所有元素的字体大小的有效方法

如何根据另一个列元素的大小选择一个列元素?

如何使一个元素跟随另一个元素的大小

通过将布尔值转换为数组来减小JSON大小是一个好主意吗?