C语言

C程序调用python脚本脱坑指南

Jaydon · 10月19日 · 2018年 ·

C语言调用python脚本步骤:

1、Linux下先要配置好python环境

2、写好一个python脚本

3、写好一个C程序,并且在编译的时候要设置编译环境

  • 设置编译环境,其实就是设置python的头文件和库文件目录
  • 初始化python解释器:Py_Initialize()
  • 调用python脚本中的函数
  • 释放资源,就是关闭python: Py_Finalize()

我们先来基本的调用,先写一个python代码

def add(a,b):
	print("运行了python!")
	return a+b

然后写C调用python的代码

#include<stdio.h>
#include<stdlib.h>
//#include "C:/Python27/include/python.h"
//#pragma comment(lib, "C:\\Python27\\libs\\python27.lib") 
//若在编译器设置了include和lib路径以上两行可以转化为
#include<Python.h>
int main(char argc,char **argv)
{
//初始化Python
//在使用Python系统前,必须使用Py_Initialize对其进行初始化。
//它会载入Python的内建模块并添加系统路径到模块搜索路径中。
    PyObject *pName,*pModule,*pDict,*pFunc,*pArgs,*pRetVal;
    Py_Initialize();
    if(!Py_IsInitialized())
    {
        return -1;
    }
//载入名为pytest的脚本,(注意:不是pytest.py)    
    pName = PyString_FromString("pytest"); 
    pModule = PyImport_Import(pName);
    if(!pModule)
    {
        printf("can't find pytest.py");
        getchar();
        return -1;
    }
    pDict = PyModule_GetDict(pModule);
    if(!pDict)
    {
        return -1;
    }
//找出函数名为add的函数
    pFunc = PyDict_GetItemString(pDict,"add");
    if(!pFunc || !PyCallable_Check(pFunc))
    {
        printf("can't find function [add]");
        getchar();
        return -1;
    }
//参数进栈
    pArgs = PyTuple_New(2);
//下面的是重点了,从c/c++传参数到Python中时需要用到一个函数,原型为
//PyObject* Py_BuildValue(char *format,……)
//类似c的printf,但格式不同。常见的格式有
//s 字符串
//i 整形变量
//f 浮点型
//o Python对象
    PyTuple_SetItem(pArgs,0,Py_BuildValue("l",3));
    PyTuple_SetItem(pArgs,1,Py_BuildValue("l",4));
//调用Python函数
    pRetVal = PyObject_CallObject(pFunc,pArgs);
    printf("function return value: %ld\r\n",PyInt_AsLong(pRetVal));
    Py_DECREF(pName);  
    Py_DECREF(pArgs);  
    Py_DECREF(pModule);  
    Py_DECREF(pRetVal);  
// 关闭Python  
    Py_Finalize();  
    return 0;  
}

关于编译运行等常见问题在文章最后,建议你先看一下!

---------------------------------------------------------------------我是分割线----------------------------------------------------------------------------

基础调用可以之后我们来调用多个函数

先写一个python脚本

#定义一个传参的函数
def print_arg(str): 
    print str 

#定义一个传多个参数且有返回值的函数   
def add(a,b): 
    print 'a=', a 
    print 'b=', b 
    return a + b 

#定一个类及类的一个方法fun(传参数,有返回值)   
class Class_A: 
    def __init__(self): 
        print "init" 
    def fun(self, str): 
        print 'hello', str 
        return str 
   
#定义一个类及类的一个方法check(传多个参数,返回值是个元祖)   
class Class_B: 
    def __init__(self): 
        '''''
        ''' 
        self._run = True 
   
    #must rewrite function 
    def check(self,site,port): 
        '''''
        ''' 
        print "Exploiting Host:%s,Port:(%d)......" % (site,port) 
        flag = 1 
        if flag: 
            content={"flag":1,"content":"POST http://www.baidu.com/shell.php (cmd)"} 
        else: 
            content={"flag":0,"content":"POST http://www.baidu.com/shell.php (cmd)"} 
        return content 

if __name__=="__main__": 
    site="www.baidu.com" 
    port=80 
    obj = Calss_B() 
    ret=obj.check(site,port) 
    print ret

然后写一个C语言测试程序

#include <Python.h> 
   
int main(int argc, char* argv[]) 
{ 
    test(); 
    test1(); 
    test2(); 
    test3(); 
    test4(); 
   
    return 0; 
}

逐个分析:

导出当前环境变量

void getcurrent() 
{ 
    PyRun_SimpleString("import sys"); 
    PyRun_SimpleString("sys.path.append('./')"); 
    return; 
}

最基本的调用方式

void test() 
{ 
    Py_Initialize();//初始化python 
    PyRun_SimpleString("print 'hello python'");//直接运行python代码 
    Py_Finalize(); //释放python 
    return; 
} 
// 分析:直接运行python 代码,在调用的时候必须先做初始化操作(Py_Initialize),调用完后做清理工作(Py_Finalize)

调用模块中的一个普通函数

void test1() 
{ 
    Py_Initialize();//初始化python 
    getcurrent(); 
   
    PyObject *pModule = NULL, *pFunc = NULL, *pArg = NULL; 
    pModule = PyImport_ImportModule("demo");//引入模块 
    pFunc = PyObject_GetAttrString(pModule, "print_arg");//直接获取模块中的函数 
    pArg = Py_BuildValue("(s)", "hello_python"); //参数类型转换,传递一个字符串。将c/c++类型的字符串转换为python类型,元组中的python类型查看python文档 
    PyEval_CallObject(pFunc, pArg); //调用直接获得的函数,并传递参数 
   
    Py_Finalize(); //释放python 
    return; 
} 
// 分析:先引用模块(PyImport_ImportModule),然后获取模块中的函数(PyObject_GetAttrString),
// 对c传入python 的参数做类型转换(Py_BuildValue("(s)","hello_python")),最后直接调用函数并传递参数(PyEval_CallObject)。

调用模块中的一个函数(多参数,带返回值)

void test2() 
{ 
    Py_Initialize(); 
    getcurrent(); 
   
    PyObject *pModule = NULL, *pDict = NULL, *pFunc = NULL, *pArg = NULL, *result = NULL; 
    pModule = PyImport_ImportModule("demo"); //引入模块 
    pDict = PyModule_GetDict(pModule); //获取模块字典属性 //相当于Python模块对象的__dict__ 属性,得到模块名称空间下的字典对象 
    pFunc = PyDict_GetItemString(pDict, "add"); //从字典属性中获取函数 
    pArg = Py_BuildValue("(i, i)", 1, 2); //参数类型转换,传递两个整型参数 
    result = PyEval_CallObject(pFunc, pArg); //调用函数,并得到python类型的返回值 
    int sum; 
    PyArg_Parse(result, "i", &sum); //将python类型的返回值转换为c/c++类型 
    printf("sum=%d\n", sum); 
   
    Py_Finalize(); 
}

调用模块中简单的一个类(单个返回值)

void test3() 
{ 
    Py_Initialize(); 
    getcurrent(); 
   
    PyObject *pModule = NULL, *pDict = NULL, *pClass = NULL, *pInstance = NULL, *result = NULL; 
    pModule = PyImport_ImportModule("demo"); //引入模块 
    pDict = PyModule_GetDict(pModule); //获取模块字典属性 
    pClass = PyDict_GetItemString(pDict, "Class_A"); //通过字典属性获取模块中的类 
    pInstance = PyInstance_New(pClass, NULL, NULL);//实例化获取的类 
    result = PyObject_CallMethod(pInstance, "fun", "(s)", "python_000"); //调用类的方法 
    char* name=NULL; 
    PyArg_Parse(result, "s", &name); //将python类型的返回值转换为c/c++类型 
    printf("%s\n", name); 
   
    Py_Finalize(); 
}

调用模块中一个简单的类(返回值是个元组)

void test4()
{
    Py_Initialize();
    getcurrent();

    PyObject *pModule = NULL, *pDict = NULL,*pClass = NULL, *pInstance = NULL, *result = NULL;
    pModule =PyImport_ImportModule("demo"); //引入模块
    pDict = PyModule_GetDict(pModule); //获取模块字典属性
    pClass = PyDict_GetItemString(pDict,"Class_B"); //通过字典属性获取模块中的类
    pInstance = PyInstance_New(pClass, NULL,NULL);
    result = PyObject_CallMethod(pInstance,"check", "(s,i)", "www.baidu.com", 80);
    int flag;
    char*content = NULL;
    PyObject *obj_content =PyDict_GetItemString(result, "content");
    content = PyString_AsString(obj_content);
    PyObject *obj_flag =PyDict_GetItemString(result, "flag");
    flag = PyInt_AsLong(obj_flag);
    printf("content: %s, flag: %d\n",content, flag);

    Py_Finalize();
 
}

-----------------------------------------------------------------我又是一条分割线----------------------------------------------------------------------

好了 通过上面那些函数基本就能满足大部分调用了,接下来说说遇到的坑

1、头文件引用,网上大多数教程都是说#include<Python.h>就行,但是我测试了无数遍就是不行!!!!总是会提示“出现xxxx的未定义引用”或者就提示“Python.h” No such file or directory”等等找不到文件的问题;

解决办法:找到Python.h文件,引用的时候把他的整个文件路径都引用进来

2、提示 ImportError: No module named site

python在Linux中的环境变量问题,编译能通过,运行就提示这个,解决办法是输入下面临时添加环境变量语句

export PYTHONPATH=$PYTHONPATH:/usr/lib/python2.7

3、gcc 编译的时候一定要引入python路径

gcc -I/usr/include/python2.7 -L/usr/lib/python2.7 -Wall -lpython2.7 -fPIC test.c -o test

4、还有好多想不起来了。。。你遇到了自己百度吧,顺便给我留个言,我记录下来

1 条回应
  1. 粉丝2018-11-4 · 10:28

    加油哦!博主很棒!写的内容很好!!