__dict__ 和 __weakref__ 是 class 定义中的默认插槽吗?
Are __dict__ and __weakref__ the default slots at class definition?
根据 Python language documentation:
__slots__
allow us to explicitly declare data members (like properties) and deny the creation of __dict__
and __weakref__
(unless explicitly declared in __slots__
or available in a parent.)
所以我想知道这个 class
>>> 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
})
如果您不设置 __slots__
属性,默认情况下会在特定条件下创建 __weakref__
和 __dict__
插槽。
这发生在 type.__new__()
; a class 被赋予描述符以访问每个实例的内存槽:
首先,通过测试类型对象结构中的 tp_dictoffset
, tp_weaklistoffset
and tp_itemsize
值来检查类型 是否可以 具有这些插槽。
- 如果基数 class 尚未定义(
base->tp_dictoffset
为 0),则只能有一个 __dict__
槽。
- 如果基本类型没有定义(
base->tp_weaklistoffset
为 0)并且基本具有固定长度的实例(base->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 *);
}
当扩展某些本机类型(例如 int
或 bytes
)时,您不会获得 __weakref__
插槽,因为它们具有可变长度实例(由 [=18 处理) =]).
根据 Python language documentation:
__slots__
allow us to explicitly declare data members (like properties) and deny the creation of__dict__
and__weakref__
(unless explicitly declared in__slots__
or available in a parent.)
所以我想知道这个 class
>>> 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
})
如果您不设置 __slots__
属性,默认情况下会在特定条件下创建 __weakref__
和 __dict__
插槽。
这发生在 type.__new__()
; a class 被赋予描述符以访问每个实例的内存槽:
首先,通过测试类型对象结构中的
tp_dictoffset
,tp_weaklistoffset
andtp_itemsize
值来检查类型 是否可以 具有这些插槽。- 如果基数 class 尚未定义(
base->tp_dictoffset
为 0),则只能有一个__dict__
槽。 - 如果基本类型没有定义(
base->tp_weaklistoffset
为 0)并且基本具有固定长度的实例(base->tp_itemsize
为 0),则只能有一个__weakref__
槽).
- 如果基数 class 尚未定义(
然后,如果没有
__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 *);
}
当扩展某些本机类型(例如 int
或 bytes
)时,您不会获得 __weakref__
插槽,因为它们具有可变长度实例(由 [=18 处理) =]).