为什么 class __dict__ 和 __weakref__ 从未在 Python 中重新定义?

Why are the class __dict__ and __weakref__ never re-defined in Python?

Class 创作似乎从来没有重新-定义__dict____weakref__ class 属性(即,如果它们已经存在于 superclass 的字典中,则它们不会添加到其 subclasses 的字典中),但总是 re -定义__doc____module__class属性。为什么?

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

class 的 __dict__(如果存在)中的 '__dict__''__weakref__' 条目是用于从实例内存布局。它们不是实际的 class 的 __dict____weakref__ 属性——它们由元class.

上的描述符管理

如果 class 的祖先已经提供了这些描述符,那么添加这些描述符是没有意义的。然而,一个 class 确实 需要它自己的 __module____doc__,不管它的 parent 是否已经有一个 - 它没有class 继承其 parent 的模块名称或文档字符串是没有意义的。


您可以在 type_new, the (very long) C implementation of type.__new__. Look for the add_weak and add_dict variables - those are the variables that determine whether type.__new__ should add space for __dict__ and __weakref__ in the class's instance memory layout. If type.__new__ decides it should add space for one of those attributes to the instance memory layout, it also adds getset descriptors to the class (through tp_getset 中查看实现以检索属性:

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 *);
}
type->tp_basicsize = slotoffset;
type->tp_itemsize = base->tp_itemsize;
type->tp_members = PyHeapType_GET_MEMBERS(et);

if (type->tp_weaklistoffset && type->tp_dictoffset)
    type->tp_getset = subtype_getsets_full;
else if (type->tp_weaklistoffset && !type->tp_dictoffset)
    type->tp_getset = subtype_getsets_weakref_only;
else if (!type->tp_weaklistoffset && type->tp_dictoffset)
    type->tp_getset = subtype_getsets_dict_only;
else
    type->tp_getset = NULL;

如果add_dictadd_weak为false,则不保留space且不添加描述符。 add_dictadd_weak 为假的一个条件是 parent 之一已经保留 space:

add_dict = 0;
add_weak = 0;
may_add_dict = base->tp_dictoffset == 0;
may_add_weak = base->tp_weaklistoffset == 0 && base->tp_itemsize == 0;

这个检查实际上并不关心任何祖先描述符,只关心祖先是否为实例 dict 指针或 weakref 指针保留 space,所以如果 C 祖先保留 space 没有提供描述符,child将不会保留space或提供描述符。例如,set 有一个非零 tp_weaklistoffset,但没有 __weakref__ 描述符,因此 set 的后代也不会提供 __weakref__ 描述符,即使实例set 个(包括子class 个实例)支持弱引用。

您还会在 may_add_weak 的初始化中看到一个 && base->tp_itemsize == 0 - 您不能将 weakref 支持添加到 class 的子class variable-length 实例。