Мой путь от Python к Go — делюсь советами и ресурсами



    От переводчика: перевели для вас статью Илада Леева о переходе с Python на Go. Статья будет полезна не только начинающим программистам, но и всем, кто так либо иначе интересуется Go.

    Мне нравится Python. Этот язык был моим фаворитом последние пять лет. Он дружелюбный, эффективный, и его легко выучить. Используется практически для всего: от создания простых скриптов и веб-разработки до визуализации данных и машинного обучения

    Постепенное «созревание» Go, обширное комьюнити и тот факт, что все больше компаний принимают этот язык на вооружение после успешных тестов, заставили меня обратить на него внимание и углубиться в литературу. Но этот пост не о том, что лучше — Python или Go: сравнений в сети огромное количество. По моему мнению, все зависит от области применения. Я собираюсь рассказать о том, почему выбрал Go, дав несколько советов и ссылок на полезные ресурсы для всех интересующихся темой.

    Skillbox рекомендует: Практический курс Python-разработчик с нуля.

    Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».




    Наблюдения


    Первое, что я сделал в начале пути, — изучил отличный официальный туториал “Tour Of Go”. Он дает понимание синтаксиса языка.

    Для того чтобы улучшить знания, я прочитал еще и книгу “Go for Python Programmers”, которая позволила приступить к следующему этапу — пробам и ошибкам.

    Я взял привычные функции, которые использовал в Python (сериализация JSON или работа с HTTP-вызовами), и попробовал написать их на Go. Благодаря такому наглядному сравнению мне удалось выявить ключевые отличия между языками.

    Компоновка проекта

    Прежде всего, Python не требует специфической иерархии каталогов, тогда как Go — да.

    Go использует «стандартную» компоновку, которая немного сложнее и требует больше работы. С другой стороны, в результате мы получаем хорошо структурированную основу кода, где используется модульная структура, а при расширении проекта код остается строго упорядоченным.

    Официальный туториал “How to Write Go Code” объясняет, как организовать свою работу.

    Статическая строгая типизация

    Go статически типизирован, и это заставит почувствовать себя не в своей тарелке тех, кто привык к динамически типизированным языкам вроде Python и Ruby.

    Нет сомнений, что динамические языки сильнее подвержены ошибкам, со стороны разработчика требуется больше усилий при проверке входных данных. Пример — функция, которая вычисляет сумму двух целых чисел. Если передать строку в функцию (что случается не так уж и редко), то это приведет к ошибке TypeError.

    В Go такое произойти не может, поскольку здесь нужно объявлять тип для каждой переменной и функции и то, какой тип переменной функция вернет.

    Сначала это раздражает: мне казалось, что эта особенность Go замедляет работу, — но потом пришло понимание, что на самом деле объявление всего экономит время, а вероятность ошибки снижается.

    Нативный параллелизм

    У Go есть нативная поддержка параллелизма с использованием подпрограмм и каналов — это удобно.

    Концепция каналов поначалу кажется несколько запутанной. Однако с течением времени она становится более понятной, и вы начинаете наслаждаться новыми возможностями, активно с ними работая.

    Вот визуализация всего сказанного от Ивана Данилюка.
    package main
     
    func main() {
        // create new channel of type int
        ch := make(chan int)
     
    // start new anonymous goroutine
        go func() {
            // send 42 to channel
            ch <- 42
        }()
        // read from channel
        <-ch
    }



    Больше примеров здесь и здесь.

    Работа с JSON

    Ну, json.loads() больше нет. В Python все просто: используем json.loads, и нет проблем.

    Но в Go, статически типизированном языке, эта операция становится сложнее.

    Здесь при использовании JSON все заранее определено. Любое поле, которое не вписывается в заданную структуру, будет игнорироваться, и это хорошо. Об этом можно думать как о заранее согласованном протоколе между двумя сторонами. Данные, которые вы получили в JSON, должны быть ожидаемы, а поля и типы JSON «согласованы» обеими сторонами.
    {
      “first”: “Elad”,
      “last”: “Leev”,
      “location”:”IL”,
      “id”: “93”
    }

    type AccountData struct {
     First    string `json:"first"`
     Last     string `json:"last"`
     Location string `json:"location"`
     ID       string `json:"id"`
    }

    Конечно, вы можете десериализовать JSON без структур, но по возможности этого следует избегать и учитывать статическую природу языка.

    Декодирование JSON на GO лучше всего объясняется в этом посте или здесь.

    Ленитесь конвертировать ваш JSON в Go-структуру? Нет проблем, этот инструмент все сделает за вас.

    Чистый код

    Компилятор Go будет всегда стараться держать ваш код «в чистоте». Он считает неиспользуемые переменные ошибкой компиляции. В Go используется уникальный подход, позволяющий системе решать большинство проблем форматирования. Так, Go запустит программу gofmt при сохранении или компиляции и самостоятельно поправит форматирование.

    Вам нет дела до переменных? Окей! Просто используйте _ (подчеркивание) и назначьте его пустому идентификатору.

    Мастрид-туториалом для этой части работы с языком является информация из “Effective Go”.

    Поиск подходящей библиотеки и фреймворков

    Я использовал с Python фреймворки и библиотеки вроде Flask, Jinja2, Requests и даже Kazoo, поэтому боялся, что не найду ничего подходящего для Go.

    Но комьюнити уже решило эти проблемы: у языка есть собственные уникальные библиотеки, которые позволяют полностью забыть о том, что вы использовали ранее.

    Вот мои фавориты.

    Python Requests => net/http

    net/http предоставляет удобную и легкую в использовании реализацию HTTP-клиента и сервера.

    Flask + Jinja2 => Gin

    Gin — веб-фреймворк HTTP с очень простым API: параметрами в пути, загружаемыми файлами, маршрутизацией групп (/ api / v1, / api / v2), пользовательскими форматами журналов, обслуживающими статические файлы, рендерингом HTML и действительно мощным кастомным middleware.
    Оцените этот бенчмарк.

    CLI Creation => Cobra

    Cobra — библиотека для создания мощных CLI-приложений, а также программа для генерации приложений и командных файлов.
    Cobra применяется во многих крупных Go-проектах, включая Kubernetes, etcd и OpenShift.

    Вот еще несколько библиотек, которые я настоятельно рекомендую: Viper, Gonfig и этот потрясающий список — Awesome-Go.

    Другие полезные ресурсы


    [1] Francesc Campoy  —  вам определенно нужно оценить эти YouTube-канал и GitHub-профиль.

    [2] GopherCon — видео.

    [3] Go Web Examples.

    [4] Golang Weekly, Gopher Academy, Golang News — Twitter-аккаунты.

    Подводим итоги


    Будучи постоянным пользователем Python в течение пяти лет, я боялся, что переход на Go будет болезненным.

    Но нет: есть наработки сообщества Go, которое расширяет и дополняет возможности языка, а также различные полезные ресурсы, которые помогут с переходом и вам.

    Go быстро развивается, и я надеюсь, что Google сможет сделать его главным языком для написания облачных приложений и инфраструктуры.

    Присоединяйтесь!



    Skillbox рекомендует:

    Skillbox
    211,00
    Онлайн-университет профессий будущего
    Поделиться публикацией

    Комментарии 29

      0
      Python не требует специфической иерархии каталогов

      Вы уверены? Модули же надо как-то организовывать.
        +4
        Рискну дать совет: не используйте фреймворки в Go. То есть набор полезных пакетов (типа Gorilla) — само собой. А вот всё, что несёт свою идиоматику (вроде Gin), или привносит магию в код — с большой осторожностью.
        Даже больше того, на мой взгляд, в Go нет толковых и действительно необходимых фреймворков.
        И, кстати, в Go слабая типизация, как и в Си. (Но статическая, да)
          0
          Вот прямо +++, хочется подписаться под каждым словом. Не хватает только рекомендации Chi: github.com/go-chi/chi
            0
            Всячески поддерживаю! Очень удобный роутер, который логично работает с нативным net/http!
            0
            Что вы называете слабой типизацией?
            Такой код на Go не скомпилируется:
            package main
              
            type A int
            type B int
            
            func main() {
                    var a A
                    var b B
                    a = 1
                    b = a
            }
            
            

            Вылетит ошибка
            cannot use a (type A) as type B in assignment


            А такой код на Си не выведет даже warning'а (в последнем gcc). Даже с -Wall и -Wextra:
            typedef int A;
            typedef int B;
            
            int main( void )
            {
                    A a;
                    B b;
                    a = 1;
                    b = a;
            }
            
              0
              Я ничего не называю, это всё термины. ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D0%B8_%D1%81%D0%BB%D0%B0%D0%B1%D0%B0%D1%8F_%D1%82%D0%B8%D0%BF%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F
              Для программиста это всё явно не видно и с помощью кода вряд ли можно продемонстрировать.
              В Go слабая статическая типизация. В Python типизация динамическая (слабая или сильная — не знаю).
                0
                Я всегда полагал, что неявные приведения типов (яркий пример которого на Си я написал выше) — атрибут слабой типизации.
                P.S. В Python считается, что строгая типизация (насколько она может быть строгой в динамически типизированном языке).
                  0
                  Приведение типов средой исполнения — динамическая типизация. Она может быть сильной или слабой, зависит от внутренней реализации языка. Про слабую типизацию в Go на Гитхабе писал один из разработчиков языка. Ну и логично, Go ведь пошёл от Си, а Си — известный пример языка со статической но слабой типизацией.
                    0

                    Сильная/строгая (strong) и слабая (weak) типизации — это про явное и неявное приведение типов. У Go строгая типизация, так как приведение типов всегда явное.


                    Касательно информации от разработчика Go, то подозреваю, что имеется в виду этот известный комментарий от Ian Taylor. Увы, но в нём речь идёт не о типизации (typing), но о системе типов (type system). Понятия хоть и связанные, но все же разные. Слабая система типов Go выражается в том, что шаг вправо/влево, и уже приходится чертыхаться с interface{}, теряя гарантии проверки корректности типов на этапе компиляции (ошибка несоответствия типа выстрелит только во время выполнения). И Ian Taylor сообщает, что это как раз сделано намеренно, дабы программисты "писали код, а не типы".

                      0
                      Полезно.
                        –1
                        Еще можно отказаться от функций, чтобы программисты писали код, а не функции.

                        Странное обоснование, как по мне.
                          0

                          Ну вот в brainfuck, к примеру, программисты как раз и пишут "код", а не функции =)


                          Я понимаю вашу позицию, я и сам её сторонник. Но разработчики Go спроектировали язык именно так под свои задачи. Им надо именно такой трейд-офф. Нам лишь остаётся либо пользоваться этим инструментом, либо выбрать другой. Каждый сам решает.

                      0
                      Кстати, в Go тоже есть неявное приведение типов. Думаю что в вашем же примере каждый из пользовательских типов получится присвоить переменной базового — int — без явного приведения.
                        0
                        Не получилось. Что я делаю не так?
                        package main
                          
                        type A int
                        type B int
                        
                        func main() {
                                var a A
                                var b B
                                var i int = 3
                                a = 1
                                b = 1
                                a = i
                                println(a)
                                println(b)
                        }
                        

                        cannot use i (type int) as type A in assignment

                        Go пошёл не только от Си:
                        Go's heritage is at least as much Oberon as it is C!
                        Вот в этой книге см. 13 страницу.

                        И Go изначально задумывался сильно-типизированным:
                        essentially strongly typed, but probably w/ support for runtime types
                          0
                          Вот так, к примеру, можно. Но это всё ерунда бесполезная.
                          package main
                          
                          import "fmt"
                          
                          type A [1]int
                          
                          func main() {
                          	var a A
                          	i := [1]int{3}
                          	a = i
                          	fmt.Println(a)
                          	fmt.Println(i)
                          }


                          [3]
                          [3]
                            0
                            Это уже не простые типы, а коллекции. Если бы в Go была такая штука как structural equality (и, соотв., inequality ), то этот код, наверное, тоже не собрался. Но это другой уровень, Clojure, F#, Haskell
                              0
                              Здесь одинаковый тип элементов, как выше и написали.

                              И из не приведенных примеров, в Go нельзя без явного приведения типов смешивать очень близкие типы, например сложить байт и инт, или знаковое с беззнаковым.
                              Это непривычно первое время, но защищает от ошибок и позволяет делать удобные вещи типа 5*time.Second (имеющий тип).

                              Все же считаю Go языком со строгой типизацией, в отличие от C.
                  +2
                  Переводчики а можно не подменять понятия?
                  Нативный параллелизм

                  В оригинале
                  Native concurrency

                  И это важно потому что:
                  Concurrency is not parallelism
                    0
                    А как перевести корректнее? Во всех книжках именно параллелизм (Донован-Кериган, что-то ещё вроде было на русском). Есть какой-то устоявшийся термин?
                      0
                      Думаю, что можно так и написать, конкурентность.
                      0
                      Я думаю, что можно сказать про изначально присущую многозадачность или многопоточность.
                      Вот еще линк для раздумий на тему
                      –5
                      Э… А как можно перейти из Python в Go? Как в дополнение к Python выучить Go я понимаю — языки взаимодополняющие, но как перейти — нет. Языки почти не пересекаются по назначению. Да, Python когда-то начинал как скриптовый язык (роднит с Go), но Python сейчас это про сложную бизнес-логику или математику, а Go про инфраструктурные задачи без бизнес-логики.

                      Может автор изначально ошибся в языке?
                        +3

                        Мне всегда казалось, что сложную бизнес логику пишут на Java или C#. Тогда как Python больше про скорость разработки для стартапов и науки. Мимо этого заблуждения ещё можно пройти.


                        Но записать Go в скриптовые языки (да даже в статье упоминаются статическая типизация и компиляция)! Это на грани добра и зла.

                          +1
                          Как скриптовый язык Go вполне себе. Не Powershell, конечно, но можно применять.
                          Запускать можно go run — одной командой. Модуль OS имеется.
                            +3
                            У Go настолько быстрый компилятор, что иногда он быстрее компилируется и запускается (go run, как тут уже ответили), чем Python.
                              0
                              И тем не менее система сборки в Android — использует Go именно как скриптовый язык.

                              Причём она очень забавно сделана: если в других системах сборки либо всё декларативно и иногда что-то очень сложно сделать, то тут — есть дуализм. С одной стороны есть достаточно простой декларативный формат Android.bp, с другой — если нужно что-то хитрое, то можно дописать модуль на Go в soong.

                              Разумеется при изменении и того и другого всё, прозрачно для пользователя, пересобирается и перезапускается… чем не скрипт?
                              +3
                              Казалось бы, как раз таки на Go писать много логики проще и надежнее, чем на Python.
                              Вот для небольшой и выразительной математики да, лучше Python, из-за его сишных библиотек и особенностей синтаксиса. Но что-то большое и сложное на нем писать странно.
                              0

                              у меня вопрос в тему:
                              люди добрые, а подскажите чем в go можно заменить pytest, django orm и django rest framework?

                                0
                                В части ORM-фреймворков — ничем. Go прекрасный инструмент для другого. В части же тестирования — см. goconvey.co

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое