Python C扩展中的动态方法切换
我想创建一个PythonC扩展模块,其中包含一个类,该类可以根据Python C扩展中的动态方法切换,python,c,class,methods,Python,C,Class,Methods,我想创建一个PythonC扩展模块,其中包含一个类,该类可以根据\uuu init\uuu()方法参数值动态地别名/更改/选择其方法。我在“扩展和嵌入Python解释器”文档中找不到解决方案。为了说明这一点,下面是一个工作的Python示例,我想创建等效的Python C扩展类: class Test: def __init__(self, var): if var == 'x': self.func = self.func_x e
\uuu init\uuu()
方法参数值动态地别名/更改/选择其方法。我在“扩展和嵌入Python解释器”文档中找不到解决方案。为了说明这一点,下面是一个工作的Python示例,我想创建等效的Python C扩展类:
class Test:
def __init__(self, var):
if var == 'x':
self.func = self.func_x
else:
self.func = self.func_y
def func_x(self):
print('func_x')
def func_y(self):
print('func_y')
list = [Test('x'), Test('y'), Test('z')]
for i in range(len(list)):
list[i].func()
我想编写一个相当于Test
类的C代码,但创建list
对象,使用Python C扩展名Test
元素,使用别名func()
方法,使用Python
作为一个示例实现,以位于的noddy2
模块的Noddy
类的Python文档示例为例,如何扩展此示例Python C扩展代码以允许在Noddy_init()
函数中切换此动态方法?可以复制和修改Noddy_name()
函数,然后修改Noddy_init()
以基于第一个
参数值将self.func
设置为一个或另一个。您将self.func()
定义为PyMemberDef
还是PyObject
?是否应通过tp_方法或tp_成员在PyTypeObject
中注册?在定义了self.func()
之后,所需的C函数如何动态别名到它
干杯 您可以通过常规的
PyMethodDef
在C中实现方法func
,并从func
调用func\u a
或func\u b
,具体取决于构造函数中存储的标志。我发现了一种不同的解决方法或破解方法,可以解决速度问题。但是,这不是一个理想的或真正的python解决方案,因为它不会在通过\uuu init\uuu()
方法参数实例化类时别名类方法。它也不理想,因为func()=NULL
方法定义可能会导致问题。这个技巧是有一个自定义的\uuu getattr\uuu()
方法,它为func()
返回所需的方法
下面是实现原始问题的Test
Python类的C代码(它与Python 2和3兼容)。由于在示例中创建列表
变量时调用了\uuuu getattr\uuuu()
方法,因此每个列表元素只调用一次。因此,当以这种方式使用时,对func()
的多次调用,切换逻辑只发生一次,从而解决了速度问题
#include <Python.h>
#include "structmember.h"
#include <stdio.h>
/* Declaration of the Test class and its contents. */
typedef struct {
PyObject_HEAD
PyObject *var; /* The self.var string. */
} Test;
/* Class method func_x. */
static PyObject * func_x(Test *self, PyObject *args) {
printf("func_x\n");
Py_INCREF(Py_None);
return Py_None;
}
/* Class method func_y. */
static PyObject * func_y(Test *self, PyObject *args) {
printf("func_y\n");
Py_INCREF(Py_None);
return Py_None;
}
/* Definition of all functions of the test module. */
static PyMethodDef test_methods[] = {
{NULL, NULL, 0, NULL} /* Sentinel. */
};
/* Definition of all methods of the Test class. */
static PyMethodDef Test_methods[] = {
{"func", NULL, METH_VARARGS, "Target function alias."},
{"func_x", (PyCFunction)func_x, METH_NOARGS, "Target function X." },
{"func_y", (PyCFunction)func_y, METH_NOARGS, "Target function Y."},
{NULL} /* Sentinel */
};
/* Definition of the class instance objects. */
static PyMemberDef Test_members[] = {
{"var", T_OBJECT_EX, offsetof(Test, var), 0, "The function choice."},
{NULL} /* Sentinel */
};
/* Class destruction. */
static void Test_dealloc(Test *self)
{
Py_XDECREF(self->var);
#if PY_MAJOR_VERSION >= 3
Py_TYPE(self)->tp_free((PyObject*)self);
#else
self->ob_type->tp_free((PyObject*)self);
#endif
}
/* The Test.__getattr__() instance method for obtaining instance attributes. */
static PyObject *
Test_getattro(Test *self, PyObject *object)
{
PyObject *objname_bytes, *result = NULL;
char *objname, *var;
/* The variable name and object name. */
#if PY_MAJOR_VERSION >= 3
var = PyBytes_AsString(self->var);
objname_bytes = PyUnicode_AsEncodedString(object, "utf-8", "strict");
objname = PyBytes_AsString(objname_bytes);
#else
var = PyString_AsString(self->var);
objname = PyString_AsString(object);
#endif
/* Target function aliasing. */
if (strcmp(objname, "func") == 0) {
if (strcmp(var, "x") == 0)
result = PyObject_GetAttrString((PyObject *)self, "func_x");
else
result = PyObject_GetAttrString((PyObject *)self, "func_y");
}
/* Normal attribute handling (nothing else to return). */
else
result = PyObject_GenericGetAttr((PyObject *)self, object);
return result;
}
/* The Test.__new__() method definition for creating the class. */
static PyObject *
Test_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
Test *self;
self = (Test *)type->tp_alloc(type, 0);
if (self != NULL) {
/* Set up the function choice variable to the empty string. */
#if PY_MAJOR_VERSION >= 3
self->var = PyUnicode_FromString("");
#else
self->var = PyString_FromString("");
#endif
}
return (PyObject *)self;
}
/* The Test.__init__() method definition for initialising the class. */
static int
Test_init(Test *self, PyObject *args, PyObject *keywords)
{
PyObject *var=NULL, *tmp;
/* The keyword list. */
static char *keyword_list[] = {"var", NULL};
/* Parse the function arguments. */
if (! PyArg_ParseTupleAndKeywords(args, keywords, "|S", keyword_list, &var))
return -1;
/* Store the arguments in self. */
if (var) {
tmp = self->var;
Py_INCREF(var);
self->var = var;
Py_XDECREF(tmp);
}
return 0;
}
/* Define the type object to create the class. */
static PyTypeObject Test_type = {
#if PY_MAJOR_VERSION >= 3
PyVarObject_HEAD_INIT(NULL, 0)
#else
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
#endif
"test.Test", /*tp_name*/
sizeof(Test), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Test_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
(getattrofunc)Test_getattro, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Test class.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Test_methods, /* tp_methods */
Test_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Test_init, /* tp_init */
0, /* tp_alloc */
Test_new, /* tp_new */
};
/* Define the Python 3 module. */
#if PY_MAJOR_VERSION >= 3
static PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"test", /* m_name */
"C module.", /* m_doc */
-1, /* m_size */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
/* Declarations for DLL import/export */
#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif
/* Initialise as a Python module. */
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_test(void)
{
PyObject* m;
if (PyType_Ready(&Test_type) < 0)
return NULL;
m = PyModule_Create(&moduledef);
if (m == NULL)
return NULL;
Py_INCREF(&Test_type);
PyModule_AddObject(m, "Test", (PyObject *)&Test_type);
return m;
}
#else
inittest(void)
{
PyObject* m;
if (PyType_Ready(&Test_type) < 0)
return;
m = Py_InitModule3("test", test_methods,
"Example module that creates an extension type.");
if (m == NULL)
return;
Py_INCREF(&Test_type);
PyModule_AddObject(m, "Test", (PyObject *)&Test_type);
}
#endif
对于这样的问题,这是一个很好的解决方法。它可能也是最简单的实现方法。然而,有一个缺点——速度。在Python示例中,类方法别名在类实例化期间发生。因此,该逻辑只出现一次,并且可以多次调用
func()。在我的问题中,func()。在这种情况下,最好在类实例化过程中使用函数切换逻辑。不管怎样,这种变通方法比Python代码快得多。如果需要额外的速度,您可以调用并存储指向func_a
或func_b
的指针,而不是标记。
cc $(python-config --cflags --ldflags) -o test.os -c -fPIC test.c
cc -o test.so -shared test.os