Hướng dẫn dùng golang seek python
Bài viết được sự cho phép của tác giả Nguyễn Việt Hưng Show
Series bài viết giúp lập trình viên đã biết Python bắt đầu code Go mượt mà hơn – tài liệu bổ trợ cho Go tour chứ không để thay thế – phần 1. Golang là gìGo (hay còn gọi là Golang theo tên trang chủ golang.org) là một ngôn ngữ lập trình mới xuất hiện trong công chúng vào năm 2009 (vs: Python 1991, Java 1995), tại Google. Go được thiết kế ra với mục tiêu thay thế cho C++, nhưng khi tung ra cộng đồng, nó lại trở thành ngôn ngữ hấp dẫn đối với các lập trình viên dùng ngôn ngữ bậc cao hơn như Python, Ruby, PHP, NodeJS… nhờ khả năng chạy code nhanh, dùng ít tài nguyên hơn, deploy dễ hơn so với các ngôn ngữ này. Go trở thành một trào lưu (trend) công nghệ trên internet, với các bài viết “Write X in Go” luôn trở thành bài hot (và giờ thì tới Rust). Go được sử dụng như một ngôn ngữ “backend”, rất thịnh hành tại các startup công nghệ để viết “service” trong các hệ thống “microservice”, web API. Go còn được dùng phổ biến để viết các câu lệnh command line. Go đã ở giai đoạn “production ready”, đủ ổn định và đã được chạy trên các hệ thống lớn trên toàn cầu. Các sản phẩm opensource viết bằng Go được dùng rộng rãi như: Kubernetes, Docker, Terraform, InfluxDB, Prometheus, Grafana, … Những lĩnh vực khác cũng đã có mặt Go nhưng chưa thực sự thành công: mobile, frontend (JavaScript), làm website (như Django/RubyOnRails), Machine Learning. Những ưu điểm nổi bật của Go
Những khác biệt chủ yếu với PythonCách viết và chạy codeGo là compiled language, trước khi chạy được code phải qua 1 bước compile để tạo ra file binary, rồi sau đó mới chạy được file này. Go không có sẵn REPL, không bật lên gõ code trực tiếp như Python được. Mỗi lần sửa gì, thử gì, phải compile lại rồi mới chạy được. Vậy nên cách nhanh nhất để thử 1 đoạn code trong Go có lẽ là viết unittest. test rất phổ biến với một Go project, là một phần có sẵn trong bộ công cụ dev. Với 1 file code đơn giản, có thể dùng lệnh Auto format – go fmtGo là ngôn ngữ lập trình đầu tiên đưa 1 công cụ format code tự động vào tiêu chuẩn, chấm dứt mọi tranh cãi về code-style/format code. Ý tưởng tuyệt vời này sau được copy sang nhiều ngôn ngữ khác (như Python black) Static typingPython là ngôn ngữ dynamic typing, Go là static typing. Nếu chưa từng code ngôn ngữ static typing hay chưa dùng type annotation của Python, khác biệt này sẽ gây chút khó khăn lúc bắt đầu code Go. Theo python wiki
Static/dynamic typing nói tới kiểu (type) của 1 biến (variable/name). Trong dynamic typing, kiểu của 1 biến có thể thay đổi: Trong static typing, kiểu của 1 biến là cố định, và được khai báo ngay từ trước khi dùng, code sau sẽ gặp lỗi khi compile, và không tạo ra binary nào để chạy. var x int = 10 x = "PyMi" cannot use "PyMi" (type untyped string) as type int in assignment Trong Python3.6+, sử dụng type annotation, kết hợp với mypy để check cũng sẽ cho lỗi tương tự (nhưng vẫn chạy được). x: int = 10 x = "PyMi" print(x) $ mypy typetest.py typetest.py:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") Found 1 error in 1 file (checked 1 source file) Kiểu của các biến trong Go nói chung phải khai báo (declare), Go có thể tự suy luận (type inference) được trong một vài trường hợp đơn giản. Low levelĐược thiết kế nhằm thay C/C++ pha lẫn sự đơn giản dễ đọc của Python, code Go thường đơn giản hơn code C/C++ nhưng khá “thủ công”/”low level” so với code Python. Lập trình viên Python code Go nên quen với việc bỏ bớt đi nhiều tính năng tiện lợi, phải viết nhiều code hơn.
Code Go nhìn chung sẽ dài hơn code Python, vậy nên lập trình viên nên sắm cho mình một IDE xịn (như GoLand/IntelliJ IDEA ,
VSCode hay Sự bất tiện này sẽ đỡ khó chịu đi nhiều khi đã quen dùng snippet. Học lập trình Go (khi đã biết Python)Bao nhiêu là đủ? Em vui là được có phải không!Không ai đọc hết quyển từ điển tiếng Việt rồi
mới ra nói câu đầu tiên, không ai học lên tiến sỹ âm nhạc rồi mới chạm tay vào đánh đàn. Khi học Python tại PyMi.vn, ta học từng chút, dùng từng chút, chứ không học hết lý thuyết cả Python rồi mới thực hành. Cũng không phải học Học Go cũng vậy, dùng gì học đấy, cần gì học đấy. Cần gì thì phụ thuộc vào bạn định làm gì, một Python web developer sẽ học Django, ORM, làm việc với database trong khi một SysAdmin/DevOps engineer lại làm việc với file, process… Tài liệu
Viết code GoCài Go lên máy rồi viết code ra file, hoặc code online trên trang Play https://play.golang.org/ Go chạy code từ function package main import "fmt" func main() { fmt.Println("Hello, world.") } NamingGo dùng Data typesBuilt-in
ns := []int{0, 1} fmt.Printf("%v\n", ns) ns = append(ns, 2) fmt.Printf("%v\n", ns) ns = append(ns, ns...) // như extend trong python list fmt.Printf("%v\n", ns) https://play.golang.org/p/SCW8F0EqCmW
users := make(map[int]string) users[20081269] = "Pymier69" users[20081234] = "Pymier1" users[20081239] = "Pymier2" for k, v := range users { fmt.Printf("%d: %s\n", k, v) } println(users[20081239]) Cú pháp xs := []int{3,2,1} m := map[int]string{ 123: "foo", 345: "bar", } Các kiểu
khác Struct & methodPython có class, cung cấp đủ các tính năng tiêu chuẩn của OOP (object-oriented programming – lập trình hướng đối tượng). Định nghĩa 1 class tạo ra 1 kiểu dữ liệu mới và cho phép đóng gói dữ liệu (data) với tính năng (method) lại với nhau. Go struct không cung cấp các tính năng của OOP (như inheritance), nhưng cũng tạo ra 1 kiểu dữ liệu mới và cho phép đóng gói dữ liệu (data) với tính năng (method) lại với nhau. https://play.golang.org/p/tQMPLn9OXcj type Rectangle struct { Width float64 Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func main() { r := Rectangle{Width: 2, Height: 3} fmt.Printf("%f %T", r, r.Area()) } Code tương tự trong Python3, với type annotation class Rectangle(): def __init__(self, width: float, height: float): self.width = width self.height = height def area(self) -> float: return self.width * self.height def main(): print(Rectangle(width=2, height=3).area()) Trong Go, function gắn với các struct gọi là method,
interface, type assertion, type switchMột interface type định nghĩa 1 tập hợp các method.
Một value của type interface có thể chứa bất kỳ giá trị nào implement các method qui định trong interface đó. Empty interface Một value kiểu Sử dụng type assertion để truy cập giá trị ẩn dưới var anything interface{} anything = "PyMi.vn" fmt.Printf("%v %T\n", anything, anything) anything = 42 fmt.Printf("%v %T\n", anything, anything) x := 10 fmt.Printf("%v %T\n", x+anything.(int), anything.(int)) https://play.golang.org/p/mNXTI-4C3pU hoặc dùng switch v := i.(type) { case int: fmt.Printf("Twice %v is %v\n", v, v*2) case string: fmt.Printf("%q is %v bytes long\n", v, len(v)) Control flow (if/else/for)Go không có while, dùng for để loop đủ kiểu. Để lặp vô hạn như while, dùng if cho phép định nghĩa một biến chỉ dùng trong if, khá giống với warus operator của Python 3.8. y := 3 if x := y + 2; x > 2 { println("big number") } else { println("We don't talk any more") } ns := []string{"meo", "bo", "ga"} for key, value := range ns { if len(value) < 3 { fmt.Printf("%d %s\n", key, value) } } https://play.golang.org/p/rHJP4ghomzo FunctionĐịnh nghĩa function không khác Python đáng kể, ngoại trừ việc thay vì return tuple, Go có thể return nhiều giá trị (và không có kiểu tuple). func TwiceAndThrice(x int) (int, int) { return x * 2, x * 3 } Go không hỗ trợ overloading – một function dùng với số lượng argument khác nhau , nên cũng không hỗ trợ default
argument Cách duy nhất để gọi function là đưa vào các argument theo thứ tự, không có keyword argument như Python ( Có thể dùng function làm argument của function khác, việc này rất phổ biến trong Go, khái niệm này có tên Error handlingGo không có exception, lỗi không được xử lý khiến chương trình kết thúc qua việc gọi function “panic” (runtime error). Ví dụ khi dùng type assertion để truy cập kiểu string dưới 1 empty interface chứa giá trị int. panic: interface conversion: interface {} is int, not string Các function thay vì tạo ra exception, thường trả về giá trị Error kèm kết quả. Ví dụ function trong package func Atoi(s string) (int, error) Nếu thành công, error sẽ là type error interface { Error() string } mọi kiểu dữ liệu có method Do các function đều viết theo cách này, nên code gọi 1 function trong Go thường đi kèm với 1 đoạn kiểm tra s := "42" value, err := strconv.Atoi(s) if err != nil { log.Fatal(err) } println(value / 2) Cách
làm này gây nhiều tranh cãi, nhưng vẫn là cách làm chính thống của Go. Go dễ dãi hơn so với Java (khi 1 function xảy ra exception gì thì phải khai báo có thể xảy ra exception, và code gọi bắt buộc phải xử lý). Bỏ qua giá trị của Cách return error này không ÉP được lập trình viên phải xử lý mọi lỗi, nhưng tạo ra 1 nền văn hóa trong cộng đồng code Golang: luôn xử lý (hay ít nhất là nghĩ tới) error mọi lúc, mọi nơi. ImportCú pháp tương tự Python import "packagename" import "fmt" rồi gọi function qua Package & Install 3rd packagesĐầu mỗi file code Go phải bắt đầu bằng Tất cả các file go trong cùng 1 thư mục (không tính thư mục con) phải khai báo cùng package, chúng sẽ được gộp lại làm một (trừ phần import, mỗi file phải tự import thư viện mình dùng). Có thể coi việc các file khác nhau chỉ để thuận tiện tổ chức code, chứ vẫn là trong 1 file. Các function trong cùng 1 package (khác file) có thể gọi nhau thoải mái, không cần import lẫn nhau. Việc này khác với Python, mỗi file.py tự động là 1 module riêng biệt. Go cài package bằng lệnh Package “testing” của Go không có “assert”, cài package từ github rồi dùng assert.Equal để kiểm tra 2 giá trị có bằng nhau không, và message hiển thị khi chúng không bằng nhau. // main_test.go package main import ( "github.com/stretchr/testify/assert" "testing" ) func TestSomething(t *testing.T) { // assert equality assert.Equal(t, 123, 123, "they should be equal") } $ go test -v === RUN TestSomething --- PASS: TestSomething (0.00s) PASS ok _/home/hvn/MyData 0.002s Danh sách các package xem tại awesome-go và godoc IO: read/write fileĐọc file từng dòng và ghi file: Ghi file:
file, err := os.Create("bundau.mamtom") if err != nil { log.Fatal(err) } defer file.Close() _, err = file.WriteString("bundau\n") if err != nil { log.Fatal(err) } Nếu bỏ qua phần xử lý error thì cũng không khác Python là mấy. Đọc file:
file, err := os.Open("bundau.mamtom") if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) var lines []string for scanner.Scan() { lines = append(lines, scanner.Text()) } if err := scanner.Err(); err != nil { log.Fatal(err) } https://play.golang.org/p/Jv80_bRJc2H Hành động của chúng taGiải bài Project Euler 1 bằng Go:
package main import "fmt" func main() { sum := 0 for i := 0; i < 1000; i++ { if i%3 == 0 || i%5 == 0 { sum = sum + i } } fmt.Printf("%d\n", sum) } Giải bài Project Euler 16
package main import ( "fmt" "math/big" "strconv" ) func main() { var twoToThePowerOf1000 big.Int twoToThePowerOf1000.Exp(big.NewInt(2), big.NewInt(1000), nil) // Có thể tính sum luôn nhưng bài này minh họa slice digits := []int{} for _, c := range twoToThePowerOf1000.String() { digit, _ := strconv.Atoi(string(c)) digits = append(digits, digit) } sum := 0 for _, v := range digits { sum = sum + v } fmt.Printf("%d\n", sum) } Go có cộng đồng trên toàn cầu, có forum/slack/IRC để thảo luận, xem tại help – có cả bằng Tiếng Việt. Những nhân vật đáng chú ý/follow trong cộng đồng Go gồm các tác giả, core dev, …
Kết luậnGo là một ngôn ngữ lập trình đơn giản và thú vị, với những kiến thức trong bài này, ta đã có thể bắt đầu dùng Go để viết các chương trình không hề đơn giản. Phần tiếp sẽ trình bày chi tiết về các khái niệm chỉ có trong Go mà không có trong Python như Pointer, sự khác biệt về cách tổ chức package trong Go, declaration & initialization (khai báo và khởi tạo variable), cùng các standard library quan trọng nhất cho một SysAdmin/DevOps. Bài viết gốc được đăng tải tại pp.pymi.vn Có thể bạn quan tâm:
Xem thêm các việc làm lập trình it hấp dẫn tại TopDev |