Skip to content

Python 开发者学习 Go 指南

如果你熟悉 Python 和脚本编程,这份指南将帮助你快速上手 Go 语言。

快速对比表

特性PythonGo
类型系统动态类型静态类型
执行方式解释执行编译执行
包管理pip/poetryGo Modules
Web 框架Django/Flask/FastAPIZoox
列表/数组list切片/数组
字典dictMap
错误处理try-except显式错误返回
并发threading/asyncioGoroutine/Channel
部署需要 Python 运行时单一可执行文件
性能较慢接近 C 语言

概念映射

变量和类型

Python:

python
# 动态类型
name = "Alice"
age = 30
price = 19.99

# 类型提示(Python 3.5+)
def greet(name: str) -> str:
    return f"Hello, {name}"

Go:

go
// 静态类型
var name string = "Alice"
age := 30
price := 19.99

// 函数签名
func greet(name string) string {
    return fmt.Sprintf("Hello, %s", name)
}

关键差异:

  • Go 是静态类型,编译时检查
  • Go 使用 := 进行短变量声明
  • Go 的类型系统更严格,不能随意转换

列表和切片

Python:

python
# 列表
numbers = [1, 2, 3, 4, 5]
numbers.append(6)

# 列表推导式
doubled = [x * 2 for x in numbers]
evens = [x for x in numbers if x % 2 == 0]

Go:

go
// 切片(动态数组)
numbers := []int{1, 2, 3, 4, 5}
numbers = append(numbers, 6)

// 需要手动实现
var doubled []int
for _, x := range numbers {
    doubled = append(doubled, x*2)
}

var evens []int
for _, x := range numbers {
    if x%2 == 0 {
        evens = append(evens, x)
    }
}

关键差异:

  • Go 没有列表推导式,需要手动实现
  • Go 的切片类似 Python 列表,但更底层
  • Go 区分数组(固定长度)和切片(动态)

字典和 Map

Python:

python
# 字典
person = {
    "name": "Alice",
    "age": 30
}

# 访问
name = person["name"]
age = person.get("age", 0)

# 遍历
for key, value in person.items():
    print(f"{key}: {value}")

Go:

go
// Map
person := map[string]interface{}{
    "name": "Alice",
    "age":  30,
}

// 访问
name := person["name"].(string)
age, ok := person["age"]
if !ok {
    age = 0
}

// 遍历
for key, value := range person {
    fmt.Printf("%s: %v\n", key, value)
}

关键差异:

  • Go 的 Map 需要明确类型
  • Go 需要类型断言来获取值
  • Go 使用 ok 检查键是否存在

类和结构体

Python:

python
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def greet(self):
        return f"Hello, I'm {self.name}"

person = Person("Alice", 30)

Go:

go
type Person struct {
    name string
    age  int
}

func NewPerson(name string, age int) *Person {
    return &Person{name: name, age: age}
}

func (p *Person) Greet() string {
    return fmt.Sprintf("Hello, I'm %s", p.name)
}

person := NewPerson("Alice", 30)

关键差异:

  • Go 使用结构体而不是类
  • Go 没有构造函数,使用工厂函数
  • Go 的方法通过接收者定义

错误处理

Python:

python
try:
    data = read_file("file.txt")
    # 处理数据
except FileNotFoundError as e:
    print(f"文件不存在: {e}")
except Exception as e:
    print(f"发生错误: {e}")

Go:

go
data, err := os.ReadFile("file.txt")
if err != nil {
    return fmt.Errorf("读取文件失败: %w", err)
}
// 处理数据

关键差异:

  • Go 不使用异常,使用显式错误返回
  • Go 的错误是值,不是异常对象
  • Go 要求显式检查每个错误

并发编程

Python (threading):

python
import threading

def worker():
    print("Worker running")

thread = threading.Thread(target=worker)
thread.start()
thread.join()

Python (asyncio):

python
import asyncio

async def fetch_data():
    await asyncio.sleep(1)
    return "data"

async def main():
    data = await fetch_data()
    print(data)

asyncio.run(main())

Go (Goroutine/Channel):

go
// Goroutine
go func() {
    fmt.Println("Worker running")
}()

// Channel(类似 Future)
ch := make(chan string)
go func() {
    ch <- "data"
}()
data := <-ch

关键差异:

  • Go 使用 Goroutine(轻量级线程)而不是线程
  • Go 使用 Channel 进行通信
  • Go 的并发模型更简单高效

Web 开发

Python (Flask):

python
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/users/<int:id>')
def get_user(id):
    user = get_user_by_id(id)
    if not user:
        return jsonify({'error': '用户不存在'}), 404
    return jsonify(user)

Python (Django):

python
from django.http import JsonResponse

def get_user(request, id):
    try:
        user = User.objects.get(pk=id)
        return JsonResponse(user.to_dict())
    except User.DoesNotExist:
        return JsonResponse({'error': '用户不存在'}, status=404)

Python (FastAPI):

python
from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/users/{id}")
async def get_user(id: int):
    user = get_user_by_id(id)
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
    return user

Go (Zoox):

go
app := zoox.New()

app.Get("/users/:id", func(c *zoox.Context) {
    id, _ := strconv.Atoi(c.Param("id"))
    user, err := getUser(id)
    if err != nil {
        c.JSON(404, zoox.H{"error": "用户不存在"})
        return
    }
    c.JSON(200, user)
})

关键差异:

  • Go 的框架更轻量
  • Go 的错误处理是显式的
  • Go 的性能通常更好

快速上手路径

第 1 步:理解类型系统(2-3 天)

从动态类型到静态类型是最大的挑战:

  1. 类型声明 - 所有变量都需要类型
  2. 类型转换 - 需要显式转换
  3. 类型推断 - 使用 := 简化声明

重点章节:

第 2 步:学习编译和部署(1-2 天)

Python 是解释型,Go 是编译型:

  1. 编译 - go build 生成可执行文件
  2. 部署 - 单一可执行文件,不需要运行时
  3. 交叉编译 - 可以编译到不同平台

重点章节:

第 3 步:掌握 Web 框架(2-3 天)

使用 Zoox 框架,类似 Flask/FastAPI,但更简单:

  1. 路由 - 类似 Flask/FastAPI 路由
  2. 中间件 - 类似 Flask 中间件
  3. 错误处理 - 显式错误返回

重点章节:

第 4 步:学习并发编程(3-5 天)

Python 使用 threading/asyncio,Go 使用 Goroutine:

  1. Goroutine - 轻量级线程
  2. Channel - 用于通信
  3. 并发模式 - Worker Pool 等

重点章节:

代码对比示例

列表操作

Python:

python
numbers = [1, 2, 3, 4, 5]

# Map
doubled = [x * 2 for x in numbers]

# Filter
evens = [x for x in numbers if x % 2 == 0]

# Reduce
from functools import reduce
sum = reduce(lambda acc, x: acc + x, numbers, 0)

Go:

go
numbers := []int{1, 2, 3, 4, 5}

// Map
doubled := make([]int, len(numbers))
for i, x := range numbers {
    doubled[i] = x * 2
}

// Filter
var evens []int
for _, x := range numbers {
    if x%2 == 0 {
        evens = append(evens, x)
    }
}

// Reduce
sum := 0
for _, x := range numbers {
    sum += x
}

文件操作

Python:

python
# 读取文件
with open('file.txt', 'r') as f:
    data = f.read()

# 写入文件
with open('output.txt', 'w') as f:
    f.write('Hello, World!')

Go:

go
// 读取文件
data, err := os.ReadFile("file.txt")
if err != nil {
    log.Fatal(err)
}

// 写入文件
err := os.WriteFile("output.txt", []byte("Hello, World!"), 0644)
if err != nil {
    log.Fatal(err)
}

JSON 处理

Python:

python
import json

# 编码
person = {"name": "Alice", "age": 30}
json_str = json.dumps(person)

# 解码
data = json.loads(json_str)
name = data["name"]

Go:

go
import "encoding/json"

// 编码
person := map[string]interface{}{
    "name": "Alice",
    "age":  30,
}
jsonData, err := json.Marshal(person)

// 解码
var data map[string]interface{}
err := json.Unmarshal(jsonData, &data)
name := data["name"].(string)

并发处理

Python (asyncio):

python
import asyncio

async def fetch_url(url):
    # 模拟网络请求
    await asyncio.sleep(1)
    return f"Data from {url}"

async def main():
    urls = ["/api/1", "/api/2", "/api/3"]
    tasks = [fetch_url(url) for url in urls]
    results = await asyncio.gather(*tasks)
    return results

Go (Goroutine + WaitGroup):

go
import "sync"

func fetchURL(url string) string {
    // 模拟网络请求
    time.Sleep(1 * time.Second)
    return fmt.Sprintf("Data from %s", url)
}

func fetchMultiple() []string {
    urls := []string{"/api/1", "/api/2", "/api/3"}
    var wg sync.WaitGroup
    results := make([]string, len(urls))
    
    for i, url := range urls {
        wg.Add(1)
        go func(idx int, u string) {
            defer wg.Done()
            results[idx] = fetchURL(u)
        }(i, url)
    }
    
    wg.Wait()
    return results
}

常见陷阱

1. 类型转换习惯

Python 自动转换:

python
result = "5" + str(3)  # "53"
result = int("5") + 3  # 8

Go 需要显式转换:

go
// 错误
result := "5" + 3

// 正确
result := "5" + strconv.Itoa(3)
// 或者
num, _ := strconv.Atoi("5")
result := num + 3

2. 列表和切片混淆

Python 列表是动态的:

python
arr = []
arr.append(1)
arr.append(2)

Go 区分数组和切片:

go
// 数组(固定长度)
var arr [2]int
arr[0] = 1
arr[1] = 2

// 切片(动态)
var slice []int
slice = append(slice, 1)
slice = append(slice, 2)

3. 错误处理方式

Python 使用异常:

python
try:
    data = fetch_data()
except Exception as e:
    print(f"错误: {e}")

Go 使用显式错误返回:

go
data, err := fetchData()
if err != nil {
    return fmt.Errorf("错误: %w", err)
}

4. 并发编程概念

Python 使用 threading/asyncio:

python
# threading
import threading
thread = threading.Thread(target=worker)
thread.start()

# asyncio
import asyncio
async def main():
    await asyncio.sleep(1)

Go 使用 Goroutine:

go
go func() {
    // 并发执行
}()

5. 没有列表推导式

Python:

python
squares = [x**2 for x in range(10)]

Go:

go
var squares []int
for i := 0; i < 10; i++ {
    squares = append(squares, i*i)
}

学习建议

  1. 适应静态类型:这是最大的挑战,需要时间适应
  2. 理解编译过程:Go 是编译型语言,部署方式不同
  3. 学习并发编程:Python 的并发模型与 Go 完全不同
  4. 利用 Web 开发经验:你的 Python Web 开发经验很有用
  5. 性能优势:Go 的性能通常比 Python 好很多

推荐学习顺序

  1. Go 语言简介 - 理解编译型语言
  2. 基础语法 - 重点学习类型系统
  3. 函数和方法 - 注意多返回值
  4. 数据结构 - 理解数组、切片、Map
  5. 接口和类型 - 类似 Python 的协议
  6. 错误处理 - 适应新的错误处理方式
  7. 并发编程 - 重点学习
  8. 包和模块 - 类似 pip
  9. 实战项目 - Web API 开发

下一步

现在你已经了解了从 Python 到 Go 的转换要点,建议:

  1. 重点学习类型系统 - 这是最大的差异
  2. 适应错误处理方式 - 从异常到显式错误返回
  3. 学习并发编程 - Python 的并发模型与 Go 完全不同
  4. 完成 Web 项目 - 利用你的 Python Web 开发经验

祝你学习愉快!

基于 VitePress 构建