从动态到静态,从解释到编译,开启你的 Go 之旅
目录
- [为什么选 Go?](#为什么选 Go? "#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89-go")
- 环境搭建
- [Hello World 对比](#Hello World 对比 "#hello-world-%E5%AF%B9%E6%AF%94")
- 基础语法差异
- 数据类型详解
- 控制结构
- 函数与多返回值
- 结构体与方法
- 接口与多态
- [并发编程:Goroutine 与 Channel](#并发编程:Goroutine 与 Channel "#%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B")
- 错误处理
- 标准库常用包
- 工程化实践
- [从 Python 到 Go 的思维转换](#从 Python 到 Go 的思维转换 "#%E6%80%9D%E7%BB%B4%E8%BD%AC%E6%8D%A2")
为什么选 Go? {#为什么选-go}
| 特性 | Python | Go | 场景优势 |
|---|---|---|---|
| 执行速度 | 解释型,较慢 | 编译型,接近C++ | 高性能后端、微服务 |
| 并发模型 | GIL限制,多线程假并行 | Goroutine,原生支持高并发 | 云原生、网络编程 |
| 部署 | 需解释器/虚拟环境 | 单二进制文件,静态链接 | 容器化、Serverless |
| 类型系统 | 动态类型,灵活但易出错 | 静态类型,编译期检查 | 大型项目、团队协作 |
| 内存管理 | 自动GC,但优化空间有限 | 自动GC,性能调优更可控 | 长期运行服务 |
| 生态定位 | 全能型(AI/脚本/Web) | 云原生/基础设施/中间件 | Docker/K8s/Etcd 都是用 Go 写的 |
Go 的最佳战场:微服务、API 网关、DevOps 工具、区块链基础设施、实时数据处理。
环境搭建 {#环境搭建}
安装 Go(以 Linux/macOS 为例)
bash
# 下载安装包 (1.21+ 版本)
wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
# 配置环境变量
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
echo 'export GOPATH=$HOME/go' >> ~/.bashrc
echo 'export PATH=$PATH:$GOPATH/bin' >> ~/.bashrc
source ~/.bashrc
# 验证
go version # go version go1.21.5 linux/amd64
工作区模式(Go Modules,1.11+ 推荐)
bash
# 创建项目(无需放在 GOPATH/src 下)
mkdir myproject && cd myproject
go mod init github.com/yourname/myproject
# 目录结构
myproject/
├── go.mod # 依赖管理文件
├── go.sum # 依赖校验和
├── main.go # 入口文件
├── pkg/ # 可复用包
└── internal/ # 私有包(不可被外部导入)
常用命令
bash
go run main.go # 编译并运行(不生成二进制)
go build # 编译生成可执行文件
go build -o app # 指定输出文件名
go test ./... # 运行所有测试
go get github.com/gin-gonic/gin # 添加依赖
go mod tidy # 清理未使用依赖
Hello World 对比 {#hello-world-对比}
Python 版本
python
# hello.py
def main():
name = "World"
print(f"Hello, {name}!")
if __name__ == "__main__":
main()
运行 :python hello.py
Go 版本
go
// main.go
package main // 可执行程序必须是 main 包
import "fmt" // 格式化 I/O,类似 Python 的 print
func main() { // 程序入口,无参数,无返回值
name := "World" // 短变量声明,类型推断为 string
fmt.Printf("Hello, %s!\n", name) // 格式化输出,\n 必须显式添加
}
运行 :go run main.go 或 go build && ./main
关键差异:
package main:可执行程序的入口包,库文件则用其他名(如package utils)import:必须显式导入,未使用的包会编译错误(强制代码整洁):=:短变量声明,只能在函数内使用,自动推断类型fmt.Printf:格式化字符串,类似 C 的 printf,Go 中字符串插值需用格式化动词
基础语法差异 {#基础语法差异}
1. 变量声明
go
// Go 的多种声明方式
var x int = 10 // 完整声明
var y = 20 // 类型推断
var z int // 零值初始化(z = 0)
a := 30 // 短声明,只能在函数内
// 多变量
var a, b, c = 1, 2, 3
m, n := "hello", 42 // 不同类型同时推断
// 对比 Python:x, y, z = 10, 20, 30(动态类型,无需声明)
零值机制(Go 的哲学:没有未初始化的变量):
- 数值类型:
0 - 布尔:
false - 字符串:
""(空字符串,非 None) - 指针、slice、map、channel:
nil(类似 None,但类型安全)
2. 常量与枚举
go
const Pi = 3.14159 // 常量,编译期确定
const (
Monday = iota // iota 枚举生成器,从 0 开始自增
Tuesday // 1
Wednesday // 2
// ...
)
// 对比 Python:没有原生常量,通常用大写+下划线约定
# PYTHON_CONST = 100 # 只是约定,仍可修改
3. 可见性规则(重要!)
go
package mypkg
// 大写字母开头 = 公开(public)
func PublicFunction() {} // 可被其他包导入使用
var PublicVar = 10
// 小写字母开头 = 私有(private)
func privateFunction() {} // 仅在 mypkg 包内可见
var privateVar = 20
// 对比 Python:_前缀表示私有,但只是约定,技术上仍可访问
# def _private_func(): pass
Go 的可见性由命名控制,无 public/private 关键字,这是 Go 极简设计的体现。
数据类型详解 {#数据类型详解}
1. 基础类型
| Go 类型 | 说明 | Python 对应 |
|---|---|---|
int |
至少32位,通常是64位 | int(无上限) |
int8/16/32/64 |
固定位宽 | 无直接对应 |
uint |
无符号整数 | 无直接对应 |
float32/64 |
浮点数 | float |
string |
不可变 UTF-8 字符串 | str(不可变) |
bool |
true/false |
True/False |
byte |
uint8 别名 |
单个字节 |
rune |
int32 别名,表示 Unicode 码点 |
类似 ord(char) |
go
// 字符串操作(对比 Python)
s := "Hello, 世界"
fmt.Println(len(s)) // 13 字节数(UTF-8 编码)
fmt.Println(len([]rune(s))) // 9 字符数(Unicode 码点数)
// 字符串不可变!类似 Python
// s[0] = 'h' // 编译错误
// 切片操作(类似 Python slicing,但语法不同)
substr := s[0:5] // "Hello",左闭右开
2. 复合类型
数组(Array)- 固定长度,值类型
go
var arr [5]int = [5]int{1, 2, 3, 4, 5} // 长度是类型的一部分!
arr2 := [...]int{1, 2, 3} // 编译器推断长度 [3]int
// 数组是值类型,赋值会复制整个数组
b := arr // 复制 5 个 int 的内存
b[0] = 100
fmt.Println(arr[0]) // 1(原数组未变)
// 对比 Python:list 是引用类型
# a = [1, 2, 3]
# b = a
# b[0] = 100
# print(a[0]) # 100
切片(Slice)- 动态数组,引用类型 ⭐核心概念
go
// 切片是描述符:[指针, 长度, 容量],指向底层数组
slice := []int{1, 2, 3} // 字面量创建
slice2 := make([]int, 5) // 长度5,容量5,零值初始化
slice3 := make([]int, 5, 10) // 长度5,容量10
// 追加元素(类似 Python append,但语法不同)
slice = append(slice, 4, 5) // 可能触发底层数组扩容
// 切片共享底层数组(危险但高效)
a := []int{1, 2, 3, 4, 5}
b := a[1:3] // [2, 3]
b[0] = 100
fmt.Println(a) // [1, 100, 3, 4, 5] // 原数组被修改!
// 深拷贝
c := make([]int, len(a))
copy(c, a) // 显式拷贝
// 对比 Python:切片默认浅拷贝,但 list 是可变对象
# a = [1, 2, 3, 4, 5]
# b = a[1:3]
# b[0] = 100
# print(a) # [1, 2, 3, 4, 5] Python 切片创建新列表!
关键区别:Go 切片是引用类型,共享底层数组;Python 切片创建新列表。
映射(Map)- 类似 Python dict
go
// 必须 make 初始化(或字面量)
m := make(map[string]int)
m["alice"] = 25
m["bob"] = 30
// 字面量创建
m2 := map[string]int{
"alice": 25,
"bob": 30,
}
// 访问与检查存在性
age, ok := m["charlie"] // age=0, ok=false(零值 + 布尔标志)
if ok {
fmt.Println("存在,年龄", age)
}
// 删除
delete(m, "alice")
// 遍历(无序!)
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
// 对比 Python:有序字典(3.7+),get 方法,in 关键字
# age = m.get("charlie") # None
# if "charlie" in m:
3. 结构体(Struct)- 代替 Python 的 Class
go
// 定义结构体(值类型)
type Person struct {
Name string
Age int
// 小写字段私有,大写公开
email string // 包内私有
}
// 创建实例(多种方式)
p1 := Person{"Alice", 25, "alice@example.com"} // 按字段顺序
p2 := Person{Name: "Bob", Age: 30} // 命名初始化,email 为零值
p3 := new(Person) // 返回 *Person,零值初始化
// 访问字段
fmt.Println(p1.Name)
p1.Age = 26
// 对比 Python class
# class Person:
# def __init__(self, name, age):
# self.name = name
# self.age = age
# p = Person("Alice", 25)
控制结构 {#控制结构}
1. 条件语句
go
// if 支持初始化语句(常见模式)
if err := doSomething(); err != nil {
return err
}
// err 的作用域仅在 if 块内
// 无括号,必须花括号
if x > 0 {
fmt.Println("positive")
} else if x < 0 {
fmt.Println("negative")
} else {
fmt.Println("zero")
}
// 对比 Python:elif,冒号+缩进
# if x > 0:
# print("positive")
# elif x < 0:
# print("negative")
2. 循环(Go 只有 for,无 while)
go
// 类 C 风格
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// 类 while 风格
sum := 0
for sum < 100 {
sum += 10
}
// 无限循环
for {
// do something
break // 或 return
}
// 遍历 slice(range 返回索引, 值)
nums := []int{10, 20, 30}
for i, v := range nums {
fmt.Printf("index: %d, value: %d\n", i, v)
}
// 只需要值:用 _ 忽略索引
for _, v := range nums {
fmt.Println(v)
}
// 遍历 map
for k, v := range m {
fmt.Println(k, v)
}
// 对比 Python:for/while,enumerate,items()
# for i, v in enumerate(nums):
# print(i, v)
# for k, v in d.items():
3. Switch(强大且灵活)
go
// 自动 break,无需显式写
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("macOS")
case "linux":
fmt.Println("Linux")
default:
fmt.Println("其他")
}
// 无表达式 switch(替代 if-else if)
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
default:
fmt.Println("C")
}
// fallthrough 穿透(很少用)
switch n {
case 1:
fmt.Println("1")
fallthrough // 继续执行 case 2
case 2:
fmt.Println("2")
}
函数与多返回值 {#函数与多返回值}
1. 基础函数
go
// 函数签名:func 函数名(参数) (返回值)
func add(a int, b int) int {
return a + b
}
// 同类型参数可合并
func add(a, b int) int {
return a + b
}
// 多返回值(Go 特色!)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide by zero")
}
return a / b, nil
}
// 使用
result, err := divide(10, 0)
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
// 忽略返回值
result, _ = divide(10, 2) // 忽略 error(不推荐,除非确定安全)
// 对比 Python:多返回值用 tuple,异常处理不同
# def divide(a, b):
# if b == 0:
# raise ValueError("cannot divide by zero")
# return a / b, None
# try:
# result = divide(10, 0)
# except ValueError as e:
# print(e)
2. 命名返回值(较少用,但可读性好)
go
func split(sum int) (x, y int) { // 命名返回值
x = sum * 4 / 9
y = sum - x
return // 裸 return,返回命名变量
}
3. 变长参数
go
func sum(nums ...int) int { // ... 类似 Python *args
total := 0
for _, num := range nums {
total += num
}
return total
}
// 使用
sum(1, 2, 3)
nums := []int{1, 2, 3}
sum(nums...) // 展开 slice,类似 Python *nums
4. 函数是一等公民(类似 Python)
go
// 函数作为参数(回调)
func apply(nums []int, f func(int) int) []int {
result := make([]int, len(nums))
for i, v := range nums {
result[i] = f(v)
}
return result
}
// 使用
squares := apply([]int{1, 2, 3}, func(x int) int {
return x * x
})
// 闭包
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
结构体与方法 {#结构体与方法}
1. 方法定义(值接收者 vs 指针接收者)
go
type Rectangle struct {
Width, Height float64
}
// 值接收者:方法内是副本修改,不影响原对象
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 指针接收者:可以修改原对象,避免大对象拷贝
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// 使用
rect := Rectangle{10, 5}
fmt.Println(rect.Area()) // 50
rect.Scale(2) // 修改原对象
fmt.Println(rect.Width) // 20
// 对比 Python:self 总是引用,无值/指针区分
# class Rectangle:
# def area(self):
# return self.width * self.height
# def scale(self, factor):
# self.width *= factor # 总是修改原对象
选择规则:
- 不修改状态且对象小 → 值接收者
- 需要修改状态或对象大 → 指针接收者
2. 嵌入类型(类似继承,但更好)
go
type Animal struct {
Name string
}
func (a *Animal) Speak() {
fmt.Println("Some sound")
}
// Dog 嵌入 Animal,获得其字段和方法
type Dog struct {
Animal // 匿名嵌入,非继承!
Breed string
}
// 可以重写方法
func (d *Dog) Speak() {
fmt.Println("Woof!")
}
// 使用
d := Dog{Animal: Animal{Name: "Buddy"}, Breed: "Golden"}
d.Speak() // Woof!(Dog 的方法)
d.Animal.Speak() // Some sound(Animal 的方法)
fmt.Println(d.Name) // Buddy,直接访问嵌入字段
// 对比 Python 继承
# class Animal:
# def speak(self): print("Some sound")
# class Dog(Animal):
# def speak(self): print("Woof!")
关键区别 :Go 的嵌入是组合而非继承,无多态(需通过接口实现)。
接口与多态 {#接口与多态}
1. 隐式实现(Go 的革命性设计)
go
// 定义接口:只需声明方法签名
type Speaker interface {
Speak()
}
// Dog 自动实现 Speaker(无需 implements 关键字)
type Dog struct{}
func (d Dog) Speak() { fmt.Println("Woof") }
type Cat struct{}
func (c Cat) Speak() { fmt.Println("Meow") }
// 多态使用
func MakeSound(s Speaker) {
s.Speak()
}
MakeSound(Dog{}) // Woof
MakeSound(Cat{}) // Meow
// 对比 Python:鸭子类型,无显式接口
# def make_sound(animal):
# animal.speak() # 运行时检查,无编译期保障
Go 接口优势:
- 隐式实现:解耦接口定义与实现
- 编译期检查:类型安全
- 小接口原则:接口应该小(1-3 个方法),组合使用
2. 空接口与类型断言
go
// 空接口 interface{} 类似 Python 的 Any,可接受任何类型
func PrintAny(v interface{}) {
fmt.Println(v)
}
// 类型断言(类似 Python isinstance)
func process(v interface{}) {
// 方式1:安全断言
if s, ok := v.(string); ok {
fmt.Println("String:", s)
return
}
// 方式2:switch 类型判断
switch val := v.(type) {
case int:
fmt.Println("Int:", val)
case string:
fmt.Println("String:", val)
default:
fmt.Println("Unknown type")
}
}
// Go 1.18+ 泛型(推荐替代空接口)
func Add[T int | float64](a, b T) T {
return a + b
}
并发编程 {#并发编程}
这是 Go 的杀手锏!对比 Python 的 threading/multiprocessing,Go 的并发极其简单高效。
1. Goroutine - 轻量级线程
go
// 启动一个 goroutine(只需 go 关键字)
func sayHello() {
fmt.Println("Hello from goroutine")
}
go sayHello() // 非阻塞,立即返回
// 匿名函数形式
go func() {
fmt.Println("Anonymous goroutine")
}()
// 对比 Python:需要 threading.Thread
# import threading
# t = threading.Thread(target=say_hello)
# t.start()
关键差异:
- Python 线程:OS 线程,切换成本高,GIL 限制真并行
- Goroutine:用户态线程,2KB 初始栈,Go 调度器管理,可轻松启动百万级
2. Channel - goroutine 间的通信
go
// 创建 channel(类似 Python 的 Queue,但类型安全)
ch := make(chan int) // 无缓冲(同步)
ch2 := make(chan int, 10) // 有缓冲(异步)
// 发送与接收(<- 操作符)
go func() {
ch <- 42 // 发送,阻塞直到有人接收
}()
value := <-ch // 接收,阻塞直到有数据
fmt.Println(value)
// 关闭 channel
close(ch)
// range 遍历 channel(直到关闭)
for v := range ch {
fmt.Println(v)
}
// select 多路复用(类似 Python select.select)
ch1 := make(chan int)
ch2 := make(chan string)
go func() { ch1 <- 1 }()
go func() { ch2 <- "hi" }()
select {
case v1 := <-ch1:
fmt.Println("ch1:", v1)
case v2 := <-ch2:
fmt.Println("ch2:", v2)
case <-time.After(1 * time.Second): // 超时
fmt.Println("timeout")
default: // 非阻塞
fmt.Println("no data")
}
3. 并发模式示例
Worker Pool(类似 Python 的进程池)
go
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("worker %d processing job %d\n", id, j)
time.Sleep(time.Second)
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// 启动 3 个 worker
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送 5 个任务
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// 收集结果
for a := 1; a <= 5; a++ {
<-results
}
}
并发安全(sync.Mutex)
go
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock() // 确保解锁,类似 Python 的 try-finally
c.count++
}
// 对比 Python:需要 threading.Lock
# lock = threading.Lock()
# with lock:
# count += 1
4. Context(请求生命周期管理)
go
// 带超时的上下文(微服务必备)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-time.Sleep(3 * time.Second):
fmt.Println("work done")
case <-ctx.Done():
fmt.Println("timeout:", ctx.Err()) // context deadline exceeded
}
错误处理 {#错误处理}
Go 显式错误处理 vs Python 异常机制。
1. 基本模式
go
// 函数返回 (result, error)
func readFile(filename string) ([]byte, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("read %s: %w", filename, err) // 包装错误
}
return data, nil
}
// 使用
content, err := readFile("config.json")
if err != nil {
log.Fatal(err)
}
// 错误链检查(Go 1.13+)
if errors.Is(err, os.ErrNotExist) {
fmt.Println("file not found")
}
2. 对比 Python
python
# Python:try-except 捕获异常
try:
with open("config.json") as f:
content = f.read()
except FileNotFoundError:
print("file not found")
except Exception as e:
log.error(e)
raise
Go 哲学:错误是值,不是异常。显式处理每个错误,代码虽冗长但健壮。
3. panic 与 recover(类似异常,但少用)
go
// panic 类似 raise,停止当前 goroutine 的调用栈
func mayPanic() {
panic("something went wrong")
}
// recover 类似 except,只在 defer 中有效
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
mayPanic()
fmt.Println("这行不会执行")
}
原则:库代码返回 error,程序级错误才 panic。
标准库常用包 {#标准库常用包}
| Go 包 | 用途 | Python 对应 |
|---|---|---|
fmt |
格式化 I/O | print, str.format |
os |
操作系统接口 | os, sys |
io/bufio |
读写接口 | io |
net/http |
HTTP 客户端/服务器 | http.client, flask |
encoding/json |
JSON 处理 | json |
database/sql |
SQL 数据库接口 | sqlite3, SQLAlchemy |
time |
时间处理 | datetime |
strings/strconv |
字符串/转换 | str, int() |
sync |
并发原语 | threading |
context |
请求上下文 | 无直接对应 |
HTTP Server 示例(一行 Python vs Go)
python
# Python Flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
# Go 标准库(无需框架)
package main
import (
"fmt"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8080", nil)
}
工程化实践 {#工程化实践}
1. 项目结构(标准布局)
csharp
myapp/
├── cmd/ # 可执行程序入口
│ ├── server/ # 服务1
│ │ └── main.go
│ └── cli/ # 命令行工具
│ └── main.go
├── internal/ # 私有代码(不可被外部导入)
│ ├── auth/ # 认证模块
│ └── db/ # 数据库
├── pkg/ # 可复用库代码
│ └── utils/
├── api/ # API 定义(proto/openapi)
├── web/ # 静态资源
├── configs/ # 配置文件
├── go.mod
├── go.sum
└── README.md
2. 测试
go
// math.go
func Add(a, b int) int {
return a + b
}
// math_test.go(_test.go 后缀)
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}
// 表驱动测试(Go 惯用模式)
func TestAddTable(t *testing.T) {
tests := []struct {
a, b, want int
}{
{2, 3, 5},
{0, 0, 0},
{-1, 1, 0},
}
for _, tt := range tests {
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
}
}
}
// 基准测试
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
运行 :go test -v(详细输出),go test -bench=.(基准测试)
3. 常用工具链
bash
go fmt ./... # 格式化代码(强制风格统一)
go vet ./... # 静态分析,检查潜在错误
golint ./... # 风格检查(需安装)
go mod tidy # 整理依赖
# 性能分析
go test -cpuprofile=cpu.out
go tool pprof cpu.out
从 Python 到 Go 的思维转换 {#思维转换}
| Python 思维 | Go 思维 | 转换建议 |
|---|---|---|
| "写代码要快" | "代码要读6个月后的自己" | 接受显式错误处理,写注释 |
| "动态类型真方便" | "编译器是我最好的朋友" | 利用类型安全减少运行时 bug |
| "继承实现复用" | "组合优于继承" | 用嵌入结构体 + 接口 |
| "线程/进程处理并发" | "goroutine + channel" | 忘掉 GIL,拥抱 CSP 模型 |
| "异常处理所有错误" | "error 是值,显式处理" | 每个错误都检查,不 panic |
| "pip install 解决一切" | "标准库 + 少量依赖" | 优先用标准库,谨慎引入第三方 |
| "写脚本快速验证" | "写测试快速验证" | 养成写单元测试的习惯 |
学习路线图
- 第 1 周:语法基础(变量、控制流、函数、结构体)
- 第 2 周:复合类型(slice、map、接口)
- 第 3 周:并发编程(goroutine、channel、select)
- 第 4 周:标准库实战(HTTP、JSON、数据库)
- 第 5-6 周:项目实战(Web 服务、CLI 工具、爬虫)
推荐资源
- 官方 :A Tour of Go(交互式教程)
- 书籍:《Go 程序设计语言》(Kernighan 著,经典)
- 社区 :Go 夜读、GopherChina
- 练习 :LeetCode Go 题解、Exercism Go 路径
Go 的设计哲学:简单、显式、组合、并发。作为 Python 开发者,你会失去一些灵活性,但获得编译期安全、极致性能和优雅的并发模型。从写一个小型 HTTP 服务开始,你会爱上这种"less is more"的体验。