Всем привет! Меня зовут Иван, я программирую на Python и недавно решил освоить новый ЯП. Долго думал, какой выбрать. В итоге решил довериться утверждению, что Python во многом похож на Go — на нем и остановился.
Ниже поделюсь личным опытом изучения языка — и да, я еще продолжаю его осваивать. Расскажу, какие встретил различия между Python и Go в типах данных, преобразовании типов данных, подходах к обработке ошибок. А еще обсудим, какой ЯП быстрее и можно ли импортировать и выполнить код Go в Python. Надеюсь, этот пост будет интересен тем, кто тоже собирается освоить второй ЯП. Ну, let’s Go!

Такие разные Go и Python
Осваивая Go, я понял, что некоторые моменты действительно напоминают Python — например, описание функций и оператор ветвления. Так что и давались они мне легко. А вот многообразие типов данных, их преобразование и обработка ошибок заставили попотеть — об этом и расскажу дальше.
Типизация — статическая и обязательная
Первое и, возможно, самое заметное различие двух языков программирования — это их типизация. В Python типизация динамическая, а в Go статическая, то есть во время выполнения программы нельзя изменить тип переменной.
Например, если в Python можно создать переменную x и сначала присвоить число 1, а после — строку "hello", ошибки не будет:
x = 1
x = “hello”
А вот к чему приведут такие же действия в Go:
var x int = 1
x = “hello” // Ошибка
Обязательную типизацию лучше рассмотреть на примере функции сложения двух чисел.
В Python можно не указывать тип аргументов функции:
def add(a, b):
return a + b
Но если то же самое сделать в Go, возникнет ошибка:
func add(a, b) { // Ошибка
return a + b
}
Чтобы ее избежать, добавим тип аргументов (int) и тип данных на выходе из функции (int):
func add(a int, b int) int {
return a + b
}
Простые типы данных
Еще одно крупное различие — более обширный арсенал простых типов данных у Go. Посмотрите:
Тип данных | Python | Go |
integer | int | int, uint, uintptr (32 бита на 32-битной системе и 64 на 64-битной системе) uint8, uint16, uint32, uint64 |
float | float | float32, float64 |
complex | complex | complex64, complex128 |
string | str | string |
boolean | bool | bool |
В Python для создания целого числа выбирается int. Зная о динамическом распределении памяти, можно не беспокоиться о его увеличении. Но в Go ситуация немного другая. Если число будет слишком большим, может произойти переполнение.
Пусть Go запускается на 32-битной машине, тогда:
package main
import "fmt"
func main() {
var number int32 = 2147483647 // Максимальное значение для int32
fmt.Println(number + 1) // -2147483648 —> Произошло переполнение
}
В случае же с Python:
number = 2147483647
print(number + 1) # 2147483648 —> Переполнения не возникло
Преобразование типов данных
Превратить один объект в другой в Python максимально просто. Достаточно написать тип данных и двойные скобки вокруг преобразуемого объекта:
number = 103
str_number = str(number)
В случае с Go придется знать чуть больше:
нужно импортировать модуль strconv и воспользоваться функцией Itoa;
использовать модуль fmt и выполнить форматирование при помощи Sprintf.
package main
import (
"fmt"
"strconv"
)
func main() {
number := 103
str_number_1 := strconv.Itoa(num)
str_number_2 := fmt.Sprintf("%d", num)
}
Обработка ошибок
Что меня действительно удивило при изучении Go — так это работа с ошибками. Допустим, нам нужно открыть файл test.txt и обработать возможные ошибки. В Python достаточно поместить код в блок TRY и использовать EXCEPT для различных исключений:
try:
with open("test.txt", "r") as file:
content = file.read()
print("Содержимое файла:")
print(content)
except FileNotFoundError:
print("Ошибка: файл не найден!")
except IOError as exception:
print(f"Ошибка ввода-вывода: {exception}")
except Exception as exception:
print(f"Неизвестная ошибка: {exception}")
В Go все по-другому: часто функции передают дополнительный параметр, который указывает, возникла какая-либо ошибка или нет. В нашем примере функция ReadFile возвращает на втором месте nil (похож на None в Python), если ошибки не было. И error (интерфейс), если была.
package main
import (
"fmt"
"os"
)
func main() {
content, err := os.ReadFile("test.txt")
if err != nil {
fmt.Println("Ошибка при чтении файла:", err)
return
}
fmt.Println("Содержимое файла:")
fmt.Println(string(content))
}
Скорость
Как Python и Go отличаются по скорости, рассмотрим на бенчмарках.
1. Комплексный анализ.
Задача: каждый бенчмарк выполняется на всех ядрах с пулом подключений к БД. При каждом запросе:
из БД извлекается 100 тестовых пользователей;
создается экземпляр класса для каждой строки, преобразуя объект datetime в строку формата ISO и шифруя одно из полей с помощью шифра Цезаря;
результат (массив) сериализуется в JSON и возвращается в качестве ответа.
Версия Golang — 1.22.3, а Python — 3.12.3.
Получается, в среднем Go быстрее Python в два раза:
ЯП | Сервер/фреймворк | RPS | Время (миллисекунды) |
Golang | net/http, json | 6 671 | 0,15 |
net/http, json, chi | 6 177 | 0,16 | |
net/http, easyjson | 7 384 | 0,14 | |
fasthttp, easyjson | 8 054 | 0,12 | |
Python | gunicorn | 1 994 | 0,50 |
gunicorn, flask | 1 931 | 0,52 | |
uvicorn, asyncio | 3 486 | 0,29 | |
uvicorn, uvloop | 3 664 | 0,27 | |
gunicorn, aiohttp | 3 276 | 0,31 |
2. Простые числа.
Задача: вычислить ряд простых чисел от 0 до 10 000 000. И вот что получилось:
Язык программирования | Время (секунды) |
Go (int32) | 2,37 |
Go (int64) | 7,12 |
Python | 59,64 |
Python + Numba | 7,43 |
В среднем Python уступает Go по времени выполнения в 17 раз. Но когда на помощь приходит Numba, Python догоняет Go (int64).
Go в Python
Как ни странно, но код, написанный на Go, можно использовать в Python.
Покажу такой пример. Пусть на Go написана функция умножения Multiply:
package main
import "C"
//export Multiply
func Multiply(a, b int) int {
return a * b
}
func main() {}
Чтобы использовать ее в Python, добавляем перед функцией комментарий со словом export и названием функции.
Выполняем компиляцию:
go build -o multiply.so -buildmode=c-shared multiply.go
Дальше открываем файл multiply.h для определения типа данных. Находим описание функции:
extern GoInt Multiply(GoInt a, GoInt b);
Видим, что тип данных у аргументов функции — GoInt. Значит, идем выше по коду и ищем, что означает GoInt. Можно увидеть такую строку:
typedef GoInt64 GoInt;
Теперь идем еще выше и ищем GoInt64:
typedef long long GoInt64;
Видим long long. Получается что для аргументов нужно будет указать тип данных long long языка программирования C.
Для использования скомпилированного файла в Python возьмем библиотеку ctypes. Укажем типы данных аргументов C long long и в конце выполним функцию Multiply:
>>> import ctypes
>>> lib = ctypes.cdll.LoadLibrary("./multiply.so")
>>> lib.Multiply.argtypes = [ctypes.c_longlong, ctypes.c_longlong]
>>> lib.Multiply(2, 10)
20
Где используют Go
Изучать новый язык программирования всегда классно. Но если не применять знания на практике, они будут постепенно забываться. Это как с иностранным: я учил немецкий в школе и после выпуска не практиковал, так что сейчас из предложения «ты любишь учиться» смогу перевести только местоимение «du».
Чтобы усилия с ЯП не прошли напрасно, я сразу решил, что буду использовать Go для разработки микросервисов. Скорее всего, это будет непросто, но так у меня есть цель.
Вообще Go всегда приходит на помощь там, где нельзя или неудобно использовать Python. Например, при работе с API идеальный выбор Python, но если планируется работа с чанками (например, раздача чанков видео), Go справится лучше. Другие примеры использования Go — облачные и сетевые сервисы, интерфейсы командной строки (CLI).
Среда выполнения Go включает race-детектор и инструменты для бенчмаркинга и статического анализа кода, а мощная экосистема позволяет комфортно разрабатывать сервисы. В стандартную библиотеку входят пакеты для:
работы с HTTP-серверами и клиентами;
парсинга JSON/XML;
взаимодействия с базой данных SQL;
обеспечения безопасности и шифрования.
Напоследок несколько примеров, в каких процессах Go используют зарубежные компании:
Allegro — для разработки сервиса кэширования.
American Express — для работы с платежами.
Armut — для сокращения потребления ресурсов и времени ответа на запрос.
Bitly перенесли старые проекты на Go и разрабатывают новые на нем же.
Dropbox — основные части инфраструктуры теперь работают на Go.
Microsoft использует Go для облачной инфраструктуры.
Ну и, конечно, Google использует свой ЯП для создания различных сервисов.
Пока я только начинаю грызть гранит Go. Мне удалось разобраться с циклами, функциями, пакетами, но впереди еще постижение интерфейсов, работы с файлами и параллелизма. Не знаю, как быстро пойдет прогресс, но интересно будет точно!
Буду рад, если в комментариях поделитесь своим опытом освоения Go: что было для вас самым сложным и где используете этот ЯП сейчас. И спасибо, что читали!