Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Moscow Python Meetup №93. Сурен Хоренян (ВК Ре...

Moscow Python Meetup №93. Сурен Хоренян (ВК Реклама, Руководитель команды в команде интерфейсов). Python нельзя Go: почему меня не зацепил новый тренд.

Будучи Python разработчиком я познакомился с Go и не нашёл причин переходить на Go для веб-разработки и прикладных задач. Возможно, я чего-то не понял. В докладе поделюсь наблюдениями и расскажу, почему Go меня не очаровал.

Видео: https://moscowpython.ru/meetup/93/python-no-go/

Moscow Python: http://moscowpython.ru
Курсы Learn Python: http://learn.python.ru
Moscow Python Podcast: http://podcast.python.ru
Заявки на доклады: https://bit.ly/mp-speaker

Moscow Python Meetup

September 08, 2024
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. • почему я не ”заценил” Go • с какими утверждениями

    я не согласен • как быть • что делать 3
  2. Как Python разработчик я не понимаю всеобщей любви к Go

    Как Python разработчик я не понимаю повышенного внимания к Go и не вижу причин переходить на Go, тем более, если вы уже хороши в своей сфере. 4
  3. Предуведомление Любой язык создан для решения определенных задач. Все используемые

    нами языки решают задачи в своей сфере и справляются с этим достаточно хорошо, чтобы продолжать расти и развиваться. Нет плохого языка, есть неподходящий язык для решения выбранной задачи. 5
  4. Сразу обозначу, что я: ничего не понял ретроград / консерватор

    ни в чём не разобрался вообще не программист 6
  5. О чем доклад Будучи Python разработчиком я познакомился с Go

    и не нашёл причин переходить на Go для веб-разработки и прикладных задач. Возможно, я чего-то не понял. В докладе поделюсь наблюдениями и расскажу, почему Go меня не очаровал. 7
  6. Классный набор занятий: ✦Позволяет ознакомиться с особенностями языка; ✦Объясняет все

    шаги и возможности; ✦Плавно вводит в курс дела; ✦Предоставляет редактор и компилятор на одной странице с уроками; ✦Ёмкий. И этот курс я, конечно же, не прошёл. A Tour of Go 9
  7. Что за гошный сервис? Как получился новый микросервис: • Нужен

    был новый микросервис для аутентификации и авторизации; • Коллеге очень хотелось сделать быстрый микросервис. 11
  8. Что получилось: • Самостоятельный Go сервис (с прогретыми кэшами и

    без обращений к БД) отвечал за 80-90 мс. (это быстро или медленно?) • Самостоятельно работать этот Go сервис не был задуман, это же auth центр. Работает только как прослойка для общения Python приложений. Что за гошный сервис? 12
  9. Почему его нужно чинить мне, питонисту? 1. Автор микросервиса ушёл

    в долгий отпуск; 2. Моя ответственность за комплекс сервисов, куда вошёл новый микросервис; 3. В команде и в соседних командах больше нет компетенций в Go. 13
  10. Что нужно было сделать Обновить view (в терминах Python): 1.

    Принимать новые данные; 2. Обрабатывать новые данные по-новому; 3. Изменить пару запросов в БД. Фото с места событий: 14
  11. Почему отступы табами, а не пробелами? В Goland и на

    GitHub / GitLab код выглядит по-разному Отступы 18
  12. Отступы для форматирования приводят к гит-вандализму +type Config struct {

    + Foo int `json:"foo"` + Bar int `json:"bar"` +} type Config struct { - Foo int `json:"foo"` - Bar int `json:"bar"` + Foo int `json:"foo"` + Bar int `json:"bar"` + FizzBuzz int `json:"FizzBuzz"` } -Нарушает историю; -Усложняет код-ревью. 19
  13. Отступы для форматирования приводят к гит-вандализму +var ( + ToBe

    bool = false +) var ( - ToBe bool = false + ToBe bool = false + MaxInt uint64 = 1<<64 - 1 ) -Нарушает историю; -Усложняет код-ревью. Код из официального урока https://go.dev/tour/basics/11 «variable declarations may be "factored" into blocks» var ( - ToBe bool = false - MaxInt uint64 = 1<<64 - 1 + ToBe bool = false + MaxInt uint64 = 1<<64 - 1 + z complex128 = cmplx.Sqrt(-5 + 12i) ) _________________________________________ _________________________________________ 20
  14. Давайте сразу писать без багов Так мы сократим время разработки

    ещё сильнее. И очень порадуем наших продактов. 22
  15. Почему гит-вандализм — это плохо ★ Нарушение гит-истории усложняет поиск

    ответственного за код; ★ На ревью нужно уделять внимание строчкам, которые не относятся к фиче совсем. 23
  16. import ( "fmt"; "math/cmplx" ) 1. ок 2. тоже ок

    import ( "fmt" "math/cmplx" ) Отступы нужны для стиля, следить за переносами необходимо для соблюдения синтаксиса. и такой стиль приветствуется Перенос — это часть синтаксиса. 24
  17. func main() { fmt.Println("Welcome!") } 1. ок 2. тоже ок

    func main() { fmt.Println( "Welcome!") } func main() { fmt.Println( "Welcome!" ) } 3. совсем не ок Это ограничение усложняет форматирование длинных кусков кода. ./prog.go:17:41: syntax error:
 unexpected newline in argument list;
 possibly missing comma or ) Перенос — это часть синтаксиса. 25
  18. это ок func main() { fmt.Println( "Welcome!", ) } Стандартный

    форматер поможет вам. Перенос — это часть синтаксиса. 26
  19. Функции умеют возвращать несколько значений package main import "fmt" func

    getValues() (int, string) { return 42, "Hello, Go!" } func main() { num, str := getValues() fmt.Println("Number:", num) // Output: Number: 42 fmt.Println("String:", str) // Output: String: Hello, Go! } Необходимо сразу принять указанное количество значений. 28
  20. Функции умеют возвращать несколько значений package main func getValues() (int,

    string) { return 42, "Hello, Go!" } func main() { ./main.go:10:9: assignment mismatch:
 1 variable but getValues returns 2 values res := getValues() } Нельзя ”собрать всё в одну кучку”. 29
  21. Количество получателей влияет на результат функции. func main() { names

    := []string{"Bob", "Sam"} for idx := range names { fmt.Println(idx, names[idx]) } // Output: // 0 Bob // 1 Sam } Итерирование по массиву. Получаем индекс, работаем с индексом: 32
  22. Количество получателей влияет на результат функции. func main() { names

    := []string{"Bob", "Sam"} for idx, name := range names { fmt.Println(idx, name) } // Output: // 0 Bob // 1 Sam } Итерирование по массиву. Получаем индекс и имя: 33
  23. Количество получателей влияет на результат функции. Получение по ключу всегда

    возвращает успешный результат: func main() { jobTitles := map[string]string{ "Bob": "Accountant", "Alice": "Engineer", } fmt.Printf("Bob's Job Title: %q\n", jobTitles["Bob"]) // Output: Bob's Job Title: "Accountant" fmt.Printf("Alice's Job Title: %q\n", jobTitles["Alice"]) // Output: Alice's Job Title: "Engineer" fmt.Printf("John's Job Title: %q\n", jobTitles["John"]) // Output: John's Job Title: "" } 35
  24. Количество получателей влияет на результат функции. Получение по ключу всегда

    возвращает успешный результат: func main() { jobTitles := map[string]string{ "Bob": "Accountant", "Alice": "Engineer", } bTitle := jobTitles["Bob"] fmt.Printf("Bob's Job Title: %q\n", bTitle) // Output: Bob's Job Title: "Accountant" jTitle := jobTitles["John"] fmt.Printf("John's Job Title: %q\n", jTitle) // Output: John's Job Title: "" } 36
  25. Количество получателей влияет на результат функции. Чтобы проверить наличие ключа,

    нужно получить два результата. Вторым будет true, если ключ есть. Иначе вторым придёт false. func main() { jobTitles := map[string]string{ "Bob": "Accountant", "Alice": "Engineer", } bTitle, ok1 := jobTitles["Bob"] fmt.Printf( "Bob's Job Title: %q (exists: %t)\n", bTitle, ok1) // Out: Bob's Job Title: "Accountant" (exists: true) jTitle, ok2 := jobTitles["John"] fmt.Printf( "John's Job Title: %q (exists: %t)\n", jTitle, ok2) // Out: John's Job Title: "" (exists: false) } 37
  26. Количество получателей влияет на результат функции. Лишнее необходимо игнорировать: func

    main() { jobTitles := map[string]string{ "Bob": "Accountant", "Alice": "Engineer", } names := []string{"Bob", "Sam"} for _, name := range names { if jobTitle, exists := jobTitles[name]; exists { fmt.Printf( "%s's Job title: %q\n", name, jobTitle) } else { fmt.Printf( "Missing job title: for %q\n", name) } } // Bob's Job title: "Accountant" // Missing job title: for "Sam" } 38
  27. Регистр первого символа имени имеет значение. package main import (

    "fmt" "math" ) func main() {
 ./prog.go:9:19:
 undefined: math.pi fmt.Println(math.pi) } В Go имя экспортируется, если оно начинается с заглавной буквы. 41
  28. package main import ( "fmt" "math" ) func main() {

    fmt.Println(math.Pi) // 3.141592653589793 } В Go имя экспортируется, если оно начинается с заглавной буквы. Регистр первого символа имени имеет значение. 42
  29. Функция, куда можно передать структуру type Person struct { Name

    string Age int } func IncreaseAge(p *Person, years int) { p.Age += years } func main() { person := Person{"Alice", 30} IncreaseAge(&person, 5) fmt.Printf("%s is %d years old\n", person.Name, person.Age) // Output: Alice is 35 years old } Обычная функция. Передаём различные аргументы, изменяем структуру. 45
  30. Метод, который доступен для вызова со структуры type Person struct

    { Name string Age int } func (p *Person) IncreaseAge(years int) { p.Age += years } func main() { person := Person{"Alice", 30} person.IncreaseAge(5) fmt.Printf("%s is %d years old\n", person.Name, person.Age) // Output: Alice is 35 years old } Схожий синтаксис. Функция становится методом. 46
  31. Объявляем структуры // Person struct type Person struct { Name

    string Age int } // Employee struct type Employee struct { // Embedding the Person struct Person Title string Salary int } И встраиваем одну структуру в другую: 48
  32. Инициализируем func main() { // Create a new Employee e

    := Employee{ Person: Person{ Name: "John Doe", Age: 30, }, Title: ”Golang Software Engineer", Salary: 300_000, } } Создаём новый экземпляр Employee со встроенным экземпляром Person: 49
  33. Обращаемся func main() { // Create a new Employee e

    := Employee{ Person: Person{ Name: "John Doe", Age: 30, }, Title: ”Golang Software Engineer", Salary: 300_000, } fmt.Printf("Employee name: %s, age: %d\n", e.Name, e.Age) // Output: Employee name: John Doe, age: 30 } Мы можем обращаться к свойствам Person через структуру Employee, так как Person встроен в Employee 50
  34. Объявляем методы // Speak method for Person func (p Person)

    Speak() { fmt.Printf( "Hello, my name is %s, and I am %d years old.\n", p.Name, p.Age) } // Work method for Employee func (e Employee) Work() { fmt.Printf( "I am working as a %s and earning a salary of %d per year.\n", e.Title, e.Salary) } 51
  35. Обращаемся func main() { // Create a new Employee e

    := Employee{ Person: Person{ Name: "John Doe", Age: 30, }, Title: "Golang Software Engineer", Salary: 300_000, } fmt.Printf("Employee name: %s, age: %d\n", e.Name, e.Age) // Output: Employee name: John Doe, age: 30 e.Speak() // Output: // Hello, my name is John Doe, // and I am 30 years old. e.Work() // Output: // I am working as a Golang Software Engineer // and earning a salary of 300000 per year. } Мы можем обращаться к свойствам и методам Person через структуру Employee, так как Person встроен в Employee 52
  36. Множественное встраивание type Reader interface { Read(p []byte) (n int,

    err error) } type Writer interface { Write(p []byte) (n int, err error) } // ReadWriter is the interface that combines // the Reader and Writer interfaces. type ReadWriter interface { Reader Writer } Наследования нет. Твердо и чётко. 53
  37. Интересное решение с defer При помощи defer мы откладываем выполнение

    переданной ”задачи” на момент завершения функции. func main() { f := createFile("/py.txt") defer closeFile(f) writeFile(f, "Hello!") } Полный пример: https://gobyexample.com/defer 56
  38. package main import ( "fmt" "os" ) func main() {

    f := createFile("./py.txt") defer closeFile(f) writeFile(f, "Hello!") } func createFile(p string) *os.File { fmt.Println("creating") f, err := os.Create(p) if err != nil { panic(err) } return f } func writeFile(f *os.File, data string) { fmt.Println("writing") _, err := fmt.Fprintln(f, data) if err != nil { panic(err) } } func closeFile(f *os.File) { fmt.Println("closing") err := f.Close() if err != nil { _, err = fmt.Fprintf(os.Stderr, "error: %v\n", err) if err != nil { panic(err) } os.Exit(1) } } Каждое действие требует дополнительной обработки всех возможных исходов. Полный листинг 57
  39. Контекстный менеджер в Python легче Это полный код, готовый к

    выполнению ”as is”. with open("go.txt") as f: f.write("Hello there") 58
  40. Удобные горутины Легчайший запуск потоков (threading). Может напомнить asyncio в

    Python. func f(line string) { for i := 0; i < 2; i++ { fmt.Println(line, i) } } func main() { go f("goroutine") f("direct") time.Sleep(time.Second) fmt.Println("done") // direct 0 // direct 1 // goroutine 0 // goroutine 1 // done } Пример: https://gobyexample.com/goroutines 59
  41. Классные встроенные пакеты, взять тот же прекрасный net/http Но: 1.

    Батарейки идут в комплекте не только в Go; 2. Для разработки в большинстве случаев будут использованы сторонние обертки, упрощающие работу. 60
  42. if err != nil 1. Интересное решение для явности обработки

    всех ошибок; 2. Все ошибки так не обработать, в некоторых случаях всё равно летит паника; 3. Утомляет на каждый чих писать эту конструкцию. Функции разрастаются молниеносно. 61
  43. Обнаружение протокола Удобно реализовано автоматическое обнаружение протокола. Указывать протокол явно

    не требуется. type Person struct { Name string Age int } func (p Person) GetFormatted() string { return fmt.Sprintf( "%s is %d years old", p.Name, p.Age) } type Formattable interface { GetFormatted() string } func printFormatted(d Formattable) { fmt.Println(d.GetFormatted()) } func main() { p := Person{ Name: "John Doe", Age: 42, } printFormatted(p) // John Doe is 42 years old } 62
  44. Политика одной мажорной версии Go с 2012 года поддерживается на

    версии 1.x, мажорную версию обновлять не планируют. Обещают совместимость. Что-то напоминает… 63
  45. Share Memory By Communicating Go продвигает идею обмена данными через

    коммуникацию через каналы вместо использования одной области памяти для общения. 64
  46. Быстрый пример работы с каналами Increment отправляет в канал значение

    каждые 10 мс. Print постоянно читает значения из канала. func Increment(ch chan<- int) { counter := 0 for { ch <- counter fmt.Println( "Sent count", counter) counter++ time.Sleep( 10 * time.Millisecond) } } func Print(ch <-chan int) { for { counter := <-ch fmt.Println( "Received count", counter) } } 65
  47. Запускаем работу с каналами в потоках - Создаём канал для

    общения - Запускаем потоки - Ждём и смотрим func main() { // Create a channel to share memory ch := make(chan int) // Start the goroutines go Increment(ch) go Print(ch) // Let the program run for a while time.Sleep(50 * time.Millisecond) // Received count 0 // Sent count 0 // Sent count 1 // Received count 1 // Sent count 2 // Received count 2 // Received count 3 // Sent count 3 // Sent count 4 // Received count 4 } 66
  48. Каналы можно использовать только в потоках Отправку в канал можно

    воспринимать как фоновую задачу, которая может выполниться только когда запущены треды. Если не запущена ни одна горутина, программа упадёт с ошибкой. fatal error:
 all goroutines are asleep - deadlock! Это неочевидное поведение. 67
  49. 68

  50. Дело не в Go И дело даже не в Python.

    Я вижу смысл учить новый язык для перехода между сферами: мобилки - фронт - бек - embedded. Переход с Python / Java на Go / C# не повлияет на вашу работу настолько, чтобы это стоило даунгрейда по должности. 70
  51. Классные инструменты • Какой классный gofmt; • Насколько легко запускать

    горутины; • Как круто придумали defer; • Очень удобные каналы; • Компактная сборка в один бинарь; • и т. д.; • и т. п.. Это тезисы про развлечения разработчика, но нет речи о пользе для бизнеса. 72
  52. Производительност ь программы 1. Математика; 2. Работа с изображениями и

    видео; 3. Конкурентность (горутины); 4. Доступ к низкому уровню; 5. Быстрая сборка. Есть не только в Go. 74
  53. Вычислительные ресурсы — не основная трата бизнеса 1. Зарплата сотрудникам;

    2. Реклама; 3. Рабочие ноутбуки и мониторы; 4. Уборка в офисе; 5. Печенье + кофе и чай; 6. …; 7. Серверы. Недавно мы ”просто” докидывали одному сервису ещё 300 CPU и 600 Gb RAM. 79
  54. Аргумент за Go из-за gRPC уровня ”JSON для JavaScript и

    только”. •Реализация протокола, и не менее удобная, есть в других языках. Как-то же они общаются с Go сервисами. •К сожалению, эта технология и не близко к притеснению HTTP + REST. gRPC и Protobuf есть не только в Go 82
  55. • Python слишком гибкий, а Go ограничивает разработчика, тем самым

    упрощая код. А все эти ваши декораторы, переопределения, наследования, метаклассы - это плохо; • Go быстро компилируется. А Python вообще компилировать не надо, как вам такое? • Возможность собрать команду. (??) И тут же: Прежде всего, начнем с очевидного: разработчиков Go не так много по сравнению со старыми языками, такими как C++ и Java. А собрать команду питонистов возможности нет? • Сильная экосистема: инструменты для работы с СУБД, планировщиками задач, шаблонизаторы, и тд. Конечно, не такая сильная, как у Java, Python или Node, но он вполне крепкая. Вот у Rust и Elixir похуже. Попытки найти ответы в статьях не приводят к пониманию В статьях Go vs Python я встречал тезисы, которые не смог переварить: Why We Switched from Python to Go By Thierry Schellenbach 83
  56. Порог входа Для входа в Go разработку нужно быть знакомым

    с программированием: • Следовать строгим правилам; • понимать указатели и не только; На старте в Python можно городить всё, что угодно, и учиться на этом. 87
  57. 88

  58. Если вы ещё ”зелёный” 1. Python комьюнити уже огромное, есть

    куда вливаться; 2. Невероятный объем готовых библиотек и ответов на StackOverflow; 3. Простой синтаксис; 4. Ветвистый путь — множество направлений (не только веб). 89
  59. Пример кода для сравнения простейшего CRUD Примера не будет, так

    как он не влез на слайд. Если вам хочется поковыряться и вручную поописывать все круды, можно взять тот же FastAPI и там начать городить свои велосипеды. 91
  60. Когда Go реально нужен в продукте Потребности разработчика. У разработчика

    есть потребности. Хочется пощупать что-то новое, кто-то просто хочет ”написать что-нибудь на гошке”. Если дать сотруднику такое развлечение, ты особо не потеряешь в скорости реализации фичи, зато сможешь закрыть хотелки ребят. 93
  61. Выбор языка для решения задачи, а не поиск подходящей задачи

    для этого языка. Мы решаем бизнес задачи в первую очередь. 94