本系列將介紹如何用C/C++擴展Python模塊,使用C語言編寫Python模塊,添加到Python中作為一個built-in模塊。Python與C之間的交互目前有幾種方案:
1. 原生的Python C/C++ API, 官網有非常詳細的文檔說明
2. boost python,一個C++的編程框架,對官方API進行了封裝,可以方便的用C++擴展Python模塊,省去了很多諸如引用計數的煩惱。 http://www.boost.org/doc/libs/1_64_0/libs/python/doc/html/index.html
3. Cython. Cython其實是一個可以優化Python程序的編譯器,將Python代碼翻譯成C代碼,然后使用C/C++編譯器進行編譯得到pyd(windows)或so(linux),在Python解釋器中可以直接import。這種方法里面的坑有點多,在本系列博客中會介紹到Cython。 http://cython.org/
4. 谷歌最近開源的CLIF項目,同時支持Python2和Python3,目前來說還不太完善。 https://github.com/google/clif
其他的框架。。。。。。
本篇將介紹如何用官方提供的Python C/C++ API來擴展Python模塊。工欲善其事必先利其器,先講一下如何搭建開發環境(默認已經安裝了Python)。以下為【DEBUG】版的配置:
1. Python版本: Python2.7 32位; 操作系統: Windows7; IDE: Visual Studio 2015。
2. 配置Library Directories、Additional Include Directories、Link Input
3. 寫一段hello world程序 ,先不糾結語法細節
[test.c]
#include <Python.h> static PyMethodDef test_methods[] = { NULL, NULL, 0, NULL }; PyMODINIT_FUNC test_init(void) { Py_InitModule3("test", test_methods, "Common test Written in C."); }
[Souce.cpp]
#include <Python.h> PyMODINIT_FUNC test_init(); int main() { Py_Initialize(); test_init(); return 0; }
然后編譯,很大可能會出現Link時找不到解析符號以及無法找到python27_d.lib,這主要是pyconfig.h里代碼的一些宏導致的。
1)找到python2.7/include/pyconfig.h,並找到
#ifdef _DEBUG #define Py_DEBUG #endif
修改為
#ifdef _DEBUG //#define Py_DEBUG #endif
2)Configuration Properties->C/C++->Preprocessor->Preprocessor Definitions添加MS_NO_COREDLL或Py_NO_ENABLE_SHARED
原因解釋:
1)
在pyconfig.h中:
#ifdef _DEBUG #define Py_DEBUG #endif
在object.h中有以下定義:
/* Py_DEBUG implies Py_TRACE_REFS. */ #if defined(Py_DEBUG) && !defined(Py_TRACE_REFS) #define Py_TRACE_REFS #endif
在modesupport.h中
#ifdef Py_TRACE_REFS /* When we are tracing reference counts, rename Py_InitModule4 so modules compiled with incompatible settings will generate a link-time error. */ #if SIZEOF_SIZE_T != SIZEOF_INT #undef Py_InitModule4 #define Py_InitModule4 Py_InitModule4TraceRefs_64 #else #define Py_InitModule4 Py_InitModule4TraceRefs #endif #endif
所以當定義了Py_DEBUG,就會導致Py_InitModule4重命名為Py_initModule4TraceRefs,而lib中沒有此符號,所以導致了鏈接錯誤。故而注釋掉#define Py_DEBUG這一行即可。
2)找不到python27_d.lib問題
同樣在pyconfig.h中:
/* For Windows the Python core is in a DLL by default. Test Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ #if !defined(MS_NO_COREDLL) && !defined(Py_NO_ENABLE_SHARED) # define Py_ENABLE_SHARED 1 /* standard symbol for shared library */ # define MS_COREDLL /* deprecated old symbol */ #endif /* !MS_NO_COREDLL && ... */ /* All windows compilers that use this header support __declspec */ #define HAVE_DECLSPEC_DLL /* For an MSVC DLL, we can nominate the .lib files used by extensions */ #ifdef MS_COREDLL # ifndef Py_BUILD_CORE /* not building the core - must be an ext */ # if defined(_MSC_VER) /* So MSVC users need not specify the .lib file in their Makefile (other compilers are generally taken care of by distutils.) */ # ifdef _DEBUG # pragma comment(lib,"python27_d.lib") # else # pragma comment(lib,"python27.lib") # endif /* _DEBUG */ # endif /* _MSC_VER */ # endif /* Py_BUILD_CORE */ #endif /* MS_COREDLL */
可以看到如果沒有定義MS_NO_COREDLL和Py_NO_ENABLE_SHARED時,會定義MS_COREDLL,然后在DEBUG模式下會加載python27_d.lib。所以在Preprocessor Definitions中定義MS_NO_COREDLL即可解決。
4. 編譯&鏈接錯誤解決之后,可以正常運行。
上面的例子中,test.c定義了一個模塊test,在Source.cpp中初始化了該模塊。下一節將用一個例子來詳細介紹使用Python C/C++ API擴展Python模塊。