一、环境准备


假设python安装位置为:C://python310

编译环境为:

  1. 头文件 位置:C://python310//include
  2. lib库 位置:C://python310//libs (需要python310.lib)

运行环境为:

  1. dll动态库 位置:C://python310 (需要python310.dll)
  2. pyd扩展库 位置:
  3. Python库 位置:C://python310//Lib

二、执行python字符串

  1. void python1() // 执行Python字符串
  2. {
  3. // 设置Python的home路径
  4. Py_SetPythonHome(L"D:\\CPP\\CppCallPython\\Python");
  5. // Python解释器初始化
  6. Py_Initialize();
  7. int ret = 0;
  8. // 执行Python脚本字符串,执行在 __main__ 模块
  9. ret = PyRun_SimpleString("print('hello world')");
  10. ret = PyRun_SimpleString("print(f'__name__ = {__name__}')");
  11. // 都是执行在__main__模块下的,所以可以访问变量
  12. ret = PyRun_SimpleString("a=10");
  13. ret = PyRun_SimpleString("print(a)");
  14. if (ret != 0) {
  15. PyErr_Print(); // 输出错误信息
  16. }
  17. //
  18. Py_Finalize();
  19. }
  1. 需要先设置python的home目录,此步骤必须在初始化python解释器之前调用。
  2. 初始化python解释器
  3. 执行python脚本,通过PyRun_SimpleString()
    1. python脚本字符串都执行在__main__模块,相当于 C++ 的 void main(int argc, char* argv[]){ }
    2. 即使先通过PyRun_SimpleString()设置一个变量,后面也是可以调用该变量的
    3. PyRun_SimpleString() 会返回执行状态,是否执行成功的标识,0代表成功,1代表失败
  4. 最后调用 Py_Finalize() 销毁python解释器

三、执行python脚本

python文件:python_scripy.py

if __name__ == "__main__":
    print("this is a python file")

C++代码:

void python2() // 执行Python文件
{
    // 设置Python的home路径
    Py_SetPythonHome(L"D:\\CPP\\CppCallPython\\Python");

    // Python解释器初始化
    Py_Initialize();

    const char* filename = "python_scripy.py";
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        cout << "open file " << filename << " failed.";
    }
    int ret = PyRun_AnyFile(fp, filename);
    if (ret != 0) {
        PyErr_Print();
        cout << "PyRun_AnyFile failed!" << endl;
        Py_Finalize();
        return;
    }
    Py_Finalize();
}
  1. 需要先设置python的home目录,此步骤必须在初始化python解释器之前调用。
  2. 初始化python解释器
  3. 通过PyRun_AnyFile()打开文件,第一个参数为FILE*,第二个参数为文件名。
  4. 最后调用 Py_Finalize() 销毁python解释器

四、获取python脚本的变量(以字典为例)

python文件:python_scripy.py

if __name__ == "__main__":
    conf = {
        "width": 1920,
        "height": 1080,
        "title": "C++ call python"
    }

C++代码

void python3() // 执行Python文件并读取字典中元素的值
{ // 设置Python的home路径
    Py_SetPythonHome(L"D:\\CPP\\CppCallPython\\Python");

    // Python解释器初始化
    Py_Initialize();

    // 运行python文件
    const char* filename = "python_scripy.py";
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        cout << "open file " << filename << " failed.";
    }
    int ret = PyRun_AnyFile(fp, filename);
    if (ret != 0) {
        PyErr_Print();
        cout << "PyRun_AnyFile failed!" << endl;
        Py_Finalize();
        return;
    }

    // 获取主模块
    PyObject* __main__ = PyUnicode_FromString("__main__");
    PyObject* module = PyImport_GetModule(__main__); // 不清理参数,需要手动清理
    Py_XDECREF(__main__);

    // 根据模块和名称获取对象->字典
    PyObject* conf = PyObject_GetAttrString(module, "conf");
    if (!conf) {
        cout << " conf not find!" << endl;
    }

    // 从字典中取值
    PyObject* keyWidth = PyUnicode_FromString("width");
    int width = PyLong_AsLong(PyDict_GetItem(conf, keyWidth));
    Py_XDECREF(keyWidth);

    PyObject* keyHeight = PyUnicode_FromString("height");
    int height = PyLong_AsLong(PyDict_GetItem(conf, keyHeight));
    Py_XDECREF(keyHeight);
    cout << "width: " << width << " height: " << height << endl;

    PyObject* keyTitle = PyUnicode_FromString("title");
    wchar_t title[1024] = { 0 };
    int size = PyUnicode_AsWideChar(PyDict_GetItem(conf, keyHeight), title, 1024);
    Py_XDECREF(keyTitle);
    wcout << "size: " << size << " title: " << title << endl;

    Py_XDECREF(conf);
    Py_XDECREF(module);
    Py_Finalize();
}
  1. 需要先设置python的home目录,此步骤必须在初始化python解释器之前调用。
  2. 初始化python解释器
  3. 获取主模块
    1. 通过PyUnicode_FromString("__main__")获取 __main__ 这个变量
    2. __main__这个变量传递给PyImport_GetModule()即可
  4. 通过key获取字典的value
    1. 通过PyObject_GetAttrString()获取字典对象,第一个参数为模块对象,第二个参数为字典变量的名称
    2. 通过PyUnicode_FromString()获取该key对应的value对象
    3. 通过PyLong_AsLong()/PyUnicode_AsWideChar()转换为C++的类型
  5. 最后调用 Py_Finalize() 销毁python解释器

    注:创建一个**PyObject***记得通过**Py_XDECREF()**进行释放

五、调用python的类实例化并访问成员函数和成员变量

python文件:python_scripy.py

class TypePy:

    global_field = 99

    def __init__(self):
        print("TypePy init")

    def test(self):
        print("TypePy test")

    def test_param(self, num:int, value:str):
        print(f"num: {num}, value: {value}")
        return "test_param"

C++代码

void python4() // 调用python的类实例化对象、访问成员函数和成员
{
    // 设置Python的home路径
    Py_SetPythonHome(L"D:\\CPP\\CppCallPython\\Python");

    // Python解释器初始化
    Py_Initialize();

    // 运行python文件
    const char* filename = "python_scripy.py";
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        cout << "open file " << filename << " failed.";
    }
    int ret = PyRun_AnyFile(fp, filename);
    if (ret != 0) {
        PyErr_Print();
        cout << "PyRun_AnyFile failed!" << endl;
        Py_Finalize();
        return;
    }

    // 获取主模块
    PyObject* __main__ = PyUnicode_FromString("__main__");
    PyObject* module = PyImport_GetModule(__main__); // 不清理参数,需要手动清理
    Py_XDECREF(__main__);

    // 1.获取类
    PyObject* typePy = PyObject_GetAttrString(module, "TypePy");
    if (!typePy) {
        cout << "get class TypePy failed!" << endl;
        return;
    }

    // 2.实例化对象, 即调用构造函数__init__。
    PyObject* object = PyObject_CallObject(typePy, nullptr);
    if (!object) {
        cout << "create TypePy object failed!" << endl;
    }

    // 3. 调用成员函数(无参数)
    PyObject_CallMethod(object, "test", nullptr);

    // 4. 调用成员函数(带参数并含返回值,如果有返回值需要清理)
    PyObject* func = PyObject_CallMethod(object, "test_param", "is", 100, "student");
    const char* funcRet = PyUnicode_AsUTF8(func);
    cout << "return: " << funcRet << endl;

    // 5. 成员变量
    PyObject* var = PyObject_GetAttrString(object, "global_field");
    cout << "TypePy id=" << PyLong_AsLong(var) << endl;

    Py_XDECREF(var);
    Py_XDECREF(func);
    Py_XDECREF(object);
    Py_XDECREF(typePy);

    // 类
    Py_XDECREF(module);
    Py_Finalize();
}

六、调用python函数并传递list参数及获取返回值

python文件:python_scripy.py

def func_main1():
    print("python func main1")


def func_main2(a_list:list):

    print(f"python func main2-> param:{a_list}")

    return [1,2,3,4,5,6, 8]

C++代码

void python5() // 调用python函数并传递list参数并获取返回
{
    // 设置Python的home路径
    Py_SetPythonHome(L"D:\\CPP\\CppCallPython\\Python");

    // Python解释器初始化
    Py_Initialize();

    // 运行python文件
    const char* filename = "python_scripy.py";
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        cout << "open file " << filename << " failed.";
    }
    int ret = PyRun_AnyFile(fp, filename);
    if (ret != 0) {
        PyErr_Print();
        cout << "PyRun_AnyFile failed!" << endl;
        Py_Finalize();
        return;
    }

    // 获取主模块
    PyObject* __main__ = PyUnicode_FromString("__main__");
    PyObject* module = PyImport_GetModule(__main__); // 不清理参数,需要手动清理
    Py_XDECREF(__main__);

    // 调用python的函数
    PyObject* func1 = PyObject_GetAttrString(module, "func_main1"); // 无参无返回值
    if (func1 && PyCallable_Check(func1)) {
        PyObject_CallObject(func1, 0);
    }
    Py_XDECREF(func1);

    PyObject* func2 = PyObject_GetAttrString(module, "func_main2"); // 带参带返回值
    if (func1 && PyCallable_Check(func1)) {
        // 参数准备 参数变量是tuple
        PyObject* args = PyTuple_New(1); // 参数元组1只有一个参数
        // 传递的list对象
        PyObject* lst = PyList_New(0);
        for (int i = 0; i < 5; i++) {
            PyList_Append(lst, PyLong_FromLong(i + 100));
        }
        // 将list写入参数列表
        PyTuple_SetItem(args, 0, lst);
        // 函数对象和参数 返回对象
        PyObject* ret = PyObject_CallObject(func2, args);
        int size = PyList_Size(ret);
        cout << "size: " << size << endl;

        // 获取返回值
        list<int> a_list;
        for (int i = 0; i < size; i++) {
            PyObject* val = PyList_GetItem(ret, i);
            if (!val)
                continue;
            // printf("[%ld]", PyLong_AsLong(val)); 
            a_list.push_back(PyLong_AsLong(val));
        } 
        for (int item : a_list) {
            cout << item << " ";
        }

        Py_XDECREF(ret);
        Py_XDECREF(args); // lst也在args中销毁
    }

    // 类
    Py_XDECREF(module);
    Py_Finalize();
}

七、给python传递变量的两种方法

python文件:python_scripy.py

if __name__ == "__main__":
    print(f"a={a}")
    print(f"count={count}")

C++代码

//传递位置 要在python代码调用之前
void python6() // 给python传递变量的两种方法
{
    // 设置Python的home路径
    Py_SetPythonHome(L"D:\\CPP\\CppCallPython\\Python");

    // Python解释器初始化
    Py_Initialize();

    // 获取主模块
    PyObject* __main__ = PyUnicode_FromString("__main__");
    PyObject* module = PyImport_GetModule(__main__); // 不清理参数,需要手动清理
    Py_XDECREF(__main__);

    // 方法1. 只能传给主模块(__main__)
    PyRun_SimpleString("a=888");
    // 方法2. 空间转给python管理
    PyObject_SetAttrString(module, "count", PyLong_FromLong(777));

    // 运行python文件
    const char* filename = "python_scripy.py";
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        cout << "open file " << filename << " failed.";
    }
    int ret = PyRun_AnyFile(fp, filename);
    if (ret != 0) {
        PyErr_Print();
        cout << "PyRun_AnyFile failed!" << endl;
        Py_Finalize();
        return;
    }

    // 类
    Py_XDECREF(module);
    Py_Finalize();
}

八、给python传递函数

python文件:python_scripy.py

if __name__ == "__main__":
    test_cfun()

C++代码

static PyObject* test_cfun(PyObject* self, PyObject* args)
{
    cout << "in c++ call test_cfun function" << endl;
    Py_RETURN_TRUE;
}
//传递位置 要在python代码调用之前
void python7() // 给python传递函数和类 PyModule_Ad
{
    // 设置Python的home路径
    Py_SetPythonHome(L"D:\\CPP\\CppCallPython\\Python");

    // Python解释器初始化
    Py_Initialize();

    // 获取主模块
    PyObject* __main__ = PyUnicode_FromString("__main__");
    PyObject* module = PyImport_GetModule(__main__); // 不清理参数,需要手动清理
    Py_XDECREF(__main__);

    // 给python传递函数
    PyMethodDef cfunc[] = {
        { "test_cfun", test_cfun, METH_VARARGS, 0 },
        { nullptr }
    };

    PyModule_AddFunctions(module, cfunc);

    // 运行python文件
    const char* filename = "python_scripy.py";
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        cout << "open file " << filename << " failed.";
    }
    int ret = PyRun_AnyFile(fp, filename);
    if (ret != 0) {
        PyErr_Print();
        cout << "PyRun_AnyFile failed!" << endl;
        Py_Finalize();
        return;
    }

    // 类
    Py_XDECREF(module);
    Py_Finalize();
}

八、给python传递类

pass

九、读取python模块并给python主模块传递模块

python文件:python_scripy.py

if __name__ == "__main__":
    testmod.testmod()

python文件:testmod.py

print("this is testmod")

def testmod():
    print("testmod function")

C++代码

void python8() // 读取python模块并给python主模块传递
{
    // 设置Python的home路径
    Py_SetPythonHome(L"D:\\AB_Code\\CPP\\CppCallPython\\Python");

    // Python解释器初始化
    Py_Initialize();

    // 获取主模块
    PyObject* __main__ = PyUnicode_FromString("__main__");
    PyObject* module = PyImport_GetModule(__main__); // 不清理参数,需要手动清理
    Py_XDECREF(__main__);

    // 读取模块 脚本文件直接当做模块
    PyObject* testmod = PyImport_ImportModule("testmod");
    if (!testmod) {
        cout << "PyImport_ImportModule testmod failed" << endl;
        return;
    }

    PyModule_AddObject(module, "testmod", testmod);

    // 运行python文件
    const char* filename = "python_test.py";
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        cout << "open file " << filename << " failed.";
    }
    int ret = PyRun_AnyFile(fp, filename);
    if (ret != 0) {
        PyErr_Print();
        cout << "PyRun_AnyFile failed!" << endl;
        Py_Finalize();
        return;
    }

    // 类
    Py_XDECREF(module);
    Py_Finalize();
}

注意:testmod.py需要和可执行文件位于同一目录,否则会导入模块失败