python - 调用在 c 中创建文件的 python 函数

标签 python c

我有这个创建文件的简单 python 函数:

kmer_counter.py

def counter(k):

  list_kmer = []
  freq = {}

  reader = open("out/clean_read.txt", 'r')

  while True:
    line = reader.readline().rstrip()

    if not line:
      break

    for i in range(0, len(line) - k + 1):
      kmer = line[i : i + k]

      if kmer in freq:
        freq[kmer] += 1
      else:
        freq[kmer] = 1

  reader.close()

  freq = {key:val for key, val in freq.items() if val != 1}

  writer = open("out/kmer.txt", 'w')

  for key in freq.keys():
    writer.write(key + '\n')

  writer.close()

我尝试使用 Python.h 头文件在 C 程序中调用它们。我没有任何类型的错误,但是在主程序结束时,没有创建文件。 这是 main.c 文件:

#include <stdio.h>
#include <stdlib.h>

#if defined(__APPLE__) || defined(__MACH__)
    #include <Python/Python.h>
#elif defined(unix) || defined(__unix__) || defined(__unix)
    #include <Python.h>
    #include <stdint.h>
#endif

int main(void) {
    Py_SetProgramName(argv[0]);
    Py_Initialize();

    PyObject* myModuleString = PyUnicode_FromString((char *) "kmer_counter");
    PyObject* myModule = PyImport_Import(myModuleString);
    PyObject* myFunction = PyObject_GetAttrString(myModule,(char *) "counter");

    PyObject* args = PyUnicode_FromString("5");
    PyObject *myResult = PyObject_CallFunctionObjArgs(myFunction, args);

    Py_Finalize();
}

为了编译文件,我使用标志 -I/usr/include/python2.7 -lpython2.7

我看到了关于这个论点的不同问题,但我没有找到解决这个问题的任何正确方法。

这是文件 clean_read.txt 的内容:

ACCAG
CCAGTG
GTGAAC
CAGTGA
ACCAGT
TGAACG
GAACGGTA
CAGTGTA
AACGGTA
GAACGG
AGTGAACG
AACGGT
CAGTGAA
TGAACGGTA

编辑

我在 PyObject_CallFunctionObjArgs 之后添加了以下代码行,但它没有打印任何内容。

if (myResult) { 
  fputs("result: ", stderr); 
  PyObject_Print(myResult, stderr, 0); 
  putc('\n', stderr); 
}
else { 
    fputs("exception:\n", stderr);
    PyErr_PrintEx(0);
}

strace在此 link 输出

用第一个答案中的代码替换我的主要代码后,这是输出:

This is the report what it outputs:
main begins
Py_SetProgramName: ok
Py_Initialize: ok
PyUnicode_FromString('kmer_counter'): result: r
u'kmer_counter'
PyImport_Import(myModuleString): exception:
ImportError: No module named kmer_counter

最佳答案

您的代码中至少有两个严重错误,也许更多。您需要检查您正在调用的所有函数的返回值,否则您会错过它们。

两个显而易见的问题是:

  1. 您未能传递 PyObject_CallFunctionObjArgs 要求的 NULL 标记;该调用必须传递 NULL 的最终参数,以用作指示可变长度参数列表结束位置的标记。 PyObject *myResult = PyObject_CallFunctionObjArgs(myFunction, args); 应该是 PyObject *myResult = PyObject_CallFunctionObjArgs(myFunction, args, NULL);
  2. 你的函数需要一个参数,k,它以一种明确期望它是 Python int 的方式使用,但你明确地构建了一个 Python str 代替;当 for i in range(0, len(line) - k + 1): 到达时,它会立即引发一个 TypeErrorPyObject* args = PyUnicode_FromString("5"); 应该是 PyObject* args = PyLong_FromLong(5); 以生成正确的类型。

在这两种情况下,错误都发生在您没有检查返回码的最终调用上;类型问题会引发 Python 级别的异常,绕过 Python 函数的其余部分,而 varargs 错误可能会做出不可预测的可怕事情。您需要更严格地检查返回值,并且需要更密切地关注 API 要求。

旁注:在这种特殊情况下,您可以使用 PyObject_CallFunction 来节省一些精力替换相当数量的代码,包括所有错误代码。所有这些:

PyObject* args = PyUnicode_FromString("5");
PyObject *myResult = PyObject_CallFunctionObjArgs(myFunction, args);

可以替换为:

PyObject *myResult = PyObject_CallFunction(myFunction, "i", 5);

这简化了工作(并减少了泄漏的对象数量)。


地址编辑更新:

看起来 kmer_counter.py 既不在您的工作目录中,也不在 sys.path 中的其他任何地方。如果您从命令行运行它,最简单的解决方案是在运行您的可执行文件(不需要位于同一目录)。另一个(有点 hacky)解决方案是创建/扩展 PYTHONPATH 环境变量以包含包含 kmer_counter.py 的路径,例如在 bash 中,您可能会这样做(遵循 your comment 中的路径):

PYTHONPATH=./src/prepros ./app

或者作为双线(其中 export 只需要运行一次):

export PYTHONPATH=./src/prepros
./app

无论如何,为了让您了解您正在做多少不必要的工作,这里简化了您的 C 代码以使用尽可能少的 API 调用(使用 zwol's check_PyAPI 来简化错误检查,尽管实际上代码,您可能希望通过不同于立即死于错误消息的方式来处理一些错误):

int main(void) {
    Py_SetProgramName(argv[0]);
    Py_Initialize();

    // Load module without needing to construct PyUnicode manually
    PyObject* myModule = PyImport_ImportModule("kmer_counter");
    check_PyAPI("PyImport_ImportModule(\"kmer_counter\")", myModule);

    // Call function on module without needing to construct PyUnicode
    // or PyLong, and without needing to load the function itself
    PyObject* myResult = PyObject_CallMethod(myModule, "counter", "i", 5);
    Py_DECREF(myModule);  // Done with module, release reference
    check_PyAPI("PyObject_CallMethod(myModule, \"counter\", \"i\", 5)", myResult);

    // Do whatever you want with successful result

    Py_DECREF(myResult); // Done with result, release reference

    Py_Finalize();
}

你在两个调用中进行的所有五个调用是否起作用(我还在 Py_DECREF 中添加了只是为了监管你的引用计数);当然,如果需要重用各种临时文件,可能值得制作它们一次并一遍又一遍地使用它们,而不是让更高级别的函数一遍又一遍地重建 PyUnicode 或不断地重新查找有问题的功能。但是当您刚开始时,让 Python 为您做更多的工作;稍后优化(由于避免了字节码解释器开销,您已经比 Python 级代码更快)。

关于python - 调用在 c 中创建文件的 python 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55853322/

相关文章:

python - NumPy:填充数组中 1 周围的字段

c - 是否可以使用跟踪服务器来建立我自己的点对点网络?

c - 如何在 C 中分离缓冲区结果

c - 无法理解以下宏

c - *(void **) (&funcp) 在这行涉及 dlsym() 的代码中做了什么?

python - 在 python os 库中,进程被阻止

python - 使用 Python RegEx re.findall 解析文本

Python:从函数中压平包含另一个元组的元组的最简单方法

python - 以参数作为列表的 neo4j 密码性能

C - 在 Sqlite 数据库中存储 MD5