1.安装python开发需要的包,

sudo apt-get install python-dev

2.go文件主体需要一个空main相关导入的库

package main
 
import "C"
func main() {}



3.cgo

 在 import "C" 上方的注释块可以预先调用,并且与import "C"之间不能有空格,并且能包含实际的 C 代码,在本例中导入Python.h并提供PyObject参数转换和模块初始化代码

#cgo pkg-config: python-2.7
 #define Py_LIMITED_API
 #include <Python.h> 
static PyObject *
 vtest(PyObject *self, PyObject *args)
 {
     long a, b;    if (!PyArg_ParseTuple(args, "ll", &a, &b))
         return NULL;
     return Py_BuildValue("l",  a+b);
 } static PyMethodDef FooMethods[] = {
     {"vt", vtest, METH_VARARGS, "test"},
     {NULL, NULL, 0, NULL}
 }; PyMODINIT_FUNC
 initfoo()
 {
     Py_InitModule("foo", FooMethods);
 }


4.

4.1PyMODINIT_FUNC提供模块初始化功能,初始化函数名称必须为init+模块名,Py_InitModule("foo", FooMethods)定义模块名和模块方法集合。

 

4.2PyMethodDef为模块方法集合的数组,定义在/usr/include/python2.7/methodobject.h

struct PyMethodDef {
     const char  *ml_name;   /* The name of the built-in function/method */
     PyCFunction  ml_meth;   /* The C function that implements it */
     int      ml_flags;  /* Combination of METH_xxx flags, which mostly
                    describe the args expected by the C func */
     const char  *ml_doc;    /* The __doc__ attribute, or NULL */
 };
 typedef struct PyMethodDef PyMethodDef;

在本例{"vt", vtest, METH_VARARGS, "test"} 中,"vt"为函数vtest在模块中的名称,"test"为函数注释。

第三个参数METH_VARARGS定义函数类型,可多种类型混用(METH_VARARGS|METH_KEYWORDS),其中METH_NOARGS和METH_O不能混用。共有如下定义:

METH_VARARGS:传入位置参数(即 *args)
 METH_KEYWORDS:传入键值参数(即**kwargs)、
 METH_NOARGS:没有参数
 METH_O:参数为单个对象(应该是def func(self)这种类型)
 METH_CLASS:@classmethod静态函数
 METH_STATIC:@staticmethod静态函数
 METH_OLDARGS:???没见过这种用法,貌似已经不支持了
 METH_COEXIST:monkey函数,可将已有函数mock掉

PyMethodDef必须用{NULL, NULL, 0, NULL}结束。

 

4.3通过PyArg_ParseTuple(args, "ll", &a, &b)解析如参,args为函数入参,'ll'定义解析的参数类型,最后跟参数保存的变量指针。

Py_BuildValue("l",  a+b)与之相反,将对应类型的参数转换成py_object类型。

参数类型对应关系

"s" (string) [char *] :将C字符串转换成Python对象,如果C字符串为空,返回NONE。
"s#" (string) [char *, int] :将C字符串和它的长度转换成Python对象,如果C字符串为空指针,长度忽略,返回NONE。
"z" (string or None) [char *] :作用同"s"。
        "z#" (string or None) [char *, int] :作用同"s#"。
        "i" (integer) [int] :将一个C类型的int转换成Python int对象。
"b" (integer) [char] :作用同"i"。
"h" (integer) [short int] :作用同"i"。
"l" (integer) [long int] :将C类型的long转换成Pyhon中的int对象。
"c" (string of length 1) [char] :将C类型的char转换成长度为1的Python字符串对象。
"d" (float) [double] :将C类型的double转换成python中的浮点型对象。
"f" (float) [float] :作用同"d"。
"O&" (object) [converter, anything] :将任何数据类型通过转换函数转换成Python对象,这些数据作为转换函数的参数被调用并且返回一个新的Python对象,如果发生错误返回NULL。
"(items)" (tuple) [matching-items] :将一系列的C值转换成Python元组。
"[items]" (list) [matching-items] :将一系列的C值转换成Python列表。
"{items}" (dictionary) [matching-items] :将一系类的C值转换成Python的字典,每一对连续的C值将转换成一个键

 

5.执行go build编译成so共享库

go build -buildmode=c-shared -o foo.so foo.go

 

6.至此实现了一个动态库,但是在上面的例子中golang只提供了包装(甚至包装都不需要,将注释直接编译都行),实际上可以让golang进行具体的业务计算。

新建文件vo1.go,export将函数pvvv设为全局。

package main
 import "C"
 //export pvvv
 func pvvv(a, b C.long) C.long{
     return a * b
 }

在foo.go中添加声明即可使用该函数

extern long pvvv(long a, long b);

最后foo.go为

package main
/*
#cgo pkg-config: python-2.7
 #define Py_LIMITED_API
 #include <Python.h>extern long pvvv(long a, long b);
 static PyObject *
 vtest(PyObject *self, PyObject *args)
 {
     long a, b;    if (!PyArg_ParseTuple(args, "ll", &a, &b))
         return NULL;
     return Py_BuildValue("l", pvvv(a,b));
 } static PyMethodDef FooMethods[] = {
     {"vt", vtest, METH_VARARGS, "test"},
     {NULL, NULL, 0, NULL}
 }; PyMODINIT_FUNC
 initfoo()
 {
     Py_InitModule("foo", FooMethods);
 }*/
 import "C"func main() {}

 

执行编译即生成了vfoo.so

go build -buildmode=c-shared -o foo.so foo.go vol.go

 7.

>>> import foo
 >>> dir(foo)
 ['__doc__', '__file__', '__name__', '__package__', 'vt']
 >>> foo.vt(23,4)
 92