使用CPP扩展Python
基本需求是这样的,当
const char* p = NULL;
if(!PyArg_ParseTuple(args, "s", &p))
return NULL;
看python帮助文档,当在PyArg_ParseTuple中使用格式字符串s时,一定要传入指针的地址。 不能为该指针分配存储空间。char str[100],这样肯定是不行的。 通过该调用,该指针就指向了一个已经存在的对象。
于是我想,把指针,字符串地址保存起来,需要时,就可以直接输出Python虚拟机内的一个字符串了。 这样不用自己分配内存,管理内存了。 关于引用计数问题,既然是指针指向了一个已经存在的对象,理所当然的认为Python虚拟机会自己搞定引用计数问题。
经过实践
int i = 0;
static PyObject*
test_test(PyObject* self, PyObject* args)
{
return Py_BuildValue("i", i++);
}
const char* ptest[10];
static PyObject*
test_str(PyObject* self, PyObject* args)
{
const char* p = NULL;
if(!PyArg_ParseTuple(args, "s", &p))
return NULL;
ptest[i] = p;
Py_RETURN_NONE;
}
static PyObject*
test_flush(PyObject* self, PyObject* args)
{
for(int j = 0; j < 10; ++j){
printf("%d=%s\n", j, ptest[j]);
}
Py_RETURN_NONE;
}
static PyMethodDef testMethods[] = {
{"test", test_test, METH_VARARGS, "hello world test"},
{"str", test_str, METH_VARARGS, "hello world test"},
{"flush", test_flush, METH_VARARGS, "hello world test"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
inittest(void)
{
(void)Py_InitModule("test", testMethods);
}
上面是cpp代码
import test
for i in range(10):
strs = "nihao"+str(i)
test.str(strs)
test.test()
test.flush()
这是测试用的py代码。
结果证明,偶是错误的。 形式主义害死人啊。
仔细想想,如果在这种情况下,虚拟机可以自己搞定引用计数问题, 也就是PyArg_ParseTuple(args, “s”, &p)这段代码增加虚拟机内字符串对象的引用计数,什么时候削减该对象的引用呢? 如果需要自己手动调用delete的话,在写代码时也不符合成对编程,也就是new/delete,malloc/free成对出现的良好习惯。 忽略strdup这个东东。
而且文档里说,指针指向已经存在的一个对象,并没有说引用计数的问题。 而且传入一个指针就想增加引用计数,也不合情理。 如果增加引用计数,cpp代码又没有主动调用delete或者free,导致的内存泄漏,是虚拟机的责任,还是自己代码的责任? 如果你是设计者,你会怎么简单有效暴力的解决这个问题?!
所以告诫自己,这个地方要自己管理内存。
=====================================================
补充:
PyArg_ParseTuple中的关于传递object对象的格式字符串是大写o,不是0,也不是小写o。是大写O。
=====================================================
同样可想而知,如果在CPP内分配了一段内存,如new char[],通过buildvalue传递给py时,自己也要记得delete。