有时候在写C语言程序的时候又想利用一下python强大的模块,于是C与python的混合编程便应运而生。
下面简单说说在C语言编译环境中调用python脚本文件的基础应用。
以vs2017为例。
首先你要知道你电脑上安装的python环境是64位还是32位,vs的编译平台需要与python环境一致。
比如我的python环境是64位,vs工程就要配置成x64。
右键点击你的解决方案,点击属性,
在属性窗口双击“VC++ Directories”(VC++目录),把在Include Directories (包含目录)和 Library Directories(库目录)下添加python安装路径下的include和ibs文件夹的路径。
在添加之前一定要先确保自己安装了python的debug版本,详见我的另一篇博客【VS2017】“LNK1104 cannot open file ‘python39_d.lib‘
双击“linker”(链接器)下的“Input”,添加python39_d.lib这个依赖项
点击确定则配置完成。
这样在写程序的时候添加Python.h头文件就不会报错,python39_d.lib里的API函数也就可以正常使用了。
主要流程就是:
#include<stdio.h>
#include <Python.h>
int main()
{
PyObject *pName, *pModule, *pDict, *pFunc;
PyObject *pArgs, *pValue;
//待传参数
int time[6]={
1,2,3,4,5,6};
//初始化python
Py_Initialize();
// 检查初始化是否成功
if (!Py_IsInitialized())
{
printf("初始化失败\n");
Py_Finalize();
}
//设置python模块,搜寻位置,文件放在.c文件一起
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
//获取python文件名,导入模块(我这里的py文件是graph.py)
pModule = PyImport_ImportModule("graph");
if (!pModule) {
printf("py文件导入失败\n");
Py_Finalize();
}
else {
//直接获取模块中的函数
pFunc = PyObject_GetAttrString(pModule, "create_graph");
//验证函数是否获取成功
if (!pFunc) {
printf("函数导入失败\n");
Py_Finalize();
}
//将c/c++类型数据转换为python类型,利用元组传递
pArgs = PyTuple_New(6);
pValue = PyLong_FromLong(time[0]);
PyTuple_SetItem(pArgs, 0, pValue);
pValue = PyLong_FromLong(time[1]);
PyTuple_SetItem(pArgs, 1, pValue);
pValue = PyLong_FromLong(time[2]);
PyTuple_SetItem(pArgs, 2, pValue);
pValue = PyLong_FromLong(time[3]);
PyTuple_SetItem(pArgs, 3, pValue);
pValue = PyLong_FromLong(time[4]);
PyTuple_SetItem(pArgs, 4, pValue);
pValue = PyLong_FromLong(time[5]);
PyTuple_SetItem(pArgs, 5, pValue);
//调用直接获得的函数,并传递参数
pValue = PyObject_CallObject(pFunc, pArgs);
//释放python
Py_Finalize();
printf("success");
return 0;
}
}
顺便给出我的graph.py的脚本,这是一个以参数的数值生成对应的excel文件的脚本,这两个源代码是根据我的另一篇关于排序算法的博文改的案例。
# -*- coding:utf-8 -*-
import xlsxwriter
def create_graph(a,b,c,d,e,f):
# 创建一个excel
workbook = xlsxwriter.Workbook("排序算法比较结果.xlsx")
# 创建一个sheet
worksheet = workbook.add_worksheet()
# worksheet = workbook.add_worksheet("bug_analysis")
# 自定义样式,加粗
bold = workbook.add_format({
'bold': 1})
# --------1、准备数据并写入excel---------------
# 向excel中写入数据,建立图标时要用到
headings = ["排序方法", "排序时间"]
data = [["简单选择排序", "直接插入排序", "冒泡排序", "快速排序", "两路合并排序", "堆排序"],[a,b,c,d,e,f]]
# 写入表头
worksheet.write_row('A1', headings, bold)
# 写入数据
worksheet.write_column('A2', data[0])
worksheet.write_column('B2', data[1])
# --------2、生成图表并插入到excel---------------
# 创建一个柱状图(column chart)
chart_col = workbook.add_chart({
'type': 'column'})
# 配置第一个系列数据
chart_col.add_series({
'name': '=Sheet1!$B$1','categories': '=Sheet1!$A$2:$A$7','values': '=Sheet1!$B$2:$B$7','line': {
'color': 'red'},})
# 这里的sheet1是默认的值,因为我们在新建sheet时没有指定sheet名
# 如果我们新建sheet时设置了sheet名,这里就要设置成相应的值
# 设置图表的title 和 x,y轴信息
chart_col.set_title({
'name': "排序算法结果"})
chart_col.set_x_axis({
'name': "排序方法"})
chart_col.set_y_axis({
'name': "花费时间(ms)"})
# 设置图表的风格
chart_col.set_style(1)
# 把图表插入到worksheet以及偏移
worksheet.insert_chart('A10', chart_col, {
'x_offset': 25, 'y_offset': 10})
workbook.close()
return 0
if __name__=="__main__":
create_graph(10, 40, 50, 20, 10, 50)
1、运行Python指令
PyRun_SimpleString("print(os.getcwd(),a)");
pyext.eval(R"(a+='qwer')");
2、加载Python模块
PyObject * pModule =PyImport_ImportModule("tp"); //test:Python文件名,若脚本有错则返回空
PyRun_SimpleString("import os");
3、给Python的变量赋值
对于数值,使用Py_BuildValue:
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}", "abc", 123, "def", 456) {
'abc': 123, 'def': 456}
对于其他数据结构,使用相应的函数设置,例如:
PyObject *pArgs = PyTuple_New(1);
PyObject *pDict = PyDict_New(); //创建字典类型变量
PyDict_SetItemString(pDict, "Name", Py_BuildValue("s", "YQC")); //往字典类型变量中填充数据
PyDict_SetItemString(pDict, "Age", Py_BuildValue("i", 25)); //往字典类型变量中填充数据
PyTuple_SetItem(pArgs, 0, pDict);//0---序号 将字典类型变量添加到参数元组中
构造好对象以后,通过PyObject_SetAttrString来设置进入Python中:
PyObject *ps=PyUnicode_DecodeUTF8(val,strlen(val),"ignore"); //构造了一个对象
PyObject_SetAttrString(p_main_Module,key,ps); //设置
4、获取Python变量的值
首先取得变量的指针,然后通过PyArg_Parse解析
pModule =PyImport_ImportModule("__main__");
pReturn = PyObject_GetAttrString(pModule, "a"); //可以获得全局变量
int size = PyDict_Size(pReturn);
PyObject *pNewAge = PyDict_GetItemString(pReturn, "Age");
int newAge;
PyArg_Parse(pNewAge, "i", &newAge);
对于元组的解析:
int ok;
ok = PyArg_ParseTuple(args, "s", &s); //Python call: f('whoops!')
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s);//Python call: f(1, 2,'three')
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);//Python call: f((1, 2), 'three')
ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);//Python calls:
//f('spam')
//f('spam', 'w')
//f('spam', 'wb', 100000)
5、调用Python函数
PyObject * pfun=PyObject_GetAttrString(pModule, "testdict"); //testdict:Python文件中的函数名
PyObject *pReturn = PyEval_CallObject(pfun, pArgs); //调用函数
6、设置函数让Python调用
首先定义c函数,然后声明方法列表,然后声明模块,然后增加这个模块,最后调用
static int numargs=1890;
static PyObject* emb_numargs(PyObject *self, PyObject *args) //C函数
{
if(!PyArg_ParseTuple(args, ":numargs"))
return NULL;
return PyLong_FromLong(numargs);
}
static PyMethodDef EmbMethods[] = {
//方法列表
{
"numargs", emb_numargs, METH_VARARGS,
"Return the number of arguments received by the process."},
{
NULL, NULL, 0, NULL}
};
static PyModuleDef EmbModule = {
//模块声明
PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_emb(void) //模块初始化函数
{
return PyModule_Create(&EmbModule);
}
//增加模块:
PyImport_AppendInittab("emb", &PyInit_emb); //增加一个模块