Python C API,覆盖 PyNumberMethods nb_add 字段也更改 tp_base 中的字段
Python C API, override the PyNumberMethods nb_add field also changes the field in tp_base
我开发了一个新类型:PyAlignArray_Type 派生自 PyArray_Type
PyAlignArray_Type.tp_base = &PyArray_Type;
我重写 tp_as_number 的 nb_add 字段如下:
PyAlignArray_Type.tp_as_number->nb_add = (binaryfunc)ndarray_add ;
这工作正常,但该字段也针对基本类型 (PyArray_Type) 进行了更改。
我的问题是:如何在不更改基类型的情况下覆盖 PyNumberMethods 字段?
typedef struct {
PyArrayObject array;
int pitch;
size_t size;
} PyAlignArrayObject;
/*---------------PyAlignArray_Type------------------*/
static PyTypeObject PyAlignArray_Type = {
PyObject_HEAD_INIT(NULL)
"plibs_8.ndarray", /* tp_name */
sizeof(PyAlignArrayObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)array_dealloc,/* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
ndarray_methods, /* tp_methods */
ndarray_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)ndarray_new, /* tp_new */
};
static PyModuleDef ndarraymodule = {
PyModuleDef_HEAD_INIT,
"ndarray",
"ndarray module",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC
PyInit_ndarray(void)
{
PyObject *m;
import_array();
PyAlignArray_Type.tp_base = &PyArray_Type;
if (PyType_Ready(&PyAlignArray_Type) < 0)
return NULL;
// __add__ overloading
PyAlignArray_Type.tp_as_number->nb_add = (binaryfunc)ndarray_add ;
m = PyModule_Create(&ndarraymodule);
if (m == NULL)
return NULL;
Py_INCREF(&PyAlignArray_Type);
PyModule_AddObject(m, "ndarray", (PyObject *) &PyAlignArray_Type);
return m;
}
我认为你是对的 - 这是一种奇怪的、非直觉的行为。它是可以修复的。 The documentation says
The tp_as_number
field is not inherited, but the contained fields are inherited individually.
这意味着可以像您所做的那样覆盖它们。它实际上的意思是:
The tp_as_number
field is not inherited, but the contained fields are inherited individually if you provide space for them. If you don't provide space for them then the tp_as_number
field is set to point at the base classes tp_as_number
field.
(斜体是我添加的。请参阅 https://github.com/python/cpython/blob/3e8d6cb1892377394e4b11819c33fbac728ea9e0/Objects/typeobject.c#L5111 and https://github.com/python/cpython/blob/3e8d6cb1892377394e4b11819c33fbac728ea9e0/Objects/typeobject.c#L4757 处的代码)。这意味着,就像在您的示例中一样,您也在更改基本 class 方法。
解决这个问题的方法是创建一个零初始化 PyNumberMethods
并在您的 class
中引用它
static PyNumberMethods align_array_number_methods = { NULL };
/* You could also specify the nb_add field here and you wouldn't
have to do it in your module init */
static PyTypeObject PyAlignArray_Type = {
/* whole bunch of code unchanged */
&align_array_number_methods, /* tp_as_number */
/* whole bunch of code unchanged */
你留为NULL的槽位根据PyType_Ready
中的继承规则进行填充
我认为这种奇怪行为的原因是为了避免引入内存泄漏。当您调用 PyType_Ready
(这是处理继承的地方)时,它不能静态分配 PyNumberMethods
因此它必须 malloc
它。因为您不知道它是否 malloc
ed 这些字段或永远不会被释放的内存。 (这只是动态创建类型的一个问题)。
我开发了一个新类型:PyAlignArray_Type 派生自 PyArray_Type
PyAlignArray_Type.tp_base = &PyArray_Type;
我重写 tp_as_number 的 nb_add 字段如下:
PyAlignArray_Type.tp_as_number->nb_add = (binaryfunc)ndarray_add ;
这工作正常,但该字段也针对基本类型 (PyArray_Type) 进行了更改。 我的问题是:如何在不更改基类型的情况下覆盖 PyNumberMethods 字段?
typedef struct {
PyArrayObject array;
int pitch;
size_t size;
} PyAlignArrayObject;
/*---------------PyAlignArray_Type------------------*/
static PyTypeObject PyAlignArray_Type = {
PyObject_HEAD_INIT(NULL)
"plibs_8.ndarray", /* tp_name */
sizeof(PyAlignArrayObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)array_dealloc,/* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
ndarray_methods, /* tp_methods */
ndarray_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)ndarray_new, /* tp_new */
};
static PyModuleDef ndarraymodule = {
PyModuleDef_HEAD_INIT,
"ndarray",
"ndarray module",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC
PyInit_ndarray(void)
{
PyObject *m;
import_array();
PyAlignArray_Type.tp_base = &PyArray_Type;
if (PyType_Ready(&PyAlignArray_Type) < 0)
return NULL;
// __add__ overloading
PyAlignArray_Type.tp_as_number->nb_add = (binaryfunc)ndarray_add ;
m = PyModule_Create(&ndarraymodule);
if (m == NULL)
return NULL;
Py_INCREF(&PyAlignArray_Type);
PyModule_AddObject(m, "ndarray", (PyObject *) &PyAlignArray_Type);
return m;
}
我认为你是对的 - 这是一种奇怪的、非直觉的行为。它是可以修复的。 The documentation says
The
tp_as_number
field is not inherited, but the contained fields are inherited individually.
这意味着可以像您所做的那样覆盖它们。它实际上的意思是:
The
tp_as_number
field is not inherited, but the contained fields are inherited individually if you provide space for them. If you don't provide space for them then thetp_as_number
field is set to point at the base classestp_as_number
field.
(斜体是我添加的。请参阅 https://github.com/python/cpython/blob/3e8d6cb1892377394e4b11819c33fbac728ea9e0/Objects/typeobject.c#L5111 and https://github.com/python/cpython/blob/3e8d6cb1892377394e4b11819c33fbac728ea9e0/Objects/typeobject.c#L4757 处的代码)。这意味着,就像在您的示例中一样,您也在更改基本 class 方法。
解决这个问题的方法是创建一个零初始化 PyNumberMethods
并在您的 class
static PyNumberMethods align_array_number_methods = { NULL };
/* You could also specify the nb_add field here and you wouldn't
have to do it in your module init */
static PyTypeObject PyAlignArray_Type = {
/* whole bunch of code unchanged */
&align_array_number_methods, /* tp_as_number */
/* whole bunch of code unchanged */
你留为NULL的槽位根据PyType_Ready
我认为这种奇怪行为的原因是为了避免引入内存泄漏。当您调用 PyType_Ready
(这是处理继承的地方)时,它不能静态分配 PyNumberMethods
因此它必须 malloc
它。因为您不知道它是否 malloc
ed 这些字段或永远不会被释放的内存。 (这只是动态创建类型的一个问题)。