“探坑”——在C++中執行python指令碼的艱辛嘗試
之前的一篇部落格提到,喵哥打算用C++呼叫python,然後在python中執行powershell,但是在初次嘗試後,喵哥發現這不是一個簡單的工作,有很多坑需要注意!此篇文章主要圍繞如何在C++中呼叫python(windows10)來陳述喵哥遇到的一些初學者需要留心的地方。
在VS2013中配置python環境
跟大部分環境配置差不多,主要是在專案里加入標頭檔案和庫檔案。需要注意的是VS的專案的位數要跟python一致,不然會出現“無法解析的外部符號”。
1.新增標頭檔案(.h)和庫檔案(.lib)的路徑
在“VC++目錄”下的包含目錄和庫目錄分別輸入對應python的標頭檔案和庫檔案路徑,喵哥用的是Anaconda管理python,所以不能按照這個找路徑。通常,只要在python直譯器(python.exe)根目錄下找到“include”和“libs”資料夾即可。
2.新增所需要的庫檔案——.lib和.dll
在上述庫檔案路徑下找到python27.lib,複製全稱到上圖的附加依賴項中,然後在python直譯器的路徑下找到python27.dll,複製這個檔案到VS解決方案生成的可執行檔案路徑下(需要生成解決方案才有這個路徑)。
3.檢查系統環境變數中有無PYTHONHOME變數
檢查系統環境變數有無PYTHONHOME變數,並且這個變數對應的值應該是對應python直譯器的路徑。不然會執行程式碼出錯,雖然可能沒有錯誤顯示。不過一般來說,只要是直接安裝python的話,都有這個變數的。喵哥用的是Anaconda,所以得自己新增。
4.修改python裡的一個頭檔案
通常需要修改的標頭檔案是pyconfig.h,因為在這個標頭檔案裡有這麼一行程式碼:
# ifdef _DEBUG
# pragma comment(lib,"python27_d.lib")
# else
# pragma comment(lib,"python27.lib")
# endif /* _DEBUG */
應該是在Debug時,讀取python27_d.lib,而在release時,讀取python27.lib,python給的只有python27.lib,按理說,只要用release就可以正常執行,但是喵哥遇到的情況是:在release下,還是第一句程式碼有效。。。所以有誰遇到這樣的情況(提示找不到python27_d.lib)不要著急,自己手動修改標頭檔案就好,都改成python27.lib。
另外,喵哥在實際運用中還遇到一些標頭檔案對某些變數重定義的情況,這些基本都是修改標頭檔案,記得標註就好了。
5.例項
python,1.py:
def add(a,b):
print ("in python function add")
print ("a = " + str(a))
print ("b = " + str(b))
print ("ret = " + str(a+b))
return
C++:
#include "stdafx.h"
#include "Python.h"
int _tmain(int argc, _TCHAR* argv[])
{
// 初始化Python
//在使用Python系統前,必須使用Py_Initialize對其
//進行初始化。它會載入Python的內建模組並新增系統路
//徑到模組搜尋路徑中。這個函式沒有返回值,檢查系統
//是否初始化成功需要使用Py_IsInitialized。
Py_Initialize();
// 檢查初始化是否成功
if (!Py_IsInitialized())
{
return -1;
}
// 添加當前路徑
//把輸入的字串作為Python程式碼直接執行,返回0
//表示成功,-1表示有錯。大多時候錯誤都是因為字串
//中有語法錯誤。
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
PyRun_SimpleString("import subprocess");
PyObject *pName, *pModule, *pDict, *pFunc, *pArgs;
// 載入名為1的指令碼
pName = PyString_FromString("1");
pModule = PyImport_Import(pName);
if (!pModule)
{
printf("can't find 1.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);
// PyObject* Py_BuildValue(char *format, ...)
// 把C++的變數轉換成一個Python物件。當需要從
// C++傳遞變數到Python時,就會使用這個函式。此函式
// 有點類似C的printf,但格式不同。常用的格式有
// s 表示字串,
// i 表示整型變數,
// f 表示浮點數,
// O 表示一個Python物件。
PyTuple_SetItem(pArgs, 0, Py_BuildValue("l", 3)); //"l" (integer) [long int] :將C型別的long轉換成Pyhon中的int物件。
PyTuple_SetItem(pArgs, 1, Py_BuildValue("l", 4));
// 呼叫Python函式
PyObject_CallObject(pFunc, pArgs);
//pFunc = PyDict_GetItemString(pDict, "redis");
//pArgs = PyTuple_New(1);
//PyTuple_SetItem(pArgs, 0, Py_BuildValue("")); //
//PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pName);
Py_DECREF(pArgs);
Py_DECREF(pModule);
// 關閉Python
Py_Finalize();
return 0;
}