简体   繁体   English

使用Python3 C API覆盖对象方法

[英]Overriding object method with the Python3 C API

I'm working on porting the core functionality of a project from python to C, but want to retain the ability to call into modular Python components. 我正在努力将项目的核心功能从python移植到C,但希望保留调用模块化Python组件的功能。 So I'm embedding the python interpreter in my project. 所以我将python解释器嵌入到我的项目中。 I've gotten to the point where calls can be made on both the C, and Python side to the other end, and shared data is converted and correctly handled. 我已经到了可以在C端和Python端进行另一端调用,共享数据被转换并正确处理的地步。

However, I seem unable to override methods in my defined Python classes. 但是,我似乎无法覆盖定义的Python类中的方法。 Rather, there's no errors, however self is not set for my PyCFunction . 相反,有没有错误,但是self未设置为我PyCFunction

Basic example code is like: 基本示例代码如下:

component.py component.py

class Component(object):
  def my_function(self):
    print("orig function")


class CompObj(Component):
  def test(self):
    self.my_function()

example.c example.c

#include <Python.h>

static PyObject *my_function_override(PyObject *self, PyObject *args) {
  printf("Override method %p\n", self);
  return Py_None;
}

PyMethodDef override_method = {
  "my_function",
  (PyCFunction)my_function_override,
  METH_VARARGS,
  NULL
};

int main () {
  wchar_t *program;
  PyObject *sys_path,  *module_path;
  PyObject *pModule, *pCompObj, *pCompObjInst;
  PyObject *cfunc, *cmeth;

  program = Py_DecodeLocale("exmaple", NULL);
  Py_SetProgramName(program);
  Py_InitializeEx(0);

  sys_path = PySys_GetObject("path");
  module_path = PyUnicode_FromString("/Users/dwalker/ex");
  PyList_Append(sys_path, module_path);
  Py_DECREF(module_path);

  if ((pModule = PyImport_ImportModule("component")) == NULL) {
    printf("No Module\n");
    return -1;
  }

  pCompObj = PyObject_GetAttrString(pModule, "CompObj");
  pCompObjInst = PyObject_CallFunction(pCompObj, NULL);

  cfunc = PyCFunction_New(&override_method, NULL);
  cmeth = PyMethod_New(cfunc, pCompObjInst);

  PyObject_SetAttrString(pCompObjInst, "my_function", cmeth);
  PyObject_CallMethod(pCompObjInst, "test", NULL);

  Py_Finalize();
  PyMem_RawFree(program);

  return 0;
}

CMakeLists.txt CMakeLists.txt

set(SRC_CORE
  example.c
)

add_executable(example ${SRC_CORE})

set_property(TARGET example PROPERTY C_STANDARD 90)

find_package(PythonInterp)
find_package(PythonLibs)
include_directories(${PYTHON_INCLUDE_PATH})
target_link_libraries(example ${PYTHON_LIBRARIES})

So, here after I build and run this I get the output: 因此,在构建并运行此代码后,我得到了输出:

Override method 0x0

The override method is being called, but the instance is not being passed as the first self argument. 正在调用重写方法,但是没有将实例作为第一个self变量传递。 I assume I'm doing something wrong binding the method to the instance. 我认为我在将方法绑定到实例上做错了。 For what it's worth, I've also tried similar execution paths, by setting the method via PyInstanceMethod_New to pCompObj and not the instance. 对于它的价值,我还尝试了类似的执行路径,方法是通过PyInstanceMethod_New将方法设置为pCompObj而不是实例。 But no matter what, I'm unsure how to get the self value correctly passed. 但是无论如何,我不确定如何正确传递self价值。

After more playing, and finally looking at the CPython source ( https://github.com/python/cpython/blob/master/Objects/methodobject.c ) it became obvious that using PyMethod_New(PyObject *func, PyObject *self) and setting this return was not correct. 经过更多的玩耍,最后查看了CPython源代码( https://github.com/python/cpython/blob/master/Objects/methodobject.c ),很明显,使用PyMethod_New(PyObject *func, PyObject *self)和设置此返回值不正确。 The PyCFunction call accepts two arguments, the latter being the instance to use for self. PyCFunction调用接受两个参数,后者是用于self的实例。 So in my example, changed to the following we have success: 因此,在我的示例中,更改为以下内容即可成功:

pCompObj = PyObject_GetAttrString(pModule, "CompObj");
pCompObjInst = PyObject_CallFunction(pCompObj, NULL);

cfunc = PyCFunction_New(&override_method, pCompObjInst);

PyObject_SetAttrString(pCompObjInst, "my_function", cfunc);
Py_DECREF(cfunc);

So this seems to be what I'm intending to do, however, anyone much more versed in the Python C API can follow up with a "more correct" answer. 因此,这似乎是我打算做的事情,但是,任何精通Python C API的人都可以跟进“更正确的”答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM