Всем привет! Меня зовут Иван, я программирую на 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-битной системе) int8, int16, int32, int64 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: что было для вас самым сложным и где используете этот ЯП сейчас. И спасибо, что читали!
ссылка на оригинал статьи https://habr.com/ru/articles/902244/
Добавить комментарий