3

考虑到我有数百万个对象,其中 3__slots__

x像这样的短插槽名称与长像这样的插槽名称相比,内存效率更高would_you_like_fries_with_that_cheeseburger吗?

还是每个类只分配一次名称(而不是每个实例一次?)

4

1 回答 1

6

插槽的名称只占用每个类的内存,而不是每个实例。

插槽使用直接映射到为实例保留的内存的描述符,属性名称映射到类上的这些描述符。

因此,名称的长度不会影响每个实例用于插槽的内存量;名称仅在__dict__类的属性中(将名称映射到描述符)和描述符对象本身(以提供对象的字符串表示)中占用空间;该字符串甚至被拘留。

您可以检查自定义描述符在C 源代码中type.__new__()的状态(负责创建类对象):

if (et->ht_slots != NULL) {
    for (i = 0; i < nslots; i++, mp++) {
        mp->name = PyUnicode_AsUTF8(
            PyTuple_GET_ITEM(et->ht_slots, i));
        if (mp->name == NULL)
            goto error;
        mp->type = T_OBJECT_EX;
        mp->offset = slotoffset;

        /* __dict__ and __weakref__ are already filtered out */
        assert(strcmp(mp->name, "__dict__") != 0);
        assert(strcmp(mp->name, "__weakref__") != 0);

        slotoffset += sizeof(PyObject *);
    }
}

mp->offset实例的内存索引在哪里。

使用的描述符是一个PyMemberDescr_Type对象,其member_get函数使用(非常通用的)PyMember_GetOne()函数;使用偏移量从实例中检索指针:

PyMember_GetOne(const char *addr, PyMemberDef *l)
{
    PyObject *v;


    addr += l->offset;

addr是实例的内存地址。该函数的其余部分处理各种类型的成员;slot 成员始终设置为 type T_OBJECT_EX

case T_OBJECT_EX:
    v = *(PyObject **)addr;
    if (v == NULL)
        PyErr_SetString(PyExc_AttributeError, l->name);
    Py_XINCREF(v);
    break;

然后函数返回;如果从未设置该属性(因此v == NULL),AttributeError则会引发异常。

于 2014-05-09T07:41:05.513 回答