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