在Python中将对象传递给C模块

11 投票
1 回答
2871 浏览
提问于 2025-04-17 07:18

我遇到了一个关于纯Python和C语言Python模块的问题。简单来说,我想知道如何在C模块中接收和处理Python对象。我的Python部分看起来是这样的。


    #!/usr/bin/env python

    import os, sys
    from c_hello import *

    class Hello:
        busyHello = _sayhello_obj

    class Man:
        def __init__(self, name):
            self.name = name
        def getName(self):
            return self.name

    h = Hello()
    h.busyHello( Man("John") )

在C语言中,有两个问题需要解决。首先,我该如何接收这个对象?其次,我该如何调用这个对象的方法?


    static PyObject *
    _sayhello_obj(PyObject *self, PyObject *args)
    {
      PyObject *obj;
      // How can I fill obj?

      char s[1024];
      // How can I fill s, from obj.getName() ?

      printf("Hello, %s\n", s);
      return Py_None;
    }

1 个回答

10

要从你的方法调用中提取参数,你需要查看一些文档,比如解析参数和构建值中的函数,比如PyArg_ParseTuple。这个函数是用来处理位置参数的!如果你还需要处理关键字参数等,文档里还有其他的函数可以使用。

PyArg_ParseTuple返回的对象,它的引用计数不会增加。对于简单的C函数来说,你可能不需要担心这个问题。但如果你在和其他Python/C函数交互,或者在释放全局解释器锁(也就是允许多线程)时,你就需要非常仔细地考虑对象的所有权问题。

static PyObject *
_sayhello_obj(PyObject *self, PyObject *args)
{
  PyObject *obj = NULL;
  // How can I fill obj?

  static char fmt_string = "O" // For "object"

  int parse_result = PyArg_ParseTuple(args, fmt_string, &obj);

  if(!parse_res)
  {
    // Don't worry about using PyErr_SetString, all the exception stuff should be
    // done in PyArg_ParseTuple()
    return NULL;
  }

  // Of course, at this point you need to do your own verification of whatever
  // constraints might be on your argument.

如果你想在一个对象上调用方法,你需要使用PyObject_CallMethod或者PyObject_CallMethodObjArgs,具体取决于你是如何构造参数列表和方法名称的。还有,注意我在代码中提到的对象所有权问题!

稍微插一句,确保你不会在后面遇到麻烦:如果你只是想把字符串提取出来打印,直接获取对象引用并传给PyObject_Print会更好。当然,也许你只是为了举例,或者你比我更清楚你想用这些数据做什么;)

  char s[1024];
  // How can I fill s, from obj.getName() ?

  // Name of the method
  static char method_name = "getName";
  // No arguments? Score! We just need NULL here
  char method_fmt_string = NULL;

  PyObject *objname = PyObject_CallMethod(obj, obj_method, method_fmt_string);
  // This is really important! What we have here now is a Python object with a newly
  // incremented reference count! This means you own it, and are responsible for
  // decrementing the ref count when you're done. See below.

  // If there's a failure, we'll get NULL
  if(objname == NULL)
  {
    // Again, this should just propagate the exception information
    return NULL;
  }

现在,在字符串/字节对象部分的具体对象层文档中有很多函数,你可以选择最适合你的那个。

不要忘记这一点:

  // Now that we're done with the object we obtained, decrement the reference count
  Py_XDECREF(objname);

  // You didn't mention whether you wanted to return a value from here, so let's just
  // return the "None" singleton.
  // Note: this macro includes the "return" statement!
  Py_RETURN_NONE;
}

注意这里使用了Py_RETURN_NONE,并且注意它不是return Py_RETURN_NONE

PS:这段代码的结构在很大程度上是由个人风格决定的(例如,早期返回、函数内部的static char格式字符串、初始化为NULL)。希望重要的信息除了风格约定之外,能清晰易懂。

撰写回答