【问题标题】:Create PyString from c character array without copying从c字符数组创建PyString而不复制
【发布时间】:2014-07-31 19:53:36
【问题描述】:

我有一个来自 C 应用程序的大量字符串缓冲区(基本上 12GB)。

我想在 C 中为嵌入式 Python 解释器创建 PyString 对象,而不复制字符串。这可能吗?

【问题讨论】:

  • 只要有足够的时间、金钱和计算资源,计算中的一切皆有可能。这真的是你的问题吗?
  • 我想在不重写 PyString 的情况下这样做。
  • @RobertHarvey 不,该示例使用副本。见docs.python.org/2/c-api/string.html#PyString_FromStringAndSize
  • BufferProtocols 和 NumPy 就是这样工作的,只需给 c 指针。我希望有一种方法可以用字符串来做到这一点。
  • @Santa 你有从 C 调用 ctypes 到嵌入式 Python 解释器的示例吗?

标签: python c


【解决方案1】:

我认为这是不可能的,因为 Python String 对象被嵌入到 PyObject 结构中。换句话说,Python 字符串对象是 PyObject_HEAD 后跟字符串的字节。您必须在内存中有空间才能将 PyObject_HEAD 信息放在现有字节周围。

【讨论】:

  • 我可以只使用 numpy.str_ 吗?不过,与其他 PyString 相比,这些似乎存在问题。
【解决方案2】:

没有副本就不能使用 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.searchsorted 之类的东西在 numpy 级别进行二进制搜索,但它确实在没有足够副本的情况下绕过 char*。

【讨论】:

    猜你喜欢
    • 2012-10-20
    • 2018-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    相关资源
    最近更新 更多