精通Docker第三版第九章更新中...

Google资深工程师深度讲解Go语言

学习笔记 Alan 3周前 (04-04) 134次浏览 2个评论

Go语言的安装与开发环境
基础语法
内建容器
面向“对象”
面向接口
函数式编程
错误处理和资源管理
测试与性能调优
Goroutine
Channel
http及其他标准库
迷宫的广度优先搜索
开始实战项目
单任务版爬虫
并发版爬虫
数据存储和展示
分布式爬虫
课程总结

Go语言的安装与开发环境

下载:

国内:http://studygolang.com/dl

https://golang.org/dl/

开发环境:vi, emacs, idea, eclipse, vs, sublime … + go 插件

IDE:Goland, liteIDE

本课程使用 idea + go 插件

本文GitHub仓库:https://github.com/alanhou/learning-go

基础语法

变量

变量定义

  • var a, b, c bool
  • var s1, s2 string = “hello”, “world”
  • 可放在函数内,或直接放在包内
  • 使用 var()集中定义变量
  • 让编译器自动决定类型
    • var a, b, i, s1, s2 = true, false, 3, “hello”, “world”
  • 使用 := 定义变量
    • a, b, i, s1, s2 := true, false, 3, “hello”, “world”
    • 只能在函数内使用

内建变量类型

  • bool, string
  • (u)int, (u)int8, (u)int16, (u)int32, (u)int64, uintptr(指针)未指令长度时根据操作系统是多少位来决定
  • byte, rune(长度32位,相当于 char,解决多国语言问题)
  • float32, float64, complex64, complex128

复数回顾Google资深工程师深度讲解Go语言

  • i = \(\sqrt{-1}\)
  • 复数:3 + 4i (实部+虚部)
  • \(|3 + 4i| = \sqrt{3^2 + 4^2} =5\)
  • \(i^2 = -1, i^3 = -i, i^4 = 1,…\)
  • \(e^{i\phi} = cos\phi + isin\phi\)
  • \(|e^{i\phi}| = \sqrt{cos^2\phi + sin^2\phi} = 1\)
  • \(e^0 = 1, e^{i{\pi\over2}} = i\)
  • \(e^{i\pi} = -1, e^{i{3\over2}\pi} = -i, e^{i2\pi} = 1\)

最美公式 – 欧拉公式Google资深工程师深度讲解Go语言

$$e^{i\pi} + 1 = 0$$

强制类型转换

  • 类型转换是强制的
  • var a, b int = 3, 4
  • var c int = math.Sqrt(a*a + b*b)   
  • var c int = int(math.Sqrt(float64(a*a + b*b)))  ?(尚有浮点数所带来的偏差问题需解决)

常量与枚举

常量的定义

  • const filename = “abc.txt”
  • const 数值可作为各种类型使用
  • const a,b = 3,4
  • var c = int(math.Sqrt(a*a + b*b)) // a,b 未指定类型无需转换为 float

使用常量定义枚举类型

  • 普通枚举类型
  • 自增值枚举类型

变量定义要点回顾

  • 变量类型写在变量名之后
  • 编译器可推测变量类型
  • 没有 char,只有 rune
  • 原生支持复数类型

条件语句

if

  • if 的条件里不需要括号

  • if 的条件里可以赋值
  • if 的条件里赋值的变量作用域就在这个 if 语句里

switch

  • switch 会自动 break,除非使用fallthrough
  • switch 后可以没有表达式

循环

for

  • for 的条件里不需要括号
  • for 的条件里可以省略初始条件,结束条件,递增表达式

  • 省略初始条件,相当于 while

  • 省略所有条件,无限循环/死循环

基本语法要点回顾

  • for,if后面的条件没有括号
  • if 条件里也可定义变量
  • 没有 while
  • switch 不需要 break,也可直接 switch 多个条件

函数

  • func eval(a, b int, op string) int
  • 函数可返回多个值
  • 函数返回多个值时可以起名字,但仅用于非常简单的函数,对于调用者而言没有区别
  • 函数可以作为参数,函数式编程
  • 可变参数列表

函数语法要点回顾

  • 返回值类型写在最后面
  • 可返回多个值
  • 函数可作为参数
  • 没有默认参数、可选参数

指针

  • 指针不能运算

参数传递

值传递?引用传递?

  • Go 语言只有值传递一种方式

内建容器

数组、切片和容器

数组

  • 数量写在类型前
  • 可通过 _ 来省略变量,不仅仅是 range,任何地方都可通过 _ 来省略变量
  • 如果只要下标 i,可写成for i := range numbers

为人么要用 range?

  • 意义明确、美观
  • c++:没有类似能力
  • Java/Python:只能 for each value,不能同时获取 i, v

数组是值类型

  • [10]int 和[20]int 是不同类型
  • 调用 func f(arr [10]int)会 拷贝 数组
  • 在 go 语言中一般不直接使用数组(指针),使用切片

切片(Slice)

  • s 就是一个切片,值为[2 3 4 5]
  • Slice本身没有数据,是对底层 array 的一个 view
  • arr 的值变为 [0 1 10 3 4 5 6 7]

Reslice

Slice 的扩展

  • s1的值为?
  • s2的值为?

Google资深工程师深度讲解Go语言

  • s1的值下为[2 3 4 5],s2的值为[5 6]
  • slice 可以向后扩展,不可以向前扩展
  • s[i]不可以超越 len(s),向后扩展不可以超越底层数组 cap(s)

Slice 的实现

Google资深工程师深度讲解Go语言

向 Slice 添加元素

  • s3, s4, s5的值为?arr 的值为?
  • 添加元素时如果超越 cap,系统会重新分配更大的底层数组
  • 由于值传递的关系,必须接收 append 的返回值
  • s = append(s, val)

Map

  • map[K]V, map[K1]map[K2]V(复合 map)

map 的操作

  • 创建:make(map[string] int)
  • 获取元素:m[key]
  • key 不存在时,获得Value 类型的初始值(Zero value)
  • 用 value, ok := m[key]来判断是否存在 key
  • 用 delete 删除一个 key

map 的遍历

  • 使用 range 遍历 key,或者遍历 key, value 对
  • 不保证遍历顺序,如需顺序,需手动对 key 排序
  • 使用 len 获取元素个数

map 的 key

  • map 使用哈希表,必须可以比较相等
  • 除 slice, map, function 外的内建类型都可以作为 key
  • Struct 类型不包含上述字段,也可作为 key

map例题

寻找最长不含有重复字符的子串

解题思路:

对于一个字母 x

  • lastOccurred[x]不存在,或者<start → 无需操作
  • lastOccurred[x] >= start → 更新 start
  • 更新 lastOccurred[x],更新 maxLength

解决中文等国际化字符的问题:

rune相当于 go 语言的 char

  • 使用 range 遍历 pos, rune 对
  • 使用 utf8.RuneCountInString 获得字符数量
  • 使用 len 获得字节长度
  • 使用[]byte 获得字节

其它字符串操作

  • Fields, Split, Join
  • Contains, Index
  • ToLower, ToUpper
  • Trim, TrimRight, TrimLeft

strings 包下有更多可进行查看

面向“对象”

结构体和方法

面向对象

  • go 语言仅支持封装,不支持继承和多态
  • go语言没有 class,只有 struct

结构的创建

  • 不论地址还是结构本身,一律使用 . 来访问成员

  • 使用自定义工厂函数
  • 注意返回了局部变量的地址!

结构创建在堆上还是栈上?

  • 不需要知道

为结构定义方法

  • 显示定义和命名方法接收者

使用指针作为方法接收者

  • 只有使用指针才可以改变结构内容
  • nil 指针也可以调用方法!

值接收者 vs 指针接收者

  • 要改变内容必须使用指针接收者
  • 结构过大也考虑使用指针接收者(性能考虑)
  • 一致性:如有指针接收者,最好都是指针接收者
  • 值接收者是 go语言特有
  • 值/指针接收者均可接收值/指针

包和封装

封装

  • 名字一般使用 CamelCase
  • 首字母大写:public
  • 首字母小写:private

  • 每个目录一个包
  • main 包包含可执行入口
  • 为结构定义的方法必须放在同一包内
  • 可以是不同文件

go 语言中如何扩充系统类型或者别人的类型

  • 定义别名
  • 使用组合

GOPATH以及目录结构

GOPATH 环境变量

  • 默认在~/go(Unix, Linux), %USERPROFILE%\go(Windows)
  • 官方推荐:所有项目和第三方库都放在同一个 GOPATH 下
  • 也可以将每个项目放在不同的 GOPATH

以 Mac 为值(~/.bash_profile)

go get 获取第三方库

  • go get 命令演示
  • 使用 gopm 来获取无法下载的包(如官网 golang.org 下的包)

    通过Preferences 的如下配置可以在保存时自动整理所导入的包,如删除未使用或错误的包导入(IDEA 原来通过 On Save 来实现,已淘汰并即将删除)

Google资深工程师深度讲解Go语言

  • go build 来编译
  • go install 产生 pkg 文件和可执行文件
  • go run 直接编译运行

GOPATH下目录结构

  • src
    • git repository 1
    • git repository 2
  • pkg
    • git repository 1
    • git repository 2
  • bin
    • 执行文件1, 2, 3…

面向接口

duck typing的概念

Google资深工程师深度讲解Go语言

大黄鸭是鸭子吗?

  • 传统类型系统:脊索动物门,脊椎动物亚门,鸟纲雁形目…  不是
  • duck typing:是鸭子
  • 概念:“像鸭子走路,像鸭子叫(长得像鸭子),那么就是鸭子”
  • 描述事物的外部行为而非内部结构
  • 严格说 go 属于结构化类型系统,类似 duck typing

Python 中的 duck typing

  • 运行时才知道传入的 retriever 有没有 get
  • 需要注释来说明接口

C++中的 duck typing

  • 编译时才知道传入的 retriever 有没有 get
  • 需要注释来说明接口

Java 中的类似代码

  • 传入的参数必须实现 Retriever 接口
  • 不是 duck typing
  • 同时需要 Readable, Appendable 怎么办?(apache polygene)

Go 语言的 duck typing

  • 同时具有 Python, C++的 duck typing 的灵活性
  • 又具有 Java 的类型检查

接口的定义和实现

接口的定义

使用者(download)→实现者(retriever)

  • 接口由使用者定义

接口的实现

  • 接口的实现是隐式的
  • 只要实现接口里的方法

接口的值类型

接口变量里面有什么

接口变量

  • 实现者的类型
  • 实现者的值或实现者的指针

接口变量里面有什么

  • 接口变量自带指针
  • 接口变量同样采用值传递,几乎不需要使用接口的指针
  • 指针接收者实现只能以指针方式使用;值接收者都可

查看接口变量

  • 表示任何类型:interface{}
  • Type Assertion
  • Type Switch

接口的组合

常用系统接口

  • Stringer
  • Reader/Writer

函数式编程

函数与闭包

函数式编程 vs. 函数指针

  • 函数是一等公民:参数、变量、返回值都可以是函数
  • 高阶函数
  • 函数 → 闭包

“正统”函数式编程

  • 不可变性:不能有状态,只有常量和函数
  • 函数只能有一个参数
  • 本课程不作上述规定

闭包

Google资深工程师深度讲解Go语言

Python 中闭包

  • Python 原生支持闭包
  • 使用__closure__来查看闭包的内容

C++中的闭包

  • 过去:stl 或者 boost 带有类似库
  • C++11及以后:支持闭包

Java 中的闭包

  • 1.8以后:使用 Function 接口和 Lambda表达式来创建函数对象
  • 匿名类或 Lambda 表达式均支持闭包

Go语言闭包的应用

  • 例一:斐波那契数列
  • 例二:为函数实现接口
  • 例三:使用函数来遍历二叉树
    Google资深工程师深度讲解Go语言

总结

  • 更为自然,不需要修饰如何访问自由变量
  • 没有 Lambda 表达式,但是有匿名函数

错误处理和资源管理

defer调用

  • 确保调用在函数结束时发生
  • 参数在 defer 语句时计算
  • defer 列表为后进先出

何时使用 defer 调用

  • Open/Close
  • Lock/Unlock
  • PrintHeader/PrintFooter

错误处理概念

错误处理

服务器统一出错处理

  • 如何实现统一错误处理逻辑

panic和recover

服务器统一出错处理2

测试与性能调优

Goroutine

Channel

http及其他标准库

迷宫的广度优先搜索

开始实战项目

单任务版爬虫

并发版爬虫

数据存储和展示

分布式爬虫

课程总结

更新中…

喜欢 (1)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(2)个小伙伴在吐槽
  1. 一如既往地优秀,坐等更新
    向往2019-04-08 10:41 回复