我有这个创建文件的简单 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
最佳答案
您的代码中至少有两个严重错误,也许更多。您需要检查您正在调用的所有函数的返回值,否则您会错过它们。
两个显而易见的问题是:
- 您未能传递
PyObject_CallFunctionObjArgs
要求的NULL
标记;该调用必须传递NULL
的最终参数,以用作指示可变长度参数列表结束位置的标记。PyObject *myResult = PyObject_CallFunctionObjArgs(myFunction, args);
应该是PyObject *myResult = PyObject_CallFunctionObjArgs(myFunction, args, NULL);
- 你的函数需要一个参数,
k
,它以一种明确期望它是 Pythonint
的方式使用,但你明确地构建了一个 Pythonstr
代替;当for i in range(0, len(line) - k + 1):
到达时,它会立即引发一个TypeError
。PyObject* 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/