问题描述
使用“受限 API”时,PyTypeObject 的结构不可用。所以,你不能定义静态分配的类型对象。
对于动态分配的类型对象(又名堆类型),您可以通过 PyType_Spec.name 指定类型的名称。 文档说:“对于动态分配的类型对象,这应该只是类型名称,模块名称显式存储在类型字典中作为键 '__module__' 的值。”
但是没有用于设置 PyTypeObject.tp_dict 的类型槽。那么,如何设置模块名称?
您可以在创建类型后动态执行此操作。但这给出了“DeprecationWarning: builtin type ... has no __module__ attribute”。
解决方法
__module__
是根据类型的 tp_name
计算的,它应该看起来像 "module_name.TypeName"
。在 C 代码中创建堆类型时,通过 tp_name
指定 PyType_Spec.name
。
tp_name
docs 说了一些矛盾的话:
对于动态分配的类型对象,这应该只是类型名称,以及明确存储在类型字典中作为键 '__module__'
的值的模块名称。
但是对于像 PyType_FromModuleAndSpec
这样在 C 代码中动态分配类型对象的调用来说,这些文档似乎是错误的。检查 implementation 表明它希望模块名称成为 tp_name
的一部分:
/* Set type.__module__ */
if (_PyDict_GetItemIdWithError(type->tp_dict,&PyId___module__) == NULL) {
if (PyErr_Occurred()) {
goto fail;
}
s = strrchr(spec->name,'.');
if (s != NULL) {
int err;
modname = PyUnicode_FromStringAndSize(
spec->name,(Py_ssize_t)(s - spec->name));
if (modname == NULL) {
goto fail;
}
err = _PyDict_SetItemId(type->tp_dict,&PyId___module__,modname);
Py_DECREF(modname);
if (err != 0)
goto fail;
} else {
if (PyErr_WarnFormat(PyExc_DeprecationWarning,1,"builtin type %.200s has no __module__ attribute",spec->name))
goto fail;
}
}
并检查标准库中的 PyType_Spec
的 uses 表明模块名称在 tp_name
中:
static PyType_Spec Comptype_spec = {
"zlib.Compress",sizeof(compobject),Py_TPFLAGS_DEFAULT,Comptype_slots
};
请注意,尽管 PyType_FromModuleAndSpec
将模块作为参数,但该模块并未用于设置 __module__
。