C/C++调用Python3 | VS2017配置流程

10 篇文章 0 订阅

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Hsin96/article/details/94622553
 系统环境:
    Windows10
    Python3.6
    Visual Studio 2017
1
2
3
4
在C/C++中调用Python,网上很多文章都有简单的介绍,但是很多都不全面或已失效。本文将我自己在配置过程中的主要流程和遇到的问题记录下来,以供参考。

1、PYTHONHOME
检查系统的环境变量中是否已有PYTHONHOME一项,如果缺失则需要补上,否则会导致之后在C程序中调用Py_Initialize()时遇到Python初始化错误:

PYTHONHOME项的值为系统中Python所在的根目录,以本人系统中的环境变量为例:

或:可以在Py_Initialize()之前通过Py_SetPythonHome()函数来设定Python路径,如下:

wchar_t* c2w(const char *c)
{
    const size_t cSize = strlen(c) + 1;
    wchar_t* wc = new wchar_t[cSize];
    mbstowcs(wc, c, cSize);
    return wc;
}

Py_SetPythonHome(c2w(“my/python/home”));
Py_Initialize();
1
2
3
4
5
6
7
8
9
10
还要注意Py_SetPythonHome()的参数是wchar_t类型的,不能直接传char字符串,需要经过转换。

2、64位 v.s. 32位
在visual studio中配置Python,需要与Python的系统版本相对应,比如64位的Python以vs中x86的配置来调试,也是会报错的,必须用x64的配置才能正确运行。

查看Python是64位还是32位:

在上图中的位置,64位会显示64 bit,32位会显示32 bit。本人系统中的Python是64位的,以下以64位为例。

3、visual studio中的属性设置
主要是把包含目录和库目录地址加入到项目中。

3.1 包含目录
包含目录就是Python根目录下的include文件夹,Python.h和其它必需的头文件就在该文件夹下。


3.2 库目录
库目录是Python根目录下的libs文件夹,是python36.lib等所在的文件夹。如果该文件夹下没有python36_d.lib,可复制python36.lib并重命名为python36_d.lib备用。

或:包含目录和库目录也可以直接在下图所示的位置添加,与上文中效果是一致的(细节上的区别可参考该文章)。


3.3 添加依赖项
接下来添加依赖项,这里release和debug的配置是不一样的,需要分别配置。

3.3.1 release配置


3.3.2 debug配置

上面的python36.lib和python36_d.lib已经在3.2节提到过。
*实际上,依赖项添加与否好像没什么用,实测不添加依赖项,程序仍然可以成功运行。不过保险起见还是加上这一步。

4、代码测试
测试用代码来自该博客。原博客的代码是基于Python2,这里都改为了基于Python3的版本。

4.1 Python代码
将以下代码保存在一个文件中,命名为pytest.py。

#test function: pytest.py
def add(a,b):  
    print("in python function add")
    print("a = " + str(a))
    # print("b = " + str(b))
    # print("ret = " + str(a+b))
    # return  
  # 
def foo(a):  
  
    print("in python function foo")
    print("a = " + str(a))
    # print("ret = " + str(a * a))
    # return   
  # 
class guestlist:  
    def __init__(self):  
        print("aaaa")
    def p():  
      print("bbbbb")
    def __getitem__(self, id):  
      return "ccccc"  
def update():  
    guest = guestlist()  
    print(guest['aa'])
  
update()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
4.2 C++代码
visual studio中新建一个c++空项目,新建一个源文件,导入以下代码:

#include <Python.h>  

int main(int argc, char** argv)
{
    // 初始化Python  
    //在使用Python系统前,必须使用Py_Initialize对其  
    //进行初始化。它会载入Python的内建模块并添加系统路  
    //径到模块搜索路径中。这个函数没有返回值,检查系统  
    //是否初始化成功需要使用Py_IsInitialized。  
    Py_Initialize();

    // 检查初始化是否成功  
    if (!Py_IsInitialized()) {
        return -1;
    }
    // 添加当前路径  
    //把输入的字符串作为Python代码直接运行,返回0  
    //表示成功,-1表示有错。大多时候错误都是因为字符串  
    //中有语法错误。  
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("print('---import sys---')");
    PyRun_SimpleString("sys.path.append('./')");

    // 输出当前目录
    PyRun_SimpleString("import os");
    PyRun_SimpleString("print('pwd:', os.getcwd())");

    PyRun_SimpleString("import pytest");

    PyObject *pName, *pModule, *pDict, *pFunc, *pArgs;

    // 载入名为pytest的脚本  
    pName = PyUnicode_FromString("pytest");  // Python2中为PyString_FromString()
    pModule = PyImport_Import(pName);
    if (!pModule) {
        printf("can't find pytest.py");
        getchar();
        return -1;
    }
    pDict = PyModule_GetDict(pModule);
    if (!pDict) {
        return -1;
    }

    // 找出函数名为add的函数  
    printf("----------------------\n");
    pFunc = PyDict_GetItemString(pDict, "add");
    if (!pFunc || !PyCallable_Check(pFunc)) {
        printf("can't find function [add]");
        getchar();
        return -1;
    }

    // 参数进栈  
    *pArgs;
    pArgs = PyTuple_New(2);

    //  PyObject* Py_BuildValue(char *format, ...)  
    //  把C++的变量转换成一个Python对象。当需要从  
    //  C++传递变量到Python时,就会使用这个函数。此函数  
    //  有点类似C的printf,但格式不同。常用的格式有  
    //  s 表示字符串,  
    //  i 表示整型变量,  
    //  f 表示浮点数,  
    //  O 表示一个Python对象。  

    PyTuple_SetItem(pArgs, 0, Py_BuildValue("l", 3));
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("l", 4));

    // 调用Python函数  
    PyObject_CallObject(pFunc, pArgs);

    //下面这段是查找函数foo 并执行foo  
    printf("----------------------\n");
    pFunc = PyDict_GetItemString(pDict, "foo");
    if (!pFunc || !PyCallable_Check(pFunc)) {
        printf("can't find function [foo]");
        getchar();
        return -1;
    }

    pArgs = PyTuple_New(1);
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("l", 2));

    PyObject_CallObject(pFunc, pArgs);

    printf("----------------------\n");
    pFunc = PyDict_GetItemString(pDict, "update");
    if (!pFunc || !PyCallable_Check(pFunc)) {
        printf("can't find function [update]");
        getchar();
        return -1;
    }
    pArgs = PyTuple_New(0);
    PyTuple_SetItem(pArgs, 0, Py_BuildValue(""));
    PyObject_CallObject(pFunc, pArgs);

    Py_DECREF(pName);
    Py_DECREF(pArgs);
    Py_DECREF(pModule);

    // 关闭Python  
    Py_Finalize();
    system("pause");
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
需要注意的是,Python2中的PyString_FromString(),到了Python3中应修改为PyUnicode_FromString(),而不是网上一些资料中提到的PyBytes_FromString()。如上面程序中33行的pName = PyUnicode_FromString("pytest");。

在visual studio中调试时,将pytest.py放到.cpp文件所在目录即可。如果使用生成的.exe文件,需要将pytest.py放到.exe文件所在目录。

成功运行的界面如下图所示:


补充:
上面调用python类的部分其实是有问题的,实例化的时候python类的构造函数并没有被真正调用,对类中各方法的操作本质上与普通函数无异,当遇到类中成员变量的传递就会遇到问题。
正确的python类调用方法可以参考这篇博客,主要流程如下:

// 得到类的构造函数
PyObject* pConstruct = PyInstanceMethod_New(pClass);
// 调用构造函数使其实例化
PyObject* pIns = PyObject_CallObject(pConstruct, nullptr);  // 需传参时将nullptr替换为参数
// 调用类的方法
PyObject_CallMethod(pIns,"some method", nullptr);  // 需传参时将nullptr替换为参数
1
2
3
4
5
6
5、一些参考网站
[1]https://www.cnblogs.com/yanzi-meng/p/8066944.html
[2]https://blog.csdn.net/hnlylyb/article/details/89498651
[3]https://docs.python.org/3.6/c-api/
[4]https://docs.python.org/3/extending/embedding.html#pure-embedding
————————————————
版权声明:本文为CSDN博主「Hsin96」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Hsin96/article/details/94622553

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值