从C字符数组创建PyString而不复制
我有一个很大的字符串缓存(大约12GB),是从一个C语言的应用程序中得到的。
我想在C语言中为一个嵌入式的Python解释器创建PyString对象,但不想复制这些字符串。这样做可以吗?
2 个回答
7
我觉得这不太可能,原因很简单:Python的字符串对象是嵌入在PyObject结构里的。换句话说,Python的字符串对象由PyObject_HEAD和字符串的字节组成。你需要在内存中有足够的空间,把PyObject_HEAD的信息放在现有字节的周围。
7
在使用PyString时,不能直接使用而不进行复制,但可以使用ctypes。实际上,ctypes.c_char_p
的工作方式基本上就像字符串。例如,下面这段C代码:
static char* names[7] = {"a", "b", "c", "d", "e", "f", "g"};
PyObject *pFunc, *pArgs, *pValue;
pFunc = td_py_get_callable("my_func");
pArgs = PyTuple_New(2);
pValue = PyLong_FromSize_t((size_t) names);
PyTuple_SetItem(pArgs, 0, pValue);
pValue = PyLong_FromLong(7);
PyTuple_SetItem(pArgs, 1, pValue);
pValue = PyObject_CallObject(pFunc, pArgs);
然后可以通过以下的Python函数my_func
传递地址和字符字符串的数量:
def my_func(names_addr, num_strs):
type_char_p = ctypes.POINTER(ctypes.c_char_p)
names = type_char_p.from_address(names_addr)
for idx in range(num_strs):
print(names[idx])
当然,谁会想在Python中到处传递地址和长度呢?我们可以把这些放进一个numpy数组里,然后在需要的时候再转换一下:
def my_func(name_addr, num_strs):
type_char_p = ctypes.POINTER(ctypes.c_char_p)
names = type_char_p.from_address(names_addr)
// Cast to size_t pointers to be held by numpy
p = ctypes.cast(names, ctypes.POINTER(ctypes.c_size_t))
name_addrs = numpy.ctypeslib.as_array(p, shape=(num_strs,))
// pass to some numpy functions
my_numpy_fun(name_addrs)
挑战在于,评估numpy数组的索引只会给你一个地址,但这个内存和原来的C指针是一样的。我们可以把它转换回ctypes.POINTER(ctypes.c_char_p)
来访问值:
def my_numpy_func(name_addrs):
names = name_addrs.ctypes.data_as(ctypes.POINTER(ctypes.c_char_p))
for i in range(len(name_addrs)):
print names[i]
这并不是完美的,因为我不能像在numpy层面使用numpy.searchsorted
进行二分查找,但它确实可以在不复制的情况下很好地传递char*。