Go как клей: Интеграция с Python, C и другими языками

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему Go — это не остров, а мост

Go — замечательный язык. Он быстрый, компилируемый, с отличной стандартной библиотекой. Но давайте честно: в реальном мире мы редко пишем всё с нуля на одном языке. У вас есть старая, проверенная временем библиотека на C, которую жалко переписывать. Или крутой скрипт на Python для машинного обучения. Или вы хотите запустить логику на Go прямо в браузере.

И тут встаёт вопрос: как интегрировать Go с другими языками? Не бойтесь, Go специально проектировался как хороший «сосед». В этой статье на PythonLib мы разберём три главных способа подружить вашу Go-программу с чужим кодом. Вы узнаете, как работает cgo, как дёргать Python-функции через системные вызовы и даже как компилировать Go в WebAssembly.

Интеграция Go с C через cgo: классика жанра

Самый прямой и мощный путь — это cgo. Это не библиотека, а встроенный механизм Go, который позволяет вашему коду напрямую вызывать C-функции. Как это работает? Вы просто пишете импорт "C" и перед ним — комментарий с кодом на C. Go-компилятор всё склеит.

Пишем первую программу с cgo

Давайте напишем простой пример: вызовем стандартную printf из C. Создайте файл main.go:

package main

/*
#include <stdio.h>
#include <stdlib.h>

void hello_cgo(const char* name) {
    printf("Привет из C, %s!\n", name);
}
*/
import "C"
import "unsafe"

func main() {
    name := C.CString("Gopher")
    defer C.free(unsafe.Pointer(name))
    C.hello_cgo(name)
}

Запускаем: go run main.go. Если у вас установлен GCC (или MinGW на Windows), всё скомпилируется. Вы увидите «Привет из C, Gopher!». Магия!

Важно: C.CString выделяет память в куче C, поэтому мы обязаны её освободить через C.free. Иначе — утечка. Cgo требует от вас дисциплины.

Передача сложных типов: структуры

Часто нужно работать не со строками, а со структурами. Допустим, у нас есть C-библиотека для работы с матрицами. Мы хотим передать в неё Go-структуру. Сделаем это:

package main

/*
typedef struct {
    int x;
    int y;
    int result;
} Point;

Point add_points(Point a, Point b) {
    Point r;
    r.x = a.x + b.x;
    r.y = a.y + b.y;
    r.result = r.x + r.y;
    return r;
}
*/
import "C"
import "fmt"

type GoPoint struct {
    X, Y, Result int
}

func main() {
    a := C.Point{x: 10, y: 20}
    b := C.Point{x: 30, y: 40}
    r := C.add_points(a, b)

    goPoint := GoPoint{
        X:      int(r.x),
        Y:      int(r.y),
        Result: int(r.result),
    }
    fmt.Printf("Go получил: Point{X:%d, Y:%d, Result:%d}\n", goPoint.X, goPoint.Y, goPoint.Result)
}

Обратите внимание: мы явно преобразуем C-типы в Go-типы. Cgo не умеет автоматически маппить структуры, поэтому вы всегда отвечаете за конвертацию. Это один из главных компромиссов интеграции Go с языком C.

Когда cgo не нужен

Не используйте cgo, если:

  • Вам нужно просто позвать команду в терминале — используйте os/exec.
  • Вы пишете кроссплатформенный код — cgo усложняет сборку.
  • Производительность не критична — cgo добавляет накладные расходы на переключение контекста.

Интеграция Go с Python: скриптуем с умом

Python и Go — отличная пара. Go — за скорость и параллелизм, Python — за гибкость и экосистему (NumPy, TensorFlow). Как их подружить? У нас есть два подхода: «тяжёлый» (через cgo и Python C API) и «лёгкий» (через подпроцессы).

Способ 1: Вызов Python через os/exec (простой и надёжный)

Самый безопасный для образования способ — запустить Python-скрипт как отдельный процесс. Go-программа передаёт данные через stdin/stdout или JSON-файл.

package main

import (
    "encoding/json"
    "fmt"
    "os/exec"
    "strings"
)

type Input struct {
    Values []float64 `json:"values"`
}

type Output struct {
    Mean float64 `json:"mean"`
    Std  float64 `json:"std"`
}

func main() {
    // Данные для Python
    input := Input{Values: []float64{2.5, 3.0, 4.1, 5.6}}
    inputJSON, _ := json.Marshal(input)

    // Запускаем Python-скрипт
    cmd := exec.Command("python3", "compute_stats.py")
    cmd.Stdin = strings.NewReader(string(inputJSON))

    outputBytes, err := cmd.Output()
    if err != nil {
        panic(err)
    }

    var result Output
    json.Unmarshal(outputBytes, &result)
    fmt.Printf("Среднее: %.2f, Стандартное отклонение: %.2f\n", result.Mean, result.Std)
}

А вот сам скрипт compute_stats.py:

import sys, json, numpy as np

data = json.load(sys.stdin)
arr = np.array(data['values'])
result = {
    'mean': float(np.mean(arr)),
    'std': float(np.std(arr))
}
print(json.dumps(result))

Плюсы: простота, не нужно компилировать Python в Go. Минусы: оверхед на запуск процесса. Для вызова одного скрипта — идеально.

Способ 2: Встраивание Python через cgo (для профи)

Если вам нужно вызывать Python-функции сотни раз в секунду, запуск процесса не годится. Тогда идём в интеграцию Go с Python через Python.h. Пример (только для Linux/macOS):

package main

/*
#cgo LDFLAGS: -lpython3.10
#include <Python.h>
*/
import "C"
import "fmt"

func main() {
    C.Py_Initialize()
    defer C.Py_Finalize()

    // Выполняем Python-код
    code := C.CString("result = 42 + 10")
    defer C.free(unsafe.Pointer(code))
    C.PyRun_SimpleString(code)

    // Получаем результат
    mainModule := C.PyImport_AddModule(C.CString("__main__"))
    dict := C.PyModule_GetDict(mainModule)
    resultObj := C.PyDict_GetItemString(dict, C.CString("result"))
    if resultObj != nil {
        val := int(C.PyLong_AsLong(resultObj))
        fmt.Printf("Python сказал: %d\n", val)
    }
}

Этот код требует, чтобы у вас был установлен Python dev-пакет (python

Блоги

Рекомендаций по книгам