为什么 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_dict
或add_weak
为false,则不保留space且不添加描述符。 add_dict
或 add_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 实例。
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_dict
或add_weak
为false,则不保留space且不添加描述符。 add_dict
或 add_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 实例。