Я работаю над переносом основных функций проекта с Python на C, но хочу сохранить возможность вызова модульных компонентов Python. Поэтому я встраиваю интерпретатор Python в свой проект. Я дошел до того, что вызовы могут выполняться как на стороне C, так и на стороне Python на другой конец, а общие данные преобразуются и правильно обрабатываются.

Однако мне кажется, что я не могу переопределить методы в моих определенных классах Python. Скорее всего, ошибок нет, однако self не установлен для моего PyCFunction.

Базовый пример кода выглядит так:

component.py

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


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

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

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})

Итак, здесь, после того, как я построю и запустил это, я получаю результат:

Override method 0x0

Вызывается метод переопределения, но экземпляр не передается в качестве первого аргумента self. Я предполагаю, что делаю что-то не так, привязывая метод к экземпляру. Как бы то ни было, я также пробовал аналогичные пути выполнения, задав для метода через PyInstanceMethod_New значение pCompObj, а не экземпляра. Но несмотря ни на что, я не уверен, как правильно передать значение self.

2
David Walker 27 Ноя 2017 в 06:31

1 ответ

Лучший ответ

После продолжительного воспроизведения и, наконец, просмотра исходного кода CPython (https: // github.com/python/cpython/blob/master/Objects/methodobject.c) стало очевидно, что использование PyMethod_New(PyObject *func, PyObject *self) и установка этого возврата неверны. Вызов PyCFunction принимает два аргумента, последний из которых используется для self. Итак, в моем примере, измененном на следующее, мы добились успеха:

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);

Кажется, это именно то, что я собираюсь сделать, однако любой, кто более разбирается в Python C API, может дать «более правильный» ответ.

0
David Walker 27 Ноя 2017 в 14:53