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