python + go через c
Недавно заинтересовался объединением python и go (например для ускорения каких-либо задач). Я нашёл пару сайтов на эту тему, но выяснилось, что для их объединения нужен c. Прошу помощи: объясните человеку, который не знает c, где нужно сделать ctrl + c, ctrl + v для того, чтобы написанная на go функция вызывалась из python и отдавала результат.
Ответы (1 шт):
Функции оформляешь в пакет main. Минимально выглядит так sum.go:
package main
import "C"
//export Sum
func Sum(a, b int) int {
return a + b
}
func main() {}
//export Sum строчка обязательна, без неё python не увидит функцию через си.
Компилишь таким способом:
go build -o sum.so -buildmode=c-shared sum.go
На выходе sum.so и sum.h. В sum.h смотри типы переменных.
С типами немного надо подробнее. Функция отображается как extern GoInt Sum(GoInt a, GoInt b); GoInt определение выше typedef GoInt64 GoInt;, GoInt64 уже определенно как сишный тип long long, который в питоне преобразуется через ctypes.c_longlong в питонский int. В этом примере указание типа в питоне не обязательно, но если нужно передать что-то сложное понадобится.
В питоне загружется библиотека через ctypes:
import ctypes
lib = ctypes.cdll.LoadLibrary("./sum.so")
lib.Sum.argtypes = [ctypes.c_longlong, ctypes.c_longlong]
print(lib.Sum(1,3))
Работа со строками
Через c char
Приводим строку к C.char в Го
//export Echo
func Echo(s *C.char) *C.char {
return C.CString(
strings.ToUpper(
C.GoString(
s
)
)
)
}
И в питоне:
lib.Echo.argtypes = [ctypes.c_char_p]
lib.Echo.restype = ctypes.c_char_p
print(lib.Echo('hello'.encode()).decode())
Но тут надо быть осторожным в том плане что результат выданный через указатель не освобождает память. Надо проверить освобождает ли её Питон при удалении указателя.
Через GoString
Если экспортировать функцию func Echo(s string) string {return strings.ToUpper(s) } то это может привести к проблемам со сборщиком мусора Go, а при вызове падает с ошибкой panic: runtime error: cgo result has Go pointer
Но входящие строки можно передать из питона так
class GoString(ctypes.Structure):
"typedef struct { const char *p; ptrdiff_t n; } _GoString_;"
def __init__(self, s):
super().__init__(s.encode(), len(s))
def __str__(self):
return self.p.decode()
_fields_ = [("p", ctypes.c_char_p),
("n", ctypes.c_size_t)]
lib.Echo.argtypes = [GoString]
lib.Echo(GoString('hello'))