【问题标题】:Are __dict__ and __weakref__ the default slots at class definition?__dict__ 和 __weakref__ 是类定义中的默认插槽吗?
【发布时间】:2021-07-02 11:40:04
【问题描述】:

根据Python language documentation

__slots__ 允许我们显式声明数据成员(如属性)并拒绝创建 __dict____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
})

【问题讨论】:

    标签: python class instance python-internals slots


    【解决方案1】:

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

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

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

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

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

        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 *);
        }
    

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

    【讨论】:

    • 非常感谢。 “对于 Python 类,情况总是如此”除了 variable-length 类型的子类(如内置类型 inttuplebytes),因为它们具有非零 __itemsize__ 属性,因此他们不会得到 __weakref__ 属性,对吧?
    • @Maggyero 对,这取决于基地的类型。我应该更清楚。
    • 不错的编辑。关于术语,当我们说“槽”时,我们是在谈论类上的描述符还是描述符引用的每个类实例上的保留存储,还是两者兼而有之?
    • @Maggyero 保留的存储空间。描述符是该内存和 Python 代码之间的桥梁。
    • Descriptor HowTo 似乎证实了插槽是类 instances 上的保留存储,就像您说的那样:“当一个类定义 __slots__ 时,它将实例字典替换为固定长度的槽值数组。”和“object.__new__() 方法负责创建具有插槽而不是实例字典的实例。”
    猜你喜欢
    • 2021-06-29
    • 2013-08-26
    • 1970-01-01
    • 2013-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多