首页 > 解决方案 > __dict__ 和 __weakref__ 是类定义中的默认插槽吗?

问题描述

根据Python 语言文档

__slots__允许我们显式声明数据成员(如属性)并拒绝创建__dict__and __weakref__(除非__slots__在父级中显式声明或可用。)

所以我想知道这门课是否

>>> class A: __slots__ = ('__dict__', '__weakref__')
... 
>>> del A.__slots__
>>> vars(A)
mappingproxy({
    '__module__': '__main__',
    '__dict__': <attribute '__dict__' of 'A' objects>,
    '__weakref__': <attribute '__weakref__' of 'A' objects>,
    '__doc__': None
})

相当于这个:

>>> class A: pass
... 
>>> vars(A)
mappingproxy({
    '__module__': '__main__',
    '__dict__': <attribute '__dict__' of 'A' objects>,
    '__weakref__': <attribute '__weakref__' of 'A' objects>,
    '__doc__': None
})

标签: pythonclassinstancepython-internalsslots

解决方案


如果不设置__slots__属性,默认是在特定条件下创建__weakref____dict__插槽。

这发生在type.__new__();一个类被赋予描述符来访问每个实例的内存槽:

  • 首先,通过测试类型对象结构中的,和值来检查该类型是否可以具有这些插槽。tp_dictoffsettp_weaklistoffsettp_itemsize

    • __dict__如果基类尚未定义一个(base->tp_dictoffset为 0),则只能有一个插槽。
    • __weakref__如果基本类型未定义一个(base->tp_weaklistoffset为 0)并且基本类型具有固定长度的实例(为 0),则您只能拥有一个插槽base->tp_itemsize
  • 然后,如果没有__slots__,则设置标志变量以实际启用正在添加的插槽。

type.__new__()实现很长,但相关部分如下所示:

    slots = _PyDict_GetItemIdWithError(dict, &PyId___slots__);
    nslots = 0;
    add_dict = 0;
    add_weak = 0;
    may_add_dict = base->tp_dictoffset == 0;
    may_add_weak = base->tp_weaklistoffset == 0 && base->tp_itemsize == 0;
    if (slots == NULL) {
        if (PyErr_Occurred()) {
            goto error;
        }
        if (may_add_dict) {
            add_dict++;
        }
        if (may_add_weak) {
            add_weak++;
        }
    }
    else {
        /* Have slots */
        ...
    }

然后,向下约 400 行,您将找到实际创建插槽的代码:

    if (add_dict) {
        if (base->tp_itemsize)
            type->tp_dictoffset = -(long)sizeof(PyObject *);
        else
            type->tp_dictoffset = slotoffset;
        slotoffset += sizeof(PyObject *);
    }
    if (add_weak) {
        assert(!base->tp_itemsize);
        type->tp_weaklistoffset = slotoffset;
        slotoffset += sizeof(PyObject *);
    }

扩展某些本机类型(例如intor bytes)时,您不会获得__weakref__插槽,因为它们具有可变长度实例(由 处理tp_itemsize)。


推荐阅读