From 2a321fffd9850030db65cc7fbfc2ab02b93053e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 22:42:18 +0800 Subject: [PATCH 001/147] =?UTF-8?q?update:=20=E6=94=AF=E6=8C=81=20windows?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E4=B8=94=E5=BC=95=E5=85=A5=20git=20=E5=BA=93?= =?UTF-8?q?=E5=81=9A=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- md2rst.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/md2rst.py b/md2rst.py index bd2fa23..69d2365 100644 --- a/md2rst.py +++ b/md2rst.py @@ -1,33 +1,40 @@ # coding:utf-8 import os -import commands +# import commands import subprocess import platform +from git import Repo +repo = Repo.init(path='.') +if not repo.is_dirty(): + # 没有文件变更 + os._exit(0) + blog_path = '' base_link = "http://python-online.cn/zh_CN/latest/" readme_header = ''' 这是我的个人博客( [MING's BLOG](http://python-online.cn/) ),主要写关于Python的一些思考总结。 -关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python-online.cn/zh_CN/latest/c04/c04_03.rst) +关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python-online.cn/zh_CN/latest/c04/c04_03.html) ''' readme_tooter = ''' --- ![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) + ''' osName = platform.system() if (osName == 'Windows'): - blog_path = 'F:\\VMP-Code\\07. PythonCodingTime\\source' - index_path = 'F:\\VMP-Code\\07. PythonCodingTime\\README.md' + blog_path = 'E:\\MING-Git\\06. PythonCodingTime\\source' + index_path = 'E:\\MING-Git\\06. PythonCodingTime\\README.md' elif (osName == 'Darwin'): blog_path = '/Users/MING/Github/PythonCodingTime/source' index_path = '/Users/MING/Github/PythonCodingTime/README.md' def get_file_info(filename): - with open(filename, 'r') as file: + with open(filename, 'r', encoding="utf-8") as file: first_line = file.readline().replace("#", "").strip() return first_line.split(' ', 1) @@ -64,10 +71,11 @@ def convert_md5_to_rst(file): convert_cmd = 'pandoc -V mainfont="SimSun" -f markdown -t rst {md_file} -o {rst_file}'.format( md_file=filename+'.md', rst_file=filename+'.rst' ) - status, output = commands.getstatusoutput(convert_cmd) - #retcode = subprocess.call(convert_cmd) + # status, output = commands.getstatusoutput(convert_cmd) + status = subprocess.call(convert_cmd) if status != 0: - print(output) + print("命令执行失败: " + convert_cmd) + os._exit(1) if status == 0: print(file + ' 处理完成') else: @@ -95,7 +103,7 @@ def init_index_info(): os.chdir(chapter_dir) for file in os.listdir(chapter_dir): name, _ = os.path.splitext(file) - with open(file, 'r') as f: + with open(file, 'r', encoding="utf-8") as f: chapter_name = f.readlines()[1].strip() index_info[name.replace("p", "")] = {"name": chapter_name, "contents": []} return index_info From 4ce99178d8954d58528fea65b6af32913a466e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 22:43:22 +0800 Subject: [PATCH 002/147] =?UTF-8?q?update=EF=BC=9A=E4=BF=AE=E8=AE=A2Go?= =?UTF-8?q?=E5=8E=86=E5=8F=B2=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c09/c09_01.md | 13 +- source/c09/c09_02.md | 2 +- source/c09/c09_05.md | 240 +++++++++++++++--- source/c09/c09_06.md | 181 ++++++++++---- source/c09/c09_07.md | 291 +++++++++++----------- source/c09/c09_08.md | 397 ++++++++++++++---------------- source/c09/c09_09.md | 323 +++++++++++++++--------- source/c09/c09_10.md | 176 ++++++------- source/c09/c09_11.md | 571 ++++++++++++------------------------------- source/c09/c09_12.md | 236 +++++++++--------- source/c09/c09_13.md | 167 ++++++++++++- source/c09/c09_14.md | 237 ++++++++++++------ 12 files changed, 1542 insertions(+), 1292 deletions(-) diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index 1c06d36..3919e76 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -1,6 +1,6 @@ # 9.1 开发环境的搭建(Goland和VSCode) -## 9.1.1 为什么学习Go? +## 1. 为什么学习Go? 一直很喜欢 Python 的我,为什么突然学起了Go呢? @@ -26,7 +26,7 @@ 这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。Go语言做到了真正的国际化! -## 9.1.2 下载安装 Go语言 +## 2. 下载安装 Go语言 下载地址:https://golang.google.cn/dl/ @@ -40,7 +40,7 @@ ![](http://image.python-online.cn/20200102221840.png) -## 9.1.3 配置 Goland 环境 +## 3. 配置 Goland 环境 学习编程语言,使用一个称心的 IDE,可以帮你省去很多麻烦。 @@ -126,7 +126,7 @@ Goland 下载地址:https://download.jetbrains.com/go/goland-2019.2.3.exe ![](http://image.python-online.cn/20200102225550.png) -## 9.1.4 配置 VS Code 环境 +## 4. 配置 VS Code 环境 提前设置用户级的环境变量 @@ -216,7 +216,7 @@ $ git clone https://github.com/golang/lint.git -## 9.1.5 配置环境变量 +## 5. 配置环境变量 当你在终端使用 `go env` 的时候,会打印出go 相关的所有环境变量 @@ -289,7 +289,4 @@ $ go env -w GO111MODULE=on ``` - - - ![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md index 56e4b20..b3b9448 100644 --- a/source/c09/c09_02.md +++ b/source/c09/c09_02.md @@ -125,7 +125,7 @@ age: 28 ptr: 0xc000010098 ``` -而这里要说的 new 函数,是 Go 里的一个内奸函数。 +而这里要说的 new 函数,是 Go 里的一个内建函数。 使用表达式 new(Type) 将创建一个Type类型的匿名变量,初始化为Type类型的零值,然后返回变量地址,返回的指针类型为`*Type`。 diff --git a/source/c09/c09_05.md b/source/c09/c09_05.md index f8378f7..4587576 100644 --- a/source/c09/c09_05.md +++ b/source/c09/c09_05.md @@ -1,59 +1,235 @@ -# 9.5 详解数据类型:布尔 +# 9.7 详解数据类型:数组与切片 -### 布尔类型 +## 1. 数组 -关于布尔值,其实没什么说的,无非就两个值。只是这两个值,在不同的语言里可能不同。 +数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,所以在Go语言中很少直接使用数组。 -在 Python 中,真值用 True 表示,与 1 相等,假值用 False 表示,与 0 相等 +声明数组,并给该数组里的每个元素赋值(索引值的最小有效值和其他大多数语言一样是 0,不是1) -```python ->>> True == 1 -True ->>> False == 0 -True ->>> +```go +// [3] 里的3 表示该数组的元素个数 +var arr [3]int +arr[0] = 1 +arr[1] = 2 +arr[2] = 3 ``` -而在 Go 中,真值用 true 表示,不但不与 1 相等,并且更加严格,不同类型无法进行比较,而假值用 false 表示,同样与 0 无法比较。如下图所示,Goland 直接波浪线提示类型不匹配,不能比较。 +声明并直接初始化数组 -![](http://image.python-online.cn/20200106201856.png) +```go +// 第一种方法 +var arr [3]int = [3]int{1,2,3} -在 Python 中使用 not 对布尔取值,而 Go 中使用 `!` 符号 +// 第二种方法 +arr := [3]int{1,2,3} +``` + +上面的 3 表示数组的元素个数 ,万一你哪天想往该数组中增加元素,你得对应修改这个数字,为了避免这种硬编码,你可以这样写,使用 `...` 让Go语言自己根据实际情况来分配空间。 ```go -import "fmt" +arr := [...]int{1,2,3} +``` -var male bool = true -func main() { - fmt.Println( !male == false) + + +`[3]int` 和 `[4]int` 虽然都是数组,但他们却是不同的类型,使用 fmt 的 `%T` 可以查得。 + +```go +import ( + "fmt" +) + +func main() { + arr01 := [...]int{1, 2, 3} + arr02 := [...]int{1, 2, 3, 4} + fmt.Printf("%d 的类型是: %T\n", arr01, arr01) + fmt.Printf("%d 的类型是: %T", arr02, arr02) } +``` -// output: true +输出 如下 + +``` +[1 2 3] 的类型是: [3]int +[1 2 3 4] 的类型是: [4]int ``` -一个 if 判断语句,有可能不只一个判断条件,在 Python 中是使用 `and` 和 `or` 来执行逻辑运算符, +如果你觉得每次写 `[3]int` 有点麻烦,你可以为 `[3]int` 定义一个类型字面量,也就是别名类型。 + +使用 `type` 关键字可以定义一个类型字面量,后面只要你想定义一个容器大小为3,元素类型为int的数组 ,都可以使用这个别名类型。 + +```go +import ( + "fmt" +) + +func main() { + type arr3 [3]int -```python ->>> age = 15 ->>> gender = "male" ->>> ->>> gender == "male" and age >18 -False + myarr := arr3{1,2,3} + fmt.Printf("%d 的类型是: %T", myarr, myarr) +} ``` -而在 Go 语言中,则使用 `&&` 和 `||` 。 +输出 如下 + +``` +[1 2 3] 的类型是: main.arr3 +``` + + + +## 2. 切片 + +切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。 + +切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(意思是这是个左闭右开的区间) ```go -import "fmt" +import ( + "fmt" +) -var age int = 15 -var gender string = "male" -func main() { - fmt.Println( gender == "male" && age > 18) +func main() { + myarr := [...]int{1, 2, 3} + fmt.Printf("%d 的类型是: %T", myarr[0:2], myarr[0:2]) } +``` + +输出 如下 -// output: false ``` +[1 2] 的类型是: []int +``` + + + +切片的构造,有三种方式 + +1. 对数组进行片段截取(上面例子已经展示:myarr[0:2],0是索引起始值,2是索引终止值,区间左半右开) + +2. 从头声明赋值(例子如下) + + ```go + // 声明字符串切片 + var strList []string + + // 声明整型切片 + var numList []int + + // 声明一个空切片 + var numListEmpty = []int{} + ``` + +3. 使用 make 函数构造,make 函数的格式:`make( []Type, size, cap )` + + 这个函数刚好指出了,一个切片具备的三个要素:类型(Type),长度(size),容量(cap) + + ```go + import ( + "fmt" + ) + + func main() { + a := make([]int, 2) + b := make([]int, 2, 10) + fmt.Println(a, b) + fmt.Println(len(a), len(b)) + fmt.Println(cap(a), cap(b)) +} + ``` + + 输出 如下 + + ``` + [0 0] [0 0] + 2 2 + 2 10 + ``` + + +关于 len 和 cap 的概念,可能不好理解 ,这里举个例子: + +- 公司名,相当于字面量,也就是变量名。 + +- 公司里的所有工位,相当于已分配到的内存空间 + +- 公司里的员工,相当于元素。 + +- cap 代表你这个公司最多可以容纳多少员工 + +- len 代表你这个公司当前有多少个员工 + + + +由于 切片是引用类型,所以你不对它进行赋值的话,它的零值(默认值)是 nil + +```go +var myarr []int +fmt.Println(myarr == nil) +// true +``` + + + +数组 与 切片 有相同点,它们都是可以容纳若干类型相同的元素的容器 + +也有不同点,数组的容器大小固定,而切片本身是引用类型,它更像是 Python 中的 list ,我们可以对它 append 进行元素的添加。 + +```go +import ( + "fmt" +) + +func main() { + myarr := []int{1} + // 追加一个元素 + myarr = append(myarr, 2) + // 追加多个元素 + myarr = append(myarr, 3, 4) + // 追加一个切片, ... 表示解包,不能省略 + myarr = append(myarr, []int{7, 8}...) + // 在第一个位置插入元素 + myarr = append([]int{0}, myarr...) + // 在中间插入一个切片(两个元素) + myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]...)...) + fmt.Println(myarr) +} +``` + +输出 如下 + +``` +[0 1 2 3 4 5 6 7 8] +``` + + + +最后,给你留一道思考题,如下 这段代码 + +```go +import ( + "fmt" +) + +func main() { + var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + myslice := numbers4[4:6:8] + fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice)) + + myslice = myslice[:cap(myslice)] + fmt.Printf("myslice的第四个元素为: %d", myslice[3]) +} +``` + +为什么 myslice 的长度为2,却能访问到第四个元素 + +``` +myslice为 [5 6], 其长度为: 2 +myslice的第四个元素为: 8 +``` + + diff --git a/source/c09/c09_06.md b/source/c09/c09_06.md index ef54557..00b8f7d 100644 --- a/source/c09/c09_06.md +++ b/source/c09/c09_06.md @@ -1,16 +1,18 @@ -# 9.6 详解数据类型:字典 +# 9.6 详解数据类型:字典与布尔类型 ## 1. 字典 -字典,是由若干个 `key:value` 这样的键值对映射组合在一起的数据结构。 +字典(Map 类型),是由若干个 `key:value` 这样的键值对映射组合在一起的数据结构。 -它是哈希表的一个实现,这就要求它的每个映射里的key,都是唯一的,可以使用 `==` 和 `!=` 来进行判等操作,换句话说就是可哈希的。 +它是哈希表的一个实现,这就要求它的每个映射里的key,都是唯一的,可以使用 `==` 和 `!=` 来进行判等操作,换句话说就是key必须是可哈希的。 -什么叫可哈希的?简单来说,一个不可变对象,都可以用一个哈希值来唯一表示,这样的不可变对象有字符串等(可以说除了切片、 字典,函数之外的其他内建类型) +什么叫可哈希的?简单来说,一个不可变对象,都可以用一个哈希值来唯一表示,这样的不可变对象,比如字符串类型的对象(可以说除了切片、 字典,函数之外的其他内建类型都算)。 -字典由key和value组成,各自有各自的类型。 +意思就是,你的 key 不能是切片,不能是字典,不能是函数。。 + +字典由key和value组成,它们各自有各自的类型。 在声明字典时,必须指定好你的key和value是什么类型的,然后使用 map 关键字来告诉Go这是一个字典。 @@ -18,15 +20,17 @@ map[KEY_TYPE]VALUE_TYPE ``` +### 声明初始化字典 - -举例:三种声明并初始化字典的方法 +三种声明并初始化字典的方法 ```go // 第一种方法 var scores map[string]int = map[string]int{"english": 80, "chinese": 85} + // 第二种方法 scores := map[string]int{"english": 80, "chinese": 85} + // 第三种方法 scores := make(map[string]int) scores["english"] = 80 @@ -54,27 +58,91 @@ func main() { } ``` +### **字典的相关操作** + +添加元素 + +```go +scores["math"] = 95 +``` + +更新元素,若key已存在,则直接更新value + +```go +scores["math"] = 100 +``` + +读取元素,直接使用 `[key]` 即可 ,如果 key 不存在,也不报错,会返回其value-type 的零值。 + +```go +fmt.Println(scores["math"]) +``` + +删除元素,使用 delete 函数,如果 key 不存在,delete 函数会静默处理,不会报错。 + +```go +delete(scores, "math") +``` + 当访问一个不存在的key时,并不会直接报错,而是会返回这个 value 的零值,如果 value的类型是int,就返回0。 ```go +package main + import "fmt" func main() { scores := make(map[string]int) - scores["chinese"] = 90 - fmt.Println(scores["english"]) // 输出 true + fmt.Println(scores["english"]) // 输出 0 +} +``` + + + +### 判断 key 是否存在 + +当key不存在,会返回value-type的零值 ,所以你不能通过返回的结果是否是零值来判断对应的 key 是否存在,因为 key 对应的 value 值可能恰好就是零值。 + +其实字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key 是否存在,若存在ok为true,若不存在,则ok为false + +```go +import "fmt" + +func main() { + scores := map[string]int{"english": 80, "chinese": 85} + math, ok := scores["math"] + if ok { + fmt.Printf("math 的值是: %d", math) + } else { + fmt.Println("math 不存在") + } +} +``` + +我们将上面的代码再优化一下 + +```go +import "fmt" + +func main() { + scores := map[string]int{"english": 80, "chinese": 85} + if math, ok := scores["math"]; ok { + fmt.Printf("math 的值是: %d", math) + } else { + fmt.Println("math 不存在") + } } ``` -**如何对字典进行循环** +### **如何对字典进行循环** Go 语言中没有提供类似 Python 的 keys() 和 values() 这样方便的函数,想要获取,你得自己循环。 -循环还分两种 +循环还分三种 1. 获取 key 和 value @@ -120,66 +188,91 @@ func main() { -**字典的相关操作** +## 2. 布尔类型 -添加 +关于布尔值,无非就两个值:true 和 false。只是这两个值,在不同的语言里可能不同。 -```go -scores["math"] = 95 +在 Python 中,真值用 True 表示,与 1 相等,假值用 False 表示,与 0 相等 + +```python +>>> True == 1 +True +>>> False == 0 +True +>>> ``` -更新,若key已存在,则直接更新value +而在 Go 中,真值用 true 表示,不但不与 1 相等,并且更加严格,不同类型无法进行比较,而假值用 false 表示,同样与 0 无法比较。 -```go -scores["math"] = 100 -``` +如下图所示,Goland 直接波浪线提示类型不匹配,不能比较。 -读取,直接使用 `[key]` 即可 ,如果 key 不存在,也不报错,会返回其value-type 的零值。 +![](http://image.python-online.cn/20200106201856.png) -```go -fmt.Println(scores["math"]) -``` +Go 中确实不如 Python 那样灵活,bool 与 int 不能直接转换,如果要转换,需要你自己实现函数。 -删除,使用 delete 函数,如果 key 不存在,delete 函数会静默处理,不会报错。 +**bool 转 int** ```go -delete(scores, "math") +func bool2int(b bool) int { + if b { + return 1 + } + return 0 +} ``` +**int 转 bool** +```go +func int2bool(i int) bool { + return i != 0 +} +``` -**判断 key 是否存在** -当key不存在,会返回value-type的零值 ,所以你不能通过返回的结果是否是零值来判断对应的 key 是否存在,因为 key 对应的 value 值可能恰好就是零值。 -其实字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key 是否存在。 +在 Python 中使用 not 对逻辑值取反,而 Go 中使用 `!` 符号 ```go import "fmt" -func main() { - scores := map[string]int{"english": 80, "chinese": 85} - math, ok := scores["math"] - if ok { - fmt.Printf("math 的值是: %d", math) - } else { - fmt.Println("math 不存在") - } +var male bool = true +func main() { + fmt.Println( !male == false) + // 或者 + fmt.Println( male != false) } + +// output: true ``` -我们将上面的代码再优化一下 + + +一个 if 判断语句,有可能不只一个判断条件,在 Python 中是使用 `and` 和 `or` 来执行逻辑运算 + +```python +>>> age = 15 +>>> gender = "male" +>>> +>>> gender == "male" and age >18 +False +``` + +而在 Go 语言中,则使用 `&&` 表示`且`,用 `||` 表示`或`,并且有短路行为(即左边表达式已经可以确认整个表达式的值,那么右边将不会再被求值。 ```go import "fmt" -func main() { - scores := map[string]int{"english": 80, "chinese": 85} - if math, ok := scores["math"]; ok { - fmt.Printf("math 的值是: %d", math) - } else { - fmt.Println("math 不存在") - } +var age int = 15 +var gender string = "male" +func main() { + // && 两边的表达式都会执行 + fmt.Println( age > 18 && gender == "male") + // gender == "male" 并不会执行 + fmt.Println( age > 18 || gender == "male") } + +// output: false +// output: true ``` diff --git a/source/c09/c09_07.md b/source/c09/c09_07.md index 4587576..f9e51fe 100644 --- a/source/c09/c09_07.md +++ b/source/c09/c09_07.md @@ -1,235 +1,226 @@ -# 9.7 详解数据类型:数组与切片 +# 9.7 详解数据类型:指针 -## 1. 数组 +## 0. 什么是指针 -数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,所以在Go语言中很少直接使用数组。 - -声明数组,并给该数组里的每个元素赋值(索引值的最小有效值和其他大多数语言一样是 0,不是1) +当我们定义一个变量 name ```go -// [3] 里的3 表示该数组的元素个数 -var arr [3]int -arr[0] = 1 -arr[1] = 2 -arr[2] = 3 +var name string = "Go编程时光" ``` -声明并直接初始化数组 +此时,name 是变量名,它只是编程语言中方便程序员编写和理解代码的一个标签。 -```go -// 第一种方法 -var arr [3]int = [3]int{1,2,3} +当我们访问这个标签时,机算机会返回给我们它指向的内存地址里存储的值:`Go编程时光`。 -// 第二种方法 -arr := [3]int{1,2,3} -``` +出于某些需要,我们会将这个内存地址赋值给另一个变量名,通常叫做 ptr(pointer的简写),而这个变量,我们称之为指针变量。 -上面的 3 表示数组的元素个数 ,万一你哪天想往该数组中增加元素,你得对应修改这个数字,为了避免这种硬编码,你可以这样写,使用 `...` 让Go语言自己根据实际情况来分配空间。 +换句话说,指针变量(一个标签)的值是指针,也就是内存地址。 -```go -arr := [...]int{1,2,3} -``` +根据变量指向的值,是否是内存地址,我把变量分为两种: +- 普通变量:存数据值本身 +- 指针变量:存值的内存地址 -`[3]int` 和 `[4]int` 虽然都是数组,但他们却是不同的类型,使用 fmt 的 `%T` 可以查得。 -```go -import ( - "fmt" -) +## 1. 指针的创建 -func main() { - arr01 := [...]int{1, 2, 3} - arr02 := [...]int{1, 2, 3, 4} - fmt.Printf("%d 的类型是: %T\n", arr01, arr01) - fmt.Printf("%d 的类型是: %T", arr02, arr02) -} -``` +指针创建有三种方法 -输出 如下 +**第一种方法** -``` -[1 2 3] 的类型是: [3]int -[1 2 3 4] 的类型是: [4]int +先定义对应的变量,再通过变量取得内存地址,创建指针 + +```go +// 定义普通变量 +aint := 1 +// 定义指针变量 +ptr := &aint ``` -如果你觉得每次写 `[3]int` 有点麻烦,你可以为 `[3]int` 定义一个类型字面量,也就是别名类型。 +**第二种方法** -使用 `type` 关键字可以定义一个类型字面量,后面只要你想定义一个容器大小为3,元素类型为int的数组 ,都可以使用这个别名类型。 +先创建指针,分配好内存后,再给指针指向的内存地址写入对应的值。 ```go -import ( - "fmt" -) +// 创建指针 +astr := new(string) +// 给指针赋值 +*astr = "Go编程时光" +``` -func main() { - type arr3 [3]int - myarr := arr3{1,2,3} - fmt.Printf("%d 的类型是: %T", myarr, myarr) -} -``` -输出 如下 +**第三种方法** +先声明一个指针变量,再从其他变量取得内存地址赋值给它 + +```go +aint := 1 +var bint *int // 声明一个指针 +bint = &aint // 初始化 ``` -[1 2 3] 的类型是: main.arr3 -``` -## 2. 切片 +上面的三段代码中,指针的操作都离不开这两个符号: + +- `&` :从一个普通变量中取得内存地址 +- `*`:当`*`在赋值操作值的右边,是从一个指针变量中取得变量值,当`*`在赋值操作值的左边,是指该指针指向的变量 + -切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。 -切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(意思是这是个左闭右开的区间) +通过下面这段代码,你可以熟悉这两个符号的用法 ```go -import ( - "fmt" -) +package main + +import "fmt" func main() { - myarr := [...]int{1, 2, 3} - fmt.Printf("%d 的类型是: %T", myarr[0:2], myarr[0:2]) + aint := 1 // 定义普通变量 + ptr := &aint // 定义指针变量 + fmt.Println("普通变量存储的是:", aint) + fmt.Println("普通变量存储的是:", *ptr) + fmt.Println("指针变量存储的是:", &aint) + fmt.Println("指针变量存储的是:", ptr) } ``` -输出 如下 +输出如下 ``` -[1 2] 的类型是: []int +普通变量存储的是: 1 +普通变量存储的是: 1 +指针变量存储的是: 0xc0000100a0 +指针变量存储的是: 0xc0000100a0 ``` -切片的构造,有三种方式 - -1. 对数组进行片段截取(上面例子已经展示:myarr[0:2],0是索引起始值,2是索引终止值,区间左半右开) - -2. 从头声明赋值(例子如下) +要想打印指针指向的内存地址,方法有两种 - ```go - // 声明字符串切片 - var strList []string - - // 声明整型切片 - var numList []int - - // 声明一个空切片 - var numListEmpty = []int{} - ``` +```go +// 第一种 +fmt.Printf("%p", ptr) -3. 使用 make 函数构造,make 函数的格式:`make( []Type, size, cap )` +// 第二种 +fmt.Println(ptr) +``` - 这个函数刚好指出了,一个切片具备的三个要素:类型(Type),长度(size),容量(cap) - - ```go - import ( - "fmt" - ) - - func main() { - a := make([]int, 2) - b := make([]int, 2, 10) - fmt.Println(a, b) - fmt.Println(len(a), len(b)) - fmt.Println(cap(a), cap(b)) -} - ``` - 输出 如下 - - ``` - [0 0] [0 0] - 2 2 - 2 10 - ``` - -关于 len 和 cap 的概念,可能不好理解 ,这里举个例子: +## 2. 指针的类型 -- 公司名,相当于字面量,也就是变量名。 +我们知道字符串的类型是 string,整型是int,那么指针如何表示呢? -- 公司里的所有工位,相当于已分配到的内存空间 +写段代码试验一下就知道了 -- 公司里的员工,相当于元素。 +```go +package main -- cap 代表你这个公司最多可以容纳多少员工 +import "fmt" -- len 代表你这个公司当前有多少个员工 +func main() { + astr := "hello" + aint := 1 + abool := false + arune := 'a' + afloat := 1.2 + + fmt.Printf("astr 指针类型是:%T\n", &astr) + fmt.Printf("aint 指针类型是:%T\n", &aint) + fmt.Printf("abool 指针类型是:%T\n", &abool) + fmt.Printf("arune 指针类型是:%T\n", &arune) + fmt.Printf("afloat 指针类型是:%T\n", &afloat) +} +``` +输出如下,可以发现用 `*`+所指向变量值的数据类型,就是对应的指针类型。 +``` +astr 指针类型是:*string +aint 指针类型是:*int +abool 指针类型是:*bool +arune 指针类型是:*int32 +afloat 指针类型是:*float64 +``` -由于 切片是引用类型,所以你不对它进行赋值的话,它的零值(默认值)是 nil +所以若我们定义一个只接收指针类型的参数的函数,可以这么写 -```go -var myarr []int -fmt.Println(myarr == nil) -// true +``` +func mytest(ptr *int) { + fmt.Println(*ptr) +} ``` -数组 与 切片 有相同点,它们都是可以容纳若干类型相同的元素的容器 +## 3. 指针的零值 -也有不同点,数组的容器大小固定,而切片本身是引用类型,它更像是 Python 中的 list ,我们可以对它 append 进行元素的添加。 +当指针声明后,没有进行初始化,其零值是 nil。 ```go -import ( - "fmt" -) - -func main() { - myarr := []int{1} - // 追加一个元素 - myarr = append(myarr, 2) - // 追加多个元素 - myarr = append(myarr, 3, 4) - // 追加一个切片, ... 表示解包,不能省略 - myarr = append(myarr, []int{7, 8}...) - // 在第一个位置插入元素 - myarr = append([]int{0}, myarr...) - // 在中间插入一个切片(两个元素) - myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]...)...) - fmt.Println(myarr) +func main() { + a := 25 + var b *int // 声明一个指针 + + if b == nil { + fmt.Println(b) + b = &a // 初始化:将a的内存地址给b + fmt.Println(b) + } } ``` -输出 如下 +输出如下 ``` -[0 1 2 3 4 5 6 7 8] + +0xc0000100a0 ``` -最后,给你留一道思考题,如下 这段代码 +## 4. 指针与切片 -```go -import ( - "fmt" -) +切片与指针一样,都是引用类型。 -func main() { - var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - myslice := numbers4[4:6:8] - fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice)) +如果我们想通过一个函数改变一个数组的值,有两种方法 - myslice = myslice[:cap(myslice)] - fmt.Printf("myslice的第四个元素为: %d", myslice[3]) -} -``` +1. 将这个数组的切片做为参数传给函数 +2. 将这个数组的指针做为参数传给函数 -为什么 myslice 的长度为2,却能访问到第四个元素 -``` -myslice为 [5 6], 其长度为: 2 -myslice的第四个元素为: 8 -``` +尽管二者都可以实现我们的目的,但是按照 Go 语言的使用习惯,建议使用第一种方法,因为第一种方法,写出来的代码会更加简洁,易读。具体你可以参数下面两种方法的代码实现 +**使用切片** +```go +func modify(sls []int) { + sls[0] = 90 +} + +func main() { + a := [3]int{89, 90, 91} + modify(a[:]) + fmt.Println(a) +} +``` + +**使用指针** + +```go +func modify(arr *[3]int) { + (*arr)[0] = 90 +} + +func main() { + a := [3]int{89, 90, 91} + modify(&a) + fmt.Println(a) +} +``` diff --git a/source/c09/c09_08.md b/source/c09/c09_08.md index 0b27dad..49e10be 100644 --- a/source/c09/c09_08.md +++ b/source/c09/c09_08.md @@ -1,283 +1,256 @@ -# 9.8 详解数据结构:信道 +# 9.8 面向对象编程:结构体与继承 -Go 语言之所以开始流行起来,很大一部分原因是因为它自带的并发机制。 +## 0. 什么是结构体? -如果说 goroutine 是 Go语言程序的并发体的话,那么 channel(信道) 就是它们之间的通信机制。一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。 +在之前学过的数据类型中,数组与切片,只能存储同一类型的变量。若要存储多个类型的变量,就需要用到结构体,它是将多个容易类型的命令变量组合在一起的聚合数据类型。 -信道,可以比喻成水管,连接多个goroutine程序 ,它是一种队列式的数据结构,遵循先入先出的规则。 +每个变量都成为该结构体的成员变量。 -## 信道的定义 +可以理解为 Go语言 的结构体struct和其他语言的class有相等的地位,但是Go语言放弃大量面向对象的特性,所有的Go语言类型除了指针类型外,都可以有自己的方法,提高了可扩展性。 -每个信道都只能传递一种数据类型的数据,所以在你声明的时候,你得指定数据类型(string int 等等) +在 Go 语言中没有没有 class 类的概念,只有 struct 结构体的概念,因此也没有继承,本篇文章,带你学习一下结构体相关的内容。 + +## 1. 定义结构体 + +声明结构体 ```go -var 信道变量 chan 信道类型 +type 结构体名 struct { + 属性名 属性类型 + 属性名 属性类型 + ... +} ``` -声明后的信道,其零值是nil,无法直接使用,必须配合make函进行初始化。 +比如我要定义一个可以存储个人资料名为 Profile 的结构体,可以这么写 ```go -信道实例 = make(chan 信道类型) +type Profile struct { + name string + age int + gender string + mother *Profile // 指针 + father *Profile // 指针 +} ``` -亦或者,上面两行可以合并成一句,以下我都使用这样的方式进行信道的声明 +## 2. 定义方法 + +在 Go 语言中,我们无法在结构体内定义方法,那如何给一个结构体定义方法呢,答案是可以使用组合函数的方式来定义结构体方法。它和普通函数的定义方式有些不一样,比如下面这个方法 ```go -信道实例 := make(chan 信道类型) +func (person Profile) FmtProfile() { + fmt.Printf("名字:%s\n", person.name) + fmt.Printf("年龄:%d\n", person.age) + fmt.Printf("性别:%s\n", person.gender) +} ``` +其中`fmt_profile` 是方法名,而`(person Profile)` :表示将 fmt_profile 方法与 Profile 的实例绑定。我们把 Profile 称为方法的接收者,而 person 表示实例本身,它相当于 Python 中的 self,在方法内可以使用 `person.属性名` 的方法来访问实例属性。 +完整代码如下: -信道有两种分类方法。 - -## 缓存信道与非缓存信道 +```go +package main -按照是否可缓存数据(由 make 函数的第二个参数决定)可分为 +import "fmt" -- 无缓冲信道,默认情况下你使用的即是非缓冲信道,这意味着,发送完数据后,接收端必须立马接收数据,否则无法再往信道中发送数据。 +// 定义一个名为Profile 的结构体 +type Profile struct { + name string + age int + gender string + mother *Profile // 指针 + father *Profile // 指针 +} - ```go - pipline := make(chan int) - - // 或者 - pipline := make(chan int, 0) - ``` +// 定义一个与 Profile 的绑定的方法 +func (person Profile) FmtProfile() { + fmt.Printf("名字:%s\n", person.name) + fmt.Printf("年龄:%d\n", person.age) + fmt.Printf("性别:%s\n", person.gender) +} - 无缓冲信道,要求发送端和接收端都准备好,才会进行下一行代码,不然会造成另一方的运行阻塞,也就是说发送端和接收端是同步运行的。 +func main() { + // 实例化 + myself := Profile{name: "小明", age: 24, gender: "male"} + // 调用函数 + myself.FmtProfile() +} +``` - +输出如下 -- 缓冲信道,允许管道里存储多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。 +``` +名字:小明 +年龄:24 +性别:male +``` - ```go - // 只要make函数大于1即是缓冲信道 - pipline := make(chan int, 10) - ``` +## 3. 方法的参数传递方式 -可缓冲的数值个数,也算是信道的一个属性。可以用 cap 函数获取 +上面定义方法的方式叫当你想要在方法内改变实例的属性的时候,必须使用指针做为方法的接收者。 ```go +package main + import "fmt" +// 声明一个 Profile 的结构体 +type Profile struct { + name string + age int + gender string + mother *Profile // 指针 + father *Profile // 指针 +} + +// 重点在于这个星号: * +func (person *Profile) increase_age() { + person.age += 1 +} + func main() { - pipline := make(chan int, 10) - fmt.Printf("信道可缓冲 %d 个数值", cap(pipline)) + myself := Profile{name: "小明", age: 24, gender: "male"} + fmt.Printf("当前年龄:%d\n", myself.age) + myself.increase_age() + fmt.Printf("当前年龄:%d", myself.age) } -// output: 信道可缓冲 10 个数值 ``` +输出结果 如下,可以看到在方法内部对 age 的修改已经生效。你可以尝试去掉 `*`,使用值做为方法接收者,看看age是否会发生改变。 +``` +当前年龄:24 +当前年龄:25 +``` -```go -import ( - "fmt" - "time" -) +至此,我们知道了两种定义方法的方式: -type Sender chan<- int -type Receiver <-chan int +- 以值做为方法接收者 +- 以指针做为方法接收者 -func main() { - var myChannel = make(chan int, 0) - var number = 6 - go func() { - var sender Sender = myChannel - sender <- number - fmt.Println("Sent!") - }() - go func() { - var receiver Receiver = myChannel - fmt.Println("Received!", <-receiver) - }() - // 让main函数执行结束的时间延迟1秒, - // 以使上面两个代码块有机会被执行。 - time.Sleep(time.Second) -} -``` +那我们如何进行选择呢?以下几种情况,应当直接使用指针做为方法的接收者。 + +1. 你需要在方法内部改变结构体内容的时候 +2. 出于性能的问题,当结构体过大的时候 + +有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,建议都使用指针做为接收者。 -## 双向信道与单向信道 - -按照信道的流向可分为: - -- 双向信道,默认情况下你使用的即是双向信道 - - ```go - import ( - "fmt" - "time" - ) - - func main() { - pipline := make(chan int) - - go func() { - fmt.Println("准备发送数据: 100") - pipline <- 100 - }() - - go func() { - num := <-pipline - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) - } - ``` - - - -- 单向信道,又可以细分为接收信道和发送信道,区别在于 `<-` 符号在关键字 chan 的左边还是右边,将上面的例子改造一下: - - ```go - import ( - "fmt" - "time" - ) - //定义一个send-only type - type Sender chan<- int - //定义一个receive-only type - type Receiver <-chan int - - func main() { - var pipline = make(chan int) - - go func() { - var sender Sender = pipline - fmt.Println("准备发送数据: 100") - sender <- 100 - }() - - go func() { - var receiver Receiver = pipline - num := <-receiver - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) - } - ``` - - 若你往一个send-only信道读取数据 ,或者往一个receive-only发送数据 ,都会抛出异常。 - - - - ## 遍历信道 - - 遍历信道,可以使用 for 搭配 range关键字,在range时,要确保信道是处于关闭状态,否则循环会阻塞。 - - ```go - import "fmt" - - func fibonacci(mychan chan int) { - n := cap(mychan) - x, y := 0, 1 - for i := 0; i < n; i++ { - mychan <- x - x, y = y, x+y - } - // 记得 close 信道 - // 不然主函数中遍历完并不会结束,而是会阻塞。 - close(mychan) - } - - func main() { - pipline := make(chan int, 10) - - go fibonacci(pipline) - - for k := range pipline { - fmt.Println(k) - } - } - ``` - - - -## 用信道来做锁 - -当信道里的数据量已经达到设定的容量时,此时再往里发送数据会阻塞整个程序。 - -利用这个特性,可以用当他来当程序的锁。 - -示例如下,详情可以看注释 +不管你使用哪种方法定义方法,指针实例对象、值实例对象都可以直接调用,而没有什么约束。这一点Go语言做得非常好。 + + + +## 4. 结构体实现 “继承” + +为什么标题的继承,加了双引号,因为Go 语言本身并不支持继承。 + +但我们可以使用组合的方法,实现类似继承的效果。 + +在生活中,组合的例子非常多,比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起,最后才有了我们用的电脑。 + +同样的,在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。 + +现在这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。 ```go -package main +type company struct { + companyName string + companyAddr string +} -import { - "fmt" - "time" +type staff struct { + name string + age int + gender string + position string } +``` + +若要将公司信息与公司职员关联起来,一般都会想到将 company 结构体的内容照抄到 staff 里。 -// 由于 x=x+1 不是原子操作 -// 所以应避免多个协程对x进行操作 -// 使用容量为1的信道可以达到锁的效果 -func increment(ch chan bool, x *int) { - ch <- true - *x = *x + 1 - <- ch +```go +type staff struct { + name string + age int + gender string + companyName string + companyAddr string + position string } +``` -func main() { - // 注意要设置容量为 1 的缓冲信道 - pipline := make(chan bool, 1) +虽然在实现上并没有什么问题,但在你对同一公司的多个staff初始化的时候,都得重复初始化相同的公司信息,这做得并不好,借鉴继承的思想,我们可以将公司的属性都“继承”过来。 - var x int - for i:=0;i<1000;i++{ - go increment(pipline, &x) - } +但是在 Go 中没有类的概念,只有组合,你可以将 company 这个 结构体嵌入到 staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company 的所有属性了。 - // 确保所有的协程都已完成 - // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep - time.Sleep(3) - fmt.Println("x 的值:", x) -} +```go +type staff struct { + name string + age int + gender string + position string + company // 匿名字段 +} ``` -输出如下 +来写个完整的程序验证一下。 -``` -x 的值:1000 -``` +```go +package main -如果不加锁,输出会小于1000。 +import "fmt" +type company struct { + companyName string + companyAddr string +} +type staff struct { + name string + age int + gender string + position string + company +} +func main() { + myCom := company{ + companyName: "Tencent", + companyAddr: "深圳市南山区", + } + staffInfo := staff{ + name: "小明", + age: 28, + gender: "男", + position: "云计算开发工程师", + company: myCom, + } + fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName) + fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName) +} +``` -确保主函数在所有的协程退出后,再退出。除了上面使用 sleep,但这种方法不可控,可以使用原生的 sync.WaitGroup 方法:https://blog.csdn.net/yangxiaodong88/article/details/96309601 +输出结果如下,可见`staffInfo.companyName` 和 `staffInfo.company.companyName` 的效果是一样的。 +``` +小明 在 Tencent 工作 +小明 在 Tencent 工作 +``` -如何给一个函数加锁,避免多个协程出现争抢资源的情况:https://mp.weixin.qq.com/s/AyjUi4jBJ2YvaiY7YnQLWQ -- 使用信道 +## 5. 内部方法与外部方法 - ```go - ch := make(chan bool, 1) - - func increment(wg *sync.WaitGroup, ch chan bool) { - ch <- true - x = x + 1 - <- ch - } - ``` +在 Go 语言中,函数名的首字母大小写非常重要,它被来实现控制对方法的访问权限。 -- 使用Mutex +- 当方法的首字母为大写时,这个方法对于所有包都是Public,其他包可以随意调用 +- 当方法的首字母为小写时,这个方法是Private,其他包是无法访问的。 - ```go - var m sync.Mutex - - func increment(wg *sync.WaitGroup, m *sync.Mutex) { - m.Lock() - x = x + 1 - m.Unlock() - } - ``` diff --git a/source/c09/c09_09.md b/source/c09/c09_09.md index 3eb9dcd..5aeaa9c 100644 --- a/source/c09/c09_09.md +++ b/source/c09/c09_09.md @@ -1,175 +1,260 @@ -# 9.9 Go语言命名编码规范 +# 9.9 一篇文章理解 Go 里的函数 -每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 +## 1. 关于函数 -以下内容整理自:[Go语言(Golang)编码规范](https://www.bookstack.cn/books/go-code-convention) +函数是基于功能或 逻辑进行封装的可复用的代码结构。将一段功能复杂、很长的一段代码封装成多个代码片段(即函数),有助于提高代码可读性和可维护性。 -命名规范分为以下几点 +在 Go 语言中,函数可以分为两种: -**1. 文件命名** +- 带有名字的普通函数 +- 没有名字的匿名函数 -文件名应一律使用小写(因为Windows的原因), 不同单词之间用下划线分割。 +由于 Go语言是编译型语言,所以函数编写的顺序是无关紧要的,它不像 Python 那样,函数在位置上需要定义在调用之前。 -应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 Gogs 的入口应当为 gogs.go +## 2. 函数的声明 -**2. 常量命名 ** +函数的声明,使用 func 关键字,后面依次接 `函数名`,`参数列表`,`返回值列表`,`用 {} 包裹的代码逻辑体` -- 常量均需使用全部大写字母组成,并使用下划线分词: +``` +func 函数名(形式参数列表)(返回值列表){ + 函数体 +} +``` - ```go - const APP_VER = "0.7.0.1110 Beta" - ``` +- 形式参数列表描述了函数的参数名以及参数类型,这些参数作为局部变量,其值由参数调用者提供 -- 如果是枚举类型的常量,需要先创建相应类型: +- 返回值列表描述了函数返回值的变量名以及类型,如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。 - ```go - type Scheme string - const ( - HTTP Scheme = "http" - HTTPS Scheme = "https" - ) - ``` +举个例子,定义一个 sum 函数,接收两个 int 类型的参数,在运行中,将其值分别赋值给 a,b,并规定必须返回一个int类型的值 。 -- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: +```go +func sum(a int, b int) (int){ + return a + b +} +func main() { + fmt.Println(sum(1,2)) +} +``` - ```go - type PullRequestStatus int - const ( - PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota - PULL_REQUEST_STATUS_CHECKING - PULL_REQUEST_STATUS_MERGEABLE - ) - ``` +## 3. 函数实现可变参数 -**3. 变量命名** +上面举的例子,参数个数都是固定的,这很好理解 ,指定什么类型的参数就传入什么类型的变量,数量上,不能多一个,也不能少一个。(好像没有可选参数)。 -使用驼峰命名法 +在 Python 中我们可以使用 *args 和 **kw ,还实现可变参数的函数。 -- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u -- 若该变量为 bool 类型,则名称应以 `Has`, `Is`, `Can` 或 `Allow` 开头。例如:isExist ,hasConflict 。 -- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 startDate 。 -- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 `apiClient`。 -- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient +可变参数分为几种: -下面列举了一些常见的特有名词: +- 多个类型一致的参数 +- 多个类型不一致的参数 -``` -// A GonicMapper that contains a list of common initialisms taken from golang/lint -var LintGonicMapper = GonicMapper{ - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SSH": true, - "TLS": true, - "TTL": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XSRF": true, - "XSS": true, +### 多个类型一致的参数 + +首先是多个类型一致的参数。 + +这边定义一个可以对多个数值进行求和的函数, + +使用 `...int`,表示一个元素为int类型的切片,用来接收调用者传入的参数。 + +```go +// 使用 ...类型,表示一个元素为int类型的切片 +func sum(args ...int) int { + var sum int + for _, v := range args { + sum += v + } + return sum +} +func main() { + fmt.Println(sum(1, 2, 3)) } + +// output: 6 ``` +其中 `...` 是 Go 语言为了方便程序员写代码而实现的语法糖,如果该函数下会多个类型的函数,这个语法糖必须得是最后一个参数。 +同时这个语法糖,只能在定义函数时使用。 -**接口命名** -使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 + +### 多个类型不一致的参数 + +上面那个例子中,我们的参数类型都是 int,如果你希望传多个参数且这些参数的类型都不一样,可以指定类型为 `...interface{}`,然后再遍历。 + +比如下面这段代码,是Go语言标准库中 fmt.Printf() 的函数原型: ```go -type helloWorld interface { - func Hello(); +import "fmt" +func MyPrintf(args ...interface{}) { + for _, arg := range args { + switch arg.(type) { + case int: + fmt.Println(arg, "is an int value.") + case string: + fmt.Println(arg, "is a string value.") + case int64: + fmt.Println(arg, "is an int64 value.") + default: + fmt.Println(arg, "is an unknown type.") + } + } } -type SayHello helloWorld +func main() { + var v1 int = 1 + var v2 int64 = 234 + var v3 string = "hello" + var v4 float32 = 1.234 + MyPrintf(v1, v2, v3, v4) +} +``` + + + +在某些情况下,我们需要定义一个参数个数可变的函数,具体传入几个参数,由调用者自己决定,但不管传入几个参数,函数都能够处理。 + +比如这边实现一个累加 + +```go +func myfunc(args ...int) { + for _, arg := range args { + fmt.Println(arg) + } +} ``` -**注释规范** +## 4. 多个可变参数函数传递参数 + +上面提到了可以使用 `...` 来接收多个参数,除此之外,它还有一个用法,就是用来解序列,将函数的可变参数(一个切片)一个一个取出来,传递给另一个可变参数的函数,而不是传递可变参数变量本身。 + +同样这个用法,也只能在给函数传递参数里使用。 -单行注释使用 `//` ,多行注释使用 `/* comment */` +例子如下: ```go -// go语言 +import "fmt" + +func sum(args ...int) int { + var result int + for _, v := range args { + result += v + } + return result +} -/* -Go 语言 -Hello, World -*/ +func Sum(args ...int) int { + // 利用 ... 来解序列 + result := sum(args...) + return result +} +func main() { + fmt.Println(sum(1, 2, 3)) +} ``` -- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 -- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 -- 包、函数、方法和类型的注释说明都是一个完整的句子。 +## 5. 函数的返回值 + +Go语言中的函数,在你定义的时候,就规定了此函数 + +1. 有没有返回值? + + 当没有指明返回值的类型时, 函数体不能有 return,Go并不像 Python 那样没有return,就默认返回None + +2. 返回几个值? + + Go 支持一个函数返回多个值 -- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 + ```go + func double(a int) (int, int) { + b := a * 2 + return a, b + } + func main() { + // 接收参数用逗号分隔 + a, b := double(2) + fmt.Println(a, b) + } + ``` -- 注释的单行长度不能超过 80 个字符。 + -- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 +3. 怎么返回值? -- 如果是特别复杂的包,可单独创建 doc.go 文件说明 + Go支持返回带有变量名的值 -- 类型的定义一般都以单数形式描述: + ```go + func double(a int) (b int) { + // 不能使用 := ,因为在返回值哪里已经声明了为int + b = a * 2 + // 不需要指明写回哪个变量,在返回值类型那里已经指定了 + return + } + func main() { + fmt.Println(double(2)) + } + // output: 4 + ``` - ```go - // Request represents a request to run a command. type Request struct { ... - ``` -- 如果为接口,则一般以以下形式描述: - ```go - // FileInfo is the interface that describes a file and is returned by Stat and Lstat. - type FileInfo interface { ... - ``` -- 函数与方法的注释需以函数或方法的名称作为开头: +## 6. 方法与函数 - ```go - // Post returns *BeegoHttpRequest with POST method. - ``` +方法,在上一节《[08. 面向对象编程:结构体与继承](https://mp.weixin.qq.com/s/8NsSI7EsYqbCpj0OHu7ImQ)》里已经介绍过了,它的定义与函数有些不同,你可以点击前面的标题进行交叉学习。 -- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: +那 **方法和函数有什么区别?** 为防会有朋友第一次接触面向对象,这里多嘴一句。 - ```go - // Copy copies file from source to target path. - // It returns false and error when error occurs in underlying function calls. - ``` +方法,是一种特殊的函数。当你一个函数和对象/结构体进行绑定的时候,我们就称这个函数是一个方法。 -- 若函数或方法为判断类型(返回值主要为 `bool` 类型),则以 ` returns true if` 开头: - ```go - // HasPrefix returns true if name has any string in given slice as prefix. - func HasPrefix(name string, prefixes []string) bool { ... - ``` -特别注释 +## 7. 匿名函数的使用 + +所谓匿名函数,就是没有名字的函数,它只有函数逻辑体,而没有函数名。 + +定义的格式如下 + +```go +func(参数列表)(返回参数列表){ + 函数体 +} +``` + +一个名字实际上并没有多大区别,所有使用匿名函数都可以改成普通有名函数,那么什么情况下,会使用匿名函数呢? + +定义变量名,是一个不难但是还费脑子的事情,对于那到只使用一次的函数,是没必要拥有姓名的。这才有了匿名函数。 + +有了这个背景,决定了匿名函数只有拥有短暂的生命,一般都是定义后立即使用。 + +就像这样,定义后立马执行(这里只是举例,实际代码没有意义)。 + +```go +func(data int) { + fmt.Println("hello", data) +}(100) +``` + +亦或是做为回调函数使用 + +```go +// 第二个参数为函数 +func visit(list []int, f func(int)) { + for _, v := range list { + // 执行回调函数 + f(v) + } +} +func main() { + // 使用匿名函数直接做为参数 + visit([]int{1, 2, 3, 4}, func(v int) { + fmt.Println(v) + }) +} +``` + + -- TODE:提醒维护人员此部分代码待完成 -- FIXME:提醒维护人员此处有BUG待修复 -- NOTE:维护人员要关注的一些问题说明 \ No newline at end of file diff --git a/source/c09/c09_10.md b/source/c09/c09_10.md index ee2da80..e9c1586 100644 --- a/source/c09/c09_10.md +++ b/source/c09/c09_10.md @@ -1,148 +1,126 @@ -# 9.10 理解语句块与作用域 +# 9.10 Go语言流程控制:if-else -由于 Go 使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 Go 中的语句块是怎么一回事? +## 1. 条件语句模型 -## 1. 显示语句块与隐式语句块 +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: -通俗地说,语句块是由花括弧(`{}`)所包含的一系列语句。 +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 -语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域,我会在下节讲到。 +今天先来讲讲 if-else 条件语句 -用花括弧包含的语句块,属于显示语句块。 - -在 Go 中还有很多的隐式语句块: - -- 主语句块:包括所有源码,对应内置作用域 -- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 -- 文件语句块:包括该文件中的所有源码,对应文件级作用域 -- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 - -前面三点好理解,第四点举几个例子 - -for 循环 - -```go -for i := 0; i < 5; i++ { - fmt.Println(i) -} -``` - -if 语句 +Go 里的条件语句模型是这样的 ```go -if i := 0; i >= 0 { - fmt.Println(i) +if 条件 1 { + 分支 1 +} else if 条件 2 { + 分支 2 +} else if 条件 ... { + 分支 ... +} else { + 分支 else } ``` -switch 语句 +Go编译器,对于 `{` 和 `}` 的位置有严格的要求,它要求 else if (或 else)和 两边的花括号,必须在同一行。 -```go -switch i := 2; i * 4 { -case 8: - fmt.Println(i) -default: - fmt.Println(“default”) -} -``` +由于 Go是 强类型,所以要求你条件表达式必须严格返回布尔型的数据(nil 和 0 和 1 都不行,具体可查看《详解数据类型:字典与布尔类型》)。 -且每个 switch 语句的子句都是一个隐式的语句块 +对于这个模型,分别举几个例子来看一下。 -```go -switch i := 2; i * 4 { -case 8: - j := 0 - fmt.Println(i, j) -default: - // "j" is undefined here - fmt.Println(“default”) -} -// "j" is undefined here -``` -## 2. 四种作用域的理解 -变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 +## 2. 单分支判断 -根据声明位置的不同,作用域可以分为以下四个类型: +只有一个 if ,没有 else -- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 -- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 -- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 -- 局部作用域:在自己的语句块内声明,包括函数,for、if 等语句块,或自定义的 {} 语句块形成的作用域,只在自己的局部作用域内可用 +```go +import "fmt" -以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这时将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 +func main() { + age := 20 + if age > 18 { + fmt.Println("已经成年了") + } +} +``` -对于作用域,有以下几点总结: +如果条件里需要满足多个条件,可以使用 `&&` 和 `||` -- 低层作用域,可以访问高层作用域 -- 同一层级的作用域,是相互隔离的 -- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 +- `&&`:表示且,左右都需要为true,最终结果才能为 true,否则为 false +- `||`:表示或,左右只要有一个为true,最终结果即为true,否则 为 false +```go +import "fmt" +func main() { + age := 20 + gender := "male" + if (age > 18 && gender == "male") { + fmt.Println("是成年男性") + } +} +``` -在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 -而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 +## 3. 多分支判断 +if - else -## 3. 静态作用域与动态作用域 +```go +import "fmt" -根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: +func main() { + age := 20 + if age > 18 { + fmt.Println("已经成年了") + } else { + fmt.Println("还未成年") + } +} +``` -- 静态作用域,如 Go 语言 -- 动态作用域,如 Shell 语言 +if - else if - else -具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 +```go +import "fmt" -```python -#!/bin/bash -func01() { - local value=1 - func02 -} -func02() { - echo "func02 sees value as ${value}" +func main() { + age := 20 + if age > 18 { + fmt.Println("已经成年了") + } else if age >12 { + fmt.Println("已经是青少年了") + } else { + fmt.Println("还不是青少年") + } } - -# 执行函数 -func01 -func02 ``` -从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 -但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 是访问不了 value 变量的。 -所以此时的输出结果是 +## 4. 高级写法 -```shell -func02 sees value as 1 -func02 sees value as -``` - -但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 name 这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 +在 if 里可以允许先运行一个表达式,取得变量后,再对其进行判断,比如第一个例子里代码也可以写成这样 ```go import "fmt" -func func01() { - fmt.Println("在 func01 函数中,name:", name) +func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } } +``` -func main() { - var name string = "Python编程时光" - fmt.Println("在 main 函数中,name:", name) - func01() -} -``` -因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 -参考文章:https://studygolang.com/articles/12632 -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_11.md b/source/c09/c09_11.md index d73095f..b6586fd 100644 --- a/source/c09/c09_11.md +++ b/source/c09/c09_11.md @@ -1,519 +1,252 @@ -# 9.11 面向对象:结构体 +# 9.11 Go语言流程控制:switch-case -在 Go 语言中没有继承和多态,所以它没有 class 类的概念,而只有 struct 结构体的概念。 -## 1. 定义结构体 +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: -声明结构体 +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 -```go -type 结构体名 struct { - 属性名 属性类型 - 属性名 属性类型 - ... -} -``` +上一篇讲了 if -else 条件语句,今天先来讲讲 switch - case 选择语句。 -比如我要定义一个可以存储个人资料名为 Profile 的结构体,可以这么写 -```go -type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 -} -``` -## 2. 定义方法 +## 0. 语句模型 -在 Go 语言中,我们无法在结构体内定义方法,那如何给一个结构体定义方法呢,答案是可以使用组合函数的方式来定义结构体方法。它和普通函数的定义方式有些不一样,比如下面这个方法 +Go 里的选择语句模型是这样的 ```go -func (person Profile) FmtProfile() { - fmt.Printf("名字:%s\n", person.name) - fmt.Printf("年龄:%d\n", person.age) - fmt.Printf("性别:%s\n", person.gender) -} -``` - -其中`fmt_profile` 是方法名,而`(person Profile)` :表示将 fmt_profile 方法与 Profile 的实例绑定。我们把 Profile 称为方法的接收者,而 person 表示实例本身,它相当于 Python 中的 self,在方法内可以使用 `person.属性名` 的方法来访问实例属性。 - -完整代码如下: - -```go -package main - -import "fmt" - -// 定义一个名为Profile 的结构体 -type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 -} - -// 定义一个与 Profile 的绑定的方法 -func (person Profile) FmtProfile() { - fmt.Printf("名字:%s\n", person.name) - fmt.Printf("年龄:%d\n", person.age) - fmt.Printf("性别:%s\n", person.gender) -} - -func main() { - // 实例化 - myself := Profile{name: "小明", age: 24, gender: "male"} - // 调用函数 - myself.FmtProfile() +switch 表达式 { + case 表达式1: + 代码块 + case 表达式2: + 代码块 + case 表达式3: + 代码块 + case 表达式4: + 代码块 + case 表达式5: + 代码块 + default: + 代码块 } ``` -输出如下 +拿 switch 后的表达式分别和 case 后的表达式进行对比,只要有一个 case 满足条件,就会执行对应的代码块,然后直接退出 switch - case ,如果 一个都没有满足,才会执行 default 的代码块。 -``` -名字:小明 -年龄:24 -性别:male -``` +## 1. 最简单的示例 -## 3. 方法的参数传递方式 +switch 后接一个你要判断变量 `education` (学历),然后 case 会拿这个 变量去和它后面的表达式(可能是常量、变量、表达式等)进行判等。 -上面定义方法的方式叫当你想要在方法内改变实例的属性的时候,必须使用指针做为方法的接收者。 +如果相等,就执行相应的代码块。如果不相等,就接着下一个 case。 ```go -package main - import "fmt" -// 声明一个 Profile 的结构体 -type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 -} - -// 重点在于这个星号: * -func (person *Profile) increase_age() { - person.age += 1 -} - func main() { - myself := Profile{name: "小明", age: 24, gender: "male"} - fmt.Printf("当前年龄:%d\n", myself.age) - myself.increase_age() - fmt.Printf("当前年龄:%d", myself.age) + education := "本科" + + switch education { + case "博士": + fmt.Println("我是博士") + case "研究生": + fmt.Println("我是研究生") + case "本科": + fmt.Println("我是本科生") + case "大专": + fmt.Println("我是大专生") + case "高中": + fmt.Println("我是高中生") + default: + fmt.Println("学历未达标..") + } } -``` -输出结果 如下,可以看到在方法内部对 age 的修改已经生效。你可以尝试去掉 `*`,使用值做为方法接收者,看看age是否会发生改变。 - -``` -当前年龄:24 -当前年龄:25 ``` +输出如下 - -至此,我们知道了两种定义方法的方式: - -- 以值做为方法接收者 -- 以指针做为方法接收者 - - - -那我们如何进行选择呢?以下几种情况,应当直接使用指针做为方法的接收者。 - -1. 你需要在方法内部改变结构体内容的时候 -2. 出于性能的问题,当结构体过大的时候 - -有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,建议都使用指针做为接收者。 - -不管你使用哪种方法定义方法,指针实例对象、值实例对象都可以直接调用,而没有什么约束。这一点Go语言做得非常好。 - - - -## 4. 结构体实现 “继承” - -Go 语言并不支持继承,但你可以使用组合的方法,实现类似继承的效果。 - -在生活中,组合的例子非常多,比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起,最后才有了我们用的电脑。 - -同样的,在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。 - -现在这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。 - -```go -type company struct { - companyName string - companyAddr string -} - -type staff struct { - name string - age int - gender string - position string -} ``` - -若要将公司信息与公司职员关联起来,一般都会想到将 company 结构体的内容照抄到 staff 里。 - -```go -type staff struct { - name string - age int - gender string - companyName string - companyAddr string - position string -} +我是本科生 ``` -虽然在实现上并没有什么问题,但在你对同一公司的多个staff初始化的时候,都得重复初始化相同的公司信息,这做得并不好,借鉴继承的思想,我们可以将公司的属性都“继承”过来。 -但是在 Go 中没有类的概念,只有组合,你可以将 company 这个 结构体嵌入到 staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company 的所有属性了。 -```go -type staff struct { - name string - age int - gender string - position string - company // 匿名字段 -} -``` +## 2. 一个 case 多个条件 -来写个完整的程序验证一下。 +case 后可以接多个多个条件,多个条件之间是 `或` 的关系,用逗号相隔。 ```go -package main - import "fmt" -type company struct { - companyName string - companyAddr string -} - -type staff struct { - name string - age int - gender string - position string - company -} - -func main() { - myCom := company{ - companyName: "Tencent", - companyAddr: "深圳市南山区", - } - staffInfo := staff{ - name: "小明", - age: 28, - gender: "男", - position: "云计算开发工程师", - company: myCom, +func main() { + month := 2 + + switch month { + case 3, 4, 5: + fmt.Println("春天") + case 6, 7, 8: + fmt.Println("夏天") + case 9, 10, 11: + fmt.Println("秋天") + case 12, 1, 2: + fmt.Println("冬天") + default: + fmt.Println("输入有误...") } - - fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName) - fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName) } ``` -输出结果如下,可见`staffInfo.companyName` 和 `staffInfo.company.companyName` 的效果是一样的。 +输出如下 ``` -小明 在 Tencent 工作 -小明 在 Tencent 工作 +冬天 ``` -## 5. 结构体实现“多态” - -接触过 Python 的朋友 一定知道鸭子类型,只要你长得像鸭子,那你就是一只鸭子。这就是我们所说的多态。 +## 3. case 条件常量不能重复 -在 Go 语言中,是通过接口来实现的多态。 +当 case 后接的是常量时,该常量只能出现一次。 -当一个类型/结构体,实现了一个接口的所有方法,它就隐式的实现了该接口。两个类型之间的实现关系不需要在代码中显式地表示出来。Go语言中没有类似于 implements 的关键字。 Go编译器将自动在需要的时候检查两个类型之间的实现关系。 +以下两种情况,在编译时,都会报错: duplicate case "male" in switch -先定义一个商品(Good)的接口,意思是一个类型或者结构体,只要实现了`settleAccount()` 和 `orderInfo()` 两个方法,那这个类型/结构体就是一个商品。 +**错误案例一** ```go -type Good interface { - settleAccount() int - orderInfo() string -} -``` - -然后我们定义两个结构体,分别是手机和赠品。 +gender := "male" -```go -type Phone struct { - name string - quantity int - price int -} - -type FreeGift struct { - name string - quantity int - price int +switch gender { + case "male": + fmt.Println("男性") + // 与上面重复 + case "male": + fmt.Println("男性") + case "female": + fmt.Println("女性") } ``` -然后分别为他们实现 Good 接口的两个方法 +**错误案例二** ```go -// Phone -func (phone Phone) settleAccount() int { - return phone.quantity * phone.price -} -func (phone Phone) orderInfo() string{ - return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + - phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" -} +gender := "male" -// FreeGift -func (gift FreeGift) settleAccount() int { - return 0 -} -func (gift FreeGift) orderInfo() string{ - return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + - gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" +switch gender { + case "male", "male": + fmt.Println("男性") + case "female": + fmt.Println("女性") } ``` -实现了 Good 接口要求的两个方法后,手机和赠品在Go语言看来就都是商品(Good)类型了。 +## 4. switch 后可接函数 -这里候,我挑选了两件商品(实例化),分别是手机和耳机(赠品,不要钱) +switch 后面可以接一个函数,只要保证 case 后的值类型与函数的返回值 一致即可。 ```go -iPhone := Phone{ - name: "iPhone", - quantity: 1, - price: 8000, -} -earphones := FreeGift{ - name: "耳机", - quantity: 1, - price: 200, -} -``` - -然后创建一个购物车(也就是类型为 Good的切片),来存放这些商品。 - -```go -goods := []Good{iPhone, earphones} -``` - -最后,定义一个方法来计算购物车里的订单金额 +import "fmt" -```go -func calculateAllPrice(goods []Good) int { - var allPrice int - for _,good := range goods{ - fmt.Println(good.orderInfo()) - allPrice += good.settleAccount() +// 判断一个同学是否有挂科记录的函数 +// 返回值是布尔类型 +func getResult(args ...int) bool { + for _, i := range args { + if i < 60 { + return false + } } - return allPrice -} -``` - -完整代码,我贴在下面,供你参考。 - -```go -package main - -import ( - "fmt" - "strconv" -) - -// 定义一个接口 -type Good interface { - settleAccount() int - orderInfo() string -} - -type Phone struct { - name string - quantity int - price int + return true } -func (phone Phone) settleAccount() int { - return phone.quantity * phone.price -} -func (phone Phone) orderInfo() string{ - return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + - phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" -} - -type FreeGift struct { - name string - quantity int - price int -} - -func (gift FreeGift) settleAccount() int { - return 0 -} -func (gift FreeGift) orderInfo() string{ - return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + - gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" -} - -func calculateAllPrice(goods []Good) int { - var allPrice int - for _,good := range goods{ - fmt.Println(good.orderInfo()) - allPrice += good.settleAccount() - } - return allPrice -} -func main() { - iPhone := Phone{ - name: "iPhone", - quantity: 1, - price: 8000, - } - earphones := FreeGift{ - name: "耳机", - quantity: 1, - price: 200, +func main() { + chinese := 80 + english := 50 + math := 100 + + switch getResult(chinese, english, math) { + // case 后也必须 是布尔类型 + case true: + fmt.Println("该同学所有成绩都合格") + case false: + fmt.Println("该同学有挂科记录") } - - goods := []Good{iPhone, earphones} - allPrice := calculateAllPrice(goods) - fmt.Printf("该订单总共需要支付 %d 元", allPrice) } ``` -运行后,输出如下 - -``` -您要购买1个iPhone计:8000元 -您要购买1个耳机计:0元 -该订单总共需要支付 8000 元 -``` - - - -## 6. 内部方法与外部方法 -在 Go 语言中,使用函数名来对控制方法的访问权限。 -- 当方法的首字母为大写时,这个方法对于所有包都是Public +## 5. switch 可不接表达式 -- 当方法的首字母为小写时,这个方法对于其他包是Private,无法访问 +switch 后可以不接任何变量、表达式、函数。 - - - - -## 7. 接口的定义和使用 - - - -当你的实例只被声明,还未进行初始化,它是一个 nil 的指针,nil 指针也是可以调用方法的,但是无法访问属性。 +当不接任何东西时,switch - case 就相当于 if - elseif - else ```go -var myself *Profile +score := 30 + +switch { + case score >= 95 && score <= 100: + fmt.Println("优秀") + case score >= 80: + fmt.Println("良好") + case score >= 60: + fmt.Println("合格") + case score >= 0: + fmt.Println("不合格") + default: + fmt.Println("输入有误...") +} ``` +## 6. switch 的穿透能力 +正常情况下 switch - case 的执行顺序是:只要有一个 case 满足条件,就会直接退出 switch - case ,如果 一个都没有满足,才会执行 default 的代码块。 -```go -package main +但是有一种情况是例外。 -import "fmt" +那就是当 case 使用关键字 `fallthrough` 开启穿透能力的时候。 -func main() { - // 声明一个 Profile 的结构体 - type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 - } - // 声明三个对象,并初始化赋值 - mymother := Profile{name: "ming's mother", age: 45} - myfather := Profile{name: "ming's father", age: 45} - myself := Profile{name: "ming", age: 24, gender: "male", mother: &mymother, father: &myfather} - fmt.Println(myself) - - // 访问普通成员属性 - fmt.Println(myself.name) - // 访问指针成员属性 - fmt.Println(myself.father.name) +```go +s := "hello" +switch { +case s == "hello": + fmt.Println("hello") + fallthrough +case s != "world": + fmt.Println("world") } ``` -输出如下 +代码输出如下: ``` -{ming 24 male 0xc000058040 0xc000058080} -ming -ming's father +hello +world ``` -用接口来做,面向接口编程 - -构造函数,不需要考虑是在堆上还是在栈上 - - - -每个目录有且只能有一个包,包名不一定要和目录名一样,main包包含可执行入口 - -为结构定义的包必须 放于同一包内,但是可以是不同的文件 - - - - - - - - - -扩充系统类型或者别人的类型 - -- 定义别名:最简单 -- 使用组合:最常用 +需要注意的是,fallthrough 只能穿透一层,意思是它只给你一次再判断case的机会,不管你有没有匹配上,都要退出了。 ```go -import ( - "fmt" - "imooc.com/ccmouse/learngo/profile" -) - -type MyProfile struct { - Profile *profile.Profile +s := "hello" +switch { +case s == "hello": + fmt.Println("hello") + fallthrough +case s == "xxxx": + fmt.Println("xxxx") +case s != "world": + fmt.Println("world") } +``` -func (person *MyProfile) add_age() { - person.Profile.Age += 1 -} - -func main() { - var pro profile.Profile - pro = profile.Profile{Name: "wangbm", Age: 24, Gender: "male"} - - mypro := MyProfile{Profile:&pro} - mypro.add_age() - fmt.Printf("当前年龄:%d\n", mypro.Profile.Age) +输出如下,并不会输出 `world`(即使它符合条件) - mypro.Profile.FmtProfile() -} +``` +hello +xxxx ``` -- 使用内嵌的方法:Embedding ,可以省下很多代码 \ No newline at end of file diff --git a/source/c09/c09_12.md b/source/c09/c09_12.md index 4acae46..c19da3f 100644 --- a/source/c09/c09_12.md +++ b/source/c09/c09_12.md @@ -1,182 +1,168 @@ -# 9.12 Go语言的包依赖管理 +# 9.11 Go语言流程控制:for -在以前,Go 语言的的包依赖管理一直都被大家所诟病,但最近几年,这个窘境开始得到缓解。 +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: -现在最主流的包依赖管理方式是使用官方推荐的 go module 的方式,如果你和我一样是刚开始学习Go语言的,建议也了解一下Go语言包依赖管理方案,是如何一步一步发展到今天的。 +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 -简单来说,Go语言的包依赖管理方案,可以分为三个阶段。 +上一篇讲了switch - case 选择语句,今天先来讲讲 for 循环语句。 -## **第一阶段**:使用最古老的 GOPATH 进行管理 +## 0. 语句模型 -使用 GOPATH 的话,你需要将你所有的第三方库都下载到本地,这本身没有什么问题,其他语言也都是这么做的,问题就在于,在本地只能保存一个版本的第三方库,如果你的机器上有多个项目,而这些项目是基于不同版本的库进行开发的,那就尴尬了。 +这是 for 循环的基本模型。 +``` +for [condition | ( init; condition; increment ) | Range] +{ + statement(s); +} +``` +可以看到 for 后面,可以接三种类型的表达式。 -## **第二阶段**:使用 GOVENDOR 解决方案 - -为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 开始支持 vendor 。 - -以前使用 GOPATH 的时候,所有的项目都共享一个 GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH 下只能有一个版本的第三方库。 - -解决的思路就是,在每个项目下都创建一个 vendor 目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5+ 的Go 会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。如果在 vendor 目录下没有找到,才会去 `$GOAPTH/src` 中去查找 - -在这个阶段,也催生出了各种第三方的管理包依赖插件:godep,dep,glide - -使用 godep 的开发流程是这样的: - -1. 先保证程序在本地能够正常编译 -2. 执行`godep save`保存当前项目的所有第三方依赖的版本信息和代码到 `Godeps/Godeps.json`文件中 -3. 提交Godeps目录和vender目录到代码库。 -4. 如果要更新依赖的版本,可以直接修改`Godeps.json`文件中的对应项 - -虽然这个方案解决了一些问题,但是解决得并不完美。 +1. 接一个条件表达式 +2. 接三个表达式 +3. 接一个 range 表达式 -如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 +但其实还有第四种 +4. 不接表达式 +## 1. 接一个条件表达式 -## **第三阶段**:使用 GO MODULE 版本管理工具 +这个例子会打印 1 到 5 的数值。 -go module 在 v1.11 版本推出,并在 v1.13版本中,成为官方默认的包依赖管理工具。 +```go +a := 1 +for a <= 5 { + fmt.Println(a) + a ++ +} +``` -v1.11 开始,`go env` 多了个环境变量: `GO111MODULE`,这是一个开关,通过它可以开启或关闭模块支持,它有三个可选值:`off`、`on`、`auto`,默认值是`auto`。 +输出如下 -1. `GO111MODULE=off`禁用模块支持,编译时会从`GOPATH`和`vendor`文件夹中查找包。 -2. `GO111MODULE=on`启用模块支持,编译时会忽略`GOPATH`和`vendor`文件夹,只根据 `go.mod`下载依赖。 -3. `GO111MODULE=auto`,当项目在`$GOPATH/src`外且项目根目录有`go.mod`文件时,开启模块支持。 +``` +1 +2 +3 +4 +5 +``` -go mod 出现后, GOPATH 和 GOVENDOR 将被逐步淘汰,但是若你的项目仍然要使用那些 out 的包依赖管理方案,需要注意将 GO111MODULE 置为 off。 +## 2. 接三个表达式 +for 后面,紧接着三个表达式,使用 `;` 分隔。 +这三个表达式,各有各的用途 -接下来,介绍一下,如何使用 go module 来管理依赖。 +- 第一个表达式:初始化控制变量,在整个循环生命周期内,只运行一次; +- 第二个表达式:设置循环控制条件,当返回true,继续循环,返回false,结束循环; +- 第三个表达式:每次循完开始(除第一次)时,给控制变量增量或减量。 -使用 go module 管理依赖后会在项目根目录下生成两个文件`go.mod`和`go.sum`。 +这边的例子和上面的例子,是等价的。 -`go.mod` 文件记录了项目所有的依赖信息,其结构大致如下: +```go +import "fmt" +func main() { + for i := 1; i <= 5; i++ { + fmt.Println(i) + } +} ``` -module github.com/Q1mi/studygo/blogger - -go 1.12 - -require ( - github.com/DeanThompson/ginpprof v0.0.0-20190408063150-3be636683586 - github.com/gin-gonic/gin v1.4.0 - github.com/go-sql-driver/mysql v1.4.1 - github.com/jmoiron/sqlx v1.2.0 - github.com/satori/go.uuid v1.2.0 - google.golang.org/appengine v1.6.1 // indirect -) -``` - -其中, - -- `module`用来定义包名 -- `require`用来定义依赖包及版本 -- `indirect`表示间接引用 - -而 `go.sum` 记录该项目中每个依赖库的版本和哈希值。 - - -使用 go module 需熟悉 go mod 的命令 +输出如下 ``` -go mod init 初始化当前文件夹, 创建go.mod文件,后可接参数指定 module 名 -go mod graph 打印模块依赖图 -go mod tidy 增加缺少的module,删除无用的module -go mod vendor 将依赖复制到vendor下 -go mod verify 校验依赖 -go mod why 解释为什么需要依赖 -go mod download 下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录) - -go mod edit 编辑go.mod文件 - 接 -fmt 参数格式化 go.mod 文件 - 接 -require=golang.org/x/text 添加依赖 - 接 -droprequire=golang.org/x/text 删除依赖 - 更加用法,参看 go help mod edit +1 +2 +3 +4 +5 ``` -如何给项目添加依赖(下载包并将依赖写进go.mod文件)? - -有两种方法: - -- 你只要在项目中有 import,然后 go build 就会 go module 就会自动下载并添加。 - -- 自己手工使用 go get 下载,支持语义化版本号 - -```shell -# 拉取最新 -go get github.com/foo +## 2. 不接表达式:无限循环 -# 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) -go get -u github.com/foo +在 Go 语言中,没有 while 循环,如果要实现无限循环,也完全可以 for 来实现。 -# 升级到最新的修订版本 -go get -u=patch github.com/foo +当你不加任何的判断条件时, 就相当于你每次的判断都为 true,程序就会一直处于运行状态,但是一般我们并不会让程序处于死循环,在满足一定的条件下,可以使用关键字 `break` 退出循环体,也可以使用 `continue` 直接跳到下一循环。 -# 指定版本 -go get github.com/foo@v1.2.3 +下面两种写法都是无限循环的写法。 -# 指定分支 -go get github.com/foo@master +```go +for { + 代码块 +} -# 指定git提交的hash值 -go get github.com/foo@e3702bed2 +// 等价于 +for ;; { + 代码块 +} +``` -# 指定版本 -go get github.com/foo@v1.11.0 +举个例子 + +```go +import "fmt" + +func main() { + var i int = 1 + for { + if i > 5 { + break + } + fmt.Printf("hello, %d\n", i) + i++ + } +} ``` +输出如下 +``` +hello, 1 +hello, 2 +hello, 3 +hello, 4 +hello, 5 +``` -使用以上方式添加完依赖后,在 go.mod 文件里会有你所依赖的包及其版本。 +## 3. 接 for-range 语句 -如果项目下已经有这个 go.mod 文件,但是包还没拉取,如何 触发下载呢?两种方法 +遍历一个可迭代对象,是一个很常用的操作。在 Go 可以使用 for-range 的方式来实现。 -- 可以执行命令 `go build ./...`,go module 会自动下载 -- 使用 `go mod download` ,手动下载 +range 后可接数组、切片,字符串等 -如果你连这个 go.mod 文件都丢失了,那怎么生成?两种方法 +由于 range 会返回两个值:索引和数据,若你后面的代码用不到索引,需要使用 `_` 表示 。 -- 使用 Goland 的话,可以点击 create go.mod 文件来生成。 -- 也可以在项目目录下执行这条命令 +```go +import "fmt" -```shell -$ go mod init +func main() { + myarr := [...]string{"world", "python", "go"} + for _, item := range myarr { + fmt.Printf("hello, %s\n", item) + } +} ``` - - -由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 +输出如下 ``` -replace ( - golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac - golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d - golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 -) +hello, world +hello, python +hello, go ``` -以上几种解决方案,不同之处就在于它们的依赖包的搜索路径优先级不同 - -- 使用 go mod,只在 `$GOPATH/pkg/mod` 查找依赖包(GO111MODULE=on) -- 使用 GOVENDOR,优先在 vendor目录中查找,然后才去 `$GOROOT/src` 和 `$GOPATH/src`查找 -- 使用 GOPATH,先去`$GOROOT/src` 查找 ,找不到再去 `$GOPATH/src` 中查找 - - - -## 参考文章: - -- [Go语言之依赖管理](https://www.cnblogs.com/Dr-wei/p/11742253.html) - - diff --git a/source/c09/c09_13.md b/source/c09/c09_13.md index 17b605c..4eca702 100644 --- a/source/c09/c09_13.md +++ b/source/c09/c09_13.md @@ -1,9 +1,170 @@ -# 9.13 go 命令详解 +# 9.13 Go语言流程控制:goto 无条件跳转 +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 +前面三种,我已经都讲过了,今天要讲讲 goto 的无条件跳转。 +很难想象在 Go 居然会保留 goto,因为很多人不建议使用 goto,所以在一些编程语言中甚至直接取消了 goto。 -go build ./... 不会生成可执行文件到 ${GOPATH}/bin ,只要能编译过就行 +我感觉 Go 既然保留,一定有人家的理由,只是我目前还没感受到。不管怎样,咱还是照常学习吧。 + + + +## 0. 基本模型 + +`goto` 顾言思义,是跳转的意思。 + +goto 后接一个标签,这个标签的意义是告诉 Go程序下一步要执行哪里的代码。 + +所以这个标签如何放置,放置在哪里,是 goto 里最需要注意的。 + +```go +goto 标签; +... +... +标签: 表达式; +``` + +## 1. 最简单的示例 + +`goto` 可以打破原有代码执行顺序,直接跳转到某一行执行代码。 + +```go +import "fmt" + +func main() { + + goto flag + fmt.Println("B") +flag: + fmt.Println("A") + +} +``` + +执行结果,并不会输出 B ,而只会输出 A + +``` +A +``` + + + +## 2. 如何使用? + +`goto` 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。 + +这边举一个例子,用 `goto` 的方式来实现一个打印 1到5 的循环。 + +```go +import "fmt" + +func main() { + i := 1 +flag: + if i <= 5 { + fmt.Println(i) + i++ + goto flag + } +} +``` + +输出如下 + +``` +1 +2 +3 +4 +5 +``` + +再举个例子,使用 goto 实现 类型 break 的效果。 + +```go +import "fmt" + +func main() { + i := 1 + for { + if i > 5 { + goto flag + } + fmt.Println(i) + i++ + } +flag: +} +``` + +输出如下 + +``` +1 +2 +3 +4 +5 +``` + +最后再举个例子,使用 goto 实现 类型 continue的效果,打印 1到10 的所有偶数。 + +```go +import "fmt" + +func main() { + i := 1 +flag: + for i <= 10 { + if i%2 == 1 { + i++ + goto flag + } + fmt.Println(i) + i++ + } +} +``` + +输出如下 + +``` +2 +4 +6 +8 +10 +``` + + + +## 3. 注意事项 + +goto语句与标签之间不能有变量声明,否则编译错误。 + +```go +import "fmt" + +func main() { + fmt.Println("start") + goto flag + var say = "hello oldboy" + fmt.Println(say) +flag: + fmt.Println("end") +} +``` + +编译错误 + +``` +.\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6 +``` -go install ./... 会生成可执行文件到 ${GOPATH}/bin \ No newline at end of file diff --git a/source/c09/c09_14.md b/source/c09/c09_14.md index 8d2c407..2f19b4b 100644 --- a/source/c09/c09_14.md +++ b/source/c09/c09_14.md @@ -1,148 +1,225 @@ -# 9.14 几个信道死锁经典错误案例详解 +# 9.14 Go语言流程控制:defer 延迟语句 -刚接触 Go 语言的信道的时候,经常会遇到死锁的错误,而导致这个错误的原因有很多种,这里整理了几种常见的。 +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: -``` -fatal error: all goroutines are asleep - deadlock! -``` +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 +今天是最后一篇讲控制流程了,内容是 defer 延迟语句,这个在其他编程语言里好像没有见到。应该是属于 Go 语言里的独有的关键字,但即使如此,阅读后这篇文章后,你可以发现 defer 在其他编程语言里的影子。 -## 错误示例一 -看下面这段代码 +## 1. 延迟调用 + +defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个 `xxx` 函数的调用延迟到当前函数执行完后再执行。 ```go -package main +defer xxx() +``` +这是一个很简单的例子,可以很快帮助你理解 defer 的使用效果。 + +```go import "fmt" +func myfunc() { + fmt.Println("B") +} + func main() { - pipline := make(chan string) - pipline <- "hello world" - fmt.Println(<-pipline) -} + defer myfunc() + fmt.Println("A") +} ``` -运行会抛出错误,如下 +输出如下 ``` -fatal error: all goroutines are asleep - deadlock! +A +B ``` -看起来好像没有什么问题?先往信道中存入数据,再从信道中读取数据。 +当然了,对于上面这个例子可以简写为成如下,输出结果是一致的 + +```go +import "fmt" + +func main() { + defer fmt.Println("B") + fmt.Println("A") +} +``` -回顾前面的基础,我们知道使用 make 创建信道的时候,若不传递第二个参数,则你定义的是无缓冲信道,而对于无缓冲信道,在接收者未准备好之前,发送操作是阻塞的。 -因此,对于解决此问题有两种方法: -1. 使接收者代码在发送者之前执行 -2. 使用缓冲信道,而不使用无缓冲信道 +## 2. 即时求值的变量快照 -**第一种方法**: +使用 defer 只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响。 -若要程序正常执行,需要保证接收者程序在发送数据到信道前就进行阻塞状态,修改代码如下 +比如这边的例子 ```go -package main - import "fmt" func main() { - pipline := make(chan string) - fmt.Println(<-pipline) - pipline <- "hello world" -} + name := "go" + defer fmt.Println(name) // 输出: go + + name = "python" + fmt.Println(name) // 输出: python +} +``` + +输出如下,可见给 name 重新赋值为 `python`,后续调用 defer 的时候,仍然使用未重新赋值的变量值,就好在 defer 这里,给所有的这是做了一个快照一样。 + +``` +python +go ``` -运行的时候还是报同样的错误。问题出在哪里呢? -原来我们将发送者和接收者写在了同一协程中,虽然保证了接收者代码在发送者之前执行,但是由于前面接收者一直在等待数据 而处于阻塞状态,所以无法执行到后面的发送数据。还是一样造成了死锁。 -有了前面的经验,我们将接收者代码写在另一个协程里,并保证在发送者之前执行,就像这样的代码 +## 3. 多个defer 反序调用 + +当我们在一个函数里使用了 多个defer,那么这些defer 的执行函数是如何的呢? + +做个试验就知道了 ```go -package main +import "fmt" -func hello(pipline chan string) { - <-pipline -} +func main() { + name := "go" + defer fmt.Println(name) // 输出: go -func main() { - pipline := make(chan string) - go hello(pipline) - pipline <- "hello world" + name = "python" + defer fmt.Println(name) // 输出: python + + name = "java" + fmt.Println(name) } ``` -运行之后 ,一切正常。 +输出如下,可见 多个defer 是反序调用的,有点类似栈一样,后进先出。 -**第二种方法**: +``` +java +python +go +``` -接收者代码必须在发送者代码之前 执行,这是针对无缓冲信道才有的约束。 -既然这样,我们改使用可缓冲信道不就OK了吗? -```go -package main +## 3. defer 与 return 孰先孰后 + +至此,defer 还算是挺好理解的。在一般的使用上,是没有问题了。 + +在这里提一个稍微复杂一点的问题,defer 和 return 到底是哪个先调用? + +使用下面这段代码,可以很容易的观察出来 +```go import "fmt" +var name string = "go" + +func myfunc() string { + defer func() { + name = "python" + }() + + fmt.Printf("myfunc 函数里的name:%s\n", name) + return name +} + func main() { - pipline := make(chan string, 1) - pipline <- "hello world" - fmt.Println(<-pipline) -} + myname := myfunc() + fmt.Printf("main 函数里的name: %s\n", name) + fmt.Println("main 函数里的myname: ", myname) +} ``` -运行之后,一切正常。 +输出如下 +``` +myfunc 函数里的name:go +main 函数里的name: python +main 函数里的myname: go +``` +来一起理解一下这段代码,第一行很直观,name 此时还是全局变量,值还是go -## 错误示例二 +第二行也不难理解,在 defer 里改变了这个全局变量,此时name的值已经变成了 python -信道的一端是数据的输入,另一端是数据的读取。 +重点在第三行,为什么输出的是 go ? -少了任意一端,都会形成死锁,触发 panic 异常。 +解释只有一个,那就是 defer 是return 后才调用的。所以在执行 defer 前,myname 已经被赋值成 go 了。 -发送一个数据,必然要有一个人来接收数据,若出现二者不匹配,就会出现死锁。有两种情况 -1. 发送了一个数据,没人接收 -2. 接收者在等待一个无人发送的数据 -**第一种情况** +## 4. 为什么要有 defer? -发送了两条数据 ,却只有一个接收者。 +看完上面的例子后,不知道你是否和我一样,对这个defer的使用效果感到熟悉?貌似在 Python 也见过类似的用法。 -```go -func main() { - ch1 := make(chan string) - go func() { - fmt.Println(<-ch1) - }() - ch1 <- "hello world" - ch1 <- "hello China" -} -``` +虽然 Python 中没有 defer ,但是它有 with 上下文管理器。我们知道在 Python 中可以使用 defer 实现对资源的管理。最常用的例子就是文件的打开关闭。 -**第二种情况** +你可能会有疑问,这也没什么意义呀,我把这个放在 defer 执行的函数放在 return 那里执行不就好了。 -for 循环接收了两次消息("hello world"和“hello China”)后,再也没有人发送数据,接收者就牌一个等待无人发送的数据的囧境。造成死锁。 +固然可以,但是当一个函数里有多个 return 时,你得多调用好多次这个函数,代码就臃肿起来了。 + +若是没有 defer,你可以写出这样的代码 ```go -package main +func f() { + r := getResource() //0,获取资源 + ...... + if ... { + r.release() //1,释放资源 + return + } + ...... + if ... { + r.release() //2,释放资源 + return + } + ...... + if ... { + r.release() //3,释放资源 + return + } + ...... + r.release() //4,释放资源 + return +} +``` -import "fmt" +使用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer 后的函数。 -func main() { - ch1 := make(chan string) - go func() { - ch1 <- "hello world" - ch1 <- "hello China" - }() - for { - fmt.Println(<-ch1) - } +```go +func f() { + r := getResource() //0,获取资源 + + defer r.release() //1,释放资源 + ...... + if ... { + ... + return + } + ...... + if ... { + ... + return + } + ...... + if ... { + ... + return + } + ...... + return } ``` From 08f7943248adc79a3f6b74481788704a3f234a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 22:44:54 +0800 Subject: [PATCH 003/147] =?UTF-8?q?update:=20=E4=BF=AE=E6=94=B9=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_01.md | 16 +++++++++++++++- source/c04/c04_21.md | 24 ++++++++++++++++++++++++ source/c07/c07_01.md | 15 +++++++++++++++ source/c07/c07_03.md | 43 ++++++++++++++++++++++++++++++++++--------- source/c08/c08_01.md | 14 +++++++++++++- source/c08/c08_02.md | 3 ++- 6 files changed, 103 insertions(+), 12 deletions(-) diff --git a/source/c04/c04_01.md b/source/c04/c04_01.md index a3d44e2..687cd85 100644 --- a/source/c04/c04_01.md +++ b/source/c04/c04_01.md @@ -43,8 +43,9 @@ $ virtualenv --version 由于virtualenv创建虚拟环境是在当前环境下创建的。所以我们要准备一个专门存放虚拟环境的目录。(以下操作在Linux在完成,windows相对简单,请自行完成,有不明白的请微信与我联系。) **创建** + ```bash -# 准备目录并进行 +# 准备目录并进入 $ mkdir -p /home/wangbm/Envs $ cd !$ @@ -117,7 +118,14 @@ pip install virtualenvwrapper-win find / -name virtualenvwrapper.sh # /usr/bin/virtualenvwrapper.sh ``` +若是 windows 则使用everything 查找 virtualenvwrapper.bat 脚本 + +``` +D:\Program Files (x86)\Python38-32\Scripts\virtualenvwrapper.bat +``` + 在~/.bashrc 文件新增配置 + ``` export WORKON_HOME=$HOME/.virtualenvs export PROJECT_HOME=$HOME/workspace @@ -125,6 +133,12 @@ export VIRTUALENVWRAPPER_SCRIPT=/usr/bin/virtualenvwrapper.sh source /usr/bin/virtualenvwrapper.sh ``` +若是 windows 则新增环境变量:`WORKON_HOME` + +![](http://image.python-online.cn/20200209161935.png) + + + **基本语法**: mkvirtualenv [-a project_path] [-i package] [-r requirements_file] [virtualenv options] ENVNAME diff --git a/source/c04/c04_21.md b/source/c04/c04_21.md index 317dc2f..ad0ba65 100644 --- a/source/c04/c04_21.md +++ b/source/c04/c04_21.md @@ -324,6 +324,30 @@ pip install --upgrade pkg1 pip install --upgrade pkg1 --upgrade-strategy only-if-need ``` +## 6. 配置文件 + +由于在使用 pip 安装一些包时,默认会使用 pip 的官方源,所以经常会报网络超时失败。 + +常用的解决办法是,在安装包时,使用 `-i` 参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 + +这时候,其实可以将这个源写进 pip 的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 + +那怎么配置呢?文件文件在哪? + +使用` win+r` 输入 `%APPDATA%` 进入用户资料文件夹,查看有没有一个 pip 的文件夹,若没有则创建之。 + +然后进入这个 文件夹,新建一个 `pip.ini` 的文件,内容如下 + +```ini +[global] +time-out=60 +index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ +[install] +trusted-host=tsinghua.edu.cn +``` + + + 以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 diff --git a/source/c07/c07_01.md b/source/c07/c07_01.md index f4d6116..5ac8fef 100644 --- a/source/c07/c07_01.md +++ b/source/c07/c07_01.md @@ -341,6 +341,21 @@ sed 's/ruby//g' somefile #删除ruby 【注意】:以上对源文件都不做修改,若要修改,要加上`-i` +替换换行符 + +```shell +# 将换行符换成逗号 +$ cat a.txt +1 +2 +3 +3 +$ sed ":a;N;s/\n/,/g;ta" a.txt +1,2,3,4 +``` + + + ### 1.4 文件重构 **cut** diff --git a/source/c07/c07_03.md b/source/c07/c07_03.md index b41ae89..c087cbe 100644 --- a/source/c07/c07_03.md +++ b/source/c07/c07_03.md @@ -2,11 +2,12 @@ --- -## 一、安装Docker +## 1. 安装Docker 参照官方文档,不同Linux发行版,安装方法略有不同。 CentOS:[CentOS 7 上安装 Docker 详解](https://docs.docker.com/engine/installation/linux/docker-ce/centos/#set-up-the-repository) Ubuntu:[Ubuntu 16.04 上安装 Docker 详解](https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/) +Windows:[Windows 10 上安装 Docker 指导](https://hub.docker.com/?overlay=onboarding) 因为本人都是使用CentOS 7.2的系统,所以以下示例都将在CentOS上操作。 @@ -59,7 +60,31 @@ $ sudo systemctl start docker $ chkconfig docker on ``` -## 二、创建第一个容器 +## 2. 配置镜像源 + +docker默认镜像拉取地址为国外仓库下载速度较慢,网速不好,则会报错”net/http: TLS handshake timeout”。 + +解决方法很简单,给你的 Docker 配置一个国内的镜像源。 + +在 Win 10 上可以这样配。 + +点击 Docker 图标,选择 `Setting` -> `Docker Engine` 输入 + +``` +{ + "registry-mirrors": [ + "https://dockerhub.azk8s.cn", + "https://hub-mirror.c.163.com" + ], + "insecure-registries": [], + "debug": true, + "experimental": false +} +``` + + + +## 3. 创建第一个容器 `docker run -d -p 80:80 httpd` ``` @@ -76,9 +101,9 @@ $ docker ps 或者 docker container ls -## 三、关于容器 +## 4、关于容器 -### 3.1 相关命令 +### 4.1 相关命令 ``` # 运行 docker run [-d] --name --hostname /bin/bash -c "while true;do sleep 1;done" @@ -118,7 +143,7 @@ docker rm ... docker create ``` -### 3.2 内存限额 +### 4.2 内存限额 ``` $ docker run -m 200M --memory-swap=300M ubuntu @@ -131,7 +156,7 @@ $ docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 28 --vm-bytes 280M:每个线程分配 280M 内存。如果分配超过300,就会出错,容器退出 ``` -### 3.3 cpu配额 +### 4.3 cpu配额 默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。 Docker 可以通过 `-c` 或 `--cpu-shares` 设置容器使用 CPU 的权重。如果不指定,默认值为 1024。 @@ -148,7 +173,7 @@ docker run --name container_B -it -c 512 --cpu 1 ``` -### 3.4 blkio配额 +### 4.4 blkio配额 `Block IO` 限制不同容器的读写资源分配 Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽。 默认情况下,所有容器能平等地读写磁盘,可以通过设置 `--blkio-weight` 参数来改变容器 block IO 的优先级。设置的是相对权重值,默认为 500。 @@ -178,7 +203,7 @@ time dd if=/dev/zero out=test.out bs=1M count=800 oflag=direct # 测试速度 ``` -### 3.5 cgroup +### 4.5 cgroup `cgroup`全称`Control Group`。`Linux` 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额。 ``` 在相应路径下,每个容器都有对应一个以id命令的文件夹,里面有一些配置文件,就记录了配额信息。 @@ -193,7 +218,7 @@ time dd if=/dev/zero out=test.out bs=1M count=800 oflag=direct # 测试速度 路径:/sys/fs/blkio/cpu/docker ``` -### 3.6 namespace +### 4.6 namespace `namespace` 管理着 `host` 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,`namespace` 实现了容器间资源的隔离。 namespace 有下面六种 diff --git a/source/c08/c08_01.md b/source/c08/c08_01.md index bb6f729..cac7472 100644 --- a/source/c08/c08_01.md +++ b/source/c08/c08_01.md @@ -260,7 +260,7 @@ $ openstack role remove -h ### 1.2 virsh命令 -``` +```shell # 查看虚拟机的网卡信息 $ virsh domiflist VM1 @@ -294,6 +294,12 @@ virsh set-user-password instance-00000444 root "root12#$" ``` +热去除带宽限速 + +```shell +$ virsh domiftune ws_controller01 vnet4 --inbound 0,0,0 --outbound 0,0,0 --config --live +``` + 压缩镜像 ```shell @@ -312,6 +318,12 @@ echo 'export TMPDIR=/data/tmp' >> /etc/profile source /etc/profile ``` +在线查看虚拟机的messages日志 + +```shell +virt-log -d ws_controller01 +``` + ### 1.2 LVM管理 diff --git a/source/c08/c08_02.md b/source/c08/c08_02.md index 80fe2b1..33d5456 100644 --- a/source/c08/c08_02.md +++ b/source/c08/c08_02.md @@ -19,6 +19,7 @@ SR-IOV 规范定义了新的标准,根据该标准,创建的新设备可允 vim /etc/sysconfig/grub(或者/etc/default/grub):intel_iommu=on ![](https://i.loli.net/2018/01/19/5a61c022d68d3.png) 改完后,重新生成配置 + ``` grub2-mkconfig -o /etc/grub2.cfg ``` @@ -36,7 +37,7 @@ echo "options igb max_vfs=32" >>/etc/modprobe.d/igb.conf # 持久化 echo "echo '32' > /sys/class/net/eth0/device/sriov_numvfs" >> /etc/rc.local # 验证VF是否创建完成 -lcpci |grep Ethernet +lspci |grep Ethernet ``` ### 8.2.1.5 验证基础环境 From 4175ab7e5b300ee5499baead9aeaa3aff1fb18b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 22:45:27 +0800 Subject: [PATCH 004/147] =?UTF-8?q?add:=20=E5=A2=9E=E5=8A=A0=E7=AC=AC?= =?UTF-8?q?=E5=8D=81=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_01.md | 142 ++++++++++++++++++++++++++++++++++++++++ source/chapters/p10.rst | 17 +++++ 2 files changed, 159 insertions(+) create mode 100644 source/c10/c10_01.md create mode 100644 source/chapters/p10.rst diff --git a/source/c10/c10_01.md b/source/c10/c10_01.md new file mode 100644 index 0000000..858ef63 --- /dev/null +++ b/source/c10/c10_01.md @@ -0,0 +1,142 @@ +# 10.1 情人节来了,教你使用 Python 来表白 + +**作者**:@明哥 +**公众号**:Python编程时光 + +--- + +2020年,这个看起来如此浪漫的年份,你还是一个人吗? + +2018年的时候,写过一篇介绍如何使用 Python 来表白的文章。 + +虽然创意和使用效果都不错,但有一缺点,这是那个exe文件,女神需要打开电脑,才有可能参与进来,进而被你成功"调戏”。 + +由于是很早期的文章了,应该有很多人没有看过。 + +没有看过的,你可以点击这里查看:[用Python写一个表白神器让你脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) + +提醒你一下,后天就是 2月14日了。什么?还是一条狗呢? + +行吧,那你赶上了,今天的文章,就是为你而写。 + +明哥今天来教你如何使用 Python 来向心中的女神表白。 + +前段时间,在微博上刷到了一条推荐。内容是这样的 + +![微博截图](http://image.python-online.cn/20200211211522.png) + +出于好奇,我点开了,放大再放大,emmm,有点意思吖… + +![img](http://image.python-online.cn/20200211211657.png) + +这四个字,对于像我这样腼腆的DS男来说,还真不好意思说,说出来,万一被拒绝了咋办? + +使用套路来表白,并观察对方的反应,你大概能清楚对方是否对你也有好感,先测试下自己有几成的把握再下手或许更稳妥。 + +今天就教大家一个这样的套路:如何使用 Python 来做出来这样的图,有点浪漫,又有点极客。能不能拿下你女神,就要靠你(命)了。(๑•́₃ •̀๑) + +首先,你得先找到一张你女神的高清图片(尽量分辨率高点的吧,效果会好点)。 + +这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 + +![](http://image.python-online.cn/20200214104413.png) + +使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? + +![](http://image.python-online.cn/save.jpeg) + +然后将这张图片发给你的女神,具体话术你自己想咯。 + +------ + +相比女神来说,你可能更在意这是如何实现的(**活该你单身**)。 + +其实原理很简单,代码也还不到20 行。 + +首先,来讲讲原理。 + +事实上,每一张图片都是由一个一个的像素点所组成的。而每个像素点,都有自己的颜色,其颜色可以用一个数组来表示:(a,b,c),其中每位数的取值范围都是 0-255。 + +比如(0,0,0)代表黑色色,(255,255,255)代表白色。 + +当像素点足够多的时候,这张照片就是我们所说的高清照片。 + +而如果当像素点太少,我们的肉眼就能感知到明显的锯齿感。 + +用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 + +![](http://image.python-online.cn/20200214104646.png) + +我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 + +有了思路,就可以开始我们的代码。 + +首先,使用 pillow.Image读取图像,并使用load函数获取到每一个像素值。 + +```python +img_raw = Image.open(img_path) +img_array = img_raw.load() +``` + +然后新建一张画布,并选好你要使用的字体和字体大小。 + +```python +img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) +draw = ImageDraw.Draw(img_new) +font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) +``` + +由于需要不断循环 “我喜欢你!”,这五个字符。所以这里可以while循环 yield 来实现一个生成器。 + +```python +def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] +``` + +最后,要给这些字加上相应的颜色,写入新创建的画布中。 + +```python +for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) +``` + +最后将成品保存 + +```python +img_new.convert('RGB').save("F://gyy_save.jpeg") +``` + +完整代码如下,供你参考 + +```python +from PIL import Image, ImageDraw, ImageFont + +font_size = 7 +text = "我喜欢你!" +img_path = "F://gyy.jpeg" + +img_raw = Image.open(img_path) +img_array = img_raw.load() + +img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) +draw = ImageDraw.Draw(img_new) +font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) + +def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] + +ch_gen = character_generator(text) + +for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) + +img_new.convert('RGB').save("F://save.jpeg") +``` + + diff --git a/source/chapters/p10.rst b/source/chapters/p10.rst new file mode 100644 index 0000000..fab4ef4 --- /dev/null +++ b/source/chapters/p10.rst @@ -0,0 +1,17 @@ +============================= +第十章:有趣的工具 +============================= + +Python 是一门可以快速开发的语言,使用它可以用最少的时间,做出符合预期,且效果还不错的产品。 + +这一章里,我将写一写我使用 Python 做出过哪一些好用的工具。 + +本章节,会持续更新,敬请关注… + +---------------------------- + +.. toctree:: + :maxdepth: 1 + :glob: + + ../c10/* From ea9d50e7fa47bc54b829466aadb5418750b25f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 22:46:05 +0800 Subject: [PATCH 005/147] =?UTF-8?q?add:=20=E5=A2=9E=E5=8A=A0=E4=B8=A4?= =?UTF-8?q?=E7=AF=87=20Go=20=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c09/c09_15.md | 232 +++++++++++++++++++++++++++++++++++++++++++ source/c09/c09_16.md | 96 ++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 source/c09/c09_15.md create mode 100644 source/c09/c09_16.md diff --git a/source/c09/c09_15.md b/source/c09/c09_15.md new file mode 100644 index 0000000..2cab642 --- /dev/null +++ b/source/c09/c09_15.md @@ -0,0 +1,232 @@ +# 9.15 面向对象编程:接口与多态 + +## 0. 接口是什么? + +> 这一段摘自 Go语言中文网 + +在面向对象的领域里,接口一般这样定义:**接口定义一个对象的行为**。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定。 + +在 Go 语言中,接口就是方法签名(Method Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。这与面向对象编程(OOP)的说法很类似。**接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法**。 + +## 1. 如何定义接口 + +使用 type 关键字来定义接口。 + +如下代码,定义了一个电话接口,接口要求必须实现 call 方法。 + +```go +type Phone interface { + call() +} +``` + +## 2. 如何实现接口 + +如果有一个类型/结构体,实现了一个接口要求的所有方法,这里 Phone 接口只有 call方法,所以只要实现了 call 方法,我们就可以称它实现了 Phone 接口。 + +意思是如果有一台机器,可以给别人打电话,那么我们就可以把它叫做电话。 + +这个接口的实现是隐式的,不像 JAVA 中要用 implements 显示说明。 + +继续上面电话的例子,我们先定义一个 Nokia 的结构体,而它实现了 call 的方法,所以它也是一台电话。 + +```go +type Nokia struct { + name string +} + +// 接收者为 Nokia +func (phone Nokia) call() { + fmt.Println("我是 Nokia,是一台电话") +} +``` + + + +## 3. 接口实现多态 + +鸭子类型(Duck typing)的定义是,只要你长得像鸭子,叫起来也像鸭子,那我认为你就是一只鸭子。 + +举个通俗的例子 + +**什么样子的人可以称做老师呢?** + +不同的人标准不一,有的人认为必须有一定的学历,有的人认为必须要有老师资格证。 + +而我认为只要能育人,能给传授给其他人知识的,都可以称之为老师。 + +而不管你教的什么学科?是体育竞技,还是教人烹饪。 + +也不管你怎么教?是在教室里手执教教鞭、拿着粉笔,还是追求真实,直接实战演练。 + +通通不管。 + +这就一个接口(老师)下,在不同对象(人)上的不同表现。这就是多态。 + + + +**在 Go 语言中,是通过接口来实现的多态。** + +这里以商品接口来写一段代码演示一下。 + +先定义一个商品(Good)的接口,意思是一个类型或者结构体,只要实现了`settleAccount()` 和 `orderInfo()` 两个方法,那这个类型/结构体就是一个商品。 + +```go +type Good interface { + settleAccount() int + orderInfo() string +} +``` + +然后我们定义两个结构体,分别是手机和赠品。 + +```go +type Phone struct { + name string + quantity int + price int +} + +type FreeGift struct { + name string + quantity int + price int +} +``` + +然后分别为他们实现 Good 接口的两个方法 + +```go +// Phone +func (phone Phone) settleAccount() int { + return phone.quantity * phone.price +} +func (phone Phone) orderInfo() string{ + return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + + phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" +} + +// FreeGift +func (gift FreeGift) settleAccount() int { + return 0 +} +func (gift FreeGift) orderInfo() string{ + return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + + gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" +} +``` + +实现了 Good 接口要求的两个方法后,手机和赠品在Go语言看来就都是商品(Good)类型了。 + +这里候,我挑选了两件商品(实例化),分别是手机和耳机(赠品,不要钱) + +```go +iPhone := Phone{ + name: "iPhone", + quantity: 1, + price: 8000, +} +earphones := FreeGift{ + name: "耳机", + quantity: 1, + price: 200, +} +``` + +然后创建一个购物车(也就是类型为 Good的切片),来存放这些商品。 + +```go +goods := []Good{iPhone, earphones} +``` + +最后,定义一个方法来计算购物车里的订单金额 + +```go +func calculateAllPrice(goods []Good) int { + var allPrice int + for _,good := range goods{ + fmt.Println(good.orderInfo()) + allPrice += good.settleAccount() + } + return allPrice +} +``` + +完整代码,我贴在下面,供你参考。 + +```go +package main + +import ( + "fmt" + "strconv" +) + +// 定义一个接口 +type Good interface { + settleAccount() int + orderInfo() string +} + +type Phone struct { + name string + quantity int + price int +} + +func (phone Phone) settleAccount() int { + return phone.quantity * phone.price +} +func (phone Phone) orderInfo() string{ + return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + + phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" +} + +type FreeGift struct { + name string + quantity int + price int +} + +func (gift FreeGift) settleAccount() int { + return 0 +} +func (gift FreeGift) orderInfo() string{ + return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + + gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" +} + +func calculateAllPrice(goods []Good) int { + var allPrice int + for _,good := range goods{ + fmt.Println(good.orderInfo()) + allPrice += good.settleAccount() + } + return allPrice +} +func main() { + iPhone := Phone{ + name: "iPhone", + quantity: 1, + price: 8000, + } + earphones := FreeGift{ + name: "耳机", + quantity: 1, + price: 200, + } + + goods := []Good{iPhone, earphones} + allPrice := calculateAllPrice(goods) + fmt.Printf("该订单总共需要支付 %d 元", allPrice) +} +``` + +运行后,输出如下 + +``` +您要购买1个iPhone计:8000元 +您要购买1个耳机计:0元 +该订单总共需要支付 8000 元 +``` + diff --git a/source/c09/c09_16.md b/source/c09/c09_16.md new file mode 100644 index 0000000..02ea177 --- /dev/null +++ b/source/c09/c09_16.md @@ -0,0 +1,96 @@ +# 9.16 关键字:make 和 new 的区别? + +## 1. new 函数 + +在官方文档中,new 函数的描述如下 + +```go +// The new built-in function allocates memory. The first argument is a type, +// not a value, and the value returned is a pointer to a newly +// allocated zero value of that type. +func new(Type) *Type +``` + +可以看到,new 只能传递一个参数,该参数为一个任意类型,可以是Go语言内建的类型,也可以是你自定义的类型 + +那么 new 函数到底做了哪些事呢: + +- 分配内存 +- 设置零值 +- 返回指针(重要) + + + +举个例子 + +```go +import "fmt" + +type Student struct { + name string + age int +} + +func main() { + // new 一个内建类型 + num := new(int) + fmt.Println(*num) //打印零值:0 + + // new 一个自定义类型 + s := new(Student) + s.name = "wangbm" +} +``` + + + +## 2. make 函数 + +在官方文档中,make 函数的描述如下 + +>//The make built-in function allocates and initializes an object +>//of type slice, map, or chan (only). Like new, the first argument is +>// a type, not a value. Unlike new, make's return type is the same as +>// the type of its argument, not a pointer to it. +> +>func make(t Type, size ...IntegerType) Type + +翻译一下注释内容 + +1. 内建函数 make 用来为 slice,map 或 chan 类型(注意:也只能用在这三种类型上)分配内存和初始化一个对象 +2. make 返回类型的本身而不是指针,而返回值也依赖于具体传入的类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了 + +注意,因为这三种类型是引用类型,所以必须得初始化(size和cap),但是不是置为零值,这个和new是不一样的。 + +举几个例子 + +```go +//切片 +a := make([]int, 2, 10) + +// 字典 +b := make(map[string]int) + +// 通道 +c := make(chan int, 10) +``` + + + +## 3. 总结 + +new:为所有的类型分配内存,并初始化为零值,返回指针。 + +make:只能为 slice,map,chan 分配内存,并初始化,返回的是类型。 + +另外,目前来看 new 函数并不常用,大家更喜欢使用短语句声明的方式。 + +```go +a := new(int) +a = 1 +// 等价于 +a := 1 +``` + +但是 make 就不一样了,它的地位无可替代,在使用slice、map以及channel的时候,还是要使用make进行初始化,然后才可以对他们进行操作。 + From 5c08e4e72b9d342344492d865d91a44b21ed2949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 22:48:12 +0800 Subject: [PATCH 006/147] =?UTF-8?q?delete:=20=E5=88=A0=E9=99=A4=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c09/c09_20.md | 151 ------------------------------------------- 1 file changed, 151 deletions(-) delete mode 100644 source/c09/c09_20.md diff --git a/source/c09/c09_20.md b/source/c09/c09_20.md deleted file mode 100644 index 6cb06aa..0000000 --- a/source/c09/c09_20.md +++ /dev/null @@ -1,151 +0,0 @@ -程序实体:变量、常量、函数、结构体和接口都是程序实体 - -标识符:程序实体的名字,也就是“变量名”,它可以使用字母字符、数字以及下划线“_”。不过,首字母不能是数字或下划线。还有很重要的一点是,它不能是Go语言的保留关键字。 - -![](http://image.python-online.cn/20200120201849.png) - -在Go语言中,程序实体的标识符,是有自己的讲究的,利用标识符来实现对程序实体的访问权限控制。名字首字母为大写的程序实体可以被任何代码包中的代码访问到。而名字首字母为小写的程序实体则只能被同一个代码包中的代码所访问。 - -这与 Python 中的 单下划线和双下划线有点类似。 - - - - - -$GOOS_$GOARCH: linux_amd64 - -$GOBIN 在两种情况下 - - - -源码文件是以 .go 为后缀的文件,一个源码包由一个或多个 .go 文件组成 - - - -代码包的作用: - -- 是编译和归档代码包的最基本单位 -- 代码划分、集结和依赖的有效组织形式,也是权限控制的辅助手段 - -代码包的规则: - -- 一个代码包实际上就是一个导入路径下的一个目录 -- 导入路径就是 <工作区目录>/src 或者 <工作区目录>/pkg/<平台相关目录>的某段子路径 - -代码包的声明: - -- 每个源码包都必须声明其所属的代码包 - -代码包导入: - -- 声明:是该代码包的导入路径的最右子路径,hypermind.cn/pkgtool 这个包,声明语句应为`package pkgtool` -- 代码包导入语句使用的包名称与其导入路径应一致,例如:fmt 导入语句应为 import "fmt" -- import str "strings" ,起别名 -- 本地化的导入,比如 import . "strings" ,后面使用 strings 的方法就可以不写strings,而直接写方法名,HasPrefix("abc", "a") -- 仅初始化,impor_ "strings",仅执行代码包的初始化函数,而实际上并没有导入代码包任何实体。 - -代码包的初始化: - -- 无参数声明,也没有结果声明的init函数。 -- init函数在一个代码包可以有多个,其执行顺序是不确定,但可以确定的是这些init函数都会执行,且只会执行一次 -- 不同代码包之间的init函数执行顺序,比旭 A 导入 B,B导入C,执行顺序是反着来的,C最先执行,然后是B,最后是A -- 同一代码包中的多个被导入的包,这些包的init执行函数也是不确定的 - -## 命令 - -**go run** - -用于运行命令源码文件,但只能 接受一个命令源码文件以及若干个库源码文件作为文件参数 - -其内部的操作是:先编译源码文件再运行。 - -![image-20200119204935546](C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200119204935546.png) - -![image-20200119205503805](C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200119205503805.png) - -![image-20200119205556277](C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200119205556277.png) - -![image-20200119205715715](C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200119205715715.png) - -**go build 和 go install** - -go build 可以编译命令源码文件和代码包 - -- 编译命令源码文件,会在当前目录下生成一个可执行文件 -- 编译代码包,后面直接跟这个包的导入路径即可 - -go install 用于编译并安装代码包或源码文件 - -![image-20200119210837479](C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200119210837479.png) - -- 如果不追加任何参数,会试图把当前目录作为代码包进行安装 -- 若以某代码包的导入路径作为参数,该代码包及其依赖会被安装 -- 若以命令源码文件及相关库源码文件作为参数,只有这些文件会被编译并安装 - -**go get** - -从远程代码仓库(git如github,gitlab或gogs,当然也支持其他的代码控制系统:Mercurial,svn、bazaar)上下载并安装代码包。 - -加 `-x` 参数,可以看到 go get 执行过程中做了哪些操作 - -- 下载到 GOPATH中包含的第一个工作区的 src 目录中 -- 安装到 GOPATH中包含的第一个工作区的pkg目录中 - --d: 只下载 - --fix:下载后先修复再编译安装 - --x:查看执行过程 - --u:下载最新的代码包,若对比当前已安装的包,没有更新是不是再安装的 - - - -《Go命令教程》 - - - - - - - - - - - -**字符串拼接**,直接使用 `+` 即可 - -```go -import "fmt" - -var str1 string = "Life " + "is " - -func main() { - str2 := str1 + "Short, " - str2 += "I Use Go" - fmt.Println(str2) -} - -// output: Life is Short, I Use Go -``` - -于在Go语言中,使用双引号书写字符串的方式是字符串常见表达方式之一,但是其有一个缺点,不能用来表示多行字符串,在 Python 中 可以使用 三个绰号来表示多行,而在 Go 比较特殊,它使用 反引号 - -```go -var slogan string = ` -Life is Short -I Use Go. -` - -func main() { - fmt.Println(slogan) -} -``` - -输出如下 - -```go -Life is Short -I Use Go. -``` - From f972e82126ad096fb2a5d421bdb740b0390b4bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 23:04:54 +0800 Subject: [PATCH 007/147] =?UTF-8?q?update:=20=E7=94=9F=E6=88=90rst?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_01.rst | 2 +- source/c01/c01_04.rst | 2 +- source/c01/c01_05.rst | 1 + source/c01/c01_06.rst | 1 + source/c01/c01_07.rst | 1 + source/c01/c01_08.rst | 2 +- source/c01/c01_09.rst | 1 + source/c01/c01_10.rst | 2 +- source/c01/c01_11.rst | 1 + source/c01/c01_12.rst | 2 +- source/c01/c01_13.rst | 2 +- source/c01/c01_14.rst | 2 +- source/c01/c01_15.rst | 1 + source/c01/c01_16.rst | 1 + source/c01/c01_17.rst | 2 +- source/c01/c01_18.rst | 1 - source/c01/c01_20.rst | 2 +- source/c01/c01_21.rst | 1 + source/c01/c01_22.rst | 2 +- source/c01/c01_23.rst | 1 + source/c01/c01_24.rst | 2 +- source/c01/c01_25.rst | 1 + source/c01/c01_26.rst | 1 + source/c01/c01_27.rst | 2 +- source/c01/c01_30.rst | 1 - source/c01/c01_31.rst | 22 ++- source/c02/c02_01.rst | 2 +- source/c02/c02_02.rst | 1 + source/c02/c02_03.rst | 1 + source/c02/c02_04.rst | 1 + source/c02/c02_05.rst | 1 + source/c02/c02_06.rst | 1 + source/c02/c02_07.rst | 2 +- source/c02/c02_08.rst | 3 +- source/c02/c02_09.rst | 2 +- source/c02/c02_10.rst | 1 + source/c02/c02_11.rst | 2 +- source/c02/c02_12.rst | 2 + source/c03/c03_01.rst | 2 +- source/c03/c03_02.rst | 1 + source/c03/c03_03.rst | 2 +- source/c03/c03_04.rst | 4 +- source/c03/c03_05.rst | 2 +- source/c03/c03_06.rst | 2 +- source/c04/c04_01.rst | 31 ++-- source/c04/c04_02.rst | 2 +- source/c04/c04_03.rst | 4 +- source/c04/c04_04.rst | 194 ++++++++++--------------- source/c04/c04_05.rst | 2 +- source/c04/c04_06.rst | 2 +- source/c04/c04_07.rst | 2 +- source/c04/c04_08.rst | 2 + source/c04/c04_09.rst | 2 +- source/c04/c04_10.rst | 4 +- source/c04/c04_11.rst | 2 +- source/c04/c04_12.rst | 98 +++++-------- source/c04/c04_13.rst | 1 + source/c04/c04_14.rst | 2 +- source/c04/c04_15.rst | 4 +- source/c04/c04_16.rst | 1 + source/c04/c04_17.rst | 2 +- source/c04/c04_18.rst | 2 +- source/c04/c04_19.rst | 1 + source/c04/c04_21.rst | 29 +++- source/c04/c04_22.rst | 1 - source/c05/c05_01.rst | 2 +- source/c05/c05_02.rst | 1 + source/c05/c05_03.rst | 2 +- source/c06/c06_01.rst | 2 +- source/c06/c06_02.rst | 2 +- source/c06/c06_03.rst | 2 +- source/c06/c06_04.rst | 2 +- source/c06/c06_05.rst | 2 +- source/c06/c06_06.rst | 2 +- source/c07/c07_01.rst | 15 +- source/c07/c07_02.rst | 4 +- source/c07/c07_03.rst | 52 +++++-- source/c07/c07_04.rst | 2 +- source/c07/c07_05.rst | 2 +- source/c07/c07_06.rst | 2 +- source/c07/c07_07.rst | 1 + source/c07/c07_08.rst | 1 + source/c07/c07_09.rst | 142 ++++++++++++++++++ source/c07/c07_10.rst | 2 +- source/c07/c07_11.rst | 1 - source/c07/c07_12.rst | 20 ++- source/c07/c07_15.rst | 1 - source/c07/c07_16.rst | 1 - source/c08/c08_01.rst | 17 ++- source/c08/c08_02.rst | 4 +- source/c08/c08_03.rst | 2 +- source/c08/c08_04.rst | 2 +- source/c08/c08_05.rst | 2 +- source/c08/c08_06.rst | 2 +- source/c08/c08_07.rst | 2 +- source/c08/c08_08.rst | 13 +- source/c08/c08_09.rst | 2 +- source/c08/c08_10.rst | 1 - source/c08/c08_11.rst | 2 +- source/c08/c08_12.rst | 2 +- source/c08/c08_13.rst | 2 +- source/c08/c08_14.rst | 2 +- source/c08/c08_15.rst | 1 - source/c09/c09_01.rst | 188 +++++++++++++++++------- source/c09/c09_02.rst | 5 +- source/c09/c09_03.rst | 270 +++++++++++++++++++++------------- source/c09/c09_04.rst | 298 +++++++++++++++++++++++--------------- source/c09/c09_05.rst | 330 +++++++++++++++++++----------------------- 108 files changed, 1149 insertions(+), 740 deletions(-) diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 46e9e6f..fa95a2c 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -317,7 +317,7 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165542.png .. |image1| image:: http://image.python-online.cn/20190511165551.png - diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index 88358aa..dbce83e 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -118,6 +118,6 @@ .. figure:: http://image.python-online.cn/20191117142849.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190404215330.png - diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index 4ea0c74..eb5ba75 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -180,3 +180,4 @@ locals() .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index 1b19712..5e9ddf0 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -107,3 +107,4 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index 84e8c34..1e05763 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -351,3 +351,4 @@ Pythonic .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 4b9c6d7..216a8b2 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -272,6 +272,7 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg .. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg @@ -279,4 +280,3 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 .. |image3| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg .. |image4| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg .. |image5| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg - diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index 8af376a..a1ebebe 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -76,3 +76,4 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 7010189..4eff488 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1307,9 +1307,9 @@ Tips `__ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165650.png .. |image1| image:: http://image.python-online.cn/20190511165716.png .. |image2| image:: http://image.python-online.cn/20190511165735.png .. |image3| image:: http://image.python-online.cn/20190915213412.png - diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index ea0b985..8985569 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -347,3 +347,4 @@ start(),end(),end() .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index 77ac879..025c3d3 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -157,7 +157,7 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/02/598168fe2b016.png .. |image1| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png - diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index dd30fc1..0f4eae0 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -160,6 +160,6 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg - diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index 3f807a9..d33d425 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -212,6 +212,6 @@ open)的上下文管理器。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190310172800.png - diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index 06608ea..b2c3812 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -81,3 +81,4 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst index 2d5f893..c23d993 100644 --- a/source/c01/c01_16.rst +++ b/source/c01/c01_16.rst @@ -169,3 +169,4 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index 26d6e71..ef1d764 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -580,9 +580,9 @@ super 的实现原理,就交由你来自己完成。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190425221322.png .. |image1| image:: http://image.python-online.cn/20190425221322.png .. |image2| image:: http://image.python-online.cn/20190425221233.png .. |image3| image:: http://image.python-online.cn/20190519001930.png - diff --git a/source/c01/c01_18.rst b/source/c01/c01_18.rst index 0ba5e6b..a96250f 100644 --- a/source/c01/c01_18.rst +++ b/source/c01/c01_18.rst @@ -161,4 +161,3 @@ mysql,这时也请将其卸载再重新安装吧。 .. |image4| image:: http://image.python-online.cn/20190615001908.png .. |image5| image:: http://image.python-online.cn/20190615112422.png .. |image6| image:: http://image.python-online.cn/20190705225651.png - diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index f2fc0c4..2e15b18 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -108,9 +108,9 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190630111243.png .. |image1| image:: http://image.python-online.cn/20190630104956.png .. |image2| image:: http://image.python-online.cn/20190630105735.png .. |image3| image:: http://image.python-online.cn/20190630105600.png - diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index add2b32..f9b7698 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -92,3 +92,4 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index 54b3838..b7219d3 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -200,6 +200,6 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190831160317.png - diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index 9686174..c8ae423 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -121,3 +121,4 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index 27ae8e6..677e639 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -796,6 +796,6 @@ sys.path)查找器 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191027192949.png - diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index c96373d..f95f27f 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -108,3 +108,4 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index 2551678..c782da4 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -285,3 +285,4 @@ getchar() & putchar() .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 74519f4..8c9f704 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -686,9 +686,9 @@ Index)上,它是 Python .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191218202833.png .. |image1| image:: http://image.python-online.cn/20191218203005.png .. |image2| image:: http://image.python-online.cn/20191218203255.png .. |image3| image:: http://image.python-online.cn/20191218203517.png - diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index 790cd1a..e93a544 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -6,4 +6,3 @@ |image0| .. |image0| image:: http://image.python-online.cn/20200104144109.png - diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index 0b98804..2328dec 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -1,9 +1,21 @@ -1.31 学习编程的几大网站 -======================= +1.31 学习 Pillow 笔记 +===================== -`书栈网 `__ +1. 安装 pillow +-------------- -|image0| +:: -.. |image0| image:: http://image.python-online.cn/20200104144109.png + pip install pillow +2. 使用 +------- + +ARGB +是一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。 + +RGB +色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 + +3. +-- diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index db907da..3866bfc 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -251,10 +251,10 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png .. |image1| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg .. |image2| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg .. |image3| image:: http://image.python-online.cn/20190112205155.png .. |image4| image:: http://image.python-online.cn/20190112204930.png - diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index 7de913b..47124aa 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -147,3 +147,4 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index c72a77e..b1ce3a8 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -357,3 +357,4 @@ CPython,所以也就默许了Python具有GIL锁这个事。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index a0fec60..c93d0df 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -290,3 +290,4 @@ Condition和Event 是类似的,并没有多大区别。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 6d2d6f7..070e3ad 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -262,3 +262,4 @@ Out),就是先进入队列的消息,将优先被消费。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 301eb18..03fa42a 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -112,3 +112,4 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index 03da235..9e40938 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -393,7 +393,7 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190527123516.png .. |image1| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png - diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index 75c217c..03a414c 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -3,7 +3,7 @@ -------------- -直到上一篇,我们终于迎来了Python并发编程中,最高级、最重要、当然也是最难的知识点–\ ``协程``\ 。 +直到上一篇,我们终于迎来了Python并发编程中,最高级、最重要、当然也是最难的知识点–``协程``\ 。 当你看到这一篇的时候,请确保你对生成器的知识,有一定的了解。当然不了解,也没有关系,你只要花个几分钟的时间,来看下我上一篇文章,就能够让你认识生成器,入门协程了。 @@ -362,3 +362,4 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 6743988..2a6a770 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -247,8 +247,8 @@ emmm,和上面的结果是一样的。nice .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |普通函数中 不能使用 await| image:: https://i.loli.net/2018/05/26/5b09794f45340.png .. |async 中 不能使用yield| image:: https://i.loli.net/2018/05/26/5b0978b646230.png .. |验证通过| image:: https://i.loli.net/2018/05/26/5b09814dc4714.png - diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index 2e62621..a19e62f 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -510,3 +510,4 @@ asyncio.gather .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 87ff018..0d21b1f 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -219,9 +219,9 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/06/03/5b13ba8525bcf.png .. |image1| image:: https://i.loli.net/2018/06/03/5b13ba9f66baa.png .. |image2| image:: https://i.loli.net/2018/06/03/5b13bab682a32.png .. |image3| image:: https://i.loli.net/2018/06/03/5b13bad79f5ce.png - diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index 7d50871..8e60122 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -90,6 +90,7 @@ yield **协程的优点:** - 线程属于系统级别调度,而协程是程序员级别的调度。使用协程避免了无意义的调度,减少了线程上下文切换的开销,由此可以提高性能。 + - 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。 - 无需原子操作锁定及同步的开销 @@ -110,3 +111,4 @@ yield .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 3a025a1..3937210 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -776,7 +776,7 @@ property .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190811100737.png .. |image1| image:: http://image.python-online.cn/20190512113917.png - diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index 01ab177..687fe14 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -377,3 +377,4 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index 6d073ae..08282a4 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -295,7 +295,7 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png .. |image1| image:: https://i.loli.net/2018/04/30/5ae6c31b2d1c8.png - diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index 6ce07d3..46242de 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -53,7 +53,7 @@ pip install virtualenv pip install virtualenvwrapper -配置:\ ``vim ~/.bashrc``\ ,编辑完成后,记得\ ``source ~/.bashrc``\ 使之生效 +配置:``vim ~/.bashrc``\ ,编辑完成后,记得\ ``source ~/.bashrc``\ 使之生效 :: @@ -513,6 +513,7 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/20/599982f513b7e.png .. |image1| image:: https://i.loli.net/2017/08/20/59998b33265d9.png @@ -520,4 +521,3 @@ .. |image3| image:: https://i.loli.net/2017/08/20/59998bdf36c79.png .. |image4| image:: https://i.loli.net/2017/08/25/59a0236def497.png .. |image5| image:: https://i.loli.net/2017/08/25/59a0244d239f0.png - diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index fac243a..05e7397 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -501,8 +501,8 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX .. |image1| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup .. |image2| image:: http://image.python-online.cn/FimULzWaeZWS2KJx_EQLAK_yRZ4A - diff --git a/source/c03/c03_06.rst b/source/c03/c03_06.rst index 8f8e32a..518f7b2 100644 --- a/source/c03/c03_06.rst +++ b/source/c03/c03_06.rst @@ -885,6 +885,7 @@ response 进行初步封装。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190607131728.png .. |image1| image:: http://image.python-online.cn/20190607191954.png @@ -909,4 +910,3 @@ response 进行初步封装。 .. |image20| image:: http://image.python-online.cn/20190602220511.png .. |image21| image:: http://image.python-online.cn/20190602220700.png .. |image22| image:: http://image.python-online.cn/20190605203016.png - diff --git a/source/c04/c04_01.rst b/source/c04/c04_01.rst index 0750fac..0735a22 100755 --- a/source/c04/c04_01.rst +++ b/source/c04/c04_01.rst @@ -50,7 +50,7 @@ virtualenvwrapper,就必须先安装 virtualenv。 .. code:: bash - # 准备目录并进行 + # 准备目录并进入 $ mkdir -p /home/wangbm/Envs $ cd !$ @@ -129,6 +129,12 @@ virtualenv 虽然已经相当好用了,可是功能还是不够完善。 find / -name virtualenvwrapper.sh # /usr/bin/virtualenvwrapper.sh +若是 windows 则使用everything 查找 virtualenvwrapper.bat 脚本 + +:: + + D:\Program Files (x86)\Python38-32\Scripts\virtualenvwrapper.bat + 在~/.bashrc 文件新增配置 :: @@ -138,6 +144,10 @@ virtualenv 虽然已经相当好用了,可是功能还是不够完善。 export VIRTUALENVWRAPPER_SCRIPT=/usr/bin/virtualenvwrapper.sh source /usr/bin/virtualenvwrapper.sh +若是 windows 则新增环境变量:\ ``WORKON_HOME`` + +|image0| + **基本语法**\ : mkvirtualenv [-a project_path] [-i package] [-r requirements_file] @@ -211,7 +221,7 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html 先对比下,全局环境和虚拟环境的区别,全局环境中有requests包,而虚拟环境中并未安装。 当我们敲入 ``workon my_env01``\ ,前面有\ ``my_env01``\ 的标识,说明我们已经处在虚拟环境中。后面所有的操作,都将在虚拟环境下执行。 -|image0| +|image1| 4.2 工程项目中 ~~~~~~~~~~~~~~ @@ -244,23 +254,24 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html $ ./ming.py -发现和预期一样,真的报错了。说明我们指定的虚拟环境有效果。 |image1| +发现和预期一样,真的报错了。说明我们指定的虚拟环境有效果。 |image2| 4.3 PyCharm中 ~~~~~~~~~~~~~ -点击 File - Settings - Project - Interpreter |image2| +点击 File - Settings - Project - Interpreter |image3| 点击小齿轮。如图点击添加,按提示添加一个虚拟环境。然后点 OK -就可以使用这个虚拟环境,之后的项目都会在这个虚拟环境下运行。 |image3| +就可以使用这个虚拟环境,之后的项目都会在这个虚拟环境下运行。 |image4| -------------- .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2018/06/11/5b1e7d36ce8ad.png -.. |image1| image:: https://i.loli.net/2018/06/11/5b1e7f140be6a.png -.. |image2| image:: https://i.loli.net/2018/06/11/5b1e805c996c8.png -.. |image3| image:: https://i.loli.net/2018/06/11/5b1e812db603f.png - +.. |image0| image:: http://image.python-online.cn/20200209161935.png +.. |image1| image:: https://i.loli.net/2018/06/11/5b1e7d36ce8ad.png +.. |image2| image:: https://i.loli.net/2018/06/11/5b1e7f140be6a.png +.. |image3| image:: https://i.loli.net/2018/06/11/5b1e805c996c8.png +.. |image4| image:: https://i.loli.net/2018/06/11/5b1e812db603f.png diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index 1ecac5b..ee78c1d 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -169,10 +169,10 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511162815.png .. |image1| image:: http://image.python-online.cn/20190511162524.png .. |image2| image:: http://image.python-online.cn/20190511162607.png .. |image3| image:: http://image.python-online.cn/20190511162716.png .. |image4| image:: http://image.python-online.cn/20190511162730.png - diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index d1698e2..181be30 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -26,7 +26,7 @@ 4.3.1 成品展示 -------------- -以我的博客(\ ``python-online.cn``)为例,先给大家展示一下。 +以我的博客(``python-online.cn``)为例,先给大家展示一下。 这是首页。显示了你所有的文章索引。 |image0| @@ -481,6 +481,7 @@ Python 3.x ,所以这里的代码也要对应修改。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511160523.png .. |image1| image:: http://image.python-online.cn/20190511161056.png @@ -500,4 +501,3 @@ Python 3.x ,所以这里的代码也要对应修改。 .. |image15| image:: http://image.python-online.cn/20191016205653.png .. |image16| image:: http://image.python-online.cn/20191015225652.png .. |image17| image:: http://image.python-online.cn/20191016211012.png - diff --git a/source/c04/c04_04.rst b/source/c04/c04_04.rst index dc94574..ad8b027 100755 --- a/source/c04/c04_04.rst +++ b/source/c04/c04_04.rst @@ -70,55 +70,43 @@ NoteBook,qtconsole,VS Ccode等。 |image1| 运行代码 ~~~~~~~~ -+-------------+----------------+----------------+ -| 快捷键 | 作用 | 说明 | -+=============+================+================+ -| Enter | 输入框换行 | | -+-------------+----------------+----------------+ -| Shift-Enter | 运行本单元代码 | 并跳转至下单元 | -+-------------+----------------+----------------+ -| Shift-Enter | 运行本单元代码 | 停留在本单元 | -+-------------+----------------+----------------+ -| Alt-Enter | 运行本单元代码 | 其下插入新单元 | -+-------------+----------------+----------------+ +=========== ============== ============== +快捷键 作用 说明 +=========== ============== ============== +Enter 输入框换行 +Shift-Enter 运行本单元代码 并跳转至下单元 +Shift-Enter 运行本单元代码 停留在本单元 +Alt-Enter 运行本单元代码 其下插入新单元 +=========== ============== ============== 切换模式 ~~~~~~~~ 在不同的模式下,你细心点会发现单元格的颜色也是变化的。以下字母,不区分大小写。 -+--------+--------------------------+------+ -| 快捷键 | 作用 | 说明 | -+========+==========================+======+ -| Enter | 进入编辑模式 | | -+--------+--------------------------+------+ -| Esc | 退出编辑状态 | | -+--------+--------------------------+------+ -| Y | 单元格转入代码状态 | | -+--------+--------------------------+------+ -| R | 单元格进入 Raw 状态 | | -+--------+--------------------------+------+ -| M | 单元格进入 Markdown 状态 | | -+--------+--------------------------+------+ +====== ======================== ==== +快捷键 作用 说明 +====== ======================== ==== +Enter 进入编辑模式 +Esc 退出编辑状态 +Y 单元格转入代码状态 +R 单元格进入 Raw 状态 +M 单元格进入 Markdown 状态 +====== ======================== ==== Markdown下快捷键 ~~~~~~~~~~~~~~~~ -+--------+---------------+------+ -| 快捷键 | 作用 | 说明 | -+========+===============+======+ -| 1 | 设定 1 级标题 | | -+--------+---------------+------+ -| 2 | 设定 2 级标题 | | -+--------+---------------+------+ -| 3 | 设定 3 级标题 | | -+--------+---------------+------+ -| 4 | 设定 4 级标题 | | -+--------+---------------+------+ -| 5 | 设定 5 级标题 | | -+--------+---------------+------+ -| 6 | 设定 6 级标题 | | -+--------+---------------+------+ +====== ============= ==== +快捷键 作用 说明 +====== ============= ==== +1 设定 1 级标题 +2 设定 2 级标题 +3 设定 3 级标题 +4 设定 4 级标题 +5 设定 5 级标题 +6 设定 6 级标题 +====== ============= ==== 操作单元格 ~~~~~~~~~~ @@ -130,93 +118,61 @@ Markdown下快捷键 其他的,还有一些和我们日常操作很像。 -+---------+------------------------+------+ -| 快捷键 | 作用 | 说明 | -+=========+========================+======+ -| c | 复制选中的单元 | | -+---------+------------------------+------+ -| x | 剪切选中的单元 | | -+---------+------------------------+------+ -| shift-v | 粘贴到上方单元 | | -+---------+------------------------+------+ -| v | 粘贴到下方单元 | | -+---------+------------------------+------+ -| z | 恢复删除的最后一个单元 | | -+---------+------------------------+------+ -| shift-m | 合并选中的单元 | | -+---------+------------------------+------+ -| a | 在上方插入新单元 | | -+---------+------------------------+------+ -| b | 在下方插入新单元 | | -+---------+------------------------+------+ -| shitf-k | 向上连续选中单元格 | | -+---------+------------------------+------+ -| shitf-j | 向下连续选中单元格 | | -+---------+------------------------+------+ +======= ====================== ==== +快捷键 作用 说明 +======= ====================== ==== +c 复制选中的单元 +x 剪切选中的单元 +shift-v 粘贴到上方单元 +v 粘贴到下方单元 +z 恢复删除的最后一个单元 +shift-m 合并选中的单元 +a 在上方插入新单元 +b 在下方插入新单元 +shitf-k 向上连续选中单元格 +shitf-j 向下连续选中单元格 +======= ====================== ==== 编辑模式下快捷键 ~~~~~~~~~~~~~~~~ -+----------------+-------------------+----------------+ -| 快捷键 | 作用 | 说明 | -+================+===================+================+ -| Tab | 代码补全或缩进 | | -+----------------+-------------------+----------------+ -| Shift-Tab | 提示 | | -+----------------+-------------------+----------------+ -| Ctrl-] | 缩进 | | -+----------------+-------------------+----------------+ -| Ctrl-[ | 解除缩进 | | -+----------------+-------------------+----------------+ -| Ctrl-D | 删除整行 | | -+----------------+-------------------+----------------+ -| Ctrl-Z | 撤消 | | -+----------------+-------------------+----------------+ -| Ctrl-Y | 取消撤消 | | -+----------------+-------------------+----------------+ -| Ctrl-Shift-Z | 取消撤消 | | -+----------------+-------------------+----------------+ -| Ctrl-A | 全选 | | -+----------------+-------------------+----------------+ -| Ctrl-U | 取消选择 | | -+----------------+-------------------+----------------+ -| Alt-U | 恢复选择 | | -+----------------+-------------------+----------------+ -| Ctrl-Home | 跳到单元开头 | | -+----------------+-------------------+----------------+ -| Ctrl-Up | 跳到单元开头 | | -+----------------+-------------------+----------------+ -| Ctrl-End | 跳到单元末尾 | | -+----------------+-------------------+----------------+ -| Ctrl-Down | 跳到单元末尾 | | -+----------------+-------------------+----------------+ -| Ctrl-Left | 跳到左边一个字首 | | -+----------------+-------------------+----------------+ -| Ctrl-Right | 跳到右边一个字首 | | -+----------------+-------------------+----------------+ -| Ctrl-Backspace | 删除前面一个字 | | -+----------------+-------------------+----------------+ -| Ctrl-Delete | 删除后面一个字 | | -+----------------+-------------------+----------------+ -| Ctrl-/ | 注释整行/撤销注释 | 仅代码状态有效 | -+----------------+-------------------+----------------+ -| Shift | 忽略 | | -+----------------+-------------------+----------------+ -| Ctrl-S | 保存当前 NoteBook | | -+----------------+-------------------+----------------+ +============== ================= ============== +快捷键 作用 说明 +============== ================= ============== +Tab 代码补全或缩进 +Shift-Tab 提示 +Ctrl-] 缩进 +Ctrl-[ 解除缩进 +Ctrl-D 删除整行 +Ctrl-Z 撤消 +Ctrl-Y 取消撤消 +Ctrl-Shift-Z 取消撤消 +Ctrl-A 全选 +Ctrl-U 取消选择 +Alt-U 恢复选择 +Ctrl-Home 跳到单元开头 +Ctrl-Up 跳到单元开头 +Ctrl-End 跳到单元末尾 +Ctrl-Down 跳到单元末尾 +Ctrl-Left 跳到左边一个字首 +Ctrl-Right 跳到右边一个字首 +Ctrl-Backspace 删除前面一个字 +Ctrl-Delete 删除后面一个字 +Ctrl-/ 注释整行/撤销注释 仅代码状态有效 +Shift 忽略 +Ctrl-S 保存当前 NoteBook +============== ================= ============== 其他快捷键 ~~~~~~~~~~ -+--------------+----------------+------+ -| 快捷键 | 作用 | 说明 | -+==============+================+======+ -| f | 搜索并替换 | | -+--------------+----------------+------+ -| l(L的小写) | 形状代码行号 | | -+--------------+----------------+------+ -| h | 显示快捷键帮助 | | -+--------------+----------------+------+ +============ ============== ==== +快捷键 作用 说明 +============ ============== ==== +f 搜索并替换 +l(L的小写) 形状代码行号 +h 显示快捷键帮助 +============ ============== ==== 其实以上快捷键,在非编辑模式下,按 h 就会出现快捷键帮助菜单。 @@ -240,6 +196,7 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511163102.png .. |image1| image:: http://image.python-online.cn/20190511163123.png @@ -250,4 +207,3 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 .. |image6| image:: http://image.python-online.cn/20190511163253.png .. |image7| image:: http://image.python-online.cn/20190511163304.png .. |image8| image:: http://image.python-online.cn/20190511163311.png - diff --git a/source/c04/c04_05.rst b/source/c04/c04_05.rst index 6188ada..087ad7d 100755 --- a/source/c04/c04_05.rst +++ b/source/c04/c04_05.rst @@ -160,6 +160,7 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511163441.png .. |image1| image:: http://image.python-online.cn/20190511163457.png @@ -178,4 +179,3 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 .. |image14| image:: http://image.python-online.cn/20190511163750.png .. |image15| image:: http://image.python-online.cn/20190511163757.png .. |image16| image:: http://image.python-online.cn/20190511163805.png - diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 4a1d768..0b7cd4b 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -540,6 +540,7 @@ Desktop真心觉得不好用)。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191217150942.png .. |image1| image:: http://image.python-online.cn/20191231165152.png @@ -548,4 +549,3 @@ Desktop真心觉得不好用)。 .. |image4| image:: http://image.python-online.cn/20190511163855.png .. |image5| image:: https://i.loli.net/2018/04/15/5ad2c2a9813b9.png .. |image6| image:: http://image.python-online.cn/20190430235625.png - diff --git a/source/c04/c04_07.rst b/source/c04/c04_07.rst index a30c8da..fdc7942 100755 --- a/source/c04/c04_07.rst +++ b/source/c04/c04_07.rst @@ -824,6 +824,7 @@ bash窗口,不然会提示找不到npm命令) .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-9-9/50205036.jpg .. |image1| image:: http://image.python-online.cn/17-9-9/5214564.jpg @@ -832,4 +833,3 @@ bash窗口,不然会提示找不到npm命令) .. |image4| image:: http://image.python-online.cn/17-9-9/63041495.jpg .. |image5| image:: https://i.loli.net/2018/04/15/5ad3018e18cc7.png .. |image6| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png - diff --git a/source/c04/c04_08.rst b/source/c04/c04_08.rst index f3b7183..4b3406b 100755 --- a/source/c04/c04_08.rst +++ b/source/c04/c04_08.rst @@ -80,6 +80,7 @@ - https://zhuanlan.zhihu.com/p/36147819 - https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf + - https://www.jetbrains.com/help/pycharm/ctrl-alt.html 4.8.2 Sublime Text 3 @@ -431,3 +432,4 @@ URL相关 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c04/c04_09.rst b/source/c04/c04_09.rst index 0d5511c..cf7ecee 100755 --- a/source/c04/c04_09.rst +++ b/source/c04/c04_09.rst @@ -713,6 +713,7 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/25/599feabef31a6.png .. |image1| image:: https://i.loli.net/2017/08/25/59a0345577dc0.png @@ -750,4 +751,3 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 .. |image33| image:: https://i.loli.net/2017/08/27/59a261734e896.png .. |image34| image:: https://i.loli.net/2017/08/27/59a261734ff8b.png .. |image35| image:: https://i.loli.net/2017/08/25/599fc9aa85094.png - diff --git a/source/c04/c04_10.rst b/source/c04/c04_10.rst index ca03f02..07d2623 100755 --- a/source/c04/c04_10.rst +++ b/source/c04/c04_10.rst @@ -298,7 +298,7 @@ limit子句:限制返回数据的量。 ~~~~~~~~~~~~ ``自然连接: natural join`` ``自然连接``, 就是\ ``自动匹配``\ 连接条件: -系统以字段名字作为匹配模式(\ **同名字段就作为条件, +系统以字段名字作为匹配模式(**同名字段就作为条件, 多个同名字段都作为条件**). 自然连接: 可以分为自然内连接和自然外连接. @@ -539,6 +539,7 @@ limit子句:限制返回数据的量。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://ooo.0o0.ooo/2017/08/26/59a1429722061.png .. |image1| image:: https://i.loli.net/2017/08/27/59a2307e7b6bc.png @@ -564,4 +565,3 @@ limit子句:限制返回数据的量。 .. |image21| image:: https://i.loli.net/2017/08/27/59a2756c66952.png .. |image22| image:: https://i.loli.net/2017/08/27/59a27651cd78e.png .. |image23| image:: https://i.loli.net/2017/08/27/59a2784f6562d.png - diff --git a/source/c04/c04_11.rst b/source/c04/c04_11.rst index 6a8d921..bb83bd9 100644 --- a/source/c04/c04_11.rst +++ b/source/c04/c04_11.rst @@ -176,6 +176,7 @@ Host .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190113104817.png .. |image1| image:: http://image.python-online.cn/20190113105512.png @@ -194,4 +195,3 @@ Host .. |image14| image:: http://image.python-online.cn/20190113113211.png .. |image15| image:: http://image.python-online.cn/20190113112456.png .. |image16| image:: http://image.python-online.cn/20190113112649.png - diff --git a/source/c04/c04_12.rst b/source/c04/c04_12.rst index 3c78f48..dfc4132 100644 --- a/source/c04/c04_12.rst +++ b/source/c04/c04_12.rst @@ -87,71 +87,49 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 **最常用** -+------+-----------+--------------------------+ -| 指令 | 英文 | 解释 | -+======+===========+==========================+ -| n | Next | 下一步 | -+------+-----------+--------------------------+ -| l | list | 列出当前断点处源码 | -+------+-----------+--------------------------+ -| p | print | 打印变量 | -+------+-----------+--------------------------+ -| s | step into | 执行当前行,可以进入函数 | -+------+-----------+--------------------------+ -| r | return | 运行完当前函数,返回结果 | -+------+-----------+--------------------------+ -| c | continue | 执行到下一断点或者结束 | -+------+-----------+--------------------------+ -| b | break | 设置断点 | -+------+-----------+--------------------------+ -| q | quit | 退出程序 | -+------+-----------+--------------------------+ +==== ========= ======================== +指令 英文 解释 +==== ========= ======================== +n Next 下一步 +l list 列出当前断点处源码 +p print 打印变量 +s step into 执行当前行,可以进入函数 +r return 运行完当前函数,返回结果 +c continue 执行到下一断点或者结束 +b break 设置断点 +q quit 退出程序 +==== ========= ======================== **有时使用** -+-------------+----------+------------------------------+ -| 指令 | 英文 | 解释 | -+=============+==========+==============================+ -| a | args | 列出当前函数的参数 | -+-------------+----------+------------------------------+ -| pp | pprint | 一种可视化更好的打印 | -+-------------+----------+------------------------------+ -| j | jump | 跳到指定行 | -+-------------+----------+------------------------------+ -| cl | clear | 清除断点 | -+-------------+----------+------------------------------+ -| w | where | 打印当前堆栈 | -+-------------+----------+------------------------------+ -| u | up | 执行跳转到当前堆栈的上一层 | -+-------------+----------+------------------------------+ -| unt | until | 行数递增执行(忽略循环和函数) | -+-------------+----------+------------------------------+ -| ll | longlist | 列出更多的源码 | -+-------------+----------+------------------------------+ -| run/restart | run | 重新启动 debug(-m pdb) | -+-------------+----------+------------------------------+ +=========== ======== ============================ +指令 英文 解释 +=========== ======== ============================ +a args 列出当前函数的参数 +pp pprint 一种可视化更好的打印 +j jump 跳到指定行 +cl clear 清除断点 +w where 打印当前堆栈 +u up 执行跳转到当前堆栈的上一层 +unt until 行数递增执行(忽略循环和函数) +ll longlist 列出更多的源码 +run/restart run 重新启动 debug(-m pdb) +=========== ======== ============================ **几乎不用** -+---------+-----------------+--------------------+ -| 指令 | 英文 | 解释 | -+=========+=================+====================+ -| tbreak | temporary break | 临时断点 | -+---------+-----------------+--------------------+ -| disable | | 停用断点 | -+---------+-----------------+--------------------+ -| enable | | 启用断点 | -+---------+-----------------+--------------------+ -| alias | | 设置别名 | -+---------+-----------------+--------------------+ -| unalias | | 删除别名 | -+---------+-----------------+--------------------+ -| whatis | | 打印对象类型 | -+---------+-----------------+--------------------+ -| ignore | | 设置忽略的断点 | -+---------+-----------------+--------------------+ -| source | | 列出给定对象的源码 | -+---------+-----------------+--------------------+ +======= =============== ================== +指令 英文 解释 +======= =============== ================== +tbreak temporary break 临时断点 +disable 停用断点 +enable 启用断点 +alias 设置别名 +unalias 删除别名 +whatis 打印对象类型 +ignore 设置忽略的断点 +source 列出给定对象的源码 +======= =============== ================== 其上全部是我翻译自官方文档,原文在这里:https://docs.python.org/3/library/pdb.html @@ -177,10 +155,10 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190118000111.png .. |image1| image:: http://image.python-online.cn/20190118000234.png .. |image2| image:: http://image.python-online.cn/20190118000557.png .. |image3| image:: http://image.python-online.cn/20190118083809.png .. |image4| image:: http://image.python-online.cn/20190118005507.png - diff --git a/source/c04/c04_13.rst b/source/c04/c04_13.rst index 96b8d55..7e362a4 100644 --- a/source/c04/c04_13.rst +++ b/source/c04/c04_13.rst @@ -402,3 +402,4 @@ arguments)。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 2b2b01f..6f8c636 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -171,6 +171,7 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn .. |image1| image:: http://image.python-online.cn/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX @@ -180,4 +181,3 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 .. |image5| image:: http://image.python-online.cn/20190612211925.png .. |image6| image:: http://image.python-online.cn/20190614000336.png .. |image7| image:: http://image.python-online.cn/20190612215924.png - diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index d1c7048..385cee9 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -460,7 +460,7 @@ PyCharm 版本有效,可以实现永久激活(到 2099 / **第二步**\ : 若是 Windows 系统,请找到并进入你的 PyCharm -安装启动目录(以我的为例):E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\PyCharm `2017.1.5:raw-latex:`\bin` +安装启动目录(以我的为例):E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\PyCharm 2017.1`.5:raw-latex:`\bin` 将第一步下载的 jar 包放入这个目录,并打开如下两个以 ``vmoptions`` 后缀结尾的文件: @@ -1066,6 +1066,7 @@ Debug ,而远程调试需要不少前置步骤。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190323164120.png .. |image1| image:: http://image.python-online.cn/20190323211635.png @@ -1167,4 +1168,3 @@ Debug ,而远程调试需要不少前置步骤。 .. |image97| image:: http://image.python-online.cn/20191211102826.png .. |image98| image:: http://image.python-online.cn/20191211133836.png .. |image99| image:: http://image.python-online.cn/20191211143510.png - diff --git a/source/c04/c04_16.rst b/source/c04/c04_16.rst index 7ecfb5d..f9c6823 100644 --- a/source/c04/c04_16.rst +++ b/source/c04/c04_16.rst @@ -75,3 +75,4 @@ Python:如何在被调用方法中获取调用者的方法名? .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c04/c04_17.rst b/source/c04/c04_17.rst index 954e7d6..e10b6af 100644 --- a/source/c04/c04_17.rst +++ b/source/c04/c04_17.rst @@ -475,9 +475,9 @@ Order .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190414144511.png .. |image1| image:: http://image.python-online.cn/20190512113846.png .. |image2| image:: http://image.python-online.cn/20190512113917.png .. |image3| image:: http://image.python-online.cn/20190512114028.png - diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 297cb7c..06fde2b 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -173,7 +173,7 @@ GoodSync:和 windows 平台同步文件 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190810161513.png .. |image1| image:: http://image.python-online.cn/20190810162315.png - diff --git a/source/c04/c04_19.rst b/source/c04/c04_19.rst index 3fb748b..66496d6 100644 --- a/source/c04/c04_19.rst +++ b/source/c04/c04_19.rst @@ -722,3 +722,4 @@ n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index e9c4c37..2400390 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -336,6 +336,33 @@ pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎 pip install --upgrade pkg1 pip install --upgrade pkg1 --upgrade-strategy only-if-need +6. 配置文件 +----------- + +由于在使用 pip 安装一些包时,默认会使用 pip +的官方源,所以经常会报网络超时失败。 + +常用的解决办法是,在安装包时,使用 ``-i`` +参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 + +这时候,其实可以将这个源写进 pip +的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 + +那怎么配置呢?文件文件在哪? + +使用\ ``win+r`` 输入 ``%APPDATA%`` 进入用户资料文件夹,查看有没有一个 +pip 的文件夹,若没有则创建之。 + +然后进入这个 文件夹,新建一个 ``pip.ini`` 的文件,内容如下 + +.. code:: ini + + [global] + time-out=60 + index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ + [install] + trusted-host=tsinghua.edu.cn + 以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 @@ -344,6 +371,6 @@ pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191105200041.png - diff --git a/source/c04/c04_22.rst b/source/c04/c04_22.rst index c791369..2045055 100644 --- a/source/c04/c04_22.rst +++ b/source/c04/c04_22.rst @@ -11,4 +11,3 @@ 开启完成后,需要重启浏览器,你可以随便打开一篇博客,然后在地址栏右边会有一个阅读模式的按钮。 .. |image0| image:: http://image.python-online.cn/20191201103653.png - diff --git a/source/c05/c05_01.rst b/source/c05/c05_01.rst index 18eabe6..af941e9 100755 --- a/source/c05/c05_01.rst +++ b/source/c05/c05_01.rst @@ -422,6 +422,7 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |快速排序| image:: http://image.python-online.cn/Fpj4DFN_YCtfmJwb_85QnsuIVLqk .. |\|冒泡排序\|| image:: http://image.python-online.cn/FvbrVECeq58hY8TptG4ilkL5Owcc @@ -435,4 +436,3 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD .. |合并两个有序数组| image:: http://image.python-online.cn/FnCZ-3Pj39T_ELROSsGRDN31yWtY .. |桶排序| image:: http://image.python-online.cn/FljGa3F3wM_YGACETeuRHCiERXKb .. |基数排序| image:: http://image.python-online.cn/FhwWVp4LVABIMPHqNo_cpjJA9kHV - diff --git a/source/c05/c05_02.rst b/source/c05/c05_02.rst index 9293bd8..0969487 100755 --- a/source/c05/c05_02.rst +++ b/source/c05/c05_02.rst @@ -123,3 +123,4 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c05/c05_03.rst b/source/c05/c05_03.rst index c455c46..67c6a40 100755 --- a/source/c05/c05_03.rst +++ b/source/c05/c05_03.rst @@ -162,6 +162,6 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190112181126.png - diff --git a/source/c06/c06_01.rst b/source/c06/c06_01.rst index 506885a..a00c794 100755 --- a/source/c06/c06_01.rst +++ b/source/c06/c06_01.rst @@ -134,7 +134,7 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png .. |image1| image:: http://image.python-online.cn/20190511164650.png - diff --git a/source/c06/c06_02.rst b/source/c06/c06_02.rst index 6f86b7a..2478c2d 100755 --- a/source/c06/c06_02.rst +++ b/source/c06/c06_02.rst @@ -312,6 +312,7 @@ show image |image9| .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511164738.png .. |image1| image:: http://image.python-online.cn/20190511164753.png @@ -323,4 +324,3 @@ show image |image9| .. |image7| image:: http://image.python-online.cn/20190511164852.png .. |image8| image:: http://image.python-online.cn/20190511164900.png .. |image9| image:: http://image.python-online.cn/20190511164915.png - diff --git a/source/c06/c06_03.rst b/source/c06/c06_03.rst index 960de44..5940979 100755 --- a/source/c06/c06_03.rst +++ b/source/c06/c06_03.rst @@ -208,10 +208,10 @@ show image |image4| .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511164936.png .. |image1| image:: http://image.python-online.cn/20190511164949.png .. |image2| image:: http://image.python-online.cn/20190511165003.png .. |image3| image:: http://image.python-online.cn/20190511165013.png .. |image4| image:: http://image.python-online.cn/20190511165020.png - diff --git a/source/c06/c06_04.rst b/source/c06/c06_04.rst index 17db400..cee716c 100755 --- a/source/c06/c06_04.rst +++ b/source/c06/c06_04.rst @@ -202,6 +202,7 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165103.png .. |image1| image:: http://image.python-online.cn/20190511165132.png @@ -210,4 +211,3 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib .. |image4| image:: http://image.python-online.cn/20190511165211.png .. |image5| image:: http://image.python-online.cn/20190511165221.png .. |image6| image:: http://image.python-online.cn/20190511165229.png - diff --git a/source/c06/c06_05.rst b/source/c06/c06_05.rst index 3f85d80..c78fcd0 100755 --- a/source/c06/c06_05.rst +++ b/source/c06/c06_05.rst @@ -144,6 +144,6 @@ matplotlib 给我们提供了一个函数,\ ``animation.FuncAnimation`` .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/12/25/5c2226078799b.gif - diff --git a/source/c06/c06_06.rst b/source/c06/c06_06.rst index 3bc04a0..4625f5d 100755 --- a/source/c06/c06_06.rst +++ b/source/c06/c06_06.rst @@ -140,6 +140,6 @@ Jupyter NoteBook 里观察整个变化的过程。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165315.png - diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index e2a249c..7ee3d15 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -381,6 +381,19 @@ more、cat file \| head等。 【注意】:以上对源文件都不做修改,若要修改,要加上\ ``-i`` +替换换行符 + +.. code:: shell + + # 将换行符换成逗号 + $ cat a.txt + 1 + 2 + 3 + 3 + $ sed ":a;N;s/\n/,/g;ta" a.txt + 1,2,3,4 + 1.4 文件重构 ~~~~~~~~~~~~ @@ -1898,8 +1911,8 @@ url** 即可。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-9-20/47469030.jpg .. |image1| image:: http://image.python-online.cn/20190705182629.png .. |image2| image:: http://image.python-online.cn/17-10-15/97911325.jpg - diff --git a/source/c07/c07_02.rst b/source/c07/c07_02.rst index 956f080..fe58352 100755 --- a/source/c07/c07_02.rst +++ b/source/c07/c07_02.rst @@ -160,7 +160,7 @@ Zabbix Proxy 是一个数据收集器,它不计算触发器、不处理事件 注意数据库,这里不要选localhost,使用vip,或者使用vip绑定的域名。 -用默认密码进行登陆(\ ``Admin``/``zabbix``)。 登陆之后,马上修改密码。 +用默认密码进行登陆(``Admin``/``zabbix``)。 登陆之后,马上修改密码。 安装调试工具 ^^^^^^^^^^^^ @@ -696,6 +696,7 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190404193811.png .. |image1| image:: http://image.python-online.cn/20190404194416.png @@ -719,4 +720,3 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 .. |image19| image:: http://image.python-online.cn/20190605173956.png .. |image20| image:: http://image.python-online.cn/20190409103417.png .. |image21| image:: http://image.python-online.cn/20190409104026.png - diff --git a/source/c07/c07_03.rst b/source/c07/c07_03.rst index 9b281d1..b7b3ea5 100755 --- a/source/c07/c07_03.rst +++ b/source/c07/c07_03.rst @@ -3,14 +3,16 @@ -------------- -一、安装Docker --------------- +1. 安装Docker +------------- 参照官方文档,不同Linux发行版,安装方法略有不同。 CentOS:\ `CentOS 7 上安装 Docker 详解 `__ Ubuntu:\ `Ubuntu 16.04 上安装 Docker 详解 `__ +Windows:\ `Windows 10 上安装 Docker +指导 `__ 因为本人都是使用CentOS 7.2的系统,所以以下示例都将在CentOS上操作。 @@ -74,8 +76,32 @@ Ubuntu:\ `Ubuntu 16.04 上安装 Docker $ sudo systemctl start docker $ chkconfig docker on -二、创建第一个容器 ------------------- +2. 配置镜像源 +------------- + +docker默认镜像拉取地址为国外仓库下载速度较慢,网速不好,则会报错”net/http: +TLS handshake timeout”。 + +解决方法很简单,给你的 Docker 配置一个国内的镜像源。 + +在 Win 10 上可以这样配。 + +点击 Docker 图标,选择 ``Setting`` -> ``Docker Engine`` 输入 + +:: + + { + "registry-mirrors": [ + "https://dockerhub.azk8s.cn", + "https://hub-mirror.c.163.com" + ], + "insecure-registries": [], + "debug": true, + "experimental": false + } + +3. 创建第一个容器 +----------------- ``docker run -d -p 80:80 httpd`` @@ -93,10 +119,10 @@ Ubuntu:\ `Ubuntu 16.04 上安装 Docker $ docker ps 或者 docker container ls -三、关于容器 ------------- +4、关于容器 +----------- -3.1 相关命令 +4.1 相关命令 ~~~~~~~~~~~~ :: @@ -138,7 +164,7 @@ Ubuntu:\ `Ubuntu 16.04 上安装 Docker # 创建一个容器 docker create -3.2 内存限额 +4.2 内存限额 ~~~~~~~~~~~~ :: @@ -153,7 +179,7 @@ Ubuntu:\ `Ubuntu 16.04 上安装 Docker --vm 1:启动 1 个内存工作线程。 --vm-bytes 280M:每个线程分配 280M 内存。如果分配超过300,就会出错,容器退出 -3.3 cpu配额 +4.3 cpu配额 ~~~~~~~~~~~ 默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。 Docker @@ -174,7 +200,7 @@ Ubuntu:\ `Ubuntu 16.04 上安装 Docker docker run --name container_A -it -c 1024 --cpu 1 docker run --name container_B -it -c 512 --cpu 1 -3.4 blkio配额 +4.4 blkio配额 ~~~~~~~~~~~~~ ``Block IO`` 限制不同容器的读写资源分配 Block IO @@ -209,7 +235,7 @@ Ubuntu:\ `Ubuntu 16.04 上安装 Docker docker run -it ubuntu time dd if=/dev/zero out=test.out bs=1M count=800 oflag=direct # 测试速度 -3.5 cgroup +4.5 cgroup ~~~~~~~~~~ ``cgroup``\ 全称\ ``Control Group``\ 。\ ``Linux`` 操作系统通过 cgroup @@ -228,7 +254,7 @@ Ubuntu:\ `Ubuntu 16.04 上安装 Docker 3. blkio 路径:/sys/fs/blkio/cpu/docker -3.6 namespace +4.6 namespace ~~~~~~~~~~~~~ ``namespace`` 管理着 ``host`` @@ -264,7 +290,7 @@ namespace 有下面六种 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-12-23/44035514.jpg .. |image1| image:: http://image.python-online.cn/17-12-23/20133481.jpg - diff --git a/source/c07/c07_04.rst b/source/c07/c07_04.rst index 390b2f9..e61f4f4 100755 --- a/source/c07/c07_04.rst +++ b/source/c07/c07_04.rst @@ -361,6 +361,7 @@ CMD:两个功能 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-12-23/49304868.jpg .. |image1| image:: http://image.python-online.cn/17-12-23/36753853.jpg @@ -371,4 +372,3 @@ CMD:两个功能 .. |image6| image:: http://image.python-online.cn/17-12-24/42825662.jpg .. |image7| image:: http://image.python-online.cn/17-12-24/80077038.jpg .. |image8| image:: http://image.python-online.cn/17-12-24/98318652.jpg - diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index 74de2ea..7e7ce6b 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -320,9 +320,9 @@ Socket发些“奇怪”的数据。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/18-1-28/92519416.jpg .. |image1| image:: http://image.python-online.cn/18-1-28/37395940.jpg .. |image2| image:: https://i.loli.net/2018/01/28/5a6de8702428c.png .. |image3| image:: https://i.loli.net/2018/01/28/5a6de73776390.png - diff --git a/source/c07/c07_06.rst b/source/c07/c07_06.rst index d920038..f3d3b24 100755 --- a/source/c07/c07_06.rst +++ b/source/c07/c07_06.rst @@ -329,8 +329,8 @@ centos的配置文件路径如下,ubuntu的有所不同 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png .. |image1| image:: https://i.loli.net/2018/01/03/5a4ce6eeaff7d.png .. |image2| image:: https://i.loli.net/2018/01/03/5a4ce964d3e73.png - diff --git a/source/c07/c07_07.rst b/source/c07/c07_07.rst index 4b2d451..1ea190e 100755 --- a/source/c07/c07_07.rst +++ b/source/c07/c07_07.rst @@ -419,3 +419,4 @@ salt命令格式 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c07/c07_08.rst b/source/c07/c07_08.rst index 8dd89ef..9e43062 100644 --- a/source/c07/c07_08.rst +++ b/source/c07/c07_08.rst @@ -176,3 +176,4 @@ chk_zabbix.sh .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c07/c07_09.rst b/source/c07/c07_09.rst index 1b3be0d..c69a010 100644 --- a/source/c07/c07_09.rst +++ b/source/c07/c07_09.rst @@ -85,6 +85,148 @@ /root/deployment/inventory/get_controller_v1.py --list +7. playbook 参数 +---------------- + +:: + + Options: + --ask-vault-pass + #ask for vault password + #加密playbook文件时提示输入密码 + -C, --check + #don't make any changes; instead, try to predict some of the changes that may occur + #模拟执行,不会真正在机器上执行(查看执行会产生什么变化) + -D, --diff + #when changing (small) files and templates, show the differences in those files; works great with --check + #当更新的文件数及内容较少时,该选项可显示这些文件不同的地方,该选项结合-C用会有较好的效果 + -e EXTRA_VARS, --extra-vars=EXTRA_VARS + #set additional variables as key=value or YAML/JSON + #在Playbook中引入外部参数变量 + --flush-cache + #clear the fact cache + #将fact清除到的远程主机缓存 + --force-handlers + #run handlers even if a task fails + #强制运行handlers的任务,即使在任务失败的情况下 + -f FORKS, --forks=FORKS + #specify number of parallel processes to use(default=5) + #并行任务数。FORKS被指定为一个整数,默认是5 + -h, --help + #show this help message and exit + #打开帮助文档API + -i INVENTORY, --inventory-file=INVENTORY + #specify inventory host path (default=/etc/ansible/hosts) or comma separated host list. + #指定要读取的Inventory文件 + -l SUBSET, --limit=SUBSET + #further limit selected hosts to an additional pattern + #限定执行的主机范围 + --list-hosts + #outputs a list of matching hosts; does not execute anything else + #列出执行匹配到的主机,但并不会执行 + --list-tags + #list all available tags + #列出所有可用的tags + --list-tasks + #list all tasks that would be executed + #列出所有即将被执行的任务 + -M MODULE_PATH, --module-path=MODULE_PATH + #specify path(s) to module library (default=None) + #要执行的模块的路径 + --new-vault-password-file=NEW_VAULT_PASSWORD_FILE + #new vault password file for rekey + # + --output=OUTPUT_FILE + #output file name for encrypt or decrypt; use - for stdout + # + --skip-tags=SKIP_TAGS + #only run plays and tasks whose tags do not match these values + #跳过指定的tags任务 + --start-at-task=START_AT_TASK + #start the playbook at the task matching this name + #从第几条任务(START_AT_TASK)开始执行 + --step + #one-step-at-a-time: confirm each task before running + #逐步执行Playbook定义的任务,并经人工确认后继续执行下一步任务 + --syntax-check + #perform a syntax check on the playbook, but do not execute it + #检查Playbook中的语法书写,并不实际执行 + -t TAGS, --tags=TAGS + #only run plays and tasks tagged with these values + #指定执行该tags的任务 + --vault-password-file=VAULT_PASSWORD_FILE + #vault password file + # + -v, --verbose + #verbose mode (-vvv for more, -vvvv to enable connection debugging) + #执行详细输出 + --version + #show program's version number and exit + #显示版本 + + Connection Options: + control as whom and how to connect to hosts + + -k, --ask-pass + #ask for connection password + # + --private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE + #use this file to authenticate the connection + # + -u REMOTE_USER, --user=REMOTE_USER + #connect as this user (default=None) + #指定远程主机以USERNAME运行命令 + -c CONNECTION, --connection=CONNECTION + #connection type to use (default=smart) + #指定连接方式,可用选项paramiko (SSH)、ssh、local,local方式常用于crontab和kickstarts + -T TIMEOUT, --timeout=TIMEOUT + #override the connection timeout in seconds(default=10) + #SSH连接超时时间设定,默认10s + --ssh-common-args=SSH_COMMON_ARGS + #specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand) + # + --sftp-extra-args=SFTP_EXTRA_ARGS + #specify extra arguments to pass to sftp only (e.g. -f, -l) + # + --scp-extra-args=SCP_EXTRA_ARGS + #specify extra arguments to pass to scp only (e.g. -l) + # + --ssh-extra-args=SSH_EXTRA_ARGS + #specify extra arguments to pass to ssh only (e.g. -R) + # + + Privilege Escalation Options: + control how and which user you become as on target hosts + + -s, --sudo + #run operations with sudo (nopasswd) (deprecated, use become) + #相当于Linux系统下的sudo命令 + -U SUDO_USER, --sudo-user=SUDO_USER + #desired sudo user (default=root) (deprecated, use become) + #使用sudo,相当于Linux下的sudo命令 + -S, --su + #run operations with su (deprecated, use become) + # + -R SU_USER, --su-user=SU_USER + #run operations with su as this user (default=root)(deprecated, use become) + -b, --become + #run operations with become (does not imply password prompting) + # + --become-method=BECOME_METHOD + #privilege escalation method to use (default=sudo),valid choices: [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ] + # + --become-user=BECOME_USER + #run operations as this user (default=root) + # + --ask-sudo-pass + #ask for sudo password (deprecated, use become) + #传递sudo密码到远程主机,来保证sudo命令的正常运行 + --ask-su-pass + #ask for su password (deprecated, use become) + # + -K, --ask-become-pass + #ask for privilege escalation password + 快速传送门 ---------- diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index 0d8b702..d3f93f8 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -187,9 +187,9 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190716111523.png .. |image1| image:: http://image.python-online.cn/20190716112113.png .. |image2| image:: http://image.python-online.cn/20190716112824.png .. |image3| image:: http://image.python-online.cn/20190716112838.png - diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst index 19b278b..e8b7dd7 100644 --- a/source/c07/c07_11.rst +++ b/source/c07/c07_11.rst @@ -76,4 +76,3 @@ K8s 角色详解 **共享资源**\ 。 .. |image0| image:: http://image.python-online.cn/20190907162015.png - diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index 23d2c03..b9cdb90 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -137,7 +137,25 @@ yum-utils 使用 |image2| +查看rpm包的版本 + +.. code:: bash + + # 查看软件包的详细信息 + rpm -qpi xxx.rpm + + # 查看软件包所包含的目录和文件 + rpm -qpl xxx.rpm + + # 查看软件包的文档所在的位置 + rpm -qpd xxx.rpm + + # 查看软件包的配置文件 + rpm -qpc rpm + + # 查看软件包的依赖关系 + rpm -qpR xxx.rpm + .. |image0| image:: http://image.python-online.cn/20191219152328.png .. |image1| image:: http://image.python-online.cn/20191225173340.png .. |image2| image:: http://image.python-online.cn/20191225175350.png - diff --git a/source/c07/c07_15.rst b/source/c07/c07_15.rst index a14de00..d3ea595 100644 --- a/source/c07/c07_15.rst +++ b/source/c07/c07_15.rst @@ -218,4 +218,3 @@ wsrep_flow_control_sent 和 wsrep_local_recv_queue_avg - http://blog.itpub.net/30126024/viewspace-2221483/ .. |image0| image:: http://image.python-online.cn/20191213162259.png - diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index f20a6c2..c633e14 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -50,4 +50,3 @@ .. |image1| image:: http://image.python-online.cn/20191220203403.png .. |image2| image:: http://image.python-online.cn/20191220202408.png .. |image3| image:: http://image.python-online.cn/20191220203205.png - diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index fa58340..8c9288a 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -272,7 +272,7 @@ aggregate管理 1.2 virsh命令 ~~~~~~~~~~~~~ -:: +.. code:: shell # 查看虚拟机的网卡信息 $ virsh domiflist VM1 @@ -293,7 +293,7 @@ aggregate管理 # 修改虚拟机密码,需要虚拟机内部安装 qga:qemu-guest-agent virsh set-user-password instance-00000444 root "root12#$" -热增加网卡:\ ``virsh attach-device ws_controller01 ./tmp.xml --persistent --live`` +热增加网卡:``virsh attach-device ws_controller01 ./tmp.xml --persistent --live`` .. code:: xml @@ -306,6 +306,12 @@ aggregate管理 +热去除带宽限速 + +.. code:: shell + + $ virsh domiftune ws_controller01 vnet4 --inbound 0,0,0 --outbound 0,0,0 --config --live + 压缩镜像 .. code:: shell @@ -324,6 +330,12 @@ aggregate管理 echo 'export TMPDIR=/data/tmp' >> /etc/profile source /etc/profile +在线查看虚拟机的messages日志 + +.. code:: shell + + virt-log -d ws_controller01 + 1.2 LVM管理 ~~~~~~~~~~~ @@ -435,3 +447,4 @@ aggregate管理 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index 12fe912..6565fb4 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -49,7 +49,7 @@ vim /etc/sysconfig/grub(或者/etc/default/grub):intel_iommu=on echo "echo '32' > /sys/class/net/eth0/device/sriov_numvfs" >> /etc/rc.local # 验证VF是否创建完成 - lcpci |grep Ethernet + lspci |grep Ethernet 8.2.1.5 验证基础环境 ~~~~~~~~~~~~~~~~~~~~ @@ -306,6 +306,7 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/01/19/5a61bfa0ca66f.png .. |image1| image:: https://i.loli.net/2018/01/19/5a61bfd243111.png @@ -315,4 +316,3 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr .. |image5| image:: https://i.loli.net/2018/01/19/5a61c246451e7.png .. |image6| image:: http://image.python-online.cn/20190529202132.png .. |image7| image:: http://image.python-online.cn/20190529202440.png - diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index 9632a81..c632bf6 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -531,10 +531,10 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png .. |image1| image:: https://i.loli.net/2018/01/27/5a6c34b14c6ec.png .. |image2| image:: http://image.python-online.cn/20190827200522.png .. |image3| image:: http://image.python-online.cn/20191211174659.png .. |image4| image:: http://image.python-online.cn/20191211174956.png - diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index fe58ce2..7fdfff8 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -486,6 +486,7 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190714161353.png .. |image1| image:: http://image.python-online.cn/20190716004341.png @@ -493,4 +494,3 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 .. |image3| image:: http://image.python-online.cn/20190716005951.png .. |image4| image:: http://image.python-online.cn/20190714141644.png .. |image5| image:: https://i.loli.net/2019/02/25/5c73e6160764a.png - diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index 01a2553..4a31122 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -733,6 +733,7 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190526144846.png .. |image1| image:: http://image.python-online.cn/20190529135942.png @@ -794,4 +795,3 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 .. |image57| image:: http://image.python-online.cn/20190830092203.png .. |image58| image:: http://image.python-online.cn/20190830093613.png .. |image59| image:: http://image.python-online.cn/20190912135302.png - diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index 8f1d27b..ea088e2 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -708,6 +708,7 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190430204707.png .. |image1| image:: http://image.python-online.cn/20190430204933.png @@ -750,4 +751,3 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 .. |image38| image:: http://image.python-online.cn/20190911203953.png .. |image39| image:: http://image.python-online.cn/20190911204805.png .. |image40| image:: http://image.python-online.cn/20190911205518.png - diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index 67c58e6..c8b79e3 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -151,6 +151,7 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190419144135.png .. |image1| image:: http://image.python-online.cn/20190419144044.png @@ -163,4 +164,3 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 .. |image8| image:: http://image.python-online.cn/20190528105021.png .. |image9| image:: http://image.python-online.cn/20190528114526.png .. |image10| image:: http://image.python-online.cn/20190606185531.png - diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index 65c3f3f..ec0a913 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -138,8 +138,10 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 -------------------- 1. 启用DHCP后,dhcp server 会占用一个allocation_pools中的ip。 + 2. 子网启用了dhcp,ConfigDrive不会携带静态的ip地址(类型为ipv4,左图),而是携带了将类型变更为ipv4_dhcp(右图),虚拟机启动时发送广播消息DHCP discover从dhcp server获得真正的ip地址。 |image3| + 3. 若子网启用了dhcp,并且创建了虚拟机,此时 disable dhcp,只是表象上 dhcp 被关了,而实际 dhcp port 被是占用着,因为 dhcp server 还要给之前创建的虚拟机提供服务。如果再用这个子网创建的虚拟机会使用dhcp @@ -151,20 +153,26 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 ip。如果一个子网开启了dhcp,并且这个子网下\ **有虚拟机**\ ,更新子网 disable dhcp,dhcp-port\ **不会立即删除**\ 。再用这个子网创建的虚拟机会使用dhcp获取ip。 + 4. dnsmasq进程是由dhcp-agent 服务管理的。如果停用 dhcp-agent 时,dnsmasq 进程并不会关闭。如果同一个网络下有多个dnsmasq ,不会影响正常的dhcp获取ip。 + 5. 如果三个控制节点上的 dhcp-agent 都是关闭状态,此时创建虚拟机时,ip仍然会正常分配、port会正常创建,但由于nova-compute等不到neutron的port plugged事件,过一段时间就超时导致创建虚拟机失败。而如果在超时范围内将dhcp-agent启动起来,就可以立即创建成功。 + 6. 如果一台节点上的 dhcp-agent 关闭了,neutron-server 会等待150s(agent_down_time*2)后再重新调度,将负责这个节点上的dnsmasq进程在另一台上启动起来。 + 7. 虚拟机通过dhcp获取ip,和用config drive 注入静态ip配置的时间差不多,经验证从创建到ping通,dhcp花了22s,静态ip花了23s + 8. 如果一个子网没有配置 dns,那么用这个子网创建虚拟机,虚拟机内部会将这个子网的dhcp server 的ip拿来做dns配在 /etc/resolv.conf 里,而且在排在最上面,可能会导致虚拟机上不了网。 + 9. 使用 dhcp 的模式,cloudinit 从 configure drive 中知道是dhcp后就不会去刷新配置文件将static 改为dhcp(使用的是NetworkManager自动获取ip,这在开机启动时 @@ -172,6 +180,7 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 去做),所以如果这个镜像原网卡配置文件里是静态ip,那么使用这个镜像创建dhcp 的虚拟机,就会暴露旧ip,但是这对于配置ip没有影响,NetworkManager 配置ip的顺序是先dhcp,获取不到再从配置文件读。 + 10. 如果一个子网只有dhcp port,子网可以被删除,如果有其他port,则子网不能删除。 @@ -240,7 +249,7 @@ DOWN后会触发重新调度将dnsmasq迁到另一台,对应函数:reschedul **dhcp-port 是如何被创建出来的?** 从 -neutron:raw-latex:`\neutron`-0.0.1.dev2:raw-latex:`\neutron`:raw-latex:`\agent`:raw-latex:`\linux`:raw-latex:`\dhcp`.py: +neutron:raw-latex:`\neutron-0.0`.1.dev2:raw-latex:`\neutron`:raw-latex:`\agent`:raw-latex:`\linux`:raw-latex:`\dhcp`.py: setup() 开始 再进入 @@ -252,6 +261,7 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190514202013.png .. |image1| image:: http://image.python-online.cn/20190514202442.png @@ -260,4 +270,3 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: .. |image4| image:: http://image.python-online.cn/20190430204707.png .. |image5| image:: http://image.python-online.cn/20190430204933.png .. |image6| image:: http://image.python-online.cn/20190430205449.png - diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index 7ed28d6..62dc54d 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -785,6 +785,7 @@ rpc server 和rpc client 的四个重要方法 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190623185008.png .. |image1| image:: http://image.python-online.cn/20190623165341.png @@ -807,4 +808,3 @@ rpc server 和rpc client 的四个重要方法 .. |image18| image:: http://image.python-online.cn/20190526175100.png .. |image19| image:: http://image.python-online.cn/20190526180708.png .. |image20| image:: http://image.python-online.cn/20190526181433.png - diff --git a/source/c08/c08_10.rst b/source/c08/c08_10.rst index 87f65c6..1cbe2ce 100644 --- a/source/c08/c08_10.rst +++ b/source/c08/c08_10.rst @@ -26,4 +26,3 @@ cd,操作文件时,需要使用绝对路径。 .. |image0| image:: http://image.python-online.cn/20191111112221.png .. |image1| image:: http://image.python-online.cn/20191111112421.png .. |image2| image:: http://image.python-online.cn/20191111112548.png - diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index 3fc6df8..db9026a 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -48,6 +48,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190530175817.png - diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index 04b9474..af2a8c9 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -89,6 +89,7 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190424212211.png .. |image1| image:: http://image.python-online.cn/20190424213430.png @@ -98,4 +99,3 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 .. |image5| image:: http://image.python-online.cn/20190424215735.png .. |image6| image:: http://image.python-online.cn/20190424220008.png .. |image7| image:: http://image.python-online.cn/20191011103832.png - diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index f042b5d..6136ab0 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -315,9 +315,9 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190706114314.png .. |image1| image:: http://image.python-online.cn/20190706093904.png .. |image2| image:: http://image.python-online.cn/20190706160632.png .. |image3| image:: http://image.python-online.cn/20190804162402.png - diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index 9c8f929..9a6cdc2 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -326,6 +326,7 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190716175250.png .. |image1| image:: http://image.python-online.cn/20190716180655.png @@ -341,4 +342,3 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local .. |image11| image:: http://image.python-online.cn/20190829111917.png .. |image12| image:: http://image.python-online.cn/20190829161243.png .. |image13| image:: http://image.python-online.cn/20190926171038.png - diff --git a/source/c08/c08_15.rst b/source/c08/c08_15.rst index a063773..95c2d81 100644 --- a/source/c08/c08_15.rst +++ b/source/c08/c08_15.rst @@ -66,4 +66,3 @@ subnet_id。 .. |image9| image:: http://image.python-online.cn/20190804091911.png .. |image10| image:: http://image.python-online.cn/20190809213209.png .. |image11| image:: http://image.python-online.cn/20190809213223.png - diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index f949770..7358ab7 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -1,8 +1,8 @@ 9.1 开发环境的搭建(Goland和VSCode) ==================================== -9.1.1 为什么学习Go? --------------------- +1. 为什么学习Go? +----------------- 一直很喜欢 Python 的我,为什么突然学起了Go呢? @@ -37,8 +37,8 @@ Linux 系统的计算机上开发可以在 Windows 上运行的应用程序。 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。Go语言做到了真正的国际化! -9.1.2 下载安装 Go语言 ---------------------- +2. 下载安装 Go语言 +------------------ 下载地址:https://golang.google.cn/dl/ @@ -53,8 +53,8 @@ Linux 系统的计算机上开发可以在 Windows 上运行的应用程序。 |image2| -9.1.3 安装并破解 Goland ------------------------ +3. 配置 Goland 环境 +------------------- 学习编程语言,使用一个称心的 IDE,可以帮你省去很多麻烦。 @@ -93,7 +93,7 @@ Goland 下载地址:https://download.jetbrains.com/go/goland-2019.2.3.exe - 激活码.txt:激活码 将 jetbrains-agent.jar 拷贝到 你的 Goland -安装目录的bin文件夹下,我的路径是:E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\GoLand `2019.2.3:raw-latex:`\bin` +安装目录的bin文件夹下,我的路径是:E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\GoLand 2019.2`.3:raw-latex:`\bin` 然后用编辑器打开这两个文件 @@ -115,14 +115,51 @@ Goland 下载地址:https://download.jetbrains.com/go/goland-2019.2.3.exe |image9| -9.1.4 配置 VS Code 环境 ------------------------ +创建好Project后,再点击 +Files->Settings->GOPATH,添加我们的项目目录\ ``F:\Go-Player`` + +|image10| + +随便点击一个go文件,就能在下图箭头处看到配置入口,点击进入配置一下 +Go运行器。 + +|image11| + +按照如下指示进行配置。 + +|image12| + +去掉参数提示 + +|image13| + +设置 goproxy + +|image14| + +设置 goimports(自动格式化插件),如果 你之前 没有安装 ,会提示你点击 +``yes`` 下载安装 。 + +|image15| + +至此,环境配置完成。 + +在项目根目录下,创建如下三个文件夹,并在 src 目录下创建一个hello.go +的文件。 + +|image16| + +点击运行按钮,在控制台我们看到了熟悉的 ``Hello, World!`` + +|image17| + +4. 配置 VS Code 环境 +-------------------- 提前设置用户级的环境变量 :: - # 用户级 GOPATH = F:\Go-Player PATH = %GOPATH%\bin # 以追加的方式 @@ -155,7 +192,7 @@ Go的工作目录,这个目录指定了需要从哪个地方寻找GO的包、 打开你的 VS Code软件,先确认你设置的环境变量已经生效,点击 ``Terminal`` -> ``New Terminal``\ ,使用 cmd 命令查看环境变量。 -|image10| +|image18| 如上图所求,我的环境变量是OK的,如果你的输出是指向你的用户目录:\ ``%USERPROFILE%\go`` 建议你不要折腾(因为我无论重启多少次 VS @@ -176,72 +213,119 @@ Code,其记录的GOPATH始终指向%USERPROFILE%:raw-latex:`\go`), 第一个是:Go 语言的扩展插件 -|image11| +|image19| 第二个是:Code Runner,让你的 VS Code 能够编译运行 Go 的程序。 -|image12| +|image20| 随便点开一个 go 文件,在你的右下角会提示要你安装一些工具,点击 ``Install All`` -|image13| +|image21| 然后你在 OUTPUT 就能看到安装进度 -|image14| +|image22| 查看 OUTPUT 会有一些安装失败的信息。 -|image15| +|image23| 把这两条单独拿出来执行吧(记住执行的话,要切回 %GOPATH%),先使用 ``go get`` 下载,再使用 ``go install`` 安装(若你想安装其他的包,其实也是一样的逻辑)。 -|image16| +|image24| 安装的 exe 文件会放在 %GOPATH%/bin 下,也就是 ``F:\Go-Player\bin`` -|image17| +|image25| 而此的 src 目录结构是这样的 -|image18| +|image26| 到这时环境配置完成,编写 HelloWorld,并运行查看输出,一切完成。 -|image19| +|image27| -9.1.5 编写HelloWorld --------------------- +5. 配置环境变量 +--------------- -在项目根目录下,创建如下三个文件夹,并在 src 目录下创建一个hello.go -的文件。 +当你在终端使用 ``go env`` 的时候,会打印出go 相关的所有环境变量 -|image20| +.. code:: shell -上面 Goland 提示我们 GOPATH 还未设置,那我们就先设置一下,点击 -Files->Settings->GOPATH,添加我们的项目目录\ ``F:\Go-Player`` + $ go env + set GO111MODULE= + set GOARCH=amd64 + set GOBIN= + set GOCACHE=C:\Users\wangbm\AppData\Local\go-build + set GOENV=C:\Users\wangbm\AppData\Roaming\go\env + set GOEXE=.exe + set GOFLAGS= + set GOHOSTARCH=amd64 + set GOHOSTOS=windows + set GONOPROXY= + set GONOSUMDB= + set GOOS=windows + set GOPATH=E:\MING-Code\GoPlayer + set GOPRIVATE= + set GOPROXY=https://proxy.golang.org,direct + set GOROOT=D:\Program Files (x86)\Go-1.13.6 + set GOSUMDB=sum.golang.org + set GOTMPDIR= + set GOTOOLDIR=D:\Program Files (x86)\Go-1.13.6\pkg\tool\windows_amd64 + set GCCGO=gccgo + set AR=ar + set CC=gcc + set CXX=g++ + set CGO_ENABLED=1 + set GOMOD= + set CGO_CFLAGS=-g -O2 + set CGO_CPPFLAGS= + set CGO_CXXFLAGS=-g -O2 + set CGO_FFLAGS=-g -O2 + set CGO_LDFLAGS=-g -O2 + set PKG_CONFIG=pkg-config + +想查看几个特定的环境变量就加在 ``go env`` 后面 -|image21| +.. code:: shell -接下来还要配置Go运行器 + $ go env GOPATH + E:\MING-Code\GoPlayer + $ go env GOROOT + D:\Program Files (x86)\Go-1.13.6 + $ go env GOPROXY + https://goproxy.cn,direct -|image22| +其中有几个比较重要的,我这里会讲一下。 -按照如下指示进行配置。 +``GOPATH``\ : -|image23| +``GOROOT``\ : -一切完成之后,就可以点击运行按钮,在控制台我们看到了熟悉的 -``Hello, World!`` +``GOPROXY``\ :你安装下载包的时候去哪里下载,一条命令即可,更多详情可以查看 +`Github · +GoProxy `__ -|image24| +.. code:: shell + + $ go env -w GOPROXY=https://goproxy.cn,direct + +``GO111MODULE``\ :on、 off或 +auto,必须为小写,不能为为true或false,也不能为1或0。这里推荐设置为on + +.. code:: shell + + $ go env -w GO111MODULE=on .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200102220841.png .. |image1| image:: http://image.python-online.cn/20200102221555.png @@ -253,19 +337,21 @@ Files->Settings->GOPATH,添加我们的项目目录\ ``F:\Go-Player`` .. |image7| image:: http://image.python-online.cn/20200102223113.png .. |image8| image:: http://image.python-online.cn/20200102223451.png .. |image9| image:: http://image.python-online.cn/20200102223946.png -.. |image10| image:: http://image.python-online.cn/20200109210630.png -.. |image11| image:: http://image.python-online.cn/20200108202934.png -.. |image12| image:: http://image.python-online.cn/20200109153948.png -.. |image13| image:: http://image.python-online.cn/20200109210654.png -.. |image14| image:: http://image.python-online.cn/20200109211543.png -.. |image15| image:: http://image.python-online.cn/20200109212824.png -.. |image16| image:: http://image.python-online.cn/20200109213032.png -.. |image17| image:: http://image.python-online.cn/20200109213056.png -.. |image18| image:: http://image.python-online.cn/20200109214117.png -.. |image19| image:: http://image.python-online.cn/20200109154657.png -.. |image20| image:: http://image.python-online.cn/20200102224417.png -.. |image21| image:: http://image.python-online.cn/20200102224643.png -.. |image22| image:: http://image.python-online.cn/20200102225750.png -.. |image23| image:: http://image.python-online.cn/20200102225349.png -.. |image24| image:: http://image.python-online.cn/20200102225550.png - +.. |image10| image:: http://image.python-online.cn/20200102224643.png +.. |image11| image:: http://image.python-online.cn/20200102225750.png +.. |image12| image:: http://image.python-online.cn/20200102225349.png +.. |image13| image:: http://image.python-online.cn/20200127192147.png +.. |image14| image:: http://image.python-online.cn/20200127192512.png +.. |image15| image:: http://image.python-online.cn/20200127192748.png +.. |image16| image:: http://image.python-online.cn/20200102224417.png +.. |image17| image:: http://image.python-online.cn/20200102225550.png +.. |image18| image:: http://image.python-online.cn/20200109210630.png +.. |image19| image:: http://image.python-online.cn/20200108202934.png +.. |image20| image:: http://image.python-online.cn/20200109153948.png +.. |image21| image:: http://image.python-online.cn/20200109210654.png +.. |image22| image:: http://image.python-online.cn/20200109211543.png +.. |image23| image:: http://image.python-online.cn/20200109212824.png +.. |image24| image:: http://image.python-online.cn/20200109213032.png +.. |image25| image:: http://image.python-online.cn/20200109213056.png +.. |image26| image:: http://image.python-online.cn/20200109214117.png +.. |image27| image:: http://image.python-online.cn/20200109154657.png diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index 31d59a5..3f2025a 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -1,4 +1,4 @@ -9.3 五种变量创建的方法 +9.2 五种变量创建的方法 ====================== 对于只有 Python 语言经验的朋友,也许会不太理解声明这个词,在 Python @@ -138,7 +138,7 @@ float64,但是很多情况下,我们并不需要这么高的精度(占用 age: 28 ptr: 0xc000010098 -而这里要说的 new 函数,是 Go 里的一个内奸函数。 +而这里要说的 new 函数,是 Go 里的一个内建函数。 使用表达式 new(Type) 将创建一个Type类型的匿名变量,初始化为Type类型的零值,然后返回变量地址,返回的指针类型为\ ``*Type``\ 。 @@ -205,3 +205,4 @@ float64,但是很多情况下,我们并不需要这么高的精度(占用 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index 2646c46..4776f45 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -1,158 +1,230 @@ -9.3 理解语句块与作用域 -====================== +9.3 详解数据类型:整型与浮点型 +============================== -由于 Go -使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 -Go 中的语句块是怎么一回事? +1. 整型 +------- -9.3.1 显示语句块与隐式语句块 ----------------------------- +Go +语言中,整数类型可以再细分成10个类型,为了方便大家学习,我将这些类型整理成一张表格。 -通俗地说,语句块是由花括弧(\ ``{}``\ )所包含的一系列语句。 +|image0| -语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域,我会在下节讲到。 +int 和 uint 的区别就在于一个 ``u``\ ,有 ``u`` 说明是无符号,没有 ``u`` +代表有符号。 -用花括弧包含的语句块,属于显示语句块。 +**解释这个符号的区别** -在 Go 中还有很多的隐式语句块: +以 ``int8`` 和 ``uint8`` 举例,8 代表 8个bit,能表示的数值个数有 2^8 = +256。 -- 主语句块:包括所有源码,对应内置作用域 -- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 -- 文件语句块:包括该文件中的所有源码,对应文件级作用域 -- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 +uint8 是无符号,能表示的都是正数,0-255,刚好256个数。 -前面三点好理解,第四点举几个例子 +int8 +是有符号,既可以正数,也可以负数,那怎么办?对半分呗,-128-127,也刚好 +256个数。 -for 循环 +int8 int16 int32 int64 +这几个类型的最后都有一个数值,这表明了它们能表示的数值个数是固定的。 + +而 int +没有并没有指定它的位数,说明它的大小,是可以变化的,那根据什么变化呢? + +- 当你在32位的系统下,int 和 uint 都占用 4个字节,也就是32位。 +- 若你在64位的系统下,int 和 uint 都占用 8个字节,也就是64位。 + +出于这个原因,在某些场景下,你应当避免使用 int 和 uint +,而使用更加精确的 int32 和 +int64,比如在二进制传输、读写文件的结构描述(为了保持文件的结构不会受到不同编译目标平台字节长度的影响) + +**不同进制的表示方法** + +出于习惯,在初始化数据类型为整型的变量时,我们会使用10进制的表示法,因为它最直观,比如这样,表示整数10. + +:: + + var num int = 10 + +不过,你要清楚,你一样可以使用其他进制来表示一个整数,这里以比较常用的2进制、8进制和16进制举例。 + +2进制:以\ ``0b``\ 或\ ``0B``\ 为前缀 .. code:: go - for i := 0; i < 5; i++ { - fmt.Println(i) - } + var num01 int = 0b1100 -if 语句 +8进制:以\ ``0o``\ 或者 ``0O``\ 为前缀 .. code:: go - if i := 0; i >= 0 { - fmt.Println(i) - } + var num02 int = 0o14 -switch 语句 +16进制:以\ ``0x`` 为前缀 .. code:: go - switch i := 2; i * 4 { - case 8: - fmt.Println(i) - default: - fmt.Println(“default”) - } + var num03 int = 0xC -且每个 switch 语句的子句都是一个隐式的语句块 +下面用一段代码分别使用二进制、8进制、16进制来表示 10 进制的数值:12 .. code:: go - switch i := 2; i * 4 { - case 8: - j := 0 - fmt.Println(i, j) - default: - // "j" is undefined here - fmt.Println(“default”) + package main + + import ( + "fmt" + ) + + func main() { + var num01 int = 0b1100 + var num02 int = 0o14 + var num03 int = 0xC + + fmt.Printf("2进制数 %b 表示的是: %d \n", num01, num01) + fmt.Printf("8进制数 %o 表示的是: %d \n", num02, num02) + fmt.Printf("16进制数 %X 表示的是: %d \n", num03, num03) } - // "j" is undefined here -9.3.2 四种作用域的理解 ----------------------- +输出如下 -变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 +:: -根据声明位置的不同,作用域可以分为以下四个类型: + 2进制数 1100 表示的是: 12 + 8进制数 14 表示的是: 12 + 16进制数 C 表示的是: 12 -- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 -- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 -- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 -- 局部作用域:在自己的语句块内声明,包括函数,for、if - 等语句块,或自定义的 {} - 语句块形成的作用域,只在自己的局部作用域内可用 +以上代码用过了 fmt 包的格式化功能,你可以参考这里去看上面的代码 -以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这时将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 +:: -对于作用域,有以下几点总结: + %b 表示为二进制 + %c 该值对应的unicode码值 + %d 表示为十进制 + %o 表示为八进制 + %q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示 + %x 表示为十六进制,使用a-f + %X 表示为十六进制,使用A-F + %U 表示为Unicode格式:U+1234,等价于"U+%04X" + %E 用科学计数法表示 + %f 用浮点数表示 -- 低层作用域,可以访问高层作用域 -- 同一层级的作用域,是相互隔离的 -- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 +2. 浮点型 +--------- -在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 +浮点数类型的值一般由整数部分、小数点“``.``”和小数部分组成。 -而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 +其中,整数部分和小数部分均由10进制表示法表示。不过还有另一种表示方法。那就是在其中加入指数部分。指数部分由“E”或“e”以及一个带正负号的10进制数组成。比如,\ ``3.7E-2``\ 表示浮点数\ ``0.037``\ 。又比如,\ ``3.7E+1``\ 表示浮点数\ ``37``\ 。 -9.3.3 静态作用域与动态作用域 ----------------------------- +有时候,浮点数类型值的表示也可以被简化。比如,\ ``37.0``\ 可以被简化为\ ``37``\ 。又比如,\ ``0.037``\ 可以被简化为\ ``.037``\ 。 -根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: +有一点需要注意,在Go语言里,浮点数的相关部分只能由10进制表示法表示,而不能由8进制表示法或16进制表示法表示。比如,\ ``03.7``\ 表示的一定是浮点数\ ``3.7``\ 。 -- 静态作用域,如 Go 语言 -- 动态作用域,如 Shell 语言 +float32 和 float64 +~~~~~~~~~~~~~~~~~~ -具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 +Go语言中提供了两种精度的浮点数 float32 和 float64。 -.. code:: python +**float32**\ ,也即我们常说的单精度,存储占用4个字节,也即4*8=32位,其中1位用来符号,8位用来指数,剩下的23位表示尾数 - #!/bin/bash - func01() { - local value=1 - func02 - } - func02() { - echo "func02 sees value as ${value}" - } +.. figure:: https://pic4.zhimg.com/80/v2-749cc641eb4d5dafd085e8c23f8826aa_hd.jpg + :alt: img + + img + +**float64**\ ,也即我们熟悉的双精度,存储占用8个字节,也即8*8=64位,其中1位用来符号,11位用来指数,剩下的52位表示尾数 + +.. figure:: https://pic2.zhimg.com/80/v2-48240f0e1e0dd33ec89100cbe2d30707_hd.jpg + :alt: img + + img + +**那么精度是什么意思?有效位有多少位?** + +精度主要取决于尾数部分的位数。 + +对于 +float32(单精度)来说,表示尾数的为23位,除去全部为0的情况以外,最小为2^-23,约等于1.19*10^-7,所以float小数部分只能精确到后面6位,加上小数点前的一位,即有效数字为7位。 - # 执行函数 - func01 - func02 +同理 float64(单精度)的尾数部分为 +52位,最小为2^-52,约为2.22*10^-16,所以精确到小数点后15位,加上小数点前的一位,有效位数为16位。 -从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 -value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 -func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 -func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 +通过以上,可以总结出以下几点: -但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 -是访问不了 value 变量的。 +**一、float32 和 float64 可以表示的数值很多** -所以此时的输出结果是 +浮点数类型的取值范围可以从很微小到很巨大。浮点数取值范围的极限值可以在 +math 包中找到: -.. code:: shell +- 常量 math.MaxFloat32 表示 float32 能取到的最大数值,大约是 3.4e38; +- 常量 math.MaxFloat64 表示 float64 能取到的最大数值,大约是 1.8e308; +- float32 和 float64 能表示的最小值分别为 1.4e-45 和 4.9e-324。 - func02 sees value as 1 - func02 sees value as +**二、数值很大但精度有限** -但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 -name -这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 +人家虽然能表示的数值很大,但精度位却没有那么大。 + +- float32的精度只能提供大约6个十进制数(表示后科学计数法后,小数点后6位)的精度 +- float64的精度能提供大约15个十进制数(表示后科学计数法后,小数点后15位)的精度 + +这里的精度是什么意思呢? + +比如 10000018这个数,用 float32 +的类型来表示的话,由于其有效位是7位,将10000018 表示成科学计数法,就是 +1.0000018 \* 10^7,能精确到小数点后面6位。 + +此时用科学计数法表示后,小数点后有7位,刚刚满足我们的精度要求,意思是什么呢?此时你对这个数进行+1或者-1等数学运算,都能保证计算结果是精确的 .. code:: go import "fmt" - - func func01() { - fmt.Println("在 func01 函数中,name:", name) + var myfloat float32 = 10000018 + func main() { + fmt.Println("myfloat: ", myfloat) + fmt.Println("myfloat: ", myfloat+1) } - func main() { - var name string = "Python编程时光" - fmt.Println("在 main 函数中,name:", name) +输出如下 + +.. code:: go + + myfloat: 1.0000018e+07 + myfloat: 1.0000019e+07 + +上面举了一个刚好满足精度要求数据的临界情况,为了做对比,下面也举一个刚好不满足精度要求的例子。只要给这个数值多加一位数就行了。 - func01() +换成 100000187,同样使用 +float32类型,表示成科学计数法,由于精度有限,表示的时候小数点后面7位是准确的,但若是对其进行数学运算,由于第八位无法表示,所以运算后第七位的值,就会变得不精确。 + +这里我们写个代码来验证一下,按照我们的理解下面 myfloat01 = 100000182 +,对其\ ``+5`` 操作后,应该等于 myfloat02 = 100000187, + +.. code:: go + + import "fmt" + + var myfloat01 float32 = 100000182 + var myfloat02 float32 = 100000187 + + func main() { + fmt.Println("myfloat: ", myfloat01) + fmt.Println("myfloat: ", myfloat01+5) + fmt.Println(myfloat02 == myfloat01+5) } -因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 +但是由于其类型是 +float32,精度不足,导致最后比较的结果是不相等(从小数点后第七位开始不精确) + +.. code:: go + + myfloat: 1.00000184e+08 + myfloat: 1.0000019e+08 + false + +由于精度的问题,就会出现这种很怪异的现象,\ ``myfloat == myfloat +1`` +会返回 ``true`` 。 -参考文章:https://studygolang.com/articles/12632 +参考文章: +---------- -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! +https://www.zhihu.com/question/26022206 +.. |image0| image:: http://image.python-online.cn/20200120204329.png diff --git a/source/c09/c09_04.rst b/source/c09/c09_04.rst index fb20c52..cd779b7 100644 --- a/source/c09/c09_04.rst +++ b/source/c09/c09_04.rst @@ -1,177 +1,239 @@ -9.4 Go语言命名编码规范 -====================== +9.4 详解数据类型:byte、rune与字符串 +==================================== -每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 +1. byte 与 rune +--------------- -以下内容整理自:\ `Go语言(Golang)编码规范 `__ +**byte**\ ,占用1个节字,就 8 个比特位,所以它和 ``uint8`` +类型本质上没有区别,它表示的是 ACSII 表中的一个字符。 -命名规范分为以下几点 +如下这段代码,分别定义了 byte 类型和 uint8 类型的变量 a 和 b -**1. 文件命名** +.. code:: go + + import "fmt" + + func main() { + var a byte = 65 + // 8进制写法: var c byte = '\101' 其中 \ 是固定前缀 + // 16进制写法: var c byte = '\x41' 其中 \x 是固定前缀 + + var b uint8 = 66 + fmt.Printf("a 的值: %c \nb 的值: %c", a, b) + // 或者使用 string 函数 + // fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b)) + } -文件名应一律使用小写(因为Windows的原因), 不同单词之间用下划线分割。 +在 ASCII 表中,由于字母 A 的ASCII 的编号为 65 ,字母 B 的ASCII 编号为 +66,所以上面的代码也可以写成这样 -应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 Gogs -的入口应当为 gogs.go +.. code:: go + + import "fmt" -**2. 常量命名** + func main() { + var a byte = 'A' + var b uint8 = 'B' + fmt.Printf("a 的值: %c \nb 的值: %c", a, b) + } -- 常量均需使用全部大写字母组成,并使用下划线分词: +他们的输出结果都是一样的。 - .. code:: go +:: - const APP_VER = "0.7.0.1110 Beta" + a 的值: A + b 的值: B -- 如果是枚举类型的常量,需要先创建相应类型: +**rune**\ ,占用4个字节,共32位比特位,所以它和 ``uint32`` +本质上也没有区别。它表示的是一个 +Unicode字符(Unicode是一个可以表示世界范围内的绝大部分字符的编码规范)。 - .. code:: go +.. code:: go - type Scheme string - const ( - HTTP Scheme = "http" - HTTPS Scheme = "https" - ) + import ( + "fmt" + "unsafe" + ) -- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: + func main() { + var a byte = 'A' + var b rune = 'B' + fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数", unsafe.Sizeof(a), unsafe.Sizeof(b)) + } - .. code:: go +输出如下 - type PullRequestStatus int - const ( - PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota - PULL_REQUEST_STATUS_CHECKING - PULL_REQUEST_STATUS_MERGEABLE - ) +:: -**3. 变量命名** + a 占用 1 个字节数 + b 占用 4 个字节数 -使用驼峰命名法 +由于 byte 类型能表示的值是有限,只有 2^8=256 +个。所以如果你想表示中文的话,你只能使用 rune 类型。 -- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u -- 若该变量为 bool 类型,则名称应以 ``Has``, ``Is``, ``Can`` 或 - ``Allow`` 开头。例如:isExist ,hasConflict 。 -- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 - startDate 。 -- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 - ``apiClient``\ 。 -- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient +.. code:: ro -下面列举了一些常见的特有名词: + var name rune = '中' -:: +或许你已经发现,上面我们在定义字符时,不管是 byte 还是 rune +,我都是使用单引号,而没使用双引号。 - // A GonicMapper that contains a list of common initialisms taken from golang/lint - var LintGonicMapper = GonicMapper{ - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SSH": true, - "TLS": true, - "TTL": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XSRF": true, - "XSS": true, - } +对于从 Python 转过来的人,这里一定要注意了,在 Go 中单引号与 +双引号并不是等价的。 + +单引号用来表示字符,在上面的例子里,如果你使用双引号,就意味着你要定义一个字符串,赋值时与前面声明的前面会不一致,这样在编译的时候就会出错。 + +.. code:: go + + cannot use "A" (type string) as type byte in assignment + +上面我说了,byte 和 uint8 没有区别,rune 和 uint32 +没有区别,那为什么还要多出一个 byte 和 rune 类型呢? + +理由很简单,因为uint8 和 uint32 +,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 +byte 和 rune 这两个别名类型。 + +2. 字符串 +--------- + +字符串,可以说是大家很熟悉的数据类型之一。定义方法很简单 + +.. code:: go + + var mystr string = "hello" -**接口命名** +上面说的byte 和 rune +都是字符类型,若多个字符放在一起,就组成了字符串,也就是这里要说的 +string 类型。 -使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 +比如 ``hello`` ,对照 ascii +编码表,每个字母对应的编号是:104,101,108,108,111 .. code:: go - type helloWorld interface { - func Hello(); + import ( + "fmt" + ) + + func main() { + var mystr01 sting = "hello" + var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111} + fmt.Printf("mystr01: %s\n", mystr01) + fmt.Printf("mystr02: %s", mystr02) } - type SayHello helloWorld +输出如下,mystr01 和 mystr02 输出一样,说明了 string 的本质,其实是一个 +byte数组 + +:: + + mystr01: hello + mystr02: hello + +通过以上学习,我们知道字符分为 byte 和 rune,占用的大小不同。 -**注释规范** +这里来考一下大家,\ ``hello,中国`` 占用几个字节? -单行注释使用 ``//`` ,多行注释使用 ``/* comment */`` +要回答这个问题,你得知道 Go 语言的 string 是用 uft-8 +进行编码的,英文字母占用一个字节,而中文字母占用 3个字节,所以 +``hello,中国`` 的长度为 5+1+(3*2)= 12个字节。 .. code:: go - // go语言 + import ( + "fmt" + ) + + func main() { + var country string = "hello,中国" + fmt.Println(len(country)) + } + // 输出 + 12 + +以上虽然我都用双引号表示 一个字符串,但这并不是字符串的唯一表示方式。 + +除了双引号之外 ,你还可以使用反引号。 + +大多情况下,二者并没有区别,但如果你的字符串中有转义字符\ ``\`` +,这里就要注意了,它们是有区别的。 + +使用反引号号包裹的字符串,相当于 Python 中的 raw +字符串,会忽略里面的转义。 - /* - Go 语言 - Hello, World - */ +比如我想表示 ``\r\n`` 这个 +字符串,使用双引号是这样写的,这种叫解释型表示法 -- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 +.. code:: go -- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 + var mystr01 string = "\\r\\n" -- 包、函数、方法和类型的注释说明都是一个完整的句子。 +而使用反引号,就方便多了,所见即所得,这种叫原生型表示法 -- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 +.. code:: go -- 注释的单行长度不能超过 80 个字符。 + var mystr02 string = `\r\n` -- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 +他们的打印结果 都是一样的 -- 如果是特别复杂的包,可单独创建 doc.go 文件说明 +.. code:: go -- 类型的定义一般都以单数形式描述: + import ( + "fmt" + ) - .. code:: go + func main() { + var mystr01 string = "\\r\\n" + var mystr02 string = `\r\n` + fmt.Println(mystr01) + fmt.Println(mystr02) + } - // Request represents a request to run a command. type Request struct { ... + // output + \r\n + \r\n -- 如果为接口,则一般以以下形式描述: +如果你仍然想使用解释型的字符串,但是各种转义实在太麻烦了。你可以使用 fmt +的 ``%q`` 来还原一下。 - .. code:: go +.. code:: go - // FileInfo is the interface that describes a file and is returned by Stat and Lstat. - type FileInfo interface { ... + import ( + "fmt" + ) -- 函数与方法的注释需以函数或方法的名称作为开头: + func main() { + var mystr01 string = `\r\n` + fmt.Println(`\r\n`) + fmt.Printf("的解释型字符串是: %q", mystr01) + } - .. code:: go +输出如下 - // Post returns *BeegoHttpRequest with POST method. +.. code:: go -- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: + \r\n + 的解释型字符串是: "\\r\\n" - .. code:: go +同时反引号可以不写换行符(因为没法写)来表示一个多行的字符串。 - // Copy copies file from source to target path. - // It returns false and error when error occurs in underlying function calls. +.. code:: go -- 若函数或方法为判断类型(返回值主要为 ``bool`` 类型),则以 - `` returns true if`` 开头: + import ( + "fmt" + ) - .. code:: go + func main() { + var mystr01 string = `你好呀! + 我的公众号是: Go编程时光,欢迎大家关注` - // HasPrefix returns true if name has any string in given slice as prefix. - func HasPrefix(name string, prefixes []string) bool { ... + fmt.Println(mystr01) + } -特别注释 +输出如下 + +:: -- TODE:提醒维护人员此部分代码待完成 -- FIXME:提醒维护人员此处有BUG待修复 -- NOTE:维护人员要关注的一些问题说明 + 你好呀! + 我的公众号是: Go编程时光,欢迎大家关注 diff --git a/source/c09/c09_05.rst b/source/c09/c09_05.rst index f091b64..078769b 100644 --- a/source/c09/c09_05.rst +++ b/source/c09/c09_05.rst @@ -1,266 +1,228 @@ -9.5 Go语言数据类型 -================== +9.7 详解数据类型:数组与切片 +============================ -int 和 uint -~~~~~~~~~~~ +1. 数组 +------- -int 和 uint 的区别就在于一个 ``u``\ ,有 ``u`` 说明是无符号,没有 ``u`` -代表有符号。 +数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,所以在Go语言中很少直接使用数组。 -**解释这个符号的区别** +声明数组,并给该数组里的每个元素赋值(索引值的最小有效值和其他大多数语言一样是 +0,不是1) -以 ``int8`` 和 ``uint8`` 举例,8 代表 8个bit,能表示的数值个数有 2^8 = -256。 - -uint8 是无符号,能表示的都是正数,0-255,刚好256个数。 - -int8 -是有符号,既可以正数,也可以负数,那怎么办?对半分呗,-128-127,也刚好 -256个数。 - -int8 int16 int32 int64 -这几个类型,都有一个数字,表明了它们能表示的数值个数是固定的。 - -而 int 没有数值,说明它的大小,是可以变化的,那根据什么变化呢? - -- 当你在32位的系统下,int 和 uint 都占用 4个字节,也就是32位。 -- 若你在64位的系统下,int 和 uint 都占用 8个字节,也就是64位。 - -出于这个原因,在某些场景下,你应当避免使用 int 和 uint -,而使用更加精确的 int32 和 -int64,比如在二进制传输、读写文件的结构描述(为了保持文件的结构不会受到不同编译目标平台字节长度的影响) - -float32 和 float64 -~~~~~~~~~~~~~~~~~~ - -Go语言提供了两种精度的浮点数 float32 和 float64。 - -这些浮点数类型的取值范围可以从很微小到很巨大。浮点数取值范围的极限值可以在 -math 包中找到: +.. code:: go -- 常量 math.MaxFloat32 表示 float32 能取到的最大数值,大约是 3.4e38; -- 常量 math.MaxFloat64 表示 float64 能取到的最大数值,大约是 1.8e308; -- float32 和 float64 能表示的最小值分别为 1.4e-45 和 4.9e-324。 + // [3] 里的3 表示该数组的元素个数 + var arr [3]int + arr[0] = 1 + arr[1] = 2 + arr[2] = 3 -人家虽然能表示的数值很大,但精度位却没有那么大。 +声明并直接初始化数组 -- float32的精度只能提供大约6个十进制数的精度 -- float64的精度能提供大约15个十进制数的精度 +.. code:: go -这里的精度是什么意思呢? + // 第一种方法 + var arr [3]int = [3]int{1,2,3} -比如 10000018这个数,若是 -6个十进制的精度,表示成科学计数法,意味着小数点后面只有6位数是准确的,就是 -1.0000018 \* 10^7 + // 第二种方法 + arr := [3]int{1,2,3} -此时你会发现,刚刚好,满足精度要求,此时我们对其+1或者-1,由于都在精度范围内,计算机算得非常准。 +上面的 3 表示数组的元素个数 +,万一你哪天想往该数组中增加元素,你得对应修改这个数字,为了避免这种硬编码,你可以这样写,使用 +``...`` 让Go语言自己根据实际情况来分配空间。 .. code:: go - import "fmt" - var myfloat float32 = 10000018 - func main() { - fmt.Println("myfloat: ", myfloat) - fmt.Println("myfloat: ", myfloat+1) - } + arr := [...]int{1,2,3} -输出如下 +``[3]int`` 和 ``[4]int`` 虽然都是数组,但他们却是不同的类型,使用 fmt 的 +``%T`` 可以查得。 .. code:: go - myfloat: 1.0000018e+07 - myfloat: 1.0000019e+07 + import ( + "fmt" + ) -但如果给这个数加一位呢?变成 -100000187,同样表示成科学计数法,由于此时还是 6个进制精度,本应该表示成 -1.00000187\* 10^8 的,现在只能写成 1.00000187\* 10^8 -,小数点后面6位是完全准确的,但是从第七位开始要受第八位的影响,有时候会准确,有时候会不准确。 + func main() { + arr01 := [...]int{1, 2, 3} + arr02 := [...]int{1, 2, 3, 4} + fmt.Printf("%d 的类型是: %T\n", arr01, arr01) + fmt.Printf("%d 的类型是: %T", arr02, arr02) + } -这时你再对其 +1 或者 -1 -,由于已经超出精度范围,计算机开始无能为力,要犯错了。 +输出 如下 -.. code:: go +:: - import "fmt" - var myfloat float32 = 100000189 - func main() { - fmt.Println("myfloat: ", myfloat) - fmt.Println("myfloat: ", myfloat+1) - fmt.Println( myfloat == myfloat +1) - } + [1 2 3] 的类型是: [3]int + [1 2 3 4] 的类型是: [4]int -输出如下 +如果你觉得每次写 ``[3]int`` 有点麻烦,你可以为 ``[3]int`` +定义一个类型字面量,也就是别名类型。 -.. code:: go +使用 ``type`` +关键字可以定义一个类型字面量,后面只要你想定义一个容器大小为3,元素类型为int的数组 +,都可以使用这个别名类型。 - myfloat: 1.0000019e+08 - myfloat: 1.0000019e+08 - true +.. code:: go -由于精度的问题,就会出现这种很怪异的现象,\ ``myfloat == myfloat +1`` -会返回 ``true`` + import ( + "fmt" + ) -布尔类型 -~~~~~~~~ + func main() { + type arr3 [3]int -关于布尔值,其实没什么说的,无非就两个值。只是这两个值,在不同的语言里可能不同。 + myarr := arr3{1,2,3} + fmt.Printf("%d 的类型是: %T", myarr, myarr) + } -在 Python 中,真值用 True 表示,与 1 相等,假值用 False 表示,与 0 相等 +输出 如下 -.. code:: python +:: - >>> True == 1 - True - >>> False == 0 - True - >>> + [1 2 3] 的类型是: main.arr3 -而在 Go 中,真值用 true 表示,不但不与 1 -相等,并且更加严格,不同类型无法进行比较,而假值用 false 表示,同样与 0 -无法比较。如下图所示,Goland 直接波浪线提示类型不匹配,不能比较。 +2. 切片 +------- -|image0| +切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。 -在 Python 中使用 not 对布尔取值,而 Go 中使用 ``!`` 符号 +切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(意思是这是个左闭右开的区间) .. code:: go - import "fmt" + import ( + "fmt" + ) - var male bool = true - func main() { - fmt.Println( !male == false) + func main() { + myarr := [...]int{1, 2, 3} + fmt.Printf("%d 的类型是: %T", myarr[0:2], myarr[0:2]) } - // output: true +输出 如下 -一个 if 判断语句,有可能不只一个判断条件,在 Python 中是使用 ``and`` 和 -``or`` 来执行逻辑运算符, +:: -.. code:: python + [1 2] 的类型是: []int - >>> age = 15 - >>> gender = "male" - >>> - >>> gender == "male" and age >18 - False +切片的构造,有三种方式 -而在 Go 语言中,则使用 ``&&`` 和 ``||`` 。 +1. 对数组进行片段截取(上面例子已经展示:myarr[0:2],0是索引起始值,2是索引终止值,区间左半右开) -.. code:: go - - import "fmt" +2. 从头声明赋值(例子如下) - var age int = 15 - var gender string = "male" - func main() { - fmt.Println( gender == "male" && age > 18) - } + .. code:: go - // output: false + // 声明字符串切片 + var strList []string -字符串 -~~~~~~ + // 声明整型切片 + var numList []int -一个字符串是一个不可改变的字节序列,字符串可以包含任意的数据,但是通常是用来包含可读的文本,字符串是 -UTF-8 字符的一个序列(当字符为 ASCII 码表上的字符时则占用 1 -个字节,其它字符根据需要占用 2-4 个字节)。 + // 声明一个空切片 + var numListEmpty = []int{} -.. code:: python +3. 使用 make 函数构造,make 函数的格式:\ ``make( []Type, size, cap )`` - import "fmt" + 这个函数刚好指出了,一个切片具备的三个要素:类型(Type),长度(size),容量(cap) - var gender string = "male" - func main() { - fmt.Println(gender[0]) - fmt.Println(string(gender[0])) - } - -输出如下,其中 109 是 m 对应的 ASCII 编码,若要将其转成 m,直接使用 -string 函数即可 + .. code:: go -.. code:: go + import ( + "fmt" + ) - 109 - m + func main() { + a := make([]int, 2) + b := make([]int, 2, 10) + fmt.Println(a, b) + fmt.Println(len(a), len(b)) + fmt.Println(cap(a), cap(b)) + } -|image1| + 输出 如下 -用下标索引的方式取值 + :: -- 取第一个字节:str[0] -- 取第 i 个字节:str[i-1] -- 取最后一个字节:str(len(str)-1),和 Python 不同,Python 可以用 [-1] - 表求最后一个字节 + [0 0] [0 0] + 2 2 + 2 10 -**字符串拼接**\ ,直接使用 ``+`` 即可 +关于 len 和 cap 的概念,可能不好理解 ,这里举个例子: -.. code:: go +- 公司名,相当于字面量,也就是变量名。 - import "fmt" +- 公司里的所有工位,相当于已分配到的内存空间 - var str1 string = "Life " + "is " +- 公司里的员工,相当于元素。 - func main() { - str2 := str1 + "Short, " - str2 += "I Use Go" - fmt.Println(str2) - } +- cap 代表你这个公司最多可以容纳多少员工 - // output: Life is Short, I Use Go +- len 代表你这个公司当前有多少个员工 -于在Go语言中,使用双引号书写字符串的方式是字符串常见表达方式之一,但是其有一个缺点,不能用来表示多行字符串,在 -Python 中 可以使用 三个绰号来表示多行,而在 Go 比较特殊,它使用 反引号 +由于 切片是引用类型,所以你不对它进行赋值的话,它的零值(默认值)是 nil .. code:: go - var slogan string = ` - Life is Short - I Use Go. - ` + var myarr []int + fmt.Println(myarr == nil) + // true - func main() { - fmt.Println(slogan) - } +数组 与 切片 有相同点,它们都是可以容纳若干类型相同的元素的容器 -输出如下 +也有不同点,数组的容器大小固定,而切片本身是引用类型,它更像是 Python +中的 list ,我们可以对它 append 进行元素的添加。 .. code:: go - Life is Short - I Use Go. - -字符 -~~~~ + import ( + "fmt" + ) + + func main() { + myarr := []int{1} + // 追加一个元素 + myarr = append(myarr, 2) + // 追加多个元素 + myarr = append(myarr, 3, 4) + // 追加一个切片, ... 表示解包,不能省略 + myarr = append(myarr, []int{7, 8}...) + // 在第一个位置插入元素 + myarr = append([]int{0}, myarr...) + // 在中间插入一个切片(两个元素) + myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]...)...) + fmt.Println(myarr) + } -字符串是由一个一个的字符组成的。比如 -``hello``\ 由5个英文字符组成,\ ``中国`` 由两个中文字符组成。 +输出 如下 -但是不同的是,有的字符占用一个字节,有的占用多个字节。 +:: -因此在 Go 语言中,字符可以分为两种: + [0 1 2 3 4 5 6 7 8] -- 占用一个字节,叫做 byte ,或者 int8 类型,代表 ASCII - 的一个字符,都是占用8个bit。 +最后,给你留一道思考题,如下 这段代码 - 如下代码,三种表示方法,打印后,都是输出同一个字母:m +.. code:: go - .. code:: go + import ( + "fmt" + ) - import "fmt" + func main() { + var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + myslice := numbers4[4:6:8] + fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice)) - func main() { - var a byte = 109 // 10进制 - var b byte = '\155' // 8进制,这时不能使用双引号,\ 是固定写法 - var c byte = '\x6d' // 16 进制,这时也不能使用双引号,\x 是固定写法 - fmt.Printf("%c \n", a) - fmt.Printf("%c \n", b) - fmt.Printf("%c \n", c) - } + myslice = myslice[:cap(myslice)] + fmt.Printf("myslice的第四个元素为: %d", myslice[3]) + } -- 占用多个字节,叫做 rune 类型,代表一个 UTF-8 字符,由于 UTF-8 - 长度不固定,所以它占用的大小也不固定。 +为什么 myslice 的长度为2,却能访问到第四个元素 -.. |image0| image:: http://image.python-online.cn/20200106201856.png -.. |image1| image:: http://image.python-online.cn/20200106210502.png +:: + myslice为 [5 6], 其长度为: 2 + myslice的第四个元素为: 8 From c97a80bf404a1a0b60bcea6e39307e065c2523f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 23:06:29 +0800 Subject: [PATCH 008/147] =?UTF-8?q?add:=20=E7=94=9F=E6=88=90=20rst?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_23.rst | 60 ++++++++ source/c07/c07_17.rst | 12 ++ source/c09/c09_06.rst | 289 +++++++++++++++++++++++++++++++++++++ source/c09/c09_07.rst | 216 ++++++++++++++++++++++++++++ source/c09/c09_08.rst | 264 ++++++++++++++++++++++++++++++++++ source/c09/c09_09.rst | 266 ++++++++++++++++++++++++++++++++++ source/c09/c09_10.rst | 122 ++++++++++++++++ source/c09/c09_11.rst | 254 +++++++++++++++++++++++++++++++++ source/c09/c09_12.rst | 170 ++++++++++++++++++++++ source/c09/c09_13.rst | 172 ++++++++++++++++++++++ source/c09/c09_14.rst | 234 ++++++++++++++++++++++++++++++ source/c09/c09_15.rst | 238 +++++++++++++++++++++++++++++++ source/c09/c09_16.rst | 97 +++++++++++++ source/c09/c09_17.rst | 27 ++++ source/c09/c09_18.rst | 159 +++++++++++++++++++++ source/c09/c09_19.rst | 177 +++++++++++++++++++++++ source/c09/c09_23.rst | 324 ++++++++++++++++++++++++++++++++++++++++++ source/c09/c09_24.rst | 175 +++++++++++++++++++++++ source/c09/c09_25.rst | 183 ++++++++++++++++++++++++ source/c09/c09_27.rst | 6 + source/c09/c09_30.rst | 16 +++ source/c10/c10_01.rst | 155 ++++++++++++++++++++ 22 files changed, 3616 insertions(+) create mode 100644 source/c04/c04_23.rst create mode 100644 source/c07/c07_17.rst create mode 100644 source/c09/c09_06.rst create mode 100644 source/c09/c09_07.rst create mode 100644 source/c09/c09_08.rst create mode 100644 source/c09/c09_09.rst create mode 100644 source/c09/c09_10.rst create mode 100644 source/c09/c09_11.rst create mode 100644 source/c09/c09_12.rst create mode 100644 source/c09/c09_13.rst create mode 100644 source/c09/c09_14.rst create mode 100644 source/c09/c09_15.rst create mode 100644 source/c09/c09_16.rst create mode 100644 source/c09/c09_17.rst create mode 100644 source/c09/c09_18.rst create mode 100644 source/c09/c09_19.rst create mode 100644 source/c09/c09_23.rst create mode 100644 source/c09/c09_24.rst create mode 100644 source/c09/c09_25.rst create mode 100644 source/c09/c09_27.rst create mode 100644 source/c09/c09_30.rst create mode 100644 source/c10/c10_01.rst diff --git a/source/c04/c04_23.rst b/source/c04/c04_23.rst new file mode 100644 index 0000000..29414ac --- /dev/null +++ b/source/c04/c04_23.rst @@ -0,0 +1,60 @@ +4.23 电脑使用技巧 +================= + +1. 添加右键菜单 +--------------- + +参考 Sublime Text3的添加方法 + +在sublime的安装目录下新增一个文件:\ ``sublime_addright.inf``\ ,内容如下,然后右键选择安装。 + +:: + + [Version] + Signature="$Windows NT$" + + [DefaultInstall] + AddReg=SublimeText3 + + [SublimeText3] + hkcr,"*\\shell\\SublimeText3",,,"Edit with SublimeText3" + hkcr,"*\\shell\\SublimeText3\\command",,,"""%1%\sublime_text.exe"" ""%%1"" %%*" + hkcr,"Directory\shell\SublimeText3",,,"Edit with SublimeText3" + hkcr,"*\\shell\\SublimeText3","Icon",0x20000,"%1%\sublime_text.exe, 0" + hkcr,"Directory\shell\SublimeText3\command",,,"""%1%\sublime_text.exe"" ""%%1""" + +2. Rime五筆安裝方法 +------------------- + +下載地址:https://rime.im/download/ + +五筆配置:https://github.com/rime/rime-wubi + +下載下來是一個zip包,裏面都是五筆的配置文件。 + +:: + + wubi_pinyin.schema.yaml + wubi_trad.schema.yaml + wubi86.dict.yaml + wubi86.schema.yaml + +將這些文件複製到你的rime安裝目錄下的data文件夾下 + +然後在 data 目錄下編輯 default.yaml 添加,兩種五筆輸入方案,你任選一種 + +:: + + schema_list: + - schema: wubi_pinyin + - schema: wubi6 + +臨時修改的配置,無法立即生效,需要重新部署。 + +在你的安裝目錄下,找到\ ``WeaselDeployer.exe`` +點擊,選擇你想要添加的輸入方案,比如這裏選擇 +五筆-拼音,再點中,然後選擇皮膚,最後就會觸發重新部署,配置生效。 + +|image0| + +.. |image0| image:: http://image.python-online.cn/20200119143952.png diff --git a/source/c07/c07_17.rst b/source/c07/c07_17.rst new file mode 100644 index 0000000..a22756d --- /dev/null +++ b/source/c07/c07_17.rst @@ -0,0 +1,12 @@ +1.17 ansible 自定义 Jinja2 过滤器 +================================= + +在 ansible 中 Jinja2 的过滤器是作为插件存在于 ansible 的项目中。 + +它的代码位于 /usr/lib/python2.7/site-packages/ansible/plugins/filter + +观察其代码结构,不难看出 + +https://blog.oddbit.com/post/2019-04-25-writing-ansible-filter-plugins/ + +https://www.jianshu.com/p/ae74f5f39828 diff --git a/source/c09/c09_06.rst b/source/c09/c09_06.rst new file mode 100644 index 0000000..485dafe --- /dev/null +++ b/source/c09/c09_06.rst @@ -0,0 +1,289 @@ +9.6 详解数据类型:字典与布尔类型 +================================ + +1. 字典 +------- + +字典(Map 类型),是由若干个 ``key:value`` +这样的键值对映射组合在一起的数据结构。 + +它是哈希表的一个实现,这就要求它的每个映射里的key,都是唯一的,可以使用 +``==`` 和 ``!=`` 来进行判等操作,换句话说就是key必须是可哈希的。 + +什么叫可哈希的?简单来说,一个不可变对象,都可以用一个哈希值来唯一表示,这样的不可变对象,比如字符串类型的对象(可以说除了切片、 +字典,函数之外的其他内建类型都算)。 + +意思就是,你的 key 不能是切片,不能是字典,不能是函数。。 + +字典由key和value组成,它们各自有各自的类型。 + +在声明字典时,必须指定好你的key和value是什么类型的,然后使用 map +关键字来告诉Go这是一个字典。 + +.. code:: go + + map[KEY_TYPE]VALUE_TYPE + +声明初始化字典 +~~~~~~~~~~~~~~ + +三种声明并初始化字典的方法 + +.. code:: go + + // 第一种方法 + var scores map[string]int = map[string]int{"english": 80, "chinese": 85} + + // 第二种方法 + scores := map[string]int{"english": 80, "chinese": 85} + + // 第三种方法 + scores := make(map[string]int) + scores["english"] = 80 + scores["chinese"] = 85 + +要注意的是,第一种方法如果拆分成多步(声明、初始化、再赋值),和其他两种有很大的不一样了,相对会比较麻烦。 + +.. code:: go + + import "fmt" + + func main() { + // 声明一个名为 score 的字典 + var scores map[string]int + + // 未初始化的 score 的零值为nil,无法直接进行赋值 + if scores == nil { + // 需要使用 make 函数先对其初始化 + scores = make(map[string]int) + } + + // 经过初始化后,就可以直接赋值 + scores["chinese"] = 90 + fmt.Println(scores) + } + +**字典的相关操作** +~~~~~~~~~~~~~~~~~~ + +添加元素 + +.. code:: go + + scores["math"] = 95 + +更新元素,若key已存在,则直接更新value + +.. code:: go + + scores["math"] = 100 + +读取元素,直接使用 ``[key]`` 即可 ,如果 key +不存在,也不报错,会返回其value-type 的零值。 + +.. code:: go + + fmt.Println(scores["math"]) + +删除元素,使用 delete 函数,如果 key 不存在,delete +函数会静默处理,不会报错。 + +.. code:: go + + delete(scores, "math") + +当访问一个不存在的key时,并不会直接报错,而是会返回这个 value +的零值,如果 value的类型是int,就返回0。 + +.. code:: go + + package main + + import "fmt" + + func main() { + scores := make(map[string]int) + fmt.Println(scores["english"]) // 输出 0 + } + +判断 key 是否存在 +~~~~~~~~~~~~~~~~~ + +当key不存在,会返回value-type的零值 +,所以你不能通过返回的结果是否是零值来判断对应的 key 是否存在,因为 key +对应的 value 值可能恰好就是零值。 + +其实字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key +是否存在,若存在ok为true,若不存在,则ok为false + +.. code:: go + + import "fmt" + + func main() { + scores := map[string]int{"english": 80, "chinese": 85} + math, ok := scores["math"] + if ok { + fmt.Printf("math 的值是: %d", math) + } else { + fmt.Println("math 不存在") + } + } + +我们将上面的代码再优化一下 + +.. code:: go + + import "fmt" + + func main() { + scores := map[string]int{"english": 80, "chinese": 85} + if math, ok := scores["math"]; ok { + fmt.Printf("math 的值是: %d", math) + } else { + fmt.Println("math 不存在") + } + } + +**如何对字典进行循环** +~~~~~~~~~~~~~~~~~~~~~~ + +Go 语言中没有提供类似 Python 的 keys() 和 values() +这样方便的函数,想要获取,你得自己循环。 + +循环还分三种 + +1. 获取 key 和 value + +.. code:: go + + import "fmt" + + func main() { + scores := map[string]int{"english": 80, "chinese": 85} + + for subject, score := range scores { + fmt.Printf("key: %s, value: %d\n", subject, scores) + } + } + +2. 只获取key,这里注意不用占用符。 + +.. code:: go + + import "fmt" + + func main() { + scores := map[string]int{"english": 80, "chinese": 85} + + for subject := range scores { + fmt.Printf("key: %s\n", subject) + } + } + +3. 只获取 value,用一个占位符替代。 + +.. code:: go + + import "fmt" + + func main() { + scores := map[string]int{"english": 80, "chinese": 85} + + for _, score := range scores { + fmt.Printf("value: %d\n", score) + } + } + +2. 布尔类型 +----------- + +关于布尔值,无非就两个值:true 和 +false。只是这两个值,在不同的语言里可能不同。 + +在 Python 中,真值用 True 表示,与 1 相等,假值用 False 表示,与 0 相等 + +.. code:: python + + >>> True == 1 + True + >>> False == 0 + True + >>> + +而在 Go 中,真值用 true 表示,不但不与 1 +相等,并且更加严格,不同类型无法进行比较,而假值用 false 表示,同样与 0 +无法比较。 + +如下图所示,Goland 直接波浪线提示类型不匹配,不能比较。 + +|image0| + +Go 中确实不如 Python 那样灵活,bool 与 int +不能直接转换,如果要转换,需要你自己实现函数。 + +**bool 转 int** + +.. code:: go + + func bool2int(b bool) int { + if b { + return 1 + } + return 0 + } + +**int 转 bool** + +.. code:: go + + func int2bool(i int) bool { + return i != 0 + } + +在 Python 中使用 not 对逻辑值取反,而 Go 中使用 ``!`` 符号 + +.. code:: go + + import "fmt" + + var male bool = true + func main() { + fmt.Println( !male == false) + // 或者 + fmt.Println( male != false) + } + + // output: true + +一个 if 判断语句,有可能不只一个判断条件,在 Python 中是使用 ``and`` 和 +``or`` 来执行逻辑运算 + +.. code:: python + + >>> age = 15 + >>> gender = "male" + >>> + >>> gender == "male" and age >18 + False + +而在 Go 语言中,则使用 ``&&`` 表示\ ``且``\ ,用 ``||`` +表示\ ``或``\ ,并且有短路行为(即左边表达式已经可以确认整个表达式的值,那么右边将不会再被求值。 + +.. code:: go + + import "fmt" + + var age int = 15 + var gender string = "male" + func main() { + // && 两边的表达式都会执行 + fmt.Println( age > 18 && gender == "male") + // gender == "male" 并不会执行 + fmt.Println( age > 18 || gender == "male") + } + + // output: false + // output: true + +.. |image0| image:: http://image.python-online.cn/20200106201856.png diff --git a/source/c09/c09_07.rst b/source/c09/c09_07.rst new file mode 100644 index 0000000..c0edb7e --- /dev/null +++ b/source/c09/c09_07.rst @@ -0,0 +1,216 @@ +9.7 详解数据类型:指针 +====================== + +0. 什么是指针 +------------- + +当我们定义一个变量 name + +.. code:: go + + var name string = "Go编程时光" + +此时,name +是变量名,它只是编程语言中方便程序员编写和理解代码的一个标签。 + +当我们访问这个标签时,机算机会返回给我们它指向的内存地址里存储的值:\ ``Go编程时光``\ 。 + +出于某些需要,我们会将这个内存地址赋值给另一个变量名,通常叫做 +ptr(pointer的简写),而这个变量,我们称之为指针变量。 + +换句话说,指针变量(一个标签)的值是指针,也就是内存地址。 + +根据变量指向的值,是否是内存地址,我把变量分为两种: + +- 普通变量:存数据值本身 +- 指针变量:存值的内存地址 + +1. 指针的创建 +------------- + +指针创建有三种方法 + +**第一种方法** + +先定义对应的变量,再通过变量取得内存地址,创建指针 + +.. code:: go + + // 定义普通变量 + aint := 1 + // 定义指针变量 + ptr := &aint + +**第二种方法** + +先创建指针,分配好内存后,再给指针指向的内存地址写入对应的值。 + +.. code:: go + + // 创建指针 + astr := new(string) + // 给指针赋值 + *astr = "Go编程时光" + +**第三种方法** + +先声明一个指针变量,再从其他变量取得内存地址赋值给它 + +.. code:: go + + aint := 1 + var bint *int // 声明一个指针 + bint = &aint // 初始化 + +上面的三段代码中,指针的操作都离不开这两个符号: + +- ``&`` :从一个普通变量中取得内存地址 +- ``*``\ :当\ ``*``\ 在赋值操作值的右边,是从一个指针变量中取得变量值,当\ ``*``\ 在赋值操作值的左边,是指该指针指向的变量 + +通过下面这段代码,你可以熟悉这两个符号的用法 + +.. code:: go + + package main + + import "fmt" + + func main() { + aint := 1 // 定义普通变量 + ptr := &aint // 定义指针变量 + fmt.Println("普通变量存储的是:", aint) + fmt.Println("普通变量存储的是:", *ptr) + fmt.Println("指针变量存储的是:", &aint) + fmt.Println("指针变量存储的是:", ptr) + } + +输出如下 + +:: + + 普通变量存储的是: 1 + 普通变量存储的是: 1 + 指针变量存储的是: 0xc0000100a0 + 指针变量存储的是: 0xc0000100a0 + +要想打印指针指向的内存地址,方法有两种 + +.. code:: go + + // 第一种 + fmt.Printf("%p", ptr) + + // 第二种 + fmt.Println(ptr) + +2. 指针的类型 +------------- + +我们知道字符串的类型是 string,整型是int,那么指针如何表示呢? + +写段代码试验一下就知道了 + +.. code:: go + + package main + + import "fmt" + + func main() { + astr := "hello" + aint := 1 + abool := false + arune := 'a' + afloat := 1.2 + + fmt.Printf("astr 指针类型是:%T\n", &astr) + fmt.Printf("aint 指针类型是:%T\n", &aint) + fmt.Printf("abool 指针类型是:%T\n", &abool) + fmt.Printf("arune 指针类型是:%T\n", &arune) + fmt.Printf("afloat 指针类型是:%T\n", &afloat) + } + +输出如下,可以发现用 +``*``\ +所指向变量值的数据类型,就是对应的指针类型。 + +:: + + astr 指针类型是:*string + aint 指针类型是:*int + abool 指针类型是:*bool + arune 指针类型是:*int32 + afloat 指针类型是:*float64 + +所以若我们定义一个只接收指针类型的参数的函数,可以这么写 + +:: + + func mytest(ptr *int) { + fmt.Println(*ptr) + } + +3. 指针的零值 +------------- + +当指针声明后,没有进行初始化,其零值是 nil。 + +.. code:: go + + func main() { + a := 25 + var b *int // 声明一个指针 + + if b == nil { + fmt.Println(b) + b = &a // 初始化:将a的内存地址给b + fmt.Println(b) + } + } + +输出如下 + +:: + + + 0xc0000100a0 + +4. 指针与切片 +------------- + +切片与指针一样,都是引用类型。 + +如果我们想通过一个函数改变一个数组的值,有两种方法 + +1. 将这个数组的切片做为参数传给函数 +2. 将这个数组的指针做为参数传给函数 + +尽管二者都可以实现我们的目的,但是按照 Go +语言的使用习惯,建议使用第一种方法,因为第一种方法,写出来的代码会更加简洁,易读。具体你可以参数下面两种方法的代码实现 + +**使用切片** + +.. code:: go + + func modify(sls []int) { + sls[0] = 90 + } + + func main() { + a := [3]int{89, 90, 91} + modify(a[:]) + fmt.Println(a) + } + +**使用指针** + +.. code:: go + + func modify(arr *[3]int) { + (*arr)[0] = 90 + } + + func main() { + a := [3]int{89, 90, 91} + modify(&a) + fmt.Println(a) + } diff --git a/source/c09/c09_08.rst b/source/c09/c09_08.rst new file mode 100644 index 0000000..3c42144 --- /dev/null +++ b/source/c09/c09_08.rst @@ -0,0 +1,264 @@ +9.8 面向对象编程:结构体与继承 +============================== + +0. 什么是结构体? +----------------- + +在之前学过的数据类型中,数组与切片,只能存储同一类型的变量。若要存储多个类型的变量,就需要用到结构体,它是将多个容易类型的命令变量组合在一起的聚合数据类型。 + +每个变量都成为该结构体的成员变量。 + +可以理解为 Go语言 +的结构体struct和其他语言的class有相等的地位,但是Go语言放弃大量面向对象的特性,所有的Go语言类型除了指针类型外,都可以有自己的方法,提高了可扩展性。 + +在 Go 语言中没有没有 class 类的概念,只有 struct +结构体的概念,因此也没有继承,本篇文章,带你学习一下结构体相关的内容。 + +1. 定义结构体 +------------- + +声明结构体 + +.. code:: go + + type 结构体名 struct { + 属性名 属性类型 + 属性名 属性类型 + ... + } + +比如我要定义一个可以存储个人资料名为 Profile 的结构体,可以这么写 + +.. code:: go + + type Profile struct { + name string + age int + gender string + mother *Profile // 指针 + father *Profile // 指针 + } + +2. 定义方法 +----------- + +在 Go +语言中,我们无法在结构体内定义方法,那如何给一个结构体定义方法呢,答案是可以使用组合函数的方式来定义结构体方法。它和普通函数的定义方式有些不一样,比如下面这个方法 + +.. code:: go + + func (person Profile) FmtProfile() { + fmt.Printf("名字:%s\n", person.name) + fmt.Printf("年龄:%d\n", person.age) + fmt.Printf("性别:%s\n", person.gender) + } + +其中\ ``fmt_profile`` 是方法名,而\ ``(person Profile)`` :表示将 +fmt_profile 方法与 Profile 的实例绑定。我们把 Profile +称为方法的接收者,而 person 表示实例本身,它相当于 Python 中的 +self,在方法内可以使用 ``person.属性名`` 的方法来访问实例属性。 + +完整代码如下: + +.. code:: go + + package main + + import "fmt" + + // 定义一个名为Profile 的结构体 + type Profile struct { + name string + age int + gender string + mother *Profile // 指针 + father *Profile // 指针 + } + + // 定义一个与 Profile 的绑定的方法 + func (person Profile) FmtProfile() { + fmt.Printf("名字:%s\n", person.name) + fmt.Printf("年龄:%d\n", person.age) + fmt.Printf("性别:%s\n", person.gender) + } + + func main() { + // 实例化 + myself := Profile{name: "小明", age: 24, gender: "male"} + // 调用函数 + myself.FmtProfile() + } + +输出如下 + +:: + + 名字:小明 + 年龄:24 + 性别:male + +3. 方法的参数传递方式 +--------------------- + +上面定义方法的方式叫当你想要在方法内改变实例的属性的时候,必须使用指针做为方法的接收者。 + +.. code:: go + + package main + + import "fmt" + + // 声明一个 Profile 的结构体 + type Profile struct { + name string + age int + gender string + mother *Profile // 指针 + father *Profile // 指针 + } + + // 重点在于这个星号: * + func (person *Profile) increase_age() { + person.age += 1 + } + + func main() { + myself := Profile{name: "小明", age: 24, gender: "male"} + fmt.Printf("当前年龄:%d\n", myself.age) + myself.increase_age() + fmt.Printf("当前年龄:%d", myself.age) + } + +输出结果 如下,可以看到在方法内部对 age 的修改已经生效。你可以尝试去掉 +``*``\ ,使用值做为方法接收者,看看age是否会发生改变。 + +:: + + 当前年龄:24 + 当前年龄:25 + +至此,我们知道了两种定义方法的方式: + +- 以值做为方法接收者 +- 以指针做为方法接收者 + +那我们如何进行选择呢?以下几种情况,应当直接使用指针做为方法的接收者。 + +1. 你需要在方法内部改变结构体内容的时候 +2. 出于性能的问题,当结构体过大的时候 + +有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,建议都使用指针做为接收者。 + +不管你使用哪种方法定义方法,指针实例对象、值实例对象都可以直接调用,而没有什么约束。这一点Go语言做得非常好。 + +4. 结构体实现 “继承” +-------------------- + +为什么标题的继承,加了双引号,因为Go 语言本身并不支持继承。 + +但我们可以使用组合的方法,实现类似继承的效果。 + +在生活中,组合的例子非常多,比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起,最后才有了我们用的电脑。 + +同样的,在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。 + +现在这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。 + +.. code:: go + + type company struct { + companyName string + companyAddr string + } + + type staff struct { + name string + age int + gender string + position string + } + +若要将公司信息与公司职员关联起来,一般都会想到将 company +结构体的内容照抄到 staff 里。 + +.. code:: go + + type staff struct { + name string + age int + gender string + companyName string + companyAddr string + position string + } + +虽然在实现上并没有什么问题,但在你对同一公司的多个staff初始化的时候,都得重复初始化相同的公司信息,这做得并不好,借鉴继承的思想,我们可以将公司的属性都“继承”过来。 + +但是在 Go 中没有类的概念,只有组合,你可以将 company 这个 结构体嵌入到 +staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company +的所有属性了。 + +.. code:: go + + type staff struct { + name string + age int + gender string + position string + company // 匿名字段 + } + +来写个完整的程序验证一下。 + +.. code:: go + + package main + + import "fmt" + + type company struct { + companyName string + companyAddr string + } + + type staff struct { + name string + age int + gender string + position string + company + } + + func main() { + myCom := company{ + companyName: "Tencent", + companyAddr: "深圳市南山区", + } + staffInfo := staff{ + name: "小明", + age: 28, + gender: "男", + position: "云计算开发工程师", + company: myCom, + } + + fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName) + fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName) + } + +输出结果如下,可见\ ``staffInfo.companyName`` 和 +``staffInfo.company.companyName`` 的效果是一样的。 + +:: + + 小明 在 Tencent 工作 + 小明 在 Tencent 工作 + +5. 内部方法与外部方法 +--------------------- + +在 Go +语言中,函数名的首字母大小写非常重要,它被来实现控制对方法的访问权限。 + +- 当方法的首字母为大写时,这个方法对于所有包都是Public,其他包可以随意调用 +- 当方法的首字母为小写时,这个方法是Private,其他包是无法访问的。 diff --git a/source/c09/c09_09.rst b/source/c09/c09_09.rst new file mode 100644 index 0000000..1fc34ec --- /dev/null +++ b/source/c09/c09_09.rst @@ -0,0 +1,266 @@ +9.9 一篇文章理解 Go 里的函数 +============================ + +1. 关于函数 +----------- + +函数是基于功能或 +逻辑进行封装的可复用的代码结构。将一段功能复杂、很长的一段代码封装成多个代码片段(即函数),有助于提高代码可读性和可维护性。 + +在 Go 语言中,函数可以分为两种: + +- 带有名字的普通函数 +- 没有名字的匿名函数 + +由于 Go语言是编译型语言,所以函数编写的顺序是无关紧要的,它不像 Python +那样,函数在位置上需要定义在调用之前。 + +2. 函数的声明 +------------- + +函数的声明,使用 func 关键字,后面依次接 +``函数名``\ ,\ ``参数列表``\ ,\ ``返回值列表``\ ,\ ``用 {} 包裹的代码逻辑体`` + +:: + + func 函数名(形式参数列表)(返回值列表){ + 函数体 + } + +- 形式参数列表描述了函数的参数名以及参数类型,这些参数作为局部变量,其值由参数调用者提供 + +- 返回值列表描述了函数返回值的变量名以及类型,如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。 + +举个例子,定义一个 sum 函数,接收两个 int +类型的参数,在运行中,将其值分别赋值给 +a,b,并规定必须返回一个int类型的值 。 + +.. code:: go + + func sum(a int, b int) (int){ + return a + b + } + func main() { + fmt.Println(sum(1,2)) + } + +3. 函数实现可变参数 +------------------- + +上面举的例子,参数个数都是固定的,这很好理解 +,指定什么类型的参数就传入什么类型的变量,数量上,不能多一个,也不能少一个。(好像没有可选参数)。 + +在 Python 中我们可以使用 \*args 和 \**kw ,还实现可变参数的函数。 + +可变参数分为几种: + +- 多个类型一致的参数 +- 多个类型不一致的参数 + +多个类型一致的参数 +~~~~~~~~~~~~~~~~~~ + +首先是多个类型一致的参数。 + +这边定义一个可以对多个数值进行求和的函数, + +使用 +``...int``\ ,表示一个元素为int类型的切片,用来接收调用者传入的参数。 + +.. code:: go + + // 使用 ...类型,表示一个元素为int类型的切片 + func sum(args ...int) int { + var sum int + for _, v := range args { + sum += v + } + return sum + } + func main() { + fmt.Println(sum(1, 2, 3)) + } + + // output: 6 + +其中 ``...`` 是 Go +语言为了方便程序员写代码而实现的语法糖,如果该函数下会多个类型的函数,这个语法糖必须得是最后一个参数。 + +同时这个语法糖,只能在定义函数时使用。 + +多个类型不一致的参数 +~~~~~~~~~~~~~~~~~~~~ + +上面那个例子中,我们的参数类型都是 +int,如果你希望传多个参数且这些参数的类型都不一样,可以指定类型为 +``...interface{}``\ ,然后再遍历。 + +比如下面这段代码,是Go语言标准库中 fmt.Printf() 的函数原型: + +.. code:: go + + import "fmt" + func MyPrintf(args ...interface{}) { + for _, arg := range args { + switch arg.(type) { + case int: + fmt.Println(arg, "is an int value.") + case string: + fmt.Println(arg, "is a string value.") + case int64: + fmt.Println(arg, "is an int64 value.") + default: + fmt.Println(arg, "is an unknown type.") + } + } + } + + func main() { + var v1 int = 1 + var v2 int64 = 234 + var v3 string = "hello" + var v4 float32 = 1.234 + MyPrintf(v1, v2, v3, v4) + } + +在某些情况下,我们需要定义一个参数个数可变的函数,具体传入几个参数,由调用者自己决定,但不管传入几个参数,函数都能够处理。 + +比如这边实现一个累加 + +.. code:: go + + func myfunc(args ...int) { + for _, arg := range args { + fmt.Println(arg) + } + } + +4. 多个可变参数函数传递参数 +--------------------------- + +上面提到了可以使用 ``...`` +来接收多个参数,除此之外,它还有一个用法,就是用来解序列,将函数的可变参数(一个切片)一个一个取出来,传递给另一个可变参数的函数,而不是传递可变参数变量本身。 + +同样这个用法,也只能在给函数传递参数里使用。 + +例子如下: + +.. code:: go + + import "fmt" + + func sum(args ...int) int { + var result int + for _, v := range args { + result += v + } + return result + } + + func Sum(args ...int) int { + // 利用 ... 来解序列 + result := sum(args...) + return result + } + func main() { + fmt.Println(sum(1, 2, 3)) + } + +5. 函数的返回值 +--------------- + +Go语言中的函数,在你定义的时候,就规定了此函数 + +1. 有没有返回值? + + 当没有指明返回值的类型时, 函数体不能有 return,Go并不像 Python + 那样没有return,就默认返回None + +2. 返回几个值? + + Go 支持一个函数返回多个值 + + .. code:: go + + func double(a int) (int, int) { + b := a * 2 + return a, b + } + func main() { + // 接收参数用逗号分隔 + a, b := double(2) + fmt.Println(a, b) + } + +3. 怎么返回值? + + Go支持返回带有变量名的值 + + .. code:: go + + func double(a int) (b int) { + // 不能使用 := ,因为在返回值哪里已经声明了为int + b = a * 2 + // 不需要指明写回哪个变量,在返回值类型那里已经指定了 + return + } + func main() { + fmt.Println(double(2)) + } + // output: 4 + +6. 方法与函数 +------------- + +方法,在上一节《\ `08. +面向对象编程:结构体与继承 `__\ 》里已经介绍过了,它的定义与函数有些不同,你可以点击前面的标题进行交叉学习。 + +那 **方法和函数有什么区别?** +为防会有朋友第一次接触面向对象,这里多嘴一句。 + +方法,是一种特殊的函数。当你一个函数和对象/结构体进行绑定的时候,我们就称这个函数是一个方法。 + +7. 匿名函数的使用 +----------------- + +所谓匿名函数,就是没有名字的函数,它只有函数逻辑体,而没有函数名。 + +定义的格式如下 + +.. code:: go + + func(参数列表)(返回参数列表){ + 函数体 + } + +一个名字实际上并没有多大区别,所有使用匿名函数都可以改成普通有名函数,那么什么情况下,会使用匿名函数呢? + +定义变量名,是一个不难但是还费脑子的事情,对于那到只使用一次的函数,是没必要拥有姓名的。这才有了匿名函数。 + +有了这个背景,决定了匿名函数只有拥有短暂的生命,一般都是定义后立即使用。 + +就像这样,定义后立马执行(这里只是举例,实际代码没有意义)。 + +.. code:: go + + func(data int) { + fmt.Println("hello", data) + }(100) + +亦或是做为回调函数使用 + +.. code:: go + + // 第二个参数为函数 + func visit(list []int, f func(int)) { + for _, v := range list { + // 执行回调函数 + f(v) + } + } + func main() { + // 使用匿名函数直接做为参数 + visit([]int{1, 2, 3, 4}, func(v int) { + fmt.Println(v) + }) + } diff --git a/source/c09/c09_10.rst b/source/c09/c09_10.rst new file mode 100644 index 0000000..776b3f5 --- /dev/null +++ b/source/c09/c09_10.rst @@ -0,0 +1,122 @@ +9.10 Go语言流程控制:if-else +============================ + +1. 条件语句模型 +--------------- + +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: + +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 + +今天先来讲讲 if-else 条件语句 + +Go 里的条件语句模型是这样的 + +.. code:: go + + if 条件 1 { + 分支 1 + } else if 条件 2 { + 分支 2 + } else if 条件 ... { + 分支 ... + } else { + 分支 else + } + +Go编译器,对于 ``{`` 和 ``}`` 的位置有严格的要求,它要求 else if (或 +else)和 两边的花括号,必须在同一行。 + +由于 Go是 强类型,所以要求你条件表达式必须严格返回布尔型的数据(nil 和 0 +和 1 都不行,具体可查看《详解数据类型:字典与布尔类型》)。 + +对于这个模型,分别举几个例子来看一下。 + +2. 单分支判断 +------------- + +只有一个 if ,没有 else + +.. code:: go + + import "fmt" + + func main() { + age := 20 + if age > 18 { + fmt.Println("已经成年了") + } + } + +如果条件里需要满足多个条件,可以使用 ``&&`` 和 ``||`` + +- ``&&``\ :表示且,左右都需要为true,最终结果才能为 true,否则为 false +- ``||``\ :表示或,左右只要有一个为true,最终结果即为true,否则 为 + false + +.. code:: go + + import "fmt" + + func main() { + age := 20 + gender := "male" + if (age > 18 && gender == "male") { + fmt.Println("是成年男性") + } + } + +3. 多分支判断 +------------- + +if - else + +.. code:: go + + import "fmt" + + func main() { + age := 20 + if age > 18 { + fmt.Println("已经成年了") + } else { + fmt.Println("还未成年") + } + } + +if - else if - else + +.. code:: go + + import "fmt" + + func main() { + age := 20 + if age > 18 { + fmt.Println("已经成年了") + } else if age >12 { + fmt.Println("已经是青少年了") + } else { + fmt.Println("还不是青少年") + } + } + +4. 高级写法 +----------- + +在 if +里可以允许先运行一个表达式,取得变量后,再对其进行判断,比如第一个例子里代码也可以写成这样 + +.. code:: go + + import "fmt" + + func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } + } diff --git a/source/c09/c09_11.rst b/source/c09/c09_11.rst new file mode 100644 index 0000000..39a7c63 --- /dev/null +++ b/source/c09/c09_11.rst @@ -0,0 +1,254 @@ +9.11 Go语言流程控制:switch-case +================================ + +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: + +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 + +上一篇讲了 if -else 条件语句,今天先来讲讲 switch - case 选择语句。 + +0. 语句模型 +----------- + +Go 里的选择语句模型是这样的 + +.. code:: go + + switch 表达式 { + case 表达式1: + 代码块 + case 表达式2: + 代码块 + case 表达式3: + 代码块 + case 表达式4: + 代码块 + case 表达式5: + 代码块 + default: + 代码块 + } + +拿 switch 后的表达式分别和 case 后的表达式进行对比,只要有一个 case +满足条件,就会执行对应的代码块,然后直接退出 switch - case ,如果 +一个都没有满足,才会执行 default 的代码块。 + +1. 最简单的示例 +--------------- + +switch 后接一个你要判断变量 ``education`` (学历),然后 case 会拿这个 +变量去和它后面的表达式(可能是常量、变量、表达式等)进行判等。 + +如果相等,就执行相应的代码块。如果不相等,就接着下一个 case。 + +.. code:: go + + import "fmt" + + func main() { + education := "本科" + + switch education { + case "博士": + fmt.Println("我是博士") + case "研究生": + fmt.Println("我是研究生") + case "本科": + fmt.Println("我是本科生") + case "大专": + fmt.Println("我是大专生") + case "高中": + fmt.Println("我是高中生") + default: + fmt.Println("学历未达标..") + } + } + +输出如下 + +:: + + 我是本科生 + +2. 一个 case 多个条件 +--------------------- + +case 后可以接多个多个条件,多个条件之间是 ``或`` 的关系,用逗号相隔。 + +.. code:: go + + import "fmt" + + func main() { + month := 2 + + switch month { + case 3, 4, 5: + fmt.Println("春天") + case 6, 7, 8: + fmt.Println("夏天") + case 9, 10, 11: + fmt.Println("秋天") + case 12, 1, 2: + fmt.Println("冬天") + default: + fmt.Println("输入有误...") + } + } + +输出如下 + +:: + + 冬天 + +3. case 条件常量不能重复 +------------------------ + +当 case 后接的是常量时,该常量只能出现一次。 + +以下两种情况,在编译时,都会报错: duplicate case “male” in switch + +**错误案例一** + +.. code:: go + + gender := "male" + + switch gender { + case "male": + fmt.Println("男性") + // 与上面重复 + case "male": + fmt.Println("男性") + case "female": + fmt.Println("女性") + } + +**错误案例二** + +.. code:: go + + gender := "male" + + switch gender { + case "male", "male": + fmt.Println("男性") + case "female": + fmt.Println("女性") + } + +4. switch 后可接函数 +-------------------- + +switch 后面可以接一个函数,只要保证 case 后的值类型与函数的返回值 +一致即可。 + +.. code:: go + + import "fmt" + + // 判断一个同学是否有挂科记录的函数 + // 返回值是布尔类型 + func getResult(args ...int) bool { + for _, i := range args { + if i < 60 { + return false + } + } + return true + } + + func main() { + chinese := 80 + english := 50 + math := 100 + + switch getResult(chinese, english, math) { + // case 后也必须 是布尔类型 + case true: + fmt.Println("该同学所有成绩都合格") + case false: + fmt.Println("该同学有挂科记录") + } + } + +5. switch 可不接表达式 +---------------------- + +switch 后可以不接任何变量、表达式、函数。 + +当不接任何东西时,switch - case 就相当于 if - elseif - else + +.. code:: go + + score := 30 + + switch { + case score >= 95 && score <= 100: + fmt.Println("优秀") + case score >= 80: + fmt.Println("良好") + case score >= 60: + fmt.Println("合格") + case score >= 0: + fmt.Println("不合格") + default: + fmt.Println("输入有误...") + } + +6. switch 的穿透能力 +-------------------- + +正常情况下 switch - case 的执行顺序是:只要有一个 case +满足条件,就会直接退出 switch - case ,如果 一个都没有满足,才会执行 +default 的代码块。 + +但是有一种情况是例外。 + +那就是当 case 使用关键字 ``fallthrough`` 开启穿透能力的时候。 + +.. code:: go + + s := "hello" + switch { + case s == "hello": + fmt.Println("hello") + fallthrough + case s != "world": + fmt.Println("world") + } + +代码输出如下: + +:: + + hello + world + +需要注意的是,fallthrough +只能穿透一层,意思是它只给你一次再判断case的机会,不管你有没有匹配上,都要退出了。 + +.. code:: go + + s := "hello" + switch { + case s == "hello": + fmt.Println("hello") + fallthrough + case s == "xxxx": + fmt.Println("xxxx") + case s != "world": + fmt.Println("world") + } + +输出如下,并不会输出 ``world``\ (即使它符合条件) + +:: + + hello + xxxx diff --git a/source/c09/c09_12.rst b/source/c09/c09_12.rst new file mode 100644 index 0000000..1a8b116 --- /dev/null +++ b/source/c09/c09_12.rst @@ -0,0 +1,170 @@ +9.11 Go语言流程控制:for +======================== + +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: + +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 + +上一篇讲了switch - case 选择语句,今天先来讲讲 for 循环语句。 + +0. 语句模型 +----------- + +这是 for 循环的基本模型。 + +:: + + for [condition | ( init; condition; increment ) | Range] + { + statement(s); + } + +可以看到 for 后面,可以接三种类型的表达式。 + +1. 接一个条件表达式 +2. 接三个表达式 +3. 接一个 range 表达式 + +但其实还有第四种 + +4. 不接表达式 + +1. 接一个条件表达式 +------------------- + +这个例子会打印 1 到 5 的数值。 + +.. code:: go + + a := 1 + for a <= 5 { + fmt.Println(a) + a ++ + } + +输出如下 + +:: + + 1 + 2 + 3 + 4 + 5 + +2. 接三个表达式 +--------------- + +for 后面,紧接着三个表达式,使用 ``;`` 分隔。 + +这三个表达式,各有各的用途 + +- 第一个表达式:初始化控制变量,在整个循环生命周期内,只运行一次; +- 第二个表达式:设置循环控制条件,当返回true,继续循环,返回false,结束循环; +- 第三个表达式:每次循完开始(除第一次)时,给控制变量增量或减量。 + +这边的例子和上面的例子,是等价的。 + +.. code:: go + + import "fmt" + + func main() { + for i := 1; i <= 5; i++ { + fmt.Println(i) + } + } + +输出如下 + +:: + + 1 + 2 + 3 + 4 + 5 + +2. 不接表达式:无限循环 +----------------------- + +在 Go 语言中,没有 while 循环,如果要实现无限循环,也完全可以 for +来实现。 + +当你不加任何的判断条件时, 就相当于你每次的判断都为 +true,程序就会一直处于运行状态,但是一般我们并不会让程序处于死循环,在满足一定的条件下,可以使用关键字 +``break`` 退出循环体,也可以使用 ``continue`` 直接跳到下一循环。 + +下面两种写法都是无限循环的写法。 + +.. code:: go + + for { + 代码块 + } + + // 等价于 + for ;; { + 代码块 + } + +举个例子 + +.. code:: go + + import "fmt" + + func main() { + var i int = 1 + for { + if i > 5 { + break + } + fmt.Printf("hello, %d\n", i) + i++ + } + } + +输出如下 + +:: + + hello, 1 + hello, 2 + hello, 3 + hello, 4 + hello, 5 + +3. 接 for-range 语句 +-------------------- + +遍历一个可迭代对象,是一个很常用的操作。在 Go 可以使用 for-range +的方式来实现。 + +range 后可接数组、切片,字符串等 + +由于 range 会返回两个值:索引和数据,若你后面的代码用不到索引,需要使用 +``_`` 表示 。 + +.. code:: go + + import "fmt" + + func main() { + myarr := [...]string{"world", "python", "go"} + for _, item := range myarr { + fmt.Printf("hello, %s\n", item) + } + } + +输出如下 + +:: + + hello, world + hello, python + hello, go diff --git a/source/c09/c09_13.rst b/source/c09/c09_13.rst new file mode 100644 index 0000000..f938ecc --- /dev/null +++ b/source/c09/c09_13.rst @@ -0,0 +1,172 @@ +9.13 Go语言流程控制:goto 无条件跳转 +==================================== + +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: + +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 + +前面三种,我已经都讲过了,今天要讲讲 goto 的无条件跳转。 + +很难想象在 Go 居然会保留 goto,因为很多人不建议使用 +goto,所以在一些编程语言中甚至直接取消了 goto。 + +我感觉 Go +既然保留,一定有人家的理由,只是我目前还没感受到。不管怎样,咱还是照常学习吧。 + +0. 基本模型 +----------- + +``goto`` 顾言思义,是跳转的意思。 + +goto 后接一个标签,这个标签的意义是告诉 Go程序下一步要执行哪里的代码。 + +所以这个标签如何放置,放置在哪里,是 goto 里最需要注意的。 + +.. code:: go + + goto 标签; + ... + ... + 标签: 表达式; + +1. 最简单的示例 +--------------- + +``goto`` 可以打破原有代码执行顺序,直接跳转到某一行执行代码。 + +.. code:: go + + import "fmt" + + func main() { + + goto flag + fmt.Println("B") + flag: + fmt.Println("A") + + } + +执行结果,并不会输出 B ,而只会输出 A + +:: + + A + +2. 如何使用? +------------- + +``goto`` 语句通常与条件语句配合使用。可用来实现条件转移, +构成循环,跳出循环体等功能。 + +这边举一个例子,用 ``goto`` 的方式来实现一个打印 1到5 的循环。 + +.. code:: go + + import "fmt" + + func main() { + i := 1 + flag: + if i <= 5 { + fmt.Println(i) + i++ + goto flag + } + } + +输出如下 + +:: + + 1 + 2 + 3 + 4 + 5 + +再举个例子,使用 goto 实现 类型 break 的效果。 + +.. code:: go + + import "fmt" + + func main() { + i := 1 + for { + if i > 5 { + goto flag + } + fmt.Println(i) + i++ + } + flag: + } + +输出如下 + +:: + + 1 + 2 + 3 + 4 + 5 + +最后再举个例子,使用 goto 实现 类型 continue的效果,打印 1到10 +的所有偶数。 + +.. code:: go + + import "fmt" + + func main() { + i := 1 + flag: + for i <= 10 { + if i%2 == 1 { + i++ + goto flag + } + fmt.Println(i) + i++ + } + } + +输出如下 + +:: + + 2 + 4 + 6 + 8 + 10 + +3. 注意事项 +----------- + +goto语句与标签之间不能有变量声明,否则编译错误。 + +.. code:: go + + import "fmt" + + func main() { + fmt.Println("start") + goto flag + var say = "hello oldboy" + fmt.Println(say) + flag: + fmt.Println("end") + } + +编译错误 + +:: + + .\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6 diff --git a/source/c09/c09_14.rst b/source/c09/c09_14.rst new file mode 100644 index 0000000..f0b31bd --- /dev/null +++ b/source/c09/c09_14.rst @@ -0,0 +1,234 @@ +9.14 Go语言流程控制:defer 延迟语句 +=================================== + +Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: + +- if - else 条件语句 +- switch - case 选择语句 +- for - range 循环语句 +- goto 无条件跳转语句 +- defer 延迟执行 + +今天是最后一篇讲控制流程了,内容是 defer +延迟语句,这个在其他编程语言里好像没有见到。应该是属于 Go +语言里的独有的关键字,但即使如此,阅读后这篇文章后,你可以发现 defer +在其他编程语言里的影子。 + +1. 延迟调用 +----------- + +defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个 ``xxx`` +函数的调用延迟到当前函数执行完后再执行。 + +.. code:: go + + defer xxx() + +这是一个很简单的例子,可以很快帮助你理解 defer 的使用效果。 + +.. code:: go + + import "fmt" + + func myfunc() { + fmt.Println("B") + } + + func main() { + defer myfunc() + fmt.Println("A") + } + +输出如下 + +:: + + A + B + +当然了,对于上面这个例子可以简写为成如下,输出结果是一致的 + +.. code:: go + + import "fmt" + + func main() { + defer fmt.Println("B") + fmt.Println("A") + } + +2. 即时求值的变量快照 +--------------------- + +使用 defer +只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响。 + +比如这边的例子 + +.. code:: go + + import "fmt" + + func main() { + name := "go" + defer fmt.Println(name) // 输出: go + + name = "python" + fmt.Println(name) // 输出: python + } + +输出如下,可见给 name 重新赋值为 ``python``\ ,后续调用 defer +的时候,仍然使用未重新赋值的变量值,就好在 defer +这里,给所有的这是做了一个快照一样。 + +:: + + python + go + +3. 多个defer 反序调用 +--------------------- + +当我们在一个函数里使用了 多个defer,那么这些defer 的执行函数是如何的呢? + +做个试验就知道了 + +.. code:: go + + import "fmt" + + func main() { + name := "go" + defer fmt.Println(name) // 输出: go + + name = "python" + defer fmt.Println(name) // 输出: python + + name = "java" + fmt.Println(name) + } + +输出如下,可见 多个defer 是反序调用的,有点类似栈一样,后进先出。 + +:: + + java + python + go + +3. defer 与 return 孰先孰后 +--------------------------- + +至此,defer 还算是挺好理解的。在一般的使用上,是没有问题了。 + +在这里提一个稍微复杂一点的问题,defer 和 return 到底是哪个先调用? + +使用下面这段代码,可以很容易的观察出来 + +.. code:: go + + import "fmt" + + var name string = "go" + + func myfunc() string { + defer func() { + name = "python" + }() + + fmt.Printf("myfunc 函数里的name:%s\n", name) + return name + } + + func main() { + myname := myfunc() + fmt.Printf("main 函数里的name: %s\n", name) + fmt.Println("main 函数里的myname: ", myname) + } + +输出如下 + +:: + + myfunc 函数里的name:go + main 函数里的name: python + main 函数里的myname: go + +来一起理解一下这段代码,第一行很直观,name 此时还是全局变量,值还是go + +第二行也不难理解,在 defer 里改变了这个全局变量,此时name的值已经变成了 +python + +重点在第三行,为什么输出的是 go ? + +解释只有一个,那就是 defer 是return 后才调用的。所以在执行 defer +前,myname 已经被赋值成 go 了。 + +4. 为什么要有 defer? +--------------------- + +看完上面的例子后,不知道你是否和我一样,对这个defer的使用效果感到熟悉?貌似在 +Python 也见过类似的用法。 + +虽然 Python 中没有 defer ,但是它有 with 上下文管理器。我们知道在 Python +中可以使用 defer 实现对资源的管理。最常用的例子就是文件的打开关闭。 + +你可能会有疑问,这也没什么意义呀,我把这个放在 defer 执行的函数放在 +return 那里执行不就好了。 + +固然可以,但是当一个函数里有多个 return +时,你得多调用好多次这个函数,代码就臃肿起来了。 + +若是没有 defer,你可以写出这样的代码 + +.. code:: go + + func f() { + r := getResource() //0,获取资源 + ...... + if ... { + r.release() //1,释放资源 + return + } + ...... + if ... { + r.release() //2,释放资源 + return + } + ...... + if ... { + r.release() //3,释放资源 + return + } + ...... + r.release() //4,释放资源 + return + } + +使用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer +后的函数。 + +.. code:: go + + func f() { + r := getResource() //0,获取资源 + + defer r.release() //1,释放资源 + ...... + if ... { + ... + return + } + ...... + if ... { + ... + return + } + ...... + if ... { + ... + return + } + ...... + return + } diff --git a/source/c09/c09_15.rst b/source/c09/c09_15.rst new file mode 100644 index 0000000..fc933dd --- /dev/null +++ b/source/c09/c09_15.rst @@ -0,0 +1,238 @@ +9.15 面向对象编程:接口与多态 +============================= + +0. 接口是什么? +--------------- + + 这一段摘自 Go语言中文网 + +在面向对象的领域里,接口一般这样定义:\ **接口定义一个对象的行为**\ 。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定。 + +在 Go 语言中,接口就是方法签名(Method +Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。这与面向对象编程(OOP)的说法很类似。\ **接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法**\ 。 + +1. 如何定义接口 +--------------- + +使用 type 关键字来定义接口。 + +如下代码,定义了一个电话接口,接口要求必须实现 call 方法。 + +.. code:: go + + type Phone interface { + call() + } + +2. 如何实现接口 +--------------- + +如果有一个类型/结构体,实现了一个接口要求的所有方法,这里 Phone 接口只有 +call方法,所以只要实现了 call 方法,我们就可以称它实现了 Phone 接口。 + +意思是如果有一台机器,可以给别人打电话,那么我们就可以把它叫做电话。 + +这个接口的实现是隐式的,不像 JAVA 中要用 implements 显示说明。 + +继续上面电话的例子,我们先定义一个 Nokia 的结构体,而它实现了 call +的方法,所以它也是一台电话。 + +.. code:: go + + type Nokia struct { + name string + } + + // 接收者为 Nokia + func (phone Nokia) call() { + fmt.Println("我是 Nokia,是一台电话") + } + +3. 接口实现多态 +--------------- + +鸭子类型(Duck +typing)的定义是,只要你长得像鸭子,叫起来也像鸭子,那我认为你就是一只鸭子。 + +举个通俗的例子 + +**什么样子的人可以称做老师呢?** + +不同的人标准不一,有的人认为必须有一定的学历,有的人认为必须要有老师资格证。 + +而我认为只要能育人,能给传授给其他人知识的,都可以称之为老师。 + +而不管你教的什么学科?是体育竞技,还是教人烹饪。 + +也不管你怎么教?是在教室里手执教教鞭、拿着粉笔,还是追求真实,直接实战演练。 + +通通不管。 + +这就一个接口(老师)下,在不同对象(人)上的不同表现。这就是多态。 + +**在 Go 语言中,是通过接口来实现的多态。** + +这里以商品接口来写一段代码演示一下。 + +先定义一个商品(Good)的接口,意思是一个类型或者结构体,只要实现了\ ``settleAccount()`` +和 ``orderInfo()`` 两个方法,那这个类型/结构体就是一个商品。 + +.. code:: go + + type Good interface { + settleAccount() int + orderInfo() string + } + +然后我们定义两个结构体,分别是手机和赠品。 + +.. code:: go + + type Phone struct { + name string + quantity int + price int + } + + type FreeGift struct { + name string + quantity int + price int + } + +然后分别为他们实现 Good 接口的两个方法 + +.. code:: go + + // Phone + func (phone Phone) settleAccount() int { + return phone.quantity * phone.price + } + func (phone Phone) orderInfo() string{ + return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + + phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" + } + + // FreeGift + func (gift FreeGift) settleAccount() int { + return 0 + } + func (gift FreeGift) orderInfo() string{ + return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + + gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" + } + +实现了 Good +接口要求的两个方法后,手机和赠品在Go语言看来就都是商品(Good)类型了。 + +这里候,我挑选了两件商品(实例化),分别是手机和耳机(赠品,不要钱) + +.. code:: go + + iPhone := Phone{ + name: "iPhone", + quantity: 1, + price: 8000, + } + earphones := FreeGift{ + name: "耳机", + quantity: 1, + price: 200, + } + +然后创建一个购物车(也就是类型为 Good的切片),来存放这些商品。 + +.. code:: go + + goods := []Good{iPhone, earphones} + +最后,定义一个方法来计算购物车里的订单金额 + +.. code:: go + + func calculateAllPrice(goods []Good) int { + var allPrice int + for _,good := range goods{ + fmt.Println(good.orderInfo()) + allPrice += good.settleAccount() + } + return allPrice + } + +完整代码,我贴在下面,供你参考。 + +.. code:: go + + package main + + import ( + "fmt" + "strconv" + ) + + // 定义一个接口 + type Good interface { + settleAccount() int + orderInfo() string + } + + type Phone struct { + name string + quantity int + price int + } + + func (phone Phone) settleAccount() int { + return phone.quantity * phone.price + } + func (phone Phone) orderInfo() string{ + return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + + phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" + } + + type FreeGift struct { + name string + quantity int + price int + } + + func (gift FreeGift) settleAccount() int { + return 0 + } + func (gift FreeGift) orderInfo() string{ + return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + + gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" + } + + func calculateAllPrice(goods []Good) int { + var allPrice int + for _,good := range goods{ + fmt.Println(good.orderInfo()) + allPrice += good.settleAccount() + } + return allPrice + } + func main() { + iPhone := Phone{ + name: "iPhone", + quantity: 1, + price: 8000, + } + earphones := FreeGift{ + name: "耳机", + quantity: 1, + price: 200, + } + + goods := []Good{iPhone, earphones} + allPrice := calculateAllPrice(goods) + fmt.Printf("该订单总共需要支付 %d 元", allPrice) + } + +运行后,输出如下 + +:: + + 您要购买1个iPhone计:8000元 + 您要购买1个耳机计:0元 + 该订单总共需要支付 8000 元 diff --git a/source/c09/c09_16.rst b/source/c09/c09_16.rst new file mode 100644 index 0000000..13d6be6 --- /dev/null +++ b/source/c09/c09_16.rst @@ -0,0 +1,97 @@ +9.16 关键字:make 和 new 的区别? +================================= + +1. new 函数 +----------- + +在官方文档中,new 函数的描述如下 + +.. code:: go + + // The new built-in function allocates memory. The first argument is a type, + // not a value, and the value returned is a pointer to a newly + // allocated zero value of that type. + func new(Type) *Type + +可以看到,new +只能传递一个参数,该参数为一个任意类型,可以是Go语言内建的类型,也可以是你自定义的类型 + +那么 new 函数到底做了哪些事呢: + +- 分配内存 +- 设置零值 +- 返回指针(重要) + +举个例子 + +.. code:: go + + import "fmt" + + type Student struct { + name string + age int + } + + func main() { + // new 一个内建类型 + num := new(int) + fmt.Println(*num) //打印零值:0 + + // new 一个自定义类型 + s := new(Student) + s.name = "wangbm" + } + +2. make 函数 +------------ + +在官方文档中,make 函数的描述如下 + + //The make built-in function allocates and initializes an object //of + type slice, map, or chan (only). Like new, the first argument is // a + type, not a value. Unlike new, make’s return type is the same as // + the type of its argument, not a pointer to it. + + func make(t Type, size …IntegerType) Type + +翻译一下注释内容 + +1. 内建函数 make 用来为 slice,map 或 chan + 类型(注意:也只能用在这三种类型上)分配内存和初始化一个对象 +2. make + 返回类型的本身而不是指针,而返回值也依赖于具体传入的类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了 + +注意,因为这三种类型是引用类型,所以必须得初始化(size和cap),但是不是置为零值,这个和new是不一样的。 + +举几个例子 + +.. code:: go + + //切片 + a := make([]int, 2, 10) + + // 字典 + b := make(map[string]int) + + // 通道 + c := make(chan int, 10) + +3. 总结 +------- + +new:为所有的类型分配内存,并初始化为零值,返回指针。 + +make:只能为 slice,map,chan 分配内存,并初始化,返回的是类型。 + +另外,目前来看 new 函数并不常用,大家更喜欢使用短语句声明的方式。 + +.. code:: go + + a := new(int) + a = 1 + // 等价于 + a := 1 + +但是 make +就不一样了,它的地位无可替代,在使用slice、map以及channel的时候,还是要使用make进行初始化,然后才可以对他们进行操作。 diff --git a/source/c09/c09_17.rst b/source/c09/c09_17.rst new file mode 100644 index 0000000..9926f95 --- /dev/null +++ b/source/c09/c09_17.rst @@ -0,0 +1,27 @@ +9.17 Go原生协程:goroutine +========================== + +一台电脑能使用多少线程 + +协程与线程的关系 + +线程:抢占式多任务处理 + +协程:非抢占式多任务处理由协程主动交出控制权 + +coroutine + +.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200202142007967.png + :alt: image-20200202142007967 + + image-20200202142007967 + +.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200202142004117.png + :alt: image-20200202142004117 + + image-20200202142004117 + +.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200202142258895.png + :alt: image-20200202142258895 + + image-20200202142258895 diff --git a/source/c09/c09_18.rst b/source/c09/c09_18.rst new file mode 100644 index 0000000..5d64732 --- /dev/null +++ b/source/c09/c09_18.rst @@ -0,0 +1,159 @@ +9.10 理解语句块与作用域 +======================= + +由于 Go +使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 +Go 中的语句块是怎么一回事? + +1. 显示语句块与隐式语句块 +------------------------- + +通俗地说,语句块是由花括弧(\ ``{}``\ )所包含的一系列语句。 + +语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域,我会在下节讲到。 + +用花括弧包含的语句块,属于显示语句块。 + +在 Go 中还有很多的隐式语句块: + +- 主语句块:包括所有源码,对应内置作用域 +- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 +- 文件语句块:包括该文件中的所有源码,对应文件级作用域 +- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 + +前面三点好理解,第四点举几个例子 + +for 循环 + +.. code:: go + + for i := 0; i < 5; i++ { + fmt.Println(i) + } + +if 语句 + +.. code:: go + + if i := 0; i >= 0 { + fmt.Println(i) + } + +switch 语句 + +.. code:: go + + switch i := 2; i * 4 { + case 8: + fmt.Println(i) + default: + fmt.Println(“default”) + } + +且每个 switch 语句的子句都是一个隐式的语句块 + +.. code:: go + + switch i := 2; i * 4 { + case 8: + j := 0 + fmt.Println(i, j) + default: + // "j" is undefined here + fmt.Println(“default”) + } + // "j" is undefined here + +2. 四种作用域的理解 +------------------- + +变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 + +根据声明位置的不同,作用域可以分为以下四个类型: + +- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 +- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 +- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 +- 局部作用域:在自己的语句块内声明,包括函数,for、if + 等语句块,或自定义的 {} + 语句块形成的作用域,只在自己的局部作用域内可用 + +以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这时将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 + +对于作用域,有以下几点总结: + +- 低层作用域,可以访问高层作用域 +- 同一层级的作用域,是相互隔离的 +- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 + +在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 + +而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 + +3. 静态作用域与动态作用域 +------------------------- + +根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: + +- 静态作用域,如 Go 语言 +- 动态作用域,如 Shell 语言 + +具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 + +.. code:: python + + #!/bin/bash + func01() { + local value=1 + func02 + } + func02() { + echo "func02 sees value as ${value}" + } + + # 执行函数 + func01 + func02 + +从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 +value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 +func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 +func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 + +但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 +是访问不了 value 变量的。 + +所以此时的输出结果是 + +.. code:: shell + + func02 sees value as 1 + func02 sees value as + +但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 +name +这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 + +.. code:: go + + import "fmt" + + func func01() { + fmt.Println("在 func01 函数中,name:", name) + } + + func main() { + var name string = "Python编程时光" + fmt.Println("在 main 函数中,name:", name) + + func01() + } + +因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 + +参考文章:https://studygolang.com/articles/12632 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_19.rst b/source/c09/c09_19.rst new file mode 100644 index 0000000..34f05dc --- /dev/null +++ b/source/c09/c09_19.rst @@ -0,0 +1,177 @@ +9.9 Go语言命名编码规范 +====================== + +每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 + +以下内容整理自:\ `Go语言(Golang)编码规范 `__ + +命名规范分为以下几点 + +**1. 文件命名** + +文件名应一律使用小写(因为Windows的原因), 不同单词之间用下划线分割。 + +应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 Gogs +的入口应当为 gogs.go + +**2. 常量命名** + +- 常量均需使用全部大写字母组成,并使用下划线分词: + + .. code:: go + + const APP_VER = "0.7.0.1110 Beta" + +- 如果是枚举类型的常量,需要先创建相应类型: + + .. code:: go + + type Scheme string + const ( + HTTP Scheme = "http" + HTTPS Scheme = "https" + ) + +- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: + + .. code:: go + + type PullRequestStatus int + const ( + PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota + PULL_REQUEST_STATUS_CHECKING + PULL_REQUEST_STATUS_MERGEABLE + ) + +**3. 变量命名** + +使用驼峰命名法 + +- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u +- 若该变量为 bool 类型,则名称应以 ``Has``, ``Is``, ``Can`` 或 + ``Allow`` 开头。例如:isExist ,hasConflict 。 +- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 + startDate 。 +- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 + ``apiClient``\ 。 +- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient + +下面列举了一些常见的特有名词: + +:: + + // A GonicMapper that contains a list of common initialisms taken from golang/lint + var LintGonicMapper = GonicMapper{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, + } + +**接口命名** + +使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 + +.. code:: go + + type helloWorld interface { + func Hello(); + } + + type SayHello helloWorld + +**注释规范** + +单行注释使用 ``//`` ,多行注释使用 ``/* comment */`` + +.. code:: go + + // go语言 + + /* + Go 语言 + Hello, World + */ + +- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 + +- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 + +- 包、函数、方法和类型的注释说明都是一个完整的句子。 + +- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 + +- 注释的单行长度不能超过 80 个字符。 + +- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 + +- 如果是特别复杂的包,可单独创建 doc.go 文件说明 + +- 类型的定义一般都以单数形式描述: + + .. code:: go + + // Request represents a request to run a command. type Request struct { ... + +- 如果为接口,则一般以以下形式描述: + + .. code:: go + + // FileInfo is the interface that describes a file and is returned by Stat and Lstat. + type FileInfo interface { ... + +- 函数与方法的注释需以函数或方法的名称作为开头: + + .. code:: go + + // Post returns *BeegoHttpRequest with POST method. + +- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: + + .. code:: go + + // Copy copies file from source to target path. + // It returns false and error when error occurs in underlying function calls. + +- 若函数或方法为判断类型(返回值主要为 ``bool`` 类型),则以 + `` returns true if`` 开头: + + .. code:: go + + // HasPrefix returns true if name has any string in given slice as prefix. + func HasPrefix(name string, prefixes []string) bool { ... + +特别注释 + +- TODE:提醒维护人员此部分代码待完成 +- FIXME:提醒维护人员此处有BUG待修复 +- NOTE:维护人员要关注的一些问题说明 diff --git a/source/c09/c09_23.rst b/source/c09/c09_23.rst new file mode 100644 index 0000000..170c0c6 --- /dev/null +++ b/source/c09/c09_23.rst @@ -0,0 +1,324 @@ +9.12 详解数据类型:信道 +======================= + +Go 语言之所以开始流行起来,很大一部分原因是因为它自带的并发机制。 + +如果说 goroutine 是 Go语言程序的并发体的话,那么 channel(信道) +就是它们之间的通信机制。channel,是一个可以让一个 goroutine 与另一个 +goroutine +传输信息的通道,我把他叫做信道,也有人将其翻译成通道,二者都是一个概念。 + +信道,就是一个管道,连接多个goroutine程序 +,它是一种队列式的数据结构,遵循先入先出的规则。 + +1. 信道的定义与使用 +------------------- + +每个信道都只能传递一种数据类型的数据,所以在你声明的时候,你得指定数据类型(string +int 等等) + +.. code:: go + + var 信道实例 chan 信道类型 + +声明后的信道,其零值是nil,无法直接使用,必须配合make函进行初始化。 + +.. code:: go + + 信道实例 = make(chan 信道类型) + +亦或者,上面两行可以合并成一句,以下我都使用这样的方式进行信道的声明 + +.. code:: go + + 信道实例 := make(chan 信道类型) + +假如我要创建一个可以传输int类型的信道,可以这样子写。 + +.. code:: go + + // 定义信道 + pipline := make(chan int) + +信道的数据操作,无非就两种:发送数据与读取数据 + +.. code:: go + + // 往信道中发送数据 + pipline<- 200 + + // 从信道中取出数据,并赋值给mydata + mydata := <-pipline + +信道用完了,可以对其进行关闭,避免有人一直在等待。 + +.. code:: go + + close(pipline) + +对一个已关闭的信道再关闭,是会报错的。所以我们还要学会,如何判断一个信道是否被关闭? + +当从信道中读取数据时,可以有多个返回值,其中第二个可以表示 +信道是否被关闭,如果已经被关闭,ok 为 false,若还没被关闭,ok 为true。 + +.. code:: go + + x, ok := <-pipline + +2. 信道的容量与长度 +------------------- + +一般创建信道都是使用 make 函数,make 函数接收两个参数 + +- 第一个参数:必填,指定信道类型 +- 第二个参数:选填,不填默认为0,指定信道的\ **容量**\ (可缓存多少数据) + +对于信道的容量,很重要,这里要多说几点: + +- 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为\ **无缓冲信道**\ 。 +- 当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 + 利用这点可以利用信道来做锁。 +- 当容量大于1时,信道中可以存放多个数据,可以用于多个协程之间的通信管道,共享资源。 + +至此我们知道,信道就是一个容器。 + +若将它比做一个纸箱子 + +- 它可以装10本书,代表其容量为10 +- 当前只装了1本书,代表其当前长度为1 + +信道的容量,可以使用 cap 函数获取 ,而信道的长度,可以使用 len +长度获取。 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan int, 10) + fmt.Printf("信道可缓冲 %d 个数据\n", cap(pipline)) + pipline<- 1 + fmt.Printf("信道中当前有 %d 个数据", len(pipline)) + } + +输出如下 + +:: + + 信道可缓冲 10 个数据 + 信道中当前有 1 个数据 + +3. 缓冲信道与无缓冲信道 +----------------------- + +按照是否可缓冲数据可分为:\ **缓冲信道** 与 **无缓冲信道** + +**缓冲信道** + +允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。 + +.. code:: go + + pipline := make(chan int, 10) + +**无缓冲信道** + +在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,信道中无法存储数据。也就是说发送端和接收端是同步运行的。 + +.. code:: go + + pipline := make(chan int) + + // 或者 + pipline := make(chan int, 0) + +4. 双向信道与单向信道 +--------------------- + +通常情况下,我们定义的信道都是双向通道,可发送数据,也可以接收数据。 + +但有时候,我们希望对信道的数据流向做一些控制,比如这个信道只能接收数据或者这个信道只能发送数据。 + +因此,就有了 **双向信道** 和 **单向信道** 两种分类。 + +**双向信道** + +默认情况下你定义的信道都是双向的,比如下面代码 + +.. code:: go + + import ( + "fmt" + "time" + ) + + func main() { + pipline := make(chan int) + + go func() { + fmt.Println("准备发送数据: 100") + pipline <- 100 + }() + + go func() { + num := <-pipline + fmt.Printf("接收到的数据是: %d", num) + }() + // 主函数sleep,使得上面两个goroutine有机会执行 + time.Sleep(1) + } + +**单向信道** + +单向信道,可以细分为 **只读信道** 和 **只写信道**\ 。 + +定义只读信道 + +.. code:: go + + var pipline = make(chan int) + type Receiver = <-chan int // 关键代码:定义别名类型 + var receiver Receiver = pipline + +定义只写信道 + +.. code:: go + + var pipline = make(chan int) + type Sender = chan<- int // 关键代码:定义别名类型 + var sender Sender = pipline + +仔细观察,区别在于 ``<-`` 符号在关键字 ``chan`` 的左边还是右边。 + +- ``<-chan`` 表示这个信道,只能从里发出数据,对于程序来说就是只读 +- ``chan<-`` 表示这个信道,只能从外面接收数据,对于程序来说就是只写 + +有同学可能会问:为什么还要先声明一个双向信道,再定义单向通道呢?比如这样写 + +.. code:: go + + type Sender = chan<- int + sender := make(Sender) + +代码是没问题,但是你要明白信道的意义是什么? + +信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入的累赘,没什么用。所以只读信道和只写信道,唇亡齿寒,缺一不可。 + +当然了,若你往一个只读信道中写入数据 ,或者从一个只写信道中读取数据 +,都会出错。 + +完整的示例代码如下,供你参考: + +.. code:: go + + import ( + "fmt" + "time" + ) + //定义只写信道类型 + type Sender = chan<- int + + //定义只读信道类型 + type Receiver = <-chan int + + func main() { + var pipline = make(chan int) + + go func() { + var sender Sender = pipline + fmt.Println("准备发送数据: 100") + sender <- 100 + }() + + go func() { + var receiver Receiver = pipline + num := <-receiver + fmt.Printf("接收到的数据是: %d", num) + }() + // 主函数sleep,使得上面两个goroutine有机会执行 + time.Sleep(1) + } + +5. 遍历信道 +----------- + +遍历信道,可以使用 for 搭配 +range关键字,在range时,要确保信道是处于关闭状态,否则循环会阻塞。 + +.. code:: go + + import "fmt" + + func fibonacci(mychan chan int) { + n := cap(mychan) + x, y := 0, 1 + for i := 0; i < n; i++ { + mychan <- x + x, y = y, x+y + } + // 记得 close 信道 + // 不然主函数中遍历完并不会结束,而是会阻塞。 + close(mychan) + } + + func main() { + pipline := make(chan int, 10) + + go fibonacci(pipline) + + for k := range pipline { + fmt.Println(k) + } + } + +6. 用信道来做锁 +--------------- + +当信道里的数据量已经达到设定的容量时,此时再往里发送数据会阻塞整个程序。 + +利用这个特性,可以用当他来当程序的锁。 + +示例如下,详情可以看注释 + +.. code:: go + + package main + + import { + "fmt" + "time" + } + + // 由于 x=x+1 不是原子操作 + // 所以应避免多个协程对x进行操作 + // 使用容量为1的信道可以达到锁的效果 + func increment(ch chan bool, x *int) { + ch <- true + *x = *x + 1 + <- ch + } + + func main() { + // 注意要设置容量为 1 的缓冲信道 + pipline := make(chan bool, 1) + + var x int + for i:=0;i<1000;i++{ + go increment(pipline, &x) + } + + // 确保所有的协程都已完成 + // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep + time.Sleep(3) + fmt.Println("x 的值:", x) + } + +输出如下 + +:: + + x 的值:1000 + +如果不加锁,输出会小于1000。 diff --git a/source/c09/c09_24.rst b/source/c09/c09_24.rst new file mode 100644 index 0000000..9cc4cb3 --- /dev/null +++ b/source/c09/c09_24.rst @@ -0,0 +1,175 @@ +9.14 几个信道死锁经典错误案例详解 +================================= + +刚接触 Go +语言的信道的时候,经常会遇到死锁的错误,而导致这个错误的原因有很多种,这里整理了几种常见的。 + +:: + + fatal error: all goroutines are asleep - deadlock! + +错误示例一 +---------- + +看下面这段代码 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + pipline <- "hello world" + fmt.Println(<-pipline) + } + +运行会抛出错误,如下 + +:: + + fatal error: all goroutines are asleep - deadlock! + +看起来好像没有什么问题?先往信道中存入数据,再从信道中读取数据。 + +回顾前面的基础,我们知道使用 make +创建信道的时候,若不传递第二个参数,则你定义的是无缓冲信道,而对于无缓冲信道,在接收者未准备好之前,发送操作是阻塞的. + +因此,对于解决此问题有两种方法: + +1. 使接收者代码在发送者之前执行 +2. 使用缓冲信道,而不使用无缓冲信道 + +**第一种方法**\ : + +若要程序正常执行,需要保证接收者程序在发送数据到信道前就进行阻塞状态,修改代码如下 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + fmt.Println(<-pipline) + pipline <- "hello world" + } + +运行的时候还是报同样的错误。问题出在哪里呢? + +原来我们将发送者和接收者写在了同一协程中,虽然保证了接收者代码在发送者之前执行,但是由于前面接收者一直在等待数据 +而处于阻塞状态,所以无法执行到后面的发送数据。还是一样造成了死锁。 + +有了前面的经验,我们将接收者代码写在另一个协程里,并保证在发送者之前执行,就像这样的代码 + +.. code:: go + + package main + + func hello(pipline chan string) { + <-pipline + } + + func main() { + pipline := make(chan string) + go hello(pipline) + pipline <- "hello world" + } + +运行之后 ,一切正常。 + +**第二种方法**\ : + +接收者代码必须在发送者代码之前 执行,这是针对无缓冲信道才有的约束。 + +既然这样,我们改使用可缓冲信道不就OK了吗? + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string, 1) + pipline <- "hello world" + fmt.Println(<-pipline) + } + +运行之后,一切正常。 + +错误示例二 +---------- + +每个缓冲信道,都有容量,当信道里的数据量等于信道的容量后,此时再往信道里发送数据,就失造成阻塞,必须等到有人从信道中消费数据后,程序才会往下进行。 + +比如这段代码,信道容量为 +1,但是往信道中写入两条数据,对于一个协程来说就会造成死锁。 + +.. code:: go + + package main + + import "fmt" + + func main() { + ch1 := make(chan string, 1) + + ch1 <- "hello world" + ch1 <- "hello China" + + fmt.Println(<-ch1) + } + +错误示例三 +---------- + +当程序一直在等待从信道里读取数据,而此时并没有人会往信道中写入数据。此时程序就会陷入死循环,造成死锁。 + +比如这段代码,for 循环接收了两次消息(“hello world”和“hello +China”)后,再也没有人发送数据了,接收者就会处于一个等待永远接收不到数据的囧境。陷入死循环,造成死锁。 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + go func() { + pipline <- "hello world" + pipline <- "hello China" + // close(pipline) + }() + for data := range pipline{ + fmt.Println(data) + } + } + +包子铺里的包子已经卖完了,可还有人在排队等着买,如果不再做包子,就要告诉排队的人:不用等了,今天的包子已经卖完了,明日请早呀。 + +不能让人家死等呀,不跟客人说明一下,人家还以为你们店后面还在蒸包子呢。 + +所以这个问题,解决方法很简单,只要在发送完数据后,手动关闭信道,告诉 +range 信道已经关闭,无需等待就行。 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + go func() { + pipline <- "hello world" + pipline <- "hello China" + close(pipline) + }() + for data := range pipline{ + fmt.Println(data) + } + } diff --git a/source/c09/c09_25.rst b/source/c09/c09_25.rst new file mode 100644 index 0000000..cef5af0 --- /dev/null +++ b/source/c09/c09_25.rst @@ -0,0 +1,183 @@ +9.12 Go语言的包依赖管理 +======================= + +在以前,Go +语言的的包依赖管理一直都被大家所诟病,但最近几年,这个窘境开始得到缓解。 + +现在最主流的包依赖管理方式是使用官方推荐的 go module +的方式,如果你和我一样是刚开始学习Go语言的,建议也了解一下Go语言包依赖管理方案,是如何一步一步发展到今天的。 + +简单来说,Go语言的包依赖管理方案,可以分为三个阶段。 + +**第一阶段**\ :使用最古老的 GOPATH 进行管理 +-------------------------------------------- + +使用 GOPATH +的话,你需要将你所有的第三方库都下载到本地,这本身没有什么问题,其他语言也都是这么做的,问题就在于,在本地只能保存一个版本的第三方库,如果你的机器上有多个项目,而这些项目是基于不同版本的库进行开发的,那就尴尬了。 + +**第二阶段**\ :使用 GOVENDOR 解决方案 +-------------------------------------- + +为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 +开始支持 vendor 。 + +以前使用 GOPATH 的时候,所有的项目都共享一个 +GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH +下只能有一个版本的第三方库。 + +解决的思路就是,在每个项目下都创建一个 vendor +目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5+ +的Go 会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。如果在 +vendor 目录下没有找到,才会去 ``$GOAPTH/src`` 中去查找 + +在这个阶段,也催生出了各种第三方的管理包依赖插件:godep,dep,glide + +使用 godep 的开发流程是这样的: + +1. 先保证程序在本地能够正常编译 +2. 执行\ ``godep save``\ 保存当前项目的所有第三方依赖的版本信息和代码到 + ``Godeps/Godeps.json``\ 文件中 +3. 提交Godeps目录和vender目录到代码库。 +4. 如果要更新依赖的版本,可以直接修改\ ``Godeps.json``\ 文件中的对应项 + +虽然这个方案解决了一些问题,但是解决得并不完美。 + +如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 + +**第三阶段**\ :使用 GO MODULE 版本管理工具 +------------------------------------------- + +go module 在 v1.11 版本推出,并在 +v1.13版本中,成为官方默认的包依赖管理工具。 + +v1.11 开始,\ ``go env`` 多了个环境变量: +``GO111MODULE``\ ,这是一个开关,通过它可以开启或关闭模块支持,它有三个可选值:\ ``off``\ 、\ ``on``\ 、\ ``auto``\ ,默认值是\ ``auto``\ 。 + +1. ``GO111MODULE=off``\ 禁用模块支持,编译时会从\ ``GOPATH``\ 和\ ``vendor``\ 文件夹中查找包。 +2. ``GO111MODULE=on``\ 启用模块支持,编译时会忽略\ ``GOPATH``\ 和\ ``vendor``\ 文件夹,只根据 + ``go.mod``\ 下载依赖。 +3. ``GO111MODULE=auto``\ ,当项目在\ ``$GOPATH/src``\ 外且项目根目录有\ ``go.mod``\ 文件时,开启模块支持。 + +go mod 出现后, GOPATH 和 GOVENDOR +将被逐步淘汰,但是若你的项目仍然要使用那些 out +的包依赖管理方案,需要注意将 GO111MODULE 置为 off。 + +接下来,介绍一下,如何使用 go module 来管理依赖。 + +使用 go module +管理依赖后会在项目根目录下生成两个文件\ ``go.mod``\ 和\ ``go.sum``\ 。 + +``go.mod`` 文件记录了项目所有的依赖信息,其结构大致如下: + +:: + + module github.com/Q1mi/studygo/blogger + + go 1.12 + + require ( + github.com/DeanThompson/ginpprof v0.0.0-20190408063150-3be636683586 + github.com/gin-gonic/gin v1.4.0 + github.com/go-sql-driver/mysql v1.4.1 + github.com/jmoiron/sqlx v1.2.0 + github.com/satori/go.uuid v1.2.0 + google.golang.org/appengine v1.6.1 // indirect + ) + +其中, + +- ``module``\ 用来定义包名 +- ``require``\ 用来定义依赖包及版本 +- ``indirect``\ 表示间接引用 + +而 ``go.sum`` 记录该项目中每个依赖库的版本和哈希值。 + +使用 go module 需熟悉 go mod 的命令 + +:: + + go mod init 初始化当前文件夹, 创建go.mod文件,后可接参数指定 module 名 + go mod graph 打印模块依赖图 + go mod tidy 增加缺少的module,删除无用的module + go mod vendor 将依赖复制到vendor下 + go mod verify 校验依赖 + go mod why 解释为什么需要依赖 + go mod download 下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录) + + go mod edit 编辑go.mod文件 + 接 -fmt 参数格式化 go.mod 文件 + 接 -require=golang.org/x/text 添加依赖 + 接 -droprequire=golang.org/x/text 删除依赖 + 更加用法,参看 go help mod edit + +如何给项目添加依赖(下载包并将依赖写进go.mod文件)? + +有两种方法: + +- 你只要在项目中有 import,然后 go build 就会 go module + 就会自动下载并添加。 + +- 自己手工使用 go get 下载,支持语义化版本号 + +.. code:: shell + + # 拉取最新 + go get github.com/foo + + # 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) + go get -u github.com/foo + + # 升级到最新的修订版本 + go get -u=patch github.com/foo + + # 指定版本 + go get github.com/foo@v1.2.3 + + # 指定分支 + go get github.com/foo@master + + # 指定git提交的hash值 + go get github.com/foo@e3702bed2 + + # 指定版本 + go get github.com/foo@v1.11.0 + +使用以上方式添加完依赖后,在 go.mod 文件里会有你所依赖的包及其版本。 + +如果项目下已经有这个 go.mod 文件,但是包还没拉取,如何 +触发下载呢?两种方法 + +- 可以执行命令 ``go build ./...``\ ,go module 会自动下载 +- 使用 ``go mod download`` ,手动下载 + +如果你连这个 go.mod 文件都丢失了,那怎么生成?两种方法 + +- 使用 Goland 的话,可以点击 create go.mod 文件来生成。 +- 也可以在项目目录下执行这条命令 + +.. code:: shell + + $ go mod init + +由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 + +:: + + replace ( + golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac + golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d + golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 + ) + +以上几种解决方案,不同之处就在于它们的依赖包的搜索路径优先级不同 + +- 使用 go mod,只在 ``$GOPATH/pkg/mod`` 查找依赖包(GO111MODULE=on) +- 使用 GOVENDOR,优先在 vendor目录中查找,然后才去 ``$GOROOT/src`` 和 + ``$GOPATH/src``\ 查找 +- 使用 GOPATH,先去\ ``$GOROOT/src`` 查找 ,找不到再去 ``$GOPATH/src`` + 中查找 + +参考文章: +---------- + +- `Go语言之依赖管理 `__ diff --git a/source/c09/c09_27.rst b/source/c09/c09_27.rst new file mode 100644 index 0000000..6b12c3a --- /dev/null +++ b/source/c09/c09_27.rst @@ -0,0 +1,6 @@ +9.13 go 命令详解 +================ + +go build ./… 不会生成可执行文件到 ${GOPATH}/bin ,只要能编译过就行 + +go install ./… 会生成可执行文件到 ${GOPATH}/bin diff --git a/source/c09/c09_30.rst b/source/c09/c09_30.rst new file mode 100644 index 0000000..a502e38 --- /dev/null +++ b/source/c09/c09_30.rst @@ -0,0 +1,16 @@ +9.30 函数式编程 +=============== + +函数是一等公民,它可以做为参数进行传递,可以做为函数返回值,也可以做为接收者。所以后来才有了高阶函数,闭包。 + +正统的函数式编程 + +- 不可变性:不能有状态,只能有函数和变量 +- 函数只能有一个参数 + +Go 语言不是正统 + +.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200131160151750.png + :alt: image-20200131160151750 + + image-20200131160151750 diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst new file mode 100644 index 0000000..1548ac4 --- /dev/null +++ b/source/c10/c10_01.rst @@ -0,0 +1,155 @@ +10.1 情人节来了,教你使用 Python 来表白 +======================================= + +**作者**\ :@明哥 **公众号**\ :Python编程时光 + +-------------- + +2020年,这个看起来如此浪漫的年份,你还是一个人吗? + +2018年的时候,写过一篇介绍如何使用 Python 来表白的文章。 + +虽然创意和使用效果都不错,但有一缺点,这是那个exe文件,女神需要打开电脑,才有可能参与进来,进而被你成功“调戏”。 + +由于是很早期的文章了,应该有很多人没有看过。 + +没有看过的,你可以点击这里查看:\ `用Python写一个表白神器让你脱离单身 `__ + +提醒你一下,后天就是 2月14日了。什么?还是一条狗呢? + +行吧,那你赶上了,今天的文章,就是为你而写。 + +明哥今天来教你如何使用 Python 来向心中的女神表白。 + +前段时间,在微博上刷到了一条推荐。内容是这样的 + +.. figure:: http://image.python-online.cn/20200211211522.png + :alt: 微博截图 + + 微博截图 + +出于好奇,我点开了,放大再放大,emmm,有点意思吖… + +.. figure:: http://image.python-online.cn/20200211211657.png + :alt: img + + img + +这四个字,对于像我这样腼腆的DS男来说,还真不好意思说,说出来,万一被拒绝了咋办? + +使用套路来表白,并观察对方的反应,你大概能清楚对方是否对你也有好感,先测试下自己有几成的把握再下手或许更稳妥。 + +今天就教大家一个这样的套路:如何使用 Python +来做出来这样的图,有点浪漫,又有点极客。能不能拿下你女神,就要靠你(命)了。(๑•́₃ +•̀๑) + +首先,你得先找到一张你女神的高清图片(尽量分辨率高点的吧,效果会好点)。 + +这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 + +|image0| + +使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? + +|image1| + +然后将这张图片发给你的女神,具体话术你自己想咯。 + +-------------- + +相比女神来说,你可能更在意这是如何实现的(\ **活该你单身**\ )。 + +其实原理很简单,代码也还不到20 行。 + +首先,来讲讲原理。 + +事实上,每一张图片都是由一个一个的像素点所组成的。而每个像素点,都有自己的颜色,其颜色可以用一个数组来表示:(a,b,c),其中每位数的取值范围都是 +0-255。 + +比如(0,0,0)代表黑色色,(255,255,255)代表白色。 + +当像素点足够多的时候,这张照片就是我们所说的高清照片。 + +而如果当像素点太少,我们的肉眼就能感知到明显的锯齿感。 + +用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 +5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 + +|image2| + +我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 + +有了思路,就可以开始我们的代码。 + +首先,使用 pillow.Image读取图像,并使用load函数获取到每一个像素值。 + +.. code:: python + + img_raw = Image.open(img_path) + img_array = img_raw.load() + +然后新建一张画布,并选好你要使用的字体和字体大小。 + +.. code:: python + + img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) + draw = ImageDraw.Draw(img_new) + font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) + +由于需要不断循环 “我喜欢你!”,这五个字符。所以这里可以while循环 yield +来实现一个生成器。 + +.. code:: python + + def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] + +最后,要给这些字加上相应的颜色,写入新创建的画布中。 + +.. code:: python + + for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) + +最后将成品保存 + +.. code:: python + + img_new.convert('RGB').save("F://gyy_save.jpeg") + +完整代码如下,供你参考 + +.. code:: python + + from PIL import Image, ImageDraw, ImageFont + + font_size = 7 + text = "我喜欢你!" + img_path = "F://gyy.jpeg" + + img_raw = Image.open(img_path) + img_array = img_raw.load() + + img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) + draw = ImageDraw.Draw(img_new) + font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) + + def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] + + ch_gen = character_generator(text) + + for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) + + img_new.convert('RGB').save("F://save.jpeg") + +.. |image0| image:: http://image.python-online.cn/20200214104413.png +.. |image1| image:: http://image.python-online.cn/save.jpeg +.. |image2| image:: http://image.python-online.cn/20200214104646.png From 3e7f8c8b8e4f5c79415cf2244e8c25faefb18670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 23:07:00 +0800 Subject: [PATCH 009/147] =?UTF-8?q?update:=20=E4=BF=AE=E6=94=B9=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20python3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- md2rst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/md2rst.py b/md2rst.py index 69d2365..7206d46 100644 --- a/md2rst.py +++ b/md2rst.py @@ -50,7 +50,7 @@ def render_index_page(index_info): 生成 readme.md 索引文件,包含所有文件目录 ''' # 重新排序 - index_info = sorted(index_info.iteritems(), key=lambda item:item[0], reverse=False) + index_info = sorted(index_info.items(), key=lambda item:item[0], reverse=False) # 写入文件 with open(index_path, 'w+') as file: From 916b18ef76887641ca649403faebebef68a427ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 15 Feb 2020 23:09:12 +0800 Subject: [PATCH 010/147] =?UTF-8?q?update:=20=E5=88=B7=E6=96=B0=20readme.m?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 39 +++++++++++++++++++++++++++++++++------ md2rst.py | 2 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 01d6de1..3229e1f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ 这是我的个人博客( [MING's BLOG](http://python-online.cn/) ),主要写关于Python的一些思考总结。 -关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python-online.cn/zh_CN/latest/c04/c04_03.rst) +关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python-online.cn/zh_CN/latest/c04/c04_03.html) ## 第一章:基础知识 - 1.1 [13条Python2.x和3.x的区别?](http://python-online.cn/zh_CN/latest/c01/c01_01.html) - 1.4 [什么是猴子补丁?](http://python-online.cn/zh_CN/latest/c01/c01_04.html) @@ -29,6 +29,7 @@ - 1.27 [全面学习 Python 包:包的构建与分发](http://python-online.cn/zh_CN/latest/c01/c01_27.html) - 1.27 [如何阅读 CPython源码?](http://python-online.cn/zh_CN/latest/c01/c01_29.html) - 1.30 [学习编程的几大网站](http://python-online.cn/zh_CN/latest/c01/c01_30.html) +- 1.31 [学习 Pillow 笔记](http://python-online.cn/zh_CN/latest/c01/c01_31.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python-online.cn/zh_CN/latest/c02/c02_01.html) @@ -75,6 +76,7 @@ - 4.20 [学会使用谷歌搜索引擎](http://python-online.cn/zh_CN/latest/c04/c04_20.html) - 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python-online.cn/zh_CN/latest/c04/c04_21.html) - 4.22 [用好 Chrome 必看](http://python-online.cn/zh_CN/latest/c04/c04_22.html) +- 4.23 [电脑使用技巧](http://python-online.cn/zh_CN/latest/c04/c04_23.html) ## 第五章:算法教程 - 5.1 [图解九大经典排序算法](http://python-online.cn/zh_CN/latest/c05/c05_01.html) @@ -90,7 +92,6 @@ - 6.6 [自动生成图像视频](http://python-online.cn/zh_CN/latest/c06/c06_06.html) ## 第七章:运维人生 -- 7.8 [Keepalived 部署文档](http://python-online.cn/zh_CN/latest/c07/C07_08.html) - 7.1 [Linux 命令行的艺术](http://python-online.cn/zh_CN/latest/c07/c07_01.html) - 7.2 [Zabbix 监控部署文档](http://python-online.cn/zh_CN/latest/c07/c07_02.html) - 7.3 [Docker:Hello world](http://python-online.cn/zh_CN/latest/c07/c07_03.html) @@ -98,6 +99,7 @@ - 7.5 [Docker:网络通信](http://python-online.cn/zh_CN/latest/c07/c07_05.html) - 7.6 [Docker:存储与多主机](http://python-online.cn/zh_CN/latest/c07/c07_06.html) - 7.7 [SaltStack 入门指南](http://python-online.cn/zh_CN/latest/c07/c07_07.html) +- 7.8 [Keepalived 部署文档](http://python-online.cn/zh_CN/latest/c07/c07_08.html) - 7.9 [Ansible 入门指南使用手册](http://python-online.cn/zh_CN/latest/c07/c07_09.html) - 7.10 [Ansible API 最全使用文档(中文)](http://python-online.cn/zh_CN/latest/c07/c07_10.html) - 7.11 [K8S:基础入门](http://python-online.cn/zh_CN/latest/c07/c07_11.html) @@ -106,6 +108,7 @@ - 7.14 [Linux 如何写判断语句](http://python-online.cn/zh_CN/latest/c07/c07_14.html) - 7.15 [Mariadb 与 Galera 集群总结](http://python-online.cn/zh_CN/latest/c07/c07_15.html) - 7.16 [Linux 运维之路](http://python-online.cn/zh_CN/latest/c07/c07_16.html) +- 1.17 [ansible 自定义 Jinja2 过滤器](http://python-online.cn/zh_CN/latest/c07/c07_17.html) ## 第八章:OpenStack - 8.1 [OpenStack 运维命令](http://python-online.cn/zh_CN/latest/c08/c08_01.html) @@ -126,11 +129,35 @@ ## 第九章:Go 语言之路 - 9.1 [开发环境的搭建(Goland和VSCode)](http://python-online.cn/zh_CN/latest/c09/c09_01.html) -- 9.3 [五种变量创建的方法](http://python-online.cn/zh_CN/latest/c09/c09_02.html) -- 9.3 [理解语句块与作用域](http://python-online.cn/zh_CN/latest/c09/c09_03.html) -- 9.4 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_04.html) -- 9.5 [Go语言数据类型](http://python-online.cn/zh_CN/latest/c09/c09_05.html) +- 9.2 [五种变量创建的方法](http://python-online.cn/zh_CN/latest/c09/c09_02.html) +- 9.3 [详解数据类型:整型与浮点型](http://python-online.cn/zh_CN/latest/c09/c09_03.html) +- 9.4 [详解数据类型:byte、rune与字符串](http://python-online.cn/zh_CN/latest/c09/c09_04.html) +- 9.7 [详解数据类型:数组与切片](http://python-online.cn/zh_CN/latest/c09/c09_05.html) +- 9.6 [详解数据类型:字典与布尔类型](http://python-online.cn/zh_CN/latest/c09/c09_06.html) +- 9.7 [详解数据类型:指针](http://python-online.cn/zh_CN/latest/c09/c09_07.html) +- 9.8 [面向对象编程:结构体与继承](http://python-online.cn/zh_CN/latest/c09/c09_08.html) +- 9.9 [一篇文章理解 Go 里的函数](http://python-online.cn/zh_CN/latest/c09/c09_09.html) +- 9.10 [Go语言流程控制:if-else](http://python-online.cn/zh_CN/latest/c09/c09_10.html) +- 9.11 [Go语言流程控制:switch-case](http://python-online.cn/zh_CN/latest/c09/c09_11.html) +- 9.11 [Go语言流程控制:for](http://python-online.cn/zh_CN/latest/c09/c09_12.html) +- 9.13 [Go语言流程控制:goto 无条件跳转](http://python-online.cn/zh_CN/latest/c09/c09_13.html) +- 9.14 [Go语言流程控制:defer 延迟语句](http://python-online.cn/zh_CN/latest/c09/c09_14.html) +- 9.15 [面向对象编程:接口与多态](http://python-online.cn/zh_CN/latest/c09/c09_15.html) +- 9.16 [关键字:make 和 new 的区别?](http://python-online.cn/zh_CN/latest/c09/c09_16.html) +- 9.17 [Go原生协程:goroutine](http://python-online.cn/zh_CN/latest/c09/c09_17.html) +- 9.10 [理解语句块与作用域](http://python-online.cn/zh_CN/latest/c09/c09_18.html) +- 9.9 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_19.html) +- 9.12 [详解数据类型:信道](http://python-online.cn/zh_CN/latest/c09/c09_23.html) +- 9.14 [几个信道死锁经典错误案例详解](http://python-online.cn/zh_CN/latest/c09/c09_24.html) +- 9.12 [Go语言的包依赖管理](http://python-online.cn/zh_CN/latest/c09/c09_25.html) +- 9.13 [go 命令详解](http://python-online.cn/zh_CN/latest/c09/c09_27.html) +- 9.30 [函数式编程](http://python-online.cn/zh_CN/latest/c09/c09_30.html) +- 9.31 [todo](http://python-online.cn/zh_CN/latest/c09/todo.html) + +## 第十章:有趣的工具 +- 10.1 [情人节来了,教你使用 Python 来表白](http://python-online.cn/zh_CN/latest/c10/c10_01.html) --- ![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) + diff --git a/md2rst.py b/md2rst.py index 7206d46..7390c7b 100644 --- a/md2rst.py +++ b/md2rst.py @@ -53,7 +53,7 @@ def render_index_page(index_info): index_info = sorted(index_info.items(), key=lambda item:item[0], reverse=False) # 写入文件 - with open(index_path, 'w+') as file: + with open(index_path, 'w+', encoding="utf-8") as file: file.write(readme_header) for chp, info in index_info: chp_name = info["name"] From 2c9bd8ff298eb758f8df2d9ccc86394e80c9d77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 29 Feb 2020 16:16:55 +0800 Subject: [PATCH 011/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +- source/c01/c01_10.md | 7 +- source/c01/c01_10.rst | 6 + source/c01/c01_29.md | 1 + source/c01/c01_29.rst | 5 + source/c01/c01_30.md | 8 + source/c01/c01_30.rst | 5 + source/c01/c01_31.md | 23 ++ source/c01/c01_31.rst | 6 +- source/c01/c01_32.md | 37 +++ source/c01/c01_32.rst | 37 +++ source/c01/c01_33.md | 52 ++++ source/c01/c01_33.rst | 50 ++++ source/c01/c01_34.md | 90 ++++++ source/c01/c01_34.rst | 96 +++++++ source/c01/c01_35.md | 342 ++++++++++++++++++++++ source/c01/c01_35.rst | 363 ++++++++++++++++++++++++ source/c02/c02_03.md | 2 +- source/c02/c02_03.rst | 2 +- source/c02/c02_04.md | 4 +- source/c02/c02_04.rst | 4 +- source/c02/c02_06.md | 2 +- source/c02/c02_06.rst | 2 +- source/c02/c02_13.md | 12 + source/c02/c02_13.rst | 14 + source/c07/c07_18.md | 57 ++++ source/c07/c07_18.rst | 58 ++++ source/c09/c09_03.md | 6 +- source/c09/c09_03.rst | 5 + source/c09/c09_04.md | 3 + source/c09/c09_04.rst | 5 + source/c09/c09_05.md | 1 + source/c09/c09_05.rst | 5 + source/c09/c09_06.md | 1 + source/c09/c09_06.rst | 5 + source/c09/c09_07.md | 6 + source/c09/c09_07.rst | 5 + source/c09/c09_08.md | 1 + source/c09/c09_08.rst | 5 + source/c09/c09_09.md | 1 + source/c09/c09_09.rst | 5 + source/c09/c09_10.md | 5 +- source/c09/c09_10.rst | 5 + source/c09/c09_11.md | 3 + source/c09/c09_11.rst | 5 + source/c09/c09_12.md | 1 + source/c09/c09_12.rst | 5 + source/c09/c09_13.md | 3 + source/c09/c09_13.rst | 5 + source/c09/c09_14.md | 1 + source/c09/c09_14.rst | 5 + source/c09/c09_15.md | 3 + source/c09/c09_15.rst | 5 + source/c09/c09_16.md | 3 + source/c09/c09_16.rst | 5 + source/c09/c09_17.md | 148 ++++++++++ source/c09/c09_17.rst | 164 +++++++++-- source/c09/c09_18.md | 131 +++++++++ source/c09/c09_18.rst | 220 +++++++-------- source/c09/c09_19.md | 338 ++++++++++++++++++++++ source/c09/c09_19.rst | 390 ++++++++++++++++++-------- source/c09/c09_20.md | 175 ++++++++++++ source/c09/c09_20.rst | 180 ++++++++++++ source/c09/c09_21.md | 109 +++++++ source/c09/c09_21.rst | 118 ++++++++ source/c09/c09_23.md | 158 +++++++++++ source/c09/c09_23.rst | 382 ++++++++----------------- source/c09/c09_24.md | 177 ++++++++++++ source/c09/c09_24.rst | 238 ++++++++-------- source/c09/c09_25.md | 182 ++++++++++++ source/c09/c09_25.rst | 7 +- source/c09/c09_27.md | 7 + source/c09/c09_27.rst | 2 +- source/c09/c09_28.md | 14 + source/c09/{c09_30.rst => c09_28.rst} | 2 +- source/c10/c10_01.md | 2 + source/c10/c10_01.rst | 5 + 77 files changed, 3916 insertions(+), 651 deletions(-) create mode 100644 source/c01/c01_31.md create mode 100644 source/c01/c01_32.md create mode 100644 source/c01/c01_32.rst create mode 100644 source/c01/c01_33.md create mode 100644 source/c01/c01_33.rst create mode 100644 source/c01/c01_34.md create mode 100644 source/c01/c01_34.rst create mode 100644 source/c01/c01_35.md create mode 100644 source/c01/c01_35.rst create mode 100644 source/c02/c02_13.md create mode 100644 source/c02/c02_13.rst create mode 100644 source/c07/c07_18.md create mode 100644 source/c07/c07_18.rst create mode 100644 source/c09/c09_17.md create mode 100644 source/c09/c09_18.md create mode 100644 source/c09/c09_19.md create mode 100644 source/c09/c09_20.md create mode 100644 source/c09/c09_20.rst create mode 100644 source/c09/c09_21.md create mode 100644 source/c09/c09_21.rst create mode 100644 source/c09/c09_23.md create mode 100644 source/c09/c09_24.md create mode 100644 source/c09/c09_25.md create mode 100644 source/c09/c09_27.md create mode 100644 source/c09/c09_28.md rename source/c09/{c09_30.rst => c09_28.rst} (95%) diff --git a/README.md b/README.md index 3229e1f..69e26d8 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ - 1.27 [如何阅读 CPython源码?](http://python-online.cn/zh_CN/latest/c01/c01_29.html) - 1.30 [学习编程的几大网站](http://python-online.cn/zh_CN/latest/c01/c01_30.html) - 1.31 [学习 Pillow 笔记](http://python-online.cn/zh_CN/latest/c01/c01_31.html) +- 1.32 [在 CentOS 7.2 上安装 Python3.7](http://python-online.cn/zh_CN/latest/c01/c01_32.html) +- 1.33 [如何调试已经运行中的程序](http://python-online.cn/zh_CN/latest/c01/c01_33.html) +- 1.34 [每日一库:sh,最优雅的命令调用方式](http://python-online.cn/zh_CN/latest/c01/c01_34.html) +- 1.35 [使用 Python 远程登陆服务器的利器](http://python-online.cn/zh_CN/latest/c01/c01_35.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python-online.cn/zh_CN/latest/c02/c02_01.html) @@ -44,6 +48,7 @@ - 2.10 [深入异步IO框架:asyncio 中篇](http://python-online.cn/zh_CN/latest/c02/c02_10.html) - 2.11 [实战异步IO框架:asyncio 下篇](http://python-online.cn/zh_CN/latest/c02/c02_11.html) - 2.12 [生成器与协程,你分清了吗?](http://python-online.cn/zh_CN/latest/c02/c02_12.html) +- 2.13 [I/O多路复用:select/poll/epoll](http://python-online.cn/zh_CN/latest/c02/c02_13.html) ## 第三章:高级编程 - 3.1 [装饰器进阶用法详解](http://python-online.cn/zh_CN/latest/c03/c03_01.html) @@ -109,6 +114,7 @@ - 7.15 [Mariadb 与 Galera 集群总结](http://python-online.cn/zh_CN/latest/c07/c07_15.html) - 7.16 [Linux 运维之路](http://python-online.cn/zh_CN/latest/c07/c07_16.html) - 1.17 [ansible 自定义 Jinja2 过滤器](http://python-online.cn/zh_CN/latest/c07/c07_17.html) +- 7.18 [Shell中去除字符串前后空格的方法](http://python-online.cn/zh_CN/latest/c07/c07_18.html) ## 第八章:OpenStack - 8.1 [OpenStack 运维命令](http://python-online.cn/zh_CN/latest/c08/c08_01.html) @@ -144,15 +150,16 @@ - 9.14 [Go语言流程控制:defer 延迟语句](http://python-online.cn/zh_CN/latest/c09/c09_14.html) - 9.15 [面向对象编程:接口与多态](http://python-online.cn/zh_CN/latest/c09/c09_15.html) - 9.16 [关键字:make 和 new 的区别?](http://python-online.cn/zh_CN/latest/c09/c09_16.html) -- 9.17 [Go原生协程:goroutine](http://python-online.cn/zh_CN/latest/c09/c09_17.html) -- 9.10 [理解语句块与作用域](http://python-online.cn/zh_CN/latest/c09/c09_18.html) -- 9.9 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_19.html) -- 9.12 [详解数据类型:信道](http://python-online.cn/zh_CN/latest/c09/c09_23.html) -- 9.14 [几个信道死锁经典错误案例详解](http://python-online.cn/zh_CN/latest/c09/c09_24.html) -- 9.12 [Go语言的包依赖管理](http://python-online.cn/zh_CN/latest/c09/c09_25.html) -- 9.13 [go 命令详解](http://python-online.cn/zh_CN/latest/c09/c09_27.html) -- 9.30 [函数式编程](http://python-online.cn/zh_CN/latest/c09/c09_30.html) -- 9.31 [todo](http://python-online.cn/zh_CN/latest/c09/todo.html) +- 9.17 [理解语句块与作用域](http://python-online.cn/zh_CN/latest/c09/c09_17.html) +- 9.18 [理解 Go 协程:goroutine](http://python-online.cn/zh_CN/latest/c09/c09_18.html) +- 9.19 [学习 Go 协程:详解信道/通道](http://python-online.cn/zh_CN/latest/c09/c09_19.html) +- 9.20 [几个信道死锁经典错误案例详解](http://python-online.cn/zh_CN/latest/c09/c09_20.html) +- 9.21 [学习 Go 协程:WaitGroup](http://python-online.cn/zh_CN/latest/c09/c09_21.html) +- 9.23 [学习一些常见的并发模型](http://python-online.cn/zh_CN/latest/c09/c09_23.html) +- 9.9 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_24.html) +- 9.25 [Go语言的包依赖管理](http://python-online.cn/zh_CN/latest/c09/c09_25.html) +- 9.27 [go 命令详解](http://python-online.cn/zh_CN/latest/c09/c09_27.html) +- 9.28 [函数式编程](http://python-online.cn/zh_CN/latest/c09/c09_28.html) ## 第十章:有趣的工具 - 10.1 [情人节来了,教你使用 Python 来表白](http://python-online.cn/zh_CN/latest/c10/c10_01.html) diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index c28cdd7..378d9cf 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -1178,9 +1178,14 @@ $ cat test.log hello, python ``` +以上两点,学习自董伟明的文章:[一些你不知道的Python Tips](https://mp.weixin.qq.com/s/KTLRwzCM7lOvBF4IEGuBPg) + +## 38. site 和 dist 的区别 + +如果你手动安装python,它会直接使用目录site-packages。 +linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ -以上两点,学习自董伟明的文章:[一些你不知道的Python Tips](https://mp.weixin.qq.com/s/KTLRwzCM7lOvBF4IEGuBPg) diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 4eff488..0cf3c8e 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1297,6 +1297,12 @@ logging 模块呢,或者直接 f.write()。 以上两点,学习自董伟明的文章:\ `一些你不知道的Python Tips `__ +38. site 和 dist 的区别 +----------------------- + +如果你手动安装python,它会直接使用目录site-packages。 +linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ + 附录:参考文章 -------------- diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md index cfd7fbe..8f78313 100644 --- a/source/c01/c01_29.md +++ b/source/c01/c01_29.md @@ -8,3 +8,4 @@ +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index 1b85788..e80932e 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -4,3 +4,8 @@ 参考学习地址:https://realpython.com/cpython-source-code-guide/ 基于 Python 3.6 的源码分析:https://he11olx.com/ + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md index 7068164..4f70fed 100644 --- a/source/c01/c01_30.md +++ b/source/c01/c01_30.md @@ -4,3 +4,11 @@ [书栈网](https://www.bookstack.cn/rank?tab=popular) ![](http://image.python-online.cn/20200104144109.png) + + + + + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index e93a544..d43e4b5 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -5,4 +5,9 @@ |image0| +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/20200104144109.png diff --git a/source/c01/c01_31.md b/source/c01/c01_31.md new file mode 100644 index 0000000..9671c96 --- /dev/null +++ b/source/c01/c01_31.md @@ -0,0 +1,23 @@ +# 1.31 学习 Pillow 笔记 + + + +## 1. 安装 pillow + +``` +pip install pillow +``` + + + +## 2. 使用 + +ARGB 是一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。 + +RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 + + + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index 2328dec..472e323 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -17,5 +17,7 @@ ARGB RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 -3. --- +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md new file mode 100644 index 0000000..c71a352 --- /dev/null +++ b/source/c01/c01_32.md @@ -0,0 +1,37 @@ +# 1.32 在 CentOS 7.2 上安装 Python3.7 + +首先下载 python3.7的源码包,然后解压 + +```shell +$ cd ~ +$ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz +$ tar xf Python-3.7.1.tgz && cd Python-3.7.1 +``` + +安装 一些依赖包 + +```shell +$ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y +``` + +编译安装 + +```shell +$ ./configure +$ make +$ sudo make install +``` + +至此,你已经成功安装 了 Python3, pip3,setuptools + +requests.get("https://www.baidu.com") + +``` +python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple +``` + + + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst new file mode 100644 index 0000000..127c5f2 --- /dev/null +++ b/source/c01/c01_32.rst @@ -0,0 +1,37 @@ +1.32 在 CentOS 7.2 上安装 Python3.7 +=================================== + +首先下载 python3.7的源码包,然后解压 + +.. code:: shell + + $ cd ~ + $ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz + $ tar xf Python-3.7.1.tgz && cd Python-3.7.1 + +安装 一些依赖包 + +.. code:: shell + + $ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y + +编译安装 + +.. code:: shell + + $ ./configure + $ make + $ sudo make install + +至此,你已经成功安装 了 Python3, pip3,setuptools + +requests.get(“https://www.baidu.com”) + +:: + + python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c01/c01_33.md b/source/c01/c01_33.md new file mode 100644 index 0000000..b856d20 --- /dev/null +++ b/source/c01/c01_33.md @@ -0,0 +1,52 @@ +# 1.33 如何调试已经运行中的程序 + +官方原始wiki:https://wiki.python.org/moin/DebuggingWithGdb + +在CentOS 下,安装包过程,官方给的不够详细。这里记录一下 + +先安装 yum-utils,装完后就能使用 debuginfo + +```shell +sudo yum install yum-utils +``` + +然后使用debuginfo 安装 glibc,不过在安装之前,有可能 你需要先配置debuginfo的仓库,编辑`/etc/yum.repos.d/CentOS-Debuginfo.repo` + +``` +#Debug Info +[debuginfo] +name=CentOS-$releasever - DebugInfo +# CentOS-4 +#baseurl=http://debuginfo.centos.org/$releasever/ +# CentOS-5 +baseurl=http://debuginfo.centos.org/$releasever/$basearch/ +gpgcheck=0 +enabled=1 +# CentOS-4 +#gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-$releasever +# CentOS-5 +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5 +protect=1 +``` + +然后就可以安装 `glibc` 了。 + +```shell +yum --nogpgcheck --enablerepo=debuginfo install glibc-debuginfo +sudo debuginfo-install glibc +``` + +最后安装 `python-debuginfo` + +```shell +sudo yum install gdb python-debuginfo +``` + + + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) + + + diff --git a/source/c01/c01_33.rst b/source/c01/c01_33.rst new file mode 100644 index 0000000..32d7caa --- /dev/null +++ b/source/c01/c01_33.rst @@ -0,0 +1,50 @@ +1.33 如何调试已经运行中的程序 +============================= + +官方原始wiki:https://wiki.python.org/moin/DebuggingWithGdb + +在CentOS 下,安装包过程,官方给的不够详细。这里记录一下 + +先安装 yum-utils,装完后就能使用 debuginfo + +.. code:: shell + + sudo yum install yum-utils + +然后使用debuginfo 安装 glibc,不过在安装之前,有可能 +你需要先配置debuginfo的仓库,编辑\ ``/etc/yum.repos.d/CentOS-Debuginfo.repo`` + +:: + + #Debug Info + [debuginfo] + name=CentOS-$releasever - DebugInfo + # CentOS-4 + #baseurl=http://debuginfo.centos.org/$releasever/ + # CentOS-5 + baseurl=http://debuginfo.centos.org/$releasever/$basearch/ + gpgcheck=0 + enabled=1 + # CentOS-4 + #gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-$releasever + # CentOS-5 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5 + protect=1 + +然后就可以安装 ``glibc`` 了。 + +.. code:: shell + + yum --nogpgcheck --enablerepo=debuginfo install glibc-debuginfo + sudo debuginfo-install glibc + +最后安装 ``python-debuginfo`` + +.. code:: shell + + sudo yum install gdb python-debuginfo + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c01/c01_34.md b/source/c01/c01_34.md new file mode 100644 index 0000000..df7a029 --- /dev/null +++ b/source/c01/c01_34.md @@ -0,0 +1,90 @@ +# 1.34 每日一库:sh,最优雅的命令调用方式 + +在编写 Python 脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 os.popen,os.system,commands,还有 subprocess。 + +今天明哥要介绍一种更加优雅的方法,就是 `sh` 这个第三方库,它能让你像调用方法那样去调用系统中的命令。 + +使用前,当然是安装它,`sh` 支持 Python 2 也支持 Python3,这里以 Python 3 为例。 + +```shell +$ python3 -m pip install sh +``` + +这里要注意一点,虽然在 Windows 上也可以安装成功,但是并不能使用,如果你尝试在 Windows 导入 它,会友好地提示你,该库只适用于 Linux 及 OSX 系统,假如你也想要在 Windows 使用,它推荐你使用它的 兄弟库 - `pbs` (https://pypi.org/project/pbs/)。 + +![](http://image.python-online.cn/20200227201644.png) + + + +安装完成后,就可以直接使用它了,以下几个示例,非常简单,简单到我感觉只要 demo ,而不需要任何的中文解释就可以让你知道他是如何使用的。 + + + +### 1. 列出目录文件 + +使用 `ls` + +```python +>>> import sh +>>> sh.ls("/home", "-l", color="never") +total 4 +drwx------ 3 centos centos 4096 Mar 8 2019 centos +``` + +使用 `glob` + +```python +>>> sh.glob("/etc/*.conf") +['/etc/mke2fs.conf', '/etc/dnsmasq.conf', '/etc/asound.conf'] +``` + + + +### 调用程序 + +```python +>>> import sh +>>> r=sh.Command('/root/test.py') +>>> r() +hello,world +``` + +上面我们的 ls ,也可以通过这种方式执行,只是不能再加参数了。 + +```python +sh.Command("ls")() +``` + + + +### 管道 + +```python +>>> print(sh.sort(sh.du(sh.glob('*'),'-shc'),'-rn')) +712K distribute-0.6.49.tar.gz +672K setuptools-1.1.5.tar.gz +548K get-pip.py +``` + +管道是有序的,默认由内而外,但如果需要并行呢?加个_piped=True + +```python +>>> for line in sh.tr(sh.tail("-f", "/home/mysql/mysql/log/alert.log", _piped=True), "[:upper:]", "[:lower:]", _iter=True): +... print line +... +innodb: doublewrite buffer not found: creating new + +innodb: doublewrite buffer created + +innodb: 127 rollback segment(s) active. + +innodb: creating foreign key constraint system tables + +innodb: foreign key constraint system tables created +``` + + + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst new file mode 100644 index 0000000..f37de8a --- /dev/null +++ b/source/c01/c01_34.rst @@ -0,0 +1,96 @@ +1.34 每日一库:sh,最优雅的命令调用方式 +======================================= + +在编写 Python +脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 +os.popen,os.system,commands,还有 subprocess。 + +今天明哥要介绍一种更加优雅的方法,就是 ``sh`` +这个第三方库,它能让你像调用方法那样去调用系统中的命令。 + +使用前,当然是安装它,\ ``sh`` 支持 Python 2 也支持 Python3,这里以 +Python 3 为例。 + +.. code:: shell + + $ python3 -m pip install sh + +这里要注意一点,虽然在 Windows +上也可以安装成功,但是并不能使用,如果你尝试在 Windows 导入 +它,会友好地提示你,该库只适用于 Linux 及 OSX 系统,假如你也想要在 +Windows 使用,它推荐你使用它的 兄弟库 - ``pbs`` +(https://pypi.org/project/pbs/)。 + +|image0| + +安装完成后,就可以直接使用它了,以下几个示例,非常简单,简单到我感觉只要 +demo ,而不需要任何的中文解释就可以让你知道他是如何使用的。 + +1. 列出目录文件 +~~~~~~~~~~~~~~~ + +使用 ``ls`` + +.. code:: python + + >>> import sh + >>> sh.ls("/home", "-l", color="never") + total 4 + drwx------ 3 centos centos 4096 Mar 8 2019 centos + +使用 ``glob`` + +.. code:: python + + >>> sh.glob("/etc/*.conf") + ['/etc/mke2fs.conf', '/etc/dnsmasq.conf', '/etc/asound.conf'] + +调用程序 +~~~~~~~~ + +.. code:: python + + >>> import sh + >>> r=sh.Command('/root/test.py') + >>> r() + hello,world + +上面我们的 ls ,也可以通过这种方式执行,只是不能再加参数了。 + +.. code:: python + + sh.Command("ls")() + +管道 +~~~~ + +.. code:: python + + >>> print(sh.sort(sh.du(sh.glob('*'),'-shc'),'-rn')) + 712K distribute-0.6.49.tar.gz + 672K setuptools-1.1.5.tar.gz + 548K get-pip.py + +管道是有序的,默认由内而外,但如果需要并行呢?加个_piped=True + +.. code:: python + + >>> for line in sh.tr(sh.tail("-f", "/home/mysql/mysql/log/alert.log", _piped=True), "[:upper:]", "[:lower:]", _iter=True): + ... print line + ... + innodb: doublewrite buffer not found: creating new + + innodb: doublewrite buffer created + + innodb: 127 rollback segment(s) active. + + innodb: creating foreign key constraint system tables + + innodb: foreign key constraint system tables created + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.python-online.cn/20200227201644.png diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md new file mode 100644 index 0000000..ecf89c7 --- /dev/null +++ b/source/c01/c01_35.md @@ -0,0 +1,342 @@ +# 1.35 使用 Python 远程登陆服务器的利器 + +在使用 Python 写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 + +在 shell 环境中,我们是这样子做的。 + +```shell +$ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l" +``` + +然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样 + +```shell +host: xx.xx.xx.xx, port: xx +Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts. +Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server. +total 4 +-rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc +``` + +对于直接使用 shell 命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果 + +## 1. 使用 subprocess + +若是使用 Python 来做这件事,通常我们会第一时间,想到使用 os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。 + +但是据我所知,这些库获取的 output 不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息) + +所以每次都要对 output 进行的数据清洗,然后整理格式化,才能得到我们想要的数据。 + +用 subprocess 举个例子,就像这样子 + +```python +import subprocess +ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'" +status, output = subprocess.getstatusoutput(ssh_cmd) + +# 数据清理,格式化的就不展示了 + +``` + + + +通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点 + +- **痛点一**:需要额外安装 sshpass(如果不免密的话) +- **痛点二**:干扰信息太多,数据清理、格式化相当麻烦 +- **痛点三**:代码实现不够优雅(有点土),可读性太差 +- **痛点四**:ssh 连接不能复用,一次连接仅能执行一次 +- **痛点五**:代码无法全平台,仅能在 Linux 和 OSX 上使用 + + + +为了解决这几个问题,我搜索了全网关于 Python ssh 的文章,没有看到有完整介绍这方面的技巧的。 + + + +为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn (https://github.com/BingmingWong/awesome-python-cn)。 + +期望在这里,找到有一些关于 远程连接 的一些好用的库。 + +还真的被我找到了两个 + +- sh.ssh +- Paramiko + + + +## 2. 使用 sh.ssh + +首先来介绍第一个,`sh.ssh` + +`sh` 是一个可以让你通过函数的调用来完成 Linxu/OSX 系统命令的一个库,非常好用,关于它有机会也写篇介绍。 + +```shell +$ python3 -m pip install sh +``` + + + +今天只介绍它其中的一个函数:`ssh` + +通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。 + +这段代码可以实现免密登陆,并执行我们的命令 `ls -l` + +```python +from sh import ssh +output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l") +print(output) +``` + +但有可能 ,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。 + +问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python 中要如何实现呢? + +原来 ssh 方法接收一个 `_out` 参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。 + +这就好办了呀。 + +我只要识别到有 `password:` 字样,就往标准输入写入我的密码就好了呀。 + + + +完整代码如下: + +```python +import sys +from sh import ssh + +aggregated = "" +def ssh_interact(char, stdin): + global aggregated + sys.stdout.write(char.encode()) + sys.stdout.flush() + aggregated += char + if aggregated.endswith("password: "): + stdin.put("you_password\n") + +output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact) +print(output) +``` + +这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。 + +尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。 + +通过调试查看源代码,仍然查不到问题所在,于是去 [Github](https://github.com/amoffat/sh/issues/393) 上搜了下,原来在 2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 `sh.ssh` 的人并不多,于是我又“追问”了下,期望能得到回复。 + +![](http://image.python-online.cn/20200228085749.png) + +以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 + +为了感受 `sh.ssh` 的使用效果,我设置了机器互信免密,然后使用如下这段代码。 + +```python +from sh import ssh + +my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22") + +# 相当于执行登陆一次执行一次命令,执行完就退出登陆 +print(my_server.ls()) + +# 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接 +time.sleep(5) + +# 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1 +print(my_server.ifconfig()) +``` + +惊奇地发现使用 `bake` 这种方式,`my_server.ls()` 和 `my_server.ifconfig()` 这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行 top 命令看到已连接的终端的变化,会先 `+1` 再 `-1`,说明两次命令的执行是通过两次连接实现的。 + +如此看来,使用 `sh.ssh` 可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。 + +但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。 + +最重要的一点是, `sh` 这个模块,仅支持 Linxu/OSX ,在 Windows 你得使用它的兄弟库 - `pbs` ,然后我又去 pypi 看了一眼 [pbs](https://pypi.org/project/pbs/),已经 “年久失修”,没人维护了。 + +![](http://image.python-online.cn/20200228093627.png) + +至此,我离 “卒”,就差最后一根稻草了。 + + + +## 3. 使用 paramiko + +带着最后一丝希望,我尝试使用了 `paramiko` 这个库,终于在 `paramiko` 这里,找回了本应属于 Python 的那种优雅。 + +你可以通过如下命令去安装它 + +``` +$ python3 -m pip install paramiko +``` + + + +然后接下来,就介绍几种常用的 ssh 登陆的方法 + +### 方法1:基于用户名和密码的 sshclient 方式登录 + +然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接 + +```python +import paramiko + +ssh = paramiko.SSHClient() +# 允许连接不在know_hosts文件中的主机 +ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + +# 建立连接 +ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password") + +# 使用这个连接执行命令 +ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") + +# 获取输出 +print(ssh_stdout.read()) + +# 关闭连接 +ssh.close() +``` + + + +### 方法2:基于用户名和密码的 transport 方式登录 + +方法1 是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。 + +有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1 则无法实现,那就可以使用 transport 的方法。 + +```python +import paramiko + +# 建立连接 +trans = paramiko.Transport(("xx.xx.xx.xx", 22)) +trans.connect(username="root", password="you_passwd") + +# 将sshclient的对象的transport指定为以上的trans +ssh = paramiko.SSHClient() +ssh._transport = trans + +# 剩下的就和上面一样了 +ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") +print(ssh_stdout.read()) + +# 关闭连接 +trans.close() +``` + + + +### 方法3:基于公钥密钥的 SSHClient 方式登录 + +```python +import paramiko + +# 指定本地的RSA私钥文件 +# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 +pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') + +# 建立连接 +ssh = paramiko.SSHClient() +ssh.connect(hostname='xx.xx.xx.xx', + port=22, + username='you_username', + pkey=pkey) + +# 执行命令 +stdin, stdout, stderr = ssh.exec_command('ls -l') + +# 结果放到stdout中,如果有错误将放到stderr中 +print(stdout.read()) + +# 关闭连接 +ssh.close() +``` + + + +### 方法4:基于密钥的 Transport 方式登录 + +```python +import paramiko + +# 指定本地的RSA私钥文件 +# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 +pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') + +# 建立连接 +trans = paramiko.Transport(('xx.xx.xx.xx', 22)) +trans.connect(username='you_username', pkey=pkey) + +# 将sshclient的对象的transport指定为以上的trans +ssh = paramiko.SSHClient() +ssh._transport = trans + +# 执行命令,和传统方法一样 +stdin, stdout, stderr = ssh.exec_command('df -hl') +print(stdout.read().decode()) + +# 关闭连接 +trans.close() +``` + + + +以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用 **方法二** 和 **方法四** + +用完后,记得关闭连接。 + +### 实现 sftp 文件传输 + +同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现 sftp 文件传输。 + +```python +import paramiko + +# 实例化一个trans对象# 实例化一个transport对象 +trans = paramiko.Transport(('xx.xx.xx.xx', 22)) + +# 建立连接 +trans.connect(username='you_username', password='you_passwd') + +# 实例化一个 sftp对象,指定连接的通道 +sftp = paramiko.SFTPClient.from_transport(trans) + +# 发送文件 +sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt') + +# 下载文件 +sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt') +trans.close() +``` + + + +到这里,Paramiko 已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是 Windows,这里就有一件好事,一件坏事了,。 + +好事就是:paramiko 支持 windows + +坏事就是:你需要做很多复杂的准备,你可 google 解决,但是我建议你直接放弃,坑太深了。 + +![](http://image.python-online.cn/20200228111654.png) + +## 4. 写在最后 + +经过了一番对比,和一些实例的展示,可以看出 Paramiko 是一个专业、让人省心的 ssh 利器,个人认为 Paramiko 模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh 到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。 + +最后,希望这篇文章,能给你带来帮助。 + + + +## 5. 参考链接 + +- https://github.com/paramiko/paramiko +- http://docs.paramiko.org +- https://www.liujiangblog.com/blog/15/ + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst new file mode 100644 index 0000000..1d514e4 --- /dev/null +++ b/source/c01/c01_35.rst @@ -0,0 +1,363 @@ +1.35 使用 Python 远程登陆服务器的利器 +===================================== + +在使用 Python +写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 + +在 shell 环境中,我们是这样子做的。 + +.. code:: shell + + $ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l" + +然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样 + +.. code:: shell + + host: xx.xx.xx.xx, port: xx + Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts. + Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server. + total 4 + -rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc + +对于直接使用 shell +命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果 + +1. 使用 subprocess +------------------ + +若是使用 Python 来做这件事,通常我们会第一时间,想到使用 +os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。 + +但是据我所知,这些库获取的 output +不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息) + +所以每次都要对 output +进行的数据清洗,然后整理格式化,才能得到我们想要的数据。 + +用 subprocess 举个例子,就像这样子 + +.. code:: python + + import subprocess + ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'" + status, output = subprocess.getstatusoutput(ssh_cmd) + + # 数据清理,格式化的就不展示了 + + +通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点 + +- **痛点一**\ :需要额外安装 sshpass(如果不免密的话) +- **痛点二**\ :干扰信息太多,数据清理、格式化相当麻烦 +- **痛点三**\ :代码实现不够优雅(有点土),可读性太差 +- **痛点四**\ :ssh 连接不能复用,一次连接仅能执行一次 +- **痛点五**\ :代码无法全平台,仅能在 Linux 和 OSX 上使用 + +为了解决这几个问题,我搜索了全网关于 Python ssh +的文章,没有看到有完整介绍这方面的技巧的。 + +为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn +(https://github.com/BingmingWong/awesome-python-cn)。 + +期望在这里,找到有一些关于 远程连接 的一些好用的库。 + +还真的被我找到了两个 + +- sh.ssh +- Paramiko + +2. 使用 sh.ssh +-------------- + +首先来介绍第一个,\ ``sh.ssh`` + +``sh`` 是一个可以让你通过函数的调用来完成 Linxu/OSX +系统命令的一个库,非常好用,关于它有机会也写篇介绍。 + +.. code:: shell + + $ python3 -m pip install sh + +今天只介绍它其中的一个函数:\ ``ssh`` + +通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。 + +这段代码可以实现免密登陆,并执行我们的命令 ``ls -l`` + +.. code:: python + + from sh import ssh + output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l") + print(output) + +但有可能 +,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。 + +问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python +中要如何实现呢? + +原来 ssh 方法接收一个 ``_out`` +参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。 + +这就好办了呀。 + +我只要识别到有 ``password:`` 字样,就往标准输入写入我的密码就好了呀。 + +完整代码如下: + +.. code:: python + + import sys + from sh import ssh + + aggregated = "" + def ssh_interact(char, stdin): + global aggregated + sys.stdout.write(char.encode()) + sys.stdout.flush() + aggregated += char + if aggregated.endswith("password: "): + stdin.put("you_password\n") + + output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact) + print(output) + +这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。 + +尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。 + +通过调试查看源代码,仍然查不到问题所在,于是去 +`Github `__ 上搜了下,原来在 +2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 +``sh.ssh`` 的人并不多,于是我又“追问”了下,期望能得到回复。 + +|image0| + +以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 + +为了感受 ``sh.ssh`` +的使用效果,我设置了机器互信免密,然后使用如下这段代码。 + +.. code:: python + + from sh import ssh + + my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22") + + # 相当于执行登陆一次执行一次命令,执行完就退出登陆 + print(my_server.ls()) + + # 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接 + time.sleep(5) + + # 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1 + print(my_server.ifconfig()) + +惊奇地发现使用 ``bake`` 这种方式,\ ``my_server.ls()`` 和 +``my_server.ifconfig()`` +这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行 +top 命令看到已连接的终端的变化,会先 ``+1`` 再 +``-1``\ ,说明两次命令的执行是通过两次连接实现的。 + +如此看来,使用 ``sh.ssh`` +可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。 + +但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。 + +最重要的一点是, ``sh`` 这个模块,仅支持 Linxu/OSX ,在 Windows +你得使用它的兄弟库 - ``pbs`` ,然后我又去 pypi 看了一眼 +`pbs `__\ ,已经 “年久失修”,没人维护了。 + +|image1| + +至此,我离 “卒”,就差最后一根稻草了。 + +3. 使用 paramiko +---------------- + +带着最后一丝希望,我尝试使用了 ``paramiko`` 这个库,终于在 ``paramiko`` +这里,找回了本应属于 Python 的那种优雅。 + +你可以通过如下命令去安装它 + +:: + + $ python3 -m pip install paramiko + +然后接下来,就介绍几种常用的 ssh 登陆的方法 + +方法1:基于用户名和密码的 sshclient 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接 + +.. code:: python + + import paramiko + + ssh = paramiko.SSHClient() + # 允许连接不在know_hosts文件中的主机 + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + # 建立连接 + ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password") + + # 使用这个连接执行命令 + ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") + + # 获取输出 + print(ssh_stdout.read()) + + # 关闭连接 + ssh.close() + +方法2:基于用户名和密码的 transport 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +方法1 +是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。 + +有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1 +则无法实现,那就可以使用 transport 的方法。 + +.. code:: python + + import paramiko + + # 建立连接 + trans = paramiko.Transport(("xx.xx.xx.xx", 22)) + trans.connect(username="root", password="you_passwd") + + # 将sshclient的对象的transport指定为以上的trans + ssh = paramiko.SSHClient() + ssh._transport = trans + + # 剩下的就和上面一样了 + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") + print(ssh_stdout.read()) + + # 关闭连接 + trans.close() + +方法3:基于公钥密钥的 SSHClient 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + import paramiko + + # 指定本地的RSA私钥文件 + # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 + pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') + + # 建立连接 + ssh = paramiko.SSHClient() + ssh.connect(hostname='xx.xx.xx.xx', + port=22, + username='you_username', + pkey=pkey) + + # 执行命令 + stdin, stdout, stderr = ssh.exec_command('ls -l') + + # 结果放到stdout中,如果有错误将放到stderr中 + print(stdout.read()) + + # 关闭连接 + ssh.close() + +方法4:基于密钥的 Transport 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + import paramiko + + # 指定本地的RSA私钥文件 + # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 + pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') + + # 建立连接 + trans = paramiko.Transport(('xx.xx.xx.xx', 22)) + trans.connect(username='you_username', pkey=pkey) + + # 将sshclient的对象的transport指定为以上的trans + ssh = paramiko.SSHClient() + ssh._transport = trans + + # 执行命令,和传统方法一样 + stdin, stdout, stderr = ssh.exec_command('df -hl') + print(stdout.read().decode()) + + # 关闭连接 + trans.close() + +以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用 +**方法二** 和 **方法四** + +用完后,记得关闭连接。 + +实现 sftp 文件传输 +~~~~~~~~~~~~~~~~~~ + +同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现 +sftp 文件传输。 + +.. code:: python + + import paramiko + + # 实例化一个trans对象# 实例化一个transport对象 + trans = paramiko.Transport(('xx.xx.xx.xx', 22)) + + # 建立连接 + trans.connect(username='you_username', password='you_passwd') + + # 实例化一个 sftp对象,指定连接的通道 + sftp = paramiko.SFTPClient.from_transport(trans) + + # 发送文件 + sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt') + + # 下载文件 + sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt') + trans.close() + +到这里,Paramiko +已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是 +Windows,这里就有一件好事,一件坏事了,。 + +好事就是:paramiko 支持 windows + +坏事就是:你需要做很多复杂的准备,你可 google +解决,但是我建议你直接放弃,坑太深了。 + +|image2| + +4. 写在最后 +----------- + +经过了一番对比,和一些实例的展示,可以看出 Paramiko +是一个专业、让人省心的 ssh 利器,个人认为 Paramiko +模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh +到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。 + +最后,希望这篇文章,能给你带来帮助。 + +5. 参考链接 +----------- + +- https://github.com/paramiko/paramiko +- http://docs.paramiko.org +- https://www.liujiangblog.com/blog/15/ + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.python-online.cn/20200228085749.png +.. |image1| image:: http://image.python-online.cn/20200228093627.png +.. |image2| image:: http://image.python-online.cn/20200228111654.png diff --git a/source/c02/c02_03.md b/source/c02/c02_03.md index 2176d07..7c7623c 100644 --- a/source/c02/c02_03.md +++ b/source/c02/c02_03.md @@ -31,7 +31,7 @@ lock = threading.Lock() # 获取锁。未获取到会阻塞程序,直到获取到锁才会往下执行 lock.acquire() -# 释放锁,归回倘,其他人可以拿去用了 +# 释放锁,归还锁,其他人可以拿去用了 lock.release() ``` diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index b1ce3a8..d08d59d 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -35,7 +35,7 @@ # 获取锁。未获取到会阻塞程序,直到获取到锁才会往下执行 lock.acquire() - # 释放锁,归回倘,其他人可以拿去用了 + # 释放锁,归还锁,其他人可以拿去用了 lock.release() 需要注意的是,lock.acquire() 和 diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 4e6cb3f..5444f6a 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -152,8 +152,8 @@ class Seeker(threading.Thread): cond = threading.Condition() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') -seeker.start() -hider.start() +seeker.run() +hider.run() ``` 通过cond来通信,阻塞自己,并使对方执行。从而,达到有顺序的执行。 看下结果 diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index c93d0df..97a7a07 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -163,8 +163,8 @@ Condition和Event 是类似的,并没有多大区别。 cond = threading.Condition() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') - seeker.start() - hider.start() + seeker.run() + hider.run() 通过cond来通信,阻塞自己,并使对方执行。从而,达到有顺序的执行。 看下结果 diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index f7bb575..e74d9d1 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -97,7 +97,7 @@ running thread-12504:1 ... ``` -构建线程池的方法,是可以很灵活的,大家有举可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 +构建线程池的方法,是可以很灵活的,大家有空可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 ---- ![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 03fa42a..c7245f6 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -105,7 +105,7 @@ ... ... -构建线程池的方法,是可以很灵活的,大家有举可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 +构建线程池的方法,是可以很灵活的,大家有空可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 -------------- diff --git a/source/c02/c02_13.md b/source/c02/c02_13.md new file mode 100644 index 0000000..17c3df8 --- /dev/null +++ b/source/c02/c02_13.md @@ -0,0 +1,12 @@ +# 2.13 I/O多路复用:select/poll/epoll + +在讲解 select/poll/epoll 之前 ,先来了解一下什么是 `I/O多路复用` + +`I/O多路复用` ,英文全称为 `I/O multiplexing`,这个中文翻译和把 socket 翻译成 套接字一样,影响了我们对其概念的理解。 + +学习 `I/O多路复用`,最好的办法就是通过其模型及其发展历程来理解。 + +## 1. 什么是I/O多路复用? + +举个现实生活中的例子。 + diff --git a/source/c02/c02_13.rst b/source/c02/c02_13.rst new file mode 100644 index 0000000..3c53390 --- /dev/null +++ b/source/c02/c02_13.rst @@ -0,0 +1,14 @@ +2.13 I/O多路复用:select/poll/epoll +=================================== + +在讲解 select/poll/epoll 之前 ,先来了解一下什么是 ``I/O多路复用`` + +``I/O多路复用`` ,英文全称为 ``I/O multiplexing``\ ,这个中文翻译和把 +socket 翻译成 套接字一样,影响了我们对其概念的理解。 + +学习 ``I/O多路复用``\ ,最好的办法就是通过其模型及其发展历程来理解。 + +1. 什么是I/O多路复用? +---------------------- + +举个现实生活中的例子。 diff --git a/source/c07/c07_18.md b/source/c07/c07_18.md new file mode 100644 index 0000000..34d8b66 --- /dev/null +++ b/source/c07/c07_18.md @@ -0,0 +1,57 @@ +# 7.18 Shell中去除字符串前后空格的方法 + +原文:https://www.jb51.net/article/157327.htm + +经常碰到的场景,需要去除字符串中的前后的空格。在Shell中不像其他语言有strip()来处理,不过也是可以使用诸如awk等命令来处理。 + + **下面是一个简单示例:** + +``` +[root@localhost ~]``# echo ' A B C ' | awk '{gsub(/^\s+|\s+$/, "");print}' +``` + +- ^\s+ 匹配行首一个或多个空格 +- \s+$ 匹配行末一个或多个空格 +- ^\s+|\s+$ 同时匹配行首或者行末的空格 + +**如果不用awk命令,也可以使用eval命令来达到相同的目的** + +``` +[root@``local` `~]``# echo " A BC "`` ``A BC``[root@``local` `~]``# eval echo " A BC "``A BC +``` + +或者 + +``` +[root@linux ~]``# echo ' A BC ' | python -c "s=raw_input();print(s.strip())"``A BC +``` + +或者 + +``` +[root@linux ~]``# s=`echo " A BC "```[root@linux ~]``# echo $s``A BC +``` + +或者 + +``` +[root@linux ~]``# echo ' A BC ' | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g'``A BC +``` + +或者 + +``` +[root@linux ~]``# echo " A BC " | awk '$1=$1'``A BC +``` + +或者 + +``` +[root@linux ~]``# echo " A BC " | sed -r 's/^[ \t]+(.*)[ \t]+$//g'``A BC +``` + +或者 + +``` +[root@linux ~]``# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1'``A BC +``` \ No newline at end of file diff --git a/source/c07/c07_18.rst b/source/c07/c07_18.rst new file mode 100644 index 0000000..2f8eae3 --- /dev/null +++ b/source/c07/c07_18.rst @@ -0,0 +1,58 @@ +7.18 Shell中去除字符串前后空格的方法 +==================================== + +原文:https://www.jb51.net/article/157327.htm + +经常碰到的场景,需要去除字符串中的前后的空格。在Shell中不像其他语言有strip()来处理,不过也是可以使用诸如awk等命令来处理。 + +**下面是一个简单示例:** + +:: + + [root@localhost ~]``# echo ' A B C ' | awk '{gsub(/^\s+|\s+$/, "");print}' + +- ^:raw-latex:`\s`+ 匹配行首一个或多个空格 +- :raw-latex:`\s`+$ 匹配行末一个或多个空格 +- ^:raw-latex:`\s`+\|:raw-latex:`\s`+$ 同时匹配行首或者行末的空格 + +**如果不用awk命令,也可以使用eval命令来达到相同的目的** + +:: + + [root@``local` `~]``# echo " A BC "`` ``A BC``[root@``local` `~]``# eval echo " A BC "``A BC + +或者 + +:: + + [root@linux ~]``# echo ' A BC ' | python -c "s=raw_input();print(s.strip())"``A BC + +或者 + +:: + + [root@linux ~]``# s=`echo " A BC "```[root@linux ~]``# echo $s``A BC + +或者 + +:: + + [root@linux ~]``# echo ' A BC ' | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g'``A BC + +或者 + +:: + + [root@linux ~]``# echo " A BC " | awk '$1=$1'``A BC + +或者 + +:: + + [root@linux ~]``# echo " A BC " | sed -r 's/^[ \t]+(.*)[ \t]+$//g'``A BC + +或者 + +:: + + [root@linux ~]``# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1'``A BC diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md index 47996bd..a6a0a18 100644 --- a/source/c09/c09_03.md +++ b/source/c09/c09_03.md @@ -213,4 +213,8 @@ false ## 参考文章: -https://www.zhihu.com/question/26022206 \ No newline at end of file +https://www.zhihu.com/question/26022206 + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index 4776f45..8fb1b94 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -227,4 +227,9 @@ float32,精度不足,导致最后比较的结果是不相等(从小数点 https://www.zhihu.com/question/26022206 +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/20200120204329.png diff --git a/source/c09/c09_04.md b/source/c09/c09_04.md index 12555ae..18a750c 100644 --- a/source/c09/c09_04.md +++ b/source/c09/c09_04.md @@ -235,3 +235,6 @@ func main() { 我的公众号是: Go编程时光,欢迎大家关注 ``` + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_04.rst b/source/c09/c09_04.rst index cd779b7..269e2e4 100644 --- a/source/c09/c09_04.rst +++ b/source/c09/c09_04.rst @@ -237,3 +237,8 @@ byte数组 你好呀! 我的公众号是: Go编程时光,欢迎大家关注 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_05.md b/source/c09/c09_05.md index 4587576..8512c9d 100644 --- a/source/c09/c09_05.md +++ b/source/c09/c09_05.md @@ -233,3 +233,4 @@ myslice的第四个元素为: 8 +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_05.rst b/source/c09/c09_05.rst index 078769b..60d8531 100644 --- a/source/c09/c09_05.rst +++ b/source/c09/c09_05.rst @@ -226,3 +226,8 @@ myslice为 [5 6], 其长度为: 2 myslice的第四个元素为: 8 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_06.md b/source/c09/c09_06.md index 00b8f7d..ff42c55 100644 --- a/source/c09/c09_06.md +++ b/source/c09/c09_06.md @@ -276,3 +276,4 @@ func main() { // output: true ``` +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_06.rst b/source/c09/c09_06.rst index 485dafe..a81ab2d 100644 --- a/source/c09/c09_06.rst +++ b/source/c09/c09_06.rst @@ -286,4 +286,9 @@ Go 中确实不如 Python 那样灵活,bool 与 int // output: false // output: true +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/20200106201856.png diff --git a/source/c09/c09_07.md b/source/c09/c09_07.md index f9e51fe..de17532 100644 --- a/source/c09/c09_07.md +++ b/source/c09/c09_07.md @@ -224,3 +224,9 @@ func main() { fmt.Println(a) } ``` + + + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_07.rst b/source/c09/c09_07.rst index c0edb7e..656a812 100644 --- a/source/c09/c09_07.rst +++ b/source/c09/c09_07.rst @@ -214,3 +214,8 @@ ptr(pointer的简写),而这个变量,我们称之为指针变量。 modify(&a) fmt.Println(a) } + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_08.md b/source/c09/c09_08.md index 49e10be..c55f27d 100644 --- a/source/c09/c09_08.md +++ b/source/c09/c09_08.md @@ -254,3 +254,4 @@ func main() { +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_08.rst b/source/c09/c09_08.rst index 3c42144..53454fc 100644 --- a/source/c09/c09_08.rst +++ b/source/c09/c09_08.rst @@ -262,3 +262,8 @@ staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 compan - 当方法的首字母为大写时,这个方法对于所有包都是Public,其他包可以随意调用 - 当方法的首字母为小写时,这个方法是Private,其他包是无法访问的。 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_09.md b/source/c09/c09_09.md index 5aeaa9c..d4e6fa5 100644 --- a/source/c09/c09_09.md +++ b/source/c09/c09_09.md @@ -258,3 +258,4 @@ func main() { +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_09.rst b/source/c09/c09_09.rst index 1fc34ec..0185047 100644 --- a/source/c09/c09_09.rst +++ b/source/c09/c09_09.rst @@ -264,3 +264,8 @@ Go语言中的函数,在你定义的时候,就规定了此函数 fmt.Println(v) }) } + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_10.md b/source/c09/c09_10.md index e9c1586..908e388 100644 --- a/source/c09/c09_10.md +++ b/source/c09/c09_10.md @@ -120,7 +120,4 @@ func main() { - - - - +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_10.rst b/source/c09/c09_10.rst index 776b3f5..1fdfbd6 100644 --- a/source/c09/c09_10.rst +++ b/source/c09/c09_10.rst @@ -120,3 +120,8 @@ if - else if - else fmt.Println("已经成年了") } } + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_11.md b/source/c09/c09_11.md index b6586fd..237f7b0 100644 --- a/source/c09/c09_11.md +++ b/source/c09/c09_11.md @@ -250,3 +250,6 @@ hello xxxx ``` + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_11.rst b/source/c09/c09_11.rst index 39a7c63..368d5b8 100644 --- a/source/c09/c09_11.rst +++ b/source/c09/c09_11.rst @@ -252,3 +252,8 @@ default 的代码块。 hello xxxx + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_12.md b/source/c09/c09_12.md index c19da3f..43e5a29 100644 --- a/source/c09/c09_12.md +++ b/source/c09/c09_12.md @@ -166,3 +166,4 @@ hello, go +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_12.rst b/source/c09/c09_12.rst index 1a8b116..da6d6f0 100644 --- a/source/c09/c09_12.rst +++ b/source/c09/c09_12.rst @@ -168,3 +168,8 @@ range 后可接数组、切片,字符串等 hello, world hello, python hello, go + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_13.md b/source/c09/c09_13.md index 4eca702..eb1b60b 100644 --- a/source/c09/c09_13.md +++ b/source/c09/c09_13.md @@ -168,3 +168,6 @@ flag: .\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6 ``` + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_13.rst b/source/c09/c09_13.rst index f938ecc..f2aeb16 100644 --- a/source/c09/c09_13.rst +++ b/source/c09/c09_13.rst @@ -170,3 +170,8 @@ goto语句与标签之间不能有变量声明,否则编译错误。 :: .\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_14.md b/source/c09/c09_14.md index 2f19b4b..f97e985 100644 --- a/source/c09/c09_14.md +++ b/source/c09/c09_14.md @@ -225,3 +225,4 @@ func f() { +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_14.rst b/source/c09/c09_14.rst index f0b31bd..fcf950c 100644 --- a/source/c09/c09_14.rst +++ b/source/c09/c09_14.rst @@ -232,3 +232,8 @@ return 那里执行不就好了。 ...... return } + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_15.md b/source/c09/c09_15.md index 2cab642..108449f 100644 --- a/source/c09/c09_15.md +++ b/source/c09/c09_15.md @@ -230,3 +230,6 @@ func main() { 该订单总共需要支付 8000 元 ``` + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_15.rst b/source/c09/c09_15.rst index fc933dd..62763bc 100644 --- a/source/c09/c09_15.rst +++ b/source/c09/c09_15.rst @@ -236,3 +236,8 @@ typing)的定义是,只要你长得像鸭子,叫起来也像鸭子,那 您要购买1个iPhone计:8000元 您要购买1个耳机计:0元 该订单总共需要支付 8000 元 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_16.md b/source/c09/c09_16.md index 02ea177..6c2c2c8 100644 --- a/source/c09/c09_16.md +++ b/source/c09/c09_16.md @@ -94,3 +94,6 @@ a := 1 但是 make 就不一样了,它的地位无可替代,在使用slice、map以及channel的时候,还是要使用make进行初始化,然后才可以对他们进行操作。 + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_16.rst b/source/c09/c09_16.rst index 13d6be6..a9648b2 100644 --- a/source/c09/c09_16.rst +++ b/source/c09/c09_16.rst @@ -95,3 +95,8 @@ make:只能为 slice,map,chan 分配内存,并初始化,返回的是 但是 make 就不一样了,它的地位无可替代,在使用slice、map以及channel的时候,还是要使用make进行初始化,然后才可以对他们进行操作。 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_17.md b/source/c09/c09_17.md new file mode 100644 index 0000000..f4658eb --- /dev/null +++ b/source/c09/c09_17.md @@ -0,0 +1,148 @@ +# 9.17 理解语句块与作用域 + +由于 Go 使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 Go 中的语句块是怎么一回事? + +## 1. 显示语句块与隐式语句块 + +通俗地说,语句块是由花括弧(`{}`)所包含的一系列语句。 + +语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域。 + +用花括弧包含的语句块,属于显示语句块。 + +在 Go 中还有很多的隐式语句块: + +- 主语句块:包括所有源码,对应内置作用域 +- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 +- 文件语句块:包括该文件中的所有源码,对应文件级作用域 +- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 + +前面三点好理解,第四点举几个例子 + +for 循环完后,不能再使用变量 i + +```go +for i := 0; i < 5; i++ { + fmt.Println(i) +} +``` + +if 语句判断完后,同样不能再使用变量 i + +```go +if i := 0; i >= 0 { + fmt.Println(i) +} +``` + +switch 语句完了后,也是不是再使用变量 i + +```go +switch i := 2; i * 4 { +case 8: + fmt.Println(i) +default: + fmt.Println(“default”) +} +``` + +且每个 switch 语句的子句都是一个隐式的语句块 + +```go +switch i := 2; i * 4 { +case 8: + j := 0 + fmt.Println(i, j) +default: + // "j" is undefined here + fmt.Println(“default”) +} +// "j" is undefined here +``` + +## 2. 四种作用域的理解 + +变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 + +根据声明位置的不同,作用域可以分为以下四个类型: + +- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 +- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 +- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 +- 局部作用域:在自己的语句块内声明,包括函数,for、if 等语句块,或自定义的 {} 语句块形成的作用域,只在自己的局部作用域内可用 + +以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这里自己将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 + +对于作用域,有以下几点总结: + +- 低层作用域,可以访问高层作用域 +- 同一层级的作用域,是相互隔离的 +- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 + + + +在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 + +而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 + + + +## 3. 静态作用域与动态作用域 + +根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: + +- 静态作用域,如 Go 语言 +- 动态作用域,如 Shell 语言 + +具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 + +```python +#!/bin/bash +func01() { + local value=1 + func02 +} +func02() { + echo "func02 sees value as ${value}" +} + +# 执行函数 +func01 +func02 +``` + +从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 + +但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 是访问不了 value 变量的。 + +所以此时的输出结果是 + +```shell +func02 sees value as 1 +func02 sees value as +``` + +但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 name 这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 + +```go +import "fmt" + +func func01() { + fmt.Println("在 func01 函数中,name:", name) +} + +func main() { + var name string = "Python编程时光" + fmt.Println("在 main 函数中,name:", name) + + func01() +} +``` + +因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 + + + +参考文章:https://studygolang.com/articles/12632 + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_17.rst b/source/c09/c09_17.rst index 9926f95..6bad9b3 100644 --- a/source/c09/c09_17.rst +++ b/source/c09/c09_17.rst @@ -1,27 +1,159 @@ -9.17 Go原生协程:goroutine -========================== +9.17 理解语句块与作用域 +======================= -一台电脑能使用多少线程 +由于 Go +使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 +Go 中的语句块是怎么一回事? -协程与线程的关系 +1. 显示语句块与隐式语句块 +------------------------- -线程:抢占式多任务处理 +通俗地说,语句块是由花括弧(\ ``{}``\ )所包含的一系列语句。 -协程:非抢占式多任务处理由协程主动交出控制权 +语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域。 -coroutine +用花括弧包含的语句块,属于显示语句块。 -.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200202142007967.png - :alt: image-20200202142007967 +在 Go 中还有很多的隐式语句块: - image-20200202142007967 +- 主语句块:包括所有源码,对应内置作用域 +- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 +- 文件语句块:包括该文件中的所有源码,对应文件级作用域 +- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 -.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200202142004117.png - :alt: image-20200202142004117 +前面三点好理解,第四点举几个例子 - image-20200202142004117 +for 循环完后,不能再使用变量 i -.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200202142258895.png - :alt: image-20200202142258895 +.. code:: go - image-20200202142258895 + for i := 0; i < 5; i++ { + fmt.Println(i) + } + +if 语句判断完后,同样不能再使用变量 i + +.. code:: go + + if i := 0; i >= 0 { + fmt.Println(i) + } + +switch 语句完了后,也是不是再使用变量 i + +.. code:: go + + switch i := 2; i * 4 { + case 8: + fmt.Println(i) + default: + fmt.Println(“default”) + } + +且每个 switch 语句的子句都是一个隐式的语句块 + +.. code:: go + + switch i := 2; i * 4 { + case 8: + j := 0 + fmt.Println(i, j) + default: + // "j" is undefined here + fmt.Println(“default”) + } + // "j" is undefined here + +2. 四种作用域的理解 +------------------- + +变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 + +根据声明位置的不同,作用域可以分为以下四个类型: + +- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 +- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 +- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 +- 局部作用域:在自己的语句块内声明,包括函数,for、if + 等语句块,或自定义的 {} + 语句块形成的作用域,只在自己的局部作用域内可用 + +以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这里自己将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 + +对于作用域,有以下几点总结: + +- 低层作用域,可以访问高层作用域 +- 同一层级的作用域,是相互隔离的 +- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 + +在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 + +而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 + +3. 静态作用域与动态作用域 +------------------------- + +根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: + +- 静态作用域,如 Go 语言 +- 动态作用域,如 Shell 语言 + +具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 + +.. code:: python + + #!/bin/bash + func01() { + local value=1 + func02 + } + func02() { + echo "func02 sees value as ${value}" + } + + # 执行函数 + func01 + func02 + +从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 +value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 +func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 +func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 + +但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 +是访问不了 value 变量的。 + +所以此时的输出结果是 + +.. code:: shell + + func02 sees value as 1 + func02 sees value as + +但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 +name +这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 + +.. code:: go + + import "fmt" + + func func01() { + fmt.Println("在 func01 函数中,name:", name) + } + + func main() { + var name string = "Python编程时光" + fmt.Println("在 main 函数中,name:", name) + + func01() + } + +因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 + +参考文章:https://studygolang.com/articles/12632 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_18.md b/source/c09/c09_18.md new file mode 100644 index 0000000..77f5b73 --- /dev/null +++ b/source/c09/c09_18.md @@ -0,0 +1,131 @@ +# 9.18 理解 Go 协程:goroutine + +说到Go语言,很多没接触过它的人,对它的第一印象,一定是它从语言层面天生支持并发,非常方便,让开发者能快速写出高性能且易于理解的程序。 + +在 Python (为Py为例,主要是我比较熟悉,其他主流编程语言也类似)中,并发编程的门槛并不低,你要学习多进程,多线程,还要掌握各种支持并发的库 asyncio,aiohttp 等,同时你还要清楚它们之间的区别及优缺点,懂得在不同的场景选择不同的并发模式。 + +而 Golang 作为一门现代化的编程语言,它不需要你直面这些复杂的问题。在 Golang 里,你不需要学习如何创建进程池/线程池,也不需要知道什么情况下使用多线程,什么时候使用多进程。因为你没得选,也不需要选,它原生提供的 goroutine (也即协程)已经足够优秀,能够自动帮你处理好所有的事情,而你要做的只是执行它,就这么简单。 + +一个 goroutine 本身就是一个函数,当你直接调用时,它就是一个普通函数,如果你在调用前加一个关键字 `go` ,那你就开启了一个 goroutine。 + +```go +// 执行一个函数 +func() + +// 开启一个协程执行这个函数 +go func() +``` + + + +## 1. 协程的初步使用 + +一个 Go 程序的入口通常是 main 函数,程序启动后,main 函数最先运行,我们称之为 `main goroutine`。 + +在 main 中或者其下调用的代码中才可以使用 `go + func()` 的方法来启动协程。 + +main 的地位相当于主线程,当 main 函数执行完成后,这个线程也就终结了,其下的运行着的所有协程也不管代码是不是还在跑,也得乖乖退出。 + +因此如下这段代码运行完,只会输出 `hello, world` ,而不会输出`hello, go`(因为协程的创建需要时间,当 `hello, world`打印后,协程还没来得及并执行) + +```go +import "fmt" + +func mytest() { + fmt.Println("hello, go") +} + +func main() { + // 启动一个协程 + go mytest() + fmt.Println("hello, world") +} +``` + +对于刚学习Go的协程同学来说,可以使用 time.Sleep 来使 main 阻塞,使其他协程能够有机会运行完全,但你要注意的是,这并不是推荐的方式(后续我们会学习其他更优雅的方式)。 + +当我在代码中加入一行 time.Sleep 输出就符合预期了。 + +```go +import ( + "fmt" + "time" +) + +func mytest() { + fmt.Println("hello, go") +} + +func main() { + go mytest() + fmt.Println("hello, world") + time.Sleep(time.Second) +} +``` + +输出如下 + +``` +hello, world +hello, go +``` + + + +## 2. 多个协程的效果 + +为了让你看到并发的效果,这里举个最简单的例子 + +```go +import ( + "fmt" + "time" +) + +func mygo(name string) { + for i := 0; i < 10; i++ { + fmt.Printf("In goroutine %s\n", name) + // 为了避免第一个协程执行过快,观察不到并发的效果,加个休眠 + time.Sleep(10 * time.Millisecond) + } +} + +func main() { + go mygo("协程1号") // 第一个协程 + go mygo("协程2号") // 第二个协程 + time.Sleep(time.Second) +} +``` + +输出如下,可以观察到两个协程就如两个线程一样,并发执行 + +``` +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程1号 +In goroutine 协程2号 +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程1号 +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程2号 +In goroutine 协程1号 +In goroutine 协程2号 +``` + + + +通过以上简单的例子,是不是折服于Go的这种强大的并发特性,将同步代码转为异步代码,真的只要一个关键字就可以了,也不需要使用其他库,简单方便。 + +本篇只介绍了协程的简单使用,真正的并发程序还是要结合 信道 (channel)来实现。关于信道的内容,将在下一篇文章中介绍。 + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_18.rst b/source/c09/c09_18.rst index 5d64732..4cba05d 100644 --- a/source/c09/c09_18.rst +++ b/source/c09/c09_18.rst @@ -1,157 +1,145 @@ -9.10 理解语句块与作用域 -======================= +9.18 理解 Go 协程:goroutine +============================ -由于 Go -使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 -Go 中的语句块是怎么一回事? +说到Go语言,很多没接触过它的人,对它的第一印象,一定是它从语言层面天生支持并发,非常方便,让开发者能快速写出高性能且易于理解的程序。 -1. 显示语句块与隐式语句块 -------------------------- +在 Python +(为Py为例,主要是我比较熟悉,其他主流编程语言也类似)中,并发编程的门槛并不低,你要学习多进程,多线程,还要掌握各种支持并发的库 +asyncio,aiohttp +等,同时你还要清楚它们之间的区别及优缺点,懂得在不同的场景选择不同的并发模式。 -通俗地说,语句块是由花括弧(\ ``{}``\ )所包含的一系列语句。 +而 Golang 作为一门现代化的编程语言,它不需要你直面这些复杂的问题。在 +Golang +里,你不需要学习如何创建进程池/线程池,也不需要知道什么情况下使用多线程,什么时候使用多进程。因为你没得选,也不需要选,它原生提供的 +goroutine +(也即协程)已经足够优秀,能够自动帮你处理好所有的事情,而你要做的只是执行它,就这么简单。 -语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域,我会在下节讲到。 +一个 goroutine +本身就是一个函数,当你直接调用时,它就是一个普通函数,如果你在调用前加一个关键字 +``go`` ,那你就开启了一个 goroutine。 -用花括弧包含的语句块,属于显示语句块。 +.. code:: go -在 Go 中还有很多的隐式语句块: + // 执行一个函数 + func() -- 主语句块:包括所有源码,对应内置作用域 -- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 -- 文件语句块:包括该文件中的所有源码,对应文件级作用域 -- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 + // 开启一个协程执行这个函数 + go func() -前面三点好理解,第四点举几个例子 +1. 协程的初步使用 +----------------- -for 循环 +一个 Go 程序的入口通常是 main 函数,程序启动后,main +函数最先运行,我们称之为 ``main goroutine``\ 。 -.. code:: go +在 main 中或者其下调用的代码中才可以使用 ``go + func()`` +的方法来启动协程。 - for i := 0; i < 5; i++ { - fmt.Println(i) - } +main 的地位相当于主线程,当 main +函数执行完成后,这个线程也就终结了,其下的运行着的所有协程也不管代码是不是还在跑,也得乖乖退出。 -if 语句 +因此如下这段代码运行完,只会输出 ``hello, world`` +,而不会输出\ ``hello, go``\ (因为协程的创建需要时间,当 +``hello, world``\ 打印后,协程还没来得及并执行) .. code:: go - if i := 0; i >= 0 { - fmt.Println(i) - } - -switch 语句 - -.. code:: go + import "fmt" - switch i := 2; i * 4 { - case 8: - fmt.Println(i) - default: - fmt.Println(“default”) + func mytest() { + fmt.Println("hello, go") } -且每个 switch 语句的子句都是一个隐式的语句块 - -.. code:: go - - switch i := 2; i * 4 { - case 8: - j := 0 - fmt.Println(i, j) - default: - // "j" is undefined here - fmt.Println(“default”) + func main() { + // 启动一个协程 + go mytest() + fmt.Println("hello, world") } - // "j" is undefined here - -2. 四种作用域的理解 -------------------- - -变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 - -根据声明位置的不同,作用域可以分为以下四个类型: - -- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 -- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 -- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 -- 局部作用域:在自己的语句块内声明,包括函数,for、if - 等语句块,或自定义的 {} - 语句块形成的作用域,只在自己的局部作用域内可用 -以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这时将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 +对于刚学习Go的协程同学来说,可以使用 time.Sleep 来使 main +阻塞,使其他协程能够有机会运行完全,但你要注意的是,这并不是推荐的方式(后续我们会学习其他更优雅的方式)。 -对于作用域,有以下几点总结: +当我在代码中加入一行 time.Sleep 输出就符合预期了。 -- 低层作用域,可以访问高层作用域 -- 同一层级的作用域,是相互隔离的 -- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 - -在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 - -而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 - -3. 静态作用域与动态作用域 -------------------------- - -根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: - -- 静态作用域,如 Go 语言 -- 动态作用域,如 Shell 语言 - -具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 +.. code:: go -.. code:: python + import ( + "fmt" + "time" + ) - #!/bin/bash - func01() { - local value=1 - func02 + func mytest() { + fmt.Println("hello, go") } - func02() { - echo "func02 sees value as ${value}" - } - - # 执行函数 - func01 - func02 -从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 -value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 -func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 -func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 + func main() { + go mytest() + fmt.Println("hello, world") + time.Sleep(time.Second) + } -但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 -是访问不了 value 变量的。 +输出如下 -所以此时的输出结果是 +:: -.. code:: shell + hello, world + hello, go - func02 sees value as 1 - func02 sees value as +2. 多个协程的效果 +----------------- -但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 -name -这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 +为了让你看到并发的效果,这里举个最简单的例子 .. code:: go - import "fmt" - - func func01() { - fmt.Println("在 func01 函数中,name:", name) + import ( + "fmt" + "time" + ) + + func mygo(name string) { + for i := 0; i < 10; i++ { + fmt.Printf("In goroutine %s\n", name) + // 为了避免第一个协程执行过快,观察不到并发的效果,加个休眠 + time.Sleep(10 * time.Millisecond) + } } - func main() { - var name string = "Python编程时光" - fmt.Println("在 main 函数中,name:", name) - - func01() + func main() { + go mygo("协程1号") // 第一个协程 + go mygo("协程2号") // 第二个协程 + time.Sleep(time.Second) } -因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 - -参考文章:https://studygolang.com/articles/12632 +输出如下,可以观察到两个协程就如两个线程一样,并发执行 + +:: + + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程1号 + In goroutine 协程2号 + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程1号 + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程2号 + In goroutine 协程1号 + In goroutine 协程2号 + +通过以上简单的例子,是不是折服于Go的这种强大的并发特性,将同步代码转为异步代码,真的只要一个关键字就可以了,也不需要使用其他库,简单方便。 + +本篇只介绍了协程的简单使用,真正的并发程序还是要结合 信道 +(channel)来实现。关于信道的内容,将在下一篇文章中介绍。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! diff --git a/source/c09/c09_19.md b/source/c09/c09_19.md new file mode 100644 index 0000000..ee1eac3 --- /dev/null +++ b/source/c09/c09_19.md @@ -0,0 +1,338 @@ +# 9.19 学习 Go 协程:详解信道/通道 + +Go 语言之所以开始流行起来,很大一部分原因是因为它自带的并发机制。 + +如果说 goroutine 是 Go语言程序的并发体的话,那么 channel(信道) 就是 它们之间的通信机制。channel,是一个可以让一个 goroutine 与另一个 goroutine 传输信息的通道,我把他叫做信道,也有人将其翻译成通道,二者都是一个概念。 + +信道,就是一个管道,连接多个goroutine程序 ,它是一种队列式的数据结构,遵循先入先出的规则。 + +## 1. 信道的定义与使用 + +每个信道都只能传递一种数据类型的数据,所以在你声明的时候,你得指定数据类型(string int 等等) + +```go +var 信道实例 chan 信道类型 + +// 定义容量为10的信道 +var 信道实例 [10]chan 信道类型 +``` + +声明后的信道,其零值是nil,无法直接使用,必须配合make函进行初始化。 + +```go +信道实例 = make(chan 信道类型) +``` + +亦或者,上面两行可以合并成一句,以下我都使用这样的方式进行信道的声明 + +```go +信道实例 := make(chan 信道类型) +``` + +假如我要创建一个可以传输int类型的信道,可以这样子写。 + +```go +// 定义信道 +pipline := make(chan int) +``` + +信道的数据操作,无非就两种:发送数据与读取数据 + +```go +// 往信道中发送数据 +pipline<- 200 + +// 从信道中取出数据,并赋值给mydata +mydata := <-pipline +``` + +信道用完了,可以对其进行关闭,避免有人一直在等待。但是你关闭信道后,接收方仍然可以从信道中取到数据,只是接收到的会永远是 0。 + +```go +close(pipline) +``` + +对一个已关闭的信道再关闭,是会报错的。所以我们还要学会,如何判断一个信道是否被关闭? + +当从信道中读取数据时,可以有多个返回值,其中第二个可以表示 信道是否被关闭,如果已经被关闭,ok 为 false,若还没被关闭,ok 为true。 + +```go +x, ok := <-pipline +``` + + + +## 2. 信道的容量与长度 + +一般创建信道都是使用 make 函数,make 函数接收两个参数 + +- 第一个参数:必填,指定信道类型 +- 第二个参数:选填,不填默认为0,指定信道的**容量**(可缓存多少数据) + +对于信道的容量,很重要,这里要多说几点: + +- 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为**无缓冲信道**。 +- 当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 利用这点可以利用信道来做锁。 +- 当容量大于1时,信道中可以存放多个数据,可以用于多个协程之间的通信管道,共享资源。 + + + +至此我们知道,信道就是一个容器。 + +若将它比做一个纸箱子 + +- 它可以装10本书,代表其容量为10 +- 当前只装了1本书,代表其当前长度为1 + + + +信道的容量,可以使用 cap 函数获取 ,而信道的长度,可以使用 len 长度获取。 + +```go +package main + +import "fmt" + +func main() { + pipline := make(chan int, 10) + fmt.Printf("信道可缓冲 %d 个数据\n", cap(pipline)) + pipline<- 1 + fmt.Printf("信道中当前有 %d 个数据", len(pipline)) +} +``` + +输出如下 + +``` +信道可缓冲 10 个数据 +信道中当前有 1 个数据 +``` + + + +## 3. 缓冲信道与无缓冲信道 + +按照是否可缓冲数据可分为:**缓冲信道** 与 **无缓冲信道** + +**缓冲信道** + +允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。 + +```go +pipline := make(chan int, 10) +``` + + + +**无缓冲信道** + +在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,信道中无法存储数据。也就是说发送端和接收端是同步运行的。 + +```go +pipline := make(chan int) + +// 或者 +pipline := make(chan int, 0) +``` + + + +## 4. 双向信道与单向信道 + +通常情况下,我们定义的信道都是双向通道,可发送数据,也可以接收数据。 + +但有时候,我们希望对信道的数据流向做一些控制,比如这个信道只能接收数据或者这个信道只能发送数据。 + + + +因此,就有了 **双向信道** 和 **单向信道** 两种分类。 + +**双向信道** + +默认情况下你定义的信道都是双向的,比如下面代码 + +```go +import ( + "fmt" + "time" +) + +func main() { + pipline := make(chan int) + + go func() { + fmt.Println("准备发送数据: 100") + pipline <- 100 + }() + + go func() { + num := <-pipline + fmt.Printf("接收到的数据是: %d", num) + }() + // 主函数sleep,使得上面两个goroutine有机会执行 + time.Sleep(1) +} +``` + + + +**单向信道** + +单向信道,可以细分为 **只读信道** 和 **只写信道**。 + +定义只读信道 + +```go +var pipline = make(chan int) +type Receiver = <-chan int // 关键代码:定义别名类型 +var receiver Receiver = pipline +``` + +定义只写信道 + +```go +var pipline = make(chan int) +type Sender = chan<- int // 关键代码:定义别名类型 +var sender Sender = pipline +``` + + + +仔细观察,区别在于 `<-` 符号在关键字 `chan` 的左边还是右边。 + +- `<-chan` 表示这个信道,只能从里发出数据,对于程序来说就是只读 +- `chan<-` 表示这个信道,只能从外面接收数据,对于程序来说就是只写 + + + +有同学可能会问:为什么还要先声明一个双向信道,再定义单向通道呢?比如这样写 + +```go +type Sender = chan<- int +sender := make(Sender) +``` + +代码是没问题,但是你要明白信道的意义是什么?(**以下是我个人见解** + +信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入了吗,没什么用。所以只读信道和只写信道,唇亡齿寒,缺一不可。 + +当然了,若你往一个只读信道中写入数据 ,或者从一个只写信道中读取数据 ,都会出错。 + +完整的示例代码如下,供你参考: + +```go +import ( + "fmt" + "time" +) + //定义只写信道类型 +type Sender = chan<- int + +//定义只读信道类型 +type Receiver = <-chan int + +func main() { + var pipline = make(chan int) + + go func() { + var sender Sender = pipline + fmt.Println("准备发送数据: 100") + sender <- 100 + }() + + go func() { + var receiver Receiver = pipline + num := <-receiver + fmt.Printf("接收到的数据是: %d", num) + }() + // 主函数sleep,使得上面两个goroutine有机会执行 + time.Sleep(1) +} +``` + + + +## 5. 遍历信道 + +遍历信道,可以使用 for 搭配 range关键字,在range时,要确保信道是处于关闭状态,否则循环会阻塞。 + +```go +import "fmt" + +func fibonacci(mychan chan int) { + n := cap(mychan) + x, y := 1, 1 + for i := 0; i < n; i++ { + mychan <- x + x, y = y, x+y + } + // 记得 close 信道 + // 不然主函数中遍历完并不会结束,而是会阻塞。 + close(mychan) +} + +func main() { + pipline := make(chan int, 10) + + go fibonacci(pipline) + + for k := range pipline { + fmt.Println(k) + } +} +``` + + + +## 6. 用信道来做锁 + +当信道里的数据量已经达到设定的容量时,此时再往里发送数据会阻塞整个程序。 + +利用这个特性,可以用当他来当程序的锁。 + +示例如下,详情可以看注释 + +```go +package main + +import { + "fmt" + "time" +} + +// 由于 x=x+1 不是原子操作 +// 所以应避免多个协程对x进行操作 +// 使用容量为1的信道可以达到锁的效果 +func increment(ch chan bool, x *int) { + ch <- true + *x = *x + 1 + <- ch +} + +func main() { + // 注意要设置容量为 1 的缓冲信道 + pipline := make(chan bool, 1) + + var x int + for i:=0;i<1000;i++{ + go increment(pipline, &x) + } + + // 确保所有的协程都已完成 + // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep + time.Sleep(3) + fmt.Println("x 的值:", x) +} +``` + +输出如下 + +``` +x 的值:1000 +``` + +如果不加锁,输出会小于1000。 + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) diff --git a/source/c09/c09_19.rst b/source/c09/c09_19.rst index 34f05dc..d0ae3d6 100644 --- a/source/c09/c09_19.rst +++ b/source/c09/c09_19.rst @@ -1,177 +1,333 @@ -9.9 Go语言命名编码规范 -====================== +9.19 学习 Go 协程:详解信道/通道 +================================ -每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 +Go 语言之所以开始流行起来,很大一部分原因是因为它自带的并发机制。 -以下内容整理自:\ `Go语言(Golang)编码规范 `__ +如果说 goroutine 是 Go语言程序的并发体的话,那么 channel(信道) 就是 +它们之间的通信机制。channel,是一个可以让一个 goroutine 与另一个 +goroutine +传输信息的通道,我把他叫做信道,也有人将其翻译成通道,二者都是一个概念。 -命名规范分为以下几点 +信道,就是一个管道,连接多个goroutine程序 +,它是一种队列式的数据结构,遵循先入先出的规则。 -**1. 文件命名** +1. 信道的定义与使用 +------------------- -文件名应一律使用小写(因为Windows的原因), 不同单词之间用下划线分割。 +每个信道都只能传递一种数据类型的数据,所以在你声明的时候,你得指定数据类型(string +int 等等) -应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 Gogs -的入口应当为 gogs.go +.. code:: go -**2. 常量命名** + var 信道实例 chan 信道类型 -- 常量均需使用全部大写字母组成,并使用下划线分词: + // 定义容量为10的信道 + var 信道实例 [10]chan 信道类型 - .. code:: go +声明后的信道,其零值是nil,无法直接使用,必须配合make函进行初始化。 - const APP_VER = "0.7.0.1110 Beta" +.. code:: go -- 如果是枚举类型的常量,需要先创建相应类型: + 信道实例 = make(chan 信道类型) - .. code:: go +亦或者,上面两行可以合并成一句,以下我都使用这样的方式进行信道的声明 - type Scheme string - const ( - HTTP Scheme = "http" - HTTPS Scheme = "https" - ) +.. code:: go -- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: + 信道实例 := make(chan 信道类型) - .. code:: go +假如我要创建一个可以传输int类型的信道,可以这样子写。 - type PullRequestStatus int - const ( - PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota - PULL_REQUEST_STATUS_CHECKING - PULL_REQUEST_STATUS_MERGEABLE - ) +.. code:: go -**3. 变量命名** + // 定义信道 + pipline := make(chan int) -使用驼峰命名法 +信道的数据操作,无非就两种:发送数据与读取数据 -- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u -- 若该变量为 bool 类型,则名称应以 ``Has``, ``Is``, ``Can`` 或 - ``Allow`` 开头。例如:isExist ,hasConflict 。 -- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 - startDate 。 -- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 - ``apiClient``\ 。 -- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient +.. code:: go -下面列举了一些常见的特有名词: + // 往信道中发送数据 + pipline<- 200 -:: + // 从信道中取出数据,并赋值给mydata + mydata := <-pipline + +信道用完了,可以对其进行关闭,避免有人一直在等待。但是你关闭信道后,接收方仍然可以从信道中取到数据,只是接收到的会永远是 +0。 + +.. code:: go + + close(pipline) + +对一个已关闭的信道再关闭,是会报错的。所以我们还要学会,如何判断一个信道是否被关闭? + +当从信道中读取数据时,可以有多个返回值,其中第二个可以表示 +信道是否被关闭,如果已经被关闭,ok 为 false,若还没被关闭,ok 为true。 + +.. code:: go + + x, ok := <-pipline + +2. 信道的容量与长度 +------------------- + +一般创建信道都是使用 make 函数,make 函数接收两个参数 + +- 第一个参数:必填,指定信道类型 +- 第二个参数:选填,不填默认为0,指定信道的\ **容量**\ (可缓存多少数据) - // A GonicMapper that contains a list of common initialisms taken from golang/lint - var LintGonicMapper = GonicMapper{ - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SSH": true, - "TLS": true, - "TTL": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XSRF": true, - "XSS": true, +对于信道的容量,很重要,这里要多说几点: + +- 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为\ **无缓冲信道**\ 。 +- 当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 + 利用这点可以利用信道来做锁。 +- 当容量大于1时,信道中可以存放多个数据,可以用于多个协程之间的通信管道,共享资源。 + +至此我们知道,信道就是一个容器。 + +若将它比做一个纸箱子 + +- 它可以装10本书,代表其容量为10 +- 当前只装了1本书,代表其当前长度为1 + +信道的容量,可以使用 cap 函数获取 ,而信道的长度,可以使用 len +长度获取。 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan int, 10) + fmt.Printf("信道可缓冲 %d 个数据\n", cap(pipline)) + pipline<- 1 + fmt.Printf("信道中当前有 %d 个数据", len(pipline)) } -**接口命名** +输出如下 + +:: + + 信道可缓冲 10 个数据 + 信道中当前有 1 个数据 + +3. 缓冲信道与无缓冲信道 +----------------------- -使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 +按照是否可缓冲数据可分为:\ **缓冲信道** 与 **无缓冲信道** + +**缓冲信道** + +允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。 .. code:: go - type helloWorld interface { - func Hello(); + pipline := make(chan int, 10) + +**无缓冲信道** + +在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,信道中无法存储数据。也就是说发送端和接收端是同步运行的。 + +.. code:: go + + pipline := make(chan int) + + // 或者 + pipline := make(chan int, 0) + +4. 双向信道与单向信道 +--------------------- + +通常情况下,我们定义的信道都是双向通道,可发送数据,也可以接收数据。 + +但有时候,我们希望对信道的数据流向做一些控制,比如这个信道只能接收数据或者这个信道只能发送数据。 + +因此,就有了 **双向信道** 和 **单向信道** 两种分类。 + +**双向信道** + +默认情况下你定义的信道都是双向的,比如下面代码 + +.. code:: go + + import ( + "fmt" + "time" + ) + + func main() { + pipline := make(chan int) + + go func() { + fmt.Println("准备发送数据: 100") + pipline <- 100 + }() + + go func() { + num := <-pipline + fmt.Printf("接收到的数据是: %d", num) + }() + // 主函数sleep,使得上面两个goroutine有机会执行 + time.Sleep(1) } - type SayHello helloWorld +**单向信道** -**注释规范** +单向信道,可以细分为 **只读信道** 和 **只写信道**\ 。 -单行注释使用 ``//`` ,多行注释使用 ``/* comment */`` +定义只读信道 .. code:: go - // go语言 + var pipline = make(chan int) + type Receiver = <-chan int // 关键代码:定义别名类型 + var receiver Receiver = pipline - /* - Go 语言 - Hello, World - */ +定义只写信道 -- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 +.. code:: go -- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 + var pipline = make(chan int) + type Sender = chan<- int // 关键代码:定义别名类型 + var sender Sender = pipline -- 包、函数、方法和类型的注释说明都是一个完整的句子。 +仔细观察,区别在于 ``<-`` 符号在关键字 ``chan`` 的左边还是右边。 -- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 +- ``<-chan`` 表示这个信道,只能从里发出数据,对于程序来说就是只读 +- ``chan<-`` 表示这个信道,只能从外面接收数据,对于程序来说就是只写 -- 注释的单行长度不能超过 80 个字符。 +有同学可能会问:为什么还要先声明一个双向信道,再定义单向通道呢?比如这样写 -- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 +.. code:: go -- 如果是特别复杂的包,可单独创建 doc.go 文件说明 + type Sender = chan<- int + sender := make(Sender) -- 类型的定义一般都以单数形式描述: +代码是没问题,但是你要明白信道的意义是什么?(**以下是我个人见解** - .. code:: go +信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入了吗,没什么用。所以只读信道和只写信道,唇亡齿寒,缺一不可。 - // Request represents a request to run a command. type Request struct { ... +当然了,若你往一个只读信道中写入数据 ,或者从一个只写信道中读取数据 +,都会出错。 -- 如果为接口,则一般以以下形式描述: +完整的示例代码如下,供你参考: - .. code:: go +.. code:: go - // FileInfo is the interface that describes a file and is returned by Stat and Lstat. - type FileInfo interface { ... + import ( + "fmt" + "time" + ) + //定义只写信道类型 + type Sender = chan<- int + + //定义只读信道类型 + type Receiver = <-chan int + + func main() { + var pipline = make(chan int) + + go func() { + var sender Sender = pipline + fmt.Println("准备发送数据: 100") + sender <- 100 + }() + + go func() { + var receiver Receiver = pipline + num := <-receiver + fmt.Printf("接收到的数据是: %d", num) + }() + // 主函数sleep,使得上面两个goroutine有机会执行 + time.Sleep(1) + } -- 函数与方法的注释需以函数或方法的名称作为开头: +5. 遍历信道 +----------- - .. code:: go +遍历信道,可以使用 for 搭配 +range关键字,在range时,要确保信道是处于关闭状态,否则循环会阻塞。 - // Post returns *BeegoHttpRequest with POST method. +.. code:: go -- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: + import "fmt" + + func fibonacci(mychan chan int) { + n := cap(mychan) + x, y := 1, 1 + for i := 0; i < n; i++ { + mychan <- x + x, y = y, x+y + } + // 记得 close 信道 + // 不然主函数中遍历完并不会结束,而是会阻塞。 + close(mychan) + } + + func main() { + pipline := make(chan int, 10) + + go fibonacci(pipline) + + for k := range pipline { + fmt.Println(k) + } + } + +6. 用信道来做锁 +--------------- + +当信道里的数据量已经达到设定的容量时,此时再往里发送数据会阻塞整个程序。 - .. code:: go +利用这个特性,可以用当他来当程序的锁。 - // Copy copies file from source to target path. - // It returns false and error when error occurs in underlying function calls. +示例如下,详情可以看注释 -- 若函数或方法为判断类型(返回值主要为 ``bool`` 类型),则以 - `` returns true if`` 开头: +.. code:: go + + package main + + import { + "fmt" + "time" + } + + // 由于 x=x+1 不是原子操作 + // 所以应避免多个协程对x进行操作 + // 使用容量为1的信道可以达到锁的效果 + func increment(ch chan bool, x *int) { + ch <- true + *x = *x + 1 + <- ch + } + + func main() { + // 注意要设置容量为 1 的缓冲信道 + pipline := make(chan bool, 1) + + var x int + for i:=0;i<1000;i++{ + go increment(pipline, &x) + } + + // 确保所有的协程都已完成 + // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep + time.Sleep(3) + fmt.Println("x 的值:", x) + } + +输出如下 + +:: - .. code:: go + x 的值:1000 - // HasPrefix returns true if name has any string in given slice as prefix. - func HasPrefix(name string, prefixes []string) bool { ... +如果不加锁,输出会小于1000。 -特别注释 +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! -- TODE:提醒维护人员此部分代码待完成 -- FIXME:提醒维护人员此处有BUG待修复 -- NOTE:维护人员要关注的一些问题说明 + 关注公众号,获取最新干货! diff --git a/source/c09/c09_20.md b/source/c09/c09_20.md new file mode 100644 index 0000000..3839fec --- /dev/null +++ b/source/c09/c09_20.md @@ -0,0 +1,175 @@ +# 9.20 几个信道死锁经典错误案例详解 + +刚接触 Go 语言的信道的时候,经常会遇到死锁的错误,而导致这个错误的原因有很多种,这里整理了几种常见的。 + +``` +fatal error: all goroutines are asleep - deadlock! +``` + + + +## 错误示例一 + +看下面这段代码 + +```go +package main + +import "fmt" + +func main() { + pipline := make(chan string) + pipline <- "hello world" + fmt.Println(<-pipline) +} +``` + +运行会抛出错误,如下 + +``` +fatal error: all goroutines are asleep - deadlock! +``` + +看起来好像没有什么问题?先往信道中存入数据,再从信道中读取数据。 + +回顾前面的基础,我们知道使用 make 创建信道的时候,若不传递第二个参数,则你定义的是无缓冲信道,而对于无缓冲信道,在接收者未准备好之前,发送操作是阻塞的. + +因此,对于解决此问题有两种方法: + +1. 使接收者代码在发送者之前执行 +2. 使用缓冲信道,而不使用无缓冲信道 + +**第一种方法**: + +若要程序正常执行,需要保证接收者程序在发送数据到信道前就进行阻塞状态,修改代码如下 + +```go +package main + +import "fmt" + +func main() { + pipline := make(chan string) + fmt.Println(<-pipline) + pipline <- "hello world" +} +``` + +运行的时候还是报同样的错误。问题出在哪里呢? + +原来我们将发送者和接收者写在了同一协程中,虽然保证了接收者代码在发送者之前执行,但是由于前面接收者一直在等待数据 而处于阻塞状态,所以无法执行到后面的发送数据。还是一样造成了死锁。 + +有了前面的经验,我们将接收者代码写在另一个协程里,并保证在发送者之前执行,就像这样的代码 + +```go +package main + +func hello(pipline chan string) { + <-pipline +} + +func main() { + pipline := make(chan string) + go hello(pipline) + pipline <- "hello world" +} +``` + +运行之后 ,一切正常。 + +**第二种方法**: + +接收者代码必须在发送者代码之前 执行,这是针对无缓冲信道才有的约束。 + +既然这样,我们改使用可缓冲信道不就OK了吗? + +```go +package main + +import "fmt" + +func main() { + pipline := make(chan string, 1) + pipline <- "hello world" + fmt.Println(<-pipline) +} +``` + +运行之后,一切正常。 + + + +## 错误示例二 + +每个缓冲信道,都有容量,当信道里的数据量等于信道的容量后,此时再往信道里发送数据,就失造成阻塞,必须等到有人从信道中消费数据后,程序才会往下进行。 + + 比如这段代码,信道容量为 1,但是往信道中写入两条数据,对于一个协程来说就会造成死锁。 + +```go +package main + +import "fmt" + +func main() { + ch1 := make(chan string, 1) + + ch1 <- "hello world" + ch1 <- "hello China" + + fmt.Println(<-ch1) +} +``` + + + +## 错误示例三 + +当程序一直在等待从信道里读取数据,而此时并没有人会往信道中写入数据。此时程序就会陷入死循环,造成死锁。 + +比如这段代码,for 循环接收了两次消息("hello world"和“hello China”)后,再也没有人发送数据了,接收者就会处于一个等待永远接收不到数据的囧境。陷入死循环,造成死锁。 + +```go +package main + +import "fmt" + +func main() { + pipline := make(chan string) + go func() { + pipline <- "hello world" + pipline <- "hello China" + // close(pipline) + }() + for data := range pipline{ + fmt.Println(data) + } +} +``` + +包子铺里的包子已经卖完了,可还有人在排队等着买,如果不再做包子,就要告诉排队的人:不用等了,今天的包子已经卖完了,明日请早呀。 + +不能让人家死等呀,不跟客人说明一下,人家还以为你们店后面还在蒸包子呢。 + +所以这个问题,解决方法很简单,只要在发送完数据后,手动关闭信道,告诉 range 信道已经关闭,无需等待就行。 + +```go +package main + +import "fmt" + +func main() { + pipline := make(chan string) + go func() { + pipline <- "hello world" + pipline <- "hello China" + close(pipline) + }() + for data := range pipline{ + fmt.Println(data) + } +} +``` + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_20.rst b/source/c09/c09_20.rst new file mode 100644 index 0000000..16c33b1 --- /dev/null +++ b/source/c09/c09_20.rst @@ -0,0 +1,180 @@ +9.20 几个信道死锁经典错误案例详解 +================================= + +刚接触 Go +语言的信道的时候,经常会遇到死锁的错误,而导致这个错误的原因有很多种,这里整理了几种常见的。 + +:: + + fatal error: all goroutines are asleep - deadlock! + +错误示例一 +---------- + +看下面这段代码 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + pipline <- "hello world" + fmt.Println(<-pipline) + } + +运行会抛出错误,如下 + +:: + + fatal error: all goroutines are asleep - deadlock! + +看起来好像没有什么问题?先往信道中存入数据,再从信道中读取数据。 + +回顾前面的基础,我们知道使用 make +创建信道的时候,若不传递第二个参数,则你定义的是无缓冲信道,而对于无缓冲信道,在接收者未准备好之前,发送操作是阻塞的. + +因此,对于解决此问题有两种方法: + +1. 使接收者代码在发送者之前执行 +2. 使用缓冲信道,而不使用无缓冲信道 + +**第一种方法**\ : + +若要程序正常执行,需要保证接收者程序在发送数据到信道前就进行阻塞状态,修改代码如下 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + fmt.Println(<-pipline) + pipline <- "hello world" + } + +运行的时候还是报同样的错误。问题出在哪里呢? + +原来我们将发送者和接收者写在了同一协程中,虽然保证了接收者代码在发送者之前执行,但是由于前面接收者一直在等待数据 +而处于阻塞状态,所以无法执行到后面的发送数据。还是一样造成了死锁。 + +有了前面的经验,我们将接收者代码写在另一个协程里,并保证在发送者之前执行,就像这样的代码 + +.. code:: go + + package main + + func hello(pipline chan string) { + <-pipline + } + + func main() { + pipline := make(chan string) + go hello(pipline) + pipline <- "hello world" + } + +运行之后 ,一切正常。 + +**第二种方法**\ : + +接收者代码必须在发送者代码之前 执行,这是针对无缓冲信道才有的约束。 + +既然这样,我们改使用可缓冲信道不就OK了吗? + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string, 1) + pipline <- "hello world" + fmt.Println(<-pipline) + } + +运行之后,一切正常。 + +错误示例二 +---------- + +每个缓冲信道,都有容量,当信道里的数据量等于信道的容量后,此时再往信道里发送数据,就失造成阻塞,必须等到有人从信道中消费数据后,程序才会往下进行。 + +比如这段代码,信道容量为 +1,但是往信道中写入两条数据,对于一个协程来说就会造成死锁。 + +.. code:: go + + package main + + import "fmt" + + func main() { + ch1 := make(chan string, 1) + + ch1 <- "hello world" + ch1 <- "hello China" + + fmt.Println(<-ch1) + } + +错误示例三 +---------- + +当程序一直在等待从信道里读取数据,而此时并没有人会往信道中写入数据。此时程序就会陷入死循环,造成死锁。 + +比如这段代码,for 循环接收了两次消息(“hello world”和“hello +China”)后,再也没有人发送数据了,接收者就会处于一个等待永远接收不到数据的囧境。陷入死循环,造成死锁。 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + go func() { + pipline <- "hello world" + pipline <- "hello China" + // close(pipline) + }() + for data := range pipline{ + fmt.Println(data) + } + } + +包子铺里的包子已经卖完了,可还有人在排队等着买,如果不再做包子,就要告诉排队的人:不用等了,今天的包子已经卖完了,明日请早呀。 + +不能让人家死等呀,不跟客人说明一下,人家还以为你们店后面还在蒸包子呢。 + +所以这个问题,解决方法很简单,只要在发送完数据后,手动关闭信道,告诉 +range 信道已经关闭,无需等待就行。 + +.. code:: go + + package main + + import "fmt" + + func main() { + pipline := make(chan string) + go func() { + pipline <- "hello world" + pipline <- "hello China" + close(pipline) + }() + for data := range pipline{ + fmt.Println(data) + } + } + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_21.md b/source/c09/c09_21.md new file mode 100644 index 0000000..2afd761 --- /dev/null +++ b/source/c09/c09_21.md @@ -0,0 +1,109 @@ +# 9.21 学习 Go 协程:WaitGroup + +在前两篇文章里,我们学习了 `协程` 和 `信道` 的内容,里面有很多例子,当时为了保证 main goroutine 在所有的 goroutine 都执行完毕后再退出,我使用了 time.Sleep 这种简单的方式。 + +由于写的 demo 都是比较简单的, sleep 个 1 秒,我们主观上认为是够用的。 + +但在实际开发中,开发人员是无法预知,所有的 goroutine 需要多长的时间才能执行完毕,sleep 多了吧主程序就阻塞了, sleep 少了吧有的子协程的任务就没法完成。 + +因此,使用time.Sleep 是一种极不推荐的方式,今天主要就要来介绍 一下如何优雅的处理这种情况。 + + + +## 1. 使用信道来标记完成 + +> “不要通过共享内存来通信,要通过通信来共享内存” + +学习了信道后,我们知道,信道可以实现多个协程间的通信,那么我们只要定义一个信道,在任务完成后,往信道中写入true,然后在主协程中获取到true,就认为子协程已经执行完毕。 + +```go +import "fmt" + +func main() { + done := make(chan bool) + go func() { + for i := 0; i < 5; i++ { + fmt.Println(i) + } + done <- true + }() + <-done +} +``` + +输出如下 + +``` +0 +1 +2 +3 +4 +``` + + + +## 2. 使用 WaitGroup + +上面使用信道的方法,在单个协程或者协程数少的时候,并不会有什么问题,但在协程数多的时候,代码就会显得非常复杂,有兴趣可以自己尝试一下。 + +那么有没有一种更加优雅的方式呢? + +有,这就要说到 sync包 提供的 WaitGroup 类型。 + +WaitGroup 你只要实例化了就能使用 + +```go +var 实例名 sync.WaitGroup +``` + +实例化完成后,就可以使用它的几个方法: + +- Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量 +- Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 defer 来调用。 +- Wait:阻塞当前协程,直到实例里的计数器归零。 + +举一个例子: + +```go +import ( + "fmt" + "sync" +) + +func worker(x int, wg *sync.WaitGroup) { + defer wg.Done() + for i := 0; i < 5; i++ { + fmt.Printf("worker %d: %d\n", x, i) + } +} + +func main() { + var wg sync.WaitGroup + + wg.Add(2) + go worker(1, &wg) + go worker(2, &wg) + + wg.Wait() +} +``` + +输出如下 + +``` +worker 2: 0 +worker 2: 1 +worker 2: 2 +worker 2: 3 +worker 2: 4 +worker 1: 0 +worker 1: 1 +worker 1: 2 +worker 1: 3 +worker 1: 4 +``` + +以上就是我们在 Go 语言中实现一主多子的协程协作方式,推荐使用 sync.WaitGroup。。 + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_21.rst b/source/c09/c09_21.rst new file mode 100644 index 0000000..97c30a3 --- /dev/null +++ b/source/c09/c09_21.rst @@ -0,0 +1,118 @@ +9.21 学习 Go 协程:WaitGroup +============================ + +在前两篇文章里,我们学习了 ``协程`` 和 ``信道`` +的内容,里面有很多例子,当时为了保证 main goroutine 在所有的 goroutine +都执行完毕后再退出,我使用了 time.Sleep 这种简单的方式。 + +由于写的 demo 都是比较简单的, sleep 个 1 秒,我们主观上认为是够用的。 + +但在实际开发中,开发人员是无法预知,所有的 goroutine +需要多长的时间才能执行完毕,sleep 多了吧主程序就阻塞了, sleep +少了吧有的子协程的任务就没法完成。 + +因此,使用time.Sleep 是一种极不推荐的方式,今天主要就要来介绍 +一下如何优雅的处理这种情况。 + +1. 使用信道来标记完成 +--------------------- + + “不要通过共享内存来通信,要通过通信来共享内存” + +学习了信道后,我们知道,信道可以实现多个协程间的通信,那么我们只要定义一个信道,在任务完成后,往信道中写入true,然后在主协程中获取到true,就认为子协程已经执行完毕。 + +.. code:: go + + import "fmt" + + func main() { + done := make(chan bool) + go func() { + for i := 0; i < 5; i++ { + fmt.Println(i) + } + done <- true + }() + <-done + } + +输出如下 + +:: + + 0 + 1 + 2 + 3 + 4 + +2. 使用 WaitGroup +----------------- + +上面使用信道的方法,在单个协程或者协程数少的时候,并不会有什么问题,但在协程数多的时候,代码就会显得非常复杂,有兴趣可以自己尝试一下。 + +那么有没有一种更加优雅的方式呢? + +有,这就要说到 sync包 提供的 WaitGroup 类型。 + +WaitGroup 你只要实例化了就能使用 + +.. code:: go + + var 实例名 sync.WaitGroup + +实例化完成后,就可以使用它的几个方法: + +- Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量 +- Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 + defer 来调用。 +- Wait:阻塞当前协程,直到实例里的计数器归零。 + +举一个例子: + +.. code:: go + + import ( + "fmt" + "sync" + ) + + func worker(x int, wg *sync.WaitGroup) { + defer wg.Done() + for i := 0; i < 5; i++ { + fmt.Printf("worker %d: %d\n", x, i) + } + } + + func main() { + var wg sync.WaitGroup + + wg.Add(2) + go worker(1, &wg) + go worker(2, &wg) + + wg.Wait() + } + +输出如下 + +:: + + worker 2: 0 + worker 2: 1 + worker 2: 2 + worker 2: 3 + worker 2: 4 + worker 1: 0 + worker 1: 1 + worker 1: 2 + worker 1: 3 + worker 1: 4 + +以上就是我们在 Go 语言中实现一主多子的协程协作方式,推荐使用 +sync.WaitGroup。。 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_23.md b/source/c09/c09_23.md new file mode 100644 index 0000000..b9daa8c --- /dev/null +++ b/source/c09/c09_23.md @@ -0,0 +1,158 @@ +# 9.23 学习一些常见的并发模型 + +本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 Golang 中的 协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 + +你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 + +## 0. 并发与并行 + +讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? + +这里我用两个例子来形象描述: + +- **并发**:当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 +- **并行**:你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 + +在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 + +而当你的机器有多个 CPU 核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 + +接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 + +但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? + +谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C 的活,然后再去做一会 A 的活,就这样不断的切换着,大家都很开心,其乐融融。 + +## 1. 并发编程的模型 + +在计算机的世界里,实现并发通常有几种方式: + +1. 多进程模型:创建新的线程处理请求 +2. 多线程模型:创建新的进程处理请求 +3. 使用线程池:线程/进程创建销毁开销大 +4. I/O 多路复用+单/多线程 + +## 2. 多进程与多线程 + +对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ ,一个微信,它们都是一个进程。 + +进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 + +在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 + +线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 + +而进程的切换,相比线程来说,会更加麻烦。 + +因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 + +此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams 等 + +说了这么多,好像都在说线程优于进程,也不尽然。 + +比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 + +同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 + + + +## 3. I/O多路复用 + +`I/O多路复用` ,英文全称为 `I/O multiplexing`,这个中文翻译和把 socket 翻译成 套接字一样,影响了我对其概念的理解。 + +在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 + +但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 + +终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O 多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 + +再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 select ,早已不能支撑用户请求了。 + +由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select 发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 + +select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 + +都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 + +由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 + +epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: + +- select 和 poll 无差别轮循fd,浪费资源,epool 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 +- select 和 poll 线程不安全,epool 线程安全 +- select 请求连接数的限制,epool 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) +- select 和 pool 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 + +虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 epool 出现了, select 就会被淘汰掉。 + +epool 关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool 的性能最好。 + +而 select 关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 + +另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool 是 Linux 所有的,其他平台上没有。 + + IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 + +- 一个线程的IO多路复用,比如 Redis +- 多个线程的IO多路复用,比如 goroutine + +IO多路复用 + 单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 + +## 4. 三种线程模型? + +实际上,goroutine 并非传统意义上的协程。 + +现在主流的线程模型分三种: + +- 内核级线程模型 +- 用户级线程模型 +- 两级线程模型(也称混合型线程模型) + +传统的协程库属于**用户级线程模型**,而 goroutine 和它的 `Go Scheduler` 在底层实现上其实是属于**两级线程模型**,因此,有时候为了方便理解可以简单把 goroutine 类比成协程,但心里一定要有个清晰的认知 — goroutine并不等同于协程。 + +关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 + + + +## 5. 协程的优势在哪? + +协程,可以认为是轻量级的“线程”。 + +对比线程,有如下几个明显的优势。 + +1. 协程的调度由 Go 的 runtime 管理,协程切换不需要经由操作系统内核,开销较小。 +2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 + +同时,在 Golang 里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 async def 而言),运行时只需要一个关键字 `go`,就可以轻松创建一个协程。 + + + +使用 -race 来检测数据 访问的冲突 + + + +协程什么时候会切换 + +1. I/O,select +2. channel +3. 等待锁 +4. 函数调用(有时 +5. runtime.Gosched() + + + + + +## 参考阅读: + +https://www.cnblogs.com/aspirant/p/9166944.html + +https://blog.csdn.net/snoweaglelord/article/details/99681179 + +https://www.jianshu.com/p/dfd940e7fca2 + +https://studygolang.com/articles/13344 + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_23.rst b/source/c09/c09_23.rst index 170c0c6..dd57072 100644 --- a/source/c09/c09_23.rst +++ b/source/c09/c09_23.rst @@ -1,324 +1,188 @@ -9.12 详解数据类型:信道 -======================= +9.23 学习一些常见的并发模型 +=========================== -Go 语言之所以开始流行起来,很大一部分原因是因为它自带的并发机制。 +本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 +Golang 中的 +协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 -如果说 goroutine 是 Go语言程序的并发体的话,那么 channel(信道) -就是它们之间的通信机制。channel,是一个可以让一个 goroutine 与另一个 -goroutine -传输信息的通道,我把他叫做信道,也有人将其翻译成通道,二者都是一个概念。 +你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 -信道,就是一个管道,连接多个goroutine程序 -,它是一种队列式的数据结构,遵循先入先出的规则。 +0. 并发与并行 +------------- -1. 信道的定义与使用 -------------------- - -每个信道都只能传递一种数据类型的数据,所以在你声明的时候,你得指定数据类型(string -int 等等) - -.. code:: go - - var 信道实例 chan 信道类型 - -声明后的信道,其零值是nil,无法直接使用,必须配合make函进行初始化。 - -.. code:: go - - 信道实例 = make(chan 信道类型) - -亦或者,上面两行可以合并成一句,以下我都使用这样的方式进行信道的声明 - -.. code:: go - - 信道实例 := make(chan 信道类型) - -假如我要创建一个可以传输int类型的信道,可以这样子写。 +讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? -.. code:: go +这里我用两个例子来形象描述: - // 定义信道 - pipline := make(chan int) +- **并发**\ :当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 +- **并行**\ :你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 -信道的数据操作,无非就两种:发送数据与读取数据 +在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 -.. code:: go - - // 往信道中发送数据 - pipline<- 200 - - // 从信道中取出数据,并赋值给mydata - mydata := <-pipline - -信道用完了,可以对其进行关闭,避免有人一直在等待。 - -.. code:: go - - close(pipline) - -对一个已关闭的信道再关闭,是会报错的。所以我们还要学会,如何判断一个信道是否被关闭? - -当从信道中读取数据时,可以有多个返回值,其中第二个可以表示 -信道是否被关闭,如果已经被关闭,ok 为 false,若还没被关闭,ok 为true。 - -.. code:: go - - x, ok := <-pipline - -2. 信道的容量与长度 -------------------- +而当你的机器有多个 CPU +核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 -一般创建信道都是使用 make 函数,make 函数接收两个参数 +接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 -- 第一个参数:必填,指定信道类型 -- 第二个参数:选填,不填默认为0,指定信道的\ **容量**\ (可缓存多少数据) +但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? -对于信道的容量,很重要,这里要多说几点: +谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C +的活,然后再去做一会 A +的活,就这样不断的切换着,大家都很开心,其乐融融。 -- 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为\ **无缓冲信道**\ 。 -- 当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 - 利用这点可以利用信道来做锁。 -- 当容量大于1时,信道中可以存放多个数据,可以用于多个协程之间的通信管道,共享资源。 +1. 并发编程的模型 +----------------- -至此我们知道,信道就是一个容器。 +在计算机的世界里,实现并发通常有几种方式: -若将它比做一个纸箱子 +1. 多进程模型:创建新的线程处理请求 +2. 多线程模型:创建新的进程处理请求 +3. 使用线程池:线程/进程创建销毁开销大 +4. I/O 多路复用+单/多线程 -- 它可以装10本书,代表其容量为10 -- 当前只装了1本书,代表其当前长度为1 +2. 多进程与多线程 +----------------- -信道的容量,可以使用 cap 函数获取 ,而信道的长度,可以使用 len -长度获取。 +对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ +,一个微信,它们都是一个进程。 -.. code:: go +进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 - package main +在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 - import "fmt" +线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 +内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 - func main() { - pipline := make(chan int, 10) - fmt.Printf("信道可缓冲 %d 个数据\n", cap(pipline)) - pipline<- 1 - fmt.Printf("信道中当前有 %d 个数据", len(pipline)) - } +而进程的切换,相比线程来说,会更加麻烦。 -输出如下 +因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 +共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 -:: +此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess +Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams +等 - 信道可缓冲 10 个数据 - 信道中当前有 1 个数据 +说了这么多,好像都在说线程优于进程,也不尽然。 -3. 缓冲信道与无缓冲信道 ------------------------ +比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 -按照是否可缓冲数据可分为:\ **缓冲信道** 与 **无缓冲信道** +同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 -**缓冲信道** +3. I/O多路复用 +-------------- -允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。 +``I/O多路复用`` ,英文全称为 ``I/O multiplexing``\ ,这个中文翻译和把 +socket 翻译成 套接字一样,影响了我对其概念的理解。 -.. code:: go +在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 - pipline := make(chan int, 10) +但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 -**无缓冲信道** +终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O +多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 -在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,信道中无法存储数据。也就是说发送端和接收端是同步运行的。 +再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 +select ,早已不能支撑用户请求了。 -.. code:: go +由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select +发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 - pipline := make(chan int) +select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 - // 或者 - pipline := make(chan int, 0) +都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file +descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 -4. 双向信道与单向信道 ---------------------- +由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 -通常情况下,我们定义的信道都是双向通道,可发送数据,也可以接收数据。 +epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: -但有时候,我们希望对信道的数据流向做一些控制,比如这个信道只能接收数据或者这个信道只能发送数据。 +- select 和 poll 无差别轮循fd,浪费资源,epool + 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 +- select 和 poll 线程不安全,epool 线程安全 +- select 请求连接数的限制,epool + 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) +- select 和 pool + 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 -因此,就有了 **双向信道** 和 **单向信道** 两种分类。 +虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 +epool 出现了, select 就会被淘汰掉。 -**双向信道** +epool +关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool +的性能最好。 -默认情况下你定义的信道都是双向的,比如下面代码 +而 select +关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 +select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 -.. code:: go +另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool +是 Linux 所有的,其他平台上没有。 - import ( - "fmt" - "time" - ) +IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 - func main() { - pipline := make(chan int) +- 一个线程的IO多路复用,比如 Redis +- 多个线程的IO多路复用,比如 goroutine - go func() { - fmt.Println("准备发送数据: 100") - pipline <- 100 - }() +IO多路复用 + +单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 - go func() { - num := <-pipline - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) - } +4. 三种线程模型? +----------------- -**单向信道** +实际上,goroutine 并非传统意义上的协程。 -单向信道,可以细分为 **只读信道** 和 **只写信道**\ 。 +现在主流的线程模型分三种: -定义只读信道 +- 内核级线程模型 +- 用户级线程模型 +- 两级线程模型(也称混合型线程模型) -.. code:: go +传统的协程库属于\ **用户级线程模型**\ ,而 goroutine 和它的 +``Go Scheduler`` +在底层实现上其实是属于\ **两级线程模型**\ ,因此,有时候为了方便理解可以简单把 +goroutine 类比成协程,但心里一定要有个清晰的认知 — +goroutine并不等同于协程。 - var pipline = make(chan int) - type Receiver = <-chan int // 关键代码:定义别名类型 - var receiver Receiver = pipline +关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 -定义只写信道 - -.. code:: go - - var pipline = make(chan int) - type Sender = chan<- int // 关键代码:定义别名类型 - var sender Sender = pipline - -仔细观察,区别在于 ``<-`` 符号在关键字 ``chan`` 的左边还是右边。 - -- ``<-chan`` 表示这个信道,只能从里发出数据,对于程序来说就是只读 -- ``chan<-`` 表示这个信道,只能从外面接收数据,对于程序来说就是只写 - -有同学可能会问:为什么还要先声明一个双向信道,再定义单向通道呢?比如这样写 - -.. code:: go - - type Sender = chan<- int - sender := make(Sender) - -代码是没问题,但是你要明白信道的意义是什么? - -信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入的累赘,没什么用。所以只读信道和只写信道,唇亡齿寒,缺一不可。 - -当然了,若你往一个只读信道中写入数据 ,或者从一个只写信道中读取数据 -,都会出错。 - -完整的示例代码如下,供你参考: - -.. code:: go - - import ( - "fmt" - "time" - ) - //定义只写信道类型 - type Sender = chan<- int - - //定义只读信道类型 - type Receiver = <-chan int - - func main() { - var pipline = make(chan int) - - go func() { - var sender Sender = pipline - fmt.Println("准备发送数据: 100") - sender <- 100 - }() - - go func() { - var receiver Receiver = pipline - num := <-receiver - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) - } - -5. 遍历信道 ------------ - -遍历信道,可以使用 for 搭配 -range关键字,在range时,要确保信道是处于关闭状态,否则循环会阻塞。 - -.. code:: go +5. 协程的优势在哪? +------------------- - import "fmt" +协程,可以认为是轻量级的“线程”。 - func fibonacci(mychan chan int) { - n := cap(mychan) - x, y := 0, 1 - for i := 0; i < n; i++ { - mychan <- x - x, y = y, x+y - } - // 记得 close 信道 - // 不然主函数中遍历完并不会结束,而是会阻塞。 - close(mychan) - } +对比线程,有如下几个明显的优势。 - func main() { - pipline := make(chan int, 10) - - go fibonacci(pipline) - - for k := range pipline { - fmt.Println(k) - } - } +1. 协程的调度由 Go 的 runtime + 管理,协程切换不需要经由操作系统内核,开销较小。 +2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 -6. 用信道来做锁 ---------------- +同时,在 Golang +里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 +async def 而言),运行时只需要一个关键字 +``go``\ ,就可以轻松创建一个协程。 -当信道里的数据量已经达到设定的容量时,此时再往里发送数据会阻塞整个程序。 +使用 -race 来检测数据 访问的冲突 -利用这个特性,可以用当他来当程序的锁。 +协程什么时候会切换 -示例如下,详情可以看注释 +1. I/O,select +2. channel +3. 等待锁 +4. 函数调用(有时 +5. runtime.Gosched() -.. code:: go +参考阅读: +---------- - package main +https://www.cnblogs.com/aspirant/p/9166944.html - import { - "fmt" - "time" - } +https://blog.csdn.net/snoweaglelord/article/details/99681179 - // 由于 x=x+1 不是原子操作 - // 所以应避免多个协程对x进行操作 - // 使用容量为1的信道可以达到锁的效果 - func increment(ch chan bool, x *int) { - ch <- true - *x = *x + 1 - <- ch - } +https://www.jianshu.com/p/dfd940e7fca2 - func main() { - // 注意要设置容量为 1 的缓冲信道 - pipline := make(chan bool, 1) +https://studygolang.com/articles/13344 - var x int - for i:=0;i<1000;i++{ - go increment(pipline, &x) - } +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! - // 确保所有的协程都已完成 - // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep - time.Sleep(3) - fmt.Println("x 的值:", x) - } - -输出如下 - -:: - - x 的值:1000 - -如果不加锁,输出会小于1000。 + 关注公众号,获取最新干货! diff --git a/source/c09/c09_24.md b/source/c09/c09_24.md new file mode 100644 index 0000000..0b0184c --- /dev/null +++ b/source/c09/c09_24.md @@ -0,0 +1,177 @@ +# 9.9 Go语言命名编码规范 + +每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 + +以下内容整理自:[Go语言(Golang)编码规范](https://www.bookstack.cn/books/go-code-convention) + +命名规范分为以下几点 + +**1. 文件命名** + +文件名应一律使用小写(因为Windows文件名不区分大小写?), 不同单词之间用下划线分割。 + +应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 MyBlog的入口应当为 myblog.go + +**2. 常量命名 ** + +- 常量均需使用全部大写字母组成,并使用下划线分词: + + ```go + const APP_VER = "0.7.0.1110 Beta" + ``` + +- 如果是枚举类型的常量,需要先创建相应类型: + + ```go + type Scheme string + const ( + HTTP Scheme = "http" + HTTPS Scheme = "https" + ) + ``` + +- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: + + ```go + type PullRequestStatus int + const ( + PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota + PULL_REQUEST_STATUS_CHECKING + PULL_REQUEST_STATUS_MERGEABLE + ) + ``` + +**3. 变量命名** + +使用驼峰命名法 + +- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u +- 若该变量为 bool 类型,则名称应以 `Has`, `Is`, `Can` 或 `Allow` 开头。例如:isExist ,hasConflict 。 +- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 startDate 。 +- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 `apiClient`。 +- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient + +下面列举了一些常见的特有名词: + +``` +// A GonicMapper that contains a list of common initialisms taken from golang/lint +var LintGonicMapper = GonicMapper{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, +} +``` + + + +**接口命名** + +使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 + +```go +type helloWorld interface { + func Hello(); +} + +type SayHello helloWorld +``` + + + +**注释规范** + +单行注释使用 `//` ,多行注释使用 `/* comment */` + +```go +// go语言 + +/* +Go 语言 +Hello, World +*/ +``` + +- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 + +- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 + +- 包、函数、方法和类型的注释说明都是一个完整的句子。 + +- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 + +- 注释的单行长度不能超过 80 个字符。 + +- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 + +- 如果是特别复杂的包,可单独创建 doc.go 文件说明 + +- 类型的定义一般都以单数形式描述: + + ```go + // Request represents a request to run a command. type Request struct { ... + ``` + +- 如果为接口,则一般以以下形式描述: + + ```go + // FileInfo is the interface that describes a file and is returned by Stat and Lstat. + type FileInfo interface { ... + ``` + +- 函数与方法的注释需以函数或方法的名称作为开头: + + ```go + // Post returns *BeegoHttpRequest with POST method. + ``` + +- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: + + ```go + // Copy copies file from source to target path. + // It returns false and error when error occurs in underlying function calls. + ``` + +- 若函数或方法为判断类型(返回值主要为 `bool` 类型),则以 ` returns true if` 开头: + + ```go + // HasPrefix returns true if name has any string in given slice as prefix. + func HasPrefix(name string, prefixes []string) bool { ... + ``` + +特别注释 + +- TODE:提醒维护人员此部分代码待完成 +- FIXME:提醒维护人员此处有BUG待修复 +- NOTE:维护人员要关注的一些问题说明 + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_24.rst b/source/c09/c09_24.rst index 9cc4cb3..63ba7c1 100644 --- a/source/c09/c09_24.rst +++ b/source/c09/c09_24.rst @@ -1,175 +1,183 @@ -9.14 几个信道死锁经典错误案例详解 -================================= +9.9 Go语言命名编码规范 +====================== -刚接触 Go -语言的信道的时候,经常会遇到死锁的错误,而导致这个错误的原因有很多种,这里整理了几种常见的。 +每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 -:: +以下内容整理自:\ `Go语言(Golang)编码规范 `__ - fatal error: all goroutines are asleep - deadlock! +命名规范分为以下几点 -错误示例一 ----------- +**1. 文件命名** -看下面这段代码 +文件名应一律使用小写(因为Windows文件名不区分大小写?), +不同单词之间用下划线分割。 -.. code:: go +应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 +MyBlog的入口应当为 myblog.go - package main +**2. 常量命名** - import "fmt" +- 常量均需使用全部大写字母组成,并使用下划线分词: - func main() { - pipline := make(chan string) - pipline <- "hello world" - fmt.Println(<-pipline) - } + .. code:: go -运行会抛出错误,如下 + const APP_VER = "0.7.0.1110 Beta" -:: +- 如果是枚举类型的常量,需要先创建相应类型: - fatal error: all goroutines are asleep - deadlock! + .. code:: go -看起来好像没有什么问题?先往信道中存入数据,再从信道中读取数据。 + type Scheme string + const ( + HTTP Scheme = "http" + HTTPS Scheme = "https" + ) -回顾前面的基础,我们知道使用 make -创建信道的时候,若不传递第二个参数,则你定义的是无缓冲信道,而对于无缓冲信道,在接收者未准备好之前,发送操作是阻塞的. +- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: -因此,对于解决此问题有两种方法: + .. code:: go -1. 使接收者代码在发送者之前执行 -2. 使用缓冲信道,而不使用无缓冲信道 + type PullRequestStatus int + const ( + PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota + PULL_REQUEST_STATUS_CHECKING + PULL_REQUEST_STATUS_MERGEABLE + ) -**第一种方法**\ : +**3. 变量命名** -若要程序正常执行,需要保证接收者程序在发送数据到信道前就进行阻塞状态,修改代码如下 - -.. code:: go +使用驼峰命名法 - package main +- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u +- 若该变量为 bool 类型,则名称应以 ``Has``, ``Is``, ``Can`` 或 + ``Allow`` 开头。例如:isExist ,hasConflict 。 +- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 + startDate 。 +- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 + ``apiClient``\ 。 +- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient - import "fmt" +下面列举了一些常见的特有名词: - func main() { - pipline := make(chan string) - fmt.Println(<-pipline) - pipline <- "hello world" - } +:: -运行的时候还是报同样的错误。问题出在哪里呢? + // A GonicMapper that contains a list of common initialisms taken from golang/lint + var LintGonicMapper = GonicMapper{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, + } -原来我们将发送者和接收者写在了同一协程中,虽然保证了接收者代码在发送者之前执行,但是由于前面接收者一直在等待数据 -而处于阻塞状态,所以无法执行到后面的发送数据。还是一样造成了死锁。 +**接口命名** -有了前面的经验,我们将接收者代码写在另一个协程里,并保证在发送者之前执行,就像这样的代码 +使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 .. code:: go - package main - - func hello(pipline chan string) { - <-pipline - } - - func main() { - pipline := make(chan string) - go hello(pipline) - pipline <- "hello world" + type helloWorld interface { + func Hello(); } -运行之后 ,一切正常。 - -**第二种方法**\ : + type SayHello helloWorld -接收者代码必须在发送者代码之前 执行,这是针对无缓冲信道才有的约束。 +**注释规范** -既然这样,我们改使用可缓冲信道不就OK了吗? +单行注释使用 ``//`` ,多行注释使用 ``/* comment */`` .. code:: go - package main + // go语言 - import "fmt" + /* + Go 语言 + Hello, World + */ - func main() { - pipline := make(chan string, 1) - pipline <- "hello world" - fmt.Println(<-pipline) - } +- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 -运行之后,一切正常。 +- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 -错误示例二 ----------- +- 包、函数、方法和类型的注释说明都是一个完整的句子。 -每个缓冲信道,都有容量,当信道里的数据量等于信道的容量后,此时再往信道里发送数据,就失造成阻塞,必须等到有人从信道中消费数据后,程序才会往下进行。 +- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 -比如这段代码,信道容量为 -1,但是往信道中写入两条数据,对于一个协程来说就会造成死锁。 +- 注释的单行长度不能超过 80 个字符。 -.. code:: go +- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 - package main +- 如果是特别复杂的包,可单独创建 doc.go 文件说明 - import "fmt" +- 类型的定义一般都以单数形式描述: - func main() { - ch1 := make(chan string, 1) + .. code:: go - ch1 <- "hello world" - ch1 <- "hello China" + // Request represents a request to run a command. type Request struct { ... - fmt.Println(<-ch1) - } +- 如果为接口,则一般以以下形式描述: -错误示例三 ----------- + .. code:: go -当程序一直在等待从信道里读取数据,而此时并没有人会往信道中写入数据。此时程序就会陷入死循环,造成死锁。 + // FileInfo is the interface that describes a file and is returned by Stat and Lstat. + type FileInfo interface { ... -比如这段代码,for 循环接收了两次消息(“hello world”和“hello -China”)后,再也没有人发送数据了,接收者就会处于一个等待永远接收不到数据的囧境。陷入死循环,造成死锁。 +- 函数与方法的注释需以函数或方法的名称作为开头: -.. code:: go + .. code:: go - package main + // Post returns *BeegoHttpRequest with POST method. - import "fmt" +- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: - func main() { - pipline := make(chan string) - go func() { - pipline <- "hello world" - pipline <- "hello China" - // close(pipline) - }() - for data := range pipline{ - fmt.Println(data) - } - } + .. code:: go -包子铺里的包子已经卖完了,可还有人在排队等着买,如果不再做包子,就要告诉排队的人:不用等了,今天的包子已经卖完了,明日请早呀。 + // Copy copies file from source to target path. + // It returns false and error when error occurs in underlying function calls. -不能让人家死等呀,不跟客人说明一下,人家还以为你们店后面还在蒸包子呢。 +- 若函数或方法为判断类型(返回值主要为 ``bool`` 类型),则以 + `` returns true if`` 开头: -所以这个问题,解决方法很简单,只要在发送完数据后,手动关闭信道,告诉 -range 信道已经关闭,无需等待就行。 + .. code:: go -.. code:: go + // HasPrefix returns true if name has any string in given slice as prefix. + func HasPrefix(name string, prefixes []string) bool { ... - package main +特别注释 - import "fmt" +- TODE:提醒维护人员此部分代码待完成 +- FIXME:提醒维护人员此处有BUG待修复 +- NOTE:维护人员要关注的一些问题说明 - func main() { - pipline := make(chan string) - go func() { - pipline <- "hello world" - pipline <- "hello China" - close(pipline) - }() - for data := range pipline{ - fmt.Println(data) - } - } +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_25.md b/source/c09/c09_25.md new file mode 100644 index 0000000..812e6a4 --- /dev/null +++ b/source/c09/c09_25.md @@ -0,0 +1,182 @@ +# 9.25 Go语言的包依赖管理 + +在以前,Go 语言的的包依赖管理一直都被大家所诟病,但最近几年,这个窘境开始得到缓解。 + +现在最主流的包依赖管理方式是使用官方推荐的 go module 的方式,如果你和我一样是刚开始学习Go语言的,建议也了解一下Go语言包依赖管理方案,是如何一步一步发展到今天的。 + +简单来说,Go语言的包依赖管理方案,可以分为三个阶段。 + +## **第一阶段**:使用最古老的 GOPATH 进行管理 + +使用 GOPATH 的话,你需要将你所有的第三方库都下载到本地,这本身没有什么问题,其他语言也都是这么做的,问题就在于,在本地只能保存一个版本的第三方库,如果你的机器上有多个项目,而这些项目是基于不同版本的库进行开发的,那就尴尬了。 + + + +## **第二阶段**:使用 GOVENDOR 解决方案 + +为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 开始支持 vendor 。 + +以前使用 GOPATH 的时候,所有的项目都共享一个 GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH 下只能有一个版本的第三方库。 + +解决的思路就是,在每个项目下都创建一个 vendor 目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5+ 的Go 会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。如果在 vendor 目录下没有找到,才会去 `$GOAPTH/src` 中去查找 + +在这个阶段,也催生出了各种第三方的管理包依赖插件:godep,dep,glide + +使用 godep 的开发流程是这样的: + +1. 先保证程序在本地能够正常编译 +2. 执行`godep save`保存当前项目的所有第三方依赖的版本信息和代码到 `Godeps/Godeps.json`文件中 +3. 提交Godeps目录和vender目录到代码库。 +4. 如果要更新依赖的版本,可以直接修改`Godeps.json`文件中的对应项 + +虽然这个方案解决了一些问题,但是解决得并不完美。 + +如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 + + + +## **第三阶段**:使用 GO MODULE 版本管理工具 + +go module 在 v1.11 版本推出,并在 v1.13版本中,成为官方默认的包依赖管理工具。 + +v1.11 开始,`go env` 多了个环境变量: `GO111MODULE`,这是一个开关,通过它可以开启或关闭模块支持,它有三个可选值:`off`、`on`、`auto`,默认值是`auto`。 + +1. `GO111MODULE=off`禁用模块支持,编译时会从`GOPATH`和`vendor`文件夹中查找包。 +2. `GO111MODULE=on`启用模块支持,编译时会忽略`GOPATH`和`vendor`文件夹,只根据 `go.mod`下载依赖。 +3. `GO111MODULE=auto`,当项目在`$GOPATH/src`外且项目根目录有`go.mod`文件时,开启模块支持。 + + + +go mod 出现后, GOPATH 和 GOVENDOR 将被逐步淘汰,但是若你的项目仍然要使用那些 out 的包依赖管理方案,需要注意将 GO111MODULE 置为 off。 + + + +接下来,介绍一下,如何使用 go module 来管理依赖。 + +使用 go module 管理依赖后会在项目根目录下生成两个文件`go.mod`和`go.sum`。 + +`go.mod` 文件记录了项目所有的依赖信息,其结构大致如下: + +``` +module github.com/Q1mi/studygo/blogger + +go 1.12 + +require ( + github.com/DeanThompson/ginpprof v0.0.0-20190408063150-3be636683586 + github.com/gin-gonic/gin v1.4.0 + github.com/go-sql-driver/mysql v1.4.1 + github.com/jmoiron/sqlx v1.2.0 + github.com/satori/go.uuid v1.2.0 + google.golang.org/appengine v1.6.1 // indirect +) +``` + +其中, + +- `module`用来定义包名 +- `require`用来定义依赖包及版本 +- `indirect`表示间接引用 + +而 `go.sum` 记录该项目中每个依赖库的版本和哈希值。 + + + +使用 go module 需熟悉 go mod 的命令 + +``` +go mod init 初始化当前文件夹, 创建go.mod文件,后可接参数指定 module 名 +go mod graph 打印模块依赖图 +go mod tidy 增加缺少的module,删除无用的module +go mod vendor 将依赖复制到vendor下 +go mod verify 校验依赖 +go mod why 解释为什么需要依赖 +go mod download 下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录) + +go mod edit 编辑go.mod文件 + 接 -fmt 参数格式化 go.mod 文件 + 接 -require=golang.org/x/text 添加依赖 + 接 -droprequire=golang.org/x/text 删除依赖 + 更加用法,参看 go help mod edit +``` + + + +如何给项目添加依赖(下载包并将依赖写进go.mod文件)? + +有两种方法: + +- 你只要在项目中有 import,然后 go build 就会 go module 就会自动下载并添加。 + +- 自己手工使用 go get 下载,支持语义化版本号 + +```shell +# 拉取最新 +go get github.com/foo + +# 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) +go get -u github.com/foo + +# 升级到最新的修订版本 +go get -u=patch github.com/foo + +# 指定版本 +go get github.com/foo@v1.2.3 + +# 指定分支 +go get github.com/foo@master + +# 指定git提交的hash值 +go get github.com/foo@e3702bed2 + +# 指定版本 +go get github.com/foo@v1.11.0 +``` + + + +使用以上方式添加完依赖后,在 go.mod 文件里会有你所依赖的包及其版本。 + + + +如果项目下已经有这个 go.mod 文件,但是包还没拉取,如何 触发下载呢?两种方法 + +- 可以执行命令 `go build ./...`,go module 会自动下载 +- 使用 `go mod download` ,手动下载 + +如果你连这个 go.mod 文件都丢失了,那怎么生成?两种方法 + +- 使用 Goland 的话,可以点击 create go.mod 文件来生成。 +- 也可以在项目目录下执行这条命令 + +```shell +$ go mod init +``` + + + +由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 + +``` +replace ( + golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac + golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d + golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 +) +``` + + + +以上几种解决方案,不同之处就在于它们的依赖包的搜索路径优先级不同 + +- 使用 go mod,只在 `$GOPATH/pkg/mod` 查找依赖包(GO111MODULE=on) +- 使用 GOVENDOR,优先在 vendor目录中查找,然后才去 `$GOROOT/src` 和 `$GOPATH/src`查找 +- 使用 GOPATH,先去`$GOROOT/src` 查找 ,找不到再去 `$GOPATH/src` 中查找 + + + +## 参考文章: + +- [Go语言之依赖管理](https://www.cnblogs.com/Dr-wei/p/11742253.html) + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) diff --git a/source/c09/c09_25.rst b/source/c09/c09_25.rst index cef5af0..f6363e4 100644 --- a/source/c09/c09_25.rst +++ b/source/c09/c09_25.rst @@ -1,4 +1,4 @@ -9.12 Go语言的包依赖管理 +9.25 Go语言的包依赖管理 ======================= 在以前,Go @@ -181,3 +181,8 @@ go mod 出现后, GOPATH 和 GOVENDOR ---------- - `Go语言之依赖管理 `__ + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c09/c09_27.md b/source/c09/c09_27.md new file mode 100644 index 0000000..916a4e2 --- /dev/null +++ b/source/c09/c09_27.md @@ -0,0 +1,7 @@ +# 9.27 go 命令详解 + + + +go build ./... 不会生成可执行文件到 ${GOPATH}/bin ,只要能编译过就行 + +go install ./... 会生成可执行文件到 ${GOPATH}/bin \ No newline at end of file diff --git a/source/c09/c09_27.rst b/source/c09/c09_27.rst index 6b12c3a..bd31969 100644 --- a/source/c09/c09_27.rst +++ b/source/c09/c09_27.rst @@ -1,4 +1,4 @@ -9.13 go 命令详解 +9.27 go 命令详解 ================ go build ./… 不会生成可执行文件到 ${GOPATH}/bin ,只要能编译过就行 diff --git a/source/c09/c09_28.md b/source/c09/c09_28.md new file mode 100644 index 0000000..a907fc5 --- /dev/null +++ b/source/c09/c09_28.md @@ -0,0 +1,14 @@ +# 9.28 函数式编程 + +函数是一等公民,它可以做为参数进行传递,可以做为函数返回值,也可以做为接收者。所以后来才有了高阶函数,闭包。 + + + +正统的函数式编程 + +- 不可变性:不能有状态,只能有函数和变量 +- 函数只能有一个参数 + +Go 语言不是正统 + +![image-20200131160151750](C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200131160151750.png) \ No newline at end of file diff --git a/source/c09/c09_30.rst b/source/c09/c09_28.rst similarity index 95% rename from source/c09/c09_30.rst rename to source/c09/c09_28.rst index a502e38..39ba71b 100644 --- a/source/c09/c09_30.rst +++ b/source/c09/c09_28.rst @@ -1,4 +1,4 @@ -9.30 函数式编程 +9.28 函数式编程 =============== 函数是一等公民,它可以做为参数进行传递,可以做为函数返回值,也可以做为接收者。所以后来才有了高阶函数,闭包。 diff --git a/source/c10/c10_01.md b/source/c10/c10_01.md index 858ef63..1d9e66a 100644 --- a/source/c10/c10_01.md +++ b/source/c10/c10_01.md @@ -140,3 +140,5 @@ img_new.convert('RGB').save("F://save.jpeg") ``` + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst index 1548ac4..887ae98 100644 --- a/source/c10/c10_01.rst +++ b/source/c10/c10_01.rst @@ -150,6 +150,11 @@ img_new.convert('RGB').save("F://save.jpeg") +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/20200214104413.png .. |image1| image:: http://image.python-online.cn/save.jpeg .. |image2| image:: http://image.python-online.cn/20200214104646.png From 58a8f8dc34a8ee2e21a63ed43fd3fdc687a7fad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sun, 1 Mar 2020 13:12:34 +0800 Subject: [PATCH 012/147] update --- source/c09/c09_24.md | 224 +++++++++------------- source/c09/c09_26.md | 177 +++++++++++++++++ source/mp_index.md | 439 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 707 insertions(+), 133 deletions(-) create mode 100644 source/c09/c09_26.md create mode 100644 source/mp_index.md diff --git a/source/c09/c09_24.md b/source/c09/c09_24.md index 0b0184c..9cbd419 100644 --- a/source/c09/c09_24.md +++ b/source/c09/c09_24.md @@ -1,177 +1,135 @@ -# 9.9 Go语言命名编码规范 +# 9.24 panic 和 recover -每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 +编程语言一般都会有异常捕获机制,在 Python 中 是使用`raise` 和 `try-except` 语句来实现的异常抛出和异常捕获的。 -以下内容整理自:[Go语言(Golang)编码规范](https://www.bookstack.cn/books/go-code-convention) +在 Golang 中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 -命名规范分为以下几点 +当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 panic,让程序退出停止运行。 -**1. 文件命名** +## 1. 触发panic -文件名应一律使用小写(因为Windows文件名不区分大小写?), 不同单词之间用下划线分割。 +手动触发宕机,是非常简单的一件事,只需要调用 panic 这个内置函数即可,就像这样子 -应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 MyBlog的入口应当为 myblog.go - -**2. 常量命名 ** - -- 常量均需使用全部大写字母组成,并使用下划线分词: +```go +package main - ```go - const APP_VER = "0.7.0.1110 Beta" - ``` +func main() { + panic("crash") +} +``` -- 如果是枚举类型的常量,需要先创建相应类型: +运行后,直接报错宕机 - ```go - type Scheme string - const ( - HTTP Scheme = "http" - HTTPS Scheme = "https" - ) - ``` +```shell +$ go run main.go +go run main.go +panic: crash -- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: +goroutine 1 [running]: +main.main() + E:/MING-Code/GoPlayer/src/imooc.com/ccmouse/learngo/main.go:4 +0x40 +exit status 2 +``` - ```go - type PullRequestStatus int - const ( - PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota - PULL_REQUEST_STATUS_CHECKING - PULL_REQUEST_STATUS_MERGEABLE - ) - ``` +## 2. 捕获 panic -**3. 变量命名** +发生了异常,有时候就得捕获,就像 Python 中的` except` 一样,那 Golang 中是如何做到的呢? -使用驼峰命名法 +这就不得不引出另外一个内建函数 -- `recover`,它可以让程序在发生宕机后起生回生。 -- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u -- 若该变量为 bool 类型,则名称应以 `Has`, `Is`, `Can` 或 `Allow` 开头。例如:isExist ,hasConflict 。 -- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 startDate 。 -- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 `apiClient`。 -- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient +但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效,其他作用域下,它是不工作的。 -下面列举了一些常见的特有名词: +这是一个简单的例子 -``` -// A GonicMapper that contains a list of common initialisms taken from golang/lint -var LintGonicMapper = GonicMapper{ - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SSH": true, - "TLS": true, - "TTL": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XSRF": true, - "XSS": true, +```go +import "fmt" + +func set_data(x int) { + defer func() { + // recover() 可以将捕获到的panic信息打印 + if err := recover(); err != nil { + fmt.Println(err) + } + }() + + // 故意制造数组越界,触发 panic + var arr [10]int + arr[x] = 88 } -``` - - - -**接口命名** -使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 +func main() { + set_data(20) -```go -type helloWorld interface { - func Hello(); + // 如果能执行到这句,说明panic被捕获了 + // 后续的程序能继续运行 + fmt.Println("everything is ok") } - -type SayHello helloWorld ``` - - -**注释规范** - -单行注释使用 `//` ,多行注释使用 `/* comment */` +运行后,输出如下 ```go -// go语言 - -/* -Go 语言 -Hello, World -*/ +$ go run main.go +runtime error: index out of range [20] with length 10 +everything is ok ``` -- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 -- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 -- 包、函数、方法和类型的注释说明都是一个完整的句子。 +通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 -- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 -- 注释的单行长度不能超过 80 个字符。 -- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 +## 3. 无法跨协程 -- 如果是特别复杂的包,可单独创建 doc.go 文件说明 +从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer 。 -- 类型的定义一般都以单数形式描述: +但是这个 defer 在多个协程之间是没有效果,在子协程里触发 panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer 函数的。 - ```go - // Request represents a request to run a command. type Request struct { ... - ``` +来做个实验就知道了 -- 如果为接口,则一般以以下形式描述: - - ```go - // FileInfo is the interface that describes a file and is returned by Stat and Lstat. - type FileInfo interface { ... - ``` +```go +import ( + "fmt" + "time" +) + +func main() { + // 这个 defer 并不会执行 + defer fmt.Println("in main") + + go func() { + defer println("in goroutine") + panic("") + }() + + time.Sleep(2 * time.Second) +} +``` -- 函数与方法的注释需以函数或方法的名称作为开头: +输出如下 - ```go - // Post returns *BeegoHttpRequest with POST method. - ``` +``` +in goroutine +panic: + +goroutine 6 [running]: +main.main.func1() + E:/MING-Code/main.go:12 +0x7b +created by main.main + E:/MING-Code/main.go:10 +0xbc +exit status 2 +``` -- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: - ```go - // Copy copies file from source to target path. - // It returns false and error when error occurs in underlying function calls. - ``` -- 若函数或方法为判断类型(返回值主要为 `bool` 类型),则以 ` returns true if` 开头: +## 总结一下 - ```go - // HasPrefix returns true if name has any string in given slice as prefix. - func HasPrefix(name string, prefixes []string) bool { ... - ``` +Golang 异常的抛出与捕获,依赖两个内置函数: -特别注释 +- panic:抛出异常,使程序崩溃 +- recover:捕获异常,恢复程序 -- TODE:提醒维护人员此部分代码待完成 -- FIXME:提醒维护人员此处有BUG待修复 -- NOTE:维护人员要关注的一些问题说明 +revocer 调用后,抛出的 panic 将会在此外终结,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![image-20200301121138527](upload\image-20200301121138527.png) \ No newline at end of file diff --git a/source/c09/c09_26.md b/source/c09/c09_26.md new file mode 100644 index 0000000..b50f796 --- /dev/null +++ b/source/c09/c09_26.md @@ -0,0 +1,177 @@ +# 9.26 Go语言命名编码规范 + +每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 + +以下内容整理自:[Go语言(Golang)编码规范](https://www.bookstack.cn/books/go-code-convention) + +命名规范分为以下几点 + +**1. 文件命名** + +文件名应一律使用小写(因为Windows文件名不区分大小写?), 不同单词之间用下划线分割。 + +应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 MyBlog的入口应当为 myblog.go + +**2. 常量命名 ** + +- 常量均需使用全部大写字母组成,并使用下划线分词: + + ```go + const APP_VER = "0.7.0.1110 Beta" + ``` + +- 如果是枚举类型的常量,需要先创建相应类型: + + ```go + type Scheme string + const ( + HTTP Scheme = "http" + HTTPS Scheme = "https" + ) + ``` + +- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: + + ```go + type PullRequestStatus int + const ( + PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota + PULL_REQUEST_STATUS_CHECKING + PULL_REQUEST_STATUS_MERGEABLE + ) + ``` + +**3. 变量命名** + +使用驼峰命名法 + +- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u +- 若该变量为 bool 类型,则名称应以 `Has`, `Is`, `Can` 或 `Allow` 开头。例如:isExist ,hasConflict 。 +- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 startDate 。 +- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 `apiClient`。 +- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient + +下面列举了一些常见的特有名词: + +``` +// A GonicMapper that contains a list of common initialisms taken from golang/lint +var LintGonicMapper = GonicMapper{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, +} +``` + + + +**接口命名** + +使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 + +```go +type helloWorld interface { + func Hello(); +} + +type SayHello helloWorld +``` + + + +**注释规范** + +单行注释使用 `//` ,多行注释使用 `/* comment */` + +```go +// go语言 + +/* +Go 语言 +Hello, World +*/ +``` + +- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 + +- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 + +- 包、函数、方法和类型的注释说明都是一个完整的句子。 + +- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 + +- 注释的单行长度不能超过 80 个字符。 + +- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 + +- 如果是特别复杂的包,可单独创建 doc.go 文件说明 + +- 类型的定义一般都以单数形式描述: + + ```go + // Request represents a request to run a command. type Request struct { ... + ``` + +- 如果为接口,则一般以以下形式描述: + + ```go + // FileInfo is the interface that describes a file and is returned by Stat and Lstat. + type FileInfo interface { ... + ``` + +- 函数与方法的注释需以函数或方法的名称作为开头: + + ```go + // Post returns *BeegoHttpRequest with POST method. + ``` + +- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: + + ```go + // Copy copies file from source to target path. + // It returns false and error when error occurs in underlying function calls. + ``` + +- 若函数或方法为判断类型(返回值主要为 `bool` 类型),则以 ` returns true if` 开头: + + ```go + // HasPrefix returns true if name has any string in given slice as prefix. + func HasPrefix(name string, prefixes []string) bool { ... + ``` + +特别注释 + +- TODE:提醒维护人员此部分代码待完成 +- FIXME:提醒维护人员此处有BUG待修复 +- NOTE:维护人员要关注的一些问题说明 + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/mp_index.md b/source/mp_index.md new file mode 100644 index 0000000..7717453 --- /dev/null +++ b/source/mp_index.md @@ -0,0 +1,439 @@ +# Python编程时光 - 学习索引 + +为了方便读者们能从本号真正学到一些有用的内容,我将 Python 编程时光干货文章进行了整理,方便你学习。 + +## 01. 基础系列 + +### 1.1 基础必学 + +1、[15个Pythonic的代码示例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485036&idx=1&sn=24de1996a63bf25b0c0deec782f688cf&scene=21#wechat_redirect) + +2、[Python基础|深入闭包与变量作用域](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485039&idx=1&sn=23557ac2640819b568a426b2db4df69c&scene=21#wechat_redirect) + +3、[Python基础|类方法的强制重写与禁止重写](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485037&idx=1&sn=8f4838b5bc919631c5cb642120b010c2&scene=21#wechat_redirect) + +4、[Python基础|多继承与Mixin设计模式](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485032&idx=1&sn=35e1c7014bc3f668cc4b48d9c318f1f9&scene=21#wechat_redirect) + +5、[Python基础|理解元组存在的意义](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485038&idx=1&sn=d0c7ab3fc20b299e4a0b9f6b414e4070&scene=21#wechat_redirect) + +6、[你知道 Python里的「单分派泛函数」?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484938&idx=1&sn=061fdb00ccc499aa0cfc304852adb143&scene=21#wechat_redirect) + +7、[类型注解的福音,提高Python代码可读性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484901&idx=1&sn=8f506cb46edb94dfb668525399f30f9e&scene=21#wechat_redirect) + +8、[写几个 Python 进阶必备函数](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484973&idx=1&sn=451d381fa3021e14b514cc15907ce0c3&scene=21#wechat_redirect) + +9、[Python 字符串连接,哪种的效率最高?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485016&idx=1&sn=88497c09c8785b656ae1fee1406827dc&scene=21#wechat_redirect) + +10、[深入理解Python中的上下文管理器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484950&idx=1&sn=9d78e469f8f190ef0484842b0391bec9&scene=21#wechat_redirect) + +11、[秒杀市面 90% 的 Python 入门教程 (上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484852&idx=1&sn=2362e034c2740d9f53c3cc99a6908cbd&scene=21#wechat_redirect) + +12、[秒杀市面 90% 的 Python 入门教程 (中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484856&idx=1&sn=881ee5d8154a85479d71ca4594f90142&scene=21#wechat_redirect) + +13、[秒杀市面 90% 的 Python 入门教程 (下)](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485231&idx=1&sn=fa7146937f51110eb7356154bc2e2282&scene=21#wechat_redirect) + +14、[检验你 Python 基本功的 17 个骚操作](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484863&idx=2&sn=ff11a17ad82938b3a4f83ee5fbc2e497&scene=21#wechat_redirect) + +25、[和import说再见,这个库教你怎么偷懒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485325&idx=2&sn=90c6dfbdbb36f63db72d1c0af67c1115&scene=21#wechat_redirect) + +16、[如何修改 CentOS 6.x 上默认Python 版本](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484881&idx=1&sn=ecb99878d3e2fca2bedc808b1e7aae9c&scene=21#wechat_redirect) + +17、[写 Python 时你要避免的十个错误](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=1&sn=6acba3bf872fe1309308d9ef6cde53ce&scene=21#wechat_redirect) + +18、[为何无法使用 ip 访问网站?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484976&idx=1&sn=503d75faea7a633729ec90d6f26e21a2&scene=21#wechat_redirect) + +19、[大白话解释什么是 Python Launcher?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485268&idx=1&sn=9cea766f1e1a1f6278fca4665fc33a87&scene=21#wechat_redirect) + +20、[13条Python2.x和3.x的区别,你知道几条?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485044&idx=1&sn=f38e2036317893be8388900dde3077e8&scene=21#wechat_redirect) + +21、[Python基础|新式类和经典类的区别?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485033&idx=1&sn=8b6357d5a66f9a06347bf0922ae11946&scene=21#wechat_redirect) + +22、[Python 中有 3 个不可思议的返回功能](https://mp.weixin.qq.com/s/dSky88bOCPgYDprzJhnlbg) + +23、[每天花 30 秒,就可以练习的 Python 小技巧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484861&idx=1&sn=e5fa97570b3c180e5a5edc753fc62669&scene=21#wechat_redirect) + +24、[常见 Python 简洁代码的样例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484823&idx=2&sn=4459a7edc4a4989068b22b31c76f2c92&scene=21#wechat_redirect) + +25、[ 别笑!Python 新手这五大坑你躲不过](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485573&idx=1&sn=362e7c5e73c27942e8250375257620e2&chksm=e8866867dff1e17140dc043b5dd33eec0e94e1616eab7e4b415bb767883d46d58a7243fb198f#rd) + +### 1.2 开发技巧 + +1、[让Python中类的属性具有惰性求值的能力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485759&idx=2&sn=e32b66c4666f96f84572738549a47a24&chksm=e88669dddff1e0cb0a4b6e6e965c9570b273236fa58a54351ae3b118d6a4bcbd52e452109a63&token=1148998814&lang=zh_CN#rd) + +2、[ 小技巧:如何在 Python 中实现延迟调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485689&idx=2&sn=43e35ff93918ef090480672d4685bce3&chksm=e886681bdff1e10dd0edee95c73a977da51fdeb514dcf8c3bb88f7f9aacc22ce155f45e84268#rd) + +3、[三个异常处理的好习惯](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484947&idx=1&sn=2bd31279dd9714045ed4dc5481b44208&scene=21#wechat_redirect) + +4、[超实用的 30 段 Python 案例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484866&idx=1&sn=9c85dd1b58f319bb0339e2db577a0be6&scene=21#wechat_redirect) + +5、[删除系统 Python 引发的惨案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485386&idx=1&sn=59eed0894159f4d0599be3fb787b5201&chksm=e8866728dff1ee3ef9fb7a0464df481306be11d9aeba944804e7ea74e660501d42e159ada7fc#rd) + +6、[ 利用 Flask 动态展示 Pyecharts 图表数据的几种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485658&idx=1&sn=f78e976889de52c0ce35e862bafacd8a&chksm=e8866838dff1e12e41b8f2dd1a43dc540ff39484ae64831332d233b5a4f7ca3b439d370ef1ed#rd) + +## 02. 进阶系列 + +### 2.1 进阶必学 + +1、[描述符:其实你不懂我(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484934&idx=1&sn=ef8b30ffe2467e5736036bd083b340ca&scene=21#wechat_redirect) + +2、[描述符:我无处不在!(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484927&idx=1&sn=8a3d673ee20bd418d93a249380ca8e76&scene=21#wechat_redirect) + +3、[Python静态方法其实暗藏玄机](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484911&idx=1&sn=a16846030c3589c912aec9efdf4f7f80&scene=21#wechat_redirect) + +4、[全面深入理解 Python 面向对象](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485208&idx=1&sn=61bf8d3ec81fa85b0bc9de6e5f5d6611&scene=21#wechat_redirect) + +5、[几个使用装饰器的小技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484930&idx=1&sn=ed731e32b6e95e7d83d74a544f97142a&scene=21#wechat_redirect) + +6、[围观大神是如何用 Python 处理文件的?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484905&idx=1&sn=7de0eaab0a4c9f8b44cba01d28ad7254&scene=21#wechat_redirect) + +7、[Python进阶开发|元类编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485080&idx=1&sn=45e6d9995e4469d8b7bce2f800ac0f9b&scene=21#wechat_redirect) + +8、[Python进阶开发之网络编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485078&idx=1&sn=e0a3855d959c178b8c01bd196a048dce&scene=21#wechat_redirect) + +9、[Python 进阶:深入 GIL (上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484875&idx=2&sn=8780a349c60522ad1b7aa20a3fe7a19c&scene=21#wechat_redirect) + +10、[没掌握好这24条,别说Python慢。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484910&idx=1&sn=df8fb32a840a28954614b09bce730b72&scene=21#wechat_redirect) + +11、[花了两个星期,我终于把WSGI整明白了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484919&idx=1&sn=bd7d2bc0ab8a41110d5d93e44ad20b1f&scene=21#wechat_redirect) + +12、[源码解读|Flask 上下文核心机制](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484943&idx=1&sn=ca846404d30a2ec775ab7db5df73aa8e&scene=21#wechat_redirect) + +13、[说说几个 Python 内存分配时的小秘密](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484853&idx=2&sn=edf7c3225d119291fae5d05820f55aac&scene=21#wechat_redirect) + +14、[写了三年代码,还是不懂 Python 世界的规则](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484886&idx=1&sn=f46917f6e23f8961c912606a281a354d&scene=21#wechat_redirect) + +15、[如何保护你写的 Python 代码?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484858&idx=1&sn=e18a1c89f14d4e4043833e3ff5cc8d32&scene=21#wechat_redirect) + +16、[27 个问题,告诉你 Python 为什么如此设计?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484862&idx=1&sn=89500433bc174dfc0530eebea2fb91d1&scene=21#wechat_redirect) + +17、[精心整理 30 个Python代码实现的常用功能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485147&idx=2&sn=c5b871e1cebde6b311609f6de703f2e3&scene=21#wechat_redirect) + +18、[高手之路:从零开始打造一个Web服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484891&idx=1&sn=ed12d175452c2efedeefb1635063177c&scene=21#wechat_redirect) + +19、[从0到1:全面理解 RPC 远程调用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484915&idx=1&sn=427b9dda155e4201c2f939c3919def91&scene=21#wechat_redirect) + +20、[一篇 Python 函数式编程指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484834&idx=1&sn=559bac4ff85f7082bc989a6fde04d3f3&scene=21#wechat_redirect) + +21、[没看完这11 条,别说你精通 Python 装饰器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484900&idx=1&sn=3997a2a377577e3d16a9b7f8e6a5ea53&scene=21#wechat_redirect) + +22、[程序卡住了?教你如何调试已在运行的程序](https://mp.weixin.qq.com/s/QC-Pc-0iifaVKOfsiNTYmA) + +23、[字符串在Python内部是如何省内存的](https://mp.weixin.qq.com/s/jp_I82fnr0-3cd6ioZcoGQ) + +24、[巧用 traceback 定位 Python 内存泄漏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485751&idx=1&sn=8a054a575767c103c830a6b3ef5f73c8&chksm=e88669d5dff1e0c3ae1c7c32f35a8f46a3a2e0a637fb4b947566f417dd8f4419bec636c8a667#rd) + +25、[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd) + +26、[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd) + +27、[教你如何阅读 Python 开源项目代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485451&idx=1&sn=e15856352c18297770c67d9df66ec3b0&chksm=e88668e9dff1e1ff0f6a39f3885f4126232213149ac09daa1fc0ec9e0de2095bd11fb727d63a#rd) + +### 2.2 包的管理 + +1、[最全的 pip 使用指南,50% 你可能没用过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484827&idx=1&sn=df0923856c820e10baca20c9873b336b&scene=21#wechat_redirect) + +2、[全面学习 Python 包:包的构建与分发](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485349&idx=1&sn=8d6da555a2852a14506a491c4cf9a234&scene=21#wechat_redirect) + +3、[深入探讨 Python 的import机制:实现远程导入模块](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&scene=21#wechat_redirect) + +4、[盘点 Python 依赖库管理的工具:pip、pipreqs、pigar、pip-tools、pipdeptree](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485120&idx=1&sn=170ef1173eabc5bed28c724e19d6c0ac&scene=21#wechat_redirect) + +### 2.3 性能优化 + +1、[Python高效代码实践:性能、内存和可用性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485161&idx=1&sn=07625f06db330897bc8bfee880ceac18&scene=21#wechat_redirect) + +2、[Python 代码的性能优化之道](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485004&idx=1&sn=8dc07a40bcac30c93874398b17a52831&scene=21#wechat_redirect) + +3、[7 个习惯帮你提升Python运行性能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484957&idx=1&sn=9b3c644e6b1777b77ce7f84047c6d500&scene=21#wechat_redirect) + +4、[如何提升你的 Python 代码健壮性(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484871&idx=1&sn=985b79143d0cafd21e5263ee79afa777&scene=21#wechat_redirect) + +5、[如何提升你的 Python 代码健壮性(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484867&idx=1&sn=b8eab8416229dc74f1ac96e099a3df71&scene=21#wechat_redirect) + +6、[将 Python 运行速度提升 30 倍的神技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484877&idx=1&sn=f6067875f1e807d19291e383f8421a3b&scene=21#wechat_redirect) + +7、[实战讲解:Python 性能分析与优化实践](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485404&idx=1&sn=486fcf898d6dd21b0feb990de3c1e08f&chksm=e886673edff1ee28b8c6e1d520b2732b2f66f4c4691ef2696b54743b1ad0769356e12a1e98b2#rd) + +### 2.4 并发编程 + +1、[并发编程01|从性能角度来初探并发编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485074&idx=1&sn=a859c6ab1d9b95c30c9f8b06f9489887&scene=21#wechat_redirect) + +2、[并发编程02|创建多线程的几种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485073&idx=1&sn=854ff524645247e5020d977e57d9c0e6&scene=21#wechat_redirect) + +3、[并发编程03|谈谈线程中的“锁机制”](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485069&idx=1&sn=52370a27d4a5c4541969921ada890e0b&scene=21#wechat_redirect) + +4、[并发编程04|消息通信机制/任务协调](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485068&idx=1&sn=fc0798e84e7845cd9efee1809f932f15&scene=21#wechat_redirect) + +5、[并发编程05|线程中的信息隔离](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485066&idx=1&sn=0bb9d6c82a6d062e50d09e1eefd09427&scene=21#wechat_redirect) + +6、[并发编程06|如何创建线程池](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485065&idx=1&sn=60891b67b139806cf6bf4c05f5861f02&scene=21#wechat_redirect) + +7、[并发编程07|从生成器使用入门协程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485064&idx=1&sn=8584bb778152a12ca335970bed9fdbdc&scene=21#wechat_redirect) + +8、[并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect) + +9、[并发编程09|初识异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485061&idx=1&sn=9e846df1a5cb57e0bc6254dcc953e243&scene=21#wechat_redirect) + +10、[并发编程12|学习异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485058&idx=1&sn=92ef1f79ce6488670ae13e5d6a1c7908&scene=21#wechat_redirect) + +11、[并发编程11|实战异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485057&idx=1&sn=8bacf0f2b42de5962fbdf32796903f27&scene=21#wechat_redirect) + +12、[百万「并发」之Python异步编程(上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484896&idx=2&sn=5d8458ede3440ae501d19c5ffd5f8a99&scene=21#wechat_redirect) + +13、[百万「并发」之Python异步编程(中篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484889&idx=2&sn=25d11042e5b2ab17dff40535d354b8f8&scene=21#wechat_redirect) + +14、[百万「并发」之Python异步编程(下篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=2&sn=b9e62a9b024358aec629e876b76e0eb5&scene=21#wechat_redirect) + +15、[如何一行 Python 代码实现并行?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484826&idx=1&sn=6c5b575b7b134642077cfde2bb8b613f&scene=21#wechat_redirect) + +16、[asyncio:从原理、源码到实现](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485245&idx=1&sn=a08826c3d28037479190fd652438bcca&scene=21#wechat_redirect) + +17、[为什么说线程是CPU调度的基本单位?](https://mp.weixin.qq.com/s/c3aZ-6UzZVD3_PvVhiDPWA) + +## 03. 数据分析 + +### 3.1 基础库 + +1、[有关 NumPy 和数据表达的可视化介绍](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485295&idx=1&sn=6220c2ed0d73feade0c9691fbf214ba7&scene=21#wechat_redirect) + +### 3.2 数据可视化 + +1、[可视化01|一图带你入门matplotlib](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485022&idx=1&sn=76d4270a15c430217588bd9f8be8303b&chksm=e88666bcdff1efaa35c6d685f19b04e62f58e768dc9d1819bcb9f8211f65d3fd83296259d923&token=1148998814&lang=zh_CN#rd) + +2、[可视化02|详解六种可视化图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485020&idx=1&sn=7f82736e6a2e5442ed59c82d8e242ca4&scene=21#wechat_redirect) + +3、[可视化03|用正余弦学习matplotlib](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485019&idx=1&sn=9d44ee27fd888831e94845d6d0256deb&scene=21#wechat_redirect) + +4、[可视化04|子图与子区难点剖析](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485014&idx=1&sn=90dbdf17f049c152944a4c28642df249&scene=21#wechat_redirect) + +5、[可视化05|绘制酷炫的GIF动态图](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485007&idx=1&sn=1464dee30d006ddb5111f9827b0c4081&scene=21#wechat_redirect) + +6、[可视化06|自动生成图像视频](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485002&idx=1&sn=f09c089328eb0fe39721b73272c81214&scene=21#wechat_redirect) + +7、[可视化07|50个最有价值的图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484920&idx=1&sn=92eb2fd55f13a8bda75a7103a73f8d50&scene=21#wechat_redirect) + +### 3.3 工具使用 + +1、[ 整理了 50个 IPython 的实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485613&idx=1&sn=70f89faf2573b3025edc73f525f22a0a&chksm=e886684fdff1e159509d30fd0a24854da39fb5a6ae55e0ed6e40b7971d3219968006f80aad7b#rd) + +2、[ Jupyter NoteBook 的使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485024&idx=1&sn=aac0db4b2c97c7f5c2871342b936eaa2&chksm=e8866682dff1ef947ec6474b8c03f668e462232ebd0ec0f137d42e5e63b9b66a3d9e303c1caf&token=1148998814&lang=zh_CN#rd) + +## 04. 开发工具 + +1、[代码调试|无图形调试工具 - pdb](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484969&idx=1&sn=a99fc31865edc4b439707d2be6f66654&scene=21#wechat_redirect) + +2、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484970&idx=1&sn=9ac9c5dfcdc8c6b48a7b4c9854825734&scene=21#wechat_redirect) + +3、[优化Python开发环境的几个技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485131&idx=1&sn=f1bd7b31a3624f015c74f56a8888a695&scene=21#wechat_redirect) + +4、[让你重新爱上 Windows 的小众软件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484964&idx=1&sn=1479536416788a6c55dddef70167eb88&scene=21#wechat_redirect) + +5、[Python 的命令行参数解析工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484960&idx=1&sn=3456d9a05b35588b060833ceb189db6e&scene=21#wechat_redirect) + +6、[搜索神器 EveryThing,你把它的潜力用到极致了吗?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484894&idx=1&sn=59877fe818739175d3d075458594f563&scene=21#wechat_redirect) + +7、[开发工具|盘点 Xshell 的那些奇淫技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485043&idx=1&sn=8c240e569c60607cd4cea8888a6aa2e1&scene=21#wechat_redirect) + +8、[开发工具|给你的项目买份保险:Python虚拟环境](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&scene=21#wechat_redirect) + +9、[学会这21条,你离 Vim 大神就不远了!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484902&idx=1&sn=d677261fda90b67bf5eda886447a4f77&scene=21#wechat_redirect) + +10、[用 Python 做开发,做到这些才能一直爽](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect) + +11、[这款神器,能把 Python 代码执行过程看地一清二楚](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484878&idx=1&sn=9bc941ff19ec38e7c7137a22df981f27&scene=21#wechat_redirect) + +12、[如何把自己的 Python 包发布到 PYPI?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484835&idx=2&sn=f24a415d47d4122c4d6b1a6ccf254a51&scene=21#wechat_redirect) + +13、[谁说 Vim 不好用?送你一个五彩斑斓的编辑器!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484872&idx=2&sn=ce718e511b754fe936172724a824a716&scene=21#wechat_redirect) + +14、[迄今为止,我见过最好的正则入门教程(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484831&idx=1&sn=077d14410db6a906c3b962f0ba18b2d4&scene=21#wechat_redirect) + +15、[迄今为止,我见过最好的正则入门教程(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484830&idx=1&sn=4d48b1c7f2b5234992732f8f15b868df&scene=21#wechat_redirect) + +16、[Win 平台做 Python 开发的最佳组合](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484837&idx=2&sn=68935077ae9eeb6fe4f708565aa0a9ed&scene=21#wechat_redirect) + +17、[告别996,全靠这个Python补全利器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484824&idx=2&sn=8f20af371954dfd2a5c890bb5814b4c9&scene=21#wechat_redirect) + +18、 + +### 4.1 PyCharm + +1、[受用一生的高效PyCharm使用技巧(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484946&idx=1&sn=12a891db18e9d1233535fa4aca9a3892&scene=21#wechat_redirect) + +2、[受用一生的高效PyCharm使用技巧(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484942&idx=1&sn=9dbb2cfe2a277bd3519d37414fcc608e&scene=21#wechat_redirect) + +3、[受用一生的高效PyCharm使用技巧(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484932&idx=1&sn=347b35ccdee94895652caa86191360c8&scene=21#wechat_redirect) + +4、[受用一生的高效PyCharm使用技巧(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484918&idx=1&sn=66b3fd784050e5cb85109faa3d063993&scene=21#wechat_redirect) + +5、[受用一生的高的PyCharm使用技巧(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484914&idx=1&sn=cd1282f94f9326647b05ad9090afd2c8&scene=21#wechat_redirect) + +6、[受用一生的高效PyCharm使用技巧(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484907&idx=1&sn=647a75b96884b39c57664f62ead11b89&scene=21#wechat_redirect) + +7、[受用一生的高效 PyCharm 使用技巧(七)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485372&idx=1&sn=3ed61c4ccbb089358213304cd70bb0f7&chksm=e886675edff1ee487911e922279675264916dd08120f5f40113f37dc080b94c7bf9ad67719c4&token=1148998814&lang=zh_CN#rd) + +8、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484304&idx=1&sn=7612f0c2fd6232723ca2733fb1e8f46d&scene=21#wechat_redirect) + +9、[手把手教你打造一个顔值超高的IDE](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485052&idx=1&sn=2e0bb1a3af4206fd2cf8e3650f695e6b&scene=21#wechat_redirect) + +### 4.2 VSCode + +1、[这 21 个VSCode 快捷键,能让你的代码飞起来](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484893&idx=1&sn=421b544115efad388314ccd027761b40&scene=21#wechat_redirect) + +2、[神技巧!在浏览器中也能用 VS Code](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484859&idx=2&sn=70abd19426374271a10cbef8e1645c7a&scene=21#wechat_redirect) + +### 4.3 好用的库 + +1、[使用 Python 远程登陆服务器的最佳实践](https://mp.weixin.qq.com/s/aRXYAP9D9rgil-0_Etb0SQ) + +2、[这么设置 Python 的环境变量,我还是第一次见](https://mp.weixin.qq.com/s/LcjIPZPSg0KbL9X-i6vigg) + +3、[太强了!Python中完美的日志解决方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485741&idx=1&sn=505c06669baa44873171a3eed81f32b4&chksm=e88669cfdff1e0d91635b75f54998667e04bfb05a1aebae096930bd23acdd9d35ea858861bb2#rd) + +4、[如何使用 Python 操作 Git 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485723&idx=1&sn=35511b71347111c00a9e1633309efd13&chksm=e88669f9dff1e0ef7aec03f2bdbddb8408bf12a57a36add441b75e486dd72df13be7629625ab#rd) + +5、[用它5分钟以后,我放弃用了四年的 Flask](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485657&idx=1&sn=e21ddc5b670a9b667135df2ca97ec99c&chksm=e886683bdff1e12d9e1d64a7f5bfbc65ca2372711cd9b08ef36ce0cdd99811ce44ff162585df#rd) + +6、[整理了 34 个被吹爆了的Python开源框架](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485653&idx=1&sn=edd893dd711b3681aa0bacdf8ab384fd&chksm=e8866837dff1e1216f6fbf413118f839e6e1dc5ed7b45117df55f13e25670c5ffba34966abff#rd) + +7、[ 为了选出最合适的 http客户端,我做了个测评](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485651&idx=1&sn=647c22ac66e469ccc33478457dadf224&chksm=e8866831dff1e1279149ff1e3af84bef86d24c524b7b7c14e9c519cf7c4739fa8f10dcc4a8d0#rd) + +## 05. 网络爬虫 + +1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect) + +2、[教你实现一个可视化爬虫监控系统](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484208&idx=1&sn=d04421526cf0e12089541063b4ec448d&scene=21#wechat_redirect) + +3、[估计是讲得最清楚的「异步爬虫」指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484929&idx=1&sn=7055414a33d7cbadaeb5645cbd5203ec&scene=21#wechat_redirect) + +4、[10 个爬虫工程师必备的工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484958&idx=1&sn=3422e53de4ae701a922104ad26f8e256&scene=21#wechat_redirect) + +## 06. 实用系列 + +1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect) + +2、[30分钟教你快速搭建一个顔值超高的博客](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485045&idx=1&sn=8b250c0c174e418e2025d86f42c695b6&scene=21#wechat_redirect) + +3、[用Python写一个表白神器让你七夕脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) + +4、[用 Sphinx 搭建博客时,如何导流到公众号?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484848&idx=1&sn=80ae18e7f53a64e62ac9c0ef0c21362e&scene=21#wechat_redirect) + +5、[10 行 Python 代码写 1 个 USB 病毒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484832&idx=1&sn=43c353856c7dd9ea2cb1a9bbcd077fa2&scene=21#wechat_redirect) + +6、[废旧 Android 手机如何改造成 Linux 服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485279&idx=1&sn=7166a2c9445438eaaf63a25b598e5427&scene=21#wechat_redirect) + +7、[如何将手机打造成 Python 开发利器?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485256&idx=1&sn=2e646e28287707c67c22fbe68112e622&scene=21#wechat_redirect) + +8、[我用 Python 做了一回黑客,批量破解了朋友的网站密码](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485156&idx=1&sn=05142ae7b4bf7e97ebe042e394aa7084&scene=21#wechat_redirect) + +9、[手把手教你安装Win+Ubuntu双系统](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485030&idx=1&sn=8383a7306381f36781957b807fa93961&scene=21#wechat_redirect) + +10、[一篇文章让你的 MacBook 进入超神状态](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484906&idx=1&sn=b2c3c969e53beae53aa7be7959227b5b&scene=21#wechat_redirect) + +11、[情人节来了,教你个用 Python 表白的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485701&idx=1&sn=ef7f0b83e60f397c1839259b275575bf&chksm=e88669e7dff1e0f1317ec429cf9e70442bb3a08ec46efe9a0d06f4ec9847574ea30b53904670&token=1148998814&lang=zh_CN#rd) + +## 07. 实用工具 + +### 7.1 Linux + +1、[值得收藏的 14 个 Linux 下 CPU 监控工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485356&idx=1&sn=9dc2f440299ad179e1874febdffa04d6&scene=21#wechat_redirect) + +2、[19 个没什么用,但是”特好玩“的命令](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485142&idx=2&sn=97b0f02f8f0153b1c7ba4899359d55e4&scene=21#wechat_redirect) + +3、[相见恨晚的15个 Linux 神器,你可能一个都没见过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484887&idx=1&sn=3473df33893a23b4de1e176985a5265e&scene=21#wechat_redirect) + +### 7.2 Git + +1、[关于 Git 和 GitHub,你所不知道的十件事](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485232&idx=2&sn=c3314e79af9029bdd76ae187f7b4eb19&scene=21#wechat_redirect) + +2、[关于 Git 图谱,这一篇文章讲得很细。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485179&idx=2&sn=a9f0a8a669d70c0dccedf71a530d1d93&scene=21#wechat_redirect) + +3、[如何巧妙处理 Git 多平台换行符问题(LF or CRLF)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485333&idx=2&sn=4e8e95e5cb12d27ea6b510aaaf15b4c0&scene=21#wechat_redirect) + +4、[这 7 个高级技巧,不会还怎么玩GitHub](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484940&idx=1&sn=9ad168943b03dff09d53e5d73e13ed46&scene=21#wechat_redirect) + +5、[ 神器!这款VSCode插件能填满Github绿色格子](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485595&idx=1&sn=65df21ab0b4042953dd8257bea8b1848&chksm=e8866879dff1e16fff97584467f2f1728f9c2da007894ae062e9201d53b82a30054036f8e81e#rd) + +### 7.3 MySQL + +1、[写给程序员的 MySQL 面试高频 100 问](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485321&idx=1&sn=30432df24d4e7858a475192bcf233914&scene=21#wechat_redirect) + +### 7.4 Chrome + +1、[推荐 8个 超实用的 Chrome 插件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484925&idx=1&sn=110e4da78a20e8b5721f00bb36ba9d4c&scene=21#wechat_redirect) + +2、[没有这 42 款插件的Chrome是没有灵魂的](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485016&idx=1&sn=ba38963413ee2926f6f5e69b1427491d&scene=21#wechat_redirect) + +### 7.5 其他工具 + +1、[整理了 11 个好用的代码质量审核和管理工具](https://mp.weixin.qq.com/s/DH_TOA3Cr3y37yZ5F4bU5A) + +2、[11 个最佳的 Python 编译器和解释器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485682&idx=1&sn=31b1b68432ca1f1db3866fb0f670270b&chksm=e8866810dff1e106a21c63c1ee198069223047afc72482c1f14ee4f473211aca85273c57d529#rd) + +## 08. 代码优化 + +### 8.1 算法讲解 + +1、[策略模式:商场促销活动背后的代码哲学](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484937&idx=1&sn=61b81dddfb20d80060fee5f22ab28d5f&scene=21#wechat_redirect) + +2、[算法教程|八张图带你轻松理解经典排序算法(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485042&idx=1&sn=b9e8503905be5a960119c2893995fb6d&scene=21#wechat_redirect) + +3、[算法教程|八张图带你轻松理解经典排序算法(中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485041&idx=1&sn=12d05e0316c8866e3876c3ed484ced15&scene=21#wechat_redirect) + +4、[一次忘记密码引发的算法思考](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484981&idx=1&sn=96f49ab2225c61b45601b68521135dd6&scene=21#wechat_redirect) + +5、[程序员走楼梯都会思考的一道题](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484993&idx=1&sn=36cc1466ed73af2fcf4a8430368a62ee&scene=21#wechat_redirect) + +6、[以为是高性能神仙算法,一看源代码才发现...](https://mp.weixin.qq.com/s/L_OvCcpIFKBLckLBvit8UQ) + +7、[ 用Python手写十大经典排序算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485494&idx=1&sn=1580e9fc17086bc83e30b0fa4c850bd8&chksm=e88668d4dff1e1c27a26b31fda2f0e413be83519aa9dd0e3c8d3cfd08a2478eb2596ed7ba535#rd) + +### 8.32设计模式 + +1、[单例模式告诉你只能娶一个老婆](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484921&idx=1&sn=493d0a492277ecb223ddfff9de8720ff&scene=21#wechat_redirect) + +### 8.3 代码美化 + +1、[看了同事的代码,我忍不住写了这份代码指南](https://mp.weixin.qq.com/s/goqj1YVdKV171LLpjtXruQ) + +2、[怎样才能写好一个 Python 函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485416&idx=1&sn=85b14857e795a92985230ba3f51b73f6&chksm=e886670adff1ee1c7f4266d4e3b931d2e3d1de4dff30dc869eae2cd174da7f54080787f60a1b#rd) + +## 09. Python 冷知识 + +1、[Python那些不为人知的冷知识(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485031&idx=1&sn=e5ab670ce6485a50c4b7eeaee11e6f34&scene=21#wechat_redirect) + +2、[Python那些不为人知的冷知识(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485029&idx=1&sn=5db54b5777348e827c274cd932a15a15&scene=21#wechat_redirect) + +3、[Python那些不为人知的冷知识(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485009&idx=1&sn=92e268c20c6f01278e220e11397cc2f0&scene=21#wechat_redirect) + +4、[Python那些不为人知的冷知识(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485001&idx=1&sn=5a02f8c912518d15a0b96319cf2da7d1&scene=21#wechat_redirect) + +5、[Python那些不为人知的冷知识(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484997&idx=1&sn=5b8cf44b62550e1bd67284fb2b0780dc&scene=21#wechat_redirect) + +6、[Python那些不为人知的冷知识(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484990&idx=1&sn=bd28368eff832ba2b24a64f637a6d0c8&scene=21#wechat_redirect) + +7、[Python那些不为人知的冷知识(七)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect) + +## 10. 云计算 + +1、[一份面向初学者的云计算通识指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484908&idx=1&sn=3085e048da685440408e1bdc54feb4aa&scene=21#wechat_redirect) + +2、[ 如何探测虚拟环境是物理机、虚拟机还是容器?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485401&idx=2&sn=8bfb6e1a71e0777a4d5e55f64b55ace5&chksm=e886673bdff1ee2d8acce291e402cd2ae0c7f64e455fdeea158eb53e535ee79b2aadc449935e#rd) + +## 11. 职场相关 + +1、[一个专科生的 Python 转行之路](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485012&idx=1&sn=751796e63a30d84be01486619b5fb806&scene=21#wechat_redirect) + +2、[一个机械生的Python转行自述](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484979&idx=1&sn=6513e940bc7c0677dedf7efff80aa4c6&scene=21#wechat_redirect) + +3、[自学 Python后端开发 到什么程度可以找工作?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485010&idx=1&sn=e76d031f91633f9bfde9da36d8dcf996&scene=21#wechat_redirect) + +4、[工作不是游戏,写给编程新人的五点建议](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484984&idx=1&sn=0934415cc48004ae76345dcfb8e33b94&scene=21#wechat_redirect) + +5、[Python 从业十年是种什么体验?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485601&idx=1&sn=0dbebc02002ab01a8cd42265822ff81c&chksm=e8866843dff1e15509f2a9d2cd551b2360607cf88e73e5d3036388716ee57949d726dcea209c#rd) + + + +--- + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file From 1eddde05ec239cdd630fc76b05834135d10e0731 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 1 Mar 2020 22:42:48 +0800 Subject: [PATCH 013/147] update --- source/c01/c01_01.rst | 1 - source/c01/c01_04.rst | 1 - source/c01/c01_05.rst | 1 - source/c01/c01_06.rst | 1 - source/c01/c01_07.rst | 1 - source/c01/c01_08.rst | 1 - source/c01/c01_09.rst | 1 - source/c01/c01_10.rst | 1 - source/c01/c01_11.rst | 1 - source/c01/c01_12.rst | 1 - source/c01/c01_13.rst | 1 - source/c01/c01_14.rst | 1 - source/c01/c01_15.rst | 1 - source/c01/c01_16.rst | 1 - source/c01/c01_17.rst | 1 - source/c01/c01_20.rst | 1 - source/c01/c01_21.rst | 1 - source/c01/c01_22.rst | 1 - source/c01/c01_23.rst | 1 - source/c01/c01_24.rst | 1 - source/c01/c01_25.rst | 1 - source/c01/c01_26.rst | 1 - source/c01/c01_27.rst | 1 - source/c01/c01_29.rst | 1 - source/c01/c01_30.md | 12 +++------ source/c01/c01_30.rst | 1 - source/c01/c01_31.rst | 1 - source/c01/c01_32.rst | 1 - source/c01/c01_33.rst | 1 - source/c01/c01_34.rst | 1 - source/c01/c01_35.rst | 1 - source/c02/c02_01.rst | 1 - source/c02/c02_02.rst | 1 - source/c02/c02_03.rst | 1 - source/c02/c02_04.rst | 1 - source/c02/c02_05.rst | 1 - source/c02/c02_06.rst | 1 - source/c02/c02_07.rst | 1 - source/c02/c02_08.rst | 1 - source/c02/c02_09.rst | 1 - source/c02/c02_10.rst | 1 - source/c02/c02_11.rst | 1 - source/c02/c02_12.rst | 1 - source/c03/c03_01.rst | 1 - source/c03/c03_02.rst | 1 - source/c03/c03_03.rst | 1 - source/c03/c03_04.rst | 1 - source/c03/c03_05.rst | 1 - source/c03/c03_06.rst | 1 - source/c04/c04_01.rst | 1 - source/c04/c04_02.rst | 1 - source/c04/c04_03.rst | 1 - source/c04/c04_04.rst | 1 - source/c04/c04_05.rst | 1 - source/c04/c04_06.rst | 1 - source/c04/c04_07.rst | 1 - source/c04/c04_08.rst | 1 - source/c04/c04_09.rst | 1 - source/c04/c04_10.rst | 1 - source/c04/c04_11.rst | 1 - source/c04/c04_12.rst | 1 - source/c04/c04_13.rst | 1 - source/c04/c04_14.rst | 1 - source/c04/c04_15.rst | 1 - source/c04/c04_16.rst | 1 - source/c04/c04_17.rst | 1 - source/c04/c04_18.md | 57 +++++++++++++++++++++++++++++++++++++++++-- source/c04/c04_18.rst | 1 - source/c04/c04_19.rst | 1 - source/c04/c04_21.rst | 1 - source/c05/c05_01.rst | 1 - source/c05/c05_02.rst | 1 - source/c05/c05_03.rst | 1 - source/c06/c06_01.rst | 1 - source/c06/c06_02.rst | 1 - source/c06/c06_03.rst | 1 - source/c06/c06_04.rst | 1 - source/c06/c06_05.rst | 1 - source/c06/c06_06.rst | 1 - source/c07/c07_01.rst | 1 - source/c07/c07_02.rst | 1 - source/c07/c07_03.rst | 1 - source/c07/c07_04.rst | 1 - source/c07/c07_05.rst | 1 - source/c07/c07_06.rst | 1 - source/c07/c07_07.rst | 1 - source/c07/c07_08.rst | 1 - source/c07/c07_10.rst | 1 - source/c08/c08_01.rst | 1 - source/c08/c08_02.rst | 1 - source/c08/c08_03.rst | 1 - source/c08/c08_04.rst | 1 - source/c08/c08_05.rst | 1 - source/c08/c08_06.rst | 1 - source/c08/c08_07.rst | 1 - source/c08/c08_08.rst | 1 - source/c08/c08_09.rst | 1 - source/c08/c08_11.rst | 1 - source/c08/c08_12.rst | 1 - source/c08/c08_13.rst | 1 - source/c08/c08_14.rst | 1 - source/c09/c09_01.rst | 1 - source/c09/c09_02.rst | 1 - source/c09/c09_03.rst | 1 - source/c09/c09_04.rst | 1 - source/c09/c09_05.rst | 1 - source/c09/c09_06.rst | 1 - source/c09/c09_07.rst | 1 - source/c09/c09_08.rst | 1 - source/c09/c09_09.rst | 1 - source/c09/c09_10.rst | 1 - source/c09/c09_11.rst | 1 - source/c09/c09_12.rst | 1 - source/c09/c09_13.rst | 1 - source/c09/c09_14.rst | 1 - source/c09/c09_15.rst | 1 - source/c09/c09_16.rst | 1 - source/c09/c09_17.rst | 1 - source/c09/c09_18.rst | 1 - source/c09/c09_19.rst | 1 - source/c09/c09_20.rst | 1 - source/c09/c09_21.rst | 1 - source/c09/c09_23.rst | 1 - source/c09/c09_24.rst | 1 - source/c09/c09_25.rst | 1 - source/c10/c10_01.rst | 1 - 126 files changed, 59 insertions(+), 134 deletions(-) diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index fa95a2c..7723f26 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -317,7 +317,6 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165542.png .. |image1| image:: http://image.python-online.cn/20190511165551.png diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index dbce83e..15a5776 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -118,6 +118,5 @@ .. figure:: http://image.python-online.cn/20191117142849.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190404215330.png diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index eb5ba75..4ea0c74 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -180,4 +180,3 @@ locals() .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index 5e9ddf0..1b19712 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -107,4 +107,3 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index 1e05763..84e8c34 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -351,4 +351,3 @@ Pythonic .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 216a8b2..469a939 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -272,7 +272,6 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg .. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index a1ebebe..8af376a 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -76,4 +76,3 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 0cf3c8e..b59b203 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1313,7 +1313,6 @@ linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165650.png .. |image1| image:: http://image.python-online.cn/20190511165716.png diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index 8985569..ea0b985 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -347,4 +347,3 @@ start(),end(),end() .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index 025c3d3..ca10f7c 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -157,7 +157,6 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/02/598168fe2b016.png .. |image1| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index 0f4eae0..ebaba82 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -160,6 +160,5 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index d33d425..bf586e0 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -212,6 +212,5 @@ open)的上下文管理器。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190310172800.png diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index b2c3812..06608ea 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -81,4 +81,3 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst index c23d993..2d5f893 100644 --- a/source/c01/c01_16.rst +++ b/source/c01/c01_16.rst @@ -169,4 +169,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index ef1d764..62d446c 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -580,7 +580,6 @@ super 的实现原理,就交由你来自己完成。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190425221322.png .. |image1| image:: http://image.python-online.cn/20190425221322.png diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index 2e15b18..5d0c622 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -108,7 +108,6 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190630111243.png .. |image1| image:: http://image.python-online.cn/20190630104956.png diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index f9b7698..add2b32 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -92,4 +92,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index b7219d3..cdb487c 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -200,6 +200,5 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190831160317.png diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index c8ae423..9686174 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -121,4 +121,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index 677e639..0fb7906 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -796,6 +796,5 @@ sys.path)查找器 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191027192949.png diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index f95f27f..c96373d 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -108,4 +108,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index c782da4..2551678 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -285,4 +285,3 @@ getchar() & putchar() .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 8c9f704..a53f1af 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -686,7 +686,6 @@ Index)上,它是 Python .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191218202833.png .. |image1| image:: http://image.python-online.cn/20191218203005.png diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index e80932e..2037618 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -8,4 +8,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md index 4f70fed..b72e802 100644 --- a/source/c01/c01_30.md +++ b/source/c01/c01_30.md @@ -1,14 +1,10 @@ # 1.30 学习编程的几大网站 -[书栈网](https://www.bookstack.cn/rank?tab=popular) + [书栈网](https://www.bookstack.cn/rank?tab=popular) -![](http://image.python-online.cn/20200104144109.png) + ![](http://image.python-online.cn/20200104144109.png) + [魔法学院](http://www.nowamagic.net/academy/) - - - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file + ![](http://image.python-online.cn/20200112210558.png) \ No newline at end of file diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index d43e4b5..6f543b7 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -8,6 +8,5 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200104144109.png diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index 472e323..9c28a3a 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -20,4 +20,3 @@ RGB .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst index 127c5f2..90c8cf2 100644 --- a/source/c01/c01_32.rst +++ b/source/c01/c01_32.rst @@ -34,4 +34,3 @@ requests.get(“https://www.baidu.com”) .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_33.rst b/source/c01/c01_33.rst index 32d7caa..3f7438a 100644 --- a/source/c01/c01_33.rst +++ b/source/c01/c01_33.rst @@ -47,4 +47,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index f37de8a..bbc7a41 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -91,6 +91,5 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200227201644.png diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index 1d514e4..87a4950 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -356,7 +356,6 @@ Windows,这里就有一件好事,一件坏事了,。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200228085749.png .. |image1| image:: http://image.python-online.cn/20200228093627.png diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index 3866bfc..1b76386 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -251,7 +251,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png .. |image1| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index 47124aa..7de913b 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -147,4 +147,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index d08d59d..a9af368 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -357,4 +357,3 @@ CPython,所以也就默许了Python具有GIL锁这个事。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 97a7a07..9fe29cd 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -290,4 +290,3 @@ Condition和Event 是类似的,并没有多大区别。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 070e3ad..6d2d6f7 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -262,4 +262,3 @@ Out),就是先进入队列的消息,将优先被消费。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index c7245f6..8a700ec 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -112,4 +112,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index 9e40938..d2da108 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -393,7 +393,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190527123516.png .. |image1| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index 03a414c..c599978 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -362,4 +362,3 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 2a6a770..4ac564e 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -247,7 +247,6 @@ emmm,和上面的结果是一样的。nice .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |普通函数中 不能使用 await| image:: https://i.loli.net/2018/05/26/5b09794f45340.png .. |async 中 不能使用yield| image:: https://i.loli.net/2018/05/26/5b0978b646230.png diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index a19e62f..2e62621 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -510,4 +510,3 @@ asyncio.gather .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 0d21b1f..1c74b14 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -219,7 +219,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/06/03/5b13ba8525bcf.png .. |image1| image:: https://i.loli.net/2018/06/03/5b13ba9f66baa.png diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index 8e60122..d082432 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -111,4 +111,3 @@ yield .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 3937210..e834dc5 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -776,7 +776,6 @@ property .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190811100737.png .. |image1| image:: http://image.python-online.cn/20190512113917.png diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index 687fe14..01ab177 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -377,4 +377,3 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index 08282a4..4902864 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -295,7 +295,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png .. |image1| image:: https://i.loli.net/2018/04/30/5ae6c31b2d1c8.png diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index 46242de..e74161d 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -513,7 +513,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/20/599982f513b7e.png .. |image1| image:: https://i.loli.net/2017/08/20/59998b33265d9.png diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index 05e7397..d6b2eea 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -501,7 +501,6 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX .. |image1| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup diff --git a/source/c03/c03_06.rst b/source/c03/c03_06.rst index 518f7b2..00aff67 100644 --- a/source/c03/c03_06.rst +++ b/source/c03/c03_06.rst @@ -885,7 +885,6 @@ response 进行初步封装。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190607131728.png .. |image1| image:: http://image.python-online.cn/20190607191954.png diff --git a/source/c04/c04_01.rst b/source/c04/c04_01.rst index 0735a22..c72888f 100755 --- a/source/c04/c04_01.rst +++ b/source/c04/c04_01.rst @@ -268,7 +268,6 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200209161935.png .. |image1| image:: https://i.loli.net/2018/06/11/5b1e7d36ce8ad.png diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index ee78c1d..c7698e6 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -169,7 +169,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511162815.png .. |image1| image:: http://image.python-online.cn/20190511162524.png diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 181be30..8611389 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -481,7 +481,6 @@ Python 3.x ,所以这里的代码也要对应修改。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511160523.png .. |image1| image:: http://image.python-online.cn/20190511161056.png diff --git a/source/c04/c04_04.rst b/source/c04/c04_04.rst index ad8b027..a120492 100755 --- a/source/c04/c04_04.rst +++ b/source/c04/c04_04.rst @@ -196,7 +196,6 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511163102.png .. |image1| image:: http://image.python-online.cn/20190511163123.png diff --git a/source/c04/c04_05.rst b/source/c04/c04_05.rst index 087ad7d..37e61de 100755 --- a/source/c04/c04_05.rst +++ b/source/c04/c04_05.rst @@ -160,7 +160,6 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511163441.png .. |image1| image:: http://image.python-online.cn/20190511163457.png diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 0b7cd4b..aeb0479 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -540,7 +540,6 @@ Desktop真心觉得不好用)。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191217150942.png .. |image1| image:: http://image.python-online.cn/20191231165152.png diff --git a/source/c04/c04_07.rst b/source/c04/c04_07.rst index fdc7942..1b93054 100755 --- a/source/c04/c04_07.rst +++ b/source/c04/c04_07.rst @@ -824,7 +824,6 @@ bash窗口,不然会提示找不到npm命令) .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-9-9/50205036.jpg .. |image1| image:: http://image.python-online.cn/17-9-9/5214564.jpg diff --git a/source/c04/c04_08.rst b/source/c04/c04_08.rst index 4b3406b..2e68037 100755 --- a/source/c04/c04_08.rst +++ b/source/c04/c04_08.rst @@ -432,4 +432,3 @@ URL相关 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c04/c04_09.rst b/source/c04/c04_09.rst index cf7ecee..4468846 100755 --- a/source/c04/c04_09.rst +++ b/source/c04/c04_09.rst @@ -713,7 +713,6 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/25/599feabef31a6.png .. |image1| image:: https://i.loli.net/2017/08/25/59a0345577dc0.png diff --git a/source/c04/c04_10.rst b/source/c04/c04_10.rst index 07d2623..bfcd002 100755 --- a/source/c04/c04_10.rst +++ b/source/c04/c04_10.rst @@ -539,7 +539,6 @@ limit子句:限制返回数据的量。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://ooo.0o0.ooo/2017/08/26/59a1429722061.png .. |image1| image:: https://i.loli.net/2017/08/27/59a2307e7b6bc.png diff --git a/source/c04/c04_11.rst b/source/c04/c04_11.rst index bb83bd9..a04587f 100644 --- a/source/c04/c04_11.rst +++ b/source/c04/c04_11.rst @@ -176,7 +176,6 @@ Host .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190113104817.png .. |image1| image:: http://image.python-online.cn/20190113105512.png diff --git a/source/c04/c04_12.rst b/source/c04/c04_12.rst index dfc4132..dae9b24 100644 --- a/source/c04/c04_12.rst +++ b/source/c04/c04_12.rst @@ -155,7 +155,6 @@ source 列出给定对象的源码 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190118000111.png .. |image1| image:: http://image.python-online.cn/20190118000234.png diff --git a/source/c04/c04_13.rst b/source/c04/c04_13.rst index 7e362a4..96b8d55 100644 --- a/source/c04/c04_13.rst +++ b/source/c04/c04_13.rst @@ -402,4 +402,3 @@ arguments)。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 6f8c636..921ab74 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -171,7 +171,6 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn .. |image1| image:: http://image.python-online.cn/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index 385cee9..e56c923 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -1066,7 +1066,6 @@ Debug ,而远程调试需要不少前置步骤。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190323164120.png .. |image1| image:: http://image.python-online.cn/20190323211635.png diff --git a/source/c04/c04_16.rst b/source/c04/c04_16.rst index f9c6823..7ecfb5d 100644 --- a/source/c04/c04_16.rst +++ b/source/c04/c04_16.rst @@ -75,4 +75,3 @@ Python:如何在被调用方法中获取调用者的方法名? .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c04/c04_17.rst b/source/c04/c04_17.rst index e10b6af..1c66017 100644 --- a/source/c04/c04_17.rst +++ b/source/c04/c04_17.rst @@ -475,7 +475,6 @@ Order .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190414144511.png .. |image1| image:: http://image.python-online.cn/20190512113846.png diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 19db00c..dc3e79e 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -41,6 +41,28 @@ command + down # 文件夹前进 # 最大化窗口与取消 command + ctrl + f + +# 关闭访达 +command + w + +# 关闭除访达外的其他程序 +command + q + +# 在不同的程序间切换 +command + tab +command + shift + tab + +# 在一个程序的不同窗口切换 +command + ~ + +# 强制退出程序 +command + option + esc + +# 删除光标处到行首 +command + backspace(删除键) + +# 新建窗口打开页面 +command + 鼠标左键 ``` @@ -79,7 +101,6 @@ finder的显示 风扇转得快了,散热自然也快了,但是风扇声音也更大了。建议只在非常烫(超过60℃?)的时候开启。 - ## 4.18.5 软件推荐 @@ -149,6 +170,38 @@ GoodSync:和 windows 平台同步文件 +## 4.18.6 brew 的使用 + +设置国内源 + +```shell +git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git + +git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git + +git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git + +brew update +``` + +如果要还原 + +```shell +git -C "$(brew --repo)" remote set-url origin https://github.com/Homebrew/brew.git + +git -C "$(brew --repo homebrew/core)" remote set-url origin https://github.com/Homebrew/homebrew-core.git + +git -C "$(brew --repo homebrew/cask)" remote set-url origin https://github.com/Homebrew/homebrew-cask.git + +brew update +``` + +安装docker + +```shell +brew cask install docker +``` + @@ -161,4 +214,4 @@ GoodSync:和 windows 平台同步文件 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 06fde2b..5d8b6bc 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -173,7 +173,6 @@ GoodSync:和 windows 平台同步文件 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190810161513.png .. |image1| image:: http://image.python-online.cn/20190810162315.png diff --git a/source/c04/c04_19.rst b/source/c04/c04_19.rst index 66496d6..3fb748b 100644 --- a/source/c04/c04_19.rst +++ b/source/c04/c04_19.rst @@ -722,4 +722,3 @@ n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index 2400390..bd1ed1c 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -371,6 +371,5 @@ pip 的文件夹,若没有则创建之。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191105200041.png diff --git a/source/c05/c05_01.rst b/source/c05/c05_01.rst index af941e9..2a0b221 100755 --- a/source/c05/c05_01.rst +++ b/source/c05/c05_01.rst @@ -422,7 +422,6 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |快速排序| image:: http://image.python-online.cn/Fpj4DFN_YCtfmJwb_85QnsuIVLqk .. |\|冒泡排序\|| image:: http://image.python-online.cn/FvbrVECeq58hY8TptG4ilkL5Owcc diff --git a/source/c05/c05_02.rst b/source/c05/c05_02.rst index 0969487..9293bd8 100755 --- a/source/c05/c05_02.rst +++ b/source/c05/c05_02.rst @@ -123,4 +123,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c05/c05_03.rst b/source/c05/c05_03.rst index 67c6a40..5e7f284 100755 --- a/source/c05/c05_03.rst +++ b/source/c05/c05_03.rst @@ -162,6 +162,5 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190112181126.png diff --git a/source/c06/c06_01.rst b/source/c06/c06_01.rst index a00c794..bd0b6a0 100755 --- a/source/c06/c06_01.rst +++ b/source/c06/c06_01.rst @@ -134,7 +134,6 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png .. |image1| image:: http://image.python-online.cn/20190511164650.png diff --git a/source/c06/c06_02.rst b/source/c06/c06_02.rst index 2478c2d..0eccf8e 100755 --- a/source/c06/c06_02.rst +++ b/source/c06/c06_02.rst @@ -312,7 +312,6 @@ show image |image9| .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511164738.png .. |image1| image:: http://image.python-online.cn/20190511164753.png diff --git a/source/c06/c06_03.rst b/source/c06/c06_03.rst index 5940979..afcbe6e 100755 --- a/source/c06/c06_03.rst +++ b/source/c06/c06_03.rst @@ -208,7 +208,6 @@ show image |image4| .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511164936.png .. |image1| image:: http://image.python-online.cn/20190511164949.png diff --git a/source/c06/c06_04.rst b/source/c06/c06_04.rst index cee716c..02df323 100755 --- a/source/c06/c06_04.rst +++ b/source/c06/c06_04.rst @@ -202,7 +202,6 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165103.png .. |image1| image:: http://image.python-online.cn/20190511165132.png diff --git a/source/c06/c06_05.rst b/source/c06/c06_05.rst index c78fcd0..dcacbb5 100755 --- a/source/c06/c06_05.rst +++ b/source/c06/c06_05.rst @@ -144,6 +144,5 @@ matplotlib 给我们提供了一个函数,\ ``animation.FuncAnimation`` .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/12/25/5c2226078799b.gif diff --git a/source/c06/c06_06.rst b/source/c06/c06_06.rst index 4625f5d..8d71f39 100755 --- a/source/c06/c06_06.rst +++ b/source/c06/c06_06.rst @@ -140,6 +140,5 @@ Jupyter NoteBook 里观察整个变化的过程。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165315.png diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index 7ee3d15..c7436c2 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -1911,7 +1911,6 @@ url** 即可。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-9-20/47469030.jpg .. |image1| image:: http://image.python-online.cn/20190705182629.png diff --git a/source/c07/c07_02.rst b/source/c07/c07_02.rst index fe58352..6e1a4ca 100755 --- a/source/c07/c07_02.rst +++ b/source/c07/c07_02.rst @@ -696,7 +696,6 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190404193811.png .. |image1| image:: http://image.python-online.cn/20190404194416.png diff --git a/source/c07/c07_03.rst b/source/c07/c07_03.rst index b7b3ea5..914eade 100755 --- a/source/c07/c07_03.rst +++ b/source/c07/c07_03.rst @@ -290,7 +290,6 @@ namespace 有下面六种 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-12-23/44035514.jpg .. |image1| image:: http://image.python-online.cn/17-12-23/20133481.jpg diff --git a/source/c07/c07_04.rst b/source/c07/c07_04.rst index e61f4f4..a43748e 100755 --- a/source/c07/c07_04.rst +++ b/source/c07/c07_04.rst @@ -361,7 +361,6 @@ CMD:两个功能 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-12-23/49304868.jpg .. |image1| image:: http://image.python-online.cn/17-12-23/36753853.jpg diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index 7e7ce6b..d192c39 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -320,7 +320,6 @@ Socket发些“奇怪”的数据。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/18-1-28/92519416.jpg .. |image1| image:: http://image.python-online.cn/18-1-28/37395940.jpg diff --git a/source/c07/c07_06.rst b/source/c07/c07_06.rst index f3d3b24..b9af741 100755 --- a/source/c07/c07_06.rst +++ b/source/c07/c07_06.rst @@ -329,7 +329,6 @@ centos的配置文件路径如下,ubuntu的有所不同 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png .. |image1| image:: https://i.loli.net/2018/01/03/5a4ce6eeaff7d.png diff --git a/source/c07/c07_07.rst b/source/c07/c07_07.rst index 1ea190e..4b2d451 100755 --- a/source/c07/c07_07.rst +++ b/source/c07/c07_07.rst @@ -419,4 +419,3 @@ salt命令格式 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c07/c07_08.rst b/source/c07/c07_08.rst index 9e43062..8dd89ef 100644 --- a/source/c07/c07_08.rst +++ b/source/c07/c07_08.rst @@ -176,4 +176,3 @@ chk_zabbix.sh .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index d3f93f8..e7adffb 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -187,7 +187,6 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190716111523.png .. |image1| image:: http://image.python-online.cn/20190716112113.png diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index 8c9288a..b31142b 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -447,4 +447,3 @@ aggregate管理 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index 6565fb4..75d62a9 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -306,7 +306,6 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/01/19/5a61bfa0ca66f.png .. |image1| image:: https://i.loli.net/2018/01/19/5a61bfd243111.png diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index c632bf6..839feb6 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -531,7 +531,6 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png .. |image1| image:: https://i.loli.net/2018/01/27/5a6c34b14c6ec.png diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index 7fdfff8..ee4b469 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -486,7 +486,6 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190714161353.png .. |image1| image:: http://image.python-online.cn/20190716004341.png diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index 4a31122..ad2ddca 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -733,7 +733,6 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190526144846.png .. |image1| image:: http://image.python-online.cn/20190529135942.png diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index ea088e2..09e548e 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -708,7 +708,6 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190430204707.png .. |image1| image:: http://image.python-online.cn/20190430204933.png diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index c8b79e3..0306dda 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -151,7 +151,6 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190419144135.png .. |image1| image:: http://image.python-online.cn/20190419144044.png diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index ec0a913..155d3dd 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -261,7 +261,6 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190514202013.png .. |image1| image:: http://image.python-online.cn/20190514202442.png diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index 62dc54d..aa4ae3c 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -785,7 +785,6 @@ rpc server 和rpc client 的四个重要方法 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190623185008.png .. |image1| image:: http://image.python-online.cn/20190623165341.png diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index db9026a..c2d8079 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -48,6 +48,5 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190530175817.png diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index af2a8c9..a99adc8 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -89,7 +89,6 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190424212211.png .. |image1| image:: http://image.python-online.cn/20190424213430.png diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index 6136ab0..d1780f0 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -315,7 +315,6 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190706114314.png .. |image1| image:: http://image.python-online.cn/20190706093904.png diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index 9a6cdc2..508e778 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -326,7 +326,6 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190716175250.png .. |image1| image:: http://image.python-online.cn/20190716180655.png diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 7358ab7..cf98dc8 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -325,7 +325,6 @@ auto,必须为小写,不能为为true或false,也不能为1或0。这里 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200102220841.png .. |image1| image:: http://image.python-online.cn/20200102221555.png diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index 3f2025a..1236e23 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -205,4 +205,3 @@ float64,但是很多情况下,我们并不需要这么高的精度(占用 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index 8fb1b94..bf8661d 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -230,6 +230,5 @@ https://www.zhihu.com/question/26022206 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200120204329.png diff --git a/source/c09/c09_04.rst b/source/c09/c09_04.rst index 269e2e4..dc0ab25 100644 --- a/source/c09/c09_04.rst +++ b/source/c09/c09_04.rst @@ -241,4 +241,3 @@ byte数组 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_05.rst b/source/c09/c09_05.rst index 60d8531..a92e64e 100644 --- a/source/c09/c09_05.rst +++ b/source/c09/c09_05.rst @@ -230,4 +230,3 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_06.rst b/source/c09/c09_06.rst index a81ab2d..ff2628d 100644 --- a/source/c09/c09_06.rst +++ b/source/c09/c09_06.rst @@ -289,6 +289,5 @@ Go 中确实不如 Python 那样灵活,bool 与 int .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200106201856.png diff --git a/source/c09/c09_07.rst b/source/c09/c09_07.rst index 656a812..8dbb3b5 100644 --- a/source/c09/c09_07.rst +++ b/source/c09/c09_07.rst @@ -218,4 +218,3 @@ ptr(pointer的简写),而这个变量,我们称之为指针变量。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_08.rst b/source/c09/c09_08.rst index 53454fc..5dfab91 100644 --- a/source/c09/c09_08.rst +++ b/source/c09/c09_08.rst @@ -266,4 +266,3 @@ staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 compan .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_09.rst b/source/c09/c09_09.rst index 0185047..236461c 100644 --- a/source/c09/c09_09.rst +++ b/source/c09/c09_09.rst @@ -268,4 +268,3 @@ Go语言中的函数,在你定义的时候,就规定了此函数 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_10.rst b/source/c09/c09_10.rst index 1fdfbd6..7867368 100644 --- a/source/c09/c09_10.rst +++ b/source/c09/c09_10.rst @@ -124,4 +124,3 @@ if - else if - else .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_11.rst b/source/c09/c09_11.rst index 368d5b8..06582f9 100644 --- a/source/c09/c09_11.rst +++ b/source/c09/c09_11.rst @@ -256,4 +256,3 @@ default 的代码块。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_12.rst b/source/c09/c09_12.rst index da6d6f0..580d72e 100644 --- a/source/c09/c09_12.rst +++ b/source/c09/c09_12.rst @@ -172,4 +172,3 @@ range 后可接数组、切片,字符串等 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_13.rst b/source/c09/c09_13.rst index f2aeb16..ba79b5f 100644 --- a/source/c09/c09_13.rst +++ b/source/c09/c09_13.rst @@ -174,4 +174,3 @@ goto语句与标签之间不能有变量声明,否则编译错误。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_14.rst b/source/c09/c09_14.rst index fcf950c..655fefc 100644 --- a/source/c09/c09_14.rst +++ b/source/c09/c09_14.rst @@ -236,4 +236,3 @@ return 那里执行不就好了。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_15.rst b/source/c09/c09_15.rst index 62763bc..96448de 100644 --- a/source/c09/c09_15.rst +++ b/source/c09/c09_15.rst @@ -240,4 +240,3 @@ typing)的定义是,只要你长得像鸭子,叫起来也像鸭子,那 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_16.rst b/source/c09/c09_16.rst index a9648b2..3a9ced0 100644 --- a/source/c09/c09_16.rst +++ b/source/c09/c09_16.rst @@ -99,4 +99,3 @@ make:只能为 slice,map,chan 分配内存,并初始化,返回的是 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_17.rst b/source/c09/c09_17.rst index 6bad9b3..0ac3487 100644 --- a/source/c09/c09_17.rst +++ b/source/c09/c09_17.rst @@ -156,4 +156,3 @@ name .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_18.rst b/source/c09/c09_18.rst index 4cba05d..ed511b7 100644 --- a/source/c09/c09_18.rst +++ b/source/c09/c09_18.rst @@ -144,4 +144,3 @@ main 的地位相当于主线程,当 main .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_19.rst b/source/c09/c09_19.rst index d0ae3d6..d1190d0 100644 --- a/source/c09/c09_19.rst +++ b/source/c09/c09_19.rst @@ -330,4 +330,3 @@ range关键字,在range时,要确保信道是处于关闭状态,否则循 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_20.rst b/source/c09/c09_20.rst index 16c33b1..8604447 100644 --- a/source/c09/c09_20.rst +++ b/source/c09/c09_20.rst @@ -177,4 +177,3 @@ range 信道已经关闭,无需等待就行。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_21.rst b/source/c09/c09_21.rst index 97c30a3..3e742c5 100644 --- a/source/c09/c09_21.rst +++ b/source/c09/c09_21.rst @@ -115,4 +115,3 @@ sync.WaitGroup。。 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_23.rst b/source/c09/c09_23.rst index dd57072..a3d6b28 100644 --- a/source/c09/c09_23.rst +++ b/source/c09/c09_23.rst @@ -185,4 +185,3 @@ https://studygolang.com/articles/13344 .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_24.rst b/source/c09/c09_24.rst index 63ba7c1..8201b83 100644 --- a/source/c09/c09_24.rst +++ b/source/c09/c09_24.rst @@ -180,4 +180,3 @@ MyBlog的入口应当为 myblog.go .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c09/c09_25.rst b/source/c09/c09_25.rst index f6363e4..5b4bad2 100644 --- a/source/c09/c09_25.rst +++ b/source/c09/c09_25.rst @@ -185,4 +185,3 @@ go mod 出现后, GOPATH 和 GOVENDOR .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst index 887ae98..166b7cb 100644 --- a/source/c10/c10_01.rst +++ b/source/c10/c10_01.rst @@ -153,7 +153,6 @@ .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! - 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200214104413.png .. |image1| image:: http://image.python-online.cn/save.jpeg From 4c16db17c213332fecfd7fdda9974d2db1fef7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Wed, 4 Mar 2020 20:23:02 +0800 Subject: [PATCH 014/147] update --- source/c08/c08_11.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/source/c08/c08_11.md b/source/c08/c08_11.md index 39c69a2..5401819 100644 --- a/source/c08/c08_11.md +++ b/source/c08/c08_11.md @@ -42,9 +42,41 @@ service libvirtd restart echo 1 > /sys/block/sdb/device/delete ``` +## 3. 修改 ConfigDrive +ConfigDrive 是一个 iso9660 格式的文件,只读。 +要对其进行修改,需要三步操作 +第一步: + +```shell +$ mount -t iso9660 /var/lib/nova/instances/6016c217-0bb1-4a88-a699-93435b64aa3a/disk.config /mnt/ + +# 拷贝到另一个文件夹中 +$ mkdir /tmp/mk_iso && cp -r /mnt/* /tmp/mk_iso + +# 然后在这个文件夹中对文件进行修改 +# 修改的时候,记得修改时间戳:https://tool.lu/timestamp/ +# 保存的时候记得使用 ":wq!" +``` + +第二步: + +```shell +# 先备份,防止有问题 +$ mv /var/lib/nova/instances/6016c217-0bb1-4a88-a699-93435b64aa3a/disk.config /tmp/disk.config.bak + +# 再生成覆盖 +$ genisoimage -o /var/lib/nova/instances/6016c217-0bb1-4a88-a699-93435b64aa3a/disk.config -ldots -allow-lowercase -allow-multidot -l -publisher "OpenStack Compute 2.2.7-20191225.el7.centos" -quiet -J -r -V config-2 /tmp/mk_iso/ +``` + +第三步: + +```shell +# 硬重启,重新生成 xml,如果有必要的话 +$ nova reboot --hard +``` From 715ecf8621c62c073d6b2b655da7b4a6e40b46b5 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 7 Mar 2020 22:52:11 +0800 Subject: [PATCH 015/147] =?UTF-8?q?add:=20=E6=9B=B4=E6=96=B0=E6=AF=8F?= =?UTF-8?q?=E6=97=A5=E4=B8=80=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_36.md | 167 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 source/c01/c01_36.md diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md new file mode 100644 index 0000000..4903907 --- /dev/null +++ b/source/c01/c01_36.md @@ -0,0 +1,167 @@ +# 1.36 每日一库:pretty_errors 解决bug 洁癖 + +当我们写的一个脚本或程序发生各种不可预知的异常,而我们也没有进行捕获处理的时候,通常都会致使程序崩溃退出,而且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 + +就像这样子,天呐,密集恐惧症要犯了都 + +![](http://image.python-online.cn/image-20200307210853246.png) + +上面这段 traceback 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了。 + +那有没有一种办法,可以解决这个问题呢? + +当然有了,在 Python 中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。 + +今天要介绍的这个库呢,叫做 `pretty-errors` ,从名字上就可以知道它的用途,是用来美化错误信息的。 + +通过这条命令你可以安装它 + +```shell +$ python3 -m pip install pretty-errors +``` + + + +## 1. 环境要求 + +由于使用了 `pretty-errors` 后,你的 traceback 信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 `pretty-error` 时,请确保你使用的终端可以输出带有颜色的字体。 + +在 windows 上你可以使用 Powershell + +在 Mac 上你可以使用 iTerm2 + + + +## 2. 效果对比 + +------ + +随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 + +![](http://image.python-online.cn/image-20200307212823345.png) + +而使用了 pretty_errors 后,报错信息被美化成这样了。 + +![image-20200307213534278](http://image.python-online.cn/image-20200307213534278.png) + +是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? + +当然这段代码少,你可能还没感受到,那就来看下 该项目在 Github上的一张效果对比图吧 + +![](https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67) + + + +## 3. 配置全局可用 + +可以看到使用了 pretty_errors 后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。 + +既然既然这样,那 pretty_errors 应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧? + +答案是显而易见的。 + +pretty_errors 和其他库不太一样,并不是开箱即用的,你在使用它之前需要做一下配置。 + +使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 traceback 输出都自动美化。 + +```shell +$ python3 -m pretty_errors +``` + +![](http://image.python-online.cn/image-20200307214742135.png) + +配置完成后,你再运行任何脚本,traceback 都会自动美化了。 + +不仅是在我的 iTerm 终端下 + +![](http://image.python-online.cn/image-20200307213534278.png) + +在 PyCharm 中也会 + +![](http://image.python-online.cn/image-20200307215530270.png) + +唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 `文件路径` 直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 下面自定义配置的方案解决这个问题(下面会讲到,参数是:`display_link`)。 + +![](http://image.python-online.cn/image-20200307215834623.png) + +因此,有些情况下,你并不想设置 `pretty_errors` 全局可用。 + +那怎么取消之前的配置呢? + +只需要再次输出 `python -m pretty_errors`,输出入 `C` 即可清除。 + +![](http://image.python-online.cn/image-20200307214600749.png) + + + +## 4. 单文件中使用 + +取消全局可用后,你可以根据自己需要,在你需要使用 `pretty-errors` 的脚本文件中导入` pretty_errors `,即可使用 + +```python +import pretty_errors +``` + +就像这样 + +```python +import pretty_errors + +def foo(): + 1/0 + +if __name__ == "__main__": + foo() +``` + + + +## 5. 自定义设置 + +若你对 pretty_errors 默认的配色及排版,并不满意,它也支持你对其进行自定义设置。 + +使用 `pretty_errors.configure()` 这个函数,就可以轻松对其进行一上些配置,比如这样: + +```python +import pretty_errors + +pretty_errors.configure( + line_length = 54, + filename_display = pretty_errors.FILENAME_FULL +) +``` + +`pretty_errors.configure()` 可以接收很多的参数,这里列出几个较为常用的。 + +- `line_length` + 此时将包装输出。如果这与控制台宽度匹配,则可能需要禁用`full_line_newline`,以防止出现明显的双换行符。 +- `full_line_newline` + 即使行已满,也插入硬换行符。如果控制台在此点自动插入自己的换行符,则禁用。 +- `filename_display` + 文件名的显示方式:可以是 `pretty_errors.FILENAME_COMPACT` 、`pretty_errors.FILENAME_EXTENDED`,或者`pretty_errors.FILENAME_FULL` +- `display_timestamp` + 启用时,时间戳将写入回溯头中。 +- `display_link` + 启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 +- `separator_character` + 用于创建标题行的字符。默认情况下使用连字符。 +- `header_color` + 转义序列设置标题颜色。 +- `timestamp_color` + 设置时间戳颜色的转义序列。 +- `default_color` + 设置默认颜色的转义序列。 +- `filename_color` + 设置文件名颜色的转义序列。 +- `line_number_color` + 转义序列设置行号颜色。 +- `function_color` + 设置函数颜色的转义序列。 +- `link_color` + 设置链接颜色的转义序列。 +- `reset_stdout` + 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。 + + + +以上,就是我对 pretty_errors 的使用体验,总的来说,这个库对于像我一样看习惯了默认的 traceback 格式来说,其实没有多大帮助,但对于某些想自定义错误输出场景的人,也许会是一个不错的解决方案。 \ No newline at end of file From dc6dca54812f58443d22fca5c0f2ef0a2ca47cb0 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 9 Mar 2020 12:43:28 +0800 Subject: [PATCH 016/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_36.md | 164 ++++++++++++++++++++++++++++++++----------- 1 file changed, 123 insertions(+), 41 deletions(-) diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md index 4903907..4757785 100644 --- a/source/c01/c01_36.md +++ b/source/c01/c01_36.md @@ -1,14 +1,17 @@ # 1.36 每日一库:pretty_errors 解决bug 洁癖 -当我们写的一个脚本或程序发生各种不可预知的异常,而我们也没有进行捕获处理的时候,通常都会致使程序崩溃退出,而且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 +当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 就像这样子,天呐,密集恐惧症要犯了都 ![](http://image.python-online.cn/image-20200307210853246.png) -上面这段 traceback 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了。 +上面这段 traceback -那有没有一种办法,可以解决这个问题呢? +- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了 +- 无法直接显示报错的代码,排查问题慢人一步,效率太低 + +那有没有一种办法,可以解决这些问题呢? 当然有了,在 Python 中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。 @@ -26,11 +29,9 @@ $ python3 -m pip install pretty-errors 由于使用了 `pretty-errors` 后,你的 traceback 信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 `pretty-error` 时,请确保你使用的终端可以输出带有颜色的字体。 -在 windows 上你可以使用 Powershell - -在 Mac 上你可以使用 iTerm2 - +在 windows 上你可以使用 Powershell,cmder 等 +在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2 ## 2. 效果对比 @@ -42,7 +43,7 @@ $ python3 -m pip install pretty-errors 而使用了 pretty_errors 后,报错信息被美化成这样了。 -![image-20200307213534278](http://image.python-online.cn/image-20200307213534278.png) +![](http://image.python-online.cn/image-20200307213534278.png) 是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? @@ -60,7 +61,7 @@ $ python3 -m pip install pretty-errors 答案是显而易见的。 -pretty_errors 和其他库不太一样,并不是开箱即用的,你在使用它之前需要做一下配置。 +pretty_errors 和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。 使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 traceback 输出都自动美化。 @@ -114,54 +115,135 @@ if __name__ == "__main__": foo() ``` +值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。 +因此,为了让美化更彻底,官方推荐你使用 `python -m pretty_errors` ## 5. 自定义设置 -若你对 pretty_errors 默认的配色及排版,并不满意,它也支持你对其进行自定义设置。 +上面的例子里,我们使用的都是 `pretty_errors` 的默认美化格式,展示的信息并没有那么全。 + +比如 + +- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。 +- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。 -使用 `pretty_errors.configure()` 这个函数,就可以轻松对其进行一上些配置,比如这样: +如果使用了 `pretty_errors` 导致异常信息有丢失,那还不如不使用 `pretty_errors` 呢。 + +不过,可以告诉你的是,`pretty_errors` 并没有你想象的那么简单。 + +它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示? + +这里举一个例子 ```python import pretty_errors +# 【重点】进行配置 pretty_errors.configure( - line_length = 54, - filename_display = pretty_errors.FILENAME_FULL + separator_character = '*', + filename_display = pretty_errors.FILENAME_EXTENDED, + line_number_first = True, + display_link = True, + lines_before = 5, + lines_after = 2, + line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, + code_color = ' ' + pretty_errors.default_config.line_color, ) + +# 原来的代码 +def foo(): + 1/0 + +if __name__ == "__main__": + foo() ``` -`pretty_errors.configure()` 可以接收很多的参数,这里列出几个较为常用的。 +在你像上面这样使用 `pretty_errrs.configure` 进行配置时,抛出的的异常信息就变成这样了。 + +![](http://image.python-online.cn/image-20200308121949011.png) + + + +当然了,`pretty_errors.configure()` 还可以接收很多的参数,你可以根据你自己的需要进行配置。 + +### 5.1 设置颜色 + +- `header_color`:设置标题行的颜色。 +- `timestamp_color`:设置时间戳颜色 +- `default_color`:设置默认的颜色 +- `filename_color`:设置文件名颜色 +- `line_number_color`:设置行号颜色。 +- `function_color`:设置函数颜色。 +- `link_color`:设置链接的颜色。 + +在设置颜色的时候,`pretty_errors` 提供了一些常用的 颜色常量供你直接调取。 + +- `BLACK`:黑色 +- `GREY`:灰色 +- `RED`:红色 +- `GREEN`:绿色 +- `YELLOW`:黄色 +- `BLUE`:蓝色 +- `MAGENTA`:品红色 +- `CYAN`:蓝绿色 +- `WHITE`:白色 + +而每一种颜色,都相应的匹配的 `BRIGHT_` 变体 和 `_BACKGROUND` 变体, + +其中,`_BACKGROUND` 用于设置背景色,举个例子如下。 + +![](http://image.python-online.cn/image-20200308125431779.png) + +### 5.2 设置显示内容 + +- `line_number_first` + 启用后,将首先显示行号,而不是文件名。 +- `lines_before` : 显示发生异常处的前几行代码 +- `lines_after`: 显示发生异常处的后几行代码 +- `display_link`:启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 +- `separator_character`:用于创建标题行的字符。默认情况下使用连字符。如果设置为 `''` 或者 `None` ,标题将被禁用。 +- `display_timestamp`:启用时,时间戳将写入回溯头中。 +- `display_locals` + 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。 + +- `display_trace_locals` + 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。 + +### 5.3 设置怎么显示 + +- `line_length`:设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用`full_line_newline`,以防止出现明显的双换行符。 + +- `full_line_newline`:当输出的字符满行时,是否要插入换行符。 + +- `timestamp_function` + 调用该函数以生成时间戳。默认值为`time.perf_counter`。 + +- `top_first` + 启用后,堆栈跟踪将反转,首先显示堆栈顶部。 + +- `display_arrow` + 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。 + +- `truncate_code` + 启用后,每行代码将被截断以适合行长。 + +- `stack_depth` + 要显示的堆栈跟踪的最大条目数。什么时候`0`将显示整个堆栈,这是默认值。 + +- `exception_above` + 启用后,异常将显示在堆栈跟踪上方。 + +- `exception_below`: + 启用后,异常显示在堆栈跟踪下方。 -- `line_length` - 此时将包装输出。如果这与控制台宽度匹配,则可能需要禁用`full_line_newline`,以防止出现明显的双换行符。 -- `full_line_newline` - 即使行已满,也插入硬换行符。如果控制台在此点自动插入自己的换行符,则禁用。 -- `filename_display` - 文件名的显示方式:可以是 `pretty_errors.FILENAME_COMPACT` 、`pretty_errors.FILENAME_EXTENDED`,或者`pretty_errors.FILENAME_FULL` -- `display_timestamp` - 启用时,时间戳将写入回溯头中。 -- `display_link` - 启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 -- `separator_character` - 用于创建标题行的字符。默认情况下使用连字符。 -- `header_color` - 转义序列设置标题颜色。 -- `timestamp_color` - 设置时间戳颜色的转义序列。 -- `default_color` - 设置默认颜色的转义序列。 -- `filename_color` - 设置文件名颜色的转义序列。 -- `line_number_color` - 转义序列设置行号颜色。 -- `function_color` - 设置函数颜色的转义序列。 -- `link_color` - 设置链接颜色的转义序列。 - `reset_stdout` 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。 + +- `filename_display` + + 设置文件名的展示方式,有三个选项: `pretty_errors.FILENAME_COMPACT` 、`pretty_errors.FILENAME_EXTENDED`,或者`pretty_errors.FILENAME_FULL` -以上,就是我对 pretty_errors 的使用体验,总的来说,这个库对于像我一样看习惯了默认的 traceback 格式来说,其实没有多大帮助,但对于某些想自定义错误输出场景的人,也许会是一个不错的解决方案。 \ No newline at end of file +以上,就是我对 `pretty_errors` 的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,`pretty_errors` 会是一个不错的解决方案,明哥把它推荐给你。 \ No newline at end of file From 0330b1ad809586652f82f67eb355fb27f82dc1ad Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 10 Mar 2020 21:22:19 +0800 Subject: [PATCH 017/147] =?UTF-8?q?update=20=E5=A4=A7=E9=87=8F=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +- c08_01.rst | 450 ++++++++++++++++++++++++++++++++++++++++++ md2rst.py | 28 +-- source/.DS_Store | Bin 10244 -> 10244 bytes source/c01/c01_01.rst | 1 + source/c01/c01_04.rst | 1 + source/c01/c01_08.rst | 1 + source/c01/c01_10.rst | 1 + source/c01/c01_12.rst | 1 + source/c01/c01_13.rst | 1 + source/c01/c01_14.rst | 1 + source/c01/c01_17.rst | 1 + source/c01/c01_18.rst | 1 + source/c01/c01_20.rst | 1 + source/c01/c01_22.rst | 1 + source/c01/c01_24.rst | 1 + source/c01/c01_27.rst | 1 + source/c01/c01_30.rst | 6 +- source/c01/c01_34.rst | 1 + source/c01/c01_35.rst | 1 + source/c01/c01_36.rst | 280 ++++++++++++++++++++++++++ source/c02/c02_01.rst | 1 + source/c02/c02_07.rst | 1 + source/c02/c02_08.rst | 2 +- source/c02/c02_09.rst | 1 + source/c02/c02_11.rst | 1 + source/c02/c02_12.rst | 1 - source/c03/c03_01.rst | 1 + source/c03/c03_03.rst | 1 + source/c03/c03_04.rst | 3 +- source/c03/c03_05.rst | 1 + source/c03/c03_06.rst | 1 + source/c04/c04_01.rst | 1 + source/c04/c04_02.rst | 1 + source/c04/c04_03.rst | 3 +- source/c04/c04_04.rst | 193 +++++++++++------- source/c04/c04_05.rst | 1 + source/c04/c04_06.rst | 1 + source/c04/c04_07.rst | 1 + source/c04/c04_08.rst | 1 - source/c04/c04_09.rst | 1 + source/c04/c04_10.rst | 3 +- source/c04/c04_11.rst | 1 + source/c04/c04_12.rst | 97 +++++---- source/c04/c04_14.rst | 1 + source/c04/c04_15.rst | 3 +- source/c04/c04_17.rst | 1 + source/c04/c04_18.md | 63 +++++- source/c04/c04_18.rst | 124 +++++++++++- source/c04/c04_21.rst | 1 + source/c04/c04_22.rst | 1 + source/c04/c04_23.rst | 1 + source/c05/c05_01.rst | 1 + source/c05/c05_03.rst | 1 + source/c06/c06_01.rst | 1 + source/c06/c06_02.rst | 1 + source/c06/c06_03.rst | 1 + source/c06/c06_04.rst | 1 + source/c06/c06_05.rst | 1 + source/c06/c06_06.rst | 1 + source/c07/c07_01.rst | 1 + source/c07/c07_02.rst | 3 +- source/c07/c07_03.rst | 1 + source/c07/c07_04.rst | 1 + source/c07/c07_05.rst | 1 + source/c07/c07_06.rst | 1 + source/c07/c07_10.rst | 1 + source/c07/c07_11.rst | 1 + source/c07/c07_12.rst | 1 + source/c07/c07_15.rst | 1 + source/c07/c07_16.rst | 1 + source/c08/c08_01.rst | 2 +- source/c08/c08_02.rst | 1 + source/c08/c08_03.rst | 1 + source/c08/c08_04.rst | 1 + source/c08/c08_05.rst | 1 + source/c08/c08_06.rst | 1 + source/c08/c08_07.rst | 1 + source/c08/c08_08.rst | 12 +- source/c08/c08_09.rst | 1 + source/c08/c08_10.rst | 1 + source/c08/c08_11.rst | 38 ++++ source/c08/c08_12.rst | 1 + source/c08/c08_13.rst | 1 + source/c08/c08_14.rst | 1 + source/c08/c08_15.rst | 1 + source/c09/c09_01.rst | 3 +- source/c09/c09_03.rst | 1 + source/c09/c09_06.rst | 1 + source/c09/c09_19.rst | 2 +- source/c09/c09_22.md | 214 ++++++++++++++++++++ source/c09/c09_22.rst | 228 +++++++++++++++++++++ source/c09/c09_24.md | 1 - source/c09/c09_24.rst | 241 ++++++++++------------ source/c09/c09_26.rst | 182 +++++++++++++++++ source/c10/c10_01.rst | 1 + 96 files changed, 1958 insertions(+), 300 deletions(-) create mode 100644 c08_01.rst create mode 100644 source/c01/c01_36.rst create mode 100644 source/c09/c09_22.md create mode 100644 source/c09/c09_22.rst create mode 100644 source/c09/c09_26.rst diff --git a/README.md b/README.md index 69e26d8..432ba5b 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ - 1.33 [如何调试已经运行中的程序](http://python-online.cn/zh_CN/latest/c01/c01_33.html) - 1.34 [每日一库:sh,最优雅的命令调用方式](http://python-online.cn/zh_CN/latest/c01/c01_34.html) - 1.35 [使用 Python 远程登陆服务器的利器](http://python-online.cn/zh_CN/latest/c01/c01_35.html) +- 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python-online.cn/zh_CN/latest/c01/c01_36.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python-online.cn/zh_CN/latest/c02/c02_01.html) @@ -76,7 +77,7 @@ - 4.15 [30个 PyCharm 实用技巧](http://python-online.cn/zh_CN/latest/c04/c04_15.html) - 4.16 [Python 开发技巧集合](http://python-online.cn/zh_CN/latest/c04/c04_16.html) - 4.17 [详解 23 种设计模式](http://python-online.cn/zh_CN/latest/c04/c04_17.html) -- 4.18 [Mac 高效工具](http://python-online.cn/zh_CN/latest/c04/c04_18.html) +- 4.18 [如何上手Mac ?](http://python-online.cn/zh_CN/latest/c04/c04_18.html) - 4.19 [程序员编码必学:Vim](http://python-online.cn/zh_CN/latest/c04/c04_19.html) - 4.20 [学会使用谷歌搜索引擎](http://python-online.cn/zh_CN/latest/c04/c04_20.html) - 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python-online.cn/zh_CN/latest/c04/c04_21.html) @@ -155,9 +156,11 @@ - 9.19 [学习 Go 协程:详解信道/通道](http://python-online.cn/zh_CN/latest/c09/c09_19.html) - 9.20 [几个信道死锁经典错误案例详解](http://python-online.cn/zh_CN/latest/c09/c09_20.html) - 9.21 [学习 Go 协程:WaitGroup](http://python-online.cn/zh_CN/latest/c09/c09_21.html) +- 9.22 [学习 Go 协程:互斥锁和读写锁](http://python-online.cn/zh_CN/latest/c09/c09_22.html) - 9.23 [学习一些常见的并发模型](http://python-online.cn/zh_CN/latest/c09/c09_23.html) -- 9.9 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_24.html) +- 9.24 [panic 和 recover](http://python-online.cn/zh_CN/latest/c09/c09_24.html) - 9.25 [Go语言的包依赖管理](http://python-online.cn/zh_CN/latest/c09/c09_25.html) +- 9.26 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_26.html) - 9.27 [go 命令详解](http://python-online.cn/zh_CN/latest/c09/c09_27.html) - 9.28 [函数式编程](http://python-online.cn/zh_CN/latest/c09/c09_28.html) diff --git a/c08_01.rst b/c08_01.rst new file mode 100644 index 0000000..cefde71 --- /dev/null +++ b/c08_01.rst @@ -0,0 +1,450 @@ +8.1 OpenStack 运维命令 +====================== + +-------------- + +一、OpenStack +------------- + +1.1 Nova +~~~~~~~~ + +虚拟机管理 +^^^^^^^^^^ + +.. code:: shell + + # 所有运行nova服务的信息 + $ nova service-list + + # 查看虚拟机的详细信息 + $ nova show + + # 查看集群节点(宿主机、组件服务)信息 + $ nova host-list + + # 查看当前计算节点,创建了哪些虚拟机 + $ ll /var/lib/nova/instances + + # List actions on a server + $ nova instance-action-list + + $ nova start # 开机 + $ nova stop # 关机 + $ nova delete # 删除 + + # 创建网段(未测试) + $ nova-manage network create --fixed_range_v4=10.0.1.0/24 --vlan=102 --project_id="tenantID" + + # 创建虚拟机 + $ nova boot \ + --flavor \ + --security-groups default \ + --nic net-id=,v4-fixed-ip= \ + --image \ + --config-drive True \ + --min-count 2 \ + --user-data [user-data-file-path] \ + [--num-instances 2] + [--snapshot ] + [--block-device-mapping vda=a9c7816b-0046-4aa9-9c82-cb881a969bdd:snapshot:300:delete-on-terminate --availability-zone nova:LX-OS-node13 --poll] + + + $ nova boot --flavor m1.tiny --image cirros --nic net-id=6df1fa59-25a3-4f8c-8d14-ae7f7828c1a2 greenboxes + + # 查看控制节点的资源信息 + $ nova host-describe + + # 关闭某节点服务 + $ nova service-disable bm-compute-01 nova-compute + $ nova service-enable bm-compute-01 nova-compute + + # 列出所有安全组 + $ nova secgroup-list + # 列出指定安全组的规则 + $ nova secgroup-list-rules + $ 增加安全组规则(开放ssh) + $ nova secgroup-add-rule default tcp <22> <22> <0.0.0.0/0> + + # 设置metadata + nova meta xxxxxx set ws:evacuate_interval=10 + +flavor管理 +^^^^^^^^^^ + +.. code:: shell + + # 新建flavor + $ nova flavor-create [--ephemeral ] [--swap ] [--rxtx-factor ] [--is-public ] + + + # 删除flavor + $ nova flavor-delete + + # 查看flavor信息 + $ nova flavor-show + + # 添加/修改flavor.extra_specs信息 + $ nova flavor-key set + + # 重置/删除flavor.extra_specs信息 + $ nova flavor-key unset + +aggregate管理 +^^^^^^^^^^^^^ + +.. code:: shell + + # 查看所有aggregate的列表,并不包含metadata + $ nova aggregate-list + + # 往aggregate添加host + $ nova aggregate-add-host + # 创建aggregage分组 + $ nova aggregate-create + + # 删除aggregate空分组,如果aggregate的host不为空,需要先使用aggregate-remove-host 清空host + $ nova aggregate-delete + # 在aggregate中移除主机 + $ nova aggregate-remove-host + + # 查看某aggregate分组的详细信息,逐渐丢弃,请使用aggregate-show + $ nova aggregate-details + $ nova aggregate-show + + # 往aggregate 里添加/更新 metadata 信息 + $ nova aggregate-set-metadata [ ...] + + # 更新aggregate的name或者availability-zone + $ nova aggregate-update [--name NAME] [--availability-zone ] + +1.2 Neutron +~~~~~~~~~~~ + +.. code:: shell + + # 重启neutron-service + $ service neutron-server restart + + # 启动linuxbridge服务 + $ service neutron-linuxbridge-agent restart + + # 创建网络 + $ neutron net-create --provider:physical_network phynet1 --provider:network_type flat private + + # 创建子网 全 + $neutron subnet-create --name public\ + --allocation-pool start=172.20.20.100,end=172.20.20.199 \ + --gateway 172.20.20.200 \ + --enable_dhcp=False \ + --dns-nameserver 114.114.114.114 \ + public 172.20.20.0/24 + + # 创建子网,更多选项可以查看 neutron subnet-create -h + neutron subnet-create --name 192.168.2.0/24 --allocation-pool start=192.168.2.200,end=192.168.2.230 --gateway 192.168.2.253 --dns-nameserver 114.114.114.114 --disable-dhcp private 192.168.2.0/24 + + # 查看网络 + $ neutron net-show + + # 查看子网 + $ neutron subnet-show + + # 查看网络列表 + $ neutron net-list + + # 查看端口占用情况 + $ neutron port-list + + # 指定 mac 创建port + neutron port-create --tenant-id 100001 --fixed-ip ip_address=192.168.0.22 --mac-address fa:16:3e:3a:e8:1b + + nova interface-attach b0cc47bc-25c3-48ca-a4fd-5523326b515a --port-id 8bcba4eb-ade0-403d-8f13-45ed70936f03 + + # 关闭port安全组 + neutron port-update --no-security-groups --port-security-enabled=False + +1.3 Glance +~~~~~~~~~~ + +.. code:: shell + + glance镜像存放:/var/lib/image + + # 官方地址 + https://docs.openstack.org/project-install-guide/baremetal/draft/configure-glance-images.html + + # 上传镜像 (具体看哪glance help image-create) + $ glance image-create --name centos6.5-old --visibility public --disk-format qcow2 --container-format bare --property ws:predownload=True --file /home/ + +1.4 keystone +~~~~~~~~~~~~ + +.. code:: shell + + # -------------------------------token-------------------------------- + # 生成token + $ openstack token issue + + + # -------------------------------domain-------------------------------- + # 创建domain + $ openstack domain create [--description "add new domain"] + + # 查看domain + $ openstack domain show + $ openstack domain list + + # 删除domain,删除前必须置为disable状态 + $ openstack domain set --disable + $ openstack domain delete + + # 更改domain属性:名字,描述,状态 + $ openstack domain set [-h] [--name ] [--description ] + [--enable | --disable] + + # -------------------------------project-------------------------------- + # 查看租户列表/信息 + $ openstack project list + $ openstack project show + + # 创建租户 + $ openstack project create [--domain ] [--description ] + + # 删除租户,可以无需指定domain,默认default + $ openstack project delete + $ openstack project delete [--domain ] [ ...] + + # 设置租户属性 + $ openstack project set [--name ] [--domain ] + [--description ] + [--enable | --disable] [--property ] + + + + # -------------------------------user-------------------------------- + + # 查看/删除/增加用户列表 + $ openstack user list + $ openstack user delete + $ openstack user create + + # 修改当前用户密码 + $ openstack user password set [--password ] [--original-password ] + + # 设置用户属性:租户,domain,名字,密码,远程密码?,Email,描述信息,是否可用 + openstack user set [--name ] [--project ] + [--project-domain ] + [--password ] [--password-prompt] + [--email ] + [--description ] [--enable | --disable] + + + + # 查看用户具体信息 + $ openstack user show + + + # -------------------------------role-------------------------------- + + # 查看角色列表 + $ openstack role list + + # 增加/删除/查看角色 + $ openstack role create + $ openstack role delete + $ openstack role show + + # 设置角色的属性:只有两个属性domain和name + $ openstack role set [--domain ] [--name ] + + # 查看角色-用户-租户的对应关系表 + $ openstack role assignment list + + # 增加/删除角色-用户-租户的对应关系表,具体查看帮助文件 + $ openstack role add -h + $ openstack role remove -h + +二、KVM/QEMU +------------ + +-------------- + +1.2 virsh命令 +~~~~~~~~~~~~~ + +.. code:: shell + + # 查看虚拟机的网卡信息 + $ virsh domiflist VM1 + + # kvm 添加硬盘 + qemu-img create -f qcow2 git-openstack.qcow2 100G + virsh attach-disk vdb --cache=none --subdriver=qcow2 + virsh detach-disk /data/test02_add01.qcow2 + + # 暂停/恢复 + virsh suspend + virsh resume + + # 开机自启 + virsh autostart + virsh list --autostart + + # 修改虚拟机密码,需要虚拟机内部安装 qga:qemu-guest-agent + virsh set-user-password instance-00000444 root "root12#$" + +热增加网卡:\ ``virsh attach-device ws_controller01 ./tmp.xml --persistent --live`` + +.. code:: xml + + + + + + + + + + +热去除带宽限速 + +.. code:: shell + + $ virsh domiftune ws_controller01 vnet4 --inbound 0,0,0 --outbound 0,0,0 --config --live + +压缩镜像 + +.. code:: shell + + # 压缩镜像 + virt-sparsify –compress ${ori_img_path} ${dest_img_path} + + # 解决 tmp 目录空间不足的情况,仅对当前终端有效 + mkdir /data/tmp + chmod 1777 /data/tmp/ + export TEMP=/data/tmp + export TMPDIR=/data/tmp + + # 解决 tmp 目录空间不足的情况,对所有终端有效 + echo 'export TEMP=/data/tmp' >> /etc/profile + echo 'export TMPDIR=/data/tmp' >> /etc/profile + source /etc/profile + +在线查看虚拟机的messages日志 + +.. code:: shell + + virt-log -d ws_controller01 + +1.2 LVM管理 +~~~~~~~~~~~ + +.. code:: shell + + # 查看计算节点VG信息 + $ vgdisplay + + # 查看虚拟机磁盘信息 + $ lvs + + # 删除LV + $ lvremove /dev/ssd-volume/* -y + + # 查看可用块设备列表 + $ lsblk + + # 将pv从vg移除 + $ vgreduce --removemissing --force hdd-volumes + + # 添加 pv 到 vg 中 + $ vgextend hdd-volumes /dev/sda + +1.3 QEMU命令 +~~~~~~~~~~~~ + +.. code:: shell + + # 查看img镜像信息 + $ qemu-img info + + # 创建qcow2文件 + $ qemu-img create -f qcow2 openstack-name.qcow2 100G + +三、集群相关 +------------ + +-------------- + +3.1 MariaDB +~~~~~~~~~~~ + +.. code:: shell + + # 查看MariaDB集群数量 + $ mysql -e 'show status like "wsrep_%"' -ppasswd|grep wsrep_cluster_size|awk '{ print $2 }' + + # 查看该节点MariaDB是否启动 + $ mariadbClusterCheck + +3.2 RabbitMQ +~~~~~~~~~~~~ + +先在一台节点启动 ``service rabbitmq-server restart`` + +启动后,会生成 ``/var/lib/rabbitmq/.erlang.cookie`` +文件,为了实现节点间的通信加密,需要将这个文件拷贝至其他两个节点。拷贝时,注意生意授予权限。 + +:: + + chown rabbitmq.rabbitmq /var/lib/rabbitmq/.erlang.cookie + +然后启动一下后面两台的服务。 + +:: + + service rabbitmq-server restart + +以上都准备好了,现在好开始构建集群了。 + +分别在后面两个节点执行如下操作。 + +:: + + rabbitmqctl stop_app + + rabbitmqctl join_cluster --ram rabbit@ws_controller01(ws_controller01为节点1的hostname) + + rabbitmqctl start_app + +执行完成后,可以查看一下集群状态。 + +:: + + rabbitmqctl cluster_status + +有了集群后,如果要(openstack)使用,还要创建一下用户 + +:: + + rabbitmqctl add_user openstack openstack12#$ + rabbitmqctl set_permissions -p / openstack ".*" ".*" ".*" + rabbitmqctl set_user_tags openstack administrator + +:: + + # 指定节点执行命令 + rabbitmq -n rabbit@ws_controller02 [command] + +3.3 pacemaker +------------- + +:: + + pkill -9 pacemaker;service pacemaker restart + +-------------- + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/md2rst.py b/md2rst.py index 7390c7b..a4cfc62 100644 --- a/md2rst.py +++ b/md2rst.py @@ -5,12 +5,25 @@ import platform from git import Repo -repo = Repo.init(path='.') + + +osName = platform.system() +repo_path ='.' +if (osName == 'Windows'): + repo_path = 'E:\\MING-Git\\06. PythonCodingTime' + blog_path = 'E:\\MING-Git\\06. PythonCodingTime\\source' + index_path = 'E:\\MING-Git\\06. PythonCodingTime\\README.md' +elif (osName == 'Darwin'): + repo_path = '/Users/MING/Github/PythonCodingTime/' + blog_path = '/Users/MING/Github/PythonCodingTime/source' + index_path = '/Users/MING/Github/PythonCodingTime/README.md' + + +repo = Repo.init(path=repo_path) if not repo.is_dirty(): # 没有文件变更 os._exit(0) -blog_path = '' base_link = "http://python-online.cn/zh_CN/latest/" readme_header = ''' 这是我的个人博客( [MING's BLOG](http://python-online.cn/) ),主要写关于Python的一些思考总结。 @@ -24,15 +37,6 @@ ''' -osName = platform.system() -if (osName == 'Windows'): - blog_path = 'E:\\MING-Git\\06. PythonCodingTime\\source' - index_path = 'E:\\MING-Git\\06. PythonCodingTime\\README.md' -elif (osName == 'Darwin'): - blog_path = '/Users/MING/Github/PythonCodingTime/source' - index_path = '/Users/MING/Github/PythonCodingTime/README.md' - - def get_file_info(filename): with open(filename, 'r', encoding="utf-8") as file: first_line = file.readline().replace("#", "").strip() @@ -72,7 +76,7 @@ def convert_md5_to_rst(file): md_file=filename+'.md', rst_file=filename+'.rst' ) # status, output = commands.getstatusoutput(convert_cmd) - status = subprocess.call(convert_cmd) + status = subprocess.call(convert_cmd.split(" ")) if status != 0: print("命令执行失败: " + convert_cmd) os._exit(1) diff --git a/source/.DS_Store b/source/.DS_Store index 7f4347c99eb716a02e45319e869c98de0ae01f1c..98eb4f449707a9751f69851f7d73cd9d3dfc08cb 100644 GIT binary patch delta 37 rcmZn(XbG6$&nUeyU^hRb^kyD`c(%=p#ZovYHdt(CS4aVIW-st&f delta 84 zcmZn(XbG6$&nUAoU^hRb%w`^ecs4;Eh9ri3Ak1dSWk_TwV#uCcFE7T#uyS*ZSTW~j Lc7+tYN(vYOmHQP2 diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 7723f26..46e9e6f 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -320,3 +320,4 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 .. |image0| image:: http://image.python-online.cn/20190511165542.png .. |image1| image:: http://image.python-online.cn/20190511165551.png + diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index 15a5776..88358aa 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -120,3 +120,4 @@ .. |image0| image:: http://image.python-online.cn/20190404215330.png + diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 469a939..4b9c6d7 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -279,3 +279,4 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 .. |image3| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg .. |image4| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg .. |image5| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg + diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index b59b203..187cf2e 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1318,3 +1318,4 @@ linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ .. |image1| image:: http://image.python-online.cn/20190511165716.png .. |image2| image:: http://image.python-online.cn/20190511165735.png .. |image3| image:: http://image.python-online.cn/20190915213412.png + diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index ca10f7c..77ac879 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -160,3 +160,4 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 .. |image0| image:: https://i.loli.net/2017/08/02/598168fe2b016.png .. |image1| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png + diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index ebaba82..dd30fc1 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -162,3 +162,4 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 .. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg + diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index bf586e0..3f807a9 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -214,3 +214,4 @@ open)的上下文管理器。 .. |image0| image:: http://image.python-online.cn/20190310172800.png + diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index 62d446c..26d6e71 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -585,3 +585,4 @@ super 的实现原理,就交由你来自己完成。 .. |image1| image:: http://image.python-online.cn/20190425221322.png .. |image2| image:: http://image.python-online.cn/20190425221233.png .. |image3| image:: http://image.python-online.cn/20190519001930.png + diff --git a/source/c01/c01_18.rst b/source/c01/c01_18.rst index a96250f..0ba5e6b 100644 --- a/source/c01/c01_18.rst +++ b/source/c01/c01_18.rst @@ -161,3 +161,4 @@ mysql,这时也请将其卸载再重新安装吧。 .. |image4| image:: http://image.python-online.cn/20190615001908.png .. |image5| image:: http://image.python-online.cn/20190615112422.png .. |image6| image:: http://image.python-online.cn/20190705225651.png + diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index 5d0c622..f2fc0c4 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -113,3 +113,4 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod .. |image1| image:: http://image.python-online.cn/20190630104956.png .. |image2| image:: http://image.python-online.cn/20190630105735.png .. |image3| image:: http://image.python-online.cn/20190630105600.png + diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index cdb487c..54b3838 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -202,3 +202,4 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip .. |image0| image:: http://image.python-online.cn/20190831160317.png + diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index 0fb7906..27ae8e6 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -798,3 +798,4 @@ sys.path)查找器 .. |image0| image:: http://image.python-online.cn/20191027192949.png + diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index a53f1af..74519f4 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -691,3 +691,4 @@ Index)上,它是 Python .. |image1| image:: http://image.python-online.cn/20191218203005.png .. |image2| image:: http://image.python-online.cn/20191218203255.png .. |image3| image:: http://image.python-online.cn/20191218203517.png + diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index 6f543b7..f1bc2a5 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -5,8 +5,10 @@ |image0| -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! +`魔法学院 `__ +|image1| .. |image0| image:: http://image.python-online.cn/20200104144109.png +.. |image1| image:: http://image.python-online.cn/20200112210558.png + diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index bbc7a41..bdbcb92 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -93,3 +93,4 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 .. |image0| image:: http://image.python-online.cn/20200227201644.png + diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index 87a4950..497848f 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -360,3 +360,4 @@ Windows,这里就有一件好事,一件坏事了,。 .. |image0| image:: http://image.python-online.cn/20200228085749.png .. |image1| image:: http://image.python-online.cn/20200228093627.png .. |image2| image:: http://image.python-online.cn/20200228111654.png + diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst new file mode 100644 index 0000000..cd790dc --- /dev/null +++ b/source/c01/c01_36.rst @@ -0,0 +1,280 @@ +1.36 每日一库:pretty_errors 解决bug 洁癖 +========================================= + +当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 +**密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 + +就像这样子,天呐,密集恐惧症要犯了都 + +|image0| + +上面这段 traceback + +- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了 +- 无法直接显示报错的代码,排查问题慢人一步,效率太低 + +那有没有一种办法,可以解决这些问题呢? + +当然有了,在 Python +中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。 + +今天要介绍的这个库呢,叫做 ``pretty-errors`` +,从名字上就可以知道它的用途,是用来美化错误信息的。 + +通过这条命令你可以安装它 + +.. code:: shell + + $ python3 -m pip install pretty-errors + +1. 环境要求 +----------- + +由于使用了 ``pretty-errors`` 后,你的 traceback +信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 +``pretty-error`` 时,请确保你使用的终端可以输出带有颜色的字体。 + +在 windows 上你可以使用 Powershell,cmder 等 + +在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2 + +2. 效果对比 +----------- + +-------------- + +随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 + +|image1| + +而使用了 pretty_errors 后,报错信息被美化成这样了。 + +|image2| + +是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? + +当然这段代码少,你可能还没感受到,那就来看下 该项目在 +Github上的一张效果对比图吧 + +|image3| + +3. 配置全局可用 +--------------- + +可以看到使用了 pretty_errors +后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。 + +既然既然这样,那 pretty_errors +应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧? + +答案是显而易见的。 + +pretty_errors +和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。 + +使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 +traceback 输出都自动美化。 + +.. code:: shell + + $ python3 -m pretty_errors + +|image4| + +配置完成后,你再运行任何脚本,traceback 都会自动美化了。 + +不仅是在我的 iTerm 终端下 + +|image5| + +在 PyCharm 中也会 + +|image6| + +唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 ``文件路径`` +直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 +下面自定义配置的方案解决这个问题(下面会讲到,参数是:\ ``display_link``\ )。 + +|image7| + +因此,有些情况下,你并不想设置 ``pretty_errors`` 全局可用。 + +那怎么取消之前的配置呢? + +只需要再次输出 ``python -m pretty_errors``\ ,输出入 ``C`` 即可清除。 + +|image8| + +4. 单文件中使用 +--------------- + +取消全局可用后,你可以根据自己需要,在你需要使用 ``pretty-errors`` +的脚本文件中导入\ ``pretty_errors``\ ,即可使用 + +.. code:: python + + import pretty_errors + +就像这样 + +.. code:: python + + import pretty_errors + + def foo(): + 1/0 + + if __name__ == "__main__": + foo() + +值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。 + +因此,为了让美化更彻底,官方推荐你使用 ``python -m pretty_errors`` + +5. 自定义设置 +------------- + +上面的例子里,我们使用的都是 ``pretty_errors`` +的默认美化格式,展示的信息并没有那么全。 + +比如 + +- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。 +- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。 + +如果使用了 ``pretty_errors`` 导致异常信息有丢失,那还不如不使用 +``pretty_errors`` 呢。 + +不过,可以告诉你的是,\ ``pretty_errors`` 并没有你想象的那么简单。 + +它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示? + +这里举一个例子 + +.. code:: python + + import pretty_errors + + # 【重点】进行配置 + pretty_errors.configure( + separator_character = '*', + filename_display = pretty_errors.FILENAME_EXTENDED, + line_number_first = True, + display_link = True, + lines_before = 5, + lines_after = 2, + line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, + code_color = ' ' + pretty_errors.default_config.line_color, + ) + + # 原来的代码 + def foo(): + 1/0 + + if __name__ == "__main__": + foo() + +在你像上面这样使用 ``pretty_errrs.configure`` +进行配置时,抛出的的异常信息就变成这样了。 + +|image9| + +当然了,\ ``pretty_errors.configure()`` +还可以接收很多的参数,你可以根据你自己的需要进行配置。 + +5.1 设置颜色 +~~~~~~~~~~~~ + +- ``header_color``\ :设置标题行的颜色。 +- ``timestamp_color``\ :设置时间戳颜色 +- ``default_color``\ :设置默认的颜色 +- ``filename_color``\ :设置文件名颜色 +- ``line_number_color``\ :设置行号颜色。 +- ``function_color``\ :设置函数颜色。 +- ``link_color``\ :设置链接的颜色。 + +在设置颜色的时候,\ ``pretty_errors`` 提供了一些常用的 +颜色常量供你直接调取。 + +- ``BLACK``\ :黑色 +- ``GREY``\ :灰色 +- ``RED``\ :红色 +- ``GREEN``\ :绿色 +- ``YELLOW``\ :黄色 +- ``BLUE``\ :蓝色 +- ``MAGENTA``\ :品红色 +- ``CYAN``\ :蓝绿色 +- ``WHITE``\ :白色 + +而每一种颜色,都相应的匹配的 ``BRIGHT_`` 变体 和 ``_BACKGROUND`` 变体, + +其中,\ ``_BACKGROUND`` 用于设置背景色,举个例子如下。 + +|image10| + +5.2 设置显示内容 +~~~~~~~~~~~~~~~~ + +- ``line_number_first`` 启用后,将首先显示行号,而不是文件名。 +- ``lines_before`` : 显示发生异常处的前几行代码 +- ``lines_after``\ : 显示发生异常处的后几行代码 +- ``display_link``\ :启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 +- ``separator_character``\ :用于创建标题行的字符。默认情况下使用连字符。如果设置为 + ``''`` 或者 ``None`` ,标题将被禁用。 +- ``display_timestamp``\ :启用时,时间戳将写入回溯头中。 +- ``display_locals`` + 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。 + +- ``display_trace_locals`` + 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。 + +5.3 设置怎么显示 +~~~~~~~~~~~~~~~~ + +- ``line_length``\ :设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用\ ``full_line_newline``\ ,以防止出现明显的双换行符。 + +- ``full_line_newline``\ :当输出的字符满行时,是否要插入换行符。 + +- ``timestamp_function`` + 调用该函数以生成时间戳。默认值为\ ``time.perf_counter``\ 。 + +- ``top_first`` 启用后,堆栈跟踪将反转,首先显示堆栈顶部。 + +- ``display_arrow`` + 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。 + +- ``truncate_code`` 启用后,每行代码将被截断以适合行长。 + +- ``stack_depth`` + 要显示的堆栈跟踪的最大条目数。什么时候\ ``0``\ 将显示整个堆栈,这是默认值。 + +- ``exception_above`` 启用后,异常将显示在堆栈跟踪上方。 + +- ``exception_below``\ : 启用后,异常显示在堆栈跟踪下方。 + +- ``reset_stdout`` + 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。 + +- ``filename_display`` + + 设置文件名的展示方式,有三个选项: ``pretty_errors.FILENAME_COMPACT`` + 、\ ``pretty_errors.FILENAME_EXTENDED``\ ,或者\ ``pretty_errors.FILENAME_FULL`` + +以上,就是我对 ``pretty_errors`` +的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 +PEP8 +规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,\ ``pretty_errors`` +会是一个不错的解决方案,明哥把它推荐给你。 + +.. |image0| image:: http://image.python-online.cn/image-20200307210853246.png +.. |image1| image:: http://image.python-online.cn/image-20200307212823345.png +.. |image2| image:: http://image.python-online.cn/image-20200307213534278.png +.. |image3| image:: https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67 +.. |image4| image:: http://image.python-online.cn/image-20200307214742135.png +.. |image5| image:: http://image.python-online.cn/image-20200307213534278.png +.. |image6| image:: http://image.python-online.cn/image-20200307215530270.png +.. |image7| image:: http://image.python-online.cn/image-20200307215834623.png +.. |image8| image:: http://image.python-online.cn/image-20200307214600749.png +.. |image9| image:: http://image.python-online.cn/image-20200308121949011.png +.. |image10| image:: http://image.python-online.cn/image-20200308125431779.png + diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index 1b76386..db907da 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -257,3 +257,4 @@ .. |image2| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg .. |image3| image:: http://image.python-online.cn/20190112205155.png .. |image4| image:: http://image.python-online.cn/20190112204930.png + diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index d2da108..03da235 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -396,3 +396,4 @@ .. |image0| image:: http://image.python-online.cn/20190527123516.png .. |image1| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png + diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index c599978..75c217c 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -3,7 +3,7 @@ -------------- -直到上一篇,我们终于迎来了Python并发编程中,最高级、最重要、当然也是最难的知识点–``协程``\ 。 +直到上一篇,我们终于迎来了Python并发编程中,最高级、最重要、当然也是最难的知识点–\ ``协程``\ 。 当你看到这一篇的时候,请确保你对生成器的知识,有一定的了解。当然不了解,也没有关系,你只要花个几分钟的时间,来看下我上一篇文章,就能够让你认识生成器,入门协程了。 diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 4ac564e..6743988 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -251,3 +251,4 @@ emmm,和上面的结果是一样的。nice .. |普通函数中 不能使用 await| image:: https://i.loli.net/2018/05/26/5b09794f45340.png .. |async 中 不能使用yield| image:: https://i.loli.net/2018/05/26/5b0978b646230.png .. |验证通过| image:: https://i.loli.net/2018/05/26/5b09814dc4714.png + diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 1c74b14..87ff018 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -224,3 +224,4 @@ .. |image1| image:: https://i.loli.net/2018/06/03/5b13ba9f66baa.png .. |image2| image:: https://i.loli.net/2018/06/03/5b13bab682a32.png .. |image3| image:: https://i.loli.net/2018/06/03/5b13bad79f5ce.png + diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index d082432..7d50871 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -90,7 +90,6 @@ yield **协程的优点:** - 线程属于系统级别调度,而协程是程序员级别的调度。使用协程避免了无意义的调度,减少了线程上下文切换的开销,由此可以提高性能。 - - 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。 - 无需原子操作锁定及同步的开销 diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index e834dc5..3a025a1 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -779,3 +779,4 @@ property .. |image0| image:: http://image.python-online.cn/20190811100737.png .. |image1| image:: http://image.python-online.cn/20190512113917.png + diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index 4902864..6d073ae 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -298,3 +298,4 @@ .. |image0| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png .. |image1| image:: https://i.loli.net/2018/04/30/5ae6c31b2d1c8.png + diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index e74161d..6ce07d3 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -53,7 +53,7 @@ pip install virtualenv pip install virtualenvwrapper -配置:``vim ~/.bashrc``\ ,编辑完成后,记得\ ``source ~/.bashrc``\ 使之生效 +配置:\ ``vim ~/.bashrc``\ ,编辑完成后,记得\ ``source ~/.bashrc``\ 使之生效 :: @@ -520,3 +520,4 @@ .. |image3| image:: https://i.loli.net/2017/08/20/59998bdf36c79.png .. |image4| image:: https://i.loli.net/2017/08/25/59a0236def497.png .. |image5| image:: https://i.loli.net/2017/08/25/59a0244d239f0.png + diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index d6b2eea..fac243a 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -505,3 +505,4 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 .. |image0| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX .. |image1| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup .. |image2| image:: http://image.python-online.cn/FimULzWaeZWS2KJx_EQLAK_yRZ4A + diff --git a/source/c03/c03_06.rst b/source/c03/c03_06.rst index 00aff67..8f8e32a 100644 --- a/source/c03/c03_06.rst +++ b/source/c03/c03_06.rst @@ -909,3 +909,4 @@ response 进行初步封装。 .. |image20| image:: http://image.python-online.cn/20190602220511.png .. |image21| image:: http://image.python-online.cn/20190602220700.png .. |image22| image:: http://image.python-online.cn/20190605203016.png + diff --git a/source/c04/c04_01.rst b/source/c04/c04_01.rst index c72888f..bf64ffc 100755 --- a/source/c04/c04_01.rst +++ b/source/c04/c04_01.rst @@ -274,3 +274,4 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html .. |image2| image:: https://i.loli.net/2018/06/11/5b1e7f140be6a.png .. |image3| image:: https://i.loli.net/2018/06/11/5b1e805c996c8.png .. |image4| image:: https://i.loli.net/2018/06/11/5b1e812db603f.png + diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index c7698e6..1ecac5b 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -175,3 +175,4 @@ .. |image2| image:: http://image.python-online.cn/20190511162607.png .. |image3| image:: http://image.python-online.cn/20190511162716.png .. |image4| image:: http://image.python-online.cn/20190511162730.png + diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 8611389..d1698e2 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -26,7 +26,7 @@ 4.3.1 成品展示 -------------- -以我的博客(``python-online.cn``)为例,先给大家展示一下。 +以我的博客(\ ``python-online.cn``)为例,先给大家展示一下。 这是首页。显示了你所有的文章索引。 |image0| @@ -500,3 +500,4 @@ Python 3.x ,所以这里的代码也要对应修改。 .. |image15| image:: http://image.python-online.cn/20191016205653.png .. |image16| image:: http://image.python-online.cn/20191015225652.png .. |image17| image:: http://image.python-online.cn/20191016211012.png + diff --git a/source/c04/c04_04.rst b/source/c04/c04_04.rst index a120492..dc94574 100755 --- a/source/c04/c04_04.rst +++ b/source/c04/c04_04.rst @@ -70,43 +70,55 @@ NoteBook,qtconsole,VS Ccode等。 |image1| 运行代码 ~~~~~~~~ -=========== ============== ============== -快捷键 作用 说明 -=========== ============== ============== -Enter 输入框换行 -Shift-Enter 运行本单元代码 并跳转至下单元 -Shift-Enter 运行本单元代码 停留在本单元 -Alt-Enter 运行本单元代码 其下插入新单元 -=========== ============== ============== ++-------------+----------------+----------------+ +| 快捷键 | 作用 | 说明 | ++=============+================+================+ +| Enter | 输入框换行 | | ++-------------+----------------+----------------+ +| Shift-Enter | 运行本单元代码 | 并跳转至下单元 | ++-------------+----------------+----------------+ +| Shift-Enter | 运行本单元代码 | 停留在本单元 | ++-------------+----------------+----------------+ +| Alt-Enter | 运行本单元代码 | 其下插入新单元 | ++-------------+----------------+----------------+ 切换模式 ~~~~~~~~ 在不同的模式下,你细心点会发现单元格的颜色也是变化的。以下字母,不区分大小写。 -====== ======================== ==== -快捷键 作用 说明 -====== ======================== ==== -Enter 进入编辑模式 -Esc 退出编辑状态 -Y 单元格转入代码状态 -R 单元格进入 Raw 状态 -M 单元格进入 Markdown 状态 -====== ======================== ==== ++--------+--------------------------+------+ +| 快捷键 | 作用 | 说明 | ++========+==========================+======+ +| Enter | 进入编辑模式 | | ++--------+--------------------------+------+ +| Esc | 退出编辑状态 | | ++--------+--------------------------+------+ +| Y | 单元格转入代码状态 | | ++--------+--------------------------+------+ +| R | 单元格进入 Raw 状态 | | ++--------+--------------------------+------+ +| M | 单元格进入 Markdown 状态 | | ++--------+--------------------------+------+ Markdown下快捷键 ~~~~~~~~~~~~~~~~ -====== ============= ==== -快捷键 作用 说明 -====== ============= ==== -1 设定 1 级标题 -2 设定 2 级标题 -3 设定 3 级标题 -4 设定 4 级标题 -5 设定 5 级标题 -6 设定 6 级标题 -====== ============= ==== ++--------+---------------+------+ +| 快捷键 | 作用 | 说明 | ++========+===============+======+ +| 1 | 设定 1 级标题 | | ++--------+---------------+------+ +| 2 | 设定 2 级标题 | | ++--------+---------------+------+ +| 3 | 设定 3 级标题 | | ++--------+---------------+------+ +| 4 | 设定 4 级标题 | | ++--------+---------------+------+ +| 5 | 设定 5 级标题 | | ++--------+---------------+------+ +| 6 | 设定 6 级标题 | | ++--------+---------------+------+ 操作单元格 ~~~~~~~~~~ @@ -118,61 +130,93 @@ Markdown下快捷键 其他的,还有一些和我们日常操作很像。 -======= ====================== ==== -快捷键 作用 说明 -======= ====================== ==== -c 复制选中的单元 -x 剪切选中的单元 -shift-v 粘贴到上方单元 -v 粘贴到下方单元 -z 恢复删除的最后一个单元 -shift-m 合并选中的单元 -a 在上方插入新单元 -b 在下方插入新单元 -shitf-k 向上连续选中单元格 -shitf-j 向下连续选中单元格 -======= ====================== ==== ++---------+------------------------+------+ +| 快捷键 | 作用 | 说明 | ++=========+========================+======+ +| c | 复制选中的单元 | | ++---------+------------------------+------+ +| x | 剪切选中的单元 | | ++---------+------------------------+------+ +| shift-v | 粘贴到上方单元 | | ++---------+------------------------+------+ +| v | 粘贴到下方单元 | | ++---------+------------------------+------+ +| z | 恢复删除的最后一个单元 | | ++---------+------------------------+------+ +| shift-m | 合并选中的单元 | | ++---------+------------------------+------+ +| a | 在上方插入新单元 | | ++---------+------------------------+------+ +| b | 在下方插入新单元 | | ++---------+------------------------+------+ +| shitf-k | 向上连续选中单元格 | | ++---------+------------------------+------+ +| shitf-j | 向下连续选中单元格 | | ++---------+------------------------+------+ 编辑模式下快捷键 ~~~~~~~~~~~~~~~~ -============== ================= ============== -快捷键 作用 说明 -============== ================= ============== -Tab 代码补全或缩进 -Shift-Tab 提示 -Ctrl-] 缩进 -Ctrl-[ 解除缩进 -Ctrl-D 删除整行 -Ctrl-Z 撤消 -Ctrl-Y 取消撤消 -Ctrl-Shift-Z 取消撤消 -Ctrl-A 全选 -Ctrl-U 取消选择 -Alt-U 恢复选择 -Ctrl-Home 跳到单元开头 -Ctrl-Up 跳到单元开头 -Ctrl-End 跳到单元末尾 -Ctrl-Down 跳到单元末尾 -Ctrl-Left 跳到左边一个字首 -Ctrl-Right 跳到右边一个字首 -Ctrl-Backspace 删除前面一个字 -Ctrl-Delete 删除后面一个字 -Ctrl-/ 注释整行/撤销注释 仅代码状态有效 -Shift 忽略 -Ctrl-S 保存当前 NoteBook -============== ================= ============== ++----------------+-------------------+----------------+ +| 快捷键 | 作用 | 说明 | ++================+===================+================+ +| Tab | 代码补全或缩进 | | ++----------------+-------------------+----------------+ +| Shift-Tab | 提示 | | ++----------------+-------------------+----------------+ +| Ctrl-] | 缩进 | | ++----------------+-------------------+----------------+ +| Ctrl-[ | 解除缩进 | | ++----------------+-------------------+----------------+ +| Ctrl-D | 删除整行 | | ++----------------+-------------------+----------------+ +| Ctrl-Z | 撤消 | | ++----------------+-------------------+----------------+ +| Ctrl-Y | 取消撤消 | | ++----------------+-------------------+----------------+ +| Ctrl-Shift-Z | 取消撤消 | | ++----------------+-------------------+----------------+ +| Ctrl-A | 全选 | | ++----------------+-------------------+----------------+ +| Ctrl-U | 取消选择 | | ++----------------+-------------------+----------------+ +| Alt-U | 恢复选择 | | ++----------------+-------------------+----------------+ +| Ctrl-Home | 跳到单元开头 | | ++----------------+-------------------+----------------+ +| Ctrl-Up | 跳到单元开头 | | ++----------------+-------------------+----------------+ +| Ctrl-End | 跳到单元末尾 | | ++----------------+-------------------+----------------+ +| Ctrl-Down | 跳到单元末尾 | | ++----------------+-------------------+----------------+ +| Ctrl-Left | 跳到左边一个字首 | | ++----------------+-------------------+----------------+ +| Ctrl-Right | 跳到右边一个字首 | | ++----------------+-------------------+----------------+ +| Ctrl-Backspace | 删除前面一个字 | | ++----------------+-------------------+----------------+ +| Ctrl-Delete | 删除后面一个字 | | ++----------------+-------------------+----------------+ +| Ctrl-/ | 注释整行/撤销注释 | 仅代码状态有效 | ++----------------+-------------------+----------------+ +| Shift | 忽略 | | ++----------------+-------------------+----------------+ +| Ctrl-S | 保存当前 NoteBook | | ++----------------+-------------------+----------------+ 其他快捷键 ~~~~~~~~~~ -============ ============== ==== -快捷键 作用 说明 -============ ============== ==== -f 搜索并替换 -l(L的小写) 形状代码行号 -h 显示快捷键帮助 -============ ============== ==== ++--------------+----------------+------+ +| 快捷键 | 作用 | 说明 | ++==============+================+======+ +| f | 搜索并替换 | | ++--------------+----------------+------+ +| l(L的小写) | 形状代码行号 | | ++--------------+----------------+------+ +| h | 显示快捷键帮助 | | ++--------------+----------------+------+ 其实以上快捷键,在非编辑模式下,按 h 就会出现快捷键帮助菜单。 @@ -206,3 +250,4 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 .. |image6| image:: http://image.python-online.cn/20190511163253.png .. |image7| image:: http://image.python-online.cn/20190511163304.png .. |image8| image:: http://image.python-online.cn/20190511163311.png + diff --git a/source/c04/c04_05.rst b/source/c04/c04_05.rst index 37e61de..6188ada 100755 --- a/source/c04/c04_05.rst +++ b/source/c04/c04_05.rst @@ -178,3 +178,4 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 .. |image14| image:: http://image.python-online.cn/20190511163750.png .. |image15| image:: http://image.python-online.cn/20190511163757.png .. |image16| image:: http://image.python-online.cn/20190511163805.png + diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index aeb0479..4a1d768 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -548,3 +548,4 @@ Desktop真心觉得不好用)。 .. |image4| image:: http://image.python-online.cn/20190511163855.png .. |image5| image:: https://i.loli.net/2018/04/15/5ad2c2a9813b9.png .. |image6| image:: http://image.python-online.cn/20190430235625.png + diff --git a/source/c04/c04_07.rst b/source/c04/c04_07.rst index 1b93054..a30c8da 100755 --- a/source/c04/c04_07.rst +++ b/source/c04/c04_07.rst @@ -832,3 +832,4 @@ bash窗口,不然会提示找不到npm命令) .. |image4| image:: http://image.python-online.cn/17-9-9/63041495.jpg .. |image5| image:: https://i.loli.net/2018/04/15/5ad3018e18cc7.png .. |image6| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png + diff --git a/source/c04/c04_08.rst b/source/c04/c04_08.rst index 2e68037..f3b7183 100755 --- a/source/c04/c04_08.rst +++ b/source/c04/c04_08.rst @@ -80,7 +80,6 @@ - https://zhuanlan.zhihu.com/p/36147819 - https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf - - https://www.jetbrains.com/help/pycharm/ctrl-alt.html 4.8.2 Sublime Text 3 diff --git a/source/c04/c04_09.rst b/source/c04/c04_09.rst index 4468846..0d5511c 100755 --- a/source/c04/c04_09.rst +++ b/source/c04/c04_09.rst @@ -750,3 +750,4 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 .. |image33| image:: https://i.loli.net/2017/08/27/59a261734e896.png .. |image34| image:: https://i.loli.net/2017/08/27/59a261734ff8b.png .. |image35| image:: https://i.loli.net/2017/08/25/599fc9aa85094.png + diff --git a/source/c04/c04_10.rst b/source/c04/c04_10.rst index bfcd002..ca03f02 100755 --- a/source/c04/c04_10.rst +++ b/source/c04/c04_10.rst @@ -298,7 +298,7 @@ limit子句:限制返回数据的量。 ~~~~~~~~~~~~ ``自然连接: natural join`` ``自然连接``, 就是\ ``自动匹配``\ 连接条件: -系统以字段名字作为匹配模式(**同名字段就作为条件, +系统以字段名字作为匹配模式(\ **同名字段就作为条件, 多个同名字段都作为条件**). 自然连接: 可以分为自然内连接和自然外连接. @@ -564,3 +564,4 @@ limit子句:限制返回数据的量。 .. |image21| image:: https://i.loli.net/2017/08/27/59a2756c66952.png .. |image22| image:: https://i.loli.net/2017/08/27/59a27651cd78e.png .. |image23| image:: https://i.loli.net/2017/08/27/59a2784f6562d.png + diff --git a/source/c04/c04_11.rst b/source/c04/c04_11.rst index a04587f..6a8d921 100644 --- a/source/c04/c04_11.rst +++ b/source/c04/c04_11.rst @@ -194,3 +194,4 @@ Host .. |image14| image:: http://image.python-online.cn/20190113113211.png .. |image15| image:: http://image.python-online.cn/20190113112456.png .. |image16| image:: http://image.python-online.cn/20190113112649.png + diff --git a/source/c04/c04_12.rst b/source/c04/c04_12.rst index dae9b24..3c78f48 100644 --- a/source/c04/c04_12.rst +++ b/source/c04/c04_12.rst @@ -87,49 +87,71 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 **最常用** -==== ========= ======================== -指令 英文 解释 -==== ========= ======================== -n Next 下一步 -l list 列出当前断点处源码 -p print 打印变量 -s step into 执行当前行,可以进入函数 -r return 运行完当前函数,返回结果 -c continue 执行到下一断点或者结束 -b break 设置断点 -q quit 退出程序 -==== ========= ======================== ++------+-----------+--------------------------+ +| 指令 | 英文 | 解释 | ++======+===========+==========================+ +| n | Next | 下一步 | ++------+-----------+--------------------------+ +| l | list | 列出当前断点处源码 | ++------+-----------+--------------------------+ +| p | print | 打印变量 | ++------+-----------+--------------------------+ +| s | step into | 执行当前行,可以进入函数 | ++------+-----------+--------------------------+ +| r | return | 运行完当前函数,返回结果 | ++------+-----------+--------------------------+ +| c | continue | 执行到下一断点或者结束 | ++------+-----------+--------------------------+ +| b | break | 设置断点 | ++------+-----------+--------------------------+ +| q | quit | 退出程序 | ++------+-----------+--------------------------+ **有时使用** -=========== ======== ============================ -指令 英文 解释 -=========== ======== ============================ -a args 列出当前函数的参数 -pp pprint 一种可视化更好的打印 -j jump 跳到指定行 -cl clear 清除断点 -w where 打印当前堆栈 -u up 执行跳转到当前堆栈的上一层 -unt until 行数递增执行(忽略循环和函数) -ll longlist 列出更多的源码 -run/restart run 重新启动 debug(-m pdb) -=========== ======== ============================ ++-------------+----------+------------------------------+ +| 指令 | 英文 | 解释 | ++=============+==========+==============================+ +| a | args | 列出当前函数的参数 | ++-------------+----------+------------------------------+ +| pp | pprint | 一种可视化更好的打印 | ++-------------+----------+------------------------------+ +| j | jump | 跳到指定行 | ++-------------+----------+------------------------------+ +| cl | clear | 清除断点 | ++-------------+----------+------------------------------+ +| w | where | 打印当前堆栈 | ++-------------+----------+------------------------------+ +| u | up | 执行跳转到当前堆栈的上一层 | ++-------------+----------+------------------------------+ +| unt | until | 行数递增执行(忽略循环和函数) | ++-------------+----------+------------------------------+ +| ll | longlist | 列出更多的源码 | ++-------------+----------+------------------------------+ +| run/restart | run | 重新启动 debug(-m pdb) | ++-------------+----------+------------------------------+ **几乎不用** -======= =============== ================== -指令 英文 解释 -======= =============== ================== -tbreak temporary break 临时断点 -disable 停用断点 -enable 启用断点 -alias 设置别名 -unalias 删除别名 -whatis 打印对象类型 -ignore 设置忽略的断点 -source 列出给定对象的源码 -======= =============== ================== ++---------+-----------------+--------------------+ +| 指令 | 英文 | 解释 | ++=========+=================+====================+ +| tbreak | temporary break | 临时断点 | ++---------+-----------------+--------------------+ +| disable | | 停用断点 | ++---------+-----------------+--------------------+ +| enable | | 启用断点 | ++---------+-----------------+--------------------+ +| alias | | 设置别名 | ++---------+-----------------+--------------------+ +| unalias | | 删除别名 | ++---------+-----------------+--------------------+ +| whatis | | 打印对象类型 | ++---------+-----------------+--------------------+ +| ignore | | 设置忽略的断点 | ++---------+-----------------+--------------------+ +| source | | 列出给定对象的源码 | ++---------+-----------------+--------------------+ 其上全部是我翻译自官方文档,原文在这里:https://docs.python.org/3/library/pdb.html @@ -161,3 +183,4 @@ source 列出给定对象的源码 .. |image2| image:: http://image.python-online.cn/20190118000557.png .. |image3| image:: http://image.python-online.cn/20190118083809.png .. |image4| image:: http://image.python-online.cn/20190118005507.png + diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 921ab74..2b2b01f 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -180,3 +180,4 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 .. |image5| image:: http://image.python-online.cn/20190612211925.png .. |image6| image:: http://image.python-online.cn/20190614000336.png .. |image7| image:: http://image.python-online.cn/20190612215924.png + diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index e56c923..d1c7048 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -460,7 +460,7 @@ PyCharm 版本有效,可以实现永久激活(到 2099 / **第二步**\ : 若是 Windows 系统,请找到并进入你的 PyCharm -安装启动目录(以我的为例):E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\PyCharm 2017.1`.5:raw-latex:`\bin` +安装启动目录(以我的为例):E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\PyCharm `2017.1.5:raw-latex:`\bin` 将第一步下载的 jar 包放入这个目录,并打开如下两个以 ``vmoptions`` 后缀结尾的文件: @@ -1167,3 +1167,4 @@ Debug ,而远程调试需要不少前置步骤。 .. |image97| image:: http://image.python-online.cn/20191211102826.png .. |image98| image:: http://image.python-online.cn/20191211133836.png .. |image99| image:: http://image.python-online.cn/20191211143510.png + diff --git a/source/c04/c04_17.rst b/source/c04/c04_17.rst index 1c66017..954e7d6 100644 --- a/source/c04/c04_17.rst +++ b/source/c04/c04_17.rst @@ -480,3 +480,4 @@ Order .. |image1| image:: http://image.python-online.cn/20190512113846.png .. |image2| image:: http://image.python-online.cn/20190512113917.png .. |image3| image:: http://image.python-online.cn/20190512114028.png + diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index dc3e79e..022b3dc 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -1,4 +1,4 @@ -# 4.18 Mac 高效工具 +# 4.18 如何上手Mac ? ## 4.18.1 iTerm2 @@ -24,7 +24,7 @@ option + command + e ## 4.18.2 截图工具 1. Xnip:取色+丰富的标注功能+滚动截屏; -2. Snip:小巧轻量+滚动截屏:; +2. [Snip](https://snip.qq.com/):小巧轻量+滚动截屏(不能从App Store下载,亲测已经失效); 3. QQ:截屏+录屏+OCR文字识别; 4. Snipaste:贴屏功能; 5. ScreenShot PSD:psd 文件,每种元素都有单独的图层。 @@ -35,10 +35,6 @@ option + command + e # 返回桌面 fn + f11 -# finder 后退与前进 -command + up # 文件夹后退 -command + down # 文件夹前进 - # 最大化窗口与取消 command + ctrl + f @@ -65,6 +61,57 @@ command + backspace(删除键) command + 鼠标左键 ``` +文件夹管理 + +``` +# 快速打开访达:先打开搜索,再打开个人家目录 +打开搜索:command + option(alt) + space +关闭标签页:command + shift + h + +# 返回父级文件夹 +command + ↑ + +# 进入文件夹 +command + ↓ + +# enter +重命名文件夹 + +# 选中所有文件,并将这些文件归档入一个新的文件夹 +右键 -> 用所选项目新建的文件夹 -> 回车,重命名 + + +# 选择 +点击 -> 拖拽 +如果想要取消选中,就 command + 点击 + +# 指定路径打开访达 +shift + command + g + +# 前进 后退 +command + [ +comand + ] + +# 打开最近使用过的文件夹 +comand + shift + f + +# +``` + +查询汇率 + +``` +command + space +输入 1港币或者1美元 +``` + +将网页图片保存到本地 + +``` +直接拖动图片 +如果不仍想让访达窗口保持在最前面,就按住 command 键 +``` + ## 4.18.4 系统设置 @@ -160,6 +207,8 @@ QSpace:[finder 的增强版](https://mp.weixin.qq.com/s/BRBZZfx0bGc8X8WueS37Xg eZip:与QSpace同一开发者。集所有同类产品所长的解压缩工具([官网可下](https://ezip.awehunt.com/)) +Keta:解压缩软件 + ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 @@ -168,7 +217,9 @@ Downie:网页视频下载,复制链接即可。 GoodSync:和 windows 平台同步文件 +[IINA](https://iina.io/):万能的视频播放器,一个就够 +TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) ## 4.18.6 brew 的使用 diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 5d8b6bc..a563185 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -1,5 +1,5 @@ -4.18 Mac 高效工具 -================= +4.18 如何上手Mac ? +=================== 4.18.1 iTerm2 ------------- @@ -27,7 +27,8 @@ --------------- 1. Xnip:取色+丰富的标注功能+滚动截屏; -2. Snip:小巧轻量+滚动截屏:; +2. `Snip `__\ :小巧轻量+滚动截屏(不能从App + Store下载,亲测已经失效); 3. QQ:截屏+录屏+OCR文字识别; 4. Snipaste:贴屏功能; 5. ScreenShot PSD:psd 文件,每种元素都有单独的图层。 @@ -40,13 +41,82 @@ # 返回桌面 fn + f11 - # finder 后退与前进 - command + up # 文件夹后退 - command + down # 文件夹前进 - # 最大化窗口与取消 command + ctrl + f + # 关闭访达 + command + w + + # 关闭除访达外的其他程序 + command + q + + # 在不同的程序间切换 + command + tab + command + shift + tab + + # 在一个程序的不同窗口切换 + command + ~ + + # 强制退出程序 + command + option + esc + + # 删除光标处到行首 + command + backspace(删除键) + + # 新建窗口打开页面 + command + 鼠标左键 + +文件夹管理 + +:: + + # 快速打开访达:先打开搜索,再打开个人家目录 + 打开搜索:command + option(alt) + space + 关闭标签页:command + shift + h + + # 返回父级文件夹 + command + ↑ + + # 进入文件夹 + command + ↓ + + # enter + 重命名文件夹 + + # 选中所有文件,并将这些文件归档入一个新的文件夹 + 右键 -> 用所选项目新建的文件夹 -> 回车,重命名 + + + # 选择 + 点击 -> 拖拽 + 如果想要取消选中,就 command + 点击 + + # 指定路径打开访达 + shift + command + g + + # 前进 后退 + command + [ + comand + ] + + # 打开最近使用过的文件夹 + comand + shift + f + + # + +查询汇率 + +:: + + command + space + 输入 1港币或者1美元 + +将网页图片保存到本地 + +:: + + 直接拖动图片 + 如果不仍想让访达窗口保持在最前面,就按住 command 键 + 4.18.4 系统设置 --------------- @@ -150,6 +220,8 @@ QSpace:\ `finder eZip:与QSpace同一开发者。集所有同类产品所长的解压缩工具(\ `官网可下 `__\ ) +Keta:解压缩软件 + ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 @@ -158,6 +230,43 @@ Downie:网页视频下载,复制链接即可。 GoodSync:和 windows 平台同步文件 +`IINA `__\ :万能的视频播放器,一个就够 + +TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) + +4.18.6 brew 的使用 +------------------ + +设置国内源 + +.. code:: shell + + git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git + + git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git + + git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git + + brew update + +如果要还原 + +.. code:: shell + + git -C "$(brew --repo)" remote set-url origin https://github.com/Homebrew/brew.git + + git -C "$(brew --repo homebrew/core)" remote set-url origin https://github.com/Homebrew/homebrew-core.git + + git -C "$(brew --repo homebrew/cask)" remote set-url origin https://github.com/Homebrew/homebrew-cask.git + + brew update + +安装docker + +.. code:: shell + + brew cask install docker + 参考文章 -------- @@ -176,3 +285,4 @@ GoodSync:和 windows 平台同步文件 .. |image0| image:: http://image.python-online.cn/20190810161513.png .. |image1| image:: http://image.python-online.cn/20190810162315.png + diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index bd1ed1c..7159939 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -373,3 +373,4 @@ pip 的文件夹,若没有则创建之。 .. |image0| image:: http://image.python-online.cn/20191105200041.png + diff --git a/source/c04/c04_22.rst b/source/c04/c04_22.rst index 2045055..c791369 100644 --- a/source/c04/c04_22.rst +++ b/source/c04/c04_22.rst @@ -11,3 +11,4 @@ 开启完成后,需要重启浏览器,你可以随便打开一篇博客,然后在地址栏右边会有一个阅读模式的按钮。 .. |image0| image:: http://image.python-online.cn/20191201103653.png + diff --git a/source/c04/c04_23.rst b/source/c04/c04_23.rst index 29414ac..63db2c5 100644 --- a/source/c04/c04_23.rst +++ b/source/c04/c04_23.rst @@ -58,3 +58,4 @@ |image0| .. |image0| image:: http://image.python-online.cn/20200119143952.png + diff --git a/source/c05/c05_01.rst b/source/c05/c05_01.rst index 2a0b221..18eabe6 100755 --- a/source/c05/c05_01.rst +++ b/source/c05/c05_01.rst @@ -435,3 +435,4 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD .. |合并两个有序数组| image:: http://image.python-online.cn/FnCZ-3Pj39T_ELROSsGRDN31yWtY .. |桶排序| image:: http://image.python-online.cn/FljGa3F3wM_YGACETeuRHCiERXKb .. |基数排序| image:: http://image.python-online.cn/FhwWVp4LVABIMPHqNo_cpjJA9kHV + diff --git a/source/c05/c05_03.rst b/source/c05/c05_03.rst index 5e7f284..c455c46 100755 --- a/source/c05/c05_03.rst +++ b/source/c05/c05_03.rst @@ -164,3 +164,4 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 .. |image0| image:: http://image.python-online.cn/20190112181126.png + diff --git a/source/c06/c06_01.rst b/source/c06/c06_01.rst index bd0b6a0..506885a 100755 --- a/source/c06/c06_01.rst +++ b/source/c06/c06_01.rst @@ -137,3 +137,4 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 .. |image0| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png .. |image1| image:: http://image.python-online.cn/20190511164650.png + diff --git a/source/c06/c06_02.rst b/source/c06/c06_02.rst index 0eccf8e..6f86b7a 100755 --- a/source/c06/c06_02.rst +++ b/source/c06/c06_02.rst @@ -323,3 +323,4 @@ show image |image9| .. |image7| image:: http://image.python-online.cn/20190511164852.png .. |image8| image:: http://image.python-online.cn/20190511164900.png .. |image9| image:: http://image.python-online.cn/20190511164915.png + diff --git a/source/c06/c06_03.rst b/source/c06/c06_03.rst index afcbe6e..960de44 100755 --- a/source/c06/c06_03.rst +++ b/source/c06/c06_03.rst @@ -214,3 +214,4 @@ show image |image4| .. |image2| image:: http://image.python-online.cn/20190511165003.png .. |image3| image:: http://image.python-online.cn/20190511165013.png .. |image4| image:: http://image.python-online.cn/20190511165020.png + diff --git a/source/c06/c06_04.rst b/source/c06/c06_04.rst index 02df323..17db400 100755 --- a/source/c06/c06_04.rst +++ b/source/c06/c06_04.rst @@ -210,3 +210,4 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib .. |image4| image:: http://image.python-online.cn/20190511165211.png .. |image5| image:: http://image.python-online.cn/20190511165221.png .. |image6| image:: http://image.python-online.cn/20190511165229.png + diff --git a/source/c06/c06_05.rst b/source/c06/c06_05.rst index dcacbb5..3f85d80 100755 --- a/source/c06/c06_05.rst +++ b/source/c06/c06_05.rst @@ -146,3 +146,4 @@ matplotlib 给我们提供了一个函数,\ ``animation.FuncAnimation`` .. |image0| image:: https://i.loli.net/2018/12/25/5c2226078799b.gif + diff --git a/source/c06/c06_06.rst b/source/c06/c06_06.rst index 8d71f39..3bc04a0 100755 --- a/source/c06/c06_06.rst +++ b/source/c06/c06_06.rst @@ -142,3 +142,4 @@ Jupyter NoteBook 里观察整个变化的过程。 .. |image0| image:: http://image.python-online.cn/20190511165315.png + diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index c7436c2..f4a442d 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -1915,3 +1915,4 @@ url** 即可。 .. |image0| image:: http://image.python-online.cn/17-9-20/47469030.jpg .. |image1| image:: http://image.python-online.cn/20190705182629.png .. |image2| image:: http://image.python-online.cn/17-10-15/97911325.jpg + diff --git a/source/c07/c07_02.rst b/source/c07/c07_02.rst index 6e1a4ca..956f080 100755 --- a/source/c07/c07_02.rst +++ b/source/c07/c07_02.rst @@ -160,7 +160,7 @@ Zabbix Proxy 是一个数据收集器,它不计算触发器、不处理事件 注意数据库,这里不要选localhost,使用vip,或者使用vip绑定的域名。 -用默认密码进行登陆(``Admin``/``zabbix``)。 登陆之后,马上修改密码。 +用默认密码进行登陆(\ ``Admin``/``zabbix``)。 登陆之后,马上修改密码。 安装调试工具 ^^^^^^^^^^^^ @@ -719,3 +719,4 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 .. |image19| image:: http://image.python-online.cn/20190605173956.png .. |image20| image:: http://image.python-online.cn/20190409103417.png .. |image21| image:: http://image.python-online.cn/20190409104026.png + diff --git a/source/c07/c07_03.rst b/source/c07/c07_03.rst index 914eade..34dc68e 100755 --- a/source/c07/c07_03.rst +++ b/source/c07/c07_03.rst @@ -293,3 +293,4 @@ namespace 有下面六种 .. |image0| image:: http://image.python-online.cn/17-12-23/44035514.jpg .. |image1| image:: http://image.python-online.cn/17-12-23/20133481.jpg + diff --git a/source/c07/c07_04.rst b/source/c07/c07_04.rst index a43748e..390b2f9 100755 --- a/source/c07/c07_04.rst +++ b/source/c07/c07_04.rst @@ -371,3 +371,4 @@ CMD:两个功能 .. |image6| image:: http://image.python-online.cn/17-12-24/42825662.jpg .. |image7| image:: http://image.python-online.cn/17-12-24/80077038.jpg .. |image8| image:: http://image.python-online.cn/17-12-24/98318652.jpg + diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index d192c39..74de2ea 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -325,3 +325,4 @@ Socket发些“奇怪”的数据。 .. |image1| image:: http://image.python-online.cn/18-1-28/37395940.jpg .. |image2| image:: https://i.loli.net/2018/01/28/5a6de8702428c.png .. |image3| image:: https://i.loli.net/2018/01/28/5a6de73776390.png + diff --git a/source/c07/c07_06.rst b/source/c07/c07_06.rst index b9af741..d920038 100755 --- a/source/c07/c07_06.rst +++ b/source/c07/c07_06.rst @@ -333,3 +333,4 @@ centos的配置文件路径如下,ubuntu的有所不同 .. |image0| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png .. |image1| image:: https://i.loli.net/2018/01/03/5a4ce6eeaff7d.png .. |image2| image:: https://i.loli.net/2018/01/03/5a4ce964d3e73.png + diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index e7adffb..0d8b702 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -192,3 +192,4 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 .. |image1| image:: http://image.python-online.cn/20190716112113.png .. |image2| image:: http://image.python-online.cn/20190716112824.png .. |image3| image:: http://image.python-online.cn/20190716112838.png + diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst index e8b7dd7..19b278b 100644 --- a/source/c07/c07_11.rst +++ b/source/c07/c07_11.rst @@ -76,3 +76,4 @@ K8s 角色详解 **共享资源**\ 。 .. |image0| image:: http://image.python-online.cn/20190907162015.png + diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index b9cdb90..8c0b99e 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -159,3 +159,4 @@ yum-utils 使用 .. |image0| image:: http://image.python-online.cn/20191219152328.png .. |image1| image:: http://image.python-online.cn/20191225173340.png .. |image2| image:: http://image.python-online.cn/20191225175350.png + diff --git a/source/c07/c07_15.rst b/source/c07/c07_15.rst index d3ea595..a14de00 100644 --- a/source/c07/c07_15.rst +++ b/source/c07/c07_15.rst @@ -218,3 +218,4 @@ wsrep_flow_control_sent 和 wsrep_local_recv_queue_avg - http://blog.itpub.net/30126024/viewspace-2221483/ .. |image0| image:: http://image.python-online.cn/20191213162259.png + diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index c633e14..f20a6c2 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -50,3 +50,4 @@ .. |image1| image:: http://image.python-online.cn/20191220203403.png .. |image2| image:: http://image.python-online.cn/20191220202408.png .. |image3| image:: http://image.python-online.cn/20191220203205.png + diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index b31142b..5c7394b 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -293,7 +293,7 @@ aggregate管理 # 修改虚拟机密码,需要虚拟机内部安装 qga:qemu-guest-agent virsh set-user-password instance-00000444 root "root12#$" -热增加网卡:``virsh attach-device ws_controller01 ./tmp.xml --persistent --live`` +热增加网卡:\ ``virsh attach-device ws_controller01 ./tmp.xml --persistent --live`` .. code:: xml diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index 75d62a9..ec97d50 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -315,3 +315,4 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr .. |image5| image:: https://i.loli.net/2018/01/19/5a61c246451e7.png .. |image6| image:: http://image.python-online.cn/20190529202132.png .. |image7| image:: http://image.python-online.cn/20190529202440.png + diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index 839feb6..9632a81 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -537,3 +537,4 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` .. |image2| image:: http://image.python-online.cn/20190827200522.png .. |image3| image:: http://image.python-online.cn/20191211174659.png .. |image4| image:: http://image.python-online.cn/20191211174956.png + diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index ee4b469..fe58ce2 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -493,3 +493,4 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 .. |image3| image:: http://image.python-online.cn/20190716005951.png .. |image4| image:: http://image.python-online.cn/20190714141644.png .. |image5| image:: https://i.loli.net/2019/02/25/5c73e6160764a.png + diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index ad2ddca..01a2553 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -794,3 +794,4 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 .. |image57| image:: http://image.python-online.cn/20190830092203.png .. |image58| image:: http://image.python-online.cn/20190830093613.png .. |image59| image:: http://image.python-online.cn/20190912135302.png + diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index 09e548e..8f1d27b 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -750,3 +750,4 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 .. |image38| image:: http://image.python-online.cn/20190911203953.png .. |image39| image:: http://image.python-online.cn/20190911204805.png .. |image40| image:: http://image.python-online.cn/20190911205518.png + diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index 0306dda..67c58e6 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -163,3 +163,4 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 .. |image8| image:: http://image.python-online.cn/20190528105021.png .. |image9| image:: http://image.python-online.cn/20190528114526.png .. |image10| image:: http://image.python-online.cn/20190606185531.png + diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index 155d3dd..65c3f3f 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -138,10 +138,8 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 -------------------- 1. 启用DHCP后,dhcp server 会占用一个allocation_pools中的ip。 - 2. 子网启用了dhcp,ConfigDrive不会携带静态的ip地址(类型为ipv4,左图),而是携带了将类型变更为ipv4_dhcp(右图),虚拟机启动时发送广播消息DHCP discover从dhcp server获得真正的ip地址。 |image3| - 3. 若子网启用了dhcp,并且创建了虚拟机,此时 disable dhcp,只是表象上 dhcp 被关了,而实际 dhcp port 被是占用着,因为 dhcp server 还要给之前创建的虚拟机提供服务。如果再用这个子网创建的虚拟机会使用dhcp @@ -153,26 +151,20 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 ip。如果一个子网开启了dhcp,并且这个子网下\ **有虚拟机**\ ,更新子网 disable dhcp,dhcp-port\ **不会立即删除**\ 。再用这个子网创建的虚拟机会使用dhcp获取ip。 - 4. dnsmasq进程是由dhcp-agent 服务管理的。如果停用 dhcp-agent 时,dnsmasq 进程并不会关闭。如果同一个网络下有多个dnsmasq ,不会影响正常的dhcp获取ip。 - 5. 如果三个控制节点上的 dhcp-agent 都是关闭状态,此时创建虚拟机时,ip仍然会正常分配、port会正常创建,但由于nova-compute等不到neutron的port plugged事件,过一段时间就超时导致创建虚拟机失败。而如果在超时范围内将dhcp-agent启动起来,就可以立即创建成功。 - 6. 如果一台节点上的 dhcp-agent 关闭了,neutron-server 会等待150s(agent_down_time*2)后再重新调度,将负责这个节点上的dnsmasq进程在另一台上启动起来。 - 7. 虚拟机通过dhcp获取ip,和用config drive 注入静态ip配置的时间差不多,经验证从创建到ping通,dhcp花了22s,静态ip花了23s - 8. 如果一个子网没有配置 dns,那么用这个子网创建虚拟机,虚拟机内部会将这个子网的dhcp server 的ip拿来做dns配在 /etc/resolv.conf 里,而且在排在最上面,可能会导致虚拟机上不了网。 - 9. 使用 dhcp 的模式,cloudinit 从 configure drive 中知道是dhcp后就不会去刷新配置文件将static 改为dhcp(使用的是NetworkManager自动获取ip,这在开机启动时 @@ -180,7 +172,6 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 去做),所以如果这个镜像原网卡配置文件里是静态ip,那么使用这个镜像创建dhcp 的虚拟机,就会暴露旧ip,但是这对于配置ip没有影响,NetworkManager 配置ip的顺序是先dhcp,获取不到再从配置文件读。 - 10. 如果一个子网只有dhcp port,子网可以被删除,如果有其他port,则子网不能删除。 @@ -249,7 +240,7 @@ DOWN后会触发重新调度将dnsmasq迁到另一台,对应函数:reschedul **dhcp-port 是如何被创建出来的?** 从 -neutron:raw-latex:`\neutron-0.0`.1.dev2:raw-latex:`\neutron`:raw-latex:`\agent`:raw-latex:`\linux`:raw-latex:`\dhcp`.py: +neutron:raw-latex:`\neutron`-0.0.1.dev2:raw-latex:`\neutron`:raw-latex:`\agent`:raw-latex:`\linux`:raw-latex:`\dhcp`.py: setup() 开始 再进入 @@ -269,3 +260,4 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: .. |image4| image:: http://image.python-online.cn/20190430204707.png .. |image5| image:: http://image.python-online.cn/20190430204933.png .. |image6| image:: http://image.python-online.cn/20190430205449.png + diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index aa4ae3c..7ed28d6 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -807,3 +807,4 @@ rpc server 和rpc client 的四个重要方法 .. |image18| image:: http://image.python-online.cn/20190526175100.png .. |image19| image:: http://image.python-online.cn/20190526180708.png .. |image20| image:: http://image.python-online.cn/20190526181433.png + diff --git a/source/c08/c08_10.rst b/source/c08/c08_10.rst index 1cbe2ce..87f65c6 100644 --- a/source/c08/c08_10.rst +++ b/source/c08/c08_10.rst @@ -26,3 +26,4 @@ cd,操作文件时,需要使用绝对路径。 .. |image0| image:: http://image.python-online.cn/20191111112221.png .. |image1| image:: http://image.python-online.cn/20191111112421.png .. |image2| image:: http://image.python-online.cn/20191111112548.png + diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index c2d8079..60e4b61 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -45,8 +45,46 @@ echo 1 > /sys/block/sdb/device/delete +3. 修改 ConfigDrive +------------------- + +ConfigDrive 是一个 iso9660 格式的文件,只读。 + +要对其进行修改,需要三步操作 + +第一步: + +.. code:: shell + + $ mount -t iso9660 /var/lib/nova/instances/6016c217-0bb1-4a88-a699-93435b64aa3a/disk.config /mnt/ + + # 拷贝到另一个文件夹中 + $ mkdir /tmp/mk_iso && cp -r /mnt/* /tmp/mk_iso + + # 然后在这个文件夹中对文件进行修改 + # 修改的时候,记得修改时间戳:https://tool.lu/timestamp/ + # 保存的时候记得使用 ":wq!" + +第二步: + +.. code:: shell + + # 先备份,防止有问题 + $ mv /var/lib/nova/instances/6016c217-0bb1-4a88-a699-93435b64aa3a/disk.config /tmp/disk.config.bak + + # 再生成覆盖 + $ genisoimage -o /var/lib/nova/instances/6016c217-0bb1-4a88-a699-93435b64aa3a/disk.config -ldots -allow-lowercase -allow-multidot -l -publisher "OpenStack Compute 2.2.7-20191225.el7.centos" -quiet -J -r -V config-2 /tmp/mk_iso/ + +第三步: + +.. code:: shell + + # 硬重启,重新生成 xml,如果有必要的话 + $ nova reboot --hard + .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190530175817.png + diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index a99adc8..04b9474 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -98,3 +98,4 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 .. |image5| image:: http://image.python-online.cn/20190424215735.png .. |image6| image:: http://image.python-online.cn/20190424220008.png .. |image7| image:: http://image.python-online.cn/20191011103832.png + diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index d1780f0..f042b5d 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -320,3 +320,4 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 .. |image1| image:: http://image.python-online.cn/20190706093904.png .. |image2| image:: http://image.python-online.cn/20190706160632.png .. |image3| image:: http://image.python-online.cn/20190804162402.png + diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index 508e778..9c8f929 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -341,3 +341,4 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local .. |image11| image:: http://image.python-online.cn/20190829111917.png .. |image12| image:: http://image.python-online.cn/20190829161243.png .. |image13| image:: http://image.python-online.cn/20190926171038.png + diff --git a/source/c08/c08_15.rst b/source/c08/c08_15.rst index 95c2d81..a063773 100644 --- a/source/c08/c08_15.rst +++ b/source/c08/c08_15.rst @@ -66,3 +66,4 @@ subnet_id。 .. |image9| image:: http://image.python-online.cn/20190804091911.png .. |image10| image:: http://image.python-online.cn/20190809213209.png .. |image11| image:: http://image.python-online.cn/20190809213223.png + diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index cf98dc8..fed4fdf 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -93,7 +93,7 @@ Goland 下载地址:https://download.jetbrains.com/go/goland-2019.2.3.exe - 激活码.txt:激活码 将 jetbrains-agent.jar 拷贝到 你的 Goland -安装目录的bin文件夹下,我的路径是:E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\GoLand 2019.2`.3:raw-latex:`\bin` +安装目录的bin文件夹下,我的路径是:E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\GoLand `2019.2.3:raw-latex:`\bin` 然后用编辑器打开这两个文件 @@ -354,3 +354,4 @@ auto,必须为小写,不能为为true或false,也不能为1或0。这里 .. |image25| image:: http://image.python-online.cn/20200109213056.png .. |image26| image:: http://image.python-online.cn/20200109214117.png .. |image27| image:: http://image.python-online.cn/20200109154657.png + diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index bf8661d..2fe5f5e 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -232,3 +232,4 @@ https://www.zhihu.com/question/26022206 .. |image0| image:: http://image.python-online.cn/20200120204329.png + diff --git a/source/c09/c09_06.rst b/source/c09/c09_06.rst index ff2628d..af2375c 100644 --- a/source/c09/c09_06.rst +++ b/source/c09/c09_06.rst @@ -291,3 +291,4 @@ Go 中确实不如 Python 那样灵活,bool 与 int .. |image0| image:: http://image.python-online.cn/20200106201856.png + diff --git a/source/c09/c09_19.rst b/source/c09/c09_19.rst index d1190d0..d9b66a8 100644 --- a/source/c09/c09_19.rst +++ b/source/c09/c09_19.rst @@ -206,7 +206,7 @@ int 等等) type Sender = chan<- int sender := make(Sender) -代码是没问题,但是你要明白信道的意义是什么?(**以下是我个人见解** +代码是没问题,但是你要明白信道的意义是什么?(\ **以下是我个人见解** 信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入了吗,没什么用。所以只读信道和只写信道,唇亡齿寒,缺一不可。 diff --git a/source/c09/c09_22.md b/source/c09/c09_22.md new file mode 100644 index 0000000..8d350ea --- /dev/null +++ b/source/c09/c09_22.md @@ -0,0 +1,214 @@ +# 9.22 学习 Go 协程:互斥锁和读写锁 + +在 「[**19. 学习 Go 协程:详解信道/通道**](http://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483741&idx=1&sn=4d4ccd8fdee404432f03447927ddb055&chksm=fc355b36cb42d2201e12b77085f7db5a5fed98674e369a5df7fd852abde09a1efad35ba28944&scene=21#wechat_redirect)」这一节里我详细地介绍信道的一些用法,要知道的是在 Go 语言中,信道的地位非常高,它是 first class 级别的,面对并发问题,我们始终应该优先考虑使用信道,如果通过信道解决不了的,不得不使用共享内存来实现并发编程的,那 Golang 中的锁机制,就是你绕不过的知识点了。 + +今天就来讲一讲 Golang 中的锁机制。 + +在 Golang 里有专门的方法来实现锁,还是上一节里介绍的 sync 包。 + +这个包有两个很重要的锁类型 + +一个叫 `Mutex`, 利用它可以实现互斥锁。 + +一个叫 `RWMutex`,利用它可以实现读写锁。 + +## 1. 互斥锁 :Mutex + +使用互斥锁(Mutex,全称 mutual exclusion)是为了来保护一个资源不会因为并发操作而引起冲突导致数据不准确。 + +举个例子,就像下面这段代码,我开启了三个协程,每个协程分别往 count 这个变量加1000次 1,理论上看,最终的 count 值应试为 3000 + +```go +package main + +import ( + "fmt" + "sync" +) + +func add(count *int, wg *sync.WaitGroup) { + for i := 0; i < 1000; i++ { + *count = *count + 1 + } + wg.Done() +} + +func main() { + var wg sync.WaitGroup + count := 0 + wg.Add(3) + go add(&count, &wg) + go add(&count, &wg) + go add(&count, &wg) + + wg.Wait() + fmt.Println("count 的值为:", count) +} +``` + +可运行多次的结果,都不相同 + +```go +// 第一次 +count 的值为: 2854 + +// 第二次 +count 的值为: 2673 + +// 第三次 +count 的值为: 2840 +``` + +原因就在于这三个协程在执行时,先读取 count 再更新 count 的值,而这个过程并不具备原子性,所以导致了数据的不准确。 + +解决这个问题的方法,就是给 add 这个函数加上 Mutex 互斥锁,要求同一时刻,仅能有一个协程能对 count 操作。 + +在写代码前,先了解一下 Mutex 锁的两种定义方法 + +```go +// 第一种 +var lock *sync.Mutex +lock = new(sync.Mutex) + +// 第二种 +lock := &sync.Mutex{} +``` + +然后就可以修改你上面的代码,如下所示 + +```go +import ( + "fmt" + "sync" +) + +func add(count *int, wg *sync.WaitGroup, lock *sync.Mutex) { + for i := 0; i < 1000; i++ { + lock.Lock() + *count = *count + 1 + lock.Unlock() + } + wg.Done() +} + +func main() { + var wg sync.WaitGroup + lock := &sync.Mutex{} + count := 0 + wg.Add(3) + go add(&count, &wg, lock) + go add(&count, &wg, lock) + go add(&count, &wg, lock) + + wg.Wait() + fmt.Println("count 的值为:", count) +} +``` + +此时,不管你执行多少次,输出都只有一个结果 + +```go +count 的值为: 3000 +``` + +使用 Mutext 锁虽然很简单,但仍然有几点需要注意: + +- 同一协程里,不要在尚未解锁时再次使加锁 +- 同一协程里,不要对已解锁的锁再次解锁 +- 加了锁后,别忘了解锁,必要时使用 defer 语句 + +## 3. 读写锁:RWMutex + +Mutex 是最简单的一种锁类型,他提供了一个傻瓜式的操作,加锁解锁加锁解锁,让你不需要再考虑其他的。 + +**简单**同时意味着在某些特殊情况下有可能会造成时间上的浪费,导致程序性能低下。 + +举个例子,我们平时去图书馆,要嘛是去借书,要嘛去还书,借书的流程繁锁,没有办卡的还要让管理员给你办卡,因此借书通常都要排老长的队,假设图书馆里只有一个管理员,按照 Mutex(互斥锁)的思想, 这个管理员同一时刻只能服务一个人,这就意味着,还书的也要跟借书的一起排队。 + +可还书的步骤非常简单,可能就把书给管理员扫下码就可以走了。 + +如果让还书的人,跟借书的人一起排队,那估计有很多人都不乐意了。 + +因此,图书馆为了提高整个流程的效率,就允许还书的人,不需要排队,可以直接自助还书。 + +图书管将馆里的人分得更细了,对于读者的不同需求提供了不同的方案。提高了效率。 + +RWMutex,也是如此,它将程序对资源的访问分为读操作和写操作 + +- 为了保证数据的安全,它规定了当有人还在读取数据(即读锁占用)时,不允计有人更新这个数据(即写锁会阻塞) +- 为了保证程序的效率,多个人(线程)读取数据(拥有读锁)时,互不影响不会造成阻塞,它不会像 Mutex 那样只允许有一个人(线程)读取同一个数据。 + +理解了这个后,再来看看,如何使用 RWMutex? + +定义一个 RWMuteux 锁,有两种方法 + +```go +// 第一种 +var lock *sync.RWMutex +lock = new(sync.RWMutex) + +// 第二种 +lock := &sync.RWMutex{} +``` + +RWMutex 里提供了两种锁,每种锁分别对应两个方法,为了避免死锁,两个方法应成对出现,必要时请使用 defer。 + +- 读锁:调用 RLock 方法开启锁,调用 RUnlock 释放锁 +- 写锁:调用 Lock 方法开启锁,调用 Unlock 释放锁(和 Mutex类似) + +接下来,直接看一下例子吧 + +```go +package main + +import ( + "fmt" + "sync" + "time" +) + +func main() { + lock := &sync.RWMutex{} + lock.Lock() + + for i := 0; i < 4; i++ { + go func(i int) { + fmt.Printf("第 %d 个协程准备开始... \n", i) + lock.RLock() + fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后,释放锁\n", i) + time.Sleep(time.Second) + lock.RUnlock() + }(i) + } + + time.Sleep(time.Second * 2) + + fmt.Println("准备释放写锁,读锁不再阻塞") + // 写锁一释放,读锁就自由了 + lock.Unlock() + + // 由于会等到读锁全部释放,才能获得写锁 + // 因为这里一定会在上面 4 个协程全部完成才能往下走 + lock.Lock() + fmt.Println("程序退出...") + lock.Unlock() +} +``` + +输出如下 + +``` +第 1 个协程准备开始... +第 0 个协程准备开始... +第 3 个协程准备开始... +第 2 个协程准备开始... +准备释放写锁,读锁不再阻塞 +第 2 个协程获得读锁, sleep 1s 后,释放锁 +第 3 个协程获得读锁, sleep 1s 后,释放锁 +第 1 个协程获得读锁, sleep 1s 后,释放锁 +第 0 个协程获得读锁, sleep 1s 后,释放锁 +程序退出... +``` + + + diff --git a/source/c09/c09_22.rst b/source/c09/c09_22.rst new file mode 100644 index 0000000..d5c65eb --- /dev/null +++ b/source/c09/c09_22.rst @@ -0,0 +1,228 @@ +9.22 学习 Go 协程:互斥锁和读写锁 +================================= + +在 「\ `19. 学习 Go +协程:详解信道/通道 `__\ 」这一节里我详细地介绍信道的一些用法,要知道的是在 +Go 语言中,信道的地位非常高,它是 first class +级别的,面对并发问题,我们始终应该优先考虑使用信道,如果通过信道解决不了的,不得不使用共享内存来实现并发编程的,那 +Golang 中的锁机制,就是你绕不过的知识点了。 + +今天就来讲一讲 Golang 中的锁机制。 + +在 Golang 里有专门的方法来实现锁,还是上一节里介绍的 sync 包。 + +这个包有两个很重要的锁类型 + +一个叫 ``Mutex``\ , 利用它可以实现互斥锁。 + +一个叫 ``RWMutex``\ ,利用它可以实现读写锁。 + +1. 互斥锁 :Mutex +----------------- + +使用互斥锁(Mutex,全称 mutual +exclusion)是为了来保护一个资源不会因为并发操作而引起冲突导致数据不准确。 + +举个例子,就像下面这段代码,我开启了三个协程,每个协程分别往 count +这个变量加1000次 1,理论上看,最终的 count 值应试为 3000 + +.. code:: go + + package main + + import ( + "fmt" + "sync" + ) + + func add(count *int, wg *sync.WaitGroup) { + for i := 0; i < 1000; i++ { + *count = *count + 1 + } + wg.Done() + } + + func main() { + var wg sync.WaitGroup + count := 0 + wg.Add(3) + go add(&count, &wg) + go add(&count, &wg) + go add(&count, &wg) + + wg.Wait() + fmt.Println("count 的值为:", count) + } + +可运行多次的结果,都不相同 + +.. code:: go + + // 第一次 + count 的值为: 2854 + + // 第二次 + count 的值为: 2673 + + // 第三次 + count 的值为: 2840 + +原因就在于这三个协程在执行时,先读取 count 再更新 count +的值,而这个过程并不具备原子性,所以导致了数据的不准确。 + +解决这个问题的方法,就是给 add 这个函数加上 Mutex +互斥锁,要求同一时刻,仅能有一个协程能对 count 操作。 + +在写代码前,先了解一下 Mutex 锁的两种定义方法 + +.. code:: go + + // 第一种 + var lock *sync.Mutex + lock = new(sync.Mutex) + + // 第二种 + lock := &sync.Mutex{} + +然后就可以修改你上面的代码,如下所示 + +.. code:: go + + import ( + "fmt" + "sync" + ) + + func add(count *int, wg *sync.WaitGroup, lock *sync.Mutex) { + for i := 0; i < 1000; i++ { + lock.Lock() + *count = *count + 1 + lock.Unlock() + } + wg.Done() + } + + func main() { + var wg sync.WaitGroup + lock := &sync.Mutex{} + count := 0 + wg.Add(3) + go add(&count, &wg, lock) + go add(&count, &wg, lock) + go add(&count, &wg, lock) + + wg.Wait() + fmt.Println("count 的值为:", count) + } + +此时,不管你执行多少次,输出都只有一个结果 + +.. code:: go + + count 的值为: 3000 + +使用 Mutext 锁虽然很简单,但仍然有几点需要注意: + +- 同一协程里,不要在尚未解锁时再次使加锁 +- 同一协程里,不要对已解锁的锁再次解锁 +- 加了锁后,别忘了解锁,必要时使用 defer 语句 + +3. 读写锁:RWMutex +------------------ + +Mutex +是最简单的一种锁类型,他提供了一个傻瓜式的操作,加锁解锁加锁解锁,让你不需要再考虑其他的。 + +**简单**\ 同时意味着在某些特殊情况下有可能会造成时间上的浪费,导致程序性能低下。 + +举个例子,我们平时去图书馆,要嘛是去借书,要嘛去还书,借书的流程繁锁,没有办卡的还要让管理员给你办卡,因此借书通常都要排老长的队,假设图书馆里只有一个管理员,按照 +Mutex(互斥锁)的思想, +这个管理员同一时刻只能服务一个人,这就意味着,还书的也要跟借书的一起排队。 + +可还书的步骤非常简单,可能就把书给管理员扫下码就可以走了。 + +如果让还书的人,跟借书的人一起排队,那估计有很多人都不乐意了。 + +因此,图书馆为了提高整个流程的效率,就允许还书的人,不需要排队,可以直接自助还书。 + +图书管将馆里的人分得更细了,对于读者的不同需求提供了不同的方案。提高了效率。 + +RWMutex,也是如此,它将程序对资源的访问分为读操作和写操作 + +- 为了保证数据的安全,它规定了当有人还在读取数据(即读锁占用)时,不允计有人更新这个数据(即写锁会阻塞) +- 为了保证程序的效率,多个人(线程)读取数据(拥有读锁)时,互不影响不会造成阻塞,它不会像 + Mutex 那样只允许有一个人(线程)读取同一个数据。 + +理解了这个后,再来看看,如何使用 RWMutex? + +定义一个 RWMuteux 锁,有两种方法 + +.. code:: go + + // 第一种 + var lock *sync.RWMutex + lock = new(sync.RWMutex) + + // 第二种 + lock := &sync.RWMutex{} + +RWMutex +里提供了两种锁,每种锁分别对应两个方法,为了避免死锁,两个方法应成对出现,必要时请使用 +defer。 + +- 读锁:调用 RLock 方法开启锁,调用 RUnlock 释放锁 +- 写锁:调用 Lock 方法开启锁,调用 Unlock 释放锁(和 Mutex类似) + +接下来,直接看一下例子吧 + +.. code:: go + + package main + + import ( + "fmt" + "sync" + "time" + ) + + func main() { + lock := &sync.RWMutex{} + lock.Lock() + + for i := 0; i < 4; i++ { + go func(i int) { + fmt.Printf("第 %d 个协程准备开始... \n", i) + lock.RLock() + fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后,释放锁\n", i) + time.Sleep(time.Second) + lock.RUnlock() + }(i) + } + + time.Sleep(time.Second * 2) + + fmt.Println("准备释放写锁,读锁不再阻塞") + // 写锁一释放,读锁就自由了 + lock.Unlock() + + // 由于会等到读锁全部释放,才能获得写锁 + // 因为这里一定会在上面 4 个协程全部完成才能往下走 + lock.Lock() + fmt.Println("程序退出...") + lock.Unlock() + } + +输出如下 + +:: + + 第 1 个协程准备开始... + 第 0 个协程准备开始... + 第 3 个协程准备开始... + 第 2 个协程准备开始... + 准备释放写锁,读锁不再阻塞 + 第 2 个协程获得读锁, sleep 1s 后,释放锁 + 第 3 个协程获得读锁, sleep 1s 后,释放锁 + 第 1 个协程获得读锁, sleep 1s 后,释放锁 + 第 0 个协程获得读锁, sleep 1s 后,释放锁 + 程序退出... diff --git a/source/c09/c09_24.md b/source/c09/c09_24.md index 9cbd419..210ff4b 100644 --- a/source/c09/c09_24.md +++ b/source/c09/c09_24.md @@ -132,4 +132,3 @@ Golang 异常的抛出与捕获,依赖两个内置函数: revocer 调用后,抛出的 panic 将会在此外终结,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 -![image-20200301121138527](upload\image-20200301121138527.png) \ No newline at end of file diff --git a/source/c09/c09_24.rst b/source/c09/c09_24.rst index 8201b83..4f2cbcb 100644 --- a/source/c09/c09_24.rst +++ b/source/c09/c09_24.rst @@ -1,182 +1,147 @@ -9.9 Go语言命名编码规范 -====================== +9.24 panic 和 recover +===================== -每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 +编程语言一般都会有异常捕获机制,在 Python 中 是使用\ ``raise`` 和 +``try-except`` 语句来实现的异常抛出和异常捕获的。 -以下内容整理自:\ `Go语言(Golang)编码规范 `__ +在 Golang +中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 -命名规范分为以下几点 +当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 +panic,让程序退出停止运行。 -**1. 文件命名** +1. 触发panic +------------ -文件名应一律使用小写(因为Windows文件名不区分大小写?), -不同单词之间用下划线分割。 +手动触发宕机,是非常简单的一件事,只需要调用 panic +这个内置函数即可,就像这样子 -应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 -MyBlog的入口应当为 myblog.go +.. code:: go -**2. 常量命名** + package main -- 常量均需使用全部大写字母组成,并使用下划线分词: + func main() { + panic("crash") + } - .. code:: go +运行后,直接报错宕机 - const APP_VER = "0.7.0.1110 Beta" +.. code:: shell -- 如果是枚举类型的常量,需要先创建相应类型: + $ go run main.go + go run main.go + panic: crash - .. code:: go + goroutine 1 [running]: + main.main() + E:/MING-Code/GoPlayer/src/imooc.com/ccmouse/learngo/main.go:4 +0x40 + exit status 2 - type Scheme string - const ( - HTTP Scheme = "http" - HTTPS Scheme = "https" - ) +2. 捕获 panic +------------- -- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: +发生了异常,有时候就得捕获,就像 Python 中的\ ``except`` 一样,那 Golang +中是如何做到的呢? - .. code:: go +这就不得不引出另外一个内建函数 – +``recover``\ ,它可以让程序在发生宕机后起生回生。 - type PullRequestStatus int - const ( - PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota - PULL_REQUEST_STATUS_CHECKING - PULL_REQUEST_STATUS_MERGEABLE - ) +但是 recover 的使用,有一个条件,就是它必须在 defer +函数中才能生效,其他作用域下,它是不工作的。 -**3. 变量命名** +这是一个简单的例子 -使用驼峰命名法 +.. code:: go -- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u -- 若该变量为 bool 类型,则名称应以 ``Has``, ``Is``, ``Can`` 或 - ``Allow`` 开头。例如:isExist ,hasConflict 。 -- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 - startDate 。 -- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 - ``apiClient``\ 。 -- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient + import "fmt" -下面列举了一些常见的特有名词: + func set_data(x int) { + defer func() { + // recover() 可以将捕获到的panic信息打印 + if err := recover(); err != nil { + fmt.Println(err) + } + }() -:: - - // A GonicMapper that contains a list of common initialisms taken from golang/lint - var LintGonicMapper = GonicMapper{ - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SSH": true, - "TLS": true, - "TTL": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XSRF": true, - "XSS": true, + // 故意制造数组越界,触发 panic + var arr [10]int + arr[x] = 88 } -**接口命名** - -使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 + func main() { + set_data(20) -.. code:: go - - type helloWorld interface { - func Hello(); + // 如果能执行到这句,说明panic被捕获了 + // 后续的程序能继续运行 + fmt.Println("everything is ok") } - type SayHello helloWorld - -**注释规范** - -单行注释使用 ``//`` ,多行注释使用 ``/* comment */`` +运行后,输出如下 .. code:: go - // go语言 - - /* - Go 语言 - Hello, World - */ - -- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 - -- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 + $ go run main.go + runtime error: index out of range [20] with length 10 + everything is ok -- 包、函数、方法和类型的注释说明都是一个完整的句子。 +通常来说,不应该对进入 panic +宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 +web +服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 +web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 -- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 +3. 无法跨协程 +------------- -- 注释的单行长度不能超过 80 个字符。 +从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 +defer 延迟函数,还是得执行完 defer 。 -- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 +但是这个 defer 在多个协程之间是没有效果,在子协程里触发 +panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer +函数的。 -- 如果是特别复杂的包,可单独创建 doc.go 文件说明 +来做个实验就知道了 -- 类型的定义一般都以单数形式描述: - - .. code:: go - - // Request represents a request to run a command. type Request struct { ... - -- 如果为接口,则一般以以下形式描述: - - .. code:: go - - // FileInfo is the interface that describes a file and is returned by Stat and Lstat. - type FileInfo interface { ... - -- 函数与方法的注释需以函数或方法的名称作为开头: - - .. code:: go - - // Post returns *BeegoHttpRequest with POST method. - -- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: +.. code:: go - .. code:: go + import ( + "fmt" + "time" + ) + + func main() { + // 这个 defer 并不会执行 + defer fmt.Println("in main") + + go func() { + defer println("in goroutine") + panic("") + }() + + time.Sleep(2 * time.Second) + } - // Copy copies file from source to target path. - // It returns false and error when error occurs in underlying function calls. +输出如下 -- 若函数或方法为判断类型(返回值主要为 ``bool`` 类型),则以 - `` returns true if`` 开头: +:: - .. code:: go + in goroutine + panic: - // HasPrefix returns true if name has any string in given slice as prefix. - func HasPrefix(name string, prefixes []string) bool { ... + goroutine 6 [running]: + main.main.func1() + E:/MING-Code/main.go:12 +0x7b + created by main.main + E:/MING-Code/main.go:10 +0xbc + exit status 2 -特别注释 +总结一下 +-------- -- TODE:提醒维护人员此部分代码待完成 -- FIXME:提醒维护人员此处有BUG待修复 -- NOTE:维护人员要关注的一些问题说明 +Golang 异常的抛出与捕获,依赖两个内置函数: -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! +- panic:抛出异常,使程序崩溃 +- recover:捕获异常,恢复程序 +revocer 调用后,抛出的 panic 将会在此外终结,不会再外抛,但是 +recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 diff --git a/source/c09/c09_26.rst b/source/c09/c09_26.rst new file mode 100644 index 0000000..cfab7c4 --- /dev/null +++ b/source/c09/c09_26.rst @@ -0,0 +1,182 @@ +9.26 Go语言命名编码规范 +======================= + +每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 + +以下内容整理自:\ `Go语言(Golang)编码规范 `__ + +命名规范分为以下几点 + +**1. 文件命名** + +文件名应一律使用小写(因为Windows文件名不区分大小写?), +不同单词之间用下划线分割。 + +应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 +MyBlog的入口应当为 myblog.go + +**2. 常量命名** + +- 常量均需使用全部大写字母组成,并使用下划线分词: + + .. code:: go + + const APP_VER = "0.7.0.1110 Beta" + +- 如果是枚举类型的常量,需要先创建相应类型: + + .. code:: go + + type Scheme string + const ( + HTTP Scheme = "http" + HTTPS Scheme = "https" + ) + +- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: + + .. code:: go + + type PullRequestStatus int + const ( + PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota + PULL_REQUEST_STATUS_CHECKING + PULL_REQUEST_STATUS_MERGEABLE + ) + +**3. 变量命名** + +使用驼峰命名法 + +- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u +- 若该变量为 bool 类型,则名称应以 ``Has``, ``Is``, ``Can`` 或 + ``Allow`` 开头。例如:isExist ,hasConflict 。 +- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 + startDate 。 +- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 + ``apiClient``\ 。 +- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient + +下面列举了一些常见的特有名词: + +:: + + // A GonicMapper that contains a list of common initialisms taken from golang/lint + var LintGonicMapper = GonicMapper{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, + } + +**接口命名** + +使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 + +.. code:: go + + type helloWorld interface { + func Hello(); + } + + type SayHello helloWorld + +**注释规范** + +单行注释使用 ``//`` ,多行注释使用 ``/* comment */`` + +.. code:: go + + // go语言 + + /* + Go 语言 + Hello, World + */ + +- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 + +- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 + +- 包、函数、方法和类型的注释说明都是一个完整的句子。 + +- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 + +- 注释的单行长度不能超过 80 个字符。 + +- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 + +- 如果是特别复杂的包,可单独创建 doc.go 文件说明 + +- 类型的定义一般都以单数形式描述: + + .. code:: go + + // Request represents a request to run a command. type Request struct { ... + +- 如果为接口,则一般以以下形式描述: + + .. code:: go + + // FileInfo is the interface that describes a file and is returned by Stat and Lstat. + type FileInfo interface { ... + +- 函数与方法的注释需以函数或方法的名称作为开头: + + .. code:: go + + // Post returns *BeegoHttpRequest with POST method. + +- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: + + .. code:: go + + // Copy copies file from source to target path. + // It returns false and error when error occurs in underlying function calls. + +- 若函数或方法为判断类型(返回值主要为 ``bool`` 类型),则以 + `` returns true if`` 开头: + + .. code:: go + + // HasPrefix returns true if name has any string in given slice as prefix. + func HasPrefix(name string, prefixes []string) bool { ... + +特别注释 + +- TODE:提醒维护人员此部分代码待完成 +- FIXME:提醒维护人员此处有BUG待修复 +- NOTE:维护人员要关注的一些问题说明 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst index 166b7cb..f0f8fce 100644 --- a/source/c10/c10_01.rst +++ b/source/c10/c10_01.rst @@ -157,3 +157,4 @@ .. |image0| image:: http://image.python-online.cn/20200214104413.png .. |image1| image:: http://image.python-online.cn/save.jpeg .. |image2| image:: http://image.python-online.cn/20200214104646.png + From 96056ea7232fbe5a77ce583dc976a2857cf73a98 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 14 Mar 2020 22:03:09 +0800 Subject: [PATCH 018/147] update --- c08_01.rst | 450 ------------------------- source/.DS_Store | Bin 10244 -> 10244 bytes source/c04/c04_18.md | 123 ++++--- source/c07/c07_19.md | 45 +++ source/c09/c09_01.md | 39 +-- source/c09/c09_23.md | 208 +++++------- source/c09/c09_24.md | 282 +++++++++++----- source/c09/c09_25.md | 182 ---------- source/c09/c09_27.md | 194 ++++++++++- source/c09/c09_29.md | 158 +++++++++ source/go_mp_index.md | 60 ++++ source/{mp_index.md => py_mp_index.md} | 0 12 files changed, 834 insertions(+), 907 deletions(-) delete mode 100644 c08_01.rst create mode 100644 source/c07/c07_19.md delete mode 100644 source/c09/c09_25.md create mode 100644 source/c09/c09_29.md create mode 100644 source/go_mp_index.md rename source/{mp_index.md => py_mp_index.md} (100%) diff --git a/c08_01.rst b/c08_01.rst deleted file mode 100644 index cefde71..0000000 --- a/c08_01.rst +++ /dev/null @@ -1,450 +0,0 @@ -8.1 OpenStack 运维命令 -====================== - --------------- - -一、OpenStack -------------- - -1.1 Nova -~~~~~~~~ - -虚拟机管理 -^^^^^^^^^^ - -.. code:: shell - - # 所有运行nova服务的信息 - $ nova service-list - - # 查看虚拟机的详细信息 - $ nova show - - # 查看集群节点(宿主机、组件服务)信息 - $ nova host-list - - # 查看当前计算节点,创建了哪些虚拟机 - $ ll /var/lib/nova/instances - - # List actions on a server - $ nova instance-action-list - - $ nova start # 开机 - $ nova stop # 关机 - $ nova delete # 删除 - - # 创建网段(未测试) - $ nova-manage network create --fixed_range_v4=10.0.1.0/24 --vlan=102 --project_id="tenantID" - - # 创建虚拟机 - $ nova boot \ - --flavor \ - --security-groups default \ - --nic net-id=,v4-fixed-ip= \ - --image \ - --config-drive True \ - --min-count 2 \ - --user-data [user-data-file-path] \ - [--num-instances 2] - [--snapshot ] - [--block-device-mapping vda=a9c7816b-0046-4aa9-9c82-cb881a969bdd:snapshot:300:delete-on-terminate --availability-zone nova:LX-OS-node13 --poll] - - - $ nova boot --flavor m1.tiny --image cirros --nic net-id=6df1fa59-25a3-4f8c-8d14-ae7f7828c1a2 greenboxes - - # 查看控制节点的资源信息 - $ nova host-describe - - # 关闭某节点服务 - $ nova service-disable bm-compute-01 nova-compute - $ nova service-enable bm-compute-01 nova-compute - - # 列出所有安全组 - $ nova secgroup-list - # 列出指定安全组的规则 - $ nova secgroup-list-rules - $ 增加安全组规则(开放ssh) - $ nova secgroup-add-rule default tcp <22> <22> <0.0.0.0/0> - - # 设置metadata - nova meta xxxxxx set ws:evacuate_interval=10 - -flavor管理 -^^^^^^^^^^ - -.. code:: shell - - # 新建flavor - $ nova flavor-create [--ephemeral ] [--swap ] [--rxtx-factor ] [--is-public ] - - - # 删除flavor - $ nova flavor-delete - - # 查看flavor信息 - $ nova flavor-show - - # 添加/修改flavor.extra_specs信息 - $ nova flavor-key set - - # 重置/删除flavor.extra_specs信息 - $ nova flavor-key unset - -aggregate管理 -^^^^^^^^^^^^^ - -.. code:: shell - - # 查看所有aggregate的列表,并不包含metadata - $ nova aggregate-list - - # 往aggregate添加host - $ nova aggregate-add-host - # 创建aggregage分组 - $ nova aggregate-create - - # 删除aggregate空分组,如果aggregate的host不为空,需要先使用aggregate-remove-host 清空host - $ nova aggregate-delete - # 在aggregate中移除主机 - $ nova aggregate-remove-host - - # 查看某aggregate分组的详细信息,逐渐丢弃,请使用aggregate-show - $ nova aggregate-details - $ nova aggregate-show - - # 往aggregate 里添加/更新 metadata 信息 - $ nova aggregate-set-metadata [ ...] - - # 更新aggregate的name或者availability-zone - $ nova aggregate-update [--name NAME] [--availability-zone ] - -1.2 Neutron -~~~~~~~~~~~ - -.. code:: shell - - # 重启neutron-service - $ service neutron-server restart - - # 启动linuxbridge服务 - $ service neutron-linuxbridge-agent restart - - # 创建网络 - $ neutron net-create --provider:physical_network phynet1 --provider:network_type flat private - - # 创建子网 全 - $neutron subnet-create --name public\ - --allocation-pool start=172.20.20.100,end=172.20.20.199 \ - --gateway 172.20.20.200 \ - --enable_dhcp=False \ - --dns-nameserver 114.114.114.114 \ - public 172.20.20.0/24 - - # 创建子网,更多选项可以查看 neutron subnet-create -h - neutron subnet-create --name 192.168.2.0/24 --allocation-pool start=192.168.2.200,end=192.168.2.230 --gateway 192.168.2.253 --dns-nameserver 114.114.114.114 --disable-dhcp private 192.168.2.0/24 - - # 查看网络 - $ neutron net-show - - # 查看子网 - $ neutron subnet-show - - # 查看网络列表 - $ neutron net-list - - # 查看端口占用情况 - $ neutron port-list - - # 指定 mac 创建port - neutron port-create --tenant-id 100001 --fixed-ip ip_address=192.168.0.22 --mac-address fa:16:3e:3a:e8:1b - - nova interface-attach b0cc47bc-25c3-48ca-a4fd-5523326b515a --port-id 8bcba4eb-ade0-403d-8f13-45ed70936f03 - - # 关闭port安全组 - neutron port-update --no-security-groups --port-security-enabled=False - -1.3 Glance -~~~~~~~~~~ - -.. code:: shell - - glance镜像存放:/var/lib/image - - # 官方地址 - https://docs.openstack.org/project-install-guide/baremetal/draft/configure-glance-images.html - - # 上传镜像 (具体看哪glance help image-create) - $ glance image-create --name centos6.5-old --visibility public --disk-format qcow2 --container-format bare --property ws:predownload=True --file /home/ - -1.4 keystone -~~~~~~~~~~~~ - -.. code:: shell - - # -------------------------------token-------------------------------- - # 生成token - $ openstack token issue - - - # -------------------------------domain-------------------------------- - # 创建domain - $ openstack domain create [--description "add new domain"] - - # 查看domain - $ openstack domain show - $ openstack domain list - - # 删除domain,删除前必须置为disable状态 - $ openstack domain set --disable - $ openstack domain delete - - # 更改domain属性:名字,描述,状态 - $ openstack domain set [-h] [--name ] [--description ] - [--enable | --disable] - - # -------------------------------project-------------------------------- - # 查看租户列表/信息 - $ openstack project list - $ openstack project show - - # 创建租户 - $ openstack project create [--domain ] [--description ] - - # 删除租户,可以无需指定domain,默认default - $ openstack project delete - $ openstack project delete [--domain ] [ ...] - - # 设置租户属性 - $ openstack project set [--name ] [--domain ] - [--description ] - [--enable | --disable] [--property ] - - - - # -------------------------------user-------------------------------- - - # 查看/删除/增加用户列表 - $ openstack user list - $ openstack user delete - $ openstack user create - - # 修改当前用户密码 - $ openstack user password set [--password ] [--original-password ] - - # 设置用户属性:租户,domain,名字,密码,远程密码?,Email,描述信息,是否可用 - openstack user set [--name ] [--project ] - [--project-domain ] - [--password ] [--password-prompt] - [--email ] - [--description ] [--enable | --disable] - - - - # 查看用户具体信息 - $ openstack user show - - - # -------------------------------role-------------------------------- - - # 查看角色列表 - $ openstack role list - - # 增加/删除/查看角色 - $ openstack role create - $ openstack role delete - $ openstack role show - - # 设置角色的属性:只有两个属性domain和name - $ openstack role set [--domain ] [--name ] - - # 查看角色-用户-租户的对应关系表 - $ openstack role assignment list - - # 增加/删除角色-用户-租户的对应关系表,具体查看帮助文件 - $ openstack role add -h - $ openstack role remove -h - -二、KVM/QEMU ------------- - --------------- - -1.2 virsh命令 -~~~~~~~~~~~~~ - -.. code:: shell - - # 查看虚拟机的网卡信息 - $ virsh domiflist VM1 - - # kvm 添加硬盘 - qemu-img create -f qcow2 git-openstack.qcow2 100G - virsh attach-disk vdb --cache=none --subdriver=qcow2 - virsh detach-disk /data/test02_add01.qcow2 - - # 暂停/恢复 - virsh suspend - virsh resume - - # 开机自启 - virsh autostart - virsh list --autostart - - # 修改虚拟机密码,需要虚拟机内部安装 qga:qemu-guest-agent - virsh set-user-password instance-00000444 root "root12#$" - -热增加网卡:\ ``virsh attach-device ws_controller01 ./tmp.xml --persistent --live`` - -.. code:: xml - - - - - - - - - - -热去除带宽限速 - -.. code:: shell - - $ virsh domiftune ws_controller01 vnet4 --inbound 0,0,0 --outbound 0,0,0 --config --live - -压缩镜像 - -.. code:: shell - - # 压缩镜像 - virt-sparsify –compress ${ori_img_path} ${dest_img_path} - - # 解决 tmp 目录空间不足的情况,仅对当前终端有效 - mkdir /data/tmp - chmod 1777 /data/tmp/ - export TEMP=/data/tmp - export TMPDIR=/data/tmp - - # 解决 tmp 目录空间不足的情况,对所有终端有效 - echo 'export TEMP=/data/tmp' >> /etc/profile - echo 'export TMPDIR=/data/tmp' >> /etc/profile - source /etc/profile - -在线查看虚拟机的messages日志 - -.. code:: shell - - virt-log -d ws_controller01 - -1.2 LVM管理 -~~~~~~~~~~~ - -.. code:: shell - - # 查看计算节点VG信息 - $ vgdisplay - - # 查看虚拟机磁盘信息 - $ lvs - - # 删除LV - $ lvremove /dev/ssd-volume/* -y - - # 查看可用块设备列表 - $ lsblk - - # 将pv从vg移除 - $ vgreduce --removemissing --force hdd-volumes - - # 添加 pv 到 vg 中 - $ vgextend hdd-volumes /dev/sda - -1.3 QEMU命令 -~~~~~~~~~~~~ - -.. code:: shell - - # 查看img镜像信息 - $ qemu-img info - - # 创建qcow2文件 - $ qemu-img create -f qcow2 openstack-name.qcow2 100G - -三、集群相关 ------------- - --------------- - -3.1 MariaDB -~~~~~~~~~~~ - -.. code:: shell - - # 查看MariaDB集群数量 - $ mysql -e 'show status like "wsrep_%"' -ppasswd|grep wsrep_cluster_size|awk '{ print $2 }' - - # 查看该节点MariaDB是否启动 - $ mariadbClusterCheck - -3.2 RabbitMQ -~~~~~~~~~~~~ - -先在一台节点启动 ``service rabbitmq-server restart`` - -启动后,会生成 ``/var/lib/rabbitmq/.erlang.cookie`` -文件,为了实现节点间的通信加密,需要将这个文件拷贝至其他两个节点。拷贝时,注意生意授予权限。 - -:: - - chown rabbitmq.rabbitmq /var/lib/rabbitmq/.erlang.cookie - -然后启动一下后面两台的服务。 - -:: - - service rabbitmq-server restart - -以上都准备好了,现在好开始构建集群了。 - -分别在后面两个节点执行如下操作。 - -:: - - rabbitmqctl stop_app - - rabbitmqctl join_cluster --ram rabbit@ws_controller01(ws_controller01为节点1的hostname) - - rabbitmqctl start_app - -执行完成后,可以查看一下集群状态。 - -:: - - rabbitmqctl cluster_status - -有了集群后,如果要(openstack)使用,还要创建一下用户 - -:: - - rabbitmqctl add_user openstack openstack12#$ - rabbitmqctl set_permissions -p / openstack ".*" ".*" ".*" - rabbitmqctl set_user_tags openstack administrator - -:: - - # 指定节点执行命令 - rabbitmq -n rabbit@ws_controller02 [command] - -3.3 pacemaker -------------- - -:: - - pkill -9 pacemaker;service pacemaker restart - --------------- - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - - 关注公众号,获取最新干货! diff --git a/source/.DS_Store b/source/.DS_Store index 98eb4f449707a9751f69851f7d73cd9d3dfc08cb..f213e2c4dd1917a3a9ccc0e9e979751f3b970aed 100644 GIT binary patch delta 46 zcmZn(XbG6$&nUAoU^hRb%w`?|H$DMohGYf<2FtXR;^d_K{2a#3{&GthH?u4JWd{H^ C{tbWt delta 32 ocmZn(XbG6$&nUeyU^hRb^kyCbH@?lOa*G%zHVAKKSNO{g0I&oKi2wiq diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 022b3dc..82655c1 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -9,15 +9,18 @@ shift + command + d(横切)command + d(竖切) 2、历史信息查找和粘贴:command + f,呼出查找功能,找到后 tab 键可以选中找到的文本,通过option + 回车粘贴。 + 3、自动完成:command + ; ,呼出自动完成窗口,根据上下文提供内容选择项 # 粘贴历史 shift + command + h + # 回放功能 option + command + b + # 光标去哪了? command + / -# Expose Tabs: +# 在所有的窗口中搜索 option + command + e ``` @@ -32,15 +35,13 @@ option + command + e ## 4.18.3 快捷操作 ``` -# 返回桌面 -fn + f11 +# 查看桌面,并不是返回桌面,再按一下就还原原来的窗口了 +fn + f11 # 通常情况下 +f11 # 在外接键盘下,请先设置 F1,F2 这些键为标准功能键,偏好设置->键盘 # 最大化窗口与取消 command + ctrl + f -# 关闭访达 -command + w - # 关闭除访达外的其他程序 command + q @@ -61,43 +62,6 @@ command + backspace(删除键) command + 鼠标左键 ``` -文件夹管理 - -``` -# 快速打开访达:先打开搜索,再打开个人家目录 -打开搜索:command + option(alt) + space -关闭标签页:command + shift + h - -# 返回父级文件夹 -command + ↑ - -# 进入文件夹 -command + ↓ - -# enter -重命名文件夹 - -# 选中所有文件,并将这些文件归档入一个新的文件夹 -右键 -> 用所选项目新建的文件夹 -> 回车,重命名 - - -# 选择 -点击 -> 拖拽 -如果想要取消选中,就 command + 点击 - -# 指定路径打开访达 -shift + command + g - -# 前进 后退 -command + [ -comand + ] - -# 打开最近使用过的文件夹 -comand + shift + f - -# -``` - 查询汇率 ``` @@ -221,6 +185,8 @@ GoodSync:和 windows 平台同步文件 TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) +NewFileMenu:使得可以在访达中新建文件 + ## 4.18.6 brew 的使用 设置国内源 @@ -253,6 +219,77 @@ brew update brew cask install docker ``` +## 4.18.7 访达使用技巧 + +详细请看这篇文章([MacOS实用技巧之Finder(访达)的使用](https://www.jianshu.com/p/3666e6954e8a)),非常好的教程 + +``` +# 快速打开访达:先打开搜索,再打开个人家目录 +打开搜索:command + option(alt) + space +关闭标签页:command + shift + h + +# 返回父级文件夹 +command + ↑ + +# 进入文件夹 +command + ↓ + +# enter +重命名文件夹 + +# 选中所有文件,并将这些文件归档入一个新的文件夹 +右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 + + +# 选择 +点击 -> 拖拽 +如果想要取消选中,就 command + 点击 + +# 打开指定路径(前提访达得是激活状态的窗口) +# 注意在这里,可以使用 tab 补全 +shift + command + g + +# 前进 后退 +command + [ +comand + ] + +# 打开最近使用过的文件夹 +comand + shift + f + +# 显示/隐藏文件 +command + shift + . + +# 查看文件/夹 详情 +command + i + +# 复制文件路径,有两种方法 +# 【第一种】:快捷键 +command + option + c +# 若你使用 alfred ,快捷键会冲突,解决方法:先右键,再 option,选择将 xx 拷贝为路径名称 + +# 第二种:使用服务 +参考 https://sspai.com/post/33422 + +# 快速跳转至第一个文件或最后一个文件 +option + ↑ +option + ↓ + +# mac 中拷贝和复制不一样 +command + c 拷贝 +command + d 复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option +command + v 粘贴 +command + option + v 称动 ,或者使用鼠标拖动 + +# 可以设置搜索的范围 +command + f + +# 新建文件夹 +command + shift + n + +# 关闭访达标签页,如果是最后一个标签页,则关闭访达 +command + w +``` + diff --git a/source/c07/c07_19.md b/source/c07/c07_19.md new file mode 100644 index 0000000..488bdbd --- /dev/null +++ b/source/c07/c07_19.md @@ -0,0 +1,45 @@ +# 7.19 Ansible 使用教程 + +## 1. 环境准备 + +安装 Ansible ,需要 epel 源,所以要先配置一下 + +```shell +$ pwd +/etc/yum.repos.d +  +$ cat aliBase.repo +[aliBase] +name=aliBase +baseurl=https://mirrors.aliyun.com/centos/$releasever/os/$basearch/ +enabled=1 +gpgcheck=1 +gpgkey=https://mirrors.aliyun.com/centos/$releasever/os/$basearch/RPM-GPG-KEY-CentOS-$releasever +  +$ cat aliEpel.repo +[aliEpel] +name=aliEpel +baseurl=https://mirrors.aliyun.com/epel/$releasever\Server/$basearch/ +enabled=1 +gpgcheck=0 +``` + +然后安装 + +```shell +$ yum install ansible +``` + +在管理节点生成密钥(公钥和私钥)对,然后将公钥放到要被 ansible 管理的机器上。 + +```shell +$ ssh-keygen +$ ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.20.20.1 +``` + +运行第一条命令 + +```shell +$ ansible 172.20.20.1 -m ping +``` + diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index 3919e76..b2cd9f3 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -182,7 +182,13 @@ $ git clone https://github.com/golang/lint.git -随便点开一个 go 文件,在你的右下角会提示要你安装一些工具,点击 `Install All` +随便点开一个 go 文件,在你的右下角会提示要你安装一些工具,安装的包有些由于墙的原因,无法下载,为了保证下载顺利,可以设置一下代理。 + +```shell +$ go env -w GOPROXY=https://goproxy.cn,direct +``` + +然后再点击 `Install All` ![](http://image.python-online.cn/20200109210654.png) @@ -190,16 +196,6 @@ $ git clone https://github.com/golang/lint.git ![](http://image.python-online.cn/20200109211543.png) -查看 OUTPUT 会有一些安装失败的信息。 - -![](http://image.python-online.cn/20200109212824.png) - -把这两条单独拿出来执行吧(记住执行的话,要切回 %GOPATH%),先使用 `go get` 下载,再使用 `go install` 安装(若你想安装其他的包,其实也是一样的逻辑)。 - -![](http://image.python-online.cn/20200109213032.png) - - - 安装的 exe 文件会放在 %GOPATH%/bin 下,也就是 `F:\Go-Player\bin` ![](http://image.python-online.cn/20200109213056.png) @@ -236,7 +232,7 @@ set GONOSUMDB= set GOOS=windows set GOPATH=E:\MING-Code\GoPlayer set GOPRIVATE= -set GOPROXY=https://proxy.golang.org,direct +set GOPROXY=https://goproxy.cn,direct set GOROOT=D:\Program Files (x86)\Go-1.13.6 set GOSUMDB=sum.golang.org set GOTMPDIR= @@ -266,27 +262,20 @@ $ go env GOPROXY https://goproxy.cn,direct ``` +以上环境变量很多,这里仅设置下面这两个就足够了 - -其中有几个比较重要的,我这里会讲一下。 - -`GOPATH`: - -`GOROOT`: - -`GOPROXY`:你安装下载包的时候去哪里下载,一条命令即可,更多详情可以查看 [Github · GoProxy](https://github.com/goproxy/goproxy.cn/blob/master/README.zh-CN.md) +- 一个是GO111MODULE 设置为 on,表示使用 go modules 模式 ```shell -$ go env -w GOPROXY=https://goproxy.cn,direct +$ go env -w GO111MODULE=on ``` - - -`GO111MODULE`:on、 off或 auto,必须为小写,不能为为true或false,也不能为1或0。这里推荐设置为on +- 一个是开启代理,防止下载包失败(前面可能你已经设置过) ```shell -$ go env -w GO111MODULE=on +$ go env -w GOPROXY=https://goproxy.cn,direct ``` + ![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_23.md b/source/c09/c09_23.md index b9daa8c..10868ac 100644 --- a/source/c09/c09_23.md +++ b/source/c09/c09_23.md @@ -1,158 +1,130 @@ -# 9.23 学习一些常见的并发模型 +# 9.23 panic 和 recover -本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 Golang 中的 协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 +编程语言一般都会有异常捕获机制,在 Python 中 是使用`raise` 和 `try-except` 语句来实现的异常抛出和异常捕获的。 -你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 +在 Golang 中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 -## 0. 并发与并行 +当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 panic,让程序退出停止运行。 -讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? +## 1. 触发panic -这里我用两个例子来形象描述: +手动触发宕机,是非常简单的一件事,只需要调用 panic 这个内置函数即可,就像这样子 -- **并发**:当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 -- **并行**:你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 +```go +package main -在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 +func main() { + panic("crash") +} +``` -而当你的机器有多个 CPU 核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 +运行后,直接报错宕机 -接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 +```shell +$ go run main.go +go run main.go +panic: crash -但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? +goroutine 1 [running]: +main.main() + E:/Go-Code/main.go:4 +0x40 +exit status 2 +``` -谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C 的活,然后再去做一会 A 的活,就这样不断的切换着,大家都很开心,其乐融融。 +## 2. 捕获 panic -## 1. 并发编程的模型 +发生了异常,有时候就得捕获,就像 Python 中的` except` 一样,那 Golang 中是如何做到的呢? -在计算机的世界里,实现并发通常有几种方式: +这就不得不引出另外一个内建函数 -- `recover`,它可以让程序在发生宕机后起生回生。 -1. 多进程模型:创建新的线程处理请求 -2. 多线程模型:创建新的进程处理请求 -3. 使用线程池:线程/进程创建销毁开销大 -4. I/O 多路复用+单/多线程 +但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效,其他作用域下,它是不工作的。 -## 2. 多进程与多线程 +这是一个简单的例子 -对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ ,一个微信,它们都是一个进程。 +```go +import "fmt" -进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 +func set_data(x int) { + defer func() { + // recover() 可以将捕获到的panic信息打印 + if err := recover(); err != nil { + fmt.Println(err) + } + }() -在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 + // 故意制造数组越界,触发 panic + var arr [10]int + arr[x] = 88 +} -线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 +func main() { + set_data(20) -而进程的切换,相比线程来说,会更加麻烦。 + // 如果能执行到这句,说明panic被捕获了 + // 后续的程序能继续运行 + fmt.Println("everything is ok") +} +``` -因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 +运行后,输出如下 -此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams 等 +```go +$ go run main.go +runtime error: index out of range [20] with length 10 +everything is ok +``` -说了这么多,好像都在说线程优于进程,也不尽然。 +通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 -比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 +## 3. 无法跨协程 -同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 +从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer 。 +但是这个 defer 在多个协程之间是没有效果,在子协程里触发 panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer 函数的。 +来做个实验就知道了 -## 3. I/O多路复用 +```go +import ( + "fmt" + "time" +) -`I/O多路复用` ,英文全称为 `I/O multiplexing`,这个中文翻译和把 socket 翻译成 套接字一样,影响了我对其概念的理解。 +func main() { + // 这个 defer 并不会执行 + defer fmt.Println("in main") + + go func() { + defer println("in goroutine") + panic("") + }() -在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 + time.Sleep(2 * time.Second) +} +``` -但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 +输出如下 -终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O 多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 +``` +in goroutine +panic: -再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 select ,早已不能支撑用户请求了。 +goroutine 6 [running]: +main.main.func1() + E:/Go-Code/main.go:12 +0x7b +created by main.main + E:/Go-Code/main.go:10 +0xbc +exit status 2 +``` -由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select 发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 -select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 -都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 +## 4. 总结一下 -由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 +Golang 异常的抛出与捕获,依赖两个内置函数: -epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: +- panic:抛出异常,使程序崩溃 +- recover:捕获异常,恢复程序或做收尾工作 -- select 和 poll 无差别轮循fd,浪费资源,epool 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 -- select 和 poll 线程不安全,epool 线程安全 -- select 请求连接数的限制,epool 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) -- select 和 pool 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 +revocer 调用后,抛出的 panic 将会在此处终结,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 -虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 epool 出现了, select 就会被淘汰掉。 - -epool 关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool 的性能最好。 - -而 select 关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 - -另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool 是 Linux 所有的,其他平台上没有。 - - IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 - -- 一个线程的IO多路复用,比如 Redis -- 多个线程的IO多路复用,比如 goroutine - -IO多路复用 + 单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 - -## 4. 三种线程模型? - -实际上,goroutine 并非传统意义上的协程。 - -现在主流的线程模型分三种: - -- 内核级线程模型 -- 用户级线程模型 -- 两级线程模型(也称混合型线程模型) - -传统的协程库属于**用户级线程模型**,而 goroutine 和它的 `Go Scheduler` 在底层实现上其实是属于**两级线程模型**,因此,有时候为了方便理解可以简单把 goroutine 类比成协程,但心里一定要有个清晰的认知 — goroutine并不等同于协程。 - -关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 - - - -## 5. 协程的优势在哪? - -协程,可以认为是轻量级的“线程”。 - -对比线程,有如下几个明显的优势。 - -1. 协程的调度由 Go 的 runtime 管理,协程切换不需要经由操作系统内核,开销较小。 -2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 - -同时,在 Golang 里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 async def 而言),运行时只需要一个关键字 `go`,就可以轻松创建一个协程。 - - - -使用 -race 来检测数据 访问的冲突 - - - -协程什么时候会切换 - -1. I/O,select -2. channel -3. 等待锁 -4. 函数调用(有时 -5. runtime.Gosched() - - - - - -## 参考阅读: - -https://www.cnblogs.com/aspirant/p/9166944.html - -https://blog.csdn.net/snoweaglelord/article/details/99681179 - -https://www.jianshu.com/p/dfd940e7fca2 - -https://studygolang.com/articles/13344 - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_24.md b/source/c09/c09_24.md index 210ff4b..36ad998 100644 --- a/source/c09/c09_24.md +++ b/source/c09/c09_24.md @@ -1,134 +1,242 @@ -# 9.24 panic 和 recover +# 9.24 超级详细的Go语言包管理方案演变及 go mod 入门 -编程语言一般都会有异常捕获机制,在 Python 中 是使用`raise` 和 `try-except` 语句来实现的异常抛出和异常捕获的。 +在以前,Go 语言的的包依赖管理一直都被大家所诟病,Go官方也在一直在努力为开发者提供更方便易用的包管理方案,从最初的 GOPATH 到 GO VENDOR,再到最新的 GO Modules,虽然走了不少的弯路,但最终还是拿出了 Go Modules 这样像样的解决方案。 -在 Golang 中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 +目前最主流的包依赖管理方式是使用官方推荐的 Go Modules ,这不前段时间 Go 1.14 版本发布,官方正式放话,强烈推荐你使用 Go Modules,并且有自信可以用于生产中。 -当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 panic,让程序退出停止运行。 +本文会大篇幅的讲解 Go Modules 的使用,但是在那之前,我仍然会简要介绍一下前两个解决方案 GOPATH 和 go vendor 到底是怎么回事?我认为这是有必要的,因为只有了解它的发展历程,才能知道 Go Modules 的到来是有多么的不容易,多么的意义非凡。 -## 1. 触发panic +## 1. 最古老的 GOPATH -手动触发宕机,是非常简单的一件事,只需要调用 panic 这个内置函数即可,就像这样子 +GOPATH 应该很多人都很眼熟了,之前在配置环境的时候,都配置过吧? -```go -package main +你可以将其理解为工作目录,在这个工作目录下,通常有如下的目录结构 -func main() { - panic("crash") -} -``` +![](http://image.python-online.cn/image-20200311220825614.png) + +每个目录存放的文件,都不相同 + +- bin:存放编译后生成的二进制可执行文件 +- pkg:存放编译后生成的 `.a` 文件 +- src:存放项目的源代码,可以是你自己写的代码,也可以是你 go get 下载的包 + +将你的包或者别人的包全部放在 `$GOPATH/src` 目录下进行管理的方式,我们称之为 GOPATH 模式。 + +在这个模式下,使用 go install 时,生成的可执行文件会放在 `$GOPATH/bin` 下 + +![](http://image.python-online.cn/image-20200312221011685.png) + +如果你安装的是一个库,则会生成 `.a` 文件到 `$GOPATH/pkg` 下对应的平台目录中(由 GOOS 和 GOARCH 组合而成),生成 `.a` 为后缀的文件。 + +![](http://image.python-online.cn/image-20200312221141028.png) + +GOOS,表示的是目标操作系统,有 darwin(Mac),linux,windows,android,netbsd,openbsd,solaris,plan9 等 + +而 GOARCH,表示目标架构,常见的有 arm,amd64 等 + +这两个都是 go env 里的变量,你可以通过 `go env 变量名` 进行查看 + +![](http://image.python-online.cn/image-20200314132614248.png) + +至此,你可能不会觉得上面的方案会产生什么样的问题,直到你开始真正使用 GOPATH 去开发程序,就不得不开始面临各种各样的问题,其中最严重的就是版本管理问题,因为 GOPATH 根本没有版本的概念。 + +以下几点是你使用 GOPATH 一定会碰到的问题: + +- 你无法在你的项目中,使用指定版本的包,因为不同版本的包的导入方法也都一样 +- 其他人运行你的开发的程序时,无法保证他下载的包版本是你所期望的版本,当对方使用了其他版本,有可能导致程序无法正常运行 +- 在本地,一个包只能保留一个版本,意味着你在本地开发的所有项目,都得用同一个版本的包,这几乎是不可能的。 + + + +## 2. go vendor 模式的过渡 + +为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 开始支持 vendor 。 -运行后,直接报错宕机 +以前使用 GOPATH 的时候,所有的项目都共享一个 GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH 模式下只能有一个版本的第三方库。 + +解决的思路就是,在每个项目下都创建一个 vendor 目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5 的 Go 在你设置了开启 `GO15VENDOREXPERIMENT=1` (注:这个变量在 v1.6 版本默认为1,但是在 v1.7 后,已去掉该环境变量,默认开启 `vendor` 特性,无需你手动设置)后,会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。 + +其搜索包的优先级顺序,由高到低是这样的 + +- 当前包下的 vendor 目录 +- 向上级目录查找,直到找到 src 下的 vendor 目录 +- 在 GOROOT 目录下查找 +- 在 GOPATH 下面查找依赖包 + +虽然这个方案解决了一些问题,但是解决得并不完美。 + +- 如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 + +- 并且如果要分享开源你的项目,你需要将你的所有的依赖包悉数上传,别人使用的时候,除了你的项目源码外,还有所有的依赖包全部下载下来,才能保证别人使用的时候,不会因为版本问题导致项目不能如你预期那样正常运行。 + +这些看似不是问题的问题,会给我们的开发使用过程变得非常难受,虽然我是初学者,还未使用过 go vendor,但能有很明显的预感,这个方案照样会另我崩溃。 + +## 3. go mod 的横空出世 + +go modules 在 v1.11 版本正式推出,在最新发布的 v1.14 版本中,官方正式发话,称其已经足够成熟,可以应用于生产上。 + +从 v1.11 开始,`go env` 多了个环境变量: `GO111MODULE` ,这里的 111,其实就是 v1.11 的象征标志, go 里好像很喜欢这样的命名方式,比如当初 vendor 出现的时候,也多了个 `GO15VENDOREXPERIMENT `环境变量,其中 15,表示的vendor 是在 v1.5 时才诞生的。 + +`GO111MODULE` 是一个开关,通过它可以开启或关闭 go mod 模式。 + +它有三个可选值:`off`、`on`、`auto`,默认值是`auto`。 + +1. `GO111MODULE=off`禁用模块支持,编译时会从`GOPATH`和`vendor`文件夹中查找包。 +2. `GO111MODULE=on`启用模块支持,编译时会忽略`GOPATH`和`vendor`文件夹,只根据 `go.mod`下载依赖。 +3. `GO111MODULE=auto`,当项目在`$GOPATH/src `外且项目根目录有`go.mod`文件时,自动开启模块支持。 + +go mod 出现后, GOPATH(肯定没人使用了) 和 GOVENDOR 将会且正在被逐步淘汰,但是若你的项目仍然要使用那些即将过时的包依赖管理方案,请注意将 GO111MODULE 置为 off。 + +具体怎么设置呢?可以使用 go env 的命令,如我要开启 go mod ,就使用这条命令 ```shell -$ go run main.go -go run main.go -panic: crash - -goroutine 1 [running]: -main.main() - E:/MING-Code/GoPlayer/src/imooc.com/ccmouse/learngo/main.go:4 +0x40 -exit status 2 +$ go env -w GO111MODULE="on" ``` -## 2. 捕获 panic -发生了异常,有时候就得捕获,就像 Python 中的` except` 一样,那 Golang 中是如何做到的呢? -这就不得不引出另外一个内建函数 -- `recover`,它可以让程序在发生宕机后起生回生。 +## 4. go mod 依赖的管理 -但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效,其他作用域下,它是不工作的。 +接下来,来演示一下 go modules 是如何来管理包依赖的。 -这是一个简单的例子 +go mod 不再依靠 $GOPATH,使得它可以脱离 GOPATH 来创建项目,于是我们在家目录下创建一个 go_test 的目录,用来创建我的项目,详细操作如下: -```go -import "fmt" +![](http://image.python-online.cn/image-20200314000227914.png) -func set_data(x int) { - defer func() { - // recover() 可以将捕获到的panic信息打印 - if err := recover(); err != nil { - fmt.Println(err) - } - }() +接下来,进入项目目录,执行如下命令进行 go modules 的初始化 - // 故意制造数组越界,触发 panic - var arr [10]int - arr[x] = 88 -} +![](http://image.python-online.cn/image-20200314000940825.png) -func main() { - set_data(20) +接下来很重要的一点,我们要看看 go install 把下载的包安装到哪里了? - // 如果能执行到这句,说明panic被捕获了 - // 后续的程序能继续运行 - fmt.Println("everything is ok") -} -``` +![](http://image.python-online.cn/image-20200314001426817.png) + +上面我们观察到,在使用 go modules 模式后,项目目录下会多生成两个文件也就是 `go.mod` 和 `go.sum` 。 + +这两个文件是 go modules 的核心所在,这里不得不好好介绍一下。 -运行后,输出如下 +![](http://image.python-online.cn/image-20200314001708640.png) + +### go.mod 文件 + +go.mod 的内容比较容易理解 + +- 第一行:模块的引用路径 +- 第二行:项目使用的 go 版本 +- 第三行:项目所需的直接依赖包及其版本 + +在实际应用上,你会看见更复杂的 go.mod 文件,比如下面这样 -```go -$ go run main.go -runtime error: index out of range [20] with length 10 -everything is ok ``` +module github.com/BingmingWong/module-test +go 1.14 +require ( + example.com/apple v0.1.2 + example.com/banana v1.2.3 + example.com/banana/v2 v2.3.4 + example.com/pear // indirect + example.com/strawberry // incompatible +) -通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 +exclude example.com/banana v1.2.4 +replace( + golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac + golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d + golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 +) +``` +主要是多出了两个 flag: +- `exclude`: 忽略指定版本的依赖包 +- `replace`:由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 -## 3. 无法跨协程 +### go.sum 文件 -从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer 。 +反观 go.sum 文件,就比较复杂了,密密麻麻的。 -但是这个 defer 在多个协程之间是没有效果,在子协程里触发 panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer 函数的。 +可以看到,内容虽然多,但是也不难理解 -来做个实验就知道了 +每一行都是由 `模块路径`,`模块版本`,`哈希检验值` 组成,其中哈希检验值是用来保证当前缓存的模块不会被篡改。hash 是以`h1:`开头的字符串,表示生成checksum的算法是第一版的hash算法(sha256)。 -```go -import ( - "fmt" - "time" -) +值得注意的是,为什么有的包只有一行 -func main() { - // 这个 defer 并不会执行 - defer fmt.Println("in main") - - go func() { - defer println("in goroutine") - panic("") - }() - - time.Sleep(2 * time.Second) -} +``` + /go.mod ``` -输出如下 +而有的包却有两行呢 ``` -in goroutine -panic: - -goroutine 6 [running]: -main.main.func1() - E:/MING-Code/main.go:12 +0x7b -created by main.main - E:/MING-Code/main.go:10 +0xbc -exit status 2 + + /go.mod ``` +那些有两行的包,区别就在于 hash 值不一行,一个是 `h1:hash`,一个是 `go.mod h1:hash` + +而 `h1:hash` 和 `go.mod h1:hash`两者,要不就是同时存在,要不就是只存在 `go.mod h1:hash`。那什么情况下会不存在 `h1:hash` 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的` h1 hash`,就会出现不存在 `h1 hash`,只存在 `go.mod h1:hash` 的情况。[引用自 3] + +go.mod 和 go.sum 是 go modules 版本管理的指导性文件,因此 go.mod 和 go.sum 文件都应该提交到你的 Git 仓库中去,避免其他人使用你写项目时,重新生成的go.mod 和 go.sum 与你开发的基准版本的不一致。 + +## 5. go mod 命令的使用 + + +- `go mod init`:初始化go mod, 生成go.mod文件,后可接参数指定 module 名,上面已经演示过。 + +- `go mod download`:手动触发下载依赖包到本地cache(默认为`$GOPATH/pkg/mod`目录) + +- `go mod graph`: 打印项目的模块依赖结构 + +![](http://image.python-online.cn/image-20200314003442400.png) + +- `go mod tidy` :添加缺少的包,且删除无用的包 + +- `go mod verify` :校验模块是否被篡改过 + +- `go mod why`: 查看为什么需要依赖 + +- `go mod vendor` :导出项目所有依赖到vendor下 + +![](http://image.python-online.cn/image-20200314003913527.png) + + - `go mod edit` :编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 -require=golang.org/x/text 添加依赖,接 -droprequire=golang.org/x/text 删除依赖,详情可参考 `go help mod edit ` + +![](http://image.python-online.cn/image-20200314004643487.png) + +- `go list -m -json all`:以 json 的方式打印依赖详情 + +![](http://image.python-online.cn/image-20200314005924877.png) + + + + +如何给项目添加依赖(写进 go.mod)呢? + +有两种方法: + +- 你只要在项目中有 import,然后 go build 就会 go module 就会自动下载并添加。 +- 自己手工使用 go get 下载安装后,会自动写入 go.mod 。 + +![](http://image.python-online.cn/image-20200314005217447.png) + + + +## 7. 总结写在最后 + +如果让我用一段话来评价 GOPATH 和 go vendor,我会说 +GOPATH 做为 Golang 的第一个包管理模式,只能保证你能用,但不保证好用,而 go vendor 解决了 GOPATH 忽视包版的本管理,保证好用,但是还不够好用,直到 go mod 的推出后,才使 Golang 包的依赖管理有了一个能让 Gopher 都统一比较满意的方案,达到了能用且好用的标准。 -## 总结一下 +如果是刚开始学习 Golang ,那么 GOPATH 和 go vendor 可以做适当了解,不必深入研究,除非你要接手的项目由于一些历史原因仍然在使用 go vender 械管理,除此之外,任何 Gopher 应该从此刻就投入 go modules 的怀抱。 -Golang 异常的抛出与捕获,依赖两个内置函数: +以上是我在这几天的学习总结,希望对还未入门阶段的你,有所帮助。另外,本篇文章如有写得不对的,请后台批评指正,以免误导其他朋友,非常感谢。 -- panic:抛出异常,使程序崩溃 -- recover:捕获异常,恢复程序 +## 8. 推荐参考文章 -revocer 调用后,抛出的 panic 将会在此外终结,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 +- [Go语言之依赖管理](https://www.cnblogs.com/Dr-wei/p/11742253.html) +- [Go 包依赖管理工具 —— govendor](https://shockerli.net/post/go-package-manage-tool-govendor/) +- [Go Modules 终极入门](https://mp.weixin.qq.com/s/fNMXfpBhBC3UWTbYCnwIMg) (强烈推荐煎鱼大佬的文章) +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) diff --git a/source/c09/c09_25.md b/source/c09/c09_25.md deleted file mode 100644 index 812e6a4..0000000 --- a/source/c09/c09_25.md +++ /dev/null @@ -1,182 +0,0 @@ -# 9.25 Go语言的包依赖管理 - -在以前,Go 语言的的包依赖管理一直都被大家所诟病,但最近几年,这个窘境开始得到缓解。 - -现在最主流的包依赖管理方式是使用官方推荐的 go module 的方式,如果你和我一样是刚开始学习Go语言的,建议也了解一下Go语言包依赖管理方案,是如何一步一步发展到今天的。 - -简单来说,Go语言的包依赖管理方案,可以分为三个阶段。 - -## **第一阶段**:使用最古老的 GOPATH 进行管理 - -使用 GOPATH 的话,你需要将你所有的第三方库都下载到本地,这本身没有什么问题,其他语言也都是这么做的,问题就在于,在本地只能保存一个版本的第三方库,如果你的机器上有多个项目,而这些项目是基于不同版本的库进行开发的,那就尴尬了。 - - - -## **第二阶段**:使用 GOVENDOR 解决方案 - -为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 开始支持 vendor 。 - -以前使用 GOPATH 的时候,所有的项目都共享一个 GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH 下只能有一个版本的第三方库。 - -解决的思路就是,在每个项目下都创建一个 vendor 目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5+ 的Go 会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。如果在 vendor 目录下没有找到,才会去 `$GOAPTH/src` 中去查找 - -在这个阶段,也催生出了各种第三方的管理包依赖插件:godep,dep,glide - -使用 godep 的开发流程是这样的: - -1. 先保证程序在本地能够正常编译 -2. 执行`godep save`保存当前项目的所有第三方依赖的版本信息和代码到 `Godeps/Godeps.json`文件中 -3. 提交Godeps目录和vender目录到代码库。 -4. 如果要更新依赖的版本,可以直接修改`Godeps.json`文件中的对应项 - -虽然这个方案解决了一些问题,但是解决得并不完美。 - -如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 - - - -## **第三阶段**:使用 GO MODULE 版本管理工具 - -go module 在 v1.11 版本推出,并在 v1.13版本中,成为官方默认的包依赖管理工具。 - -v1.11 开始,`go env` 多了个环境变量: `GO111MODULE`,这是一个开关,通过它可以开启或关闭模块支持,它有三个可选值:`off`、`on`、`auto`,默认值是`auto`。 - -1. `GO111MODULE=off`禁用模块支持,编译时会从`GOPATH`和`vendor`文件夹中查找包。 -2. `GO111MODULE=on`启用模块支持,编译时会忽略`GOPATH`和`vendor`文件夹,只根据 `go.mod`下载依赖。 -3. `GO111MODULE=auto`,当项目在`$GOPATH/src`外且项目根目录有`go.mod`文件时,开启模块支持。 - - - -go mod 出现后, GOPATH 和 GOVENDOR 将被逐步淘汰,但是若你的项目仍然要使用那些 out 的包依赖管理方案,需要注意将 GO111MODULE 置为 off。 - - - -接下来,介绍一下,如何使用 go module 来管理依赖。 - -使用 go module 管理依赖后会在项目根目录下生成两个文件`go.mod`和`go.sum`。 - -`go.mod` 文件记录了项目所有的依赖信息,其结构大致如下: - -``` -module github.com/Q1mi/studygo/blogger - -go 1.12 - -require ( - github.com/DeanThompson/ginpprof v0.0.0-20190408063150-3be636683586 - github.com/gin-gonic/gin v1.4.0 - github.com/go-sql-driver/mysql v1.4.1 - github.com/jmoiron/sqlx v1.2.0 - github.com/satori/go.uuid v1.2.0 - google.golang.org/appengine v1.6.1 // indirect -) -``` - -其中, - -- `module`用来定义包名 -- `require`用来定义依赖包及版本 -- `indirect`表示间接引用 - -而 `go.sum` 记录该项目中每个依赖库的版本和哈希值。 - - - -使用 go module 需熟悉 go mod 的命令 - -``` -go mod init 初始化当前文件夹, 创建go.mod文件,后可接参数指定 module 名 -go mod graph 打印模块依赖图 -go mod tidy 增加缺少的module,删除无用的module -go mod vendor 将依赖复制到vendor下 -go mod verify 校验依赖 -go mod why 解释为什么需要依赖 -go mod download 下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录) - -go mod edit 编辑go.mod文件 - 接 -fmt 参数格式化 go.mod 文件 - 接 -require=golang.org/x/text 添加依赖 - 接 -droprequire=golang.org/x/text 删除依赖 - 更加用法,参看 go help mod edit -``` - - - -如何给项目添加依赖(下载包并将依赖写进go.mod文件)? - -有两种方法: - -- 你只要在项目中有 import,然后 go build 就会 go module 就会自动下载并添加。 - -- 自己手工使用 go get 下载,支持语义化版本号 - -```shell -# 拉取最新 -go get github.com/foo - -# 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) -go get -u github.com/foo - -# 升级到最新的修订版本 -go get -u=patch github.com/foo - -# 指定版本 -go get github.com/foo@v1.2.3 - -# 指定分支 -go get github.com/foo@master - -# 指定git提交的hash值 -go get github.com/foo@e3702bed2 - -# 指定版本 -go get github.com/foo@v1.11.0 -``` - - - -使用以上方式添加完依赖后,在 go.mod 文件里会有你所依赖的包及其版本。 - - - -如果项目下已经有这个 go.mod 文件,但是包还没拉取,如何 触发下载呢?两种方法 - -- 可以执行命令 `go build ./...`,go module 会自动下载 -- 使用 `go mod download` ,手动下载 - -如果你连这个 go.mod 文件都丢失了,那怎么生成?两种方法 - -- 使用 Goland 的话,可以点击 create go.mod 文件来生成。 -- 也可以在项目目录下执行这条命令 - -```shell -$ go mod init -``` - - - -由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 - -``` -replace ( - golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac - golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d - golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 -) -``` - - - -以上几种解决方案,不同之处就在于它们的依赖包的搜索路径优先级不同 - -- 使用 go mod,只在 `$GOPATH/pkg/mod` 查找依赖包(GO111MODULE=on) -- 使用 GOVENDOR,优先在 vendor目录中查找,然后才去 `$GOROOT/src` 和 `$GOPATH/src`查找 -- 使用 GOPATH,先去`$GOROOT/src` 查找 ,找不到再去 `$GOPATH/src` 中查找 - - - -## 参考文章: - -- [Go语言之依赖管理](https://www.cnblogs.com/Dr-wei/p/11742253.html) - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) diff --git a/source/c09/c09_27.md b/source/c09/c09_27.md index 916a4e2..872c19b 100644 --- a/source/c09/c09_27.md +++ b/source/c09/c09_27.md @@ -1,7 +1,197 @@ # 9.27 go 命令详解 +## 1. 环境变量 +`go env` -go build ./... 不会生成可执行文件到 ${GOPATH}/bin ,只要能编译过就行 +![仅截取部分内容](/Users/MING/Library/Application Support/typora-user-images/image-20200311221418584.png) -go install ./... 会生成可执行文件到 ${GOPATH}/bin \ No newline at end of file + + +## 2. 执行 Go 程序 + +当前热门的编程语言 Python ,可以不用编译成 二进制文件,就可以直接运行。 + +但 Go 语言程序的执行,必须得先编译再执行。通常来说有如下两种方法 + +1. 先使用 go build 编译成二进制文件,再执行这个二进制文件 + + ![image-20200313222620374](/Users/MING/Library/Application Support/typora-user-images/image-20200313222620374.png) + +2. 使用 go run “直接”运行,这个命令还是会去编译,但是不会在当前目录下生成二进制文件,而是编译成临时文件后直接运行。 + + ![image-20200313222710998](/Users/MING/Library/Application Support/typora-user-images/image-20200313222710998.png) + +## 3. 编译文件 + +将 `.go` 文件编译成可执行文件,可以使用 `go build` + +如下图所示,helloworld 文件夹下,包含两个 `.go` 文件,它们都归属于同一个包。 + +当使用 `go build` 可指定包里所有的文件,就你下面这样,默认会以第一个文件(main.go)名生成可执行文件(main)。 + +![image-20200312201759541](/Users/MING/Library/Application Support/typora-user-images/image-20200312201759541.png) + +当然,你也可以不指定,此时生成的可执行文件是以 文件夹名命名 + +![image-20200312202032363](/Users/MING/Library/Application Support/typora-user-images/image-20200312202032363.png) + +当然你也可以手动指定这个可执行文件名 + +![image-20200312202520902](/Users/MING/Library/Application Support/typora-user-images/image-20200312202520902.png) + +以上是编译单个文件,当然也可以编译多个文件 + + + +## 4. 清除编译文件 + +使用 go install 或 go install 有可能会生成很多的文件,如可执行文件,归档文件等,它们的后缀名非常多,有 `.exe`, `.a`, `.test`,`.o`,`.so`,`.5` ,`.6`,`.8`,如果要手动一个一个去清理他们,可以说是相当麻烦的,这里你可以通过使用 `go clean` 一键清理。 + +![image-20200313224148510](/Users/MING/Library/Application Support/typora-user-images/image-20200313224148510.png) + +实际开发中`go clean`命令使用的可能不是很多,一般都是利用`go clean`命令清除编译文件,然后再将源码递交到 github 上,方便对于源码的管理。 + +go clean 有不少的参数: + +- `-i `:清除关联的安装的包和可运行文件,也就是通过`go install`安装的文件; +- `-n`: 把需要执行的清除命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的; +- `-r`: 循环的清除在 import 中引入的包; +- `-x`: 打印出来执行的详细命令,其实就是 -n 打印的执行版本; +- `-cache`: 删除所有`go build`命令的缓存 +- `-testcache`: 删除当前包所有的测试结果 + +## 4. 下载代码包 + +在 Golang 中,除了可以从官方网站(golang.org)下载包之外,还可以从一些代码仓库中下载,诸如 github.com,bitbucket.org 这样的代码托管网站。 + +`go get` 这条命令,你以后会最经常用到,它可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。整个过程就像安装一个 App 一样简单。 + +这个命令可以动态获取远程代码包,目前支持的有 BitBucket、GitHub、Google Code 和 Launchpad。在使用 go get 命令前,需要安装与远程包匹配的代码管理工具,如 Git、SVN等。 + +`go get` 会根据域名的不同,使用不同的工具去拉取代码包,具体可参考下图 + +![image-20200312203244402](/Users/MING/Library/Application Support/typora-user-images/image-20200312203244402.png) + +下载和安装,原本是两个动作,但使用 `go get` 后,它默认会将下载(源码包)和安装(go install)合并起来,当然你也可以通过参数指定将拆散它们。 + +在终端执行 `go help get`,会弹出 `go get ` 的帮助文档,我这里汉化总结一下,来帮助大家学习。 + +``` +go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages] +``` + +其中几个参数详解如下 + +- `-u`: + + 用于下载指定的路径包及其依赖包,默认情况下,不会下载本地已经存在的,只会下载本地不存在的代码包。就是口中常说的更新包 比如:go get -u github.com/jinzhu/gorm。会把最新的 gorm 包下载到你本地 + +- `-d`: + + 让命令程序只执行下载动作,而不执行安装动作。 + +- `-t` + + 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包 + +- `-fix` + + 命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。比如,我的代码是用1.7 开发的,现在go 版本已经是1.13 了,有些包已经发生了变化,那么我们在使用go get命令的时候可以加入-fix标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.7版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。 + +- `-v` + + 打印出那些下载的代码包的名字 + +- `-f` + + 仅在使用-u标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了 + +- `-x` + + 打印出整个过程使用了哪些命令 + +- `-insecure` + 允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。(记得 使用工具 git 时,有个版本就是 http 升级为了https) + + + +参数有点多,咱一个一个来。 + +指定 `-d`,只下载源码包而不进行安装 + +![image-20200312204335687](/Users/MING/Library/Application Support/typora-user-images/image-20200312204335687.png) + +由于此时,我们已经下载了 logging 包,当你再次执行 go get 时,并不会重复下载,只有当你指定 `-u` 时,不管你需不需要更新,都会触发重新下载强制更新。 + +![image-20200312204746007](/Users/MING/Library/Application Support/typora-user-images/image-20200312204746007.png) + +如果你想看,下载这个过程用到了哪几个命令,可以指定 `-x` 参数 + +![image-20200312205001161](/Users/MING/Library/Application Support/typora-user-images/image-20200312205001161.png) + +最后,你可能想说,为什么 golang 里的包含这么长,好难记呀,其实这个路径是有讲究的 + +![image-20200312210557326](/Users/MING/Library/Application Support/typora-user-images/image-20200312210557326.png) + + + +这样不同的人开发的包即使使用同一个名,也不会冲突了。 + +下载的包,可能有不同的版本,如何指定版本下载呢? + +```shell +# 拉取最新 +go get github.com/foo + +# 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) +go get -u github.com/foo + +# 升级到最新的修订版本 +go get -u=patch github.com/foo + +# 指定版本,若存在tag,则代行使用 +go get github.com/foo@v1.2.3 + +# 指定分支 +go get github.com/foo@master + +# 指定git提交的hash值 +go get github.com/foo@e3702bed2 +``` + + + +## 6. 安装代码包 + +`go install` 这个命令,如果你安装的是一个可执行文件(包名是 main),它会生成可执行文件到 bin 目录下。这点和 `go build` 很相似,不同的是,`go build` 编译生成的可执行文件放在当前目录,而 `go install` 会将可执行文件统一放至 `$GOPATH/bin` 目录下。 + +![image-20200312221011685](/Users/MING/Library/Application Support/typora-user-images/image-20200312221011685.png) + +如果你安装的是一个库,它会将这个库安装到 pkg 目录下,生成 `.a` 为后缀的文件。 + +![image-20200312221141028](/Users/MING/Library/Application Support/typora-user-images/image-20200312221141028.png) + + + +## 7. 格式化 go 文件 + +Go语言的开发团队制定了统一的官方代码风格,并且推出了 gofmt 工具(gofmt 或 go fmt)来帮助开发者格式化他们的代码到统一的风格。 + +gofmt 是一个 cli 程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有 .go 文件,如果不传参数,会格式化当前目录下的所有 .go 文件。 + +http://c.biancheng.net/view/4441.html + + + + + + + +## 参考文章 + +https://mp.weixin.qq.com/s/fNMXfpBhBC3UWTbYCnwIMg + +https://studygolang.com/articles/25658 + +https://juejin.im/post/5d0b865c6fb9a07f050a6f45 \ No newline at end of file diff --git a/source/c09/c09_29.md b/source/c09/c09_29.md new file mode 100644 index 0000000..777861c --- /dev/null +++ b/source/c09/c09_29.md @@ -0,0 +1,158 @@ +# 9.29 学习一些常见的并发模型 + +本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 Golang 中的 协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 + +你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 + +## 0. 并发与并行 + +讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? + +这里我用两个例子来形象描述: + +- **并发**:当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 +- **并行**:你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 + +在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 + +而当你的机器有多个 CPU 核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 + +接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 + +但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? + +谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C 的活,然后再去做一会 A 的活,就这样不断的切换着,大家都很开心,其乐融融。 + +## 1. 并发编程的模型 + +在计算机的世界里,实现并发通常有几种方式: + +1. 多进程模型:创建新的线程处理请求 +2. 多线程模型:创建新的进程处理请求 +3. 使用线程池:线程/进程创建销毁开销大 +4. I/O 多路复用+单/多线程 + +## 2. 多进程与多线程 + +对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ ,一个微信,它们都是一个进程。 + +进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 + +在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 + +线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 + +而进程的切换,相比线程来说,会更加麻烦。 + +因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 + +此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams 等 + +说了这么多,好像都在说线程优于进程,也不尽然。 + +比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 + +同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 + + + +## 3. I/O多路复用 + +`I/O多路复用` ,英文全称为 `I/O multiplexing`,这个中文翻译和把 socket 翻译成 套接字一样,影响了我对其概念的理解。 + +在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 + +但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 + +终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O 多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 + +再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 select ,早已不能支撑用户请求了。 + +由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select 发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 + +select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 + +都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 + +由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 + +epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: + +- select 和 poll 无差别轮循fd,浪费资源,epool 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 +- select 和 poll 线程不安全,epool 线程安全 +- select 请求连接数的限制,epool 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) +- select 和 pool 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 + +虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 epool 出现了, select 就会被淘汰掉。 + +epool 关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool 的性能最好。 + +而 select 关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 + +另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool 是 Linux 所有的,其他平台上没有。 + + IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 + +- 一个线程的IO多路复用,比如 Redis +- 多个线程的IO多路复用,比如 goroutine + +IO多路复用 + 单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 + +## 4. 三种线程模型? + +实际上,goroutine 并非传统意义上的协程。 + +现在主流的线程模型分三种: + +- 内核级线程模型 +- 用户级线程模型 +- 两级线程模型(也称混合型线程模型) + +传统的协程库属于**用户级线程模型**,而 goroutine 和它的 `Go Scheduler` 在底层实现上其实是属于**两级线程模型**,因此,有时候为了方便理解可以简单把 goroutine 类比成协程,但心里一定要有个清晰的认知 — goroutine并不等同于协程。 + +关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 + + + +## 5. 协程的优势在哪? + +协程,可以认为是轻量级的“线程”。 + +对比线程,有如下几个明显的优势。 + +1. 协程的调度由 Go 的 runtime 管理,协程切换不需要经由操作系统内核,开销较小。 +2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 + +同时,在 Golang 里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 async def 而言),运行时只需要一个关键字 `go`,就可以轻松创建一个协程。 + + + +使用 -race 来检测数据 访问的冲突 + + + +协程什么时候会切换 + +1. I/O,select +2. channel +3. 等待锁 +4. 函数调用(有时 +5. runtime.Gosched() + + + + + +## 参考阅读: + +https://www.cnblogs.com/aspirant/p/9166944.html + +https://blog.csdn.net/snoweaglelord/article/details/99681179 + +https://www.jianshu.com/p/dfd940e7fca2 + +https://studygolang.com/articles/13344 + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/go_mp_index.md b/source/go_mp_index.md new file mode 100644 index 0000000..f620d0d --- /dev/null +++ b/source/go_mp_index.md @@ -0,0 +1,60 @@ +# Go编程时光 - 学习索引 + +为了方便读者们能从本号真正学到一些有用的内容,我将 《Go编程时光》干货文章进行了整理,方便你学习。 + +### 系列导读: + +[01. 开发环境的搭建(Goland & VS Code)](http://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483659&idx=1&sn=a5c0836d8d3f2209979b28c7e93aca26&chksm=fc355b60cb42d2767ea13bdb53216be2aad6e4c06c2d0be49f1531a0651434d544b6558791ae&token=243502240&lang=zh_CN#rd) + +[02. 学习五种变量创建的方法 ](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483669&idx=2&sn=e70a1400c094e981f15b8da552bd8fbf&chksm=fc355b7ecb42d26824985163a3ef0c3567134975637c4efc42161751f54ab10343b485b36e23#rd) + +[03. 详解数据类型:整形与浮点型](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483669&idx=1&sn=9d8b29ed467195e4d02759fc136a2544&chksm=fc355b7ecb42d2686dbd5fa09f467439716939ea08917af2eb0ff9bbcc4f7b8479548c451710#rd) + +[04. 详解数据类型:byte、rune与string](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483673&idx=1&sn=09cc98b3f0b0e89e28c40a5f096c0d73&chksm=fc355b72cb42d26426bd4ae986ea46284b89d3bdf78465686a799684ddfc5f48f128382cc1ae&token=243502240&lang=zh_CN#rd) + +[05. 详解数据类型:数组与切片](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483677&idx=1&sn=99556dc39a349fac7c9ba2e39e78336f&chksm=fc355b76cb42d260a063c73cb6adffcd23613c131a8fad8d0049dda3378ebd6d4f444c53e540&token=243502240&lang=zh_CN#rd) + +[06. 详解数据类型:字典与布尔类型](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483696&idx=1&sn=44a7ace3a7a06c92f398ef4e35ab6dd1&chksm=fc355b5bcb42d24d6aded290ea3b5601aa8c89e5baf988c83e20d63b7ddd8c9cc1b5a48d1a31&token=243502240&lang=zh_CN#rd) + +[07. 详解数据类型:指针](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483697&idx=1&sn=e6612228a8d2df948069242269466263&chksm=fc355b5acb42d24caf65d1d5863c86c51b3faf3fa69b0b04546c50336544c20c9e8e2ca7165a&token=243502240&lang=zh_CN#rd) + +[08. 面向对象编程:结构体与继承](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483698&idx=1&sn=d12e9c0d5ee8bec8ba8cf1cd319dd783&chksm=fc355b59cb42d24ff61dd30dd5e68875170bd3953fa2cfc15a4b67c272688324cc6bc9525c74&token=243502240&lang=zh_CN#rd) + +[09. 一篇文章理解 Go 里的函数](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483702&idx=1&sn=58ed79a076381b14dd1b387783dd145b&chksm=fc355b5dcb42d24b9cee52c2b912d357e3f500e90111b68b63484d5dc8e127b2114530fb3880&token=243502240&lang=zh_CN#rd) + +[10. Go语言流程控制:if-else 条件语句](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483706&idx=1&sn=f7697b9ba8fbb1ed06c9ed9da64a87a1&chksm=fc355b51cb42d247e11263580fd5917409c1e6066ef297b6bb1ba10666c0d8eae6245c713239&token=243502240&lang=zh_CN#rd) + +[11. Go语言流程控制:switch-case 选择语句](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483710&idx=1&sn=56b4c4e0e6d12f9ba650e0d0aeb6ce70&chksm=fc355b55cb42d243b9f694b017ea57de8e843fbd46e13b002cf44d251fefc446ed49bb842e26&token=243502240&lang=zh_CN#rd) + +[12. Go语言流程控制:for 循环语句](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483714&idx=1&sn=07e2838aade6d5c21e36ec078c26628c&chksm=fc355b29cb42d23fcc4b5d4b8d99646e88a63fc29842092b67e7d2c0c6f120c782f2a9642fc5&token=243502240&lang=zh_CN#rd) + +[13. Go语言流程控制:goto 无条件跳转](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483718&idx=1&sn=63cf01a472c19f9e91a200fd6edd297a&chksm=fc355b2dcb42d23b83fe9792a92e08d19d0186dec443f53a0d342046e51e6a852b6da56db08d&token=243502240&lang=zh_CN#rd) + +[14. Go语言流程控制:defer 延迟调用](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483722&idx=1&sn=90ac47d68a5b0c3c443006d272671234&chksm=fc355b21cb42d237ad329f72670a3c6828862146d11e56c424bd0e26bb26de8c7a7895fe8de8&token=243502240&lang=zh_CN#rd) + +[15. 面向对象编程:接口与多态](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483724&idx=1&sn=660fdc0c90751e3306b19c3009965836&chksm=fc355b27cb42d2317b3b9e755ef1a9544cc1cfbfffef38082dbba0641b133d4776bfbe35df1d&token=243502240&lang=zh_CN#rd) + +[16. 关键字:make 和 new 的区别?](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483728&idx=1&sn=497137228dbd44bc9b9a4141fce8c473&chksm=fc355b3bcb42d22d5d5346051763b79633f1dbdd219bdf1f6cf967b65964d11b39dd2606637e&token=243502240&lang=zh_CN#rd) + +[17. 一篇文章理解 Go 里的语句块与作用域](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483732&idx=1&sn=10f8c1dcba40b83ade76288b499a77ea&chksm=fc355b3fcb42d229b24d7909663b8e7b7238aae333e51f4ad626bb9c1c7f3df86a7ef66fd156&token=243502240&lang=zh_CN#rd) + +[18. 学习 Go 协程:goroutine](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483736&idx=1&sn=1f54126378ca6811d618e0ccb49dfb42&chksm=fc355b33cb42d225363907ec1900d80f140b15a4a122514ac5e35509e8f2303fdf198c155e96&token=243502240&lang=zh_CN#rd) + +[19. 学习 Go 协程:详解信道/通道](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483741&idx=1&sn=4d4ccd8fdee404432f03447927ddb055&chksm=fc355b36cb42d2201e12b77085f7db5a5fed98674e369a5df7fd852abde09a1efad35ba28944&token=243502240&lang=zh_CN#rd) + +[20. 几个信道死锁经典错误案例详解](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483745&idx=1&sn=390492a25034a47519c43a839c5e73df&chksm=fc355b0acb42d21cbf064d9d8301e980127d8027803d498bf027281b4dc6a61584f81f3fcf3b&token=243502240&lang=zh_CN#rd) + +[21. 学习 Go 协程:WaitGroup](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483746&idx=1&sn=5fb55f41cd5b11d7e13959dd94b454d3&chksm=fc355b09cb42d21f2efdf871dbe611db62bcd9cbfd10d0bb27fa657246dfcb561aa228a61007&token=243502240&lang=zh_CN#rd) + +[22. 学习 Go 协程:互斥锁和读写锁](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483750&idx=1&sn=e3749252c7544d621b8327bd370f4deb&chksm=fc355b0dcb42d21b9ee41dff123714d743901b2a58738a479a8cf66138cb5827c223038a23cc&token=243502240&lang=zh_CN#rd) + +[23. Go 里的异常处理:panic 和 recover](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483755&idx=1&sn=aaa376a579ffb277d52239a64b7b5630&chksm=fc355b00cb42d216e14fd42b93cad5e8cc9a5659d3c3359bb95ef02df01266ea79ced2ec1282&token=243502240&lang=zh_CN#rd) + + + +还在持续更新中... + + + +![](http://image.python-online.cn/20200314203721.png) + diff --git a/source/mp_index.md b/source/py_mp_index.md similarity index 100% rename from source/mp_index.md rename to source/py_mp_index.md From 5e5c0fd11fbcf98385794beaec8e21809090a0b1 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 14 Mar 2020 22:07:59 +0800 Subject: [PATCH 019/147] =?UTF-8?q?=E7=94=9F=E6=88=90=20rst?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- source/c04/c04_18.rst | 124 +++++++++----- source/c07/c07_19.rst | 47 ++++++ source/c09/c09_01.rst | 53 +++--- source/c09/c09_23.rst | 248 ++++++++++++--------------- source/c09/c09_24.rst | 380 ++++++++++++++++++++++++++++++------------ source/c09/c09_27.rst | 262 ++++++++++++++++++++++++++++- source/c09/c09_29.rst | 187 +++++++++++++++++++++ source/c09/c09_40.md | 9 + source/c09/c09_40.rst | 10 ++ 10 files changed, 1001 insertions(+), 327 deletions(-) create mode 100644 source/c07/c07_19.rst create mode 100644 source/c09/c09_29.rst create mode 100644 source/c09/c09_40.md create mode 100644 source/c09/c09_40.rst diff --git a/README.md b/README.md index 432ba5b..be3fdcc 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ - 7.16 [Linux 运维之路](http://python-online.cn/zh_CN/latest/c07/c07_16.html) - 1.17 [ansible 自定义 Jinja2 过滤器](http://python-online.cn/zh_CN/latest/c07/c07_17.html) - 7.18 [Shell中去除字符串前后空格的方法](http://python-online.cn/zh_CN/latest/c07/c07_18.html) +- 7.19 [Ansible 使用教程](http://python-online.cn/zh_CN/latest/c07/c07_19.html) ## 第八章:OpenStack - 8.1 [OpenStack 运维命令](http://python-online.cn/zh_CN/latest/c08/c08_01.html) @@ -157,12 +158,13 @@ - 9.20 [几个信道死锁经典错误案例详解](http://python-online.cn/zh_CN/latest/c09/c09_20.html) - 9.21 [学习 Go 协程:WaitGroup](http://python-online.cn/zh_CN/latest/c09/c09_21.html) - 9.22 [学习 Go 协程:互斥锁和读写锁](http://python-online.cn/zh_CN/latest/c09/c09_22.html) -- 9.23 [学习一些常见的并发模型](http://python-online.cn/zh_CN/latest/c09/c09_23.html) -- 9.24 [panic 和 recover](http://python-online.cn/zh_CN/latest/c09/c09_24.html) -- 9.25 [Go语言的包依赖管理](http://python-online.cn/zh_CN/latest/c09/c09_25.html) +- 9.23 [panic 和 recover](http://python-online.cn/zh_CN/latest/c09/c09_23.html) +- 9.24 [超级详细的Go语言包管理方案演变及 go mod 入门](http://python-online.cn/zh_CN/latest/c09/c09_24.html) - 9.26 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_26.html) - 9.27 [go 命令详解](http://python-online.cn/zh_CN/latest/c09/c09_27.html) - 9.28 [函数式编程](http://python-online.cn/zh_CN/latest/c09/c09_28.html) +- 9.29 [学习一些常见的并发模型](http://python-online.cn/zh_CN/latest/c09/c09_29.html) +- 9.40 [推荐几个Go学习网站](http://python-online.cn/zh_CN/latest/c09/c09_40.html) ## 第十章:有趣的工具 - 10.1 [情人节来了,教你使用 Python 来表白](http://python-online.cn/zh_CN/latest/c10/c10_01.html) diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index a563185..2d62901 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -12,15 +12,18 @@ shift + command + d(横切)command + d(竖切) 2、历史信息查找和粘贴:command + f,呼出查找功能,找到后 tab 键可以选中找到的文本,通过option + 回车粘贴。 + 3、自动完成:command + ; ,呼出自动完成窗口,根据上下文提供内容选择项 # 粘贴历史 shift + command + h + # 回放功能 option + command + b + # 光标去哪了? command + / - # Expose Tabs: + # 在所有的窗口中搜索 option + command + e 4.18.2 截图工具 @@ -38,15 +41,13 @@ :: - # 返回桌面 - fn + f11 + # 查看桌面,并不是返回桌面,再按一下就还原原来的窗口了 + fn + f11 # 通常情况下 + f11 # 在外接键盘下,请先设置 F1,F2 这些键为标准功能键,偏好设置->键盘 # 最大化窗口与取消 command + ctrl + f - # 关闭访达 - command + w - # 关闭除访达外的其他程序 command + q @@ -66,43 +67,6 @@ # 新建窗口打开页面 command + 鼠标左键 -文件夹管理 - -:: - - # 快速打开访达:先打开搜索,再打开个人家目录 - 打开搜索:command + option(alt) + space - 关闭标签页:command + shift + h - - # 返回父级文件夹 - command + ↑ - - # 进入文件夹 - command + ↓ - - # enter - 重命名文件夹 - - # 选中所有文件,并将这些文件归档入一个新的文件夹 - 右键 -> 用所选项目新建的文件夹 -> 回车,重命名 - - - # 选择 - 点击 -> 拖拽 - 如果想要取消选中,就 command + 点击 - - # 指定路径打开访达 - shift + command + g - - # 前进 后退 - command + [ - comand + ] - - # 打开最近使用过的文件夹 - comand + shift + f - - # - 查询汇率 :: @@ -234,6 +198,8 @@ GoodSync:和 windows 平台同步文件 TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) +NewFileMenu:使得可以在访达中新建文件 + 4.18.6 brew 的使用 ------------------ @@ -267,6 +233,78 @@ TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) brew cask install docker +4.18.7 访达使用技巧 +------------------- + +详细请看这篇文章(\ `MacOS实用技巧之Finder(访达)的使用 `__\ ),非常好的教程 + +:: + + # 快速打开访达:先打开搜索,再打开个人家目录 + 打开搜索:command + option(alt) + space + 关闭标签页:command + shift + h + + # 返回父级文件夹 + command + ↑ + + # 进入文件夹 + command + ↓ + + # enter + 重命名文件夹 + + # 选中所有文件,并将这些文件归档入一个新的文件夹 + 右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 + + + # 选择 + 点击 -> 拖拽 + 如果想要取消选中,就 command + 点击 + + # 打开指定路径(前提访达得是激活状态的窗口) + # 注意在这里,可以使用 tab 补全 + shift + command + g + + # 前进 后退 + command + [ + comand + ] + + # 打开最近使用过的文件夹 + comand + shift + f + + # 显示/隐藏文件 + command + shift + . + + # 查看文件/夹 详情 + command + i + + # 复制文件路径,有两种方法 + # 【第一种】:快捷键 + command + option + c + # 若你使用 alfred ,快捷键会冲突,解决方法:先右键,再 option,选择将 xx 拷贝为路径名称 + + # 第二种:使用服务 + 参考 https://sspai.com/post/33422 + + # 快速跳转至第一个文件或最后一个文件 + option + ↑ + option + ↓ + + # mac 中拷贝和复制不一样 + command + c 拷贝 + command + d 复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option + command + v 粘贴 + command + option + v 称动 ,或者使用鼠标拖动 + + # 可以设置搜索的范围 + command + f + + # 新建文件夹 + command + shift + n + + # 关闭访达标签页,如果是最后一个标签页,则关闭访达 + command + w + 参考文章 -------- diff --git a/source/c07/c07_19.rst b/source/c07/c07_19.rst new file mode 100644 index 0000000..4c7a00a --- /dev/null +++ b/source/c07/c07_19.rst @@ -0,0 +1,47 @@ +7.19 Ansible 使用教程 +===================== + +1. 环境准备 +----------- + +安装 Ansible ,需要 epel 源,所以要先配置一下 + +.. code:: shell + + $ pwd + /etc/yum.repos.d +   + $ cat aliBase.repo + [aliBase] + name=aliBase + baseurl=https://mirrors.aliyun.com/centos/$releasever/os/$basearch/ + enabled=1 + gpgcheck=1 + gpgkey=https://mirrors.aliyun.com/centos/$releasever/os/$basearch/RPM-GPG-KEY-CentOS-$releasever +   + $ cat aliEpel.repo + [aliEpel] + name=aliEpel + baseurl=https://mirrors.aliyun.com/epel/$releasever\Server/$basearch/ + enabled=1 + gpgcheck=0 + +然后安装 + +.. code:: shell + + $ yum install ansible + +在管理节点生成密钥(公钥和私钥)对,然后将公钥放到要被 ansible +管理的机器上。 + +.. code:: shell + + $ ssh-keygen + $ ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.20.20.1 + +运行第一条命令 + +.. code:: shell + + $ ansible 172.20.20.1 -m ping diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index fed4fdf..5c96a5e 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -219,36 +219,32 @@ Code,其记录的GOPATH始终指向%USERPROFILE%:raw-latex:`\go`), |image20| -随便点开一个 go 文件,在你的右下角会提示要你安装一些工具,点击 -``Install All`` +随便点开一个 go +文件,在你的右下角会提示要你安装一些工具,安装的包有些由于墙的原因,无法下载,为了保证下载顺利,可以设置一下代理。 -|image21| - -然后你在 OUTPUT 就能看到安装进度 +.. code:: shell -|image22| + $ go env -w GOPROXY=https://goproxy.cn,direct -查看 OUTPUT 会有一些安装失败的信息。 +然后再点击 ``Install All`` -|image23| +|image21| -把这两条单独拿出来执行吧(记住执行的话,要切回 %GOPATH%),先使用 -``go get`` 下载,再使用 ``go install`` -安装(若你想安装其他的包,其实也是一样的逻辑)。 +然后你在 OUTPUT 就能看到安装进度 -|image24| +|image22| 安装的 exe 文件会放在 %GOPATH%/bin 下,也就是 ``F:\Go-Player\bin`` -|image25| +|image23| 而此的 src 目录结构是这样的 -|image26| +|image24| 到这时环境配置完成,编写 HelloWorld,并运行查看输出,一切完成。 -|image27| +|image25| 5. 配置环境变量 --------------- @@ -272,7 +268,7 @@ Code,其记录的GOPATH始终指向%USERPROFILE%:raw-latex:`\go`), set GOOS=windows set GOPATH=E:\MING-Code\GoPlayer set GOPRIVATE= - set GOPROXY=https://proxy.golang.org,direct + set GOPROXY=https://goproxy.cn,direct set GOROOT=D:\Program Files (x86)\Go-1.13.6 set GOSUMDB=sum.golang.org set GOTMPDIR= @@ -301,26 +297,19 @@ Code,其记录的GOPATH始终指向%USERPROFILE%:raw-latex:`\go`), $ go env GOPROXY https://goproxy.cn,direct -其中有几个比较重要的,我这里会讲一下。 - -``GOPATH``\ : +以上环境变量很多,这里仅设置下面这两个就足够了 -``GOROOT``\ : - -``GOPROXY``\ :你安装下载包的时候去哪里下载,一条命令即可,更多详情可以查看 -`Github · -GoProxy `__ +- 一个是GO111MODULE 设置为 on,表示使用 go modules 模式 .. code:: shell - $ go env -w GOPROXY=https://goproxy.cn,direct + $ go env -w GO111MODULE=on -``GO111MODULE``\ :on、 off或 -auto,必须为小写,不能为为true或false,也不能为1或0。这里推荐设置为on +- 一个是开启代理,防止下载包失败(前面可能你已经设置过) .. code:: shell - $ go env -w GO111MODULE=on + $ go env -w GOPROXY=https://goproxy.cn,direct .. figure:: http://image.python-online.cn/20191117155836.png :alt: 关注公众号,获取最新干货! @@ -349,9 +338,7 @@ auto,必须为小写,不能为为true或false,也不能为1或0。这里 .. |image20| image:: http://image.python-online.cn/20200109153948.png .. |image21| image:: http://image.python-online.cn/20200109210654.png .. |image22| image:: http://image.python-online.cn/20200109211543.png -.. |image23| image:: http://image.python-online.cn/20200109212824.png -.. |image24| image:: http://image.python-online.cn/20200109213032.png -.. |image25| image:: http://image.python-online.cn/20200109213056.png -.. |image26| image:: http://image.python-online.cn/20200109214117.png -.. |image27| image:: http://image.python-online.cn/20200109154657.png +.. |image23| image:: http://image.python-online.cn/20200109213056.png +.. |image24| image:: http://image.python-online.cn/20200109214117.png +.. |image25| image:: http://image.python-online.cn/20200109154657.png diff --git a/source/c09/c09_23.rst b/source/c09/c09_23.rst index a3d6b28..4989bf8 100644 --- a/source/c09/c09_23.rst +++ b/source/c09/c09_23.rst @@ -1,187 +1,147 @@ -9.23 学习一些常见的并发模型 -=========================== +9.23 panic 和 recover +===================== -本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 -Golang 中的 -协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 +编程语言一般都会有异常捕获机制,在 Python 中 是使用\ ``raise`` 和 +``try-except`` 语句来实现的异常抛出和异常捕获的。 -你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 +在 Golang +中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 -0. 并发与并行 -------------- - -讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? - -这里我用两个例子来形象描述: - -- **并发**\ :当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 -- **并行**\ :你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 - -在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 - -而当你的机器有多个 CPU -核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 - -接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 - -但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? - -谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C -的活,然后再去做一会 A -的活,就这样不断的切换着,大家都很开心,其乐融融。 - -1. 并发编程的模型 ------------------ - -在计算机的世界里,实现并发通常有几种方式: - -1. 多进程模型:创建新的线程处理请求 -2. 多线程模型:创建新的进程处理请求 -3. 使用线程池:线程/进程创建销毁开销大 -4. I/O 多路复用+单/多线程 - -2. 多进程与多线程 ------------------ +当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 +panic,让程序退出停止运行。 -对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ -,一个微信,它们都是一个进程。 +1. 触发panic +------------ -进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 +手动触发宕机,是非常简单的一件事,只需要调用 panic +这个内置函数即可,就像这样子 -在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 +.. code:: go -线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 -内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 + package main -而进程的切换,相比线程来说,会更加麻烦。 + func main() { + panic("crash") + } -因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 -共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 +运行后,直接报错宕机 -此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess -Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams -等 +.. code:: shell -说了这么多,好像都在说线程优于进程,也不尽然。 + $ go run main.go + go run main.go + panic: crash -比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 + goroutine 1 [running]: + main.main() + E:/Go-Code/main.go:4 +0x40 + exit status 2 -同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 - -3. I/O多路复用 --------------- - -``I/O多路复用`` ,英文全称为 ``I/O multiplexing``\ ,这个中文翻译和把 -socket 翻译成 套接字一样,影响了我对其概念的理解。 - -在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 - -但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 - -终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O -多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 - -再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 -select ,早已不能支撑用户请求了。 - -由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select -发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 - -select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 - -都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file -descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 - -由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 +2. 捕获 panic +------------- -epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: +发生了异常,有时候就得捕获,就像 Python 中的\ ``except`` 一样,那 Golang +中是如何做到的呢? -- select 和 poll 无差别轮循fd,浪费资源,epool - 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 -- select 和 poll 线程不安全,epool 线程安全 -- select 请求连接数的限制,epool - 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) -- select 和 pool - 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 +这就不得不引出另外一个内建函数 – +``recover``\ ,它可以让程序在发生宕机后起生回生。 -虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 -epool 出现了, select 就会被淘汰掉。 +但是 recover 的使用,有一个条件,就是它必须在 defer +函数中才能生效,其他作用域下,它是不工作的。 -epool -关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool -的性能最好。 +这是一个简单的例子 -而 select -关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 -select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 +.. code:: go -另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool -是 Linux 所有的,其他平台上没有。 + import "fmt" -IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 + func set_data(x int) { + defer func() { + // recover() 可以将捕获到的panic信息打印 + if err := recover(); err != nil { + fmt.Println(err) + } + }() -- 一个线程的IO多路复用,比如 Redis -- 多个线程的IO多路复用,比如 goroutine + // 故意制造数组越界,触发 panic + var arr [10]int + arr[x] = 88 + } -IO多路复用 + -单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 + func main() { + set_data(20) -4. 三种线程模型? ------------------ + // 如果能执行到这句,说明panic被捕获了 + // 后续的程序能继续运行 + fmt.Println("everything is ok") + } -实际上,goroutine 并非传统意义上的协程。 +运行后,输出如下 -现在主流的线程模型分三种: +.. code:: go -- 内核级线程模型 -- 用户级线程模型 -- 两级线程模型(也称混合型线程模型) + $ go run main.go + runtime error: index out of range [20] with length 10 + everything is ok -传统的协程库属于\ **用户级线程模型**\ ,而 goroutine 和它的 -``Go Scheduler`` -在底层实现上其实是属于\ **两级线程模型**\ ,因此,有时候为了方便理解可以简单把 -goroutine 类比成协程,但心里一定要有个清晰的认知 — -goroutine并不等同于协程。 +通常来说,不应该对进入 panic +宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 +web +服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 +web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 -关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 +3. 无法跨协程 +------------- -5. 协程的优势在哪? -------------------- +从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 +defer 延迟函数,还是得执行完 defer 。 -协程,可以认为是轻量级的“线程”。 +但是这个 defer 在多个协程之间是没有效果,在子协程里触发 +panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer +函数的。 -对比线程,有如下几个明显的优势。 +来做个实验就知道了 -1. 协程的调度由 Go 的 runtime - 管理,协程切换不需要经由操作系统内核,开销较小。 -2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 +.. code:: go -同时,在 Golang -里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 -async def 而言),运行时只需要一个关键字 -``go``\ ,就可以轻松创建一个协程。 + import ( + "fmt" + "time" + ) -使用 -race 来检测数据 访问的冲突 + func main() { + // 这个 defer 并不会执行 + defer fmt.Println("in main") + + go func() { + defer println("in goroutine") + panic("") + }() -协程什么时候会切换 + time.Sleep(2 * time.Second) + } -1. I/O,select -2. channel -3. 等待锁 -4. 函数调用(有时 -5. runtime.Gosched() +输出如下 -参考阅读: ----------- +:: -https://www.cnblogs.com/aspirant/p/9166944.html + in goroutine + panic: -https://blog.csdn.net/snoweaglelord/article/details/99681179 + goroutine 6 [running]: + main.main.func1() + E:/Go-Code/main.go:12 +0x7b + created by main.main + E:/Go-Code/main.go:10 +0xbc + exit status 2 -https://www.jianshu.com/p/dfd940e7fca2 +4. 总结一下 +----------- -https://studygolang.com/articles/13344 +Golang 异常的抛出与捕获,依赖两个内置函数: -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! +- panic:抛出异常,使程序崩溃 +- recover:捕获异常,恢复程序或做收尾工作 +revocer 调用后,抛出的 panic 将会在此处终结,不会再外抛,但是 +recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 diff --git a/source/c09/c09_24.rst b/source/c09/c09_24.rst index 4f2cbcb..066879d 100644 --- a/source/c09/c09_24.rst +++ b/source/c09/c09_24.rst @@ -1,147 +1,323 @@ -9.24 panic 和 recover -===================== +9.24 超级详细的Go语言包管理方案演变及 go mod 入门 +================================================= -编程语言一般都会有异常捕获机制,在 Python 中 是使用\ ``raise`` 和 -``try-except`` 语句来实现的异常抛出和异常捕获的。 +在以前,Go +语言的的包依赖管理一直都被大家所诟病,Go官方也在一直在努力为开发者提供更方便易用的包管理方案,从最初的 +GOPATH 到 GO VENDOR,再到最新的 GO +Modules,虽然走了不少的弯路,但最终还是拿出了 Go Modules +这样像样的解决方案。 -在 Golang -中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 +目前最主流的包依赖管理方式是使用官方推荐的 Go Modules ,这不前段时间 Go +1.14 版本发布,官方正式放话,强烈推荐你使用 Go +Modules,并且有自信可以用于生产中。 -当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 -panic,让程序退出停止运行。 +本文会大篇幅的讲解 Go Modules +的使用,但是在那之前,我仍然会简要介绍一下前两个解决方案 GOPATH 和 go +vendor +到底是怎么回事?我认为这是有必要的,因为只有了解它的发展历程,才能知道 +Go Modules 的到来是有多么的不容易,多么的意义非凡。 -1. 触发panic ------------- +1. 最古老的 GOPATH +------------------ -手动触发宕机,是非常简单的一件事,只需要调用 panic -这个内置函数即可,就像这样子 +GOPATH 应该很多人都很眼熟了,之前在配置环境的时候,都配置过吧? -.. code:: go +你可以将其理解为工作目录,在这个工作目录下,通常有如下的目录结构 - package main +|image0| - func main() { - panic("crash") - } +每个目录存放的文件,都不相同 -运行后,直接报错宕机 +- bin:存放编译后生成的二进制可执行文件 +- pkg:存放编译后生成的 ``.a`` 文件 +- src:存放项目的源代码,可以是你自己写的代码,也可以是你 go get + 下载的包 -.. code:: shell +将你的包或者别人的包全部放在 ``$GOPATH/src`` +目录下进行管理的方式,我们称之为 GOPATH 模式。 + +在这个模式下,使用 go install 时,生成的可执行文件会放在 ``$GOPATH/bin`` +下 + +|image1| + +如果你安装的是一个库,则会生成 ``.a`` 文件到 ``$GOPATH/pkg`` +下对应的平台目录中(由 GOOS 和 GOARCH 组合而成),生成 ``.a`` +为后缀的文件。 + +|image2| + +GOOS,表示的是目标操作系统,有 +darwin(Mac),linux,windows,android,netbsd,openbsd,solaris,plan9 +等 + +而 GOARCH,表示目标架构,常见的有 arm,amd64 等 + +这两个都是 go env 里的变量,你可以通过 ``go env 变量名`` 进行查看 + +|image3| + +至此,你可能不会觉得上面的方案会产生什么样的问题,直到你开始真正使用 +GOPATH +去开发程序,就不得不开始面临各种各样的问题,其中最严重的就是版本管理问题,因为 +GOPATH 根本没有版本的概念。 + +以下几点是你使用 GOPATH 一定会碰到的问题: + +- 你无法在你的项目中,使用指定版本的包,因为不同版本的包的导入方法也都一样 +- 其他人运行你的开发的程序时,无法保证他下载的包版本是你所期望的版本,当对方使用了其他版本,有可能导致程序无法正常运行 +- 在本地,一个包只能保留一个版本,意味着你在本地开发的所有项目,都得用同一个版本的包,这几乎是不可能的。 + +2. go vendor 模式的过渡 +----------------------- + +为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 +开始支持 vendor 。 + +以前使用 GOPATH 的时候,所有的项目都共享一个 +GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH +模式下只能有一个版本的第三方库。 + +解决的思路就是,在每个项目下都创建一个 vendor +目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5 +的 Go 在你设置了开启 ``GO15VENDOREXPERIMENT=1`` (注:这个变量在 v1.6 +版本默认为1,但是在 v1.7 后,已去掉该环境变量,默认开启 ``vendor`` +特性,无需你手动设置)后,会提升 vendor +目录的依赖包搜索路径的优先级(相较于 GOPATH)。 + +其搜索包的优先级顺序,由高到低是这样的 - $ go run main.go - go run main.go - panic: crash +- 当前包下的 vendor 目录 +- 向上级目录查找,直到找到 src 下的 vendor 目录 +- 在 GOROOT 目录下查找 +- 在 GOPATH 下面查找依赖包 - goroutine 1 [running]: - main.main() - E:/MING-Code/GoPlayer/src/imooc.com/ccmouse/learngo/main.go:4 +0x40 - exit status 2 +虽然这个方案解决了一些问题,但是解决得并不完美。 -2. 捕获 panic -------------- +- 如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 -发生了异常,有时候就得捕获,就像 Python 中的\ ``except`` 一样,那 Golang -中是如何做到的呢? +- 并且如果要分享开源你的项目,你需要将你的所有的依赖包悉数上传,别人使用的时候,除了你的项目源码外,还有所有的依赖包全部下载下来,才能保证别人使用的时候,不会因为版本问题导致项目不能如你预期那样正常运行。 -这就不得不引出另外一个内建函数 – -``recover``\ ,它可以让程序在发生宕机后起生回生。 +这些看似不是问题的问题,会给我们的开发使用过程变得非常难受,虽然我是初学者,还未使用过 +go vendor,但能有很明显的预感,这个方案照样会另我崩溃。 -但是 recover 的使用,有一个条件,就是它必须在 defer -函数中才能生效,其他作用域下,它是不工作的。 +3. go mod 的横空出世 +-------------------- -这是一个简单的例子 +go modules 在 v1.11 版本正式推出,在最新发布的 v1.14 +版本中,官方正式发话,称其已经足够成熟,可以应用于生产上。 -.. code:: go +从 v1.11 开始,\ ``go env`` 多了个环境变量: ``GO111MODULE`` ,这里的 +111,其实就是 v1.11 的象征标志, go 里好像很喜欢这样的命名方式,比如当初 +vendor 出现的时候,也多了个 ``GO15VENDOREXPERIMENT``\ 环境变量,其中 +15,表示的vendor 是在 v1.5 时才诞生的。 - import "fmt" +``GO111MODULE`` 是一个开关,通过它可以开启或关闭 go mod 模式。 - func set_data(x int) { - defer func() { - // recover() 可以将捕获到的panic信息打印 - if err := recover(); err != nil { - fmt.Println(err) - } - }() +它有三个可选值:\ ``off``\ 、\ ``on``\ 、\ ``auto``\ ,默认值是\ ``auto``\ 。 - // 故意制造数组越界,触发 panic - var arr [10]int - arr[x] = 88 - } +1. ``GO111MODULE=off``\ 禁用模块支持,编译时会从\ ``GOPATH``\ 和\ ``vendor``\ 文件夹中查找包。 +2. ``GO111MODULE=on``\ 启用模块支持,编译时会忽略\ ``GOPATH``\ 和\ ``vendor``\ 文件夹,只根据 + ``go.mod``\ 下载依赖。 +3. ``GO111MODULE=auto``\ ,当项目在\ ``$GOPATH/src``\ 外且项目根目录有\ ``go.mod``\ 文件时,自动开启模块支持。 - func main() { - set_data(20) +go mod 出现后, GOPATH(肯定没人使用了) 和 GOVENDOR +将会且正在被逐步淘汰,但是若你的项目仍然要使用那些即将过时的包依赖管理方案,请注意将 +GO111MODULE 置为 off。 - // 如果能执行到这句,说明panic被捕获了 - // 后续的程序能继续运行 - fmt.Println("everything is ok") - } +具体怎么设置呢?可以使用 go env 的命令,如我要开启 go mod +,就使用这条命令 -运行后,输出如下 +.. code:: shell + + $ go env -w GO111MODULE="on" + +4. go mod 依赖的管理 +-------------------- + +接下来,来演示一下 go modules 是如何来管理包依赖的。 + +go mod 不再依靠 $GOPATH,使得它可以脱离 GOPATH +来创建项目,于是我们在家目录下创建一个 go_test +的目录,用来创建我的项目,详细操作如下: + +|image4| + +接下来,进入项目目录,执行如下命令进行 go modules 的初始化 + +|image5| + +接下来很重要的一点,我们要看看 go install 把下载的包安装到哪里了? -.. code:: go +|image6| - $ go run main.go - runtime error: index out of range [20] with length 10 - everything is ok +上面我们观察到,在使用 go modules +模式后,项目目录下会多生成两个文件也就是 ``go.mod`` 和 ``go.sum`` 。 -通常来说,不应该对进入 panic -宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 -web -服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 -web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 +这两个文件是 go modules 的核心所在,这里不得不好好介绍一下。 -3. 无法跨协程 -------------- +|image7| -从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 -defer 延迟函数,还是得执行完 defer 。 +go.mod 文件 +~~~~~~~~~~~ -但是这个 defer 在多个协程之间是没有效果,在子协程里触发 -panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer -函数的。 +go.mod 的内容比较容易理解 -来做个实验就知道了 +- 第一行:模块的引用路径 +- 第二行:项目使用的 go 版本 +- 第三行:项目所需的直接依赖包及其版本 -.. code:: go +在实际应用上,你会看见更复杂的 go.mod 文件,比如下面这样 - import ( - "fmt" - "time" +:: + + module github.com/BingmingWong/module-test + + go 1.14 + + require ( + example.com/apple v0.1.2 + example.com/banana v1.2.3 + example.com/banana/v2 v2.3.4 + example.com/pear // indirect + example.com/strawberry // incompatible ) - func main() { - // 这个 defer 并不会执行 - defer fmt.Println("in main") - - go func() { - defer println("in goroutine") - panic("") - }() + exclude example.com/banana v1.2.4 + replace( + golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac + golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d + golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 + ) + +主要是多出了两个 flag: + +- ``exclude``\ : 忽略指定版本的依赖包 +- ``replace``\ :由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 + +go.sum 文件 +~~~~~~~~~~~ - time.Sleep(2 * time.Second) - } +反观 go.sum 文件,就比较复杂了,密密麻麻的。 -输出如下 +可以看到,内容虽然多,但是也不难理解 + +每一行都是由 ``模块路径``\ ,\ ``模块版本``\ ,\ ``哈希检验值`` +组成,其中哈希检验值是用来保证当前缓存的模块不会被篡改。hash +是以\ ``h1:``\ 开头的字符串,表示生成checksum的算法是第一版的hash算法(sha256)。 + +值得注意的是,为什么有的包只有一行 :: - in goroutine - panic: + /go.mod + +而有的包却有两行呢 + +:: + + + /go.mod + +那些有两行的包,区别就在于 hash 值不一行,一个是 ``h1:hash``\ ,一个是 +``go.mod h1:hash`` + +而 ``h1:hash`` 和 +``go.mod h1:hash``\ 两者,要不就是同时存在,要不就是只存在 +``go.mod h1:hash``\ 。那什么情况下会不存在 ``h1:hash`` 呢,就是当 Go +认为肯定用不到某个模块版本的时候就会省略它的\ ``h1 hash``\ ,就会出现不存在 +``h1 hash``\ ,只存在 ``go.mod h1:hash`` 的情况。[引用自 3] + +go.mod 和 go.sum 是 go modules 版本管理的指导性文件,因此 go.mod 和 +go.sum 文件都应该提交到你的 Git +仓库中去,避免其他人使用你写项目时,重新生成的go.mod 和 go.sum +与你开发的基准版本的不一致。 + +5. go mod 命令的使用 +-------------------- + +- ``go mod init``\ :初始化go mod, 生成go.mod文件,后可接参数指定 + module 名,上面已经演示过。 + +- ``go mod download``\ :手动触发下载依赖包到本地cache(默认为\ ``$GOPATH/pkg/mod``\ 目录) + +- ``go mod graph``\ : 打印项目的模块依赖结构 + +|image8| + +- ``go mod tidy`` :添加缺少的包,且删除无用的包 + +- ``go mod verify`` :校验模块是否被篡改过 + +- ``go mod why``\ : 查看为什么需要依赖 + +- ``go mod vendor`` :导出项目所有依赖到vendor下 + +|image9| + +- ``go mod edit`` :编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 + -require=golang.org/x/text 添加依赖,接 + -droprequire=golang.org/x/text 删除依赖,详情可参考 + ``go help mod edit`` + +|image10| + +- ``go list -m -json all``\ :以 json 的方式打印依赖详情 + +|image11| + +如何给项目添加依赖(写进 go.mod)呢? + +有两种方法: + +- 你只要在项目中有 import,然后 go build 就会 go module + 就会自动下载并添加。 +- 自己手工使用 go get 下载安装后,会自动写入 go.mod 。 + +|image12| + +7. 总结写在最后 +--------------- + +如果让我用一段话来评价 GOPATH 和 go vendor,我会说 + +GOPATH 做为 Golang 的第一个包管理模式,只能保证你能用,但不保证好用,而 +go vendor 解决了 GOPATH 忽视包版的本管理,保证好用,但是还不够好用,直到 +go mod 的推出后,才使 Golang 包的依赖管理有了一个能让 Gopher +都统一比较满意的方案,达到了能用且好用的标准。 + +如果是刚开始学习 Golang ,那么 GOPATH 和 go vendor +可以做适当了解,不必深入研究,除非你要接手的项目由于一些历史原因仍然在使用 +go vender 械管理,除此之外,任何 Gopher 应该从此刻就投入 go modules +的怀抱。 + +以上是我在这几天的学习总结,希望对还未入门阶段的你,有所帮助。另外,本篇文章如有写得不对的,请后台批评指正,以免误导其他朋友,非常感谢。 + +8. 推荐参考文章 +--------------- - goroutine 6 [running]: - main.main.func1() - E:/MING-Code/main.go:12 +0x7b - created by main.main - E:/MING-Code/main.go:10 +0xbc - exit status 2 +- `Go语言之依赖管理 `__ +- `Go 包依赖管理工具 —— + govendor `__ +- `Go Modules + 终极入门 `__ + (强烈推荐煎鱼大佬的文章) -总结一下 --------- +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! -Golang 异常的抛出与捕获,依赖两个内置函数: -- panic:抛出异常,使程序崩溃 -- recover:捕获异常,恢复程序 +.. |image0| image:: http://image.python-online.cn/image-20200311220825614.png +.. |image1| image:: http://image.python-online.cn/image-20200312221011685.png +.. |image2| image:: http://image.python-online.cn/image-20200312221141028.png +.. |image3| image:: http://image.python-online.cn/image-20200314132614248.png +.. |image4| image:: http://image.python-online.cn/image-20200314000227914.png +.. |image5| image:: http://image.python-online.cn/image-20200314000940825.png +.. |image6| image:: http://image.python-online.cn/image-20200314001426817.png +.. |image7| image:: http://image.python-online.cn/image-20200314001708640.png +.. |image8| image:: http://image.python-online.cn/image-20200314003442400.png +.. |image9| image:: http://image.python-online.cn/image-20200314003913527.png +.. |image10| image:: http://image.python-online.cn/image-20200314004643487.png +.. |image11| image:: http://image.python-online.cn/image-20200314005924877.png +.. |image12| image:: http://image.python-online.cn/image-20200314005217447.png -revocer 调用后,抛出的 panic 将会在此外终结,不会再外抛,但是 -recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 diff --git a/source/c09/c09_27.rst b/source/c09/c09_27.rst index bd31969..ec7ef42 100644 --- a/source/c09/c09_27.rst +++ b/source/c09/c09_27.rst @@ -1,6 +1,264 @@ 9.27 go 命令详解 ================ -go build ./… 不会生成可执行文件到 ${GOPATH}/bin ,只要能编译过就行 +1. 环境变量 +----------- -go install ./… 会生成可执行文件到 ${GOPATH}/bin +``go env`` + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200311221418584.png + :alt: 仅截取部分内容 + + 仅截取部分内容 + +2. 执行 Go 程序 +--------------- + +当前热门的编程语言 Python ,可以不用编译成 二进制文件,就可以直接运行。 + +但 Go 语言程序的执行,必须得先编译再执行。通常来说有如下两种方法 + +1. 先使用 go build 编译成二进制文件,再执行这个二进制文件 + + .. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200313222620374.png + :alt: image-20200313222620374 + + image-20200313222620374 + +2. 使用 go run + “直接”运行,这个命令还是会去编译,但是不会在当前目录下生成二进制文件,而是编译成临时文件后直接运行。 + + .. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200313222710998.png + :alt: image-20200313222710998 + + image-20200313222710998 + +3. 编译文件 +----------- + +将 ``.go`` 文件编译成可执行文件,可以使用 ``go build`` + +如下图所示,helloworld 文件夹下,包含两个 ``.go`` +文件,它们都归属于同一个包。 + +当使用 ``go build`` +可指定包里所有的文件,就你下面这样,默认会以第一个文件(main.go)名生成可执行文件(main)。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312201759541.png + :alt: image-20200312201759541 + + image-20200312201759541 + +当然,你也可以不指定,此时生成的可执行文件是以 文件夹名命名 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312202032363.png + :alt: image-20200312202032363 + + image-20200312202032363 + +当然你也可以手动指定这个可执行文件名 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312202520902.png + :alt: image-20200312202520902 + + image-20200312202520902 + +以上是编译单个文件,当然也可以编译多个文件 + +4. 清除编译文件 +--------------- + +使用 go install 或 go install +有可能会生成很多的文件,如可执行文件,归档文件等,它们的后缀名非常多,有 +``.exe``\ , ``.a``\ , ``.test``\ ,\ ``.o``\ ,\ ``.so``\ ,\ ``.5`` +,\ ``.6``\ ,\ ``.8``\ ,如果要手动一个一个去清理他们,可以说是相当麻烦的,这里你可以通过使用 +``go clean`` 一键清理。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200313224148510.png + :alt: image-20200313224148510 + + image-20200313224148510 + +实际开发中\ ``go clean``\ 命令使用的可能不是很多,一般都是利用\ ``go clean``\ 命令清除编译文件,然后再将源码递交到 +github 上,方便对于源码的管理。 + +go clean 有不少的参数: + +- ``-i``\ :清除关联的安装的包和可运行文件,也就是通过\ ``go install``\ 安装的文件; +- ``-n``\ : + 把需要执行的清除命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的; +- ``-r``\ : 循环的清除在 import 中引入的包; +- ``-x``\ : 打印出来执行的详细命令,其实就是 -n 打印的执行版本; +- ``-cache``\ : 删除所有\ ``go build``\ 命令的缓存 +- ``-testcache``\ : 删除当前包所有的测试结果 + +4. 下载代码包 +------------- + +在 Golang +中,除了可以从官方网站(golang.org)下载包之外,还可以从一些代码仓库中下载,诸如 +github.com,bitbucket.org 这样的代码托管网站。 + +``go get`` +这条命令,你以后会最经常用到,它可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。整个过程就像安装一个 +App 一样简单。 + +这个命令可以动态获取远程代码包,目前支持的有 BitBucket、GitHub、Google +Code 和 Launchpad。在使用 go get +命令前,需要安装与远程包匹配的代码管理工具,如 Git、SVN等。 + +``go get`` 会根据域名的不同,使用不同的工具去拉取代码包,具体可参考下图 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312203244402.png + :alt: image-20200312203244402 + + image-20200312203244402 + +下载和安装,原本是两个动作,但使用 ``go get`` +后,它默认会将下载(源码包)和安装(go +install)合并起来,当然你也可以通过参数指定将拆散它们。 + +在终端执行 ``go help get``\ ,会弹出 ``go get`` +的帮助文档,我这里汉化总结一下,来帮助大家学习。 + +:: + + go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages] + +其中几个参数详解如下 + +- ``-u``\ : + + 用于下载指定的路径包及其依赖包,默认情况下,不会下载本地已经存在的,只会下载本地不存在的代码包。就是口中常说的更新包 + 比如:go get -u github.com/jinzhu/gorm。会把最新的 gorm + 包下载到你本地 + +- ``-d``\ : + + 让命令程序只执行下载动作,而不执行安装动作。 + +- ``-t`` + + 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包 + +- ``-fix`` + + 命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。比如,我的代码是用1.7 + 开发的,现在go 版本已经是1.13 + 了,有些包已经发生了变化,那么我们在使用go + get命令的时候可以加入-fix标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.7版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。 + +- ``-v`` + + 打印出那些下载的代码包的名字 + +- ``-f`` + + 仅在使用-u标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了 + +- ``-x`` + + 打印出整个过程使用了哪些命令 + +- ``-insecure`` + 允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。(记得 + 使用工具 git 时,有个版本就是 http 升级为了https) + +参数有点多,咱一个一个来。 + +指定 ``-d``\ ,只下载源码包而不进行安装 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312204335687.png + :alt: image-20200312204335687 + + image-20200312204335687 + +由于此时,我们已经下载了 logging 包,当你再次执行 go get +时,并不会重复下载,只有当你指定 ``-u`` +时,不管你需不需要更新,都会触发重新下载强制更新。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312204746007.png + :alt: image-20200312204746007 + + image-20200312204746007 + +如果你想看,下载这个过程用到了哪几个命令,可以指定 ``-x`` 参数 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312205001161.png + :alt: image-20200312205001161 + + image-20200312205001161 + +最后,你可能想说,为什么 golang +里的包含这么长,好难记呀,其实这个路径是有讲究的 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312210557326.png + :alt: image-20200312210557326 + + image-20200312210557326 + +这样不同的人开发的包即使使用同一个名,也不会冲突了。 + +下载的包,可能有不同的版本,如何指定版本下载呢? + +.. code:: shell + + # 拉取最新 + go get github.com/foo + + # 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) + go get -u github.com/foo + + # 升级到最新的修订版本 + go get -u=patch github.com/foo + + # 指定版本,若存在tag,则代行使用 + go get github.com/foo@v1.2.3 + + # 指定分支 + go get github.com/foo@master + + # 指定git提交的hash值 + go get github.com/foo@e3702bed2 + +6. 安装代码包 +------------- + +``go install`` 这个命令,如果你安装的是一个可执行文件(包名是 +main),它会生成可执行文件到 bin 目录下。这点和 ``go build`` +很相似,不同的是,\ ``go build`` 编译生成的可执行文件放在当前目录,而 +``go install`` 会将可执行文件统一放至 ``$GOPATH/bin`` 目录下。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312221011685.png + :alt: image-20200312221011685 + + image-20200312221011685 + +如果你安装的是一个库,它会将这个库安装到 pkg 目录下,生成 ``.a`` +为后缀的文件。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312221141028.png + :alt: image-20200312221141028 + + image-20200312221141028 + +7. 格式化 go 文件 +----------------- + +Go语言的开发团队制定了统一的官方代码风格,并且推出了 gofmt 工具(gofmt +或 go fmt)来帮助开发者格式化他们的代码到统一的风格。 + +gofmt 是一个 cli +程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有 +.go 文件,如果不传参数,会格式化当前目录下的所有 .go 文件。 + +http://c.biancheng.net/view/4441.html + +参考文章 +-------- + +https://mp.weixin.qq.com/s/fNMXfpBhBC3UWTbYCnwIMg + +https://studygolang.com/articles/25658 + +https://juejin.im/post/5d0b865c6fb9a07f050a6f45 diff --git a/source/c09/c09_29.rst b/source/c09/c09_29.rst new file mode 100644 index 0000000..2990769 --- /dev/null +++ b/source/c09/c09_29.rst @@ -0,0 +1,187 @@ +9.29 学习一些常见的并发模型 +=========================== + +本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 +Golang 中的 +协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 + +你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 + +0. 并发与并行 +------------- + +讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? + +这里我用两个例子来形象描述: + +- **并发**\ :当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 +- **并行**\ :你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 + +在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 + +而当你的机器有多个 CPU +核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 + +接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 + +但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? + +谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C +的活,然后再去做一会 A +的活,就这样不断的切换着,大家都很开心,其乐融融。 + +1. 并发编程的模型 +----------------- + +在计算机的世界里,实现并发通常有几种方式: + +1. 多进程模型:创建新的线程处理请求 +2. 多线程模型:创建新的进程处理请求 +3. 使用线程池:线程/进程创建销毁开销大 +4. I/O 多路复用+单/多线程 + +2. 多进程与多线程 +----------------- + +对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ +,一个微信,它们都是一个进程。 + +进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 + +在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 + +线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 +内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 + +而进程的切换,相比线程来说,会更加麻烦。 + +因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 +共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 + +此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess +Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams +等 + +说了这么多,好像都在说线程优于进程,也不尽然。 + +比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 + +同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 + +3. I/O多路复用 +-------------- + +``I/O多路复用`` ,英文全称为 ``I/O multiplexing``\ ,这个中文翻译和把 +socket 翻译成 套接字一样,影响了我对其概念的理解。 + +在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 + +但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 + +终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O +多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 + +再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 +select ,早已不能支撑用户请求了。 + +由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select +发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 + +select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 + +都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file +descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 + +由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 + +epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: + +- select 和 poll 无差别轮循fd,浪费资源,epool + 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 +- select 和 poll 线程不安全,epool 线程安全 +- select 请求连接数的限制,epool + 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) +- select 和 pool + 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 + +虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 +epool 出现了, select 就会被淘汰掉。 + +epool +关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool +的性能最好。 + +而 select +关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 +select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 + +另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool +是 Linux 所有的,其他平台上没有。 + +IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 + +- 一个线程的IO多路复用,比如 Redis +- 多个线程的IO多路复用,比如 goroutine + +IO多路复用 + +单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 + +4. 三种线程模型? +----------------- + +实际上,goroutine 并非传统意义上的协程。 + +现在主流的线程模型分三种: + +- 内核级线程模型 +- 用户级线程模型 +- 两级线程模型(也称混合型线程模型) + +传统的协程库属于\ **用户级线程模型**\ ,而 goroutine 和它的 +``Go Scheduler`` +在底层实现上其实是属于\ **两级线程模型**\ ,因此,有时候为了方便理解可以简单把 +goroutine 类比成协程,但心里一定要有个清晰的认知 — +goroutine并不等同于协程。 + +关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 + +5. 协程的优势在哪? +------------------- + +协程,可以认为是轻量级的“线程”。 + +对比线程,有如下几个明显的优势。 + +1. 协程的调度由 Go 的 runtime + 管理,协程切换不需要经由操作系统内核,开销较小。 +2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 + +同时,在 Golang +里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 +async def 而言),运行时只需要一个关键字 +``go``\ ,就可以轻松创建一个协程。 + +使用 -race 来检测数据 访问的冲突 + +协程什么时候会切换 + +1. I/O,select +2. channel +3. 等待锁 +4. 函数调用(有时 +5. runtime.Gosched() + +参考阅读: +---------- + +https://www.cnblogs.com/aspirant/p/9166944.html + +https://blog.csdn.net/snoweaglelord/article/details/99681179 + +https://www.jianshu.com/p/dfd940e7fca2 + +https://studygolang.com/articles/13344 + +.. figure:: http://image.python-online.cn/20191117155836.png + :alt: 关注公众号,获取最新干货! + diff --git a/source/c09/c09_40.md b/source/c09/c09_40.md new file mode 100644 index 0000000..1da49a2 --- /dev/null +++ b/source/c09/c09_40.md @@ -0,0 +1,9 @@ +# 9.40 推荐几个Go学习网站 + +1、用小例子来学习 Go 语言 + +https://gobyexample-cn.github.io/ + +2、跟煎鱼学Go + +https://eddycjy.gitbook.io/golang/ \ No newline at end of file diff --git a/source/c09/c09_40.rst b/source/c09/c09_40.rst new file mode 100644 index 0000000..d196b48 --- /dev/null +++ b/source/c09/c09_40.rst @@ -0,0 +1,10 @@ +9.40 推荐几个Go学习网站 +======================= + +1、用小例子来学习 Go 语言 + +https://gobyexample-cn.github.io/ + +2、跟煎鱼学Go + +https://eddycjy.gitbook.io/golang/ From f739f8cb3f80bf33d8cd4902d67cd6999c8fe072 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 15 Mar 2020 16:54:05 +0800 Subject: [PATCH 020/147] =?UTF-8?q?delete=EF=BC=9A=E5=88=A0=E9=99=A4=20Gol?= =?UTF-8?q?ang=20=E7=9A=84=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- md2rst.py | 2 +- source/.DS_Store | Bin 10244 -> 12292 bytes source/aboutme.rst | 4 +- source/c01/c01_01.md | 2 +- source/c01/c01_01.rst | 2 +- source/c01/c01_05.md | 2 +- source/c01/c01_05.rst | 2 +- source/c01/c01_06.md | 2 +- source/c01/c01_06.rst | 2 +- source/c01/c01_07.md | 2 +- source/c01/c01_07.rst | 2 +- source/c01/c01_08.md | 2 +- source/c01/c01_08.rst | 2 +- source/c01/c01_09.md | 2 +- source/c01/c01_09.rst | 2 +- source/c01/c01_10.md | 2 +- source/c01/c01_10.rst | 2 +- source/c01/c01_11.md | 2 +- source/c01/c01_11.rst | 2 +- source/c01/c01_12.md | 2 +- source/c01/c01_12.rst | 2 +- source/c01/c01_13.md | 2 +- source/c01/c01_13.rst | 2 +- source/c01/c01_14.md | 2 +- source/c01/c01_14.rst | 2 +- source/c01/c01_15.md | 2 +- source/c01/c01_15.rst | 2 +- source/c01/c01_16.md | 2 +- source/c01/c01_16.rst | 2 +- source/c01/c01_17.md | 2 +- source/c01/c01_17.rst | 2 +- source/c01/c01_20.md | 2 +- source/c01/c01_20.rst | 2 +- source/c01/c01_21.md | 2 +- source/c01/c01_21.rst | 2 +- source/c01/c01_22.md | 2 +- source/c01/c01_22.rst | 2 +- source/c01/c01_23.md | 2 +- source/c01/c01_23.rst | 2 +- source/c01/c01_24.md | 2 +- source/c01/c01_24.rst | 2 +- source/c01/c01_25.md | 2 +- source/c01/c01_25.rst | 2 +- source/c01/c01_26.md | 2 +- source/c01/c01_26.rst | 2 +- source/c01/c01_27.md | 2 +- source/c01/c01_27.rst | 2 +- source/c01/c01_29.md | 2 +- source/c01/c01_29.rst | 2 +- source/c01/c01_31.md | 2 +- source/c01/c01_31.rst | 2 +- source/c01/c01_32.md | 2 +- source/c01/c01_32.rst | 2 +- source/c01/c01_33.md | 2 +- source/c01/c01_33.rst | 2 +- source/c01/c01_34.md | 2 +- source/c01/c01_34.rst | 2 +- source/c01/c01_35.md | 2 +- source/c01/c01_35.rst | 2 +- source/c02/c02_01.md | 2 +- source/c02/c02_01.rst | 2 +- source/c02/c02_02.md | 2 +- source/c02/c02_02.rst | 2 +- source/c02/c02_03.md | 2 +- source/c02/c02_03.rst | 2 +- source/c02/c02_04.md | 2 +- source/c02/c02_04.rst | 2 +- source/c02/c02_05.md | 2 +- source/c02/c02_05.rst | 2 +- source/c02/c02_06.md | 2 +- source/c02/c02_06.rst | 2 +- source/c02/c02_07.md | 2 +- source/c02/c02_07.rst | 2 +- source/c02/c02_08.md | 2 +- source/c02/c02_08.rst | 2 +- source/c02/c02_09.md | 2 +- source/c02/c02_09.rst | 2 +- source/c02/c02_10.md | 2 +- source/c02/c02_10.rst | 2 +- source/c02/c02_11.md | 2 +- source/c02/c02_11.rst | 2 +- source/c02/c02_12.md | 2 +- source/c02/c02_12.rst | 2 +- source/c03/c03_01.md | 2 +- source/c03/c03_01.rst | 2 +- source/c03/c03_02.md | 2 +- source/c03/c03_02.rst | 2 +- source/c03/c03_03.md | 2 +- source/c03/c03_03.rst | 2 +- source/c03/c03_04.md | 2 +- source/c03/c03_04.rst | 2 +- source/c03/c03_05.md | 2 +- source/c03/c03_05.rst | 2 +- source/c03/c03_06.md | 2 +- source/c03/c03_06.rst | 2 +- source/c04/c04_01.md | 2 +- source/c04/c04_01.rst | 2 +- source/c04/c04_02.md | 2 +- source/c04/c04_02.rst | 2 +- source/c04/c04_03.md | 2 +- source/c04/c04_03.rst | 2 +- source/c04/c04_04.md | 2 +- source/c04/c04_04.rst | 2 +- source/c04/c04_05.md | 2 +- source/c04/c04_05.rst | 2 +- source/c04/c04_06.md | 2 +- source/c04/c04_06.rst | 2 +- source/c04/c04_07.md | 2 +- source/c04/c04_07.rst | 2 +- source/c04/c04_08.md | 2 +- source/c04/c04_08.rst | 2 +- source/c04/c04_09.md | 2 +- source/c04/c04_09.rst | 2 +- source/c04/c04_10.md | 2 +- source/c04/c04_10.rst | 2 +- source/c04/c04_11.md | 2 +- source/c04/c04_11.rst | 2 +- source/c04/c04_12.md | 2 +- source/c04/c04_12.rst | 2 +- source/c04/c04_13.md | 2 +- source/c04/c04_13.rst | 2 +- source/c04/c04_14.md | 2 +- source/c04/c04_14.rst | 2 +- source/c04/c04_15.md | 2 +- source/c04/c04_15.rst | 2 +- source/c04/c04_16.md | 2 +- source/c04/c04_16.rst | 2 +- source/c04/c04_17.md | 2 +- source/c04/c04_17.rst | 2 +- source/c04/c04_18.md | 2 +- source/c04/c04_18.rst | 2 +- source/c04/c04_19.md | 2 +- source/c04/c04_19.rst | 2 +- source/c04/c04_21.md | 2 +- source/c04/c04_21.rst | 2 +- source/c05/c05_01.md | 2 +- source/c05/c05_01.rst | 2 +- source/c05/c05_02.md | 2 +- source/c05/c05_02.rst | 2 +- source/c05/c05_03.md | 2 +- source/c05/c05_03.rst | 2 +- source/c06/c06_01.md | 2 +- source/c06/c06_01.rst | 2 +- source/c06/c06_02.md | 2 +- source/c06/c06_02.rst | 2 +- source/c06/c06_03.md | 2 +- source/c06/c06_03.rst | 2 +- source/c06/c06_04.md | 2 +- source/c06/c06_04.rst | 2 +- source/c06/c06_05.md | 2 +- source/c06/c06_05.rst | 2 +- source/c06/c06_06.md | 2 +- source/c06/c06_06.rst | 2 +- source/c07/C07_08.md | 2 +- source/c07/c07_01.md | 2 +- source/c07/c07_01.rst | 2 +- source/c07/c07_02.md | 2 +- source/c07/c07_02.rst | 2 +- source/c07/c07_03.md | 2 +- source/c07/c07_03.rst | 2 +- source/c07/c07_04.md | 2 +- source/c07/c07_04.rst | 2 +- source/c07/c07_05.md | 2 +- source/c07/c07_05.rst | 2 +- source/c07/c07_06.md | 2 +- source/c07/c07_06.rst | 2 +- source/c07/c07_07.md | 2 +- source/c07/c07_07.rst | 2 +- source/c07/c07_08.rst | 2 +- source/c07/c07_10.md | 2 +- source/c07/c07_10.rst | 2 +- source/c08/c08_01.md | 2 +- source/c08/c08_01.rst | 2 +- source/c08/c08_02.md | 2 +- source/c08/c08_02.rst | 2 +- source/c08/c08_03.md | 2 +- source/c08/c08_03.rst | 2 +- source/c08/c08_04.md | 2 +- source/c08/c08_04.rst | 2 +- source/c08/c08_05.md | 2 +- source/c08/c08_05.rst | 2 +- source/c08/c08_06.md | 2 +- source/c08/c08_06.rst | 2 +- source/c08/c08_07.md | 2 +- source/c08/c08_07.rst | 2 +- source/c08/c08_08.md | 2 +- source/c08/c08_08.rst | 2 +- source/c08/c08_09.md | 2 +- source/c08/c08_09.rst | 2 +- source/c08/c08_11.md | 2 +- source/c08/c08_11.rst | 2 +- source/c08/c08_12.md | 2 +- source/c08/c08_12.rst | 2 +- source/c08/c08_13.md | 2 +- source/c08/c08_13.rst | 2 +- source/c08/c08_14.md | 2 +- source/c08/c08_14.rst | 2 +- source/c09/c09_01.md | 303 +++++++++----------------------- source/c09/c09_01.rst | 372 ++++++++++------------------------------ source/c09/c09_02.md | 196 --------------------- source/c09/c09_02.rst | 207 ---------------------- source/c09/c09_03.md | 220 ------------------------ source/c09/c09_03.rst | 235 ------------------------- source/c09/c09_04.md | 240 -------------------------- source/c09/c09_04.rst | 243 -------------------------- source/c09/c09_05.md | 236 ------------------------- source/c09/c09_05.rst | 232 ------------------------- source/c09/c09_06.md | 279 ------------------------------ source/c09/c09_06.rst | 294 ------------------------------- source/c09/c09_07.md | 232 ------------------------- source/c09/c09_07.rst | 220 ------------------------ source/c09/c09_08.md | 257 --------------------------- source/c09/c09_08.rst | 268 ----------------------------- source/c09/c09_09.md | 261 ---------------------------- source/c09/c09_09.rst | 270 ----------------------------- source/c09/c09_10.md | 123 ------------- source/c09/c09_10.rst | 126 -------------- source/c09/c09_11.md | 255 --------------------------- source/c09/c09_11.rst | 258 ---------------------------- source/c09/c09_12.md | 169 ------------------ source/c09/c09_12.rst | 174 ------------------- source/c09/c09_13.md | 173 ------------------- source/c09/c09_13.rst | 176 ------------------- source/c09/c09_14.md | 228 ------------------------ source/c09/c09_14.rst | 238 ------------------------- source/c09/c09_15.md | 235 ------------------------- source/c09/c09_15.rst | 242 -------------------------- source/c09/c09_16.md | 99 ----------- source/c09/c09_16.rst | 101 ----------- source/c09/c09_17.md | 148 ---------------- source/c09/c09_17.rst | 158 ----------------- source/c09/c09_18.md | 131 -------------- source/c09/c09_18.rst | 146 ---------------- source/c09/c09_19.md | 338 ------------------------------------ source/c09/c09_19.rst | 332 ----------------------------------- source/c09/c09_20.md | 175 ------------------- source/c09/c09_20.rst | 179 ------------------- source/c09/c09_21.md | 109 ------------ source/c09/c09_21.rst | 117 ------------- source/c09/c09_22.md | 214 ----------------------- source/c09/c09_22.rst | 228 ------------------------ source/c09/c09_23.md | 130 -------------- source/c09/c09_23.rst | 147 ---------------- source/c09/c09_24.md | 242 -------------------------- source/c09/c09_24.rst | 323 ---------------------------------- source/c09/c09_25.rst | 187 -------------------- source/c09/c09_26.md | 177 ------------------- source/c09/c09_26.rst | 182 -------------------- source/c09/c09_27.md | 197 --------------------- source/c09/c09_27.rst | 264 ---------------------------- source/c09/c09_28.md | 14 -- source/c09/c09_28.rst | 16 -- source/c09/c09_29.md | 158 ----------------- source/c09/c09_29.rst | 187 -------------------- source/c09/c09_40.md | 9 - source/c09/c09_40.rst | 10 -- source/c10/c10_01.md | 144 ---------------- source/c10/c10_01.rst | 160 ----------------- source/chapters/p01.rst | 4 + source/chapters/p02.rst | 4 + source/chapters/p03.rst | 4 + source/chapters/p04.rst | 4 + source/chapters/p05.rst | 4 + source/chapters/p06.rst | 4 + source/chapters/p07.rst | 4 + source/chapters/p08.rst | 4 + source/chapters/p09.rst | 28 +-- source/chapters/p10.rst | 17 -- source/conf.py | 22 +-- source/go_mp_index.md | 60 ------- source/index.rst | 4 + source/preface.rst | 5 +- source/py_mp_index.md | 2 +- source/roadmap.rst | 3 + source/thanks.rst | 7 +- 276 files changed, 438 insertions(+), 12122 deletions(-) delete mode 100644 source/c09/c09_02.md delete mode 100644 source/c09/c09_02.rst delete mode 100644 source/c09/c09_03.md delete mode 100644 source/c09/c09_03.rst delete mode 100644 source/c09/c09_04.md delete mode 100644 source/c09/c09_04.rst delete mode 100644 source/c09/c09_05.md delete mode 100644 source/c09/c09_05.rst delete mode 100644 source/c09/c09_06.md delete mode 100644 source/c09/c09_06.rst delete mode 100644 source/c09/c09_07.md delete mode 100644 source/c09/c09_07.rst delete mode 100644 source/c09/c09_08.md delete mode 100644 source/c09/c09_08.rst delete mode 100644 source/c09/c09_09.md delete mode 100644 source/c09/c09_09.rst delete mode 100644 source/c09/c09_10.md delete mode 100644 source/c09/c09_10.rst delete mode 100644 source/c09/c09_11.md delete mode 100644 source/c09/c09_11.rst delete mode 100644 source/c09/c09_12.md delete mode 100644 source/c09/c09_12.rst delete mode 100644 source/c09/c09_13.md delete mode 100644 source/c09/c09_13.rst delete mode 100644 source/c09/c09_14.md delete mode 100644 source/c09/c09_14.rst delete mode 100644 source/c09/c09_15.md delete mode 100644 source/c09/c09_15.rst delete mode 100644 source/c09/c09_16.md delete mode 100644 source/c09/c09_16.rst delete mode 100644 source/c09/c09_17.md delete mode 100644 source/c09/c09_17.rst delete mode 100644 source/c09/c09_18.md delete mode 100644 source/c09/c09_18.rst delete mode 100644 source/c09/c09_19.md delete mode 100644 source/c09/c09_19.rst delete mode 100644 source/c09/c09_20.md delete mode 100644 source/c09/c09_20.rst delete mode 100644 source/c09/c09_21.md delete mode 100644 source/c09/c09_21.rst delete mode 100644 source/c09/c09_22.md delete mode 100644 source/c09/c09_22.rst delete mode 100644 source/c09/c09_23.md delete mode 100644 source/c09/c09_23.rst delete mode 100644 source/c09/c09_24.md delete mode 100644 source/c09/c09_24.rst delete mode 100644 source/c09/c09_25.rst delete mode 100644 source/c09/c09_26.md delete mode 100644 source/c09/c09_26.rst delete mode 100644 source/c09/c09_27.md delete mode 100644 source/c09/c09_27.rst delete mode 100644 source/c09/c09_28.md delete mode 100644 source/c09/c09_28.rst delete mode 100644 source/c09/c09_29.md delete mode 100644 source/c09/c09_29.rst delete mode 100644 source/c09/c09_40.md delete mode 100644 source/c09/c09_40.rst delete mode 100644 source/c10/c10_01.md delete mode 100644 source/c10/c10_01.rst delete mode 100644 source/chapters/p10.rst delete mode 100644 source/go_mp_index.md diff --git a/README.md b/README.md index be3fdcc..c3a680e 100644 --- a/README.md +++ b/README.md @@ -171,5 +171,5 @@ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/md2rst.py b/md2rst.py index a4cfc62..2f979d7 100644 --- a/md2rst.py +++ b/md2rst.py @@ -32,7 +32,7 @@ ''' readme_tooter = ''' --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) ''' diff --git a/source/.DS_Store b/source/.DS_Store index f213e2c4dd1917a3a9ccc0e9e979751f3b970aed..8b16f25cacfdffc0fff13bc7781052a0a241216d 100644 GIT binary patch delta 1474 zcmbW1YfKzf6vxk57WUpi;qu%aU}5d@b}75Cl%f=&P%Ncb6%f$Mvb!^diOVc7%VLYT zjloxnk?K#^9sTCh8YIwi-?Ji}B13L`{k&-sIkM&pCJI zoSFY`X65#kNe=+vfdx4LP)A!v)3hRo!sK|eS3R|58Rj9Hl{9HD09qo8`fMZ?kF3tU zX85b@nP%EB%0mBLLg;+cm!4uEBY>)PH#3NJxkUUAa?ts1Ums9NCU2~(q^`={TH+SXc30}YrMXE{JCit)x zo>4YSoGu}iO68+TRaYZ1Wn@v66Vu~iJ*sH3s!cKBWn&8wb#6gToY)k(dAlkqC4Nk% z76#+8dR$dx@h0QxjJHyf?lChx^9rppV^+;Nsey|y8`h~&E0SvDO8AVIz>C#=@k~CqKJ`&awM`sm{ z+WMI43#Y5(Ahk7a=DKCsY>IiiQYI`k((l0A<+nV61d;?k=!8DF8=|1WB0K?4!c*`f zoPl$&4)4H+@DY3h7vU0IhVS7=_z8Z7U*K2x107g|CFn#K-hhp`2b*vow&Ts%f!%l* z2XGK?!y!C|_hKl8lXx8CIE(YB;}Wjm!}u6Jj?ds}Jd1019^b&X@LhZlFW~$5DSnRM zSS{L2V9haTne7#?&@O{owunkIPDD*yk0^KFt;L6WcR;G;zmU3|V{(*VP*_x4;jL?I zZtLhaLPahkzr4QuA6S@ZNu_1nJwh5Q_Z#2lyHdhoO2;-OlEv+D?`9;D*mA4gwTw^_ zw)BPd4Q?(nh#H=D_cU?0$Rs{k+HDDPF6;!5mEG4)*(^fE%I@!^EaVg8JY9)byL&kk z$Wq>}>k7Z4?61OEcpE+-!7srVa0PyVtMD61ZO2@a+J$9Uj&7{MTCB$g^kFOR-4eYQ z`|uFqzWrb z*8UPTIW(`u;@T8NHo_IJMUh`L1T@DK043=mvH$=8 diff --git a/source/aboutme.rst b/source/aboutme.rst index cc53a6d..3b60f98 100755 --- a/source/aboutme.rst +++ b/source/aboutme.rst @@ -4,11 +4,11 @@ * 姓名: 王炳明 * 微信: mrbensonwon -* 公众号: Python编程时光 +* 公众号: 《Python编程时光》&《Go编程时光》 * Email: wongbingming@163.com * GitHub: https://github.com/bingmingwong -------------------------------------------- -.. image:: http://image.python-online.cn/20191117155836.png +.. image:: http://image.python-online.cn/20200315144434.png diff --git a/source/c01/c01_01.md b/source/c01/c01_01.md index 10ee36a..399af2b 100644 --- a/source/c01/c01_01.md +++ b/source/c01/c01_01.md @@ -271,4 +271,4 @@ class Person(metaclass=MetaPerson): -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 46e9e6f..375e26a 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -314,7 +314,7 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_05.md b/source/c01/c01_05.md index 8d13b19..b347191 100644 --- a/source/c01/c01_05.md +++ b/source/c01/c01_05.md @@ -166,4 +166,4 @@ foobar() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index 4ea0c74..e164494 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -177,6 +177,6 @@ locals() -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_06.md b/source/c01/c01_06.md index 8b4fefc..09f489d 100644 --- a/source/c01/c01_06.md +++ b/source/c01/c01_06.md @@ -99,4 +99,4 @@ print(Xiamen_dict) -------------- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index 1b19712..ed96ba6 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -104,6 +104,6 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_07.md b/source/c01/c01_07.md index e5ba572..dc053bb 100644 --- a/source/c01/c01_07.md +++ b/source/c01/c01_07.md @@ -291,4 +291,4 @@ b = 2 if a > 2 else 1 - https://foofish.net/idiomatic_part2.html ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index 84e8c34..255870d 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -348,6 +348,6 @@ Pythonic -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_08.md b/source/c01/c01_08.md index d1a93a6..5e43213 100644 --- a/source/c01/c01_08.md +++ b/source/c01/c01_08.md @@ -250,4 +250,4 @@ A.__mro__ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 4b9c6d7..c2e77a4 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -269,7 +269,7 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_09.md b/source/c01/c01_09.md index ed4ae6a..ae35b34 100644 --- a/source/c01/c01_09.md +++ b/source/c01/c01_09.md @@ -66,5 +66,5 @@ class Airplane(Vehicle, PlaneMixin): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index 8af376a..2778631 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -73,6 +73,6 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index 378d9cf..b925039 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -1195,4 +1195,4 @@ linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 187cf2e..0945727 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1310,7 +1310,7 @@ linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_11.md b/source/c01/c01_11.md index 033c550..95e3f7d 100644 --- a/source/c01/c01_11.md +++ b/source/c01/c01_11.md @@ -322,4 +322,4 @@ match.span() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index ea0b985..79d82b7 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -344,6 +344,6 @@ start(),end(),end() -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_12.md b/source/c01/c01_12.md index ea230f7..6a5e8f3 100644 --- a/source/c01/c01_12.md +++ b/source/c01/c01_12.md @@ -134,4 +134,4 @@ GB 18030 与 GB 2312-1980 和 GBK 兼容,共收录汉字70244个。 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index 77ac879..fb8d5cb 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -154,7 +154,7 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_13.md b/source/c01/c01_13.md index 093da5f..7079093 100644 --- a/source/c01/c01_13.md +++ b/source/c01/c01_13.md @@ -124,4 +124,4 @@ from functools import reduce --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index dd30fc1..d201e64 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -157,7 +157,7 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_14.md b/source/c01/c01_14.md index 503b0ce..3c5cb01 100644 --- a/source/c01/c01_14.md +++ b/source/c01/c01_14.md @@ -195,4 +195,4 @@ with open_func('/Users/MING/mytest.txt') as file_in: ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index 3f807a9..7ca5a49 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -209,7 +209,7 @@ open)的上下文管理器。 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_15.md b/source/c01/c01_15.md index 03b14bf..cead07e 100644 --- a/source/c01/c01_15.md +++ b/source/c01/c01_15.md @@ -68,4 +68,4 @@ a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index 06608ea..7be0fdf 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -78,6 +78,6 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_16.md b/source/c01/c01_16.md index a066256..2edf64d 100644 --- a/source/c01/c01_16.md +++ b/source/c01/c01_16.md @@ -159,4 +159,4 @@ hello, world ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst index 2d5f893..31ff68a 100644 --- a/source/c01/c01_16.rst +++ b/source/c01/c01_16.rst @@ -166,6 +166,6 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md index 6ce58df..70a2ae8 100644 --- a/source/c01/c01_17.md +++ b/source/c01/c01_17.md @@ -525,4 +525,4 @@ class Student: --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index 26d6e71..02f0f1e 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -577,7 +577,7 @@ super 的实现原理,就交由你来自己完成。 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md index e8e2e97..3432fa6 100644 --- a/source/c01/c01_20.md +++ b/source/c01/c01_20.md @@ -96,5 +96,5 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index f2fc0c4..bc63617 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -105,7 +105,7 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod 写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md index 5380ddd..5c7e18e 100644 --- a/source/c01/c01_21.md +++ b/source/c01/c01_21.md @@ -83,4 +83,4 @@ with close_stdout(): -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index add2b32..4752247 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -89,6 +89,6 @@ >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) 24 -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_22.md b/source/c01/c01_22.md index 6c7c528..7abd276 100644 --- a/source/c01/c01_22.md +++ b/source/c01/c01_22.md @@ -188,4 +188,4 @@ $ cloud-init init - https://www.cnblogs.com/stonehe/p/7944366.html -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index 54b3838..82cbe3f 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -197,7 +197,7 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip - https://www.cnblogs.com/stonehe/p/7944366.html -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_23.md b/source/c01/c01_23.md index fdf042c..7ff0671 100644 --- a/source/c01/c01_23.md +++ b/source/c01/c01_23.md @@ -123,4 +123,4 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index 9686174..bb9b54c 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -118,6 +118,6 @@ Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5 -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md index 5201704..1f23ffd 100644 --- a/source/c01/c01_24.md +++ b/source/c01/c01_24.md @@ -720,4 +720,4 @@ ok -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index 27ae8e6..5fb76d9 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -793,7 +793,7 @@ sys.path)查找器 - https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc - https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_25.md b/source/c01/c01_25.md index baa1fa8..106141f 100644 --- a/source/c01/c01_25.md +++ b/source/c01/c01_25.md @@ -106,4 +106,4 @@ AttributeError: 'module' object has no attribute '__file__' -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index c96373d..e57fabd 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -105,6 +105,6 @@ 因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md index 85fdf0c..c976d1a 100644 --- a/source/c01/c01_26.md +++ b/source/c01/c01_26.md @@ -259,4 +259,4 @@ int main(int argc, char const *argv[]) -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index 2551678..974c5eb 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -282,6 +282,6 @@ getchar() & putchar() return 0; } -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index f5891f7..8427273 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -640,4 +640,4 @@ $ python setup.py upload -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 74519f4..9031274 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -683,7 +683,7 @@ Index)上,它是 Python - http://blog.konghy.cn/2018/04/29/setup-dot-py/ - https://note.qidong.name/2018/01/python-setup-requires/ -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md index 8f78313..52ccd52 100644 --- a/source/c01/c01_29.md +++ b/source/c01/c01_29.md @@ -8,4 +8,4 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index 2037618..224b310 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -5,6 +5,6 @@ 基于 Python 3.6 的源码分析:https://he11olx.com/ -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_31.md b/source/c01/c01_31.md index 9671c96..683e923 100644 --- a/source/c01/c01_31.md +++ b/source/c01/c01_31.md @@ -20,4 +20,4 @@ RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G) -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index 9c28a3a..4f4a1f5 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -17,6 +17,6 @@ ARGB RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md index c71a352..3655bcb 100644 --- a/source/c01/c01_32.md +++ b/source/c01/c01_32.md @@ -34,4 +34,4 @@ python3 -m pip install --user requests aiohttp cryptography pymysql prettytable -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst index 90c8cf2..c384497 100644 --- a/source/c01/c01_32.rst +++ b/source/c01/c01_32.rst @@ -31,6 +31,6 @@ requests.get(“https://www.baidu.com”) python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_33.md b/source/c01/c01_33.md index b856d20..fcacc16 100644 --- a/source/c01/c01_33.md +++ b/source/c01/c01_33.md @@ -46,7 +46,7 @@ sudo yum install gdb python-debuginfo -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c01/c01_33.rst b/source/c01/c01_33.rst index 3f7438a..9725d42 100644 --- a/source/c01/c01_33.rst +++ b/source/c01/c01_33.rst @@ -44,6 +44,6 @@ sudo yum install gdb python-debuginfo -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_34.md b/source/c01/c01_34.md index df7a029..876acce 100644 --- a/source/c01/c01_34.md +++ b/source/c01/c01_34.md @@ -87,4 +87,4 @@ innodb: foreign key constraint system tables created -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index bdbcb92..b0d8b0c 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -88,7 +88,7 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 innodb: foreign key constraint system tables created -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md index ecf89c7..53917f8 100644 --- a/source/c01/c01_35.md +++ b/source/c01/c01_35.md @@ -339,4 +339,4 @@ trans.close() -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index 497848f..c71d54a 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -353,7 +353,7 @@ Windows,这里就有一件好事,一件坏事了,。 - http://docs.paramiko.org - https://www.liujiangblog.com/blog/15/ -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_01.md b/source/c02/c02_01.md index 5f5db34..59eeb90 100644 --- a/source/c02/c02_01.md +++ b/source/c02/c02_01.md @@ -235,4 +235,4 @@ multi_process(io_simulation, type="模拟IO密集型") - 多进程虽然总是最快的,但是不一定是最优的选择,因为它需要CPU资源支持下才能体现优势 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index db907da..d8c3991 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -248,7 +248,7 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_02.md b/source/c02/c02_02.md index 3ab6094..72786b1 100644 --- a/source/c02/c02_02.md +++ b/source/c02/c02_02.md @@ -133,4 +133,4 @@ t.name = "My-Thread" 至此,Python线程基础知识,我们大概都介绍完了。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index 7de913b..a5acd36 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -144,6 +144,6 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_03.md b/source/c02/c02_03.md index 7c7623c..5a403a5 100644 --- a/source/c02/c02_03.md +++ b/source/c02/c02_03.md @@ -327,4 +327,4 @@ t2.start() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index a9af368..ea131d5 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -354,6 +354,6 @@ CPython,所以也就默许了Python具有GIL锁这个事。 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 5444f6a..d9e0bbb 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -263,4 +263,4 @@ teacher.call('小亮') ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 9fe29cd..d2c3e44 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -287,6 +287,6 @@ Condition和Event 是类似的,并没有多大区别。 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_05.md b/source/c02/c02_05.md index 95cb6ec..3955bf0 100644 --- a/source/c02/c02_05.md +++ b/source/c02/c02_05.md @@ -249,4 +249,4 @@ item5 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 6d2d6f7..4b9088c 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -259,6 +259,6 @@ Out),就是先进入队列的消息,将优先被消费。 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index e74d9d1..fca7bd3 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -100,4 +100,4 @@ running thread-12504:1 构建线程池的方法,是可以很灵活的,大家有空可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 8a700ec..b6f2450 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -109,6 +109,6 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_07.md b/source/c02/c02_07.md index ed96e7f..641a717 100644 --- a/source/c02/c02_07.md +++ b/source/c02/c02_07.md @@ -364,4 +364,4 @@ if __name__ == '__main__': 下一章,我将讲一个Python3.5新引入的语法:`yield from`。篇幅也比较多,所以就单独拿出来讲。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index 03da235..0c3366d 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -390,7 +390,7 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_08.md b/source/c02/c02_08.md index ca79559..38586c7 100644 --- a/source/c02/c02_08.md +++ b/source/c02/c02_08.md @@ -333,4 +333,4 @@ RESULT = _r --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index 75c217c..39b1219 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -359,6 +359,6 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_09.md b/source/c02/c02_09.md index 9c82ac9..28e7f64 100644 --- a/source/c02/c02_09.md +++ b/source/c02/c02_09.md @@ -220,4 +220,4 @@ emmm,和上面的结果是一样的。nice --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 6743988..871f816 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -244,7 +244,7 @@ emmm,和上面的结果是一样的。nice -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_10.md b/source/c02/c02_10.md index 854914c..f88b0f9 100644 --- a/source/c02/c02_10.md +++ b/source/c02/c02_10.md @@ -458,4 +458,4 @@ loop.close() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index 2e62621..23bbc0b 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -507,6 +507,6 @@ asyncio.gather -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_11.md b/source/c02/c02_11.md index d6f9c7f..928eb80 100644 --- a/source/c02/c02_11.md +++ b/source/c02/c02_11.md @@ -211,4 +211,4 @@ Thu May 31 23:42:48 2018 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 87ff018..5e28cb5 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -216,7 +216,7 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c02/c02_12.md b/source/c02/c02_12.md index 143ad34..a28379b 100644 --- a/source/c02/c02_12.md +++ b/source/c02/c02_12.md @@ -108,4 +108,4 @@ MING.send('香肠') --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index 7d50871..8f4e7b7 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -107,6 +107,6 @@ yield -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c03/c03_01.md b/source/c03/c03_01.md index 1f40ee9..5f768e4 100644 --- a/source/c03/c03_01.md +++ b/source/c03/c03_01.md @@ -704,4 +704,4 @@ def timeout_limit(timeout_time): 我的文章更新频率是远低于其他 Python 技术号,但我仍然坚持自己,坚持原创,每周虽然只有一篇,但我能保证我的每一篇文章都是诚意之作。希望那些对你有帮助的文章能够多多帮忙转发分享。这也是我更新的一大动力。非常感谢。 -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 3a025a1..636b94c 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -773,7 +773,7 @@ property 我的文章更新频率是远低于其他 Python 技术号,但我仍然坚持自己,坚持原创,每周虽然只有一篇,但我能保证我的每一篇文章都是诚意之作。希望那些对你有帮助的文章能够多多帮忙转发分享。这也是我更新的一大动力。非常感谢。 -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c03/c03_02.md b/source/c03/c03_02.md index d38f647..ad24cb0 100644 --- a/source/c03/c03_02.md +++ b/source/c03/c03_02.md @@ -344,4 +344,4 @@ in User ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index 01ab177..ca1ea02 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -374,6 +374,6 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c03/c03_03.md b/source/c03/c03_03.md index 998bea0..b260e98 100644 --- a/source/c03/c03_03.md +++ b/source/c03/c03_03.md @@ -257,4 +257,4 @@ if __name__ == '__main__': ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index 6d073ae..2d8ccf0 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -292,7 +292,7 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c03/c03_04.md b/source/c03/c03_04.md index 4dd3df5..dd3de57 100644 --- a/source/c03/c03_04.md +++ b/source/c03/c03_04.md @@ -451,4 +451,4 @@ vim /etc/nginx/nginx.conf --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index 6ce07d3..f27729b 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -510,7 +510,7 @@ -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c03/c03_05.md b/source/c03/c03_05.md index 49afeda..c7991b0 100644 --- a/source/c03/c03_05.md +++ b/source/c03/c03_05.md @@ -457,4 +457,4 @@ with app.app_context(): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index fac243a..fa18b82 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -498,7 +498,7 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c03/c03_06.md b/source/c03/c03_06.md index 71ef5db..7d06c37 100644 --- a/source/c03/c03_06.md +++ b/source/c03/c03_06.md @@ -770,4 +770,4 @@ meth : /etc/netns/ns1/resolv.conf --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index 74de2ea..313b1d5 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -317,7 +317,7 @@ Socket发些“奇怪”的数据。 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c07/c07_06.md b/source/c07/c07_06.md index 87fe7ee..2ea06df 100644 --- a/source/c07/c07_06.md +++ b/source/c07/c07_06.md @@ -280,4 +280,4 @@ docker-machine scp bm-docker-01:/tmp/a bm-docker-02:/tmp/b --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c07/c07_06.rst b/source/c07/c07_06.rst index d920038..3e716dd 100755 --- a/source/c07/c07_06.rst +++ b/source/c07/c07_06.rst @@ -326,7 +326,7 @@ centos的配置文件路径如下,ubuntu的有所不同 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c07/c07_07.md b/source/c07/c07_07.md index 59507bf..fd6d728 100644 --- a/source/c07/c07_07.md +++ b/source/c07/c07_07.md @@ -353,4 +353,4 @@ salt minion01 saltutil.sync_grains # 只同步grains --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c07/c07_07.rst b/source/c07/c07_07.rst index 4b2d451..e0cf0f6 100755 --- a/source/c07/c07_07.rst +++ b/source/c07/c07_07.rst @@ -416,6 +416,6 @@ salt命令格式 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c07/c07_08.rst b/source/c07/c07_08.rst index 8dd89ef..c45882e 100644 --- a/source/c07/c07_08.rst +++ b/source/c07/c07_08.rst @@ -173,6 +173,6 @@ chk_zabbix.sh -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c07/c07_10.md b/source/c07/c07_10.md index a85a22b..abe3257 100644 --- a/source/c07/c07_10.md +++ b/source/c07/c07_10.md @@ -171,5 +171,5 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index 0d8b702..5f59296 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -184,7 +184,7 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_01.md b/source/c08/c08_01.md index cac7472..22ed87a 100644 --- a/source/c08/c08_01.md +++ b/source/c08/c08_01.md @@ -430,4 +430,4 @@ pkill -9 pacemaker;service pacemaker restart --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index 5c7394b..fbace3b 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -444,6 +444,6 @@ aggregate管理 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_02.md b/source/c08/c08_02.md index 33d5456..9a65181 100644 --- a/source/c08/c08_02.md +++ b/source/c08/c08_02.md @@ -264,4 +264,4 @@ select * from pci_devices where instance_uuid='5a1c1828-4190-43fa-8e05-ae51b0196 ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index ec97d50..9e1de14 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -303,7 +303,7 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_03.md b/source/c08/c08_03.md index c9e8932..a2e8aef 100644 --- a/source/c08/c08_03.md +++ b/source/c08/c08_03.md @@ -484,4 +484,4 @@ $ virsh blockcommit ws_controller01 hda --active --verbose --pivot --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index 9632a81..caa18fc 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -528,7 +528,7 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_04.md b/source/c08/c08_04.md index 224f17d..5e9f7c1 100644 --- a/source/c08/c08_04.md +++ b/source/c08/c08_04.md @@ -433,4 +433,4 @@ nova boot \ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index fe58ce2..cf9ca40 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -483,7 +483,7 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_05.md b/source/c08/c08_05.md index 951b650..560347b 100644 --- a/source/c08/c08_05.md +++ b/source/c08/c08_05.md @@ -670,4 +670,4 @@ python -c 'import crypt,getpass;pw=getpass.getpass();print(crypt.crypt(pw,crypt. --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index 01a2553..73503e7 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -730,7 +730,7 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_06.md b/source/c08/c08_06.md index 98e416b..3f96d30 100644 --- a/source/c08/c08_06.md +++ b/source/c08/c08_06.md @@ -624,4 +624,4 @@ ip addr flush dev ens3 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index 8f1d27b..ed5230e 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -705,7 +705,7 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_07.md b/source/c08/c08_07.md index 165995e..2025f33 100644 --- a/source/c08/c08_07.md +++ b/source/c08/c08_07.md @@ -123,4 +123,4 @@ openstack image set --property img_hide_hypervisor_id=true --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index 67c58e6..46485f0 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -148,7 +148,7 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_08.md b/source/c08/c08_08.md index 0ed0ca1..26ba22c 100644 --- a/source/c08/c08_08.md +++ b/source/c08/c08_08.md @@ -185,4 +185,4 @@ systemctl restart neutron-openvswitch-agent --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index 65c3f3f..565c0fb 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -249,7 +249,7 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_09.md b/source/c08/c08_09.md index 01c8cfc..d2df23e 100644 --- a/source/c08/c08_09.md +++ b/source/c08/c08_09.md @@ -676,4 +676,4 @@ server.wait() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index 7ed28d6..9e929df 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -782,7 +782,7 @@ rpc server 和rpc client 的四个重要方法 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_11.md b/source/c08/c08_11.md index 5401819..1514a56 100644 --- a/source/c08/c08_11.md +++ b/source/c08/c08_11.md @@ -80,4 +80,4 @@ $ nova reboot --hard -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index 60e4b61..ec5f8f1 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -82,7 +82,7 @@ ConfigDrive 是一个 iso9660 格式的文件,只读。 # 硬重启,重新生成 xml,如果有必要的话 $ nova reboot --hard -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_12.md b/source/c08/c08_12.md index d356831..a6228ab 100644 --- a/source/c08/c08_12.md +++ b/source/c08/c08_12.md @@ -68,4 +68,4 @@ LOG.debug("Selected host: %(host)s", {'host': chosen_host}) --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index 04b9474..7a8297c 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -86,7 +86,7 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_13.md b/source/c08/c08_13.md index dd58644..585c8b1 100644 --- a/source/c08/c08_13.md +++ b/source/c08/c08_13.md @@ -312,4 +312,4 @@ ovs-vsctl set port vnet0 tag=4 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index f042b5d..26cec6d 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -312,7 +312,7 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c08/c08_14.md b/source/c08/c08_14.md index 5c1d1f2..48f2e9a 100644 --- a/source/c08/c08_14.md +++ b/source/c08/c08_14.md @@ -329,4 +329,4 @@ sudo sysctl -p --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index 9c8f929..9db3710 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -323,7 +323,7 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local -------------- -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index b2cd9f3..a9b3c9a 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -1,281 +1,144 @@ -# 9.1 开发环境的搭建(Goland和VSCode) +# 10.1 情人节来了,教你使用 Python 来表白 -## 1. 为什么学习Go? +**作者**:@明哥 +**公众号**:Python编程时光 -一直很喜欢 Python 的我,为什么突然学起了Go呢? +--- -之前有想通过 Python 转行机算机的同学,应该深有体会,你只学了 Python 就想出去找一份比较不错的工作,其实是比较难的。 +2020年,这个看起来如此浪漫的年份,你还是一个人吗? -以前经常能看到有人盘点,有哪几个国外的大型网站是使用 Python 开发的,当小白看到这个榜单的时候,也许会想原来 Python 做网站也可以这么牛。爱了。 +2018年的时候,写过一篇介绍如何使用 Python 来表白的文章。 -可是你可曾听到过有人盘点有哪些网站是用 Java 开发的?没有吧?印证了那句话:“**一个人越炫耀什么,说明内心越缺少什么**”。 +虽然创意和使用效果都不错,但有一缺点,这是那个exe文件,女神需要打开电脑,才有可能参与进来,进而被你成功"调戏”。 -有不少人都跟我说过,现在很多公司都更倾向于用 Go 来开发项目。然后好奇去了解一番,发现确实很优秀。 +由于是很早期的文章了,应该有很多人没有看过。 -如果按照开发效率、编译与执行效率,编程语言可以大致分为三类: +没有看过的,你可以点击这里查看:[用Python写一个表白神器让你脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) -1. 执行速度快但是编译速度并不理想的语言(以 C++ 为代表) -2. 编译速度较快但执行效率不佳的语言(以 Java 为代表) -3. 开发难度较低但执行速度一般的动态语言呢?(以 Python 为代表) +提醒你一下,后天就是 2月14日了。什么?还是一条狗呢? -三类语言,各有利弊,让人难以选择。 +行吧,那你赶上了,今天的文章,就是为你而写。 -而Go语言,优秀到什么程度? +明哥今天来教你如何使用 Python 来向心中的女神表白。 -它在这 3 个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发。Go语言支持交叉编译,比如说你可以在运行 Linux 系统的计算机上开发可以在 Windows 上运行的应用程序。 +前段时间,在微博上刷到了一条推荐。内容是这样的 -这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。Go语言做到了真正的国际化! +![微博截图](http://image.python-online.cn/20200211211522.png) -## 2. 下载安装 Go语言 +出于好奇,我点开了,放大再放大,emmm,有点意思吖… -下载地址:https://golang.google.cn/dl/ +![img](http://image.python-online.cn/20200211211657.png) -![](http://image.python-online.cn/20200102220841.png) +这四个字,对于像我这样腼腆的DS男来说,还真不好意思说,说出来,万一被拒绝了咋办? -下载完成后,直接双击 msi 文件进行安装,我习惯将软件安装在我的 E 盘下的 `Program Files` 目录下 +使用套路来表白,并观察对方的反应,你大概能清楚对方是否对你也有好感,先测试下自己有几成的把握再下手或许更稳妥。 -![](http://image.python-online.cn/20200102221555.png) +今天就教大家一个这样的套路:如何使用 Python 来做出来这样的图,有点浪漫,又有点极客。能不能拿下你女神,就要靠你(命)了。(๑•́₃ •̀๑) -后面就是一路点击 `Next` 即可,直到出现如下界面,安装完成。 +首先,你得先找到一张你女神的高清图片(尽量分辨率高点的吧,效果会好点)。 -![](http://image.python-online.cn/20200102221840.png) +这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 -## 3. 配置 Goland 环境 +![](http://image.python-online.cn/20200214104413.png) -学习编程语言,使用一个称心的 IDE,可以帮你省去很多麻烦。 +使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? -开发 Python 项目,我习惯使用 PyCharm,因为已经习惯了 JetBrains 风格的IDE,可以替我省去很多熟悉新IDE的成本,所以这里我照样使用 JetBrains 专门为 Go语言开发的IDE:`Goland`。 +![](http://image.python-online.cn/save.jpeg) -Goland 下载地址:https://download.jetbrains.com/go/goland-2019.2.3.exe +然后将这张图片发给你的女神,具体话术你自己想咯。 -双击下载下来的 exe 文件,除了选择安装路径,我惯例更换成 E 盘之外,一路选择 `Next` , 直到如下界面,根据你的需要全选中(推荐全选) +------ -![](http://image.python-online.cn/20200102221932.png) +相比女神来说,你可能更在意这是如何实现的(**活该你单身**)。 -接着一路 `Next`,直到出现如下界面,安装完成,选择 `Run Gogland` 立即运行。 +其实原理很简单,代码也还不到20 行。 -![](http://image.python-online.cn/20200102222123.png) +首先,来讲讲原理。 -此时如果你若没有购买 JetBrains 的激活码,此时是无法使用 Goland 的。 +事实上,每一张图片都是由一个一个的像素点所组成的。而每个像素点,都有自己的颜色,其颜色可以用一个数组来表示:(a,b,c),其中每位数的取值范围都是 0-255。 -![](http://image.python-online.cn/20200102222635.png) +比如(0,0,0)代表黑色色,(255,255,255)代表白色。 -为了让我们的学习更加顺畅,这里会教大家使用 破解的方式获得 Goland 的使用权。 +当像素点足够多的时候,这张照片就是我们所说的高清照片。 -首先下载相关的破解补丁:http://c.biancheng.net/uploads/course/go/Goland_Crack_Sinicization.zip +而如果当像素点太少,我们的肉眼就能感知到明显的锯齿感。 -下载的 zip 包里包含三个文件 +用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 -![](http://image.python-online.cn/20200102222907.png) +![](http://image.python-online.cn/20200214104646.png) -- jetbrains-agent.jar:破解补丁 -- resources_cn.jar:汉化补丁 -- 激活码.txt:激活码 +我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 -将 jetbrains-agent.jar 拷贝到 你的 Goland 安装目录的bin文件夹下,我的路径是:E:\Program Files\JetBrains\GoLand 2019.2.3\bin +有了思路,就可以开始我们的代码。 -然后用编辑器打开这两个文件 +首先,使用 pillow.Image读取图像,并使用load函数获取到每一个像素值。 -![](http://image.python-online.cn/20200102223113.png) - -在最后一行添加如下一行,你要根据自己路径对应修改后面的值 - -```shell --javaagent:E:\Program Files\JetBrains\GoLand 2019.2.3\bin\jetbrains-agent.jar +```python +img_raw = Image.open(img_path) +img_array = img_raw.load() ``` -接着打开回到你的 Goland 启动界面,点击 `Activation code`,复制 `激活码.txt` 中的激活码,填入再点击 OK - -![](http://image.python-online.cn/20200102223451.png) - -此时你的 Goland 已经可以正常使用了,创建我的项目目录,顺便设置好 GOROOT 。 - -![](http://image.python-online.cn/20200102223946.png) - -创建好Project后,再点击 Files->Settings->GOPATH,添加我们的项目目录` F:\Go-Player` - -![](http://image.python-online.cn/20200102224643.png) - -随便点击一个go文件,就能在下图箭头处看到配置入口,点击进入配置一下 Go运行器。 - -![](http://image.python-online.cn/20200102225750.png) - -按照如下指示进行配置。 - -![](http://image.python-online.cn/20200102225349.png) - -去掉参数提示 - -![](http://image.python-online.cn/20200127192147.png) - -设置 goproxy - -![](http://image.python-online.cn/20200127192512.png) - -设置 goimports(自动格式化插件),如果 你之前 没有安装 ,会提示你点击 `yes` 下载安装 。 - -![](http://image.python-online.cn/20200127192748.png) +然后新建一张画布,并选好你要使用的字体和字体大小。 -至此,环境配置完成。 - -在项目根目录下,创建如下三个文件夹,并在 src 目录下创建一个hello.go 的文件。 - -![](http://image.python-online.cn/20200102224417.png) - -点击运行按钮,在控制台我们看到了熟悉的 `Hello, World!` - -![](http://image.python-online.cn/20200102225550.png) - -## 4. 配置 VS Code 环境 - -提前设置用户级的环境变量 - -``` -GOPATH = F:\Go-Player -PATH = %GOPATH%\bin # 以追加的方式 +```python +img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) +draw = ImageDraw.Draw(img_new) +font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) ``` -昨天评论区有人问,GOPATH 和 GOROOT 是什么?为什么需要设置?回想一下 你学 Python 的话,安装 Python 解释器的时候,是不是也要设置环境变量?这里也是类似。 - -GOROOT :在GO语言中表示的是 Go语言编译、工具、标准库等的安装路径,通过它可以告诉系统你的 go.exe 是放在哪里,不设置的话,你后面执行 `go get` 、`go install` 的时候,系统就不认识它了。 - -而 GOPATH环境变量则表示 Go的工作目录,这个目录指定了需要从哪个地方寻找GO的包、可执行程序等,这个目录可以是多个目录表示。这里我设置成我的工作空间(目录你可以自己定) :`F:\Go-Player`,如果不设置的话 ,默认是在你的用户目录下的 go 文件夹。 - -这时要再说一点,GO 项目中,一般来说它的工作目录结构是这样的: - -- bin目录:包含了可执行程序,注意是可执行的,不需要解释执行。 - -- pkg目录:包含了使用的包或者说库。 - -- src目录:里面包含了go的代码源文件,其中仍按包的不同进行组织。 - -所以后面我的创建的GO工作目录,也是按照这个标准来,先说明一下。 - +由于需要不断循环 “我喜欢你!”,这五个字符。所以这里可以while循环 yield 来实现一个生成器。 - -接下来,要开始配置 VS Code 环境。 - -打开你的 VS Code软件,先确认你设置的环境变量已经生效,点击 `Terminal` -> `New Terminal`,使用 cmd 命令查看环境变量。 - -![](http://image.python-online.cn/20200109210630.png) - -如上图所求,我的环境变量是OK的,如果你的输出是指向你的用户目录:`%USERPROFILE%\go` 建议你不要折腾(因为我无论重启多少次 VS Code,其记录的GOPATH始终指向%USERPROFILE%\go), 直接重启你的电脑。 - -好了之后,我们要从 github 上下载两个仓库,之所以要手动下载,是因为有墙的存在,在线安装的话,很多插件你会下载失败。 - -创建目录 `src/goland.org/x/`,并进入此目录,执行命令 - -```shell -$ git clone https://github.com/golang/tools.git -$ git clone https://github.com/golang/lint.git +```python +def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] ``` -点击 `File` - `Open Folder` 安装两个插件: - -第一个是:Go 语言的扩展插件 +最后,要给这些字加上相应的颜色,写入新创建的画布中。 -![](http://image.python-online.cn/20200108202934.png) - -第二个是:Code Runner,让你的 VS Code 能够编译运行 Go 的程序。 - -![](http://image.python-online.cn/20200109153948.png) - - - -随便点开一个 go 文件,在你的右下角会提示要你安装一些工具,安装的包有些由于墙的原因,无法下载,为了保证下载顺利,可以设置一下代理。 - -```shell -$ go env -w GOPROXY=https://goproxy.cn,direct +```python +for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) ``` -然后再点击 `Install All` - -![](http://image.python-online.cn/20200109210654.png) - -然后你在 OUTPUT 就能看到安装进度 +最后将成品保存 -![](http://image.python-online.cn/20200109211543.png) - -安装的 exe 文件会放在 %GOPATH%/bin 下,也就是 `F:\Go-Player\bin` - -![](http://image.python-online.cn/20200109213056.png) - -而此的 src 目录结构是这样的 - -![](http://image.python-online.cn/20200109214117.png) - - - -到这时环境配置完成,编写 HelloWorld,并运行查看输出,一切完成。 - -![](http://image.python-online.cn/20200109154657.png) - - - -## 5. 配置环境变量 - -当你在终端使用 `go env` 的时候,会打印出go 相关的所有环境变量 - -```shell -$ go env -set GO111MODULE= -set GOARCH=amd64 -set GOBIN= -set GOCACHE=C:\Users\wangbm\AppData\Local\go-build -set GOENV=C:\Users\wangbm\AppData\Roaming\go\env -set GOEXE=.exe -set GOFLAGS= -set GOHOSTARCH=amd64 -set GOHOSTOS=windows -set GONOPROXY= -set GONOSUMDB= -set GOOS=windows -set GOPATH=E:\MING-Code\GoPlayer -set GOPRIVATE= -set GOPROXY=https://goproxy.cn,direct -set GOROOT=D:\Program Files (x86)\Go-1.13.6 -set GOSUMDB=sum.golang.org -set GOTMPDIR= -set GOTOOLDIR=D:\Program Files (x86)\Go-1.13.6\pkg\tool\windows_amd64 -set GCCGO=gccgo -set AR=ar -set CC=gcc -set CXX=g++ -set CGO_ENABLED=1 -set GOMOD= -set CGO_CFLAGS=-g -O2 -set CGO_CPPFLAGS= -set CGO_CXXFLAGS=-g -O2 -set CGO_FFLAGS=-g -O2 -set CGO_LDFLAGS=-g -O2 -set PKG_CONFIG=pkg-config +```python +img_new.convert('RGB').save("F://gyy_save.jpeg") ``` -想查看几个特定的环境变量就加在 `go env` 后面 +完整代码如下,供你参考 -```shell -$ go env GOPATH -E:\MING-Code\GoPlayer -$ go env GOROOT -D:\Program Files (x86)\Go-1.13.6 -$ go env GOPROXY -https://goproxy.cn,direct -``` +```python +from PIL import Image, ImageDraw, ImageFont -以上环境变量很多,这里仅设置下面这两个就足够了 +font_size = 7 +text = "我喜欢你!" +img_path = "F://gyy.jpeg" -- 一个是GO111MODULE 设置为 on,表示使用 go modules 模式 +img_raw = Image.open(img_path) +img_array = img_raw.load() + +img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) +draw = ImageDraw.Draw(img_new) +font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) -```shell -$ go env -w GO111MODULE=on -``` +def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] + +ch_gen = character_generator(text) -- 一个是开启代理,防止下载包失败(前面可能你已经设置过) +for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) -```shell -$ go env -w GOPROXY=https://goproxy.cn,direct +img_new.convert('RGB').save("F://save.jpeg") ``` -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 5c96a5e..50fed20 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -1,344 +1,160 @@ -9.1 开发环境的搭建(Goland和VSCode) -==================================== +10.1 情人节来了,教你使用 Python 来表白 +======================================= -1. 为什么学习Go? ------------------ +**作者**\ :@明哥 **公众号**\ :Python编程时光 -一直很喜欢 Python 的我,为什么突然学起了Go呢? +-------------- -之前有想通过 Python 转行机算机的同学,应该深有体会,你只学了 Python -就想出去找一份比较不错的工作,其实是比较难的。 +2020年,这个看起来如此浪漫的年份,你还是一个人吗? -以前经常能看到有人盘点,有哪几个国外的大型网站是使用 Python -开发的,当小白看到这个榜单的时候,也许会想原来 Python -做网站也可以这么牛。爱了。 +2018年的时候,写过一篇介绍如何使用 Python 来表白的文章。 -可是你可曾听到过有人盘点有哪些网站是用 Java -开发的?没有吧?印证了那句话:“**一个人越炫耀什么,说明内心越缺少什么**”。 +虽然创意和使用效果都不错,但有一缺点,这是那个exe文件,女神需要打开电脑,才有可能参与进来,进而被你成功“调戏”。 -有不少人都跟我说过,现在很多公司都更倾向于用 Go -来开发项目。然后好奇去了解一番,发现确实很优秀。 +由于是很早期的文章了,应该有很多人没有看过。 -如果按照开发效率、编译与执行效率,编程语言可以大致分为三类: +没有看过的,你可以点击这里查看:\ `用Python写一个表白神器让你脱离单身 `__ -1. 执行速度快但是编译速度并不理想的语言(以 C++ 为代表) -2. 编译速度较快但执行效率不佳的语言(以 Java 为代表) -3. 开发难度较低但执行速度一般的动态语言呢?(以 Python 为代表) +提醒你一下,后天就是 2月14日了。什么?还是一条狗呢? -三类语言,各有利弊,让人难以选择。 +行吧,那你赶上了,今天的文章,就是为你而写。 -而Go语言,优秀到什么程度? +明哥今天来教你如何使用 Python 来向心中的女神表白。 -它在这 3 -个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发。Go语言支持交叉编译,比如说你可以在运行 -Linux 系统的计算机上开发可以在 Windows 上运行的应用程序。 +前段时间,在微博上刷到了一条推荐。内容是这样的 -这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 -编码的字符串,就连它的源码文件格式都是使用的 UTF-8 -编码。Go语言做到了真正的国际化! +.. figure:: http://image.python-online.cn/20200211211522.png + :alt: 微博截图 -2. 下载安装 Go语言 ------------------- + 微博截图 -下载地址:https://golang.google.cn/dl/ +出于好奇,我点开了,放大再放大,emmm,有点意思吖… -|image0| - -下载完成后,直接双击 msi 文件进行安装,我习惯将软件安装在我的 E 盘下的 -``Program Files`` 目录下 - -|image1| - -后面就是一路点击 ``Next`` 即可,直到出现如下界面,安装完成。 - -|image2| - -3. 配置 Goland 环境 -------------------- - -学习编程语言,使用一个称心的 IDE,可以帮你省去很多麻烦。 - -开发 Python 项目,我习惯使用 PyCharm,因为已经习惯了 JetBrains -风格的IDE,可以替我省去很多熟悉新IDE的成本,所以这里我照样使用 JetBrains -专门为 Go语言开发的IDE:\ ``Goland``\ 。 - -Goland 下载地址:https://download.jetbrains.com/go/goland-2019.2.3.exe - -双击下载下来的 exe 文件,除了选择安装路径,我惯例更换成 E -盘之外,一路选择 ``Next`` , -直到如下界面,根据你的需要全选中(推荐全选) - -|image3| - -接着一路 ``Next``\ ,直到出现如下界面,安装完成,选择 ``Run Gogland`` -立即运行。 - -|image4| - -此时如果你若没有购买 JetBrains 的激活码,此时是无法使用 Goland 的。 - -|image5| - -为了让我们的学习更加顺畅,这里会教大家使用 破解的方式获得 Goland -的使用权。 - -首先下载相关的破解补丁:http://c.biancheng.net/uploads/course/go/Goland_Crack_Sinicization.zip - -下载的 zip 包里包含三个文件 - -|image6| - -- jetbrains-agent.jar:破解补丁 -- resources_cn.jar:汉化补丁 -- 激活码.txt:激活码 - -将 jetbrains-agent.jar 拷贝到 你的 Goland -安装目录的bin文件夹下,我的路径是:E::raw-latex:`\Program `Files:raw-latex:`\JetBrains`:raw-latex:`\GoLand `2019.2.3:raw-latex:`\bin` - -然后用编辑器打开这两个文件 - -|image7| - -在最后一行添加如下一行,你要根据自己路径对应修改后面的值 - -.. code:: shell - - -javaagent:E:\Program Files\JetBrains\GoLand 2019.2.3\bin\jetbrains-agent.jar - -接着打开回到你的 Goland 启动界面,点击 ``Activation code``\ ,复制 -``激活码.txt`` 中的激活码,填入再点击 OK +.. figure:: http://image.python-online.cn/20200211211657.png + :alt: img -|image8| + img -此时你的 Goland 已经可以正常使用了,创建我的项目目录,顺便设置好 GOROOT -。 +这四个字,对于像我这样腼腆的DS男来说,还真不好意思说,说出来,万一被拒绝了咋办? -|image9| +使用套路来表白,并观察对方的反应,你大概能清楚对方是否对你也有好感,先测试下自己有几成的把握再下手或许更稳妥。 -创建好Project后,再点击 -Files->Settings->GOPATH,添加我们的项目目录\ ``F:\Go-Player`` +今天就教大家一个这样的套路:如何使用 Python +来做出来这样的图,有点浪漫,又有点极客。能不能拿下你女神,就要靠你(命)了。(๑•́₃ +•̀๑) -|image10| +首先,你得先找到一张你女神的高清图片(尽量分辨率高点的吧,效果会好点)。 -随便点击一个go文件,就能在下图箭头处看到配置入口,点击进入配置一下 -Go运行器。 +这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 -|image11| - -按照如下指示进行配置。 - -|image12| - -去掉参数提示 - -|image13| - -设置 goproxy - -|image14| - -设置 goimports(自动格式化插件),如果 你之前 没有安装 ,会提示你点击 -``yes`` 下载安装 。 - -|image15| - -至此,环境配置完成。 - -在项目根目录下,创建如下三个文件夹,并在 src 目录下创建一个hello.go -的文件。 - -|image16| - -点击运行按钮,在控制台我们看到了熟悉的 ``Hello, World!`` - -|image17| - -4. 配置 VS Code 环境 --------------------- - -提前设置用户级的环境变量 - -:: - - GOPATH = F:\Go-Player - PATH = %GOPATH%\bin # 以追加的方式 - -昨天评论区有人问,GOPATH 和 GOROOT 是什么?为什么需要设置?回想一下 你学 -Python 的话,安装 Python -解释器的时候,是不是也要设置环境变量?这里也是类似。 - -GOROOT :在GO语言中表示的是 -Go语言编译、工具、标准库等的安装路径,通过它可以告诉系统你的 go.exe -是放在哪里,不设置的话,你后面执行 ``go get`` 、\ ``go install`` -的时候,系统就不认识它了。 - -而 GOPATH环境变量则表示 -Go的工作目录,这个目录指定了需要从哪个地方寻找GO的包、可执行程序等,这个目录可以是多个目录表示。这里我设置成我的工作空间(目录你可以自己定) -:\ ``F:\Go-Player``\ ,如果不设置的话 ,默认是在你的用户目录下的 go -文件夹。 - -这时要再说一点,GO 项目中,一般来说它的工作目录结构是这样的: - -- bin目录:包含了可执行程序,注意是可执行的,不需要解释执行。 - -- pkg目录:包含了使用的包或者说库。 - -- src目录:里面包含了go的代码源文件,其中仍按包的不同进行组织。 - -所以后面我的创建的GO工作目录,也是按照这个标准来,先说明一下。 - -接下来,要开始配置 VS Code 环境。 +|image0| -打开你的 VS Code软件,先确认你设置的环境变量已经生效,点击 ``Terminal`` --> ``New Terminal``\ ,使用 cmd 命令查看环境变量。 +使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? -|image18| +|image1| -如上图所求,我的环境变量是OK的,如果你的输出是指向你的用户目录:\ ``%USERPROFILE%\go`` -建议你不要折腾(因为我无论重启多少次 VS -Code,其记录的GOPATH始终指向%USERPROFILE%:raw-latex:`\go`), -直接重启你的电脑。 +然后将这张图片发给你的女神,具体话术你自己想咯。 -好了之后,我们要从 github -上下载两个仓库,之所以要手动下载,是因为有墙的存在,在线安装的话,很多插件你会下载失败。 +-------------- -创建目录 ``src/goland.org/x/``\ ,并进入此目录,执行命令 +相比女神来说,你可能更在意这是如何实现的(\ **活该你单身**\ )。 -.. code:: shell +其实原理很简单,代码也还不到20 行。 - $ git clone https://github.com/golang/tools.git - $ git clone https://github.com/golang/lint.git +首先,来讲讲原理。 -点击 ``File`` - ``Open Folder`` 安装两个插件: +事实上,每一张图片都是由一个一个的像素点所组成的。而每个像素点,都有自己的颜色,其颜色可以用一个数组来表示:(a,b,c),其中每位数的取值范围都是 +0-255。 -第一个是:Go 语言的扩展插件 +比如(0,0,0)代表黑色色,(255,255,255)代表白色。 -|image19| +当像素点足够多的时候,这张照片就是我们所说的高清照片。 -第二个是:Code Runner,让你的 VS Code 能够编译运行 Go 的程序。 +而如果当像素点太少,我们的肉眼就能感知到明显的锯齿感。 -|image20| +用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 +5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 -随便点开一个 go -文件,在你的右下角会提示要你安装一些工具,安装的包有些由于墙的原因,无法下载,为了保证下载顺利,可以设置一下代理。 +|image2| -.. code:: shell +我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 - $ go env -w GOPROXY=https://goproxy.cn,direct +有了思路,就可以开始我们的代码。 -然后再点击 ``Install All`` +首先,使用 pillow.Image读取图像,并使用load函数获取到每一个像素值。 -|image21| +.. code:: python -然后你在 OUTPUT 就能看到安装进度 + img_raw = Image.open(img_path) + img_array = img_raw.load() -|image22| +然后新建一张画布,并选好你要使用的字体和字体大小。 -安装的 exe 文件会放在 %GOPATH%/bin 下,也就是 ``F:\Go-Player\bin`` +.. code:: python -|image23| + img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) + draw = ImageDraw.Draw(img_new) + font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) -而此的 src 目录结构是这样的 +由于需要不断循环 “我喜欢你!”,这五个字符。所以这里可以while循环 yield +来实现一个生成器。 -|image24| +.. code:: python -到这时环境配置完成,编写 HelloWorld,并运行查看输出,一切完成。 + def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] -|image25| +最后,要给这些字加上相应的颜色,写入新创建的画布中。 -5. 配置环境变量 ---------------- +.. code:: python -当你在终端使用 ``go env`` 的时候,会打印出go 相关的所有环境变量 + for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) -.. code:: shell +最后将成品保存 - $ go env - set GO111MODULE= - set GOARCH=amd64 - set GOBIN= - set GOCACHE=C:\Users\wangbm\AppData\Local\go-build - set GOENV=C:\Users\wangbm\AppData\Roaming\go\env - set GOEXE=.exe - set GOFLAGS= - set GOHOSTARCH=amd64 - set GOHOSTOS=windows - set GONOPROXY= - set GONOSUMDB= - set GOOS=windows - set GOPATH=E:\MING-Code\GoPlayer - set GOPRIVATE= - set GOPROXY=https://goproxy.cn,direct - set GOROOT=D:\Program Files (x86)\Go-1.13.6 - set GOSUMDB=sum.golang.org - set GOTMPDIR= - set GOTOOLDIR=D:\Program Files (x86)\Go-1.13.6\pkg\tool\windows_amd64 - set GCCGO=gccgo - set AR=ar - set CC=gcc - set CXX=g++ - set CGO_ENABLED=1 - set GOMOD= - set CGO_CFLAGS=-g -O2 - set CGO_CPPFLAGS= - set CGO_CXXFLAGS=-g -O2 - set CGO_FFLAGS=-g -O2 - set CGO_LDFLAGS=-g -O2 - set PKG_CONFIG=pkg-config +.. code:: python -想查看几个特定的环境变量就加在 ``go env`` 后面 + img_new.convert('RGB').save("F://gyy_save.jpeg") -.. code:: shell +完整代码如下,供你参考 - $ go env GOPATH - E:\MING-Code\GoPlayer - $ go env GOROOT - D:\Program Files (x86)\Go-1.13.6 - $ go env GOPROXY - https://goproxy.cn,direct +.. code:: python -以上环境变量很多,这里仅设置下面这两个就足够了 + from PIL import Image, ImageDraw, ImageFont -- 一个是GO111MODULE 设置为 on,表示使用 go modules 模式 + font_size = 7 + text = "我喜欢你!" + img_path = "F://gyy.jpeg" -.. code:: shell + img_raw = Image.open(img_path) + img_array = img_raw.load() + + img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) + draw = ImageDraw.Draw(img_new) + font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) - $ go env -w GO111MODULE=on + def character_generator(text): + while True: + for i in range(len(text)): + yield text[i] -- 一个是开启代理,防止下载包失败(前面可能你已经设置过) + ch_gen = character_generator(text) -.. code:: shell + for y in range(0, img_raw.size[1], font_size): + for x in range(0, img_raw.size[0], font_size): + draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) - $ go env -w GOPROXY=https://goproxy.cn,direct + img_new.convert('RGB').save("F://save.jpeg") -.. figure:: http://image.python-online.cn/20191117155836.png +.. figure:: http://image.python-online.cn/20200315144434.png :alt: 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20200102220841.png -.. |image1| image:: http://image.python-online.cn/20200102221555.png -.. |image2| image:: http://image.python-online.cn/20200102221840.png -.. |image3| image:: http://image.python-online.cn/20200102221932.png -.. |image4| image:: http://image.python-online.cn/20200102222123.png -.. |image5| image:: http://image.python-online.cn/20200102222635.png -.. |image6| image:: http://image.python-online.cn/20200102222907.png -.. |image7| image:: http://image.python-online.cn/20200102223113.png -.. |image8| image:: http://image.python-online.cn/20200102223451.png -.. |image9| image:: http://image.python-online.cn/20200102223946.png -.. |image10| image:: http://image.python-online.cn/20200102224643.png -.. |image11| image:: http://image.python-online.cn/20200102225750.png -.. |image12| image:: http://image.python-online.cn/20200102225349.png -.. |image13| image:: http://image.python-online.cn/20200127192147.png -.. |image14| image:: http://image.python-online.cn/20200127192512.png -.. |image15| image:: http://image.python-online.cn/20200127192748.png -.. |image16| image:: http://image.python-online.cn/20200102224417.png -.. |image17| image:: http://image.python-online.cn/20200102225550.png -.. |image18| image:: http://image.python-online.cn/20200109210630.png -.. |image19| image:: http://image.python-online.cn/20200108202934.png -.. |image20| image:: http://image.python-online.cn/20200109153948.png -.. |image21| image:: http://image.python-online.cn/20200109210654.png -.. |image22| image:: http://image.python-online.cn/20200109211543.png -.. |image23| image:: http://image.python-online.cn/20200109213056.png -.. |image24| image:: http://image.python-online.cn/20200109214117.png -.. |image25| image:: http://image.python-online.cn/20200109154657.png +.. |image0| image:: http://image.python-online.cn/20200214104413.png +.. |image1| image:: http://image.python-online.cn/save.jpeg +.. |image2| image:: http://image.python-online.cn/20200214104646.png diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md deleted file mode 100644 index b3b9448..0000000 --- a/source/c09/c09_02.md +++ /dev/null @@ -1,196 +0,0 @@ -# 9.2 五种变量创建的方法 - -对于只有 Python 语言经验的朋友,也许会不太理解声明这个词,在 Python 中直接拿来就用,也不用声明类型啥的。 - -Go 语言是静态类型语言,由于编译时,编译器会检查变量的类型,所以要求所有的变量都要有明确的类型。 - -变量在使用前,需要先声明。声明类型,就约定了你这个变量只能赋该类型的值。 - -声明一般有以下四种方法,其中前面两种同样也可用于定义常量,只需把关键字 `var` 变成 `const` 即可。 - -**第一种** :一行声明一个变量 - -``` -var -``` - -其中 var 是关键字(固定不变),name 是变量名,type 是类型。 - -使用 var ,虽然只指定了类型,但是 Go 会对其进行隐式初始化,比如 string 类型就初始化为空字符串,int 类型就初始化为0,float 就初始化为 0.0,bool类型就初始化为false,指针类型就初始化为 nil。 - -若想在声明过程,顺便也初始化,可以这样写 - -```go -var name sting = "Python编程时光" -``` - -在 Go 文件中的完整代码如下,为了不写重复性的代码,后续不再貼完整代码,只貼关键代码 - -```go -package main - -import "fmt" - -func main() { - var name string = "Python编程时光" - fmt.Println(name) -} -``` - -从右值(等号右边的值,rvalue)来看,明显是个 string 类型(这里要注意,在 Python 双引号与单引号等价,但在 Go 中双引号和单引号是不一样的,这里要一定要使用双引号,表示字符串,而在单引号表示rune 类型的字符,这个后续会单独介绍),因此也可以将其简化为 - -```go -var name = "Python编程时光" -``` - -若你的右值带有小数点,在不指定类型的情况下,编译器会将你的这个变量声明为 float64,但是很多情况下,我们并不需要这么高的精度(占用的内存空间更大) - -这种情况下,推荐指定类型,不要偷懒 - -```go -var rate float32 0.89 -``` - -**第二种**:多个变量一起声明 - -声明多个变量,除了可以按照上面写成多行之外,还可以写成下面这样 - -```go -var ( - name string - age int - gender string -) -``` - -**第三种**:声明和初始化一个变量 - -使用 `:=` (推导声明写法或者短类型声明法:编译器会自动根据右值类型推断出左值的对应类型。),可以声明一个变量,并对其进行(显式)初始化。 - -```go -name := "Python编程时光" - -// 等价于 - -var name string = "Python编程时光" - -// 等价于 - -var name = "Python编程时光" -``` - -但这种方法有个限制就是,只能用于函数内部 - -**第四种**:声明和初始化多个变量 - -```go -name, age := "wangbm", 28 -``` - -这种方法,也经常用于变量的交换 - -```go -var a int = 100 -var b int = 200 -b, a = a, b -``` - -**第五种**:new 函数声明一个指针变量 - -在这里要先讲一下,指针的相关内容。 - -变量分为两种 `普通变量` 和 `指针变量` - -普通变量,存放的是数据本身,而指针变量存放的是数据的地址。 - -如下代码,age 是一个普通变量,存放的内容是 28,而 ptr 是 存放变量age值的内存地址:0xc000010098 - -```go -package main - -import "fmt" - -func main() { - var age int = 28 - var ptr = &age // &后面接变量名,表示取出该变量的内存地址 - fmt.Println("age: ", age) - fmt.Println("ptr: ", ptr) -} -``` - -输出 - -``` -age: 28 -ptr: 0xc000010098 -``` - -而这里要说的 new 函数,是 Go 里的一个内建函数。 - -使用表达式 new(Type) 将创建一个Type类型的匿名变量,初始化为Type类型的零值,然后返回变量地址,返回的指针类型为`*Type`。 - -```go -package main - -import "fmt" - -func main() { - ptr := new(int) - fmt.Println("ptr address: ", ptr) - fmt.Println("ptr value: ", *ptr) // * 后面接指针变量,表示从内存地址中取出值 -} - -``` - -输出 - -``` -ptr address: 0xc000010098 -ptr value: 0 -``` - -用new创建变量和普通变量声明语句方式创建变量没有什么区别,除了不需要声明一个临时变量的名字外,我们还可以在表达式中使用new(Type)。换言之,new函数类似是一种语法糖,而不是一个新的基础概念。 - -如下两种写法,可以说是等价的 - -```go -// 使用 new -func newInt() *int { - return new(int) -} - -// 使用传统的方式 -func newInt() *int { - var dummy int - return &dummy -} -``` - - - -以上不管哪种方法,变量/常量都只能声明一次,声明多次,编译就会报错。 - -但也有例外,这就要说到一个特殊变量:**匿名变量**,也称作占位符,或者空白标识符,用下划线表示。 - -匿名变量,优点有三: - -- 不分配内存,不占用内存空间 -- 不需要你为命名无用的变量名而纠结 -- 多次声明不会有任何问题 - -通常我们用匿名接收必须接收,但是又不会用到的值。 - -```go -func GetData() (int, int) { - return 100, 200 -} -func main(){ - a, _ := GetData() - _, b := GetData() - fmt.Println(a, b) -} -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst deleted file mode 100644 index 1236e23..0000000 --- a/source/c09/c09_02.rst +++ /dev/null @@ -1,207 +0,0 @@ -9.2 五种变量创建的方法 -====================== - -对于只有 Python 语言经验的朋友,也许会不太理解声明这个词,在 Python -中直接拿来就用,也不用声明类型啥的。 - -Go -语言是静态类型语言,由于编译时,编译器会检查变量的类型,所以要求所有的变量都要有明确的类型。 - -变量在使用前,需要先声明。声明类型,就约定了你这个变量只能赋该类型的值。 - -声明一般有以下四种方法,其中前面两种同样也可用于定义常量,只需把关键字 -``var`` 变成 ``const`` 即可。 - -**第一种** :一行声明一个变量 - -:: - - var - -其中 var 是关键字(固定不变),name 是变量名,type 是类型。 - -使用 var ,虽然只指定了类型,但是 Go 会对其进行隐式初始化,比如 string -类型就初始化为空字符串,int 类型就初始化为0,float 就初始化为 -0.0,bool类型就初始化为false,指针类型就初始化为 nil。 - -若想在声明过程,顺便也初始化,可以这样写 - -.. code:: go - - var name sting = "Python编程时光" - -在 Go -文件中的完整代码如下,为了不写重复性的代码,后续不再貼完整代码,只貼关键代码 - -.. code:: go - - package main - - import "fmt" - - func main() { - var name string = "Python编程时光" - fmt.Println(name) - } - -从右值(等号右边的值,rvalue)来看,明显是个 string 类型(这里要注意,在 -Python 双引号与单引号等价,但在 Go -中双引号和单引号是不一样的,这里要一定要使用双引号,表示字符串,而在单引号表示rune -类型的字符,这个后续会单独介绍),因此也可以将其简化为 - -.. code:: go - - var name = "Python编程时光" - -若你的右值带有小数点,在不指定类型的情况下,编译器会将你的这个变量声明为 -float64,但是很多情况下,我们并不需要这么高的精度(占用的内存空间更大) - -这种情况下,推荐指定类型,不要偷懒 - -.. code:: go - - var rate float32 0.89 - -**第二种**\ :多个变量一起声明 - -声明多个变量,除了可以按照上面写成多行之外,还可以写成下面这样 - -.. code:: go - - var ( - name string - age int - gender string - ) - -**第三种**\ :声明和初始化一个变量 - -使用 ``:=`` -(推导声明写法或者短类型声明法:编译器会自动根据右值类型推断出左值的对应类型。),可以声明一个变量,并对其进行(显式)初始化。 - -.. code:: go - - name := "Python编程时光" - - // 等价于 - - var name string = "Python编程时光" - - // 等价于 - - var name = "Python编程时光" - -但这种方法有个限制就是,只能用于函数内部 - -**第四种**\ :声明和初始化多个变量 - -.. code:: go - - name, age := "wangbm", 28 - -这种方法,也经常用于变量的交换 - -.. code:: go - - var a int = 100 - var b int = 200 - b, a = a, b - -**第五种**\ :new 函数声明一个指针变量 - -在这里要先讲一下,指针的相关内容。 - -变量分为两种 ``普通变量`` 和 ``指针变量`` - -普通变量,存放的是数据本身,而指针变量存放的是数据的地址。 - -如下代码,age 是一个普通变量,存放的内容是 28,而 ptr 是 -存放变量age值的内存地址:0xc000010098 - -.. code:: go - - package main - - import "fmt" - - func main() { - var age int = 28 - var ptr = &age // &后面接变量名,表示取出该变量的内存地址 - fmt.Println("age: ", age) - fmt.Println("ptr: ", ptr) - } - -输出 - -:: - - age: 28 - ptr: 0xc000010098 - -而这里要说的 new 函数,是 Go 里的一个内建函数。 - -使用表达式 new(Type) -将创建一个Type类型的匿名变量,初始化为Type类型的零值,然后返回变量地址,返回的指针类型为\ ``*Type``\ 。 - -.. code:: go - - package main - - import "fmt" - - func main() { - ptr := new(int) - fmt.Println("ptr address: ", ptr) - fmt.Println("ptr value: ", *ptr) // * 后面接指针变量,表示从内存地址中取出值 - } - -输出 - -:: - - ptr address: 0xc000010098 - ptr value: 0 - -用new创建变量和普通变量声明语句方式创建变量没有什么区别,除了不需要声明一个临时变量的名字外,我们还可以在表达式中使用new(Type)。换言之,new函数类似是一种语法糖,而不是一个新的基础概念。 - -如下两种写法,可以说是等价的 - -.. code:: go - - // 使用 new - func newInt() *int { - return new(int) - } - - // 使用传统的方式 - func newInt() *int { - var dummy int - return &dummy - } - -以上不管哪种方法,变量/常量都只能声明一次,声明多次,编译就会报错。 - -但也有例外,这就要说到一个特殊变量:\ **匿名变量**\ ,也称作占位符,或者空白标识符,用下划线表示。 - -匿名变量,优点有三: - -- 不分配内存,不占用内存空间 -- 不需要你为命名无用的变量名而纠结 -- 多次声明不会有任何问题 - -通常我们用匿名接收必须接收,但是又不会用到的值。 - -.. code:: go - - func GetData() (int, int) { - return 100, 200 - } - func main(){ - a, _ := GetData() - _, b := GetData() - fmt.Println(a, b) - } - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md deleted file mode 100644 index a6a0a18..0000000 --- a/source/c09/c09_03.md +++ /dev/null @@ -1,220 +0,0 @@ -# 9.3 详解数据类型:整型与浮点型 - -## 1. 整型 - -Go 语言中,整数类型可以再细分成10个类型,为了方便大家学习,我将这些类型整理成一张表格。 - -![](http://image.python-online.cn/20200120204329.png) - - -int 和 uint 的区别就在于一个 `u`,有 `u` 说明是无符号,没有 `u` 代表有符号。 - -**解释这个符号的区别** - -以 `int8` 和 `uint8` 举例,8 代表 8个bit,能表示的数值个数有 2^8 = 256。 - -uint8 是无符号,能表示的都是正数,0-255,刚好256个数。 - -int8 是有符号,既可以正数,也可以负数,那怎么办?对半分呗,-128-127,也刚好 256个数。 - - - -int8 int16 int32 int64 这几个类型的最后都有一个数值,这表明了它们能表示的数值个数是固定的。 - -而 int 没有并没有指定它的位数,说明它的大小,是可以变化的,那根据什么变化呢? - -- 当你在32位的系统下,int 和 uint 都占用 4个字节,也就是32位。 -- 若你在64位的系统下,int 和 uint 都占用 8个字节,也就是64位。 - -出于这个原因,在某些场景下,你应当避免使用 int 和 uint ,而使用更加精确的 int32 和 int64,比如在二进制传输、读写文件的结构描述(为了保持文件的结构不会受到不同编译目标平台字节长度的影响) - -**不同进制的表示方法** - -出于习惯,在初始化数据类型为整型的变量时,我们会使用10进制的表示法,因为它最直观,比如这样,表示整数10. - -``` -var num int = 10 -``` - -不过,你要清楚,你一样可以使用其他进制来表示一个整数,这里以比较常用的2进制、8进制和16进制举例。 - -2进制:以`0b`或`0B`为前缀 - -```go -var num01 int = 0b1100 -``` - -8进制:以`0o`或者 `0O`为前缀 - -```go -var num02 int = 0o14 -``` - -16进制:以`0x` 为前缀 - -```go -var num03 int = 0xC -``` - - - -下面用一段代码分别使用二进制、8进制、16进制来表示 10 进制的数值:12 - -```go -package main - -import ( - "fmt" -) - -func main() { - var num01 int = 0b1100 - var num02 int = 0o14 - var num03 int = 0xC - - fmt.Printf("2进制数 %b 表示的是: %d \n", num01, num01) - fmt.Printf("8进制数 %o 表示的是: %d \n", num02, num02) - fmt.Printf("16进制数 %X 表示的是: %d \n", num03, num03) -} -``` - -输出如下 - -``` -2进制数 1100 表示的是: 12 -8进制数 14 表示的是: 12 -16进制数 C 表示的是: 12 -``` - - - -以上代码用过了 fmt 包的格式化功能,你可以参考这里去看上面的代码 - -``` -%b 表示为二进制 -%c 该值对应的unicode码值 -%d 表示为十进制 -%o 表示为八进制 -%q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示 -%x 表示为十六进制,使用a-f -%X 表示为十六进制,使用A-F -%U 表示为Unicode格式:U+1234,等价于"U+%04X" -%E 用科学计数法表示 -%f 用浮点数表示 -``` - - - -## 2. 浮点型 - -浮点数类型的值一般由整数部分、小数点“`.`”和小数部分组成。 - -其中,整数部分和小数部分均由10进制表示法表示。不过还有另一种表示方法。那就是在其中加入指数部分。指数部分由“E”或“e”以及一个带正负号的10进制数组成。比如,`3.7E-2`表示浮点数`0.037`。又比如,`3.7E+1`表示浮点数`37`。 - -有时候,浮点数类型值的表示也可以被简化。比如,`37.0`可以被简化为`37`。又比如,`0.037`可以被简化为`.037`。 - - 有一点需要注意,在Go语言里,浮点数的相关部分只能由10进制表示法表示,而不能由8进制表示法或16进制表示法表示。比如,`03.7`表示的一定是浮点数`3.7`。 - -### float32 和 float64 - -Go语言中提供了两种精度的浮点数 float32 和 float64。 - -**float32**,也即我们常说的单精度,存储占用4个字节,也即4*8=32位,其中1位用来符号,8位用来指数,剩下的23位表示尾数 - -![img](https://pic4.zhimg.com/80/v2-749cc641eb4d5dafd085e8c23f8826aa_hd.jpg) - -**float64**,也即我们熟悉的双精度,存储占用8个字节,也即8*8=64位,其中1位用来符号,11位用来指数,剩下的52位表示尾数 - -![img](https://pic2.zhimg.com/80/v2-48240f0e1e0dd33ec89100cbe2d30707_hd.jpg) - -**那么精度是什么意思?有效位有多少位?** - -精度主要取决于尾数部分的位数。 - -对于 float32(单精度)来说,表示尾数的为23位,除去全部为0的情况以外,最小为2^-23,约等于1.19*10^-7,所以float小数部分只能精确到后面6位,加上小数点前的一位,即有效数字为7位。 - -同理 float64(单精度)的尾数部分为 52位,最小为2^-52,约为2.22*10^-16,所以精确到小数点后15位,加上小数点前的一位,有效位数为16位。 - - - -通过以上,可以总结出以下几点: - -**一、float32 和 float64 可以表示的数值很多** - -浮点数类型的取值范围可以从很微小到很巨大。浮点数取值范围的极限值可以在 math 包中找到: - -- 常量 math.MaxFloat32 表示 float32 能取到的最大数值,大约是 3.4e38; -- 常量 math.MaxFloat64 表示 float64 能取到的最大数值,大约是 1.8e308; -- float32 和 float64 能表示的最小值分别为 1.4e-45 和 4.9e-324。 - -**二、数值很大但精度有限** - -人家虽然能表示的数值很大,但精度位却没有那么大。 - -- float32的精度只能提供大约6个十进制数(表示后科学计数法后,小数点后6位)的精度 -- float64的精度能提供大约15个十进制数(表示后科学计数法后,小数点后15位)的精度 - - - -这里的精度是什么意思呢? - -比如 10000018这个数,用 float32 的类型来表示的话,由于其有效位是7位,将10000018 表示成科学计数法,就是 1.0000018 * 10^7,能精确到小数点后面6位。 - -此时用科学计数法表示后,小数点后有7位,刚刚满足我们的精度要求,意思是什么呢?此时你对这个数进行+1或者-1等数学运算,都能保证计算结果是精确的 - -```go -import "fmt" -var myfloat float32 = 10000018 -func main() { - fmt.Println("myfloat: ", myfloat) - fmt.Println("myfloat: ", myfloat+1) -} -``` - -输出如下 - -```go -myfloat: 1.0000018e+07 -myfloat: 1.0000019e+07 -``` - - - -上面举了一个刚好满足精度要求数据的临界情况,为了做对比,下面也举一个刚好不满足精度要求的例子。只要给这个数值多加一位数就行了。 - -换成 100000187,同样使用 float32类型,表示成科学计数法,由于精度有限,表示的时候小数点后面7位是准确的,但若是对其进行数学运算,由于第八位无法表示,所以运算后第七位的值,就会变得不精确。 - -这里我们写个代码来验证一下,按照我们的理解下面 myfloat01 = 100000182 ,对其`+5` 操作后,应该等于 myfloat02 = 100000187, - -```go -import "fmt" - -var myfloat01 float32 = 100000182 -var myfloat02 float32 = 100000187 - -func main() { - fmt.Println("myfloat: ", myfloat01) - fmt.Println("myfloat: ", myfloat01+5) - fmt.Println(myfloat02 == myfloat01+5) -} -``` - -但是由于其类型是 float32,精度不足,导致最后比较的结果是不相等(从小数点后第七位开始不精确) - -```go -myfloat: 1.00000184e+08 -myfloat: 1.0000019e+08 -false -``` - -由于精度的问题,就会出现这种很怪异的现象,`myfloat == myfloat +1` 会返回 `true` 。 - - - -## 参考文章: - -https://www.zhihu.com/question/26022206 - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst deleted file mode 100644 index 2fe5f5e..0000000 --- a/source/c09/c09_03.rst +++ /dev/null @@ -1,235 +0,0 @@ -9.3 详解数据类型:整型与浮点型 -============================== - -1. 整型 -------- - -Go -语言中,整数类型可以再细分成10个类型,为了方便大家学习,我将这些类型整理成一张表格。 - -|image0| - -int 和 uint 的区别就在于一个 ``u``\ ,有 ``u`` 说明是无符号,没有 ``u`` -代表有符号。 - -**解释这个符号的区别** - -以 ``int8`` 和 ``uint8`` 举例,8 代表 8个bit,能表示的数值个数有 2^8 = -256。 - -uint8 是无符号,能表示的都是正数,0-255,刚好256个数。 - -int8 -是有符号,既可以正数,也可以负数,那怎么办?对半分呗,-128-127,也刚好 -256个数。 - -int8 int16 int32 int64 -这几个类型的最后都有一个数值,这表明了它们能表示的数值个数是固定的。 - -而 int -没有并没有指定它的位数,说明它的大小,是可以变化的,那根据什么变化呢? - -- 当你在32位的系统下,int 和 uint 都占用 4个字节,也就是32位。 -- 若你在64位的系统下,int 和 uint 都占用 8个字节,也就是64位。 - -出于这个原因,在某些场景下,你应当避免使用 int 和 uint -,而使用更加精确的 int32 和 -int64,比如在二进制传输、读写文件的结构描述(为了保持文件的结构不会受到不同编译目标平台字节长度的影响) - -**不同进制的表示方法** - -出于习惯,在初始化数据类型为整型的变量时,我们会使用10进制的表示法,因为它最直观,比如这样,表示整数10. - -:: - - var num int = 10 - -不过,你要清楚,你一样可以使用其他进制来表示一个整数,这里以比较常用的2进制、8进制和16进制举例。 - -2进制:以\ ``0b``\ 或\ ``0B``\ 为前缀 - -.. code:: go - - var num01 int = 0b1100 - -8进制:以\ ``0o``\ 或者 ``0O``\ 为前缀 - -.. code:: go - - var num02 int = 0o14 - -16进制:以\ ``0x`` 为前缀 - -.. code:: go - - var num03 int = 0xC - -下面用一段代码分别使用二进制、8进制、16进制来表示 10 进制的数值:12 - -.. code:: go - - package main - - import ( - "fmt" - ) - - func main() { - var num01 int = 0b1100 - var num02 int = 0o14 - var num03 int = 0xC - - fmt.Printf("2进制数 %b 表示的是: %d \n", num01, num01) - fmt.Printf("8进制数 %o 表示的是: %d \n", num02, num02) - fmt.Printf("16进制数 %X 表示的是: %d \n", num03, num03) - } - -输出如下 - -:: - - 2进制数 1100 表示的是: 12 - 8进制数 14 表示的是: 12 - 16进制数 C 表示的是: 12 - -以上代码用过了 fmt 包的格式化功能,你可以参考这里去看上面的代码 - -:: - - %b 表示为二进制 - %c 该值对应的unicode码值 - %d 表示为十进制 - %o 表示为八进制 - %q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示 - %x 表示为十六进制,使用a-f - %X 表示为十六进制,使用A-F - %U 表示为Unicode格式:U+1234,等价于"U+%04X" - %E 用科学计数法表示 - %f 用浮点数表示 - -2. 浮点型 ---------- - -浮点数类型的值一般由整数部分、小数点“``.``”和小数部分组成。 - -其中,整数部分和小数部分均由10进制表示法表示。不过还有另一种表示方法。那就是在其中加入指数部分。指数部分由“E”或“e”以及一个带正负号的10进制数组成。比如,\ ``3.7E-2``\ 表示浮点数\ ``0.037``\ 。又比如,\ ``3.7E+1``\ 表示浮点数\ ``37``\ 。 - -有时候,浮点数类型值的表示也可以被简化。比如,\ ``37.0``\ 可以被简化为\ ``37``\ 。又比如,\ ``0.037``\ 可以被简化为\ ``.037``\ 。 - -有一点需要注意,在Go语言里,浮点数的相关部分只能由10进制表示法表示,而不能由8进制表示法或16进制表示法表示。比如,\ ``03.7``\ 表示的一定是浮点数\ ``3.7``\ 。 - -float32 和 float64 -~~~~~~~~~~~~~~~~~~ - -Go语言中提供了两种精度的浮点数 float32 和 float64。 - -**float32**\ ,也即我们常说的单精度,存储占用4个字节,也即4*8=32位,其中1位用来符号,8位用来指数,剩下的23位表示尾数 - -.. figure:: https://pic4.zhimg.com/80/v2-749cc641eb4d5dafd085e8c23f8826aa_hd.jpg - :alt: img - - img - -**float64**\ ,也即我们熟悉的双精度,存储占用8个字节,也即8*8=64位,其中1位用来符号,11位用来指数,剩下的52位表示尾数 - -.. figure:: https://pic2.zhimg.com/80/v2-48240f0e1e0dd33ec89100cbe2d30707_hd.jpg - :alt: img - - img - -**那么精度是什么意思?有效位有多少位?** - -精度主要取决于尾数部分的位数。 - -对于 -float32(单精度)来说,表示尾数的为23位,除去全部为0的情况以外,最小为2^-23,约等于1.19*10^-7,所以float小数部分只能精确到后面6位,加上小数点前的一位,即有效数字为7位。 - -同理 float64(单精度)的尾数部分为 -52位,最小为2^-52,约为2.22*10^-16,所以精确到小数点后15位,加上小数点前的一位,有效位数为16位。 - -通过以上,可以总结出以下几点: - -**一、float32 和 float64 可以表示的数值很多** - -浮点数类型的取值范围可以从很微小到很巨大。浮点数取值范围的极限值可以在 -math 包中找到: - -- 常量 math.MaxFloat32 表示 float32 能取到的最大数值,大约是 3.4e38; -- 常量 math.MaxFloat64 表示 float64 能取到的最大数值,大约是 1.8e308; -- float32 和 float64 能表示的最小值分别为 1.4e-45 和 4.9e-324。 - -**二、数值很大但精度有限** - -人家虽然能表示的数值很大,但精度位却没有那么大。 - -- float32的精度只能提供大约6个十进制数(表示后科学计数法后,小数点后6位)的精度 -- float64的精度能提供大约15个十进制数(表示后科学计数法后,小数点后15位)的精度 - -这里的精度是什么意思呢? - -比如 10000018这个数,用 float32 -的类型来表示的话,由于其有效位是7位,将10000018 表示成科学计数法,就是 -1.0000018 \* 10^7,能精确到小数点后面6位。 - -此时用科学计数法表示后,小数点后有7位,刚刚满足我们的精度要求,意思是什么呢?此时你对这个数进行+1或者-1等数学运算,都能保证计算结果是精确的 - -.. code:: go - - import "fmt" - var myfloat float32 = 10000018 - func main() { - fmt.Println("myfloat: ", myfloat) - fmt.Println("myfloat: ", myfloat+1) - } - -输出如下 - -.. code:: go - - myfloat: 1.0000018e+07 - myfloat: 1.0000019e+07 - -上面举了一个刚好满足精度要求数据的临界情况,为了做对比,下面也举一个刚好不满足精度要求的例子。只要给这个数值多加一位数就行了。 - -换成 100000187,同样使用 -float32类型,表示成科学计数法,由于精度有限,表示的时候小数点后面7位是准确的,但若是对其进行数学运算,由于第八位无法表示,所以运算后第七位的值,就会变得不精确。 - -这里我们写个代码来验证一下,按照我们的理解下面 myfloat01 = 100000182 -,对其\ ``+5`` 操作后,应该等于 myfloat02 = 100000187, - -.. code:: go - - import "fmt" - - var myfloat01 float32 = 100000182 - var myfloat02 float32 = 100000187 - - func main() { - fmt.Println("myfloat: ", myfloat01) - fmt.Println("myfloat: ", myfloat01+5) - fmt.Println(myfloat02 == myfloat01+5) - } - -但是由于其类型是 -float32,精度不足,导致最后比较的结果是不相等(从小数点后第七位开始不精确) - -.. code:: go - - myfloat: 1.00000184e+08 - myfloat: 1.0000019e+08 - false - -由于精度的问题,就会出现这种很怪异的现象,\ ``myfloat == myfloat +1`` -会返回 ``true`` 。 - -参考文章: ----------- - -https://www.zhihu.com/question/26022206 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - - -.. |image0| image:: http://image.python-online.cn/20200120204329.png - diff --git a/source/c09/c09_04.md b/source/c09/c09_04.md deleted file mode 100644 index 18a750c..0000000 --- a/source/c09/c09_04.md +++ /dev/null @@ -1,240 +0,0 @@ -# 9.4 详解数据类型:byte、rune与字符串 - -## 1. byte 与 rune - -**byte**,占用1个节字,就 8 个比特位,所以它和 `uint8` 类型本质上没有区别,它表示的是 ACSII 表中的一个字符。 - -如下这段代码,分别定义了 byte 类型和 uint8 类型的变量 a 和 b - -```go -import "fmt" - -func main() { - var a byte = 65 - // 8进制写法: var c byte = '\101' 其中 \ 是固定前缀 - // 16进制写法: var c byte = '\x41' 其中 \x 是固定前缀 - - var b uint8 = 66 - fmt.Printf("a 的值: %c \nb 的值: %c", a, b) - // 或者使用 string 函数 - // fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b)) -} -``` - -在 ASCII 表中,由于字母 A 的ASCII 的编号为 65 ,字母 B 的ASCII 编号为 66,所以上面的代码也可以写成这样 - -```go -import "fmt" - -func main() { - var a byte = 'A' - var b uint8 = 'B' - fmt.Printf("a 的值: %c \nb 的值: %c", a, b) -} -``` - -他们的输出结果都是一样的。 - -``` -a 的值: A -b 的值: B -``` - - - - - -**rune**,占用4个字节,共32位比特位,所以它和 `uint32` 本质上也没有区别。它表示的是一个 Unicode字符(Unicode是一个可以表示世界范围内的绝大部分字符的编码规范)。 - -```go -import ( - "fmt" - "unsafe" -) - -func main() { - var a byte = 'A' - var b rune = 'B' - fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数", unsafe.Sizeof(a), unsafe.Sizeof(b)) -} -``` - -输出如下 - -``` -a 占用 1 个字节数 -b 占用 4 个字节数 -``` - - - -由于 byte 类型能表示的值是有限,只有 2^8=256 个。所以如果你想表示中文的话,你只能使用 rune 类型。 - -```ro -var name rune = '中' -``` - - - -或许你已经发现,上面我们在定义字符时,不管是 byte 还是 rune ,我都是使用单引号,而没使用双引号。 - -对于从 Python 转过来的人,这里一定要注意了,在 Go 中单引号与 双引号并不是等价的。 - -单引号用来表示字符,在上面的例子里,如果你使用双引号,就意味着你要定义一个字符串,赋值时与前面声明的前面会不一致,这样在编译的时候就会出错。 - -```go -cannot use "A" (type string) as type byte in assignment -``` - - - -上面我说了,byte 和 uint8 没有区别,rune 和 uint32 没有区别,那为什么还要多出一个 byte 和 rune 类型呢? - -理由很简单,因为uint8 和 uint32 ,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 byte 和 rune 这两个别名类型。 - - - -## 2. 字符串 - -字符串,可以说是大家很熟悉的数据类型之一。定义方法很简单 - -```go -var mystr string = "hello" -``` - - - -上面说的byte 和 rune 都是字符类型,若多个字符放在一起,就组成了字符串,也就是这里要说的 string 类型。 - -比如 `hello` ,对照 ascii 编码表,每个字母对应的编号是:104,101,108,108,111 - -```go -import ( - "fmt" -) - -func main() { - var mystr01 sting = "hello" - var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111} - fmt.Printf("mystr01: %s\n", mystr01) - fmt.Printf("mystr02: %s", mystr02) -} -``` - -输出如下,mystr01 和 mystr02 输出一样,说明了 string 的本质,其实是一个 byte数组 - -``` -mystr01: hello -mystr02: hello -``` - - - -通过以上学习,我们知道字符分为 byte 和 rune,占用的大小不同。 - -这里来考一下大家,`hello,中国` 占用几个字节? - -要回答这个问题,你得知道 Go 语言的 string 是用 uft-8 进行编码的,英文字母占用一个字节,而中文字母占用 3个字节,所以 `hello,中国` 的长度为 5+1+(3*2)= 12个字节。 - -```go -import ( - "fmt" -) - -func main() { - var country string = "hello,中国" - fmt.Println(len(country)) -} -// 输出 -12 -``` - - - -以上虽然我都用双引号表示 一个字符串,但这并不是字符串的唯一表示方式。 - -除了双引号之外 ,你还可以使用反引号。 - -大多情况下,二者并没有区别,但如果你的字符串中有转义字符`\` ,这里就要注意了,它们是有区别的。 - -使用反引号号包裹的字符串,相当于 Python 中的 raw 字符串,会忽略里面的转义。 - -比如我想表示 `\r\n` 这个 字符串,使用双引号是这样写的,这种叫解释型表示法 - -```go -var mystr01 string = "\\r\\n" -``` - -而使用反引号,就方便多了,所见即所得,这种叫原生型表示法 - -```go -var mystr02 string = `\r\n` -``` - -他们的打印结果 都是一样的 - -```go -import ( - "fmt" -) - -func main() { - var mystr01 string = "\\r\\n" - var mystr02 string = `\r\n` - fmt.Println(mystr01) - fmt.Println(mystr02) -} - -// output -\r\n -\r\n -``` - -如果你仍然想使用解释型的字符串,但是各种转义实在太麻烦了。你可以使用 fmt 的 `%q` 来还原一下。 - -```go -import ( - "fmt" -) - -func main() { - var mystr01 string = `\r\n` - fmt.Println(`\r\n`) - fmt.Printf("的解释型字符串是: %q", mystr01) -} -``` - -输出如下 - -```go -\r\n -的解释型字符串是: "\\r\\n" -``` - - - -同时反引号可以不写换行符(因为没法写)来表示一个多行的字符串。 - -```go -import ( - "fmt" -) - -func main() { - var mystr01 string = `你好呀! -我的公众号是: Go编程时光,欢迎大家关注` - - fmt.Println(mystr01) -} -``` - -输出如下 - -``` -你好呀! -我的公众号是: Go编程时光,欢迎大家关注 -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_04.rst b/source/c09/c09_04.rst deleted file mode 100644 index dc0ab25..0000000 --- a/source/c09/c09_04.rst +++ /dev/null @@ -1,243 +0,0 @@ -9.4 详解数据类型:byte、rune与字符串 -==================================== - -1. byte 与 rune ---------------- - -**byte**\ ,占用1个节字,就 8 个比特位,所以它和 ``uint8`` -类型本质上没有区别,它表示的是 ACSII 表中的一个字符。 - -如下这段代码,分别定义了 byte 类型和 uint8 类型的变量 a 和 b - -.. code:: go - - import "fmt" - - func main() { - var a byte = 65 - // 8进制写法: var c byte = '\101' 其中 \ 是固定前缀 - // 16进制写法: var c byte = '\x41' 其中 \x 是固定前缀 - - var b uint8 = 66 - fmt.Printf("a 的值: %c \nb 的值: %c", a, b) - // 或者使用 string 函数 - // fmt.Println("a 的值: ", string(a)," \nb 的值: ", string(b)) - } - -在 ASCII 表中,由于字母 A 的ASCII 的编号为 65 ,字母 B 的ASCII 编号为 -66,所以上面的代码也可以写成这样 - -.. code:: go - - import "fmt" - - func main() { - var a byte = 'A' - var b uint8 = 'B' - fmt.Printf("a 的值: %c \nb 的值: %c", a, b) - } - -他们的输出结果都是一样的。 - -:: - - a 的值: A - b 的值: B - -**rune**\ ,占用4个字节,共32位比特位,所以它和 ``uint32`` -本质上也没有区别。它表示的是一个 -Unicode字符(Unicode是一个可以表示世界范围内的绝大部分字符的编码规范)。 - -.. code:: go - - import ( - "fmt" - "unsafe" - ) - - func main() { - var a byte = 'A' - var b rune = 'B' - fmt.Printf("a 占用 %d 个字节数\nb 占用 %d 个字节数", unsafe.Sizeof(a), unsafe.Sizeof(b)) - } - -输出如下 - -:: - - a 占用 1 个字节数 - b 占用 4 个字节数 - -由于 byte 类型能表示的值是有限,只有 2^8=256 -个。所以如果你想表示中文的话,你只能使用 rune 类型。 - -.. code:: ro - - var name rune = '中' - -或许你已经发现,上面我们在定义字符时,不管是 byte 还是 rune -,我都是使用单引号,而没使用双引号。 - -对于从 Python 转过来的人,这里一定要注意了,在 Go 中单引号与 -双引号并不是等价的。 - -单引号用来表示字符,在上面的例子里,如果你使用双引号,就意味着你要定义一个字符串,赋值时与前面声明的前面会不一致,这样在编译的时候就会出错。 - -.. code:: go - - cannot use "A" (type string) as type byte in assignment - -上面我说了,byte 和 uint8 没有区别,rune 和 uint32 -没有区别,那为什么还要多出一个 byte 和 rune 类型呢? - -理由很简单,因为uint8 和 uint32 -,直观上让人以为这是一个数值,但是实际上,它也可以表示一个字符,所以为了消除这种直观错觉,就诞生了 -byte 和 rune 这两个别名类型。 - -2. 字符串 ---------- - -字符串,可以说是大家很熟悉的数据类型之一。定义方法很简单 - -.. code:: go - - var mystr string = "hello" - -上面说的byte 和 rune -都是字符类型,若多个字符放在一起,就组成了字符串,也就是这里要说的 -string 类型。 - -比如 ``hello`` ,对照 ascii -编码表,每个字母对应的编号是:104,101,108,108,111 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - var mystr01 sting = "hello" - var mystr02 [5]byte = [5]byte{104, 101, 108, 108, 111} - fmt.Printf("mystr01: %s\n", mystr01) - fmt.Printf("mystr02: %s", mystr02) - } - -输出如下,mystr01 和 mystr02 输出一样,说明了 string 的本质,其实是一个 -byte数组 - -:: - - mystr01: hello - mystr02: hello - -通过以上学习,我们知道字符分为 byte 和 rune,占用的大小不同。 - -这里来考一下大家,\ ``hello,中国`` 占用几个字节? - -要回答这个问题,你得知道 Go 语言的 string 是用 uft-8 -进行编码的,英文字母占用一个字节,而中文字母占用 3个字节,所以 -``hello,中国`` 的长度为 5+1+(3*2)= 12个字节。 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - var country string = "hello,中国" - fmt.Println(len(country)) - } - // 输出 - 12 - -以上虽然我都用双引号表示 一个字符串,但这并不是字符串的唯一表示方式。 - -除了双引号之外 ,你还可以使用反引号。 - -大多情况下,二者并没有区别,但如果你的字符串中有转义字符\ ``\`` -,这里就要注意了,它们是有区别的。 - -使用反引号号包裹的字符串,相当于 Python 中的 raw -字符串,会忽略里面的转义。 - -比如我想表示 ``\r\n`` 这个 -字符串,使用双引号是这样写的,这种叫解释型表示法 - -.. code:: go - - var mystr01 string = "\\r\\n" - -而使用反引号,就方便多了,所见即所得,这种叫原生型表示法 - -.. code:: go - - var mystr02 string = `\r\n` - -他们的打印结果 都是一样的 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - var mystr01 string = "\\r\\n" - var mystr02 string = `\r\n` - fmt.Println(mystr01) - fmt.Println(mystr02) - } - - // output - \r\n - \r\n - -如果你仍然想使用解释型的字符串,但是各种转义实在太麻烦了。你可以使用 fmt -的 ``%q`` 来还原一下。 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - var mystr01 string = `\r\n` - fmt.Println(`\r\n`) - fmt.Printf("的解释型字符串是: %q", mystr01) - } - -输出如下 - -.. code:: go - - \r\n - 的解释型字符串是: "\\r\\n" - -同时反引号可以不写换行符(因为没法写)来表示一个多行的字符串。 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - var mystr01 string = `你好呀! - 我的公众号是: Go编程时光,欢迎大家关注` - - fmt.Println(mystr01) - } - -输出如下 - -:: - - 你好呀! - 我的公众号是: Go编程时光,欢迎大家关注 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_05.md b/source/c09/c09_05.md deleted file mode 100644 index 8512c9d..0000000 --- a/source/c09/c09_05.md +++ /dev/null @@ -1,236 +0,0 @@ -# 9.7 详解数据类型:数组与切片 - -## 1. 数组 - -数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,所以在Go语言中很少直接使用数组。 - -声明数组,并给该数组里的每个元素赋值(索引值的最小有效值和其他大多数语言一样是 0,不是1) - -```go -// [3] 里的3 表示该数组的元素个数 -var arr [3]int -arr[0] = 1 -arr[1] = 2 -arr[2] = 3 -``` - -声明并直接初始化数组 - -```go -// 第一种方法 -var arr [3]int = [3]int{1,2,3} - -// 第二种方法 -arr := [3]int{1,2,3} -``` - -上面的 3 表示数组的元素个数 ,万一你哪天想往该数组中增加元素,你得对应修改这个数字,为了避免这种硬编码,你可以这样写,使用 `...` 让Go语言自己根据实际情况来分配空间。 - -```go -arr := [...]int{1,2,3} -``` - - - -`[3]int` 和 `[4]int` 虽然都是数组,但他们却是不同的类型,使用 fmt 的 `%T` 可以查得。 - -```go -import ( - "fmt" -) - -func main() { - arr01 := [...]int{1, 2, 3} - arr02 := [...]int{1, 2, 3, 4} - fmt.Printf("%d 的类型是: %T\n", arr01, arr01) - fmt.Printf("%d 的类型是: %T", arr02, arr02) -} -``` - -输出 如下 - -``` -[1 2 3] 的类型是: [3]int -[1 2 3 4] 的类型是: [4]int -``` - - - -如果你觉得每次写 `[3]int` 有点麻烦,你可以为 `[3]int` 定义一个类型字面量,也就是别名类型。 - -使用 `type` 关键字可以定义一个类型字面量,后面只要你想定义一个容器大小为3,元素类型为int的数组 ,都可以使用这个别名类型。 - -```go -import ( - "fmt" -) - -func main() { - type arr3 [3]int - - myarr := arr3{1,2,3} - fmt.Printf("%d 的类型是: %T", myarr, myarr) -} -``` - -输出 如下 - -``` -[1 2 3] 的类型是: main.arr3 -``` - - - -## 2. 切片 - -切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。 - -切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(意思是这是个左闭右开的区间) - -```go -import ( - "fmt" -) - -func main() { - myarr := [...]int{1, 2, 3} - fmt.Printf("%d 的类型是: %T", myarr[0:2], myarr[0:2]) -} -``` - -输出 如下 - -``` -[1 2] 的类型是: []int -``` - - - -切片的构造,有三种方式 - -1. 对数组进行片段截取(上面例子已经展示:myarr[0:2],0是索引起始值,2是索引终止值,区间左半右开) - -2. 从头声明赋值(例子如下) - - ```go - // 声明字符串切片 - var strList []string - - // 声明整型切片 - var numList []int - - // 声明一个空切片 - var numListEmpty = []int{} - ``` - -3. 使用 make 函数构造,make 函数的格式:`make( []Type, size, cap )` - - 这个函数刚好指出了,一个切片具备的三个要素:类型(Type),长度(size),容量(cap) - - ```go - import ( - "fmt" - ) - - func main() { - a := make([]int, 2) - b := make([]int, 2, 10) - fmt.Println(a, b) - fmt.Println(len(a), len(b)) - fmt.Println(cap(a), cap(b)) -} - ``` - - 输出 如下 - - ``` - [0 0] [0 0] - 2 2 - 2 10 - ``` - - -关于 len 和 cap 的概念,可能不好理解 ,这里举个例子: - -- 公司名,相当于字面量,也就是变量名。 - -- 公司里的所有工位,相当于已分配到的内存空间 - -- 公司里的员工,相当于元素。 - -- cap 代表你这个公司最多可以容纳多少员工 - -- len 代表你这个公司当前有多少个员工 - - - -由于 切片是引用类型,所以你不对它进行赋值的话,它的零值(默认值)是 nil - -```go -var myarr []int -fmt.Println(myarr == nil) -// true -``` - - - -数组 与 切片 有相同点,它们都是可以容纳若干类型相同的元素的容器 - -也有不同点,数组的容器大小固定,而切片本身是引用类型,它更像是 Python 中的 list ,我们可以对它 append 进行元素的添加。 - -```go -import ( - "fmt" -) - -func main() { - myarr := []int{1} - // 追加一个元素 - myarr = append(myarr, 2) - // 追加多个元素 - myarr = append(myarr, 3, 4) - // 追加一个切片, ... 表示解包,不能省略 - myarr = append(myarr, []int{7, 8}...) - // 在第一个位置插入元素 - myarr = append([]int{0}, myarr...) - // 在中间插入一个切片(两个元素) - myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]...)...) - fmt.Println(myarr) -} -``` - -输出 如下 - -``` -[0 1 2 3 4 5 6 7 8] -``` - - - -最后,给你留一道思考题,如下 这段代码 - -```go -import ( - "fmt" -) - -func main() { - var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - myslice := numbers4[4:6:8] - fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice)) - - myslice = myslice[:cap(myslice)] - fmt.Printf("myslice的第四个元素为: %d", myslice[3]) -} -``` - -为什么 myslice 的长度为2,却能访问到第四个元素 - -``` -myslice为 [5 6], 其长度为: 2 -myslice的第四个元素为: 8 -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_05.rst b/source/c09/c09_05.rst deleted file mode 100644 index a92e64e..0000000 --- a/source/c09/c09_05.rst +++ /dev/null @@ -1,232 +0,0 @@ -9.7 详解数据类型:数组与切片 -============================ - -1. 数组 -------- - -数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,所以在Go语言中很少直接使用数组。 - -声明数组,并给该数组里的每个元素赋值(索引值的最小有效值和其他大多数语言一样是 -0,不是1) - -.. code:: go - - // [3] 里的3 表示该数组的元素个数 - var arr [3]int - arr[0] = 1 - arr[1] = 2 - arr[2] = 3 - -声明并直接初始化数组 - -.. code:: go - - // 第一种方法 - var arr [3]int = [3]int{1,2,3} - - // 第二种方法 - arr := [3]int{1,2,3} - -上面的 3 表示数组的元素个数 -,万一你哪天想往该数组中增加元素,你得对应修改这个数字,为了避免这种硬编码,你可以这样写,使用 -``...`` 让Go语言自己根据实际情况来分配空间。 - -.. code:: go - - arr := [...]int{1,2,3} - -``[3]int`` 和 ``[4]int`` 虽然都是数组,但他们却是不同的类型,使用 fmt 的 -``%T`` 可以查得。 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - arr01 := [...]int{1, 2, 3} - arr02 := [...]int{1, 2, 3, 4} - fmt.Printf("%d 的类型是: %T\n", arr01, arr01) - fmt.Printf("%d 的类型是: %T", arr02, arr02) - } - -输出 如下 - -:: - - [1 2 3] 的类型是: [3]int - [1 2 3 4] 的类型是: [4]int - -如果你觉得每次写 ``[3]int`` 有点麻烦,你可以为 ``[3]int`` -定义一个类型字面量,也就是别名类型。 - -使用 ``type`` -关键字可以定义一个类型字面量,后面只要你想定义一个容器大小为3,元素类型为int的数组 -,都可以使用这个别名类型。 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - type arr3 [3]int - - myarr := arr3{1,2,3} - fmt.Printf("%d 的类型是: %T", myarr, myarr) - } - -输出 如下 - -:: - - [1 2 3] 的类型是: main.arr3 - -2. 切片 -------- - -切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值的长度。每个切片值都会将数组作为其底层数据结构。我们也把这样的数组称为切片的底层数组。 - -切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集,需要注意的是,终止索引标识的项不包括在切片内(意思是这是个左闭右开的区间) - -.. code:: go - - import ( - "fmt" - ) - - func main() { - myarr := [...]int{1, 2, 3} - fmt.Printf("%d 的类型是: %T", myarr[0:2], myarr[0:2]) - } - -输出 如下 - -:: - - [1 2] 的类型是: []int - -切片的构造,有三种方式 - -1. 对数组进行片段截取(上面例子已经展示:myarr[0:2],0是索引起始值,2是索引终止值,区间左半右开) - -2. 从头声明赋值(例子如下) - - .. code:: go - - // 声明字符串切片 - var strList []string - - // 声明整型切片 - var numList []int - - // 声明一个空切片 - var numListEmpty = []int{} - -3. 使用 make 函数构造,make 函数的格式:\ ``make( []Type, size, cap )`` - - 这个函数刚好指出了,一个切片具备的三个要素:类型(Type),长度(size),容量(cap) - - .. code:: go - - import ( - "fmt" - ) - - func main() { - a := make([]int, 2) - b := make([]int, 2, 10) - fmt.Println(a, b) - fmt.Println(len(a), len(b)) - fmt.Println(cap(a), cap(b)) - } - - 输出 如下 - - :: - - [0 0] [0 0] - 2 2 - 2 10 - -关于 len 和 cap 的概念,可能不好理解 ,这里举个例子: - -- 公司名,相当于字面量,也就是变量名。 - -- 公司里的所有工位,相当于已分配到的内存空间 - -- 公司里的员工,相当于元素。 - -- cap 代表你这个公司最多可以容纳多少员工 - -- len 代表你这个公司当前有多少个员工 - -由于 切片是引用类型,所以你不对它进行赋值的话,它的零值(默认值)是 nil - -.. code:: go - - var myarr []int - fmt.Println(myarr == nil) - // true - -数组 与 切片 有相同点,它们都是可以容纳若干类型相同的元素的容器 - -也有不同点,数组的容器大小固定,而切片本身是引用类型,它更像是 Python -中的 list ,我们可以对它 append 进行元素的添加。 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - myarr := []int{1} - // 追加一个元素 - myarr = append(myarr, 2) - // 追加多个元素 - myarr = append(myarr, 3, 4) - // 追加一个切片, ... 表示解包,不能省略 - myarr = append(myarr, []int{7, 8}...) - // 在第一个位置插入元素 - myarr = append([]int{0}, myarr...) - // 在中间插入一个切片(两个元素) - myarr = append(myarr[:5], append([]int{5,6}, myarr[5:]...)...) - fmt.Println(myarr) - } - -输出 如下 - -:: - - [0 1 2 3 4 5 6 7 8] - -最后,给你留一道思考题,如下 这段代码 - -.. code:: go - - import ( - "fmt" - ) - - func main() { - var numbers4 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - myslice := numbers4[4:6:8] - fmt.Printf("myslice为 %d, 其长度为: %d\n", myslice, len(myslice)) - - myslice = myslice[:cap(myslice)] - fmt.Printf("myslice的第四个元素为: %d", myslice[3]) - } - -为什么 myslice 的长度为2,却能访问到第四个元素 - -:: - - myslice为 [5 6], 其长度为: 2 - myslice的第四个元素为: 8 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_06.md b/source/c09/c09_06.md deleted file mode 100644 index ff42c55..0000000 --- a/source/c09/c09_06.md +++ /dev/null @@ -1,279 +0,0 @@ -# 9.6 详解数据类型:字典与布尔类型 - - - -## 1. 字典 - -字典(Map 类型),是由若干个 `key:value` 这样的键值对映射组合在一起的数据结构。 - -它是哈希表的一个实现,这就要求它的每个映射里的key,都是唯一的,可以使用 `==` 和 `!=` 来进行判等操作,换句话说就是key必须是可哈希的。 - -什么叫可哈希的?简单来说,一个不可变对象,都可以用一个哈希值来唯一表示,这样的不可变对象,比如字符串类型的对象(可以说除了切片、 字典,函数之外的其他内建类型都算)。 - -意思就是,你的 key 不能是切片,不能是字典,不能是函数。。 - -字典由key和value组成,它们各自有各自的类型。 - -在声明字典时,必须指定好你的key和value是什么类型的,然后使用 map 关键字来告诉Go这是一个字典。 - -```go -map[KEY_TYPE]VALUE_TYPE -``` - -### 声明初始化字典 - -三种声明并初始化字典的方法 - -```go -// 第一种方法 -var scores map[string]int = map[string]int{"english": 80, "chinese": 85} - -// 第二种方法 -scores := map[string]int{"english": 80, "chinese": 85} - -// 第三种方法 -scores := make(map[string]int) -scores["english"] = 80 -scores["chinese"] = 85 -``` - -要注意的是,第一种方法如果拆分成多步(声明、初始化、再赋值),和其他两种有很大的不一样了,相对会比较麻烦。 - -```go -import "fmt" - -func main() { - // 声明一个名为 score 的字典 - var scores map[string]int - - // 未初始化的 score 的零值为nil,无法直接进行赋值 - if scores == nil { - // 需要使用 make 函数先对其初始化 - scores = make(map[string]int) - } - - // 经过初始化后,就可以直接赋值 - scores["chinese"] = 90 - fmt.Println(scores) -} -``` - -### **字典的相关操作** - -添加元素 - -```go -scores["math"] = 95 -``` - -更新元素,若key已存在,则直接更新value - -```go -scores["math"] = 100 -``` - -读取元素,直接使用 `[key]` 即可 ,如果 key 不存在,也不报错,会返回其value-type 的零值。 - -```go -fmt.Println(scores["math"]) -``` - -删除元素,使用 delete 函数,如果 key 不存在,delete 函数会静默处理,不会报错。 - -```go -delete(scores, "math") -``` - - - -当访问一个不存在的key时,并不会直接报错,而是会返回这个 value 的零值,如果 value的类型是int,就返回0。 - -```go -package main - -import "fmt" - -func main() { - scores := make(map[string]int) - fmt.Println(scores["english"]) // 输出 0 -} -``` - - - -### 判断 key 是否存在 - -当key不存在,会返回value-type的零值 ,所以你不能通过返回的结果是否是零值来判断对应的 key 是否存在,因为 key 对应的 value 值可能恰好就是零值。 - -其实字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key 是否存在,若存在ok为true,若不存在,则ok为false - -```go -import "fmt" - -func main() { - scores := map[string]int{"english": 80, "chinese": 85} - math, ok := scores["math"] - if ok { - fmt.Printf("math 的值是: %d", math) - } else { - fmt.Println("math 不存在") - } -} -``` - -我们将上面的代码再优化一下 - -```go -import "fmt" - -func main() { - scores := map[string]int{"english": 80, "chinese": 85} - if math, ok := scores["math"]; ok { - fmt.Printf("math 的值是: %d", math) - } else { - fmt.Println("math 不存在") - } -} -``` - - - -### **如何对字典进行循环** - -Go 语言中没有提供类似 Python 的 keys() 和 values() 这样方便的函数,想要获取,你得自己循环。 - -循环还分三种 - -1. 获取 key 和 value - -```go -import "fmt" - -func main() { - scores := map[string]int{"english": 80, "chinese": 85} - - for subject, score := range scores { - fmt.Printf("key: %s, value: %d\n", subject, scores) - } -} -``` - -2. 只获取key,这里注意不用占用符。 - -```go -import "fmt" - -func main() { - scores := map[string]int{"english": 80, "chinese": 85} - - for subject := range scores { - fmt.Printf("key: %s\n", subject) - } -} -``` - -3. 只获取 value,用一个占位符替代。 - -```go -import "fmt" - -func main() { - scores := map[string]int{"english": 80, "chinese": 85} - - for _, score := range scores { - fmt.Printf("value: %d\n", score) - } -} -``` - - - -## 2. 布尔类型 - -关于布尔值,无非就两个值:true 和 false。只是这两个值,在不同的语言里可能不同。 - -在 Python 中,真值用 True 表示,与 1 相等,假值用 False 表示,与 0 相等 - -```python ->>> True == 1 -True ->>> False == 0 -True ->>> -``` - -而在 Go 中,真值用 true 表示,不但不与 1 相等,并且更加严格,不同类型无法进行比较,而假值用 false 表示,同样与 0 无法比较。 - -如下图所示,Goland 直接波浪线提示类型不匹配,不能比较。 - -![](http://image.python-online.cn/20200106201856.png) - -Go 中确实不如 Python 那样灵活,bool 与 int 不能直接转换,如果要转换,需要你自己实现函数。 - -**bool 转 int** - -```go -func bool2int(b bool) int { - if b { - return 1 - } - return 0 -} -``` - -**int 转 bool** - -```go -func int2bool(i int) bool { - return i != 0 -} -``` - - - -在 Python 中使用 not 对逻辑值取反,而 Go 中使用 `!` 符号 - -```go -import "fmt" - -var male bool = true -func main() { - fmt.Println( !male == false) - // 或者 - fmt.Println( male != false) -} - -// output: true -``` - - - -一个 if 判断语句,有可能不只一个判断条件,在 Python 中是使用 `and` 和 `or` 来执行逻辑运算 - -```python ->>> age = 15 ->>> gender = "male" ->>> ->>> gender == "male" and age >18 -False -``` - -而在 Go 语言中,则使用 `&&` 表示`且`,用 `||` 表示`或`,并且有短路行为(即左边表达式已经可以确认整个表达式的值,那么右边将不会再被求值。 - -```go -import "fmt" - -var age int = 15 -var gender string = "male" -func main() { - // && 两边的表达式都会执行 - fmt.Println( age > 18 && gender == "male") - // gender == "male" 并不会执行 - fmt.Println( age > 18 || gender == "male") -} - -// output: false -// output: true -``` - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_06.rst b/source/c09/c09_06.rst deleted file mode 100644 index af2375c..0000000 --- a/source/c09/c09_06.rst +++ /dev/null @@ -1,294 +0,0 @@ -9.6 详解数据类型:字典与布尔类型 -================================ - -1. 字典 -------- - -字典(Map 类型),是由若干个 ``key:value`` -这样的键值对映射组合在一起的数据结构。 - -它是哈希表的一个实现,这就要求它的每个映射里的key,都是唯一的,可以使用 -``==`` 和 ``!=`` 来进行判等操作,换句话说就是key必须是可哈希的。 - -什么叫可哈希的?简单来说,一个不可变对象,都可以用一个哈希值来唯一表示,这样的不可变对象,比如字符串类型的对象(可以说除了切片、 -字典,函数之外的其他内建类型都算)。 - -意思就是,你的 key 不能是切片,不能是字典,不能是函数。。 - -字典由key和value组成,它们各自有各自的类型。 - -在声明字典时,必须指定好你的key和value是什么类型的,然后使用 map -关键字来告诉Go这是一个字典。 - -.. code:: go - - map[KEY_TYPE]VALUE_TYPE - -声明初始化字典 -~~~~~~~~~~~~~~ - -三种声明并初始化字典的方法 - -.. code:: go - - // 第一种方法 - var scores map[string]int = map[string]int{"english": 80, "chinese": 85} - - // 第二种方法 - scores := map[string]int{"english": 80, "chinese": 85} - - // 第三种方法 - scores := make(map[string]int) - scores["english"] = 80 - scores["chinese"] = 85 - -要注意的是,第一种方法如果拆分成多步(声明、初始化、再赋值),和其他两种有很大的不一样了,相对会比较麻烦。 - -.. code:: go - - import "fmt" - - func main() { - // 声明一个名为 score 的字典 - var scores map[string]int - - // 未初始化的 score 的零值为nil,无法直接进行赋值 - if scores == nil { - // 需要使用 make 函数先对其初始化 - scores = make(map[string]int) - } - - // 经过初始化后,就可以直接赋值 - scores["chinese"] = 90 - fmt.Println(scores) - } - -**字典的相关操作** -~~~~~~~~~~~~~~~~~~ - -添加元素 - -.. code:: go - - scores["math"] = 95 - -更新元素,若key已存在,则直接更新value - -.. code:: go - - scores["math"] = 100 - -读取元素,直接使用 ``[key]`` 即可 ,如果 key -不存在,也不报错,会返回其value-type 的零值。 - -.. code:: go - - fmt.Println(scores["math"]) - -删除元素,使用 delete 函数,如果 key 不存在,delete -函数会静默处理,不会报错。 - -.. code:: go - - delete(scores, "math") - -当访问一个不存在的key时,并不会直接报错,而是会返回这个 value -的零值,如果 value的类型是int,就返回0。 - -.. code:: go - - package main - - import "fmt" - - func main() { - scores := make(map[string]int) - fmt.Println(scores["english"]) // 输出 0 - } - -判断 key 是否存在 -~~~~~~~~~~~~~~~~~ - -当key不存在,会返回value-type的零值 -,所以你不能通过返回的结果是否是零值来判断对应的 key 是否存在,因为 key -对应的 value 值可能恰好就是零值。 - -其实字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key -是否存在,若存在ok为true,若不存在,则ok为false - -.. code:: go - - import "fmt" - - func main() { - scores := map[string]int{"english": 80, "chinese": 85} - math, ok := scores["math"] - if ok { - fmt.Printf("math 的值是: %d", math) - } else { - fmt.Println("math 不存在") - } - } - -我们将上面的代码再优化一下 - -.. code:: go - - import "fmt" - - func main() { - scores := map[string]int{"english": 80, "chinese": 85} - if math, ok := scores["math"]; ok { - fmt.Printf("math 的值是: %d", math) - } else { - fmt.Println("math 不存在") - } - } - -**如何对字典进行循环** -~~~~~~~~~~~~~~~~~~~~~~ - -Go 语言中没有提供类似 Python 的 keys() 和 values() -这样方便的函数,想要获取,你得自己循环。 - -循环还分三种 - -1. 获取 key 和 value - -.. code:: go - - import "fmt" - - func main() { - scores := map[string]int{"english": 80, "chinese": 85} - - for subject, score := range scores { - fmt.Printf("key: %s, value: %d\n", subject, scores) - } - } - -2. 只获取key,这里注意不用占用符。 - -.. code:: go - - import "fmt" - - func main() { - scores := map[string]int{"english": 80, "chinese": 85} - - for subject := range scores { - fmt.Printf("key: %s\n", subject) - } - } - -3. 只获取 value,用一个占位符替代。 - -.. code:: go - - import "fmt" - - func main() { - scores := map[string]int{"english": 80, "chinese": 85} - - for _, score := range scores { - fmt.Printf("value: %d\n", score) - } - } - -2. 布尔类型 ------------ - -关于布尔值,无非就两个值:true 和 -false。只是这两个值,在不同的语言里可能不同。 - -在 Python 中,真值用 True 表示,与 1 相等,假值用 False 表示,与 0 相等 - -.. code:: python - - >>> True == 1 - True - >>> False == 0 - True - >>> - -而在 Go 中,真值用 true 表示,不但不与 1 -相等,并且更加严格,不同类型无法进行比较,而假值用 false 表示,同样与 0 -无法比较。 - -如下图所示,Goland 直接波浪线提示类型不匹配,不能比较。 - -|image0| - -Go 中确实不如 Python 那样灵活,bool 与 int -不能直接转换,如果要转换,需要你自己实现函数。 - -**bool 转 int** - -.. code:: go - - func bool2int(b bool) int { - if b { - return 1 - } - return 0 - } - -**int 转 bool** - -.. code:: go - - func int2bool(i int) bool { - return i != 0 - } - -在 Python 中使用 not 对逻辑值取反,而 Go 中使用 ``!`` 符号 - -.. code:: go - - import "fmt" - - var male bool = true - func main() { - fmt.Println( !male == false) - // 或者 - fmt.Println( male != false) - } - - // output: true - -一个 if 判断语句,有可能不只一个判断条件,在 Python 中是使用 ``and`` 和 -``or`` 来执行逻辑运算 - -.. code:: python - - >>> age = 15 - >>> gender = "male" - >>> - >>> gender == "male" and age >18 - False - -而在 Go 语言中,则使用 ``&&`` 表示\ ``且``\ ,用 ``||`` -表示\ ``或``\ ,并且有短路行为(即左边表达式已经可以确认整个表达式的值,那么右边将不会再被求值。 - -.. code:: go - - import "fmt" - - var age int = 15 - var gender string = "male" - func main() { - // && 两边的表达式都会执行 - fmt.Println( age > 18 && gender == "male") - // gender == "male" 并不会执行 - fmt.Println( age > 18 || gender == "male") - } - - // output: false - // output: true - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - - -.. |image0| image:: http://image.python-online.cn/20200106201856.png - diff --git a/source/c09/c09_07.md b/source/c09/c09_07.md deleted file mode 100644 index de17532..0000000 --- a/source/c09/c09_07.md +++ /dev/null @@ -1,232 +0,0 @@ -# 9.7 详解数据类型:指针 - -## 0. 什么是指针 - -当我们定义一个变量 name - -```go -var name string = "Go编程时光" -``` - -此时,name 是变量名,它只是编程语言中方便程序员编写和理解代码的一个标签。 - -当我们访问这个标签时,机算机会返回给我们它指向的内存地址里存储的值:`Go编程时光`。 - -出于某些需要,我们会将这个内存地址赋值给另一个变量名,通常叫做 ptr(pointer的简写),而这个变量,我们称之为指针变量。 - -换句话说,指针变量(一个标签)的值是指针,也就是内存地址。 - -根据变量指向的值,是否是内存地址,我把变量分为两种: - -- 普通变量:存数据值本身 -- 指针变量:存值的内存地址 - - - -## 1. 指针的创建 - -指针创建有三种方法 - -**第一种方法** - -先定义对应的变量,再通过变量取得内存地址,创建指针 - -```go -// 定义普通变量 -aint := 1 -// 定义指针变量 -ptr := &aint -``` - - - -**第二种方法** - -先创建指针,分配好内存后,再给指针指向的内存地址写入对应的值。 - -```go -// 创建指针 -astr := new(string) -// 给指针赋值 -*astr = "Go编程时光" -``` - - - -**第三种方法** - -先声明一个指针变量,再从其他变量取得内存地址赋值给它 - -```go -aint := 1 -var bint *int // 声明一个指针 -bint = &aint // 初始化 -``` - - - -上面的三段代码中,指针的操作都离不开这两个符号: - -- `&` :从一个普通变量中取得内存地址 -- `*`:当`*`在赋值操作值的右边,是从一个指针变量中取得变量值,当`*`在赋值操作值的左边,是指该指针指向的变量 - - - -通过下面这段代码,你可以熟悉这两个符号的用法 - -```go -package main - -import "fmt" - -func main() { - aint := 1 // 定义普通变量 - ptr := &aint // 定义指针变量 - fmt.Println("普通变量存储的是:", aint) - fmt.Println("普通变量存储的是:", *ptr) - fmt.Println("指针变量存储的是:", &aint) - fmt.Println("指针变量存储的是:", ptr) -} -``` - -输出如下 - -``` -普通变量存储的是: 1 -普通变量存储的是: 1 -指针变量存储的是: 0xc0000100a0 -指针变量存储的是: 0xc0000100a0 -``` - - - -要想打印指针指向的内存地址,方法有两种 - -```go -// 第一种 -fmt.Printf("%p", ptr) - -// 第二种 -fmt.Println(ptr) -``` - - - -## 2. 指针的类型 - -我们知道字符串的类型是 string,整型是int,那么指针如何表示呢? - -写段代码试验一下就知道了 - -```go -package main - -import "fmt" - -func main() { - astr := "hello" - aint := 1 - abool := false - arune := 'a' - afloat := 1.2 - - fmt.Printf("astr 指针类型是:%T\n", &astr) - fmt.Printf("aint 指针类型是:%T\n", &aint) - fmt.Printf("abool 指针类型是:%T\n", &abool) - fmt.Printf("arune 指针类型是:%T\n", &arune) - fmt.Printf("afloat 指针类型是:%T\n", &afloat) -} -``` - -输出如下,可以发现用 `*`+所指向变量值的数据类型,就是对应的指针类型。 - -``` -astr 指针类型是:*string -aint 指针类型是:*int -abool 指针类型是:*bool -arune 指针类型是:*int32 -afloat 指针类型是:*float64 -``` - -所以若我们定义一个只接收指针类型的参数的函数,可以这么写 - -``` -func mytest(ptr *int) { - fmt.Println(*ptr) -} -``` - - - -## 3. 指针的零值 - -当指针声明后,没有进行初始化,其零值是 nil。 - -```go -func main() { - a := 25 - var b *int // 声明一个指针 - - if b == nil { - fmt.Println(b) - b = &a // 初始化:将a的内存地址给b - fmt.Println(b) - } -} -``` - -输出如下 - -``` - -0xc0000100a0 -``` - - - -## 4. 指针与切片 - -切片与指针一样,都是引用类型。 - -如果我们想通过一个函数改变一个数组的值,有两种方法 - -1. 将这个数组的切片做为参数传给函数 -2. 将这个数组的指针做为参数传给函数 - - - -尽管二者都可以实现我们的目的,但是按照 Go 语言的使用习惯,建议使用第一种方法,因为第一种方法,写出来的代码会更加简洁,易读。具体你可以参数下面两种方法的代码实现 - -**使用切片** - -```go -func modify(sls []int) { - sls[0] = 90 -} - -func main() { - a := [3]int{89, 90, 91} - modify(a[:]) - fmt.Println(a) -} -``` - -**使用指针** - -```go -func modify(arr *[3]int) { - (*arr)[0] = 90 -} - -func main() { - a := [3]int{89, 90, 91} - modify(&a) - fmt.Println(a) -} -``` - - - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_07.rst b/source/c09/c09_07.rst deleted file mode 100644 index 8dbb3b5..0000000 --- a/source/c09/c09_07.rst +++ /dev/null @@ -1,220 +0,0 @@ -9.7 详解数据类型:指针 -====================== - -0. 什么是指针 -------------- - -当我们定义一个变量 name - -.. code:: go - - var name string = "Go编程时光" - -此时,name -是变量名,它只是编程语言中方便程序员编写和理解代码的一个标签。 - -当我们访问这个标签时,机算机会返回给我们它指向的内存地址里存储的值:\ ``Go编程时光``\ 。 - -出于某些需要,我们会将这个内存地址赋值给另一个变量名,通常叫做 -ptr(pointer的简写),而这个变量,我们称之为指针变量。 - -换句话说,指针变量(一个标签)的值是指针,也就是内存地址。 - -根据变量指向的值,是否是内存地址,我把变量分为两种: - -- 普通变量:存数据值本身 -- 指针变量:存值的内存地址 - -1. 指针的创建 -------------- - -指针创建有三种方法 - -**第一种方法** - -先定义对应的变量,再通过变量取得内存地址,创建指针 - -.. code:: go - - // 定义普通变量 - aint := 1 - // 定义指针变量 - ptr := &aint - -**第二种方法** - -先创建指针,分配好内存后,再给指针指向的内存地址写入对应的值。 - -.. code:: go - - // 创建指针 - astr := new(string) - // 给指针赋值 - *astr = "Go编程时光" - -**第三种方法** - -先声明一个指针变量,再从其他变量取得内存地址赋值给它 - -.. code:: go - - aint := 1 - var bint *int // 声明一个指针 - bint = &aint // 初始化 - -上面的三段代码中,指针的操作都离不开这两个符号: - -- ``&`` :从一个普通变量中取得内存地址 -- ``*``\ :当\ ``*``\ 在赋值操作值的右边,是从一个指针变量中取得变量值,当\ ``*``\ 在赋值操作值的左边,是指该指针指向的变量 - -通过下面这段代码,你可以熟悉这两个符号的用法 - -.. code:: go - - package main - - import "fmt" - - func main() { - aint := 1 // 定义普通变量 - ptr := &aint // 定义指针变量 - fmt.Println("普通变量存储的是:", aint) - fmt.Println("普通变量存储的是:", *ptr) - fmt.Println("指针变量存储的是:", &aint) - fmt.Println("指针变量存储的是:", ptr) - } - -输出如下 - -:: - - 普通变量存储的是: 1 - 普通变量存储的是: 1 - 指针变量存储的是: 0xc0000100a0 - 指针变量存储的是: 0xc0000100a0 - -要想打印指针指向的内存地址,方法有两种 - -.. code:: go - - // 第一种 - fmt.Printf("%p", ptr) - - // 第二种 - fmt.Println(ptr) - -2. 指针的类型 -------------- - -我们知道字符串的类型是 string,整型是int,那么指针如何表示呢? - -写段代码试验一下就知道了 - -.. code:: go - - package main - - import "fmt" - - func main() { - astr := "hello" - aint := 1 - abool := false - arune := 'a' - afloat := 1.2 - - fmt.Printf("astr 指针类型是:%T\n", &astr) - fmt.Printf("aint 指针类型是:%T\n", &aint) - fmt.Printf("abool 指针类型是:%T\n", &abool) - fmt.Printf("arune 指针类型是:%T\n", &arune) - fmt.Printf("afloat 指针类型是:%T\n", &afloat) - } - -输出如下,可以发现用 -``*``\ +所指向变量值的数据类型,就是对应的指针类型。 - -:: - - astr 指针类型是:*string - aint 指针类型是:*int - abool 指针类型是:*bool - arune 指针类型是:*int32 - afloat 指针类型是:*float64 - -所以若我们定义一个只接收指针类型的参数的函数,可以这么写 - -:: - - func mytest(ptr *int) { - fmt.Println(*ptr) - } - -3. 指针的零值 -------------- - -当指针声明后,没有进行初始化,其零值是 nil。 - -.. code:: go - - func main() { - a := 25 - var b *int // 声明一个指针 - - if b == nil { - fmt.Println(b) - b = &a // 初始化:将a的内存地址给b - fmt.Println(b) - } - } - -输出如下 - -:: - - - 0xc0000100a0 - -4. 指针与切片 -------------- - -切片与指针一样,都是引用类型。 - -如果我们想通过一个函数改变一个数组的值,有两种方法 - -1. 将这个数组的切片做为参数传给函数 -2. 将这个数组的指针做为参数传给函数 - -尽管二者都可以实现我们的目的,但是按照 Go -语言的使用习惯,建议使用第一种方法,因为第一种方法,写出来的代码会更加简洁,易读。具体你可以参数下面两种方法的代码实现 - -**使用切片** - -.. code:: go - - func modify(sls []int) { - sls[0] = 90 - } - - func main() { - a := [3]int{89, 90, 91} - modify(a[:]) - fmt.Println(a) - } - -**使用指针** - -.. code:: go - - func modify(arr *[3]int) { - (*arr)[0] = 90 - } - - func main() { - a := [3]int{89, 90, 91} - modify(&a) - fmt.Println(a) - } - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_08.md b/source/c09/c09_08.md deleted file mode 100644 index c55f27d..0000000 --- a/source/c09/c09_08.md +++ /dev/null @@ -1,257 +0,0 @@ -# 9.8 面向对象编程:结构体与继承 - -## 0. 什么是结构体? - -在之前学过的数据类型中,数组与切片,只能存储同一类型的变量。若要存储多个类型的变量,就需要用到结构体,它是将多个容易类型的命令变量组合在一起的聚合数据类型。 - -每个变量都成为该结构体的成员变量。 - -可以理解为 Go语言 的结构体struct和其他语言的class有相等的地位,但是Go语言放弃大量面向对象的特性,所有的Go语言类型除了指针类型外,都可以有自己的方法,提高了可扩展性。 - -在 Go 语言中没有没有 class 类的概念,只有 struct 结构体的概念,因此也没有继承,本篇文章,带你学习一下结构体相关的内容。 - -## 1. 定义结构体 - -声明结构体 - -```go -type 结构体名 struct { - 属性名 属性类型 - 属性名 属性类型 - ... -} -``` - -比如我要定义一个可以存储个人资料名为 Profile 的结构体,可以这么写 - -```go -type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 -} -``` - -## 2. 定义方法 - -在 Go 语言中,我们无法在结构体内定义方法,那如何给一个结构体定义方法呢,答案是可以使用组合函数的方式来定义结构体方法。它和普通函数的定义方式有些不一样,比如下面这个方法 - -```go -func (person Profile) FmtProfile() { - fmt.Printf("名字:%s\n", person.name) - fmt.Printf("年龄:%d\n", person.age) - fmt.Printf("性别:%s\n", person.gender) -} -``` - -其中`fmt_profile` 是方法名,而`(person Profile)` :表示将 fmt_profile 方法与 Profile 的实例绑定。我们把 Profile 称为方法的接收者,而 person 表示实例本身,它相当于 Python 中的 self,在方法内可以使用 `person.属性名` 的方法来访问实例属性。 - -完整代码如下: - -```go -package main - -import "fmt" - -// 定义一个名为Profile 的结构体 -type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 -} - -// 定义一个与 Profile 的绑定的方法 -func (person Profile) FmtProfile() { - fmt.Printf("名字:%s\n", person.name) - fmt.Printf("年龄:%d\n", person.age) - fmt.Printf("性别:%s\n", person.gender) -} - -func main() { - // 实例化 - myself := Profile{name: "小明", age: 24, gender: "male"} - // 调用函数 - myself.FmtProfile() -} -``` - -输出如下 - -``` -名字:小明 -年龄:24 -性别:male -``` - -## 3. 方法的参数传递方式 - -上面定义方法的方式叫当你想要在方法内改变实例的属性的时候,必须使用指针做为方法的接收者。 - -```go -package main - -import "fmt" - -// 声明一个 Profile 的结构体 -type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 -} - -// 重点在于这个星号: * -func (person *Profile) increase_age() { - person.age += 1 -} - -func main() { - myself := Profile{name: "小明", age: 24, gender: "male"} - fmt.Printf("当前年龄:%d\n", myself.age) - myself.increase_age() - fmt.Printf("当前年龄:%d", myself.age) -} -``` - -输出结果 如下,可以看到在方法内部对 age 的修改已经生效。你可以尝试去掉 `*`,使用值做为方法接收者,看看age是否会发生改变。 - -``` -当前年龄:24 -当前年龄:25 -``` - - - -至此,我们知道了两种定义方法的方式: - -- 以值做为方法接收者 -- 以指针做为方法接收者 - - - -那我们如何进行选择呢?以下几种情况,应当直接使用指针做为方法的接收者。 - -1. 你需要在方法内部改变结构体内容的时候 -2. 出于性能的问题,当结构体过大的时候 - -有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,建议都使用指针做为接收者。 - -不管你使用哪种方法定义方法,指针实例对象、值实例对象都可以直接调用,而没有什么约束。这一点Go语言做得非常好。 - - - -## 4. 结构体实现 “继承” - -为什么标题的继承,加了双引号,因为Go 语言本身并不支持继承。 - -但我们可以使用组合的方法,实现类似继承的效果。 - -在生活中,组合的例子非常多,比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起,最后才有了我们用的电脑。 - -同样的,在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。 - -现在这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。 - -```go -type company struct { - companyName string - companyAddr string -} - -type staff struct { - name string - age int - gender string - position string -} -``` - -若要将公司信息与公司职员关联起来,一般都会想到将 company 结构体的内容照抄到 staff 里。 - -```go -type staff struct { - name string - age int - gender string - companyName string - companyAddr string - position string -} -``` - -虽然在实现上并没有什么问题,但在你对同一公司的多个staff初始化的时候,都得重复初始化相同的公司信息,这做得并不好,借鉴继承的思想,我们可以将公司的属性都“继承”过来。 - -但是在 Go 中没有类的概念,只有组合,你可以将 company 这个 结构体嵌入到 staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company 的所有属性了。 - -```go -type staff struct { - name string - age int - gender string - position string - company // 匿名字段 -} -``` - -来写个完整的程序验证一下。 - -```go -package main - -import "fmt" - -type company struct { - companyName string - companyAddr string -} - -type staff struct { - name string - age int - gender string - position string - company -} - -func main() { - myCom := company{ - companyName: "Tencent", - companyAddr: "深圳市南山区", - } - staffInfo := staff{ - name: "小明", - age: 28, - gender: "男", - position: "云计算开发工程师", - company: myCom, - } - - fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName) - fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName) -} -``` - -输出结果如下,可见`staffInfo.companyName` 和 `staffInfo.company.companyName` 的效果是一样的。 - -``` -小明 在 Tencent 工作 -小明 在 Tencent 工作 -``` - - - -## 5. 内部方法与外部方法 - -在 Go 语言中,函数名的首字母大小写非常重要,它被来实现控制对方法的访问权限。 - -- 当方法的首字母为大写时,这个方法对于所有包都是Public,其他包可以随意调用 -- 当方法的首字母为小写时,这个方法是Private,其他包是无法访问的。 - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_08.rst b/source/c09/c09_08.rst deleted file mode 100644 index 5dfab91..0000000 --- a/source/c09/c09_08.rst +++ /dev/null @@ -1,268 +0,0 @@ -9.8 面向对象编程:结构体与继承 -============================== - -0. 什么是结构体? ------------------ - -在之前学过的数据类型中,数组与切片,只能存储同一类型的变量。若要存储多个类型的变量,就需要用到结构体,它是将多个容易类型的命令变量组合在一起的聚合数据类型。 - -每个变量都成为该结构体的成员变量。 - -可以理解为 Go语言 -的结构体struct和其他语言的class有相等的地位,但是Go语言放弃大量面向对象的特性,所有的Go语言类型除了指针类型外,都可以有自己的方法,提高了可扩展性。 - -在 Go 语言中没有没有 class 类的概念,只有 struct -结构体的概念,因此也没有继承,本篇文章,带你学习一下结构体相关的内容。 - -1. 定义结构体 -------------- - -声明结构体 - -.. code:: go - - type 结构体名 struct { - 属性名 属性类型 - 属性名 属性类型 - ... - } - -比如我要定义一个可以存储个人资料名为 Profile 的结构体,可以这么写 - -.. code:: go - - type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 - } - -2. 定义方法 ------------ - -在 Go -语言中,我们无法在结构体内定义方法,那如何给一个结构体定义方法呢,答案是可以使用组合函数的方式来定义结构体方法。它和普通函数的定义方式有些不一样,比如下面这个方法 - -.. code:: go - - func (person Profile) FmtProfile() { - fmt.Printf("名字:%s\n", person.name) - fmt.Printf("年龄:%d\n", person.age) - fmt.Printf("性别:%s\n", person.gender) - } - -其中\ ``fmt_profile`` 是方法名,而\ ``(person Profile)`` :表示将 -fmt_profile 方法与 Profile 的实例绑定。我们把 Profile -称为方法的接收者,而 person 表示实例本身,它相当于 Python 中的 -self,在方法内可以使用 ``person.属性名`` 的方法来访问实例属性。 - -完整代码如下: - -.. code:: go - - package main - - import "fmt" - - // 定义一个名为Profile 的结构体 - type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 - } - - // 定义一个与 Profile 的绑定的方法 - func (person Profile) FmtProfile() { - fmt.Printf("名字:%s\n", person.name) - fmt.Printf("年龄:%d\n", person.age) - fmt.Printf("性别:%s\n", person.gender) - } - - func main() { - // 实例化 - myself := Profile{name: "小明", age: 24, gender: "male"} - // 调用函数 - myself.FmtProfile() - } - -输出如下 - -:: - - 名字:小明 - 年龄:24 - 性别:male - -3. 方法的参数传递方式 ---------------------- - -上面定义方法的方式叫当你想要在方法内改变实例的属性的时候,必须使用指针做为方法的接收者。 - -.. code:: go - - package main - - import "fmt" - - // 声明一个 Profile 的结构体 - type Profile struct { - name string - age int - gender string - mother *Profile // 指针 - father *Profile // 指针 - } - - // 重点在于这个星号: * - func (person *Profile) increase_age() { - person.age += 1 - } - - func main() { - myself := Profile{name: "小明", age: 24, gender: "male"} - fmt.Printf("当前年龄:%d\n", myself.age) - myself.increase_age() - fmt.Printf("当前年龄:%d", myself.age) - } - -输出结果 如下,可以看到在方法内部对 age 的修改已经生效。你可以尝试去掉 -``*``\ ,使用值做为方法接收者,看看age是否会发生改变。 - -:: - - 当前年龄:24 - 当前年龄:25 - -至此,我们知道了两种定义方法的方式: - -- 以值做为方法接收者 -- 以指针做为方法接收者 - -那我们如何进行选择呢?以下几种情况,应当直接使用指针做为方法的接收者。 - -1. 你需要在方法内部改变结构体内容的时候 -2. 出于性能的问题,当结构体过大的时候 - -有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,建议都使用指针做为接收者。 - -不管你使用哪种方法定义方法,指针实例对象、值实例对象都可以直接调用,而没有什么约束。这一点Go语言做得非常好。 - -4. 结构体实现 “继承” --------------------- - -为什么标题的继承,加了双引号,因为Go 语言本身并不支持继承。 - -但我们可以使用组合的方法,实现类似继承的效果。 - -在生活中,组合的例子非常多,比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起,最后才有了我们用的电脑。 - -同样的,在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。 - -现在这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。 - -.. code:: go - - type company struct { - companyName string - companyAddr string - } - - type staff struct { - name string - age int - gender string - position string - } - -若要将公司信息与公司职员关联起来,一般都会想到将 company -结构体的内容照抄到 staff 里。 - -.. code:: go - - type staff struct { - name string - age int - gender string - companyName string - companyAddr string - position string - } - -虽然在实现上并没有什么问题,但在你对同一公司的多个staff初始化的时候,都得重复初始化相同的公司信息,这做得并不好,借鉴继承的思想,我们可以将公司的属性都“继承”过来。 - -但是在 Go 中没有类的概念,只有组合,你可以将 company 这个 结构体嵌入到 -staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company -的所有属性了。 - -.. code:: go - - type staff struct { - name string - age int - gender string - position string - company // 匿名字段 - } - -来写个完整的程序验证一下。 - -.. code:: go - - package main - - import "fmt" - - type company struct { - companyName string - companyAddr string - } - - type staff struct { - name string - age int - gender string - position string - company - } - - func main() { - myCom := company{ - companyName: "Tencent", - companyAddr: "深圳市南山区", - } - staffInfo := staff{ - name: "小明", - age: 28, - gender: "男", - position: "云计算开发工程师", - company: myCom, - } - - fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName) - fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName) - } - -输出结果如下,可见\ ``staffInfo.companyName`` 和 -``staffInfo.company.companyName`` 的效果是一样的。 - -:: - - 小明 在 Tencent 工作 - 小明 在 Tencent 工作 - -5. 内部方法与外部方法 ---------------------- - -在 Go -语言中,函数名的首字母大小写非常重要,它被来实现控制对方法的访问权限。 - -- 当方法的首字母为大写时,这个方法对于所有包都是Public,其他包可以随意调用 -- 当方法的首字母为小写时,这个方法是Private,其他包是无法访问的。 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_09.md b/source/c09/c09_09.md deleted file mode 100644 index d4e6fa5..0000000 --- a/source/c09/c09_09.md +++ /dev/null @@ -1,261 +0,0 @@ -# 9.9 一篇文章理解 Go 里的函数 - -## 1. 关于函数 - -函数是基于功能或 逻辑进行封装的可复用的代码结构。将一段功能复杂、很长的一段代码封装成多个代码片段(即函数),有助于提高代码可读性和可维护性。 - -在 Go 语言中,函数可以分为两种: - -- 带有名字的普通函数 -- 没有名字的匿名函数 - -由于 Go语言是编译型语言,所以函数编写的顺序是无关紧要的,它不像 Python 那样,函数在位置上需要定义在调用之前。 - -## 2. 函数的声明 - -函数的声明,使用 func 关键字,后面依次接 `函数名`,`参数列表`,`返回值列表`,`用 {} 包裹的代码逻辑体` - -``` -func 函数名(形式参数列表)(返回值列表){ - 函数体 -} -``` - -- 形式参数列表描述了函数的参数名以及参数类型,这些参数作为局部变量,其值由参数调用者提供 - -- 返回值列表描述了函数返回值的变量名以及类型,如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。 - -举个例子,定义一个 sum 函数,接收两个 int 类型的参数,在运行中,将其值分别赋值给 a,b,并规定必须返回一个int类型的值 。 - -```go -func sum(a int, b int) (int){ - return a + b -} -func main() { - fmt.Println(sum(1,2)) -} -``` - -## 3. 函数实现可变参数 - -上面举的例子,参数个数都是固定的,这很好理解 ,指定什么类型的参数就传入什么类型的变量,数量上,不能多一个,也不能少一个。(好像没有可选参数)。 - -在 Python 中我们可以使用 *args 和 **kw ,还实现可变参数的函数。 - -可变参数分为几种: - -- 多个类型一致的参数 -- 多个类型不一致的参数 - -### 多个类型一致的参数 - -首先是多个类型一致的参数。 - -这边定义一个可以对多个数值进行求和的函数, - -使用 `...int`,表示一个元素为int类型的切片,用来接收调用者传入的参数。 - -```go -// 使用 ...类型,表示一个元素为int类型的切片 -func sum(args ...int) int { - var sum int - for _, v := range args { - sum += v - } - return sum -} -func main() { - fmt.Println(sum(1, 2, 3)) -} - -// output: 6 -``` - -其中 `...` 是 Go 语言为了方便程序员写代码而实现的语法糖,如果该函数下会多个类型的函数,这个语法糖必须得是最后一个参数。 - -同时这个语法糖,只能在定义函数时使用。 - - - -### 多个类型不一致的参数 - -上面那个例子中,我们的参数类型都是 int,如果你希望传多个参数且这些参数的类型都不一样,可以指定类型为 `...interface{}`,然后再遍历。 - -比如下面这段代码,是Go语言标准库中 fmt.Printf() 的函数原型: - -```go -import "fmt" -func MyPrintf(args ...interface{}) { - for _, arg := range args { - switch arg.(type) { - case int: - fmt.Println(arg, "is an int value.") - case string: - fmt.Println(arg, "is a string value.") - case int64: - fmt.Println(arg, "is an int64 value.") - default: - fmt.Println(arg, "is an unknown type.") - } - } -} - -func main() { - var v1 int = 1 - var v2 int64 = 234 - var v3 string = "hello" - var v4 float32 = 1.234 - MyPrintf(v1, v2, v3, v4) -} -``` - - - -在某些情况下,我们需要定义一个参数个数可变的函数,具体传入几个参数,由调用者自己决定,但不管传入几个参数,函数都能够处理。 - -比如这边实现一个累加 - -```go -func myfunc(args ...int) { - for _, arg := range args { - fmt.Println(arg) - } -} -``` - - - -## 4. 多个可变参数函数传递参数 - -上面提到了可以使用 `...` 来接收多个参数,除此之外,它还有一个用法,就是用来解序列,将函数的可变参数(一个切片)一个一个取出来,传递给另一个可变参数的函数,而不是传递可变参数变量本身。 - -同样这个用法,也只能在给函数传递参数里使用。 - -例子如下: - -```go -import "fmt" - -func sum(args ...int) int { - var result int - for _, v := range args { - result += v - } - return result -} - -func Sum(args ...int) int { - // 利用 ... 来解序列 - result := sum(args...) - return result -} -func main() { - fmt.Println(sum(1, 2, 3)) -} -``` - - - -## 5. 函数的返回值 - -Go语言中的函数,在你定义的时候,就规定了此函数 - -1. 有没有返回值? - - 当没有指明返回值的类型时, 函数体不能有 return,Go并不像 Python 那样没有return,就默认返回None - -2. 返回几个值? - - Go 支持一个函数返回多个值 - - ```go - func double(a int) (int, int) { - b := a * 2 - return a, b - } - func main() { - // 接收参数用逗号分隔 - a, b := double(2) - fmt.Println(a, b) - } - ``` - - - -3. 怎么返回值? - - Go支持返回带有变量名的值 - - ```go - func double(a int) (b int) { - // 不能使用 := ,因为在返回值哪里已经声明了为int - b = a * 2 - // 不需要指明写回哪个变量,在返回值类型那里已经指定了 - return - } - func main() { - fmt.Println(double(2)) - } - // output: 4 - ``` - - - - -## 6. 方法与函数 - -方法,在上一节《[08. 面向对象编程:结构体与继承](https://mp.weixin.qq.com/s/8NsSI7EsYqbCpj0OHu7ImQ)》里已经介绍过了,它的定义与函数有些不同,你可以点击前面的标题进行交叉学习。 - -那 **方法和函数有什么区别?** 为防会有朋友第一次接触面向对象,这里多嘴一句。 - -方法,是一种特殊的函数。当你一个函数和对象/结构体进行绑定的时候,我们就称这个函数是一个方法。 - - - -## 7. 匿名函数的使用 - -所谓匿名函数,就是没有名字的函数,它只有函数逻辑体,而没有函数名。 - -定义的格式如下 - -```go -func(参数列表)(返回参数列表){ - 函数体 -} -``` - -一个名字实际上并没有多大区别,所有使用匿名函数都可以改成普通有名函数,那么什么情况下,会使用匿名函数呢? - -定义变量名,是一个不难但是还费脑子的事情,对于那到只使用一次的函数,是没必要拥有姓名的。这才有了匿名函数。 - -有了这个背景,决定了匿名函数只有拥有短暂的生命,一般都是定义后立即使用。 - -就像这样,定义后立马执行(这里只是举例,实际代码没有意义)。 - -```go -func(data int) { - fmt.Println("hello", data) -}(100) -``` - -亦或是做为回调函数使用 - -```go -// 第二个参数为函数 -func visit(list []int, f func(int)) { - for _, v := range list { - // 执行回调函数 - f(v) - } -} -func main() { - // 使用匿名函数直接做为参数 - visit([]int{1, 2, 3, 4}, func(v int) { - fmt.Println(v) - }) -} -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_09.rst b/source/c09/c09_09.rst deleted file mode 100644 index 236461c..0000000 --- a/source/c09/c09_09.rst +++ /dev/null @@ -1,270 +0,0 @@ -9.9 一篇文章理解 Go 里的函数 -============================ - -1. 关于函数 ------------ - -函数是基于功能或 -逻辑进行封装的可复用的代码结构。将一段功能复杂、很长的一段代码封装成多个代码片段(即函数),有助于提高代码可读性和可维护性。 - -在 Go 语言中,函数可以分为两种: - -- 带有名字的普通函数 -- 没有名字的匿名函数 - -由于 Go语言是编译型语言,所以函数编写的顺序是无关紧要的,它不像 Python -那样,函数在位置上需要定义在调用之前。 - -2. 函数的声明 -------------- - -函数的声明,使用 func 关键字,后面依次接 -``函数名``\ ,\ ``参数列表``\ ,\ ``返回值列表``\ ,\ ``用 {} 包裹的代码逻辑体`` - -:: - - func 函数名(形式参数列表)(返回值列表){ - 函数体 - } - -- 形式参数列表描述了函数的参数名以及参数类型,这些参数作为局部变量,其值由参数调用者提供 - -- 返回值列表描述了函数返回值的变量名以及类型,如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。 - -举个例子,定义一个 sum 函数,接收两个 int -类型的参数,在运行中,将其值分别赋值给 -a,b,并规定必须返回一个int类型的值 。 - -.. code:: go - - func sum(a int, b int) (int){ - return a + b - } - func main() { - fmt.Println(sum(1,2)) - } - -3. 函数实现可变参数 -------------------- - -上面举的例子,参数个数都是固定的,这很好理解 -,指定什么类型的参数就传入什么类型的变量,数量上,不能多一个,也不能少一个。(好像没有可选参数)。 - -在 Python 中我们可以使用 \*args 和 \**kw ,还实现可变参数的函数。 - -可变参数分为几种: - -- 多个类型一致的参数 -- 多个类型不一致的参数 - -多个类型一致的参数 -~~~~~~~~~~~~~~~~~~ - -首先是多个类型一致的参数。 - -这边定义一个可以对多个数值进行求和的函数, - -使用 -``...int``\ ,表示一个元素为int类型的切片,用来接收调用者传入的参数。 - -.. code:: go - - // 使用 ...类型,表示一个元素为int类型的切片 - func sum(args ...int) int { - var sum int - for _, v := range args { - sum += v - } - return sum - } - func main() { - fmt.Println(sum(1, 2, 3)) - } - - // output: 6 - -其中 ``...`` 是 Go -语言为了方便程序员写代码而实现的语法糖,如果该函数下会多个类型的函数,这个语法糖必须得是最后一个参数。 - -同时这个语法糖,只能在定义函数时使用。 - -多个类型不一致的参数 -~~~~~~~~~~~~~~~~~~~~ - -上面那个例子中,我们的参数类型都是 -int,如果你希望传多个参数且这些参数的类型都不一样,可以指定类型为 -``...interface{}``\ ,然后再遍历。 - -比如下面这段代码,是Go语言标准库中 fmt.Printf() 的函数原型: - -.. code:: go - - import "fmt" - func MyPrintf(args ...interface{}) { - for _, arg := range args { - switch arg.(type) { - case int: - fmt.Println(arg, "is an int value.") - case string: - fmt.Println(arg, "is a string value.") - case int64: - fmt.Println(arg, "is an int64 value.") - default: - fmt.Println(arg, "is an unknown type.") - } - } - } - - func main() { - var v1 int = 1 - var v2 int64 = 234 - var v3 string = "hello" - var v4 float32 = 1.234 - MyPrintf(v1, v2, v3, v4) - } - -在某些情况下,我们需要定义一个参数个数可变的函数,具体传入几个参数,由调用者自己决定,但不管传入几个参数,函数都能够处理。 - -比如这边实现一个累加 - -.. code:: go - - func myfunc(args ...int) { - for _, arg := range args { - fmt.Println(arg) - } - } - -4. 多个可变参数函数传递参数 ---------------------------- - -上面提到了可以使用 ``...`` -来接收多个参数,除此之外,它还有一个用法,就是用来解序列,将函数的可变参数(一个切片)一个一个取出来,传递给另一个可变参数的函数,而不是传递可变参数变量本身。 - -同样这个用法,也只能在给函数传递参数里使用。 - -例子如下: - -.. code:: go - - import "fmt" - - func sum(args ...int) int { - var result int - for _, v := range args { - result += v - } - return result - } - - func Sum(args ...int) int { - // 利用 ... 来解序列 - result := sum(args...) - return result - } - func main() { - fmt.Println(sum(1, 2, 3)) - } - -5. 函数的返回值 ---------------- - -Go语言中的函数,在你定义的时候,就规定了此函数 - -1. 有没有返回值? - - 当没有指明返回值的类型时, 函数体不能有 return,Go并不像 Python - 那样没有return,就默认返回None - -2. 返回几个值? - - Go 支持一个函数返回多个值 - - .. code:: go - - func double(a int) (int, int) { - b := a * 2 - return a, b - } - func main() { - // 接收参数用逗号分隔 - a, b := double(2) - fmt.Println(a, b) - } - -3. 怎么返回值? - - Go支持返回带有变量名的值 - - .. code:: go - - func double(a int) (b int) { - // 不能使用 := ,因为在返回值哪里已经声明了为int - b = a * 2 - // 不需要指明写回哪个变量,在返回值类型那里已经指定了 - return - } - func main() { - fmt.Println(double(2)) - } - // output: 4 - -6. 方法与函数 -------------- - -方法,在上一节《\ `08. -面向对象编程:结构体与继承 `__\ 》里已经介绍过了,它的定义与函数有些不同,你可以点击前面的标题进行交叉学习。 - -那 **方法和函数有什么区别?** -为防会有朋友第一次接触面向对象,这里多嘴一句。 - -方法,是一种特殊的函数。当你一个函数和对象/结构体进行绑定的时候,我们就称这个函数是一个方法。 - -7. 匿名函数的使用 ------------------ - -所谓匿名函数,就是没有名字的函数,它只有函数逻辑体,而没有函数名。 - -定义的格式如下 - -.. code:: go - - func(参数列表)(返回参数列表){ - 函数体 - } - -一个名字实际上并没有多大区别,所有使用匿名函数都可以改成普通有名函数,那么什么情况下,会使用匿名函数呢? - -定义变量名,是一个不难但是还费脑子的事情,对于那到只使用一次的函数,是没必要拥有姓名的。这才有了匿名函数。 - -有了这个背景,决定了匿名函数只有拥有短暂的生命,一般都是定义后立即使用。 - -就像这样,定义后立马执行(这里只是举例,实际代码没有意义)。 - -.. code:: go - - func(data int) { - fmt.Println("hello", data) - }(100) - -亦或是做为回调函数使用 - -.. code:: go - - // 第二个参数为函数 - func visit(list []int, f func(int)) { - for _, v := range list { - // 执行回调函数 - f(v) - } - } - func main() { - // 使用匿名函数直接做为参数 - visit([]int{1, 2, 3, 4}, func(v int) { - fmt.Println(v) - }) - } - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_10.md b/source/c09/c09_10.md deleted file mode 100644 index 908e388..0000000 --- a/source/c09/c09_10.md +++ /dev/null @@ -1,123 +0,0 @@ -# 9.10 Go语言流程控制:if-else - -## 1. 条件语句模型 - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -今天先来讲讲 if-else 条件语句 - -Go 里的条件语句模型是这样的 - -```go -if 条件 1 { - 分支 1 -} else if 条件 2 { - 分支 2 -} else if 条件 ... { - 分支 ... -} else { - 分支 else -} -``` - -Go编译器,对于 `{` 和 `}` 的位置有严格的要求,它要求 else if (或 else)和 两边的花括号,必须在同一行。 - -由于 Go是 强类型,所以要求你条件表达式必须严格返回布尔型的数据(nil 和 0 和 1 都不行,具体可查看《详解数据类型:字典与布尔类型》)。 - -对于这个模型,分别举几个例子来看一下。 - - - -## 2. 单分支判断 - -只有一个 if ,没有 else - -```go -import "fmt" - -func main() { - age := 20 - if age > 18 { - fmt.Println("已经成年了") - } -} -``` - -如果条件里需要满足多个条件,可以使用 `&&` 和 `||` - -- `&&`:表示且,左右都需要为true,最终结果才能为 true,否则为 false -- `||`:表示或,左右只要有一个为true,最终结果即为true,否则 为 false - -```go -import "fmt" - -func main() { - age := 20 - gender := "male" - if (age > 18 && gender == "male") { - fmt.Println("是成年男性") - } -} -``` - - - -## 3. 多分支判断 - -if - else - -```go -import "fmt" - -func main() { - age := 20 - if age > 18 { - fmt.Println("已经成年了") - } else { - fmt.Println("还未成年") - } -} -``` - -if - else if - else - -```go -import "fmt" - -func main() { - age := 20 - if age > 18 { - fmt.Println("已经成年了") - } else if age >12 { - fmt.Println("已经是青少年了") - } else { - fmt.Println("还不是青少年") - } -} -``` - - - -## 4. 高级写法 - -在 if 里可以允许先运行一个表达式,取得变量后,再对其进行判断,比如第一个例子里代码也可以写成这样 - -```go -import "fmt" - -func main() { - if age := 20;age > 18 { - fmt.Println("已经成年了") - } -} -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_10.rst b/source/c09/c09_10.rst deleted file mode 100644 index 7867368..0000000 --- a/source/c09/c09_10.rst +++ /dev/null @@ -1,126 +0,0 @@ -9.10 Go语言流程控制:if-else -============================ - -1. 条件语句模型 ---------------- - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -今天先来讲讲 if-else 条件语句 - -Go 里的条件语句模型是这样的 - -.. code:: go - - if 条件 1 { - 分支 1 - } else if 条件 2 { - 分支 2 - } else if 条件 ... { - 分支 ... - } else { - 分支 else - } - -Go编译器,对于 ``{`` 和 ``}`` 的位置有严格的要求,它要求 else if (或 -else)和 两边的花括号,必须在同一行。 - -由于 Go是 强类型,所以要求你条件表达式必须严格返回布尔型的数据(nil 和 0 -和 1 都不行,具体可查看《详解数据类型:字典与布尔类型》)。 - -对于这个模型,分别举几个例子来看一下。 - -2. 单分支判断 -------------- - -只有一个 if ,没有 else - -.. code:: go - - import "fmt" - - func main() { - age := 20 - if age > 18 { - fmt.Println("已经成年了") - } - } - -如果条件里需要满足多个条件,可以使用 ``&&`` 和 ``||`` - -- ``&&``\ :表示且,左右都需要为true,最终结果才能为 true,否则为 false -- ``||``\ :表示或,左右只要有一个为true,最终结果即为true,否则 为 - false - -.. code:: go - - import "fmt" - - func main() { - age := 20 - gender := "male" - if (age > 18 && gender == "male") { - fmt.Println("是成年男性") - } - } - -3. 多分支判断 -------------- - -if - else - -.. code:: go - - import "fmt" - - func main() { - age := 20 - if age > 18 { - fmt.Println("已经成年了") - } else { - fmt.Println("还未成年") - } - } - -if - else if - else - -.. code:: go - - import "fmt" - - func main() { - age := 20 - if age > 18 { - fmt.Println("已经成年了") - } else if age >12 { - fmt.Println("已经是青少年了") - } else { - fmt.Println("还不是青少年") - } - } - -4. 高级写法 ------------ - -在 if -里可以允许先运行一个表达式,取得变量后,再对其进行判断,比如第一个例子里代码也可以写成这样 - -.. code:: go - - import "fmt" - - func main() { - if age := 20;age > 18 { - fmt.Println("已经成年了") - } - } - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_11.md b/source/c09/c09_11.md deleted file mode 100644 index 237f7b0..0000000 --- a/source/c09/c09_11.md +++ /dev/null @@ -1,255 +0,0 @@ -# 9.11 Go语言流程控制:switch-case - - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -上一篇讲了 if -else 条件语句,今天先来讲讲 switch - case 选择语句。 - - - -## 0. 语句模型 - -Go 里的选择语句模型是这样的 - -```go -switch 表达式 { - case 表达式1: - 代码块 - case 表达式2: - 代码块 - case 表达式3: - 代码块 - case 表达式4: - 代码块 - case 表达式5: - 代码块 - default: - 代码块 -} -``` - -拿 switch 后的表达式分别和 case 后的表达式进行对比,只要有一个 case 满足条件,就会执行对应的代码块,然后直接退出 switch - case ,如果 一个都没有满足,才会执行 default 的代码块。 - -## 1. 最简单的示例 - -switch 后接一个你要判断变量 `education` (学历),然后 case 会拿这个 变量去和它后面的表达式(可能是常量、变量、表达式等)进行判等。 - -如果相等,就执行相应的代码块。如果不相等,就接着下一个 case。 - -```go -import "fmt" - -func main() { - education := "本科" - - switch education { - case "博士": - fmt.Println("我是博士") - case "研究生": - fmt.Println("我是研究生") - case "本科": - fmt.Println("我是本科生") - case "大专": - fmt.Println("我是大专生") - case "高中": - fmt.Println("我是高中生") - default: - fmt.Println("学历未达标..") - } -} - -``` - -输出如下 - -``` -我是本科生 -``` - - - -## 2. 一个 case 多个条件 - -case 后可以接多个多个条件,多个条件之间是 `或` 的关系,用逗号相隔。 - -```go -import "fmt" - -func main() { - month := 2 - - switch month { - case 3, 4, 5: - fmt.Println("春天") - case 6, 7, 8: - fmt.Println("夏天") - case 9, 10, 11: - fmt.Println("秋天") - case 12, 1, 2: - fmt.Println("冬天") - default: - fmt.Println("输入有误...") - } -} -``` - -输出如下 - -``` -冬天 -``` - - - -## 3. case 条件常量不能重复 - -当 case 后接的是常量时,该常量只能出现一次。 - -以下两种情况,在编译时,都会报错: duplicate case "male" in switch - -**错误案例一** - -```go -gender := "male" - -switch gender { - case "male": - fmt.Println("男性") - // 与上面重复 - case "male": - fmt.Println("男性") - case "female": - fmt.Println("女性") -} -``` - -**错误案例二** - -```go -gender := "male" - -switch gender { - case "male", "male": - fmt.Println("男性") - case "female": - fmt.Println("女性") -} -``` - -## 4. switch 后可接函数 - -switch 后面可以接一个函数,只要保证 case 后的值类型与函数的返回值 一致即可。 - -```go -import "fmt" - -// 判断一个同学是否有挂科记录的函数 -// 返回值是布尔类型 -func getResult(args ...int) bool { - for _, i := range args { - if i < 60 { - return false - } - } - return true -} - -func main() { - chinese := 80 - english := 50 - math := 100 - - switch getResult(chinese, english, math) { - // case 后也必须 是布尔类型 - case true: - fmt.Println("该同学所有成绩都合格") - case false: - fmt.Println("该同学有挂科记录") - } -} -``` - - - -## 5. switch 可不接表达式 - -switch 后可以不接任何变量、表达式、函数。 - -当不接任何东西时,switch - case 就相当于 if - elseif - else - -```go -score := 30 - -switch { - case score >= 95 && score <= 100: - fmt.Println("优秀") - case score >= 80: - fmt.Println("良好") - case score >= 60: - fmt.Println("合格") - case score >= 0: - fmt.Println("不合格") - default: - fmt.Println("输入有误...") -} -``` - - - -## 6. switch 的穿透能力 - -正常情况下 switch - case 的执行顺序是:只要有一个 case 满足条件,就会直接退出 switch - case ,如果 一个都没有满足,才会执行 default 的代码块。 - -但是有一种情况是例外。 - -那就是当 case 使用关键字 `fallthrough` 开启穿透能力的时候。 - -```go -s := "hello" -switch { -case s == "hello": - fmt.Println("hello") - fallthrough -case s != "world": - fmt.Println("world") -} -``` - -代码输出如下: - -``` -hello -world -``` - -需要注意的是,fallthrough 只能穿透一层,意思是它只给你一次再判断case的机会,不管你有没有匹配上,都要退出了。 - -```go -s := "hello" -switch { -case s == "hello": - fmt.Println("hello") - fallthrough -case s == "xxxx": - fmt.Println("xxxx") -case s != "world": - fmt.Println("world") -} -``` - -输出如下,并不会输出 `world`(即使它符合条件) - -``` -hello -xxxx -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_11.rst b/source/c09/c09_11.rst deleted file mode 100644 index 06582f9..0000000 --- a/source/c09/c09_11.rst +++ /dev/null @@ -1,258 +0,0 @@ -9.11 Go语言流程控制:switch-case -================================ - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -上一篇讲了 if -else 条件语句,今天先来讲讲 switch - case 选择语句。 - -0. 语句模型 ------------ - -Go 里的选择语句模型是这样的 - -.. code:: go - - switch 表达式 { - case 表达式1: - 代码块 - case 表达式2: - 代码块 - case 表达式3: - 代码块 - case 表达式4: - 代码块 - case 表达式5: - 代码块 - default: - 代码块 - } - -拿 switch 后的表达式分别和 case 后的表达式进行对比,只要有一个 case -满足条件,就会执行对应的代码块,然后直接退出 switch - case ,如果 -一个都没有满足,才会执行 default 的代码块。 - -1. 最简单的示例 ---------------- - -switch 后接一个你要判断变量 ``education`` (学历),然后 case 会拿这个 -变量去和它后面的表达式(可能是常量、变量、表达式等)进行判等。 - -如果相等,就执行相应的代码块。如果不相等,就接着下一个 case。 - -.. code:: go - - import "fmt" - - func main() { - education := "本科" - - switch education { - case "博士": - fmt.Println("我是博士") - case "研究生": - fmt.Println("我是研究生") - case "本科": - fmt.Println("我是本科生") - case "大专": - fmt.Println("我是大专生") - case "高中": - fmt.Println("我是高中生") - default: - fmt.Println("学历未达标..") - } - } - -输出如下 - -:: - - 我是本科生 - -2. 一个 case 多个条件 ---------------------- - -case 后可以接多个多个条件,多个条件之间是 ``或`` 的关系,用逗号相隔。 - -.. code:: go - - import "fmt" - - func main() { - month := 2 - - switch month { - case 3, 4, 5: - fmt.Println("春天") - case 6, 7, 8: - fmt.Println("夏天") - case 9, 10, 11: - fmt.Println("秋天") - case 12, 1, 2: - fmt.Println("冬天") - default: - fmt.Println("输入有误...") - } - } - -输出如下 - -:: - - 冬天 - -3. case 条件常量不能重复 ------------------------- - -当 case 后接的是常量时,该常量只能出现一次。 - -以下两种情况,在编译时,都会报错: duplicate case “male” in switch - -**错误案例一** - -.. code:: go - - gender := "male" - - switch gender { - case "male": - fmt.Println("男性") - // 与上面重复 - case "male": - fmt.Println("男性") - case "female": - fmt.Println("女性") - } - -**错误案例二** - -.. code:: go - - gender := "male" - - switch gender { - case "male", "male": - fmt.Println("男性") - case "female": - fmt.Println("女性") - } - -4. switch 后可接函数 --------------------- - -switch 后面可以接一个函数,只要保证 case 后的值类型与函数的返回值 -一致即可。 - -.. code:: go - - import "fmt" - - // 判断一个同学是否有挂科记录的函数 - // 返回值是布尔类型 - func getResult(args ...int) bool { - for _, i := range args { - if i < 60 { - return false - } - } - return true - } - - func main() { - chinese := 80 - english := 50 - math := 100 - - switch getResult(chinese, english, math) { - // case 后也必须 是布尔类型 - case true: - fmt.Println("该同学所有成绩都合格") - case false: - fmt.Println("该同学有挂科记录") - } - } - -5. switch 可不接表达式 ----------------------- - -switch 后可以不接任何变量、表达式、函数。 - -当不接任何东西时,switch - case 就相当于 if - elseif - else - -.. code:: go - - score := 30 - - switch { - case score >= 95 && score <= 100: - fmt.Println("优秀") - case score >= 80: - fmt.Println("良好") - case score >= 60: - fmt.Println("合格") - case score >= 0: - fmt.Println("不合格") - default: - fmt.Println("输入有误...") - } - -6. switch 的穿透能力 --------------------- - -正常情况下 switch - case 的执行顺序是:只要有一个 case -满足条件,就会直接退出 switch - case ,如果 一个都没有满足,才会执行 -default 的代码块。 - -但是有一种情况是例外。 - -那就是当 case 使用关键字 ``fallthrough`` 开启穿透能力的时候。 - -.. code:: go - - s := "hello" - switch { - case s == "hello": - fmt.Println("hello") - fallthrough - case s != "world": - fmt.Println("world") - } - -代码输出如下: - -:: - - hello - world - -需要注意的是,fallthrough -只能穿透一层,意思是它只给你一次再判断case的机会,不管你有没有匹配上,都要退出了。 - -.. code:: go - - s := "hello" - switch { - case s == "hello": - fmt.Println("hello") - fallthrough - case s == "xxxx": - fmt.Println("xxxx") - case s != "world": - fmt.Println("world") - } - -输出如下,并不会输出 ``world``\ (即使它符合条件) - -:: - - hello - xxxx - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_12.md b/source/c09/c09_12.md deleted file mode 100644 index 43e5a29..0000000 --- a/source/c09/c09_12.md +++ /dev/null @@ -1,169 +0,0 @@ -# 9.11 Go语言流程控制:for - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -上一篇讲了switch - case 选择语句,今天先来讲讲 for 循环语句。 - -## 0. 语句模型 - -这是 for 循环的基本模型。 - -``` -for [condition | ( init; condition; increment ) | Range] -{ - statement(s); -} -``` - -可以看到 for 后面,可以接三种类型的表达式。 - -1. 接一个条件表达式 -2. 接三个表达式 -3. 接一个 range 表达式 - -但其实还有第四种 - -4. 不接表达式 - -## 1. 接一个条件表达式 - -这个例子会打印 1 到 5 的数值。 - -```go -a := 1 -for a <= 5 { - fmt.Println(a) - a ++ -} -``` - -输出如下 - -``` -1 -2 -3 -4 -5 -``` - - - -## 2. 接三个表达式 - -for 后面,紧接着三个表达式,使用 `;` 分隔。 - -这三个表达式,各有各的用途 - -- 第一个表达式:初始化控制变量,在整个循环生命周期内,只运行一次; -- 第二个表达式:设置循环控制条件,当返回true,继续循环,返回false,结束循环; -- 第三个表达式:每次循完开始(除第一次)时,给控制变量增量或减量。 - -这边的例子和上面的例子,是等价的。 - -```go -import "fmt" - -func main() { - for i := 1; i <= 5; i++ { - fmt.Println(i) - } -} -``` - -输出如下 - -``` -1 -2 -3 -4 -5 -``` - - - -## 2. 不接表达式:无限循环 - -在 Go 语言中,没有 while 循环,如果要实现无限循环,也完全可以 for 来实现。 - -当你不加任何的判断条件时, 就相当于你每次的判断都为 true,程序就会一直处于运行状态,但是一般我们并不会让程序处于死循环,在满足一定的条件下,可以使用关键字 `break` 退出循环体,也可以使用 `continue` 直接跳到下一循环。 - -下面两种写法都是无限循环的写法。 - -```go -for { - 代码块 -} - -// 等价于 -for ;; { - 代码块 -} -``` - -举个例子 - -```go -import "fmt" - -func main() { - var i int = 1 - for { - if i > 5 { - break - } - fmt.Printf("hello, %d\n", i) - i++ - } -} -``` - -输出如下 - -``` -hello, 1 -hello, 2 -hello, 3 -hello, 4 -hello, 5 -``` - - - -## 3. 接 for-range 语句 - -遍历一个可迭代对象,是一个很常用的操作。在 Go 可以使用 for-range 的方式来实现。 - -range 后可接数组、切片,字符串等 - -由于 range 会返回两个值:索引和数据,若你后面的代码用不到索引,需要使用 `_` 表示 。 - -```go -import "fmt" - -func main() { - myarr := [...]string{"world", "python", "go"} - for _, item := range myarr { - fmt.Printf("hello, %s\n", item) - } -} -``` - -输出如下 - -``` -hello, world -hello, python -hello, go -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_12.rst b/source/c09/c09_12.rst deleted file mode 100644 index 580d72e..0000000 --- a/source/c09/c09_12.rst +++ /dev/null @@ -1,174 +0,0 @@ -9.11 Go语言流程控制:for -======================== - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -上一篇讲了switch - case 选择语句,今天先来讲讲 for 循环语句。 - -0. 语句模型 ------------ - -这是 for 循环的基本模型。 - -:: - - for [condition | ( init; condition; increment ) | Range] - { - statement(s); - } - -可以看到 for 后面,可以接三种类型的表达式。 - -1. 接一个条件表达式 -2. 接三个表达式 -3. 接一个 range 表达式 - -但其实还有第四种 - -4. 不接表达式 - -1. 接一个条件表达式 -------------------- - -这个例子会打印 1 到 5 的数值。 - -.. code:: go - - a := 1 - for a <= 5 { - fmt.Println(a) - a ++ - } - -输出如下 - -:: - - 1 - 2 - 3 - 4 - 5 - -2. 接三个表达式 ---------------- - -for 后面,紧接着三个表达式,使用 ``;`` 分隔。 - -这三个表达式,各有各的用途 - -- 第一个表达式:初始化控制变量,在整个循环生命周期内,只运行一次; -- 第二个表达式:设置循环控制条件,当返回true,继续循环,返回false,结束循环; -- 第三个表达式:每次循完开始(除第一次)时,给控制变量增量或减量。 - -这边的例子和上面的例子,是等价的。 - -.. code:: go - - import "fmt" - - func main() { - for i := 1; i <= 5; i++ { - fmt.Println(i) - } - } - -输出如下 - -:: - - 1 - 2 - 3 - 4 - 5 - -2. 不接表达式:无限循环 ------------------------ - -在 Go 语言中,没有 while 循环,如果要实现无限循环,也完全可以 for -来实现。 - -当你不加任何的判断条件时, 就相当于你每次的判断都为 -true,程序就会一直处于运行状态,但是一般我们并不会让程序处于死循环,在满足一定的条件下,可以使用关键字 -``break`` 退出循环体,也可以使用 ``continue`` 直接跳到下一循环。 - -下面两种写法都是无限循环的写法。 - -.. code:: go - - for { - 代码块 - } - - // 等价于 - for ;; { - 代码块 - } - -举个例子 - -.. code:: go - - import "fmt" - - func main() { - var i int = 1 - for { - if i > 5 { - break - } - fmt.Printf("hello, %d\n", i) - i++ - } - } - -输出如下 - -:: - - hello, 1 - hello, 2 - hello, 3 - hello, 4 - hello, 5 - -3. 接 for-range 语句 --------------------- - -遍历一个可迭代对象,是一个很常用的操作。在 Go 可以使用 for-range -的方式来实现。 - -range 后可接数组、切片,字符串等 - -由于 range 会返回两个值:索引和数据,若你后面的代码用不到索引,需要使用 -``_`` 表示 。 - -.. code:: go - - import "fmt" - - func main() { - myarr := [...]string{"world", "python", "go"} - for _, item := range myarr { - fmt.Printf("hello, %s\n", item) - } - } - -输出如下 - -:: - - hello, world - hello, python - hello, go - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_13.md b/source/c09/c09_13.md deleted file mode 100644 index eb1b60b..0000000 --- a/source/c09/c09_13.md +++ /dev/null @@ -1,173 +0,0 @@ -# 9.13 Go语言流程控制:goto 无条件跳转 - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -前面三种,我已经都讲过了,今天要讲讲 goto 的无条件跳转。 - -很难想象在 Go 居然会保留 goto,因为很多人不建议使用 goto,所以在一些编程语言中甚至直接取消了 goto。 - -我感觉 Go 既然保留,一定有人家的理由,只是我目前还没感受到。不管怎样,咱还是照常学习吧。 - - - -## 0. 基本模型 - -`goto` 顾言思义,是跳转的意思。 - -goto 后接一个标签,这个标签的意义是告诉 Go程序下一步要执行哪里的代码。 - -所以这个标签如何放置,放置在哪里,是 goto 里最需要注意的。 - -```go -goto 标签; -... -... -标签: 表达式; -``` - -## 1. 最简单的示例 - -`goto` 可以打破原有代码执行顺序,直接跳转到某一行执行代码。 - -```go -import "fmt" - -func main() { - - goto flag - fmt.Println("B") -flag: - fmt.Println("A") - -} -``` - -执行结果,并不会输出 B ,而只会输出 A - -``` -A -``` - - - -## 2. 如何使用? - -`goto` 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。 - -这边举一个例子,用 `goto` 的方式来实现一个打印 1到5 的循环。 - -```go -import "fmt" - -func main() { - i := 1 -flag: - if i <= 5 { - fmt.Println(i) - i++ - goto flag - } -} -``` - -输出如下 - -``` -1 -2 -3 -4 -5 -``` - -再举个例子,使用 goto 实现 类型 break 的效果。 - -```go -import "fmt" - -func main() { - i := 1 - for { - if i > 5 { - goto flag - } - fmt.Println(i) - i++ - } -flag: -} -``` - -输出如下 - -``` -1 -2 -3 -4 -5 -``` - -最后再举个例子,使用 goto 实现 类型 continue的效果,打印 1到10 的所有偶数。 - -```go -import "fmt" - -func main() { - i := 1 -flag: - for i <= 10 { - if i%2 == 1 { - i++ - goto flag - } - fmt.Println(i) - i++ - } -} -``` - -输出如下 - -``` -2 -4 -6 -8 -10 -``` - - - -## 3. 注意事项 - -goto语句与标签之间不能有变量声明,否则编译错误。 - -```go -import "fmt" - -func main() { - fmt.Println("start") - goto flag - var say = "hello oldboy" - fmt.Println(say) -flag: - fmt.Println("end") -} -``` - -编译错误 - -``` -.\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6 -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_13.rst b/source/c09/c09_13.rst deleted file mode 100644 index ba79b5f..0000000 --- a/source/c09/c09_13.rst +++ /dev/null @@ -1,176 +0,0 @@ -9.13 Go语言流程控制:goto 无条件跳转 -==================================== - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -前面三种,我已经都讲过了,今天要讲讲 goto 的无条件跳转。 - -很难想象在 Go 居然会保留 goto,因为很多人不建议使用 -goto,所以在一些编程语言中甚至直接取消了 goto。 - -我感觉 Go -既然保留,一定有人家的理由,只是我目前还没感受到。不管怎样,咱还是照常学习吧。 - -0. 基本模型 ------------ - -``goto`` 顾言思义,是跳转的意思。 - -goto 后接一个标签,这个标签的意义是告诉 Go程序下一步要执行哪里的代码。 - -所以这个标签如何放置,放置在哪里,是 goto 里最需要注意的。 - -.. code:: go - - goto 标签; - ... - ... - 标签: 表达式; - -1. 最简单的示例 ---------------- - -``goto`` 可以打破原有代码执行顺序,直接跳转到某一行执行代码。 - -.. code:: go - - import "fmt" - - func main() { - - goto flag - fmt.Println("B") - flag: - fmt.Println("A") - - } - -执行结果,并不会输出 B ,而只会输出 A - -:: - - A - -2. 如何使用? -------------- - -``goto`` 语句通常与条件语句配合使用。可用来实现条件转移, -构成循环,跳出循环体等功能。 - -这边举一个例子,用 ``goto`` 的方式来实现一个打印 1到5 的循环。 - -.. code:: go - - import "fmt" - - func main() { - i := 1 - flag: - if i <= 5 { - fmt.Println(i) - i++ - goto flag - } - } - -输出如下 - -:: - - 1 - 2 - 3 - 4 - 5 - -再举个例子,使用 goto 实现 类型 break 的效果。 - -.. code:: go - - import "fmt" - - func main() { - i := 1 - for { - if i > 5 { - goto flag - } - fmt.Println(i) - i++ - } - flag: - } - -输出如下 - -:: - - 1 - 2 - 3 - 4 - 5 - -最后再举个例子,使用 goto 实现 类型 continue的效果,打印 1到10 -的所有偶数。 - -.. code:: go - - import "fmt" - - func main() { - i := 1 - flag: - for i <= 10 { - if i%2 == 1 { - i++ - goto flag - } - fmt.Println(i) - i++ - } - } - -输出如下 - -:: - - 2 - 4 - 6 - 8 - 10 - -3. 注意事项 ------------ - -goto语句与标签之间不能有变量声明,否则编译错误。 - -.. code:: go - - import "fmt" - - func main() { - fmt.Println("start") - goto flag - var say = "hello oldboy" - fmt.Println(say) - flag: - fmt.Println("end") - } - -编译错误 - -:: - - .\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_14.md b/source/c09/c09_14.md deleted file mode 100644 index f97e985..0000000 --- a/source/c09/c09_14.md +++ /dev/null @@ -1,228 +0,0 @@ -# 9.14 Go语言流程控制:defer 延迟语句 - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -今天是最后一篇讲控制流程了,内容是 defer 延迟语句,这个在其他编程语言里好像没有见到。应该是属于 Go 语言里的独有的关键字,但即使如此,阅读后这篇文章后,你可以发现 defer 在其他编程语言里的影子。 - - - -## 1. 延迟调用 - -defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个 `xxx` 函数的调用延迟到当前函数执行完后再执行。 - -```go -defer xxx() -``` - -这是一个很简单的例子,可以很快帮助你理解 defer 的使用效果。 - -```go -import "fmt" - -func myfunc() { - fmt.Println("B") -} - -func main() { - defer myfunc() - fmt.Println("A") -} -``` - -输出如下 - -``` -A -B -``` - -当然了,对于上面这个例子可以简写为成如下,输出结果是一致的 - -```go -import "fmt" - -func main() { - defer fmt.Println("B") - fmt.Println("A") -} -``` - - - -## 2. 即时求值的变量快照 - -使用 defer 只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响。 - -比如这边的例子 - -```go -import "fmt" - -func main() { - name := "go" - defer fmt.Println(name) // 输出: go - - name = "python" - fmt.Println(name) // 输出: python -} -``` - -输出如下,可见给 name 重新赋值为 `python`,后续调用 defer 的时候,仍然使用未重新赋值的变量值,就好在 defer 这里,给所有的这是做了一个快照一样。 - -``` -python -go -``` - - - -## 3. 多个defer 反序调用 - -当我们在一个函数里使用了 多个defer,那么这些defer 的执行函数是如何的呢? - -做个试验就知道了 - -```go -import "fmt" - -func main() { - name := "go" - defer fmt.Println(name) // 输出: go - - name = "python" - defer fmt.Println(name) // 输出: python - - name = "java" - fmt.Println(name) -} -``` - -输出如下,可见 多个defer 是反序调用的,有点类似栈一样,后进先出。 - -``` -java -python -go -``` - - - -## 3. defer 与 return 孰先孰后 - -至此,defer 还算是挺好理解的。在一般的使用上,是没有问题了。 - -在这里提一个稍微复杂一点的问题,defer 和 return 到底是哪个先调用? - -使用下面这段代码,可以很容易的观察出来 - -```go -import "fmt" - -var name string = "go" - -func myfunc() string { - defer func() { - name = "python" - }() - - fmt.Printf("myfunc 函数里的name:%s\n", name) - return name -} - -func main() { - myname := myfunc() - fmt.Printf("main 函数里的name: %s\n", name) - fmt.Println("main 函数里的myname: ", myname) -} -``` - -输出如下 - -``` -myfunc 函数里的name:go -main 函数里的name: python -main 函数里的myname: go -``` - -来一起理解一下这段代码,第一行很直观,name 此时还是全局变量,值还是go - -第二行也不难理解,在 defer 里改变了这个全局变量,此时name的值已经变成了 python - -重点在第三行,为什么输出的是 go ? - -解释只有一个,那就是 defer 是return 后才调用的。所以在执行 defer 前,myname 已经被赋值成 go 了。 - - - -## 4. 为什么要有 defer? - -看完上面的例子后,不知道你是否和我一样,对这个defer的使用效果感到熟悉?貌似在 Python 也见过类似的用法。 - -虽然 Python 中没有 defer ,但是它有 with 上下文管理器。我们知道在 Python 中可以使用 defer 实现对资源的管理。最常用的例子就是文件的打开关闭。 - -你可能会有疑问,这也没什么意义呀,我把这个放在 defer 执行的函数放在 return 那里执行不就好了。 - -固然可以,但是当一个函数里有多个 return 时,你得多调用好多次这个函数,代码就臃肿起来了。 - -若是没有 defer,你可以写出这样的代码 - -```go -func f() { - r := getResource() //0,获取资源 - ...... - if ... { - r.release() //1,释放资源 - return - } - ...... - if ... { - r.release() //2,释放资源 - return - } - ...... - if ... { - r.release() //3,释放资源 - return - } - ...... - r.release() //4,释放资源 - return -} -``` - -使用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer 后的函数。 - -```go -func f() { - r := getResource() //0,获取资源 - - defer r.release() //1,释放资源 - ...... - if ... { - ... - return - } - ...... - if ... { - ... - return - } - ...... - if ... { - ... - return - } - ...... - return -} -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_14.rst b/source/c09/c09_14.rst deleted file mode 100644 index 655fefc..0000000 --- a/source/c09/c09_14.rst +++ /dev/null @@ -1,238 +0,0 @@ -9.14 Go语言流程控制:defer 延迟语句 -=================================== - -Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: - -- if - else 条件语句 -- switch - case 选择语句 -- for - range 循环语句 -- goto 无条件跳转语句 -- defer 延迟执行 - -今天是最后一篇讲控制流程了,内容是 defer -延迟语句,这个在其他编程语言里好像没有见到。应该是属于 Go -语言里的独有的关键字,但即使如此,阅读后这篇文章后,你可以发现 defer -在其他编程语言里的影子。 - -1. 延迟调用 ------------ - -defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个 ``xxx`` -函数的调用延迟到当前函数执行完后再执行。 - -.. code:: go - - defer xxx() - -这是一个很简单的例子,可以很快帮助你理解 defer 的使用效果。 - -.. code:: go - - import "fmt" - - func myfunc() { - fmt.Println("B") - } - - func main() { - defer myfunc() - fmt.Println("A") - } - -输出如下 - -:: - - A - B - -当然了,对于上面这个例子可以简写为成如下,输出结果是一致的 - -.. code:: go - - import "fmt" - - func main() { - defer fmt.Println("B") - fmt.Println("A") - } - -2. 即时求值的变量快照 ---------------------- - -使用 defer -只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响。 - -比如这边的例子 - -.. code:: go - - import "fmt" - - func main() { - name := "go" - defer fmt.Println(name) // 输出: go - - name = "python" - fmt.Println(name) // 输出: python - } - -输出如下,可见给 name 重新赋值为 ``python``\ ,后续调用 defer -的时候,仍然使用未重新赋值的变量值,就好在 defer -这里,给所有的这是做了一个快照一样。 - -:: - - python - go - -3. 多个defer 反序调用 ---------------------- - -当我们在一个函数里使用了 多个defer,那么这些defer 的执行函数是如何的呢? - -做个试验就知道了 - -.. code:: go - - import "fmt" - - func main() { - name := "go" - defer fmt.Println(name) // 输出: go - - name = "python" - defer fmt.Println(name) // 输出: python - - name = "java" - fmt.Println(name) - } - -输出如下,可见 多个defer 是反序调用的,有点类似栈一样,后进先出。 - -:: - - java - python - go - -3. defer 与 return 孰先孰后 ---------------------------- - -至此,defer 还算是挺好理解的。在一般的使用上,是没有问题了。 - -在这里提一个稍微复杂一点的问题,defer 和 return 到底是哪个先调用? - -使用下面这段代码,可以很容易的观察出来 - -.. code:: go - - import "fmt" - - var name string = "go" - - func myfunc() string { - defer func() { - name = "python" - }() - - fmt.Printf("myfunc 函数里的name:%s\n", name) - return name - } - - func main() { - myname := myfunc() - fmt.Printf("main 函数里的name: %s\n", name) - fmt.Println("main 函数里的myname: ", myname) - } - -输出如下 - -:: - - myfunc 函数里的name:go - main 函数里的name: python - main 函数里的myname: go - -来一起理解一下这段代码,第一行很直观,name 此时还是全局变量,值还是go - -第二行也不难理解,在 defer 里改变了这个全局变量,此时name的值已经变成了 -python - -重点在第三行,为什么输出的是 go ? - -解释只有一个,那就是 defer 是return 后才调用的。所以在执行 defer -前,myname 已经被赋值成 go 了。 - -4. 为什么要有 defer? ---------------------- - -看完上面的例子后,不知道你是否和我一样,对这个defer的使用效果感到熟悉?貌似在 -Python 也见过类似的用法。 - -虽然 Python 中没有 defer ,但是它有 with 上下文管理器。我们知道在 Python -中可以使用 defer 实现对资源的管理。最常用的例子就是文件的打开关闭。 - -你可能会有疑问,这也没什么意义呀,我把这个放在 defer 执行的函数放在 -return 那里执行不就好了。 - -固然可以,但是当一个函数里有多个 return -时,你得多调用好多次这个函数,代码就臃肿起来了。 - -若是没有 defer,你可以写出这样的代码 - -.. code:: go - - func f() { - r := getResource() //0,获取资源 - ...... - if ... { - r.release() //1,释放资源 - return - } - ...... - if ... { - r.release() //2,释放资源 - return - } - ...... - if ... { - r.release() //3,释放资源 - return - } - ...... - r.release() //4,释放资源 - return - } - -使用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer -后的函数。 - -.. code:: go - - func f() { - r := getResource() //0,获取资源 - - defer r.release() //1,释放资源 - ...... - if ... { - ... - return - } - ...... - if ... { - ... - return - } - ...... - if ... { - ... - return - } - ...... - return - } - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_15.md b/source/c09/c09_15.md deleted file mode 100644 index 108449f..0000000 --- a/source/c09/c09_15.md +++ /dev/null @@ -1,235 +0,0 @@ -# 9.15 面向对象编程:接口与多态 - -## 0. 接口是什么? - -> 这一段摘自 Go语言中文网 - -在面向对象的领域里,接口一般这样定义:**接口定义一个对象的行为**。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定。 - -在 Go 语言中,接口就是方法签名(Method Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。这与面向对象编程(OOP)的说法很类似。**接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法**。 - -## 1. 如何定义接口 - -使用 type 关键字来定义接口。 - -如下代码,定义了一个电话接口,接口要求必须实现 call 方法。 - -```go -type Phone interface { - call() -} -``` - -## 2. 如何实现接口 - -如果有一个类型/结构体,实现了一个接口要求的所有方法,这里 Phone 接口只有 call方法,所以只要实现了 call 方法,我们就可以称它实现了 Phone 接口。 - -意思是如果有一台机器,可以给别人打电话,那么我们就可以把它叫做电话。 - -这个接口的实现是隐式的,不像 JAVA 中要用 implements 显示说明。 - -继续上面电话的例子,我们先定义一个 Nokia 的结构体,而它实现了 call 的方法,所以它也是一台电话。 - -```go -type Nokia struct { - name string -} - -// 接收者为 Nokia -func (phone Nokia) call() { - fmt.Println("我是 Nokia,是一台电话") -} -``` - - - -## 3. 接口实现多态 - -鸭子类型(Duck typing)的定义是,只要你长得像鸭子,叫起来也像鸭子,那我认为你就是一只鸭子。 - -举个通俗的例子 - -**什么样子的人可以称做老师呢?** - -不同的人标准不一,有的人认为必须有一定的学历,有的人认为必须要有老师资格证。 - -而我认为只要能育人,能给传授给其他人知识的,都可以称之为老师。 - -而不管你教的什么学科?是体育竞技,还是教人烹饪。 - -也不管你怎么教?是在教室里手执教教鞭、拿着粉笔,还是追求真实,直接实战演练。 - -通通不管。 - -这就一个接口(老师)下,在不同对象(人)上的不同表现。这就是多态。 - - - -**在 Go 语言中,是通过接口来实现的多态。** - -这里以商品接口来写一段代码演示一下。 - -先定义一个商品(Good)的接口,意思是一个类型或者结构体,只要实现了`settleAccount()` 和 `orderInfo()` 两个方法,那这个类型/结构体就是一个商品。 - -```go -type Good interface { - settleAccount() int - orderInfo() string -} -``` - -然后我们定义两个结构体,分别是手机和赠品。 - -```go -type Phone struct { - name string - quantity int - price int -} - -type FreeGift struct { - name string - quantity int - price int -} -``` - -然后分别为他们实现 Good 接口的两个方法 - -```go -// Phone -func (phone Phone) settleAccount() int { - return phone.quantity * phone.price -} -func (phone Phone) orderInfo() string{ - return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + - phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" -} - -// FreeGift -func (gift FreeGift) settleAccount() int { - return 0 -} -func (gift FreeGift) orderInfo() string{ - return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + - gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" -} -``` - -实现了 Good 接口要求的两个方法后,手机和赠品在Go语言看来就都是商品(Good)类型了。 - -这里候,我挑选了两件商品(实例化),分别是手机和耳机(赠品,不要钱) - -```go -iPhone := Phone{ - name: "iPhone", - quantity: 1, - price: 8000, -} -earphones := FreeGift{ - name: "耳机", - quantity: 1, - price: 200, -} -``` - -然后创建一个购物车(也就是类型为 Good的切片),来存放这些商品。 - -```go -goods := []Good{iPhone, earphones} -``` - -最后,定义一个方法来计算购物车里的订单金额 - -```go -func calculateAllPrice(goods []Good) int { - var allPrice int - for _,good := range goods{ - fmt.Println(good.orderInfo()) - allPrice += good.settleAccount() - } - return allPrice -} -``` - -完整代码,我贴在下面,供你参考。 - -```go -package main - -import ( - "fmt" - "strconv" -) - -// 定义一个接口 -type Good interface { - settleAccount() int - orderInfo() string -} - -type Phone struct { - name string - quantity int - price int -} - -func (phone Phone) settleAccount() int { - return phone.quantity * phone.price -} -func (phone Phone) orderInfo() string{ - return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + - phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" -} - -type FreeGift struct { - name string - quantity int - price int -} - -func (gift FreeGift) settleAccount() int { - return 0 -} -func (gift FreeGift) orderInfo() string{ - return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + - gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" -} - -func calculateAllPrice(goods []Good) int { - var allPrice int - for _,good := range goods{ - fmt.Println(good.orderInfo()) - allPrice += good.settleAccount() - } - return allPrice -} -func main() { - iPhone := Phone{ - name: "iPhone", - quantity: 1, - price: 8000, - } - earphones := FreeGift{ - name: "耳机", - quantity: 1, - price: 200, - } - - goods := []Good{iPhone, earphones} - allPrice := calculateAllPrice(goods) - fmt.Printf("该订单总共需要支付 %d 元", allPrice) -} -``` - -运行后,输出如下 - -``` -您要购买1个iPhone计:8000元 -您要购买1个耳机计:0元 -该订单总共需要支付 8000 元 -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_15.rst b/source/c09/c09_15.rst deleted file mode 100644 index 96448de..0000000 --- a/source/c09/c09_15.rst +++ /dev/null @@ -1,242 +0,0 @@ -9.15 面向对象编程:接口与多态 -============================= - -0. 接口是什么? ---------------- - - 这一段摘自 Go语言中文网 - -在面向对象的领域里,接口一般这样定义:\ **接口定义一个对象的行为**\ 。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定。 - -在 Go 语言中,接口就是方法签名(Method -Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。这与面向对象编程(OOP)的说法很类似。\ **接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法**\ 。 - -1. 如何定义接口 ---------------- - -使用 type 关键字来定义接口。 - -如下代码,定义了一个电话接口,接口要求必须实现 call 方法。 - -.. code:: go - - type Phone interface { - call() - } - -2. 如何实现接口 ---------------- - -如果有一个类型/结构体,实现了一个接口要求的所有方法,这里 Phone 接口只有 -call方法,所以只要实现了 call 方法,我们就可以称它实现了 Phone 接口。 - -意思是如果有一台机器,可以给别人打电话,那么我们就可以把它叫做电话。 - -这个接口的实现是隐式的,不像 JAVA 中要用 implements 显示说明。 - -继续上面电话的例子,我们先定义一个 Nokia 的结构体,而它实现了 call -的方法,所以它也是一台电话。 - -.. code:: go - - type Nokia struct { - name string - } - - // 接收者为 Nokia - func (phone Nokia) call() { - fmt.Println("我是 Nokia,是一台电话") - } - -3. 接口实现多态 ---------------- - -鸭子类型(Duck -typing)的定义是,只要你长得像鸭子,叫起来也像鸭子,那我认为你就是一只鸭子。 - -举个通俗的例子 - -**什么样子的人可以称做老师呢?** - -不同的人标准不一,有的人认为必须有一定的学历,有的人认为必须要有老师资格证。 - -而我认为只要能育人,能给传授给其他人知识的,都可以称之为老师。 - -而不管你教的什么学科?是体育竞技,还是教人烹饪。 - -也不管你怎么教?是在教室里手执教教鞭、拿着粉笔,还是追求真实,直接实战演练。 - -通通不管。 - -这就一个接口(老师)下,在不同对象(人)上的不同表现。这就是多态。 - -**在 Go 语言中,是通过接口来实现的多态。** - -这里以商品接口来写一段代码演示一下。 - -先定义一个商品(Good)的接口,意思是一个类型或者结构体,只要实现了\ ``settleAccount()`` -和 ``orderInfo()`` 两个方法,那这个类型/结构体就是一个商品。 - -.. code:: go - - type Good interface { - settleAccount() int - orderInfo() string - } - -然后我们定义两个结构体,分别是手机和赠品。 - -.. code:: go - - type Phone struct { - name string - quantity int - price int - } - - type FreeGift struct { - name string - quantity int - price int - } - -然后分别为他们实现 Good 接口的两个方法 - -.. code:: go - - // Phone - func (phone Phone) settleAccount() int { - return phone.quantity * phone.price - } - func (phone Phone) orderInfo() string{ - return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + - phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" - } - - // FreeGift - func (gift FreeGift) settleAccount() int { - return 0 - } - func (gift FreeGift) orderInfo() string{ - return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + - gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" - } - -实现了 Good -接口要求的两个方法后,手机和赠品在Go语言看来就都是商品(Good)类型了。 - -这里候,我挑选了两件商品(实例化),分别是手机和耳机(赠品,不要钱) - -.. code:: go - - iPhone := Phone{ - name: "iPhone", - quantity: 1, - price: 8000, - } - earphones := FreeGift{ - name: "耳机", - quantity: 1, - price: 200, - } - -然后创建一个购物车(也就是类型为 Good的切片),来存放这些商品。 - -.. code:: go - - goods := []Good{iPhone, earphones} - -最后,定义一个方法来计算购物车里的订单金额 - -.. code:: go - - func calculateAllPrice(goods []Good) int { - var allPrice int - for _,good := range goods{ - fmt.Println(good.orderInfo()) - allPrice += good.settleAccount() - } - return allPrice - } - -完整代码,我贴在下面,供你参考。 - -.. code:: go - - package main - - import ( - "fmt" - "strconv" - ) - - // 定义一个接口 - type Good interface { - settleAccount() int - orderInfo() string - } - - type Phone struct { - name string - quantity int - price int - } - - func (phone Phone) settleAccount() int { - return phone.quantity * phone.price - } - func (phone Phone) orderInfo() string{ - return "您要购买" + strconv.Itoa(phone.quantity)+ "个" + - phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元" - } - - type FreeGift struct { - name string - quantity int - price int - } - - func (gift FreeGift) settleAccount() int { - return 0 - } - func (gift FreeGift) orderInfo() string{ - return "您要购买" + strconv.Itoa(gift.quantity)+ "个" + - gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元" - } - - func calculateAllPrice(goods []Good) int { - var allPrice int - for _,good := range goods{ - fmt.Println(good.orderInfo()) - allPrice += good.settleAccount() - } - return allPrice - } - func main() { - iPhone := Phone{ - name: "iPhone", - quantity: 1, - price: 8000, - } - earphones := FreeGift{ - name: "耳机", - quantity: 1, - price: 200, - } - - goods := []Good{iPhone, earphones} - allPrice := calculateAllPrice(goods) - fmt.Printf("该订单总共需要支付 %d 元", allPrice) - } - -运行后,输出如下 - -:: - - 您要购买1个iPhone计:8000元 - 您要购买1个耳机计:0元 - 该订单总共需要支付 8000 元 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_16.md b/source/c09/c09_16.md deleted file mode 100644 index 6c2c2c8..0000000 --- a/source/c09/c09_16.md +++ /dev/null @@ -1,99 +0,0 @@ -# 9.16 关键字:make 和 new 的区别? - -## 1. new 函数 - -在官方文档中,new 函数的描述如下 - -```go -// The new built-in function allocates memory. The first argument is a type, -// not a value, and the value returned is a pointer to a newly -// allocated zero value of that type. -func new(Type) *Type -``` - -可以看到,new 只能传递一个参数,该参数为一个任意类型,可以是Go语言内建的类型,也可以是你自定义的类型 - -那么 new 函数到底做了哪些事呢: - -- 分配内存 -- 设置零值 -- 返回指针(重要) - - - -举个例子 - -```go -import "fmt" - -type Student struct { - name string - age int -} - -func main() { - // new 一个内建类型 - num := new(int) - fmt.Println(*num) //打印零值:0 - - // new 一个自定义类型 - s := new(Student) - s.name = "wangbm" -} -``` - - - -## 2. make 函数 - -在官方文档中,make 函数的描述如下 - ->//The make built-in function allocates and initializes an object ->//of type slice, map, or chan (only). Like new, the first argument is ->// a type, not a value. Unlike new, make's return type is the same as ->// the type of its argument, not a pointer to it. -> ->func make(t Type, size ...IntegerType) Type - -翻译一下注释内容 - -1. 内建函数 make 用来为 slice,map 或 chan 类型(注意:也只能用在这三种类型上)分配内存和初始化一个对象 -2. make 返回类型的本身而不是指针,而返回值也依赖于具体传入的类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了 - -注意,因为这三种类型是引用类型,所以必须得初始化(size和cap),但是不是置为零值,这个和new是不一样的。 - -举几个例子 - -```go -//切片 -a := make([]int, 2, 10) - -// 字典 -b := make(map[string]int) - -// 通道 -c := make(chan int, 10) -``` - - - -## 3. 总结 - -new:为所有的类型分配内存,并初始化为零值,返回指针。 - -make:只能为 slice,map,chan 分配内存,并初始化,返回的是类型。 - -另外,目前来看 new 函数并不常用,大家更喜欢使用短语句声明的方式。 - -```go -a := new(int) -a = 1 -// 等价于 -a := 1 -``` - -但是 make 就不一样了,它的地位无可替代,在使用slice、map以及channel的时候,还是要使用make进行初始化,然后才可以对他们进行操作。 - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_16.rst b/source/c09/c09_16.rst deleted file mode 100644 index 3a9ced0..0000000 --- a/source/c09/c09_16.rst +++ /dev/null @@ -1,101 +0,0 @@ -9.16 关键字:make 和 new 的区别? -================================= - -1. new 函数 ------------ - -在官方文档中,new 函数的描述如下 - -.. code:: go - - // The new built-in function allocates memory. The first argument is a type, - // not a value, and the value returned is a pointer to a newly - // allocated zero value of that type. - func new(Type) *Type - -可以看到,new -只能传递一个参数,该参数为一个任意类型,可以是Go语言内建的类型,也可以是你自定义的类型 - -那么 new 函数到底做了哪些事呢: - -- 分配内存 -- 设置零值 -- 返回指针(重要) - -举个例子 - -.. code:: go - - import "fmt" - - type Student struct { - name string - age int - } - - func main() { - // new 一个内建类型 - num := new(int) - fmt.Println(*num) //打印零值:0 - - // new 一个自定义类型 - s := new(Student) - s.name = "wangbm" - } - -2. make 函数 ------------- - -在官方文档中,make 函数的描述如下 - - //The make built-in function allocates and initializes an object //of - type slice, map, or chan (only). Like new, the first argument is // a - type, not a value. Unlike new, make’s return type is the same as // - the type of its argument, not a pointer to it. - - func make(t Type, size …IntegerType) Type - -翻译一下注释内容 - -1. 内建函数 make 用来为 slice,map 或 chan - 类型(注意:也只能用在这三种类型上)分配内存和初始化一个对象 -2. make - 返回类型的本身而不是指针,而返回值也依赖于具体传入的类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了 - -注意,因为这三种类型是引用类型,所以必须得初始化(size和cap),但是不是置为零值,这个和new是不一样的。 - -举几个例子 - -.. code:: go - - //切片 - a := make([]int, 2, 10) - - // 字典 - b := make(map[string]int) - - // 通道 - c := make(chan int, 10) - -3. 总结 -------- - -new:为所有的类型分配内存,并初始化为零值,返回指针。 - -make:只能为 slice,map,chan 分配内存,并初始化,返回的是类型。 - -另外,目前来看 new 函数并不常用,大家更喜欢使用短语句声明的方式。 - -.. code:: go - - a := new(int) - a = 1 - // 等价于 - a := 1 - -但是 make -就不一样了,它的地位无可替代,在使用slice、map以及channel的时候,还是要使用make进行初始化,然后才可以对他们进行操作。 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_17.md b/source/c09/c09_17.md deleted file mode 100644 index f4658eb..0000000 --- a/source/c09/c09_17.md +++ /dev/null @@ -1,148 +0,0 @@ -# 9.17 理解语句块与作用域 - -由于 Go 使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 Go 中的语句块是怎么一回事? - -## 1. 显示语句块与隐式语句块 - -通俗地说,语句块是由花括弧(`{}`)所包含的一系列语句。 - -语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域。 - -用花括弧包含的语句块,属于显示语句块。 - -在 Go 中还有很多的隐式语句块: - -- 主语句块:包括所有源码,对应内置作用域 -- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 -- 文件语句块:包括该文件中的所有源码,对应文件级作用域 -- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 - -前面三点好理解,第四点举几个例子 - -for 循环完后,不能再使用变量 i - -```go -for i := 0; i < 5; i++ { - fmt.Println(i) -} -``` - -if 语句判断完后,同样不能再使用变量 i - -```go -if i := 0; i >= 0 { - fmt.Println(i) -} -``` - -switch 语句完了后,也是不是再使用变量 i - -```go -switch i := 2; i * 4 { -case 8: - fmt.Println(i) -default: - fmt.Println(“default”) -} -``` - -且每个 switch 语句的子句都是一个隐式的语句块 - -```go -switch i := 2; i * 4 { -case 8: - j := 0 - fmt.Println(i, j) -default: - // "j" is undefined here - fmt.Println(“default”) -} -// "j" is undefined here -``` - -## 2. 四种作用域的理解 - -变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 - -根据声明位置的不同,作用域可以分为以下四个类型: - -- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 -- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 -- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 -- 局部作用域:在自己的语句块内声明,包括函数,for、if 等语句块,或自定义的 {} 语句块形成的作用域,只在自己的局部作用域内可用 - -以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这里自己将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 - -对于作用域,有以下几点总结: - -- 低层作用域,可以访问高层作用域 -- 同一层级的作用域,是相互隔离的 -- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 - - - -在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 - -而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 - - - -## 3. 静态作用域与动态作用域 - -根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: - -- 静态作用域,如 Go 语言 -- 动态作用域,如 Shell 语言 - -具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 - -```python -#!/bin/bash -func01() { - local value=1 - func02 -} -func02() { - echo "func02 sees value as ${value}" -} - -# 执行函数 -func01 -func02 -``` - -从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 - -但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 是访问不了 value 变量的。 - -所以此时的输出结果是 - -```shell -func02 sees value as 1 -func02 sees value as -``` - -但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 name 这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 - -```go -import "fmt" - -func func01() { - fmt.Println("在 func01 函数中,name:", name) -} - -func main() { - var name string = "Python编程时光" - fmt.Println("在 main 函数中,name:", name) - - func01() -} -``` - -因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 - - - -参考文章:https://studygolang.com/articles/12632 - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_17.rst b/source/c09/c09_17.rst deleted file mode 100644 index 0ac3487..0000000 --- a/source/c09/c09_17.rst +++ /dev/null @@ -1,158 +0,0 @@ -9.17 理解语句块与作用域 -======================= - -由于 Go -使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 -Go 中的语句块是怎么一回事? - -1. 显示语句块与隐式语句块 -------------------------- - -通俗地说,语句块是由花括弧(\ ``{}``\ )所包含的一系列语句。 - -语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域。 - -用花括弧包含的语句块,属于显示语句块。 - -在 Go 中还有很多的隐式语句块: - -- 主语句块:包括所有源码,对应内置作用域 -- 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 -- 文件语句块:包括该文件中的所有源码,对应文件级作用域 -- for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 - -前面三点好理解,第四点举几个例子 - -for 循环完后,不能再使用变量 i - -.. code:: go - - for i := 0; i < 5; i++ { - fmt.Println(i) - } - -if 语句判断完后,同样不能再使用变量 i - -.. code:: go - - if i := 0; i >= 0 { - fmt.Println(i) - } - -switch 语句完了后,也是不是再使用变量 i - -.. code:: go - - switch i := 2; i * 4 { - case 8: - fmt.Println(i) - default: - fmt.Println(“default”) - } - -且每个 switch 语句的子句都是一个隐式的语句块 - -.. code:: go - - switch i := 2; i * 4 { - case 8: - j := 0 - fmt.Println(i, j) - default: - // "j" is undefined here - fmt.Println(“default”) - } - // "j" is undefined here - -2. 四种作用域的理解 -------------------- - -变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 - -根据声明位置的不同,作用域可以分为以下四个类型: - -- 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 -- 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 -- 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 -- 局部作用域:在自己的语句块内声明,包括函数,for、if - 等语句块,或自定义的 {} - 语句块形成的作用域,只在自己的局部作用域内可用 - -以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这里自己将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 - -对于作用域,有以下几点总结: - -- 低层作用域,可以访问高层作用域 -- 同一层级的作用域,是相互隔离的 -- 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 - -在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 - -而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 - -3. 静态作用域与动态作用域 -------------------------- - -根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: - -- 静态作用域,如 Go 语言 -- 动态作用域,如 Shell 语言 - -具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 - -.. code:: python - - #!/bin/bash - func01() { - local value=1 - func02 - } - func02() { - echo "func02 sees value as ${value}" - } - - # 执行函数 - func01 - func02 - -从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 -value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 -func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 -func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 - -但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 -是访问不了 value 变量的。 - -所以此时的输出结果是 - -.. code:: shell - - func02 sees value as 1 - func02 sees value as - -但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 -name -这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 - -.. code:: go - - import "fmt" - - func func01() { - fmt.Println("在 func01 函数中,name:", name) - } - - func main() { - var name string = "Python编程时光" - fmt.Println("在 main 函数中,name:", name) - - func01() - } - -因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 - -参考文章:https://studygolang.com/articles/12632 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_18.md b/source/c09/c09_18.md deleted file mode 100644 index 77f5b73..0000000 --- a/source/c09/c09_18.md +++ /dev/null @@ -1,131 +0,0 @@ -# 9.18 理解 Go 协程:goroutine - -说到Go语言,很多没接触过它的人,对它的第一印象,一定是它从语言层面天生支持并发,非常方便,让开发者能快速写出高性能且易于理解的程序。 - -在 Python (为Py为例,主要是我比较熟悉,其他主流编程语言也类似)中,并发编程的门槛并不低,你要学习多进程,多线程,还要掌握各种支持并发的库 asyncio,aiohttp 等,同时你还要清楚它们之间的区别及优缺点,懂得在不同的场景选择不同的并发模式。 - -而 Golang 作为一门现代化的编程语言,它不需要你直面这些复杂的问题。在 Golang 里,你不需要学习如何创建进程池/线程池,也不需要知道什么情况下使用多线程,什么时候使用多进程。因为你没得选,也不需要选,它原生提供的 goroutine (也即协程)已经足够优秀,能够自动帮你处理好所有的事情,而你要做的只是执行它,就这么简单。 - -一个 goroutine 本身就是一个函数,当你直接调用时,它就是一个普通函数,如果你在调用前加一个关键字 `go` ,那你就开启了一个 goroutine。 - -```go -// 执行一个函数 -func() - -// 开启一个协程执行这个函数 -go func() -``` - - - -## 1. 协程的初步使用 - -一个 Go 程序的入口通常是 main 函数,程序启动后,main 函数最先运行,我们称之为 `main goroutine`。 - -在 main 中或者其下调用的代码中才可以使用 `go + func()` 的方法来启动协程。 - -main 的地位相当于主线程,当 main 函数执行完成后,这个线程也就终结了,其下的运行着的所有协程也不管代码是不是还在跑,也得乖乖退出。 - -因此如下这段代码运行完,只会输出 `hello, world` ,而不会输出`hello, go`(因为协程的创建需要时间,当 `hello, world`打印后,协程还没来得及并执行) - -```go -import "fmt" - -func mytest() { - fmt.Println("hello, go") -} - -func main() { - // 启动一个协程 - go mytest() - fmt.Println("hello, world") -} -``` - -对于刚学习Go的协程同学来说,可以使用 time.Sleep 来使 main 阻塞,使其他协程能够有机会运行完全,但你要注意的是,这并不是推荐的方式(后续我们会学习其他更优雅的方式)。 - -当我在代码中加入一行 time.Sleep 输出就符合预期了。 - -```go -import ( - "fmt" - "time" -) - -func mytest() { - fmt.Println("hello, go") -} - -func main() { - go mytest() - fmt.Println("hello, world") - time.Sleep(time.Second) -} -``` - -输出如下 - -``` -hello, world -hello, go -``` - - - -## 2. 多个协程的效果 - -为了让你看到并发的效果,这里举个最简单的例子 - -```go -import ( - "fmt" - "time" -) - -func mygo(name string) { - for i := 0; i < 10; i++ { - fmt.Printf("In goroutine %s\n", name) - // 为了避免第一个协程执行过快,观察不到并发的效果,加个休眠 - time.Sleep(10 * time.Millisecond) - } -} - -func main() { - go mygo("协程1号") // 第一个协程 - go mygo("协程2号") // 第二个协程 - time.Sleep(time.Second) -} -``` - -输出如下,可以观察到两个协程就如两个线程一样,并发执行 - -``` -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程1号 -In goroutine 协程2号 -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程1号 -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程2号 -In goroutine 协程1号 -In goroutine 协程2号 -``` - - - -通过以上简单的例子,是不是折服于Go的这种强大的并发特性,将同步代码转为异步代码,真的只要一个关键字就可以了,也不需要使用其他库,简单方便。 - -本篇只介绍了协程的简单使用,真正的并发程序还是要结合 信道 (channel)来实现。关于信道的内容,将在下一篇文章中介绍。 - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_18.rst b/source/c09/c09_18.rst deleted file mode 100644 index ed511b7..0000000 --- a/source/c09/c09_18.rst +++ /dev/null @@ -1,146 +0,0 @@ -9.18 理解 Go 协程:goroutine -============================ - -说到Go语言,很多没接触过它的人,对它的第一印象,一定是它从语言层面天生支持并发,非常方便,让开发者能快速写出高性能且易于理解的程序。 - -在 Python -(为Py为例,主要是我比较熟悉,其他主流编程语言也类似)中,并发编程的门槛并不低,你要学习多进程,多线程,还要掌握各种支持并发的库 -asyncio,aiohttp -等,同时你还要清楚它们之间的区别及优缺点,懂得在不同的场景选择不同的并发模式。 - -而 Golang 作为一门现代化的编程语言,它不需要你直面这些复杂的问题。在 -Golang -里,你不需要学习如何创建进程池/线程池,也不需要知道什么情况下使用多线程,什么时候使用多进程。因为你没得选,也不需要选,它原生提供的 -goroutine -(也即协程)已经足够优秀,能够自动帮你处理好所有的事情,而你要做的只是执行它,就这么简单。 - -一个 goroutine -本身就是一个函数,当你直接调用时,它就是一个普通函数,如果你在调用前加一个关键字 -``go`` ,那你就开启了一个 goroutine。 - -.. code:: go - - // 执行一个函数 - func() - - // 开启一个协程执行这个函数 - go func() - -1. 协程的初步使用 ------------------ - -一个 Go 程序的入口通常是 main 函数,程序启动后,main -函数最先运行,我们称之为 ``main goroutine``\ 。 - -在 main 中或者其下调用的代码中才可以使用 ``go + func()`` -的方法来启动协程。 - -main 的地位相当于主线程,当 main -函数执行完成后,这个线程也就终结了,其下的运行着的所有协程也不管代码是不是还在跑,也得乖乖退出。 - -因此如下这段代码运行完,只会输出 ``hello, world`` -,而不会输出\ ``hello, go``\ (因为协程的创建需要时间,当 -``hello, world``\ 打印后,协程还没来得及并执行) - -.. code:: go - - import "fmt" - - func mytest() { - fmt.Println("hello, go") - } - - func main() { - // 启动一个协程 - go mytest() - fmt.Println("hello, world") - } - -对于刚学习Go的协程同学来说,可以使用 time.Sleep 来使 main -阻塞,使其他协程能够有机会运行完全,但你要注意的是,这并不是推荐的方式(后续我们会学习其他更优雅的方式)。 - -当我在代码中加入一行 time.Sleep 输出就符合预期了。 - -.. code:: go - - import ( - "fmt" - "time" - ) - - func mytest() { - fmt.Println("hello, go") - } - - func main() { - go mytest() - fmt.Println("hello, world") - time.Sleep(time.Second) - } - -输出如下 - -:: - - hello, world - hello, go - -2. 多个协程的效果 ------------------ - -为了让你看到并发的效果,这里举个最简单的例子 - -.. code:: go - - import ( - "fmt" - "time" - ) - - func mygo(name string) { - for i := 0; i < 10; i++ { - fmt.Printf("In goroutine %s\n", name) - // 为了避免第一个协程执行过快,观察不到并发的效果,加个休眠 - time.Sleep(10 * time.Millisecond) - } - } - - func main() { - go mygo("协程1号") // 第一个协程 - go mygo("协程2号") // 第二个协程 - time.Sleep(time.Second) - } - -输出如下,可以观察到两个协程就如两个线程一样,并发执行 - -:: - - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程1号 - In goroutine 协程2号 - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程1号 - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程2号 - In goroutine 协程1号 - In goroutine 协程2号 - -通过以上简单的例子,是不是折服于Go的这种强大的并发特性,将同步代码转为异步代码,真的只要一个关键字就可以了,也不需要使用其他库,简单方便。 - -本篇只介绍了协程的简单使用,真正的并发程序还是要结合 信道 -(channel)来实现。关于信道的内容,将在下一篇文章中介绍。 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_19.md b/source/c09/c09_19.md deleted file mode 100644 index ee1eac3..0000000 --- a/source/c09/c09_19.md +++ /dev/null @@ -1,338 +0,0 @@ -# 9.19 学习 Go 协程:详解信道/通道 - -Go 语言之所以开始流行起来,很大一部分原因是因为它自带的并发机制。 - -如果说 goroutine 是 Go语言程序的并发体的话,那么 channel(信道) 就是 它们之间的通信机制。channel,是一个可以让一个 goroutine 与另一个 goroutine 传输信息的通道,我把他叫做信道,也有人将其翻译成通道,二者都是一个概念。 - -信道,就是一个管道,连接多个goroutine程序 ,它是一种队列式的数据结构,遵循先入先出的规则。 - -## 1. 信道的定义与使用 - -每个信道都只能传递一种数据类型的数据,所以在你声明的时候,你得指定数据类型(string int 等等) - -```go -var 信道实例 chan 信道类型 - -// 定义容量为10的信道 -var 信道实例 [10]chan 信道类型 -``` - -声明后的信道,其零值是nil,无法直接使用,必须配合make函进行初始化。 - -```go -信道实例 = make(chan 信道类型) -``` - -亦或者,上面两行可以合并成一句,以下我都使用这样的方式进行信道的声明 - -```go -信道实例 := make(chan 信道类型) -``` - -假如我要创建一个可以传输int类型的信道,可以这样子写。 - -```go -// 定义信道 -pipline := make(chan int) -``` - -信道的数据操作,无非就两种:发送数据与读取数据 - -```go -// 往信道中发送数据 -pipline<- 200 - -// 从信道中取出数据,并赋值给mydata -mydata := <-pipline -``` - -信道用完了,可以对其进行关闭,避免有人一直在等待。但是你关闭信道后,接收方仍然可以从信道中取到数据,只是接收到的会永远是 0。 - -```go -close(pipline) -``` - -对一个已关闭的信道再关闭,是会报错的。所以我们还要学会,如何判断一个信道是否被关闭? - -当从信道中读取数据时,可以有多个返回值,其中第二个可以表示 信道是否被关闭,如果已经被关闭,ok 为 false,若还没被关闭,ok 为true。 - -```go -x, ok := <-pipline -``` - - - -## 2. 信道的容量与长度 - -一般创建信道都是使用 make 函数,make 函数接收两个参数 - -- 第一个参数:必填,指定信道类型 -- 第二个参数:选填,不填默认为0,指定信道的**容量**(可缓存多少数据) - -对于信道的容量,很重要,这里要多说几点: - -- 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为**无缓冲信道**。 -- 当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 利用这点可以利用信道来做锁。 -- 当容量大于1时,信道中可以存放多个数据,可以用于多个协程之间的通信管道,共享资源。 - - - -至此我们知道,信道就是一个容器。 - -若将它比做一个纸箱子 - -- 它可以装10本书,代表其容量为10 -- 当前只装了1本书,代表其当前长度为1 - - - -信道的容量,可以使用 cap 函数获取 ,而信道的长度,可以使用 len 长度获取。 - -```go -package main - -import "fmt" - -func main() { - pipline := make(chan int, 10) - fmt.Printf("信道可缓冲 %d 个数据\n", cap(pipline)) - pipline<- 1 - fmt.Printf("信道中当前有 %d 个数据", len(pipline)) -} -``` - -输出如下 - -``` -信道可缓冲 10 个数据 -信道中当前有 1 个数据 -``` - - - -## 3. 缓冲信道与无缓冲信道 - -按照是否可缓冲数据可分为:**缓冲信道** 与 **无缓冲信道** - -**缓冲信道** - -允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。 - -```go -pipline := make(chan int, 10) -``` - - - -**无缓冲信道** - -在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,信道中无法存储数据。也就是说发送端和接收端是同步运行的。 - -```go -pipline := make(chan int) - -// 或者 -pipline := make(chan int, 0) -``` - - - -## 4. 双向信道与单向信道 - -通常情况下,我们定义的信道都是双向通道,可发送数据,也可以接收数据。 - -但有时候,我们希望对信道的数据流向做一些控制,比如这个信道只能接收数据或者这个信道只能发送数据。 - - - -因此,就有了 **双向信道** 和 **单向信道** 两种分类。 - -**双向信道** - -默认情况下你定义的信道都是双向的,比如下面代码 - -```go -import ( - "fmt" - "time" -) - -func main() { - pipline := make(chan int) - - go func() { - fmt.Println("准备发送数据: 100") - pipline <- 100 - }() - - go func() { - num := <-pipline - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) -} -``` - - - -**单向信道** - -单向信道,可以细分为 **只读信道** 和 **只写信道**。 - -定义只读信道 - -```go -var pipline = make(chan int) -type Receiver = <-chan int // 关键代码:定义别名类型 -var receiver Receiver = pipline -``` - -定义只写信道 - -```go -var pipline = make(chan int) -type Sender = chan<- int // 关键代码:定义别名类型 -var sender Sender = pipline -``` - - - -仔细观察,区别在于 `<-` 符号在关键字 `chan` 的左边还是右边。 - -- `<-chan` 表示这个信道,只能从里发出数据,对于程序来说就是只读 -- `chan<-` 表示这个信道,只能从外面接收数据,对于程序来说就是只写 - - - -有同学可能会问:为什么还要先声明一个双向信道,再定义单向通道呢?比如这样写 - -```go -type Sender = chan<- int -sender := make(Sender) -``` - -代码是没问题,但是你要明白信道的意义是什么?(**以下是我个人见解** - -信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入了吗,没什么用。所以只读信道和只写信道,唇亡齿寒,缺一不可。 - -当然了,若你往一个只读信道中写入数据 ,或者从一个只写信道中读取数据 ,都会出错。 - -完整的示例代码如下,供你参考: - -```go -import ( - "fmt" - "time" -) - //定义只写信道类型 -type Sender = chan<- int - -//定义只读信道类型 -type Receiver = <-chan int - -func main() { - var pipline = make(chan int) - - go func() { - var sender Sender = pipline - fmt.Println("准备发送数据: 100") - sender <- 100 - }() - - go func() { - var receiver Receiver = pipline - num := <-receiver - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) -} -``` - - - -## 5. 遍历信道 - -遍历信道,可以使用 for 搭配 range关键字,在range时,要确保信道是处于关闭状态,否则循环会阻塞。 - -```go -import "fmt" - -func fibonacci(mychan chan int) { - n := cap(mychan) - x, y := 1, 1 - for i := 0; i < n; i++ { - mychan <- x - x, y = y, x+y - } - // 记得 close 信道 - // 不然主函数中遍历完并不会结束,而是会阻塞。 - close(mychan) -} - -func main() { - pipline := make(chan int, 10) - - go fibonacci(pipline) - - for k := range pipline { - fmt.Println(k) - } -} -``` - - - -## 6. 用信道来做锁 - -当信道里的数据量已经达到设定的容量时,此时再往里发送数据会阻塞整个程序。 - -利用这个特性,可以用当他来当程序的锁。 - -示例如下,详情可以看注释 - -```go -package main - -import { - "fmt" - "time" -} - -// 由于 x=x+1 不是原子操作 -// 所以应避免多个协程对x进行操作 -// 使用容量为1的信道可以达到锁的效果 -func increment(ch chan bool, x *int) { - ch <- true - *x = *x + 1 - <- ch -} - -func main() { - // 注意要设置容量为 1 的缓冲信道 - pipline := make(chan bool, 1) - - var x int - for i:=0;i<1000;i++{ - go increment(pipline, &x) - } - - // 确保所有的协程都已完成 - // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep - time.Sleep(3) - fmt.Println("x 的值:", x) -} -``` - -输出如下 - -``` -x 的值:1000 -``` - -如果不加锁,输出会小于1000。 - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) diff --git a/source/c09/c09_19.rst b/source/c09/c09_19.rst deleted file mode 100644 index d9b66a8..0000000 --- a/source/c09/c09_19.rst +++ /dev/null @@ -1,332 +0,0 @@ -9.19 学习 Go 协程:详解信道/通道 -================================ - -Go 语言之所以开始流行起来,很大一部分原因是因为它自带的并发机制。 - -如果说 goroutine 是 Go语言程序的并发体的话,那么 channel(信道) 就是 -它们之间的通信机制。channel,是一个可以让一个 goroutine 与另一个 -goroutine -传输信息的通道,我把他叫做信道,也有人将其翻译成通道,二者都是一个概念。 - -信道,就是一个管道,连接多个goroutine程序 -,它是一种队列式的数据结构,遵循先入先出的规则。 - -1. 信道的定义与使用 -------------------- - -每个信道都只能传递一种数据类型的数据,所以在你声明的时候,你得指定数据类型(string -int 等等) - -.. code:: go - - var 信道实例 chan 信道类型 - - // 定义容量为10的信道 - var 信道实例 [10]chan 信道类型 - -声明后的信道,其零值是nil,无法直接使用,必须配合make函进行初始化。 - -.. code:: go - - 信道实例 = make(chan 信道类型) - -亦或者,上面两行可以合并成一句,以下我都使用这样的方式进行信道的声明 - -.. code:: go - - 信道实例 := make(chan 信道类型) - -假如我要创建一个可以传输int类型的信道,可以这样子写。 - -.. code:: go - - // 定义信道 - pipline := make(chan int) - -信道的数据操作,无非就两种:发送数据与读取数据 - -.. code:: go - - // 往信道中发送数据 - pipline<- 200 - - // 从信道中取出数据,并赋值给mydata - mydata := <-pipline - -信道用完了,可以对其进行关闭,避免有人一直在等待。但是你关闭信道后,接收方仍然可以从信道中取到数据,只是接收到的会永远是 -0。 - -.. code:: go - - close(pipline) - -对一个已关闭的信道再关闭,是会报错的。所以我们还要学会,如何判断一个信道是否被关闭? - -当从信道中读取数据时,可以有多个返回值,其中第二个可以表示 -信道是否被关闭,如果已经被关闭,ok 为 false,若还没被关闭,ok 为true。 - -.. code:: go - - x, ok := <-pipline - -2. 信道的容量与长度 -------------------- - -一般创建信道都是使用 make 函数,make 函数接收两个参数 - -- 第一个参数:必填,指定信道类型 -- 第二个参数:选填,不填默认为0,指定信道的\ **容量**\ (可缓存多少数据) - -对于信道的容量,很重要,这里要多说几点: - -- 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为\ **无缓冲信道**\ 。 -- 当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 - 利用这点可以利用信道来做锁。 -- 当容量大于1时,信道中可以存放多个数据,可以用于多个协程之间的通信管道,共享资源。 - -至此我们知道,信道就是一个容器。 - -若将它比做一个纸箱子 - -- 它可以装10本书,代表其容量为10 -- 当前只装了1本书,代表其当前长度为1 - -信道的容量,可以使用 cap 函数获取 ,而信道的长度,可以使用 len -长度获取。 - -.. code:: go - - package main - - import "fmt" - - func main() { - pipline := make(chan int, 10) - fmt.Printf("信道可缓冲 %d 个数据\n", cap(pipline)) - pipline<- 1 - fmt.Printf("信道中当前有 %d 个数据", len(pipline)) - } - -输出如下 - -:: - - 信道可缓冲 10 个数据 - 信道中当前有 1 个数据 - -3. 缓冲信道与无缓冲信道 ------------------------ - -按照是否可缓冲数据可分为:\ **缓冲信道** 与 **无缓冲信道** - -**缓冲信道** - -允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。 - -.. code:: go - - pipline := make(chan int, 10) - -**无缓冲信道** - -在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,信道中无法存储数据。也就是说发送端和接收端是同步运行的。 - -.. code:: go - - pipline := make(chan int) - - // 或者 - pipline := make(chan int, 0) - -4. 双向信道与单向信道 ---------------------- - -通常情况下,我们定义的信道都是双向通道,可发送数据,也可以接收数据。 - -但有时候,我们希望对信道的数据流向做一些控制,比如这个信道只能接收数据或者这个信道只能发送数据。 - -因此,就有了 **双向信道** 和 **单向信道** 两种分类。 - -**双向信道** - -默认情况下你定义的信道都是双向的,比如下面代码 - -.. code:: go - - import ( - "fmt" - "time" - ) - - func main() { - pipline := make(chan int) - - go func() { - fmt.Println("准备发送数据: 100") - pipline <- 100 - }() - - go func() { - num := <-pipline - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) - } - -**单向信道** - -单向信道,可以细分为 **只读信道** 和 **只写信道**\ 。 - -定义只读信道 - -.. code:: go - - var pipline = make(chan int) - type Receiver = <-chan int // 关键代码:定义别名类型 - var receiver Receiver = pipline - -定义只写信道 - -.. code:: go - - var pipline = make(chan int) - type Sender = chan<- int // 关键代码:定义别名类型 - var sender Sender = pipline - -仔细观察,区别在于 ``<-`` 符号在关键字 ``chan`` 的左边还是右边。 - -- ``<-chan`` 表示这个信道,只能从里发出数据,对于程序来说就是只读 -- ``chan<-`` 表示这个信道,只能从外面接收数据,对于程序来说就是只写 - -有同学可能会问:为什么还要先声明一个双向信道,再定义单向通道呢?比如这样写 - -.. code:: go - - type Sender = chan<- int - sender := make(Sender) - -代码是没问题,但是你要明白信道的意义是什么?(\ **以下是我个人见解** - -信道本身就是为了传输数据而存在的,如果只有接收者或者只有发送者,那信道就变成了只入不出或者只出不入了吗,没什么用。所以只读信道和只写信道,唇亡齿寒,缺一不可。 - -当然了,若你往一个只读信道中写入数据 ,或者从一个只写信道中读取数据 -,都会出错。 - -完整的示例代码如下,供你参考: - -.. code:: go - - import ( - "fmt" - "time" - ) - //定义只写信道类型 - type Sender = chan<- int - - //定义只读信道类型 - type Receiver = <-chan int - - func main() { - var pipline = make(chan int) - - go func() { - var sender Sender = pipline - fmt.Println("准备发送数据: 100") - sender <- 100 - }() - - go func() { - var receiver Receiver = pipline - num := <-receiver - fmt.Printf("接收到的数据是: %d", num) - }() - // 主函数sleep,使得上面两个goroutine有机会执行 - time.Sleep(1) - } - -5. 遍历信道 ------------ - -遍历信道,可以使用 for 搭配 -range关键字,在range时,要确保信道是处于关闭状态,否则循环会阻塞。 - -.. code:: go - - import "fmt" - - func fibonacci(mychan chan int) { - n := cap(mychan) - x, y := 1, 1 - for i := 0; i < n; i++ { - mychan <- x - x, y = y, x+y - } - // 记得 close 信道 - // 不然主函数中遍历完并不会结束,而是会阻塞。 - close(mychan) - } - - func main() { - pipline := make(chan int, 10) - - go fibonacci(pipline) - - for k := range pipline { - fmt.Println(k) - } - } - -6. 用信道来做锁 ---------------- - -当信道里的数据量已经达到设定的容量时,此时再往里发送数据会阻塞整个程序。 - -利用这个特性,可以用当他来当程序的锁。 - -示例如下,详情可以看注释 - -.. code:: go - - package main - - import { - "fmt" - "time" - } - - // 由于 x=x+1 不是原子操作 - // 所以应避免多个协程对x进行操作 - // 使用容量为1的信道可以达到锁的效果 - func increment(ch chan bool, x *int) { - ch <- true - *x = *x + 1 - <- ch - } - - func main() { - // 注意要设置容量为 1 的缓冲信道 - pipline := make(chan bool, 1) - - var x int - for i:=0;i<1000;i++{ - go increment(pipline, &x) - } - - // 确保所有的协程都已完成 - // 以后会介绍一种更合适的方法(Mutex),这里暂时使用sleep - time.Sleep(3) - fmt.Println("x 的值:", x) - } - -输出如下 - -:: - - x 的值:1000 - -如果不加锁,输出会小于1000。 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_20.md b/source/c09/c09_20.md deleted file mode 100644 index 3839fec..0000000 --- a/source/c09/c09_20.md +++ /dev/null @@ -1,175 +0,0 @@ -# 9.20 几个信道死锁经典错误案例详解 - -刚接触 Go 语言的信道的时候,经常会遇到死锁的错误,而导致这个错误的原因有很多种,这里整理了几种常见的。 - -``` -fatal error: all goroutines are asleep - deadlock! -``` - - - -## 错误示例一 - -看下面这段代码 - -```go -package main - -import "fmt" - -func main() { - pipline := make(chan string) - pipline <- "hello world" - fmt.Println(<-pipline) -} -``` - -运行会抛出错误,如下 - -``` -fatal error: all goroutines are asleep - deadlock! -``` - -看起来好像没有什么问题?先往信道中存入数据,再从信道中读取数据。 - -回顾前面的基础,我们知道使用 make 创建信道的时候,若不传递第二个参数,则你定义的是无缓冲信道,而对于无缓冲信道,在接收者未准备好之前,发送操作是阻塞的. - -因此,对于解决此问题有两种方法: - -1. 使接收者代码在发送者之前执行 -2. 使用缓冲信道,而不使用无缓冲信道 - -**第一种方法**: - -若要程序正常执行,需要保证接收者程序在发送数据到信道前就进行阻塞状态,修改代码如下 - -```go -package main - -import "fmt" - -func main() { - pipline := make(chan string) - fmt.Println(<-pipline) - pipline <- "hello world" -} -``` - -运行的时候还是报同样的错误。问题出在哪里呢? - -原来我们将发送者和接收者写在了同一协程中,虽然保证了接收者代码在发送者之前执行,但是由于前面接收者一直在等待数据 而处于阻塞状态,所以无法执行到后面的发送数据。还是一样造成了死锁。 - -有了前面的经验,我们将接收者代码写在另一个协程里,并保证在发送者之前执行,就像这样的代码 - -```go -package main - -func hello(pipline chan string) { - <-pipline -} - -func main() { - pipline := make(chan string) - go hello(pipline) - pipline <- "hello world" -} -``` - -运行之后 ,一切正常。 - -**第二种方法**: - -接收者代码必须在发送者代码之前 执行,这是针对无缓冲信道才有的约束。 - -既然这样,我们改使用可缓冲信道不就OK了吗? - -```go -package main - -import "fmt" - -func main() { - pipline := make(chan string, 1) - pipline <- "hello world" - fmt.Println(<-pipline) -} -``` - -运行之后,一切正常。 - - - -## 错误示例二 - -每个缓冲信道,都有容量,当信道里的数据量等于信道的容量后,此时再往信道里发送数据,就失造成阻塞,必须等到有人从信道中消费数据后,程序才会往下进行。 - - 比如这段代码,信道容量为 1,但是往信道中写入两条数据,对于一个协程来说就会造成死锁。 - -```go -package main - -import "fmt" - -func main() { - ch1 := make(chan string, 1) - - ch1 <- "hello world" - ch1 <- "hello China" - - fmt.Println(<-ch1) -} -``` - - - -## 错误示例三 - -当程序一直在等待从信道里读取数据,而此时并没有人会往信道中写入数据。此时程序就会陷入死循环,造成死锁。 - -比如这段代码,for 循环接收了两次消息("hello world"和“hello China”)后,再也没有人发送数据了,接收者就会处于一个等待永远接收不到数据的囧境。陷入死循环,造成死锁。 - -```go -package main - -import "fmt" - -func main() { - pipline := make(chan string) - go func() { - pipline <- "hello world" - pipline <- "hello China" - // close(pipline) - }() - for data := range pipline{ - fmt.Println(data) - } -} -``` - -包子铺里的包子已经卖完了,可还有人在排队等着买,如果不再做包子,就要告诉排队的人:不用等了,今天的包子已经卖完了,明日请早呀。 - -不能让人家死等呀,不跟客人说明一下,人家还以为你们店后面还在蒸包子呢。 - -所以这个问题,解决方法很简单,只要在发送完数据后,手动关闭信道,告诉 range 信道已经关闭,无需等待就行。 - -```go -package main - -import "fmt" - -func main() { - pipline := make(chan string) - go func() { - pipline <- "hello world" - pipline <- "hello China" - close(pipline) - }() - for data := range pipline{ - fmt.Println(data) - } -} -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_20.rst b/source/c09/c09_20.rst deleted file mode 100644 index 8604447..0000000 --- a/source/c09/c09_20.rst +++ /dev/null @@ -1,179 +0,0 @@ -9.20 几个信道死锁经典错误案例详解 -================================= - -刚接触 Go -语言的信道的时候,经常会遇到死锁的错误,而导致这个错误的原因有很多种,这里整理了几种常见的。 - -:: - - fatal error: all goroutines are asleep - deadlock! - -错误示例一 ----------- - -看下面这段代码 - -.. code:: go - - package main - - import "fmt" - - func main() { - pipline := make(chan string) - pipline <- "hello world" - fmt.Println(<-pipline) - } - -运行会抛出错误,如下 - -:: - - fatal error: all goroutines are asleep - deadlock! - -看起来好像没有什么问题?先往信道中存入数据,再从信道中读取数据。 - -回顾前面的基础,我们知道使用 make -创建信道的时候,若不传递第二个参数,则你定义的是无缓冲信道,而对于无缓冲信道,在接收者未准备好之前,发送操作是阻塞的. - -因此,对于解决此问题有两种方法: - -1. 使接收者代码在发送者之前执行 -2. 使用缓冲信道,而不使用无缓冲信道 - -**第一种方法**\ : - -若要程序正常执行,需要保证接收者程序在发送数据到信道前就进行阻塞状态,修改代码如下 - -.. code:: go - - package main - - import "fmt" - - func main() { - pipline := make(chan string) - fmt.Println(<-pipline) - pipline <- "hello world" - } - -运行的时候还是报同样的错误。问题出在哪里呢? - -原来我们将发送者和接收者写在了同一协程中,虽然保证了接收者代码在发送者之前执行,但是由于前面接收者一直在等待数据 -而处于阻塞状态,所以无法执行到后面的发送数据。还是一样造成了死锁。 - -有了前面的经验,我们将接收者代码写在另一个协程里,并保证在发送者之前执行,就像这样的代码 - -.. code:: go - - package main - - func hello(pipline chan string) { - <-pipline - } - - func main() { - pipline := make(chan string) - go hello(pipline) - pipline <- "hello world" - } - -运行之后 ,一切正常。 - -**第二种方法**\ : - -接收者代码必须在发送者代码之前 执行,这是针对无缓冲信道才有的约束。 - -既然这样,我们改使用可缓冲信道不就OK了吗? - -.. code:: go - - package main - - import "fmt" - - func main() { - pipline := make(chan string, 1) - pipline <- "hello world" - fmt.Println(<-pipline) - } - -运行之后,一切正常。 - -错误示例二 ----------- - -每个缓冲信道,都有容量,当信道里的数据量等于信道的容量后,此时再往信道里发送数据,就失造成阻塞,必须等到有人从信道中消费数据后,程序才会往下进行。 - -比如这段代码,信道容量为 -1,但是往信道中写入两条数据,对于一个协程来说就会造成死锁。 - -.. code:: go - - package main - - import "fmt" - - func main() { - ch1 := make(chan string, 1) - - ch1 <- "hello world" - ch1 <- "hello China" - - fmt.Println(<-ch1) - } - -错误示例三 ----------- - -当程序一直在等待从信道里读取数据,而此时并没有人会往信道中写入数据。此时程序就会陷入死循环,造成死锁。 - -比如这段代码,for 循环接收了两次消息(“hello world”和“hello -China”)后,再也没有人发送数据了,接收者就会处于一个等待永远接收不到数据的囧境。陷入死循环,造成死锁。 - -.. code:: go - - package main - - import "fmt" - - func main() { - pipline := make(chan string) - go func() { - pipline <- "hello world" - pipline <- "hello China" - // close(pipline) - }() - for data := range pipline{ - fmt.Println(data) - } - } - -包子铺里的包子已经卖完了,可还有人在排队等着买,如果不再做包子,就要告诉排队的人:不用等了,今天的包子已经卖完了,明日请早呀。 - -不能让人家死等呀,不跟客人说明一下,人家还以为你们店后面还在蒸包子呢。 - -所以这个问题,解决方法很简单,只要在发送完数据后,手动关闭信道,告诉 -range 信道已经关闭,无需等待就行。 - -.. code:: go - - package main - - import "fmt" - - func main() { - pipline := make(chan string) - go func() { - pipline <- "hello world" - pipline <- "hello China" - close(pipline) - }() - for data := range pipline{ - fmt.Println(data) - } - } - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_21.md b/source/c09/c09_21.md deleted file mode 100644 index 2afd761..0000000 --- a/source/c09/c09_21.md +++ /dev/null @@ -1,109 +0,0 @@ -# 9.21 学习 Go 协程:WaitGroup - -在前两篇文章里,我们学习了 `协程` 和 `信道` 的内容,里面有很多例子,当时为了保证 main goroutine 在所有的 goroutine 都执行完毕后再退出,我使用了 time.Sleep 这种简单的方式。 - -由于写的 demo 都是比较简单的, sleep 个 1 秒,我们主观上认为是够用的。 - -但在实际开发中,开发人员是无法预知,所有的 goroutine 需要多长的时间才能执行完毕,sleep 多了吧主程序就阻塞了, sleep 少了吧有的子协程的任务就没法完成。 - -因此,使用time.Sleep 是一种极不推荐的方式,今天主要就要来介绍 一下如何优雅的处理这种情况。 - - - -## 1. 使用信道来标记完成 - -> “不要通过共享内存来通信,要通过通信来共享内存” - -学习了信道后,我们知道,信道可以实现多个协程间的通信,那么我们只要定义一个信道,在任务完成后,往信道中写入true,然后在主协程中获取到true,就认为子协程已经执行完毕。 - -```go -import "fmt" - -func main() { - done := make(chan bool) - go func() { - for i := 0; i < 5; i++ { - fmt.Println(i) - } - done <- true - }() - <-done -} -``` - -输出如下 - -``` -0 -1 -2 -3 -4 -``` - - - -## 2. 使用 WaitGroup - -上面使用信道的方法,在单个协程或者协程数少的时候,并不会有什么问题,但在协程数多的时候,代码就会显得非常复杂,有兴趣可以自己尝试一下。 - -那么有没有一种更加优雅的方式呢? - -有,这就要说到 sync包 提供的 WaitGroup 类型。 - -WaitGroup 你只要实例化了就能使用 - -```go -var 实例名 sync.WaitGroup -``` - -实例化完成后,就可以使用它的几个方法: - -- Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量 -- Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 defer 来调用。 -- Wait:阻塞当前协程,直到实例里的计数器归零。 - -举一个例子: - -```go -import ( - "fmt" - "sync" -) - -func worker(x int, wg *sync.WaitGroup) { - defer wg.Done() - for i := 0; i < 5; i++ { - fmt.Printf("worker %d: %d\n", x, i) - } -} - -func main() { - var wg sync.WaitGroup - - wg.Add(2) - go worker(1, &wg) - go worker(2, &wg) - - wg.Wait() -} -``` - -输出如下 - -``` -worker 2: 0 -worker 2: 1 -worker 2: 2 -worker 2: 3 -worker 2: 4 -worker 1: 0 -worker 1: 1 -worker 1: 2 -worker 1: 3 -worker 1: 4 -``` - -以上就是我们在 Go 语言中实现一主多子的协程协作方式,推荐使用 sync.WaitGroup。。 - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_21.rst b/source/c09/c09_21.rst deleted file mode 100644 index 3e742c5..0000000 --- a/source/c09/c09_21.rst +++ /dev/null @@ -1,117 +0,0 @@ -9.21 学习 Go 协程:WaitGroup -============================ - -在前两篇文章里,我们学习了 ``协程`` 和 ``信道`` -的内容,里面有很多例子,当时为了保证 main goroutine 在所有的 goroutine -都执行完毕后再退出,我使用了 time.Sleep 这种简单的方式。 - -由于写的 demo 都是比较简单的, sleep 个 1 秒,我们主观上认为是够用的。 - -但在实际开发中,开发人员是无法预知,所有的 goroutine -需要多长的时间才能执行完毕,sleep 多了吧主程序就阻塞了, sleep -少了吧有的子协程的任务就没法完成。 - -因此,使用time.Sleep 是一种极不推荐的方式,今天主要就要来介绍 -一下如何优雅的处理这种情况。 - -1. 使用信道来标记完成 ---------------------- - - “不要通过共享内存来通信,要通过通信来共享内存” - -学习了信道后,我们知道,信道可以实现多个协程间的通信,那么我们只要定义一个信道,在任务完成后,往信道中写入true,然后在主协程中获取到true,就认为子协程已经执行完毕。 - -.. code:: go - - import "fmt" - - func main() { - done := make(chan bool) - go func() { - for i := 0; i < 5; i++ { - fmt.Println(i) - } - done <- true - }() - <-done - } - -输出如下 - -:: - - 0 - 1 - 2 - 3 - 4 - -2. 使用 WaitGroup ------------------ - -上面使用信道的方法,在单个协程或者协程数少的时候,并不会有什么问题,但在协程数多的时候,代码就会显得非常复杂,有兴趣可以自己尝试一下。 - -那么有没有一种更加优雅的方式呢? - -有,这就要说到 sync包 提供的 WaitGroup 类型。 - -WaitGroup 你只要实例化了就能使用 - -.. code:: go - - var 实例名 sync.WaitGroup - -实例化完成后,就可以使用它的几个方法: - -- Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量 -- Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 - defer 来调用。 -- Wait:阻塞当前协程,直到实例里的计数器归零。 - -举一个例子: - -.. code:: go - - import ( - "fmt" - "sync" - ) - - func worker(x int, wg *sync.WaitGroup) { - defer wg.Done() - for i := 0; i < 5; i++ { - fmt.Printf("worker %d: %d\n", x, i) - } - } - - func main() { - var wg sync.WaitGroup - - wg.Add(2) - go worker(1, &wg) - go worker(2, &wg) - - wg.Wait() - } - -输出如下 - -:: - - worker 2: 0 - worker 2: 1 - worker 2: 2 - worker 2: 3 - worker 2: 4 - worker 1: 0 - worker 1: 1 - worker 1: 2 - worker 1: 3 - worker 1: 4 - -以上就是我们在 Go 语言中实现一主多子的协程协作方式,推荐使用 -sync.WaitGroup。。 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_22.md b/source/c09/c09_22.md deleted file mode 100644 index 8d350ea..0000000 --- a/source/c09/c09_22.md +++ /dev/null @@ -1,214 +0,0 @@ -# 9.22 学习 Go 协程:互斥锁和读写锁 - -在 「[**19. 学习 Go 协程:详解信道/通道**](http://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483741&idx=1&sn=4d4ccd8fdee404432f03447927ddb055&chksm=fc355b36cb42d2201e12b77085f7db5a5fed98674e369a5df7fd852abde09a1efad35ba28944&scene=21#wechat_redirect)」这一节里我详细地介绍信道的一些用法,要知道的是在 Go 语言中,信道的地位非常高,它是 first class 级别的,面对并发问题,我们始终应该优先考虑使用信道,如果通过信道解决不了的,不得不使用共享内存来实现并发编程的,那 Golang 中的锁机制,就是你绕不过的知识点了。 - -今天就来讲一讲 Golang 中的锁机制。 - -在 Golang 里有专门的方法来实现锁,还是上一节里介绍的 sync 包。 - -这个包有两个很重要的锁类型 - -一个叫 `Mutex`, 利用它可以实现互斥锁。 - -一个叫 `RWMutex`,利用它可以实现读写锁。 - -## 1. 互斥锁 :Mutex - -使用互斥锁(Mutex,全称 mutual exclusion)是为了来保护一个资源不会因为并发操作而引起冲突导致数据不准确。 - -举个例子,就像下面这段代码,我开启了三个协程,每个协程分别往 count 这个变量加1000次 1,理论上看,最终的 count 值应试为 3000 - -```go -package main - -import ( - "fmt" - "sync" -) - -func add(count *int, wg *sync.WaitGroup) { - for i := 0; i < 1000; i++ { - *count = *count + 1 - } - wg.Done() -} - -func main() { - var wg sync.WaitGroup - count := 0 - wg.Add(3) - go add(&count, &wg) - go add(&count, &wg) - go add(&count, &wg) - - wg.Wait() - fmt.Println("count 的值为:", count) -} -``` - -可运行多次的结果,都不相同 - -```go -// 第一次 -count 的值为: 2854 - -// 第二次 -count 的值为: 2673 - -// 第三次 -count 的值为: 2840 -``` - -原因就在于这三个协程在执行时,先读取 count 再更新 count 的值,而这个过程并不具备原子性,所以导致了数据的不准确。 - -解决这个问题的方法,就是给 add 这个函数加上 Mutex 互斥锁,要求同一时刻,仅能有一个协程能对 count 操作。 - -在写代码前,先了解一下 Mutex 锁的两种定义方法 - -```go -// 第一种 -var lock *sync.Mutex -lock = new(sync.Mutex) - -// 第二种 -lock := &sync.Mutex{} -``` - -然后就可以修改你上面的代码,如下所示 - -```go -import ( - "fmt" - "sync" -) - -func add(count *int, wg *sync.WaitGroup, lock *sync.Mutex) { - for i := 0; i < 1000; i++ { - lock.Lock() - *count = *count + 1 - lock.Unlock() - } - wg.Done() -} - -func main() { - var wg sync.WaitGroup - lock := &sync.Mutex{} - count := 0 - wg.Add(3) - go add(&count, &wg, lock) - go add(&count, &wg, lock) - go add(&count, &wg, lock) - - wg.Wait() - fmt.Println("count 的值为:", count) -} -``` - -此时,不管你执行多少次,输出都只有一个结果 - -```go -count 的值为: 3000 -``` - -使用 Mutext 锁虽然很简单,但仍然有几点需要注意: - -- 同一协程里,不要在尚未解锁时再次使加锁 -- 同一协程里,不要对已解锁的锁再次解锁 -- 加了锁后,别忘了解锁,必要时使用 defer 语句 - -## 3. 读写锁:RWMutex - -Mutex 是最简单的一种锁类型,他提供了一个傻瓜式的操作,加锁解锁加锁解锁,让你不需要再考虑其他的。 - -**简单**同时意味着在某些特殊情况下有可能会造成时间上的浪费,导致程序性能低下。 - -举个例子,我们平时去图书馆,要嘛是去借书,要嘛去还书,借书的流程繁锁,没有办卡的还要让管理员给你办卡,因此借书通常都要排老长的队,假设图书馆里只有一个管理员,按照 Mutex(互斥锁)的思想, 这个管理员同一时刻只能服务一个人,这就意味着,还书的也要跟借书的一起排队。 - -可还书的步骤非常简单,可能就把书给管理员扫下码就可以走了。 - -如果让还书的人,跟借书的人一起排队,那估计有很多人都不乐意了。 - -因此,图书馆为了提高整个流程的效率,就允许还书的人,不需要排队,可以直接自助还书。 - -图书管将馆里的人分得更细了,对于读者的不同需求提供了不同的方案。提高了效率。 - -RWMutex,也是如此,它将程序对资源的访问分为读操作和写操作 - -- 为了保证数据的安全,它规定了当有人还在读取数据(即读锁占用)时,不允计有人更新这个数据(即写锁会阻塞) -- 为了保证程序的效率,多个人(线程)读取数据(拥有读锁)时,互不影响不会造成阻塞,它不会像 Mutex 那样只允许有一个人(线程)读取同一个数据。 - -理解了这个后,再来看看,如何使用 RWMutex? - -定义一个 RWMuteux 锁,有两种方法 - -```go -// 第一种 -var lock *sync.RWMutex -lock = new(sync.RWMutex) - -// 第二种 -lock := &sync.RWMutex{} -``` - -RWMutex 里提供了两种锁,每种锁分别对应两个方法,为了避免死锁,两个方法应成对出现,必要时请使用 defer。 - -- 读锁:调用 RLock 方法开启锁,调用 RUnlock 释放锁 -- 写锁:调用 Lock 方法开启锁,调用 Unlock 释放锁(和 Mutex类似) - -接下来,直接看一下例子吧 - -```go -package main - -import ( - "fmt" - "sync" - "time" -) - -func main() { - lock := &sync.RWMutex{} - lock.Lock() - - for i := 0; i < 4; i++ { - go func(i int) { - fmt.Printf("第 %d 个协程准备开始... \n", i) - lock.RLock() - fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后,释放锁\n", i) - time.Sleep(time.Second) - lock.RUnlock() - }(i) - } - - time.Sleep(time.Second * 2) - - fmt.Println("准备释放写锁,读锁不再阻塞") - // 写锁一释放,读锁就自由了 - lock.Unlock() - - // 由于会等到读锁全部释放,才能获得写锁 - // 因为这里一定会在上面 4 个协程全部完成才能往下走 - lock.Lock() - fmt.Println("程序退出...") - lock.Unlock() -} -``` - -输出如下 - -``` -第 1 个协程准备开始... -第 0 个协程准备开始... -第 3 个协程准备开始... -第 2 个协程准备开始... -准备释放写锁,读锁不再阻塞 -第 2 个协程获得读锁, sleep 1s 后,释放锁 -第 3 个协程获得读锁, sleep 1s 后,释放锁 -第 1 个协程获得读锁, sleep 1s 后,释放锁 -第 0 个协程获得读锁, sleep 1s 后,释放锁 -程序退出... -``` - - - diff --git a/source/c09/c09_22.rst b/source/c09/c09_22.rst deleted file mode 100644 index d5c65eb..0000000 --- a/source/c09/c09_22.rst +++ /dev/null @@ -1,228 +0,0 @@ -9.22 学习 Go 协程:互斥锁和读写锁 -================================= - -在 「\ `19. 学习 Go -协程:详解信道/通道 `__\ 」这一节里我详细地介绍信道的一些用法,要知道的是在 -Go 语言中,信道的地位非常高,它是 first class -级别的,面对并发问题,我们始终应该优先考虑使用信道,如果通过信道解决不了的,不得不使用共享内存来实现并发编程的,那 -Golang 中的锁机制,就是你绕不过的知识点了。 - -今天就来讲一讲 Golang 中的锁机制。 - -在 Golang 里有专门的方法来实现锁,还是上一节里介绍的 sync 包。 - -这个包有两个很重要的锁类型 - -一个叫 ``Mutex``\ , 利用它可以实现互斥锁。 - -一个叫 ``RWMutex``\ ,利用它可以实现读写锁。 - -1. 互斥锁 :Mutex ------------------ - -使用互斥锁(Mutex,全称 mutual -exclusion)是为了来保护一个资源不会因为并发操作而引起冲突导致数据不准确。 - -举个例子,就像下面这段代码,我开启了三个协程,每个协程分别往 count -这个变量加1000次 1,理论上看,最终的 count 值应试为 3000 - -.. code:: go - - package main - - import ( - "fmt" - "sync" - ) - - func add(count *int, wg *sync.WaitGroup) { - for i := 0; i < 1000; i++ { - *count = *count + 1 - } - wg.Done() - } - - func main() { - var wg sync.WaitGroup - count := 0 - wg.Add(3) - go add(&count, &wg) - go add(&count, &wg) - go add(&count, &wg) - - wg.Wait() - fmt.Println("count 的值为:", count) - } - -可运行多次的结果,都不相同 - -.. code:: go - - // 第一次 - count 的值为: 2854 - - // 第二次 - count 的值为: 2673 - - // 第三次 - count 的值为: 2840 - -原因就在于这三个协程在执行时,先读取 count 再更新 count -的值,而这个过程并不具备原子性,所以导致了数据的不准确。 - -解决这个问题的方法,就是给 add 这个函数加上 Mutex -互斥锁,要求同一时刻,仅能有一个协程能对 count 操作。 - -在写代码前,先了解一下 Mutex 锁的两种定义方法 - -.. code:: go - - // 第一种 - var lock *sync.Mutex - lock = new(sync.Mutex) - - // 第二种 - lock := &sync.Mutex{} - -然后就可以修改你上面的代码,如下所示 - -.. code:: go - - import ( - "fmt" - "sync" - ) - - func add(count *int, wg *sync.WaitGroup, lock *sync.Mutex) { - for i := 0; i < 1000; i++ { - lock.Lock() - *count = *count + 1 - lock.Unlock() - } - wg.Done() - } - - func main() { - var wg sync.WaitGroup - lock := &sync.Mutex{} - count := 0 - wg.Add(3) - go add(&count, &wg, lock) - go add(&count, &wg, lock) - go add(&count, &wg, lock) - - wg.Wait() - fmt.Println("count 的值为:", count) - } - -此时,不管你执行多少次,输出都只有一个结果 - -.. code:: go - - count 的值为: 3000 - -使用 Mutext 锁虽然很简单,但仍然有几点需要注意: - -- 同一协程里,不要在尚未解锁时再次使加锁 -- 同一协程里,不要对已解锁的锁再次解锁 -- 加了锁后,别忘了解锁,必要时使用 defer 语句 - -3. 读写锁:RWMutex ------------------- - -Mutex -是最简单的一种锁类型,他提供了一个傻瓜式的操作,加锁解锁加锁解锁,让你不需要再考虑其他的。 - -**简单**\ 同时意味着在某些特殊情况下有可能会造成时间上的浪费,导致程序性能低下。 - -举个例子,我们平时去图书馆,要嘛是去借书,要嘛去还书,借书的流程繁锁,没有办卡的还要让管理员给你办卡,因此借书通常都要排老长的队,假设图书馆里只有一个管理员,按照 -Mutex(互斥锁)的思想, -这个管理员同一时刻只能服务一个人,这就意味着,还书的也要跟借书的一起排队。 - -可还书的步骤非常简单,可能就把书给管理员扫下码就可以走了。 - -如果让还书的人,跟借书的人一起排队,那估计有很多人都不乐意了。 - -因此,图书馆为了提高整个流程的效率,就允许还书的人,不需要排队,可以直接自助还书。 - -图书管将馆里的人分得更细了,对于读者的不同需求提供了不同的方案。提高了效率。 - -RWMutex,也是如此,它将程序对资源的访问分为读操作和写操作 - -- 为了保证数据的安全,它规定了当有人还在读取数据(即读锁占用)时,不允计有人更新这个数据(即写锁会阻塞) -- 为了保证程序的效率,多个人(线程)读取数据(拥有读锁)时,互不影响不会造成阻塞,它不会像 - Mutex 那样只允许有一个人(线程)读取同一个数据。 - -理解了这个后,再来看看,如何使用 RWMutex? - -定义一个 RWMuteux 锁,有两种方法 - -.. code:: go - - // 第一种 - var lock *sync.RWMutex - lock = new(sync.RWMutex) - - // 第二种 - lock := &sync.RWMutex{} - -RWMutex -里提供了两种锁,每种锁分别对应两个方法,为了避免死锁,两个方法应成对出现,必要时请使用 -defer。 - -- 读锁:调用 RLock 方法开启锁,调用 RUnlock 释放锁 -- 写锁:调用 Lock 方法开启锁,调用 Unlock 释放锁(和 Mutex类似) - -接下来,直接看一下例子吧 - -.. code:: go - - package main - - import ( - "fmt" - "sync" - "time" - ) - - func main() { - lock := &sync.RWMutex{} - lock.Lock() - - for i := 0; i < 4; i++ { - go func(i int) { - fmt.Printf("第 %d 个协程准备开始... \n", i) - lock.RLock() - fmt.Printf("第 %d 个协程获得读锁, sleep 1s 后,释放锁\n", i) - time.Sleep(time.Second) - lock.RUnlock() - }(i) - } - - time.Sleep(time.Second * 2) - - fmt.Println("准备释放写锁,读锁不再阻塞") - // 写锁一释放,读锁就自由了 - lock.Unlock() - - // 由于会等到读锁全部释放,才能获得写锁 - // 因为这里一定会在上面 4 个协程全部完成才能往下走 - lock.Lock() - fmt.Println("程序退出...") - lock.Unlock() - } - -输出如下 - -:: - - 第 1 个协程准备开始... - 第 0 个协程准备开始... - 第 3 个协程准备开始... - 第 2 个协程准备开始... - 准备释放写锁,读锁不再阻塞 - 第 2 个协程获得读锁, sleep 1s 后,释放锁 - 第 3 个协程获得读锁, sleep 1s 后,释放锁 - 第 1 个协程获得读锁, sleep 1s 后,释放锁 - 第 0 个协程获得读锁, sleep 1s 后,释放锁 - 程序退出... diff --git a/source/c09/c09_23.md b/source/c09/c09_23.md deleted file mode 100644 index 10868ac..0000000 --- a/source/c09/c09_23.md +++ /dev/null @@ -1,130 +0,0 @@ -# 9.23 panic 和 recover - -编程语言一般都会有异常捕获机制,在 Python 中 是使用`raise` 和 `try-except` 语句来实现的异常抛出和异常捕获的。 - -在 Golang 中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 - -当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 panic,让程序退出停止运行。 - -## 1. 触发panic - -手动触发宕机,是非常简单的一件事,只需要调用 panic 这个内置函数即可,就像这样子 - -```go -package main - -func main() { - panic("crash") -} -``` - -运行后,直接报错宕机 - -```shell -$ go run main.go -go run main.go -panic: crash - -goroutine 1 [running]: -main.main() - E:/Go-Code/main.go:4 +0x40 -exit status 2 -``` - -## 2. 捕获 panic - -发生了异常,有时候就得捕获,就像 Python 中的` except` 一样,那 Golang 中是如何做到的呢? - -这就不得不引出另外一个内建函数 -- `recover`,它可以让程序在发生宕机后起生回生。 - -但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效,其他作用域下,它是不工作的。 - -这是一个简单的例子 - -```go -import "fmt" - -func set_data(x int) { - defer func() { - // recover() 可以将捕获到的panic信息打印 - if err := recover(); err != nil { - fmt.Println(err) - } - }() - - // 故意制造数组越界,触发 panic - var arr [10]int - arr[x] = 88 -} - -func main() { - set_data(20) - - // 如果能执行到这句,说明panic被捕获了 - // 后续的程序能继续运行 - fmt.Println("everything is ok") -} -``` - -运行后,输出如下 - -```go -$ go run main.go -runtime error: index out of range [20] with length 10 -everything is ok -``` - -通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 - -## 3. 无法跨协程 - -从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer 。 - -但是这个 defer 在多个协程之间是没有效果,在子协程里触发 panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer 函数的。 - -来做个实验就知道了 - -```go -import ( - "fmt" - "time" -) - -func main() { - // 这个 defer 并不会执行 - defer fmt.Println("in main") - - go func() { - defer println("in goroutine") - panic("") - }() - - time.Sleep(2 * time.Second) -} -``` - -输出如下 - -``` -in goroutine -panic: - -goroutine 6 [running]: -main.main.func1() - E:/Go-Code/main.go:12 +0x7b -created by main.main - E:/Go-Code/main.go:10 +0xbc -exit status 2 -``` - - - -## 4. 总结一下 - -Golang 异常的抛出与捕获,依赖两个内置函数: - -- panic:抛出异常,使程序崩溃 -- recover:捕获异常,恢复程序或做收尾工作 - -revocer 调用后,抛出的 panic 将会在此处终结,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 - diff --git a/source/c09/c09_23.rst b/source/c09/c09_23.rst deleted file mode 100644 index 4989bf8..0000000 --- a/source/c09/c09_23.rst +++ /dev/null @@ -1,147 +0,0 @@ -9.23 panic 和 recover -===================== - -编程语言一般都会有异常捕获机制,在 Python 中 是使用\ ``raise`` 和 -``try-except`` 语句来实现的异常抛出和异常捕获的。 - -在 Golang -中,有不少常规错误,在编译阶段就能提前告警,比如语法错误或类型错误等,但是有些错误仅能在程序运行后才能发生,比如数组访问越界、空指针引用等,这些运行时错误会引起程序退出。 - -当然能触发程序宕机退出的,也可以是我们自己,比如经过检查判断,当前环境无法达到我们程序进行的预期条件时(比如一个服务指定监听端口被其他程序占用),可以手动触发 -panic,让程序退出停止运行。 - -1. 触发panic ------------- - -手动触发宕机,是非常简单的一件事,只需要调用 panic -这个内置函数即可,就像这样子 - -.. code:: go - - package main - - func main() { - panic("crash") - } - -运行后,直接报错宕机 - -.. code:: shell - - $ go run main.go - go run main.go - panic: crash - - goroutine 1 [running]: - main.main() - E:/Go-Code/main.go:4 +0x40 - exit status 2 - -2. 捕获 panic -------------- - -发生了异常,有时候就得捕获,就像 Python 中的\ ``except`` 一样,那 Golang -中是如何做到的呢? - -这就不得不引出另外一个内建函数 – -``recover``\ ,它可以让程序在发生宕机后起生回生。 - -但是 recover 的使用,有一个条件,就是它必须在 defer -函数中才能生效,其他作用域下,它是不工作的。 - -这是一个简单的例子 - -.. code:: go - - import "fmt" - - func set_data(x int) { - defer func() { - // recover() 可以将捕获到的panic信息打印 - if err := recover(); err != nil { - fmt.Println(err) - } - }() - - // 故意制造数组越界,触发 panic - var arr [10]int - arr[x] = 88 - } - - func main() { - set_data(20) - - // 如果能执行到这句,说明panic被捕获了 - // 后续的程序能继续运行 - fmt.Println("everything is ok") - } - -运行后,输出如下 - -.. code:: go - - $ go run main.go - runtime error: index out of range [20] with length 10 - everything is ok - -通常来说,不应该对进入 panic -宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 -web -服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 -web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。 - -3. 无法跨协程 -------------- - -从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 -defer 延迟函数,还是得执行完 defer 。 - -但是这个 defer 在多个协程之间是没有效果,在子协程里触发 -panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer -函数的。 - -来做个实验就知道了 - -.. code:: go - - import ( - "fmt" - "time" - ) - - func main() { - // 这个 defer 并不会执行 - defer fmt.Println("in main") - - go func() { - defer println("in goroutine") - panic("") - }() - - time.Sleep(2 * time.Second) - } - -输出如下 - -:: - - in goroutine - panic: - - goroutine 6 [running]: - main.main.func1() - E:/Go-Code/main.go:12 +0x7b - created by main.main - E:/Go-Code/main.go:10 +0xbc - exit status 2 - -4. 总结一下 ------------ - -Golang 异常的抛出与捕获,依赖两个内置函数: - -- panic:抛出异常,使程序崩溃 -- recover:捕获异常,恢复程序或做收尾工作 - -revocer 调用后,抛出的 panic 将会在此处终结,不会再外抛,但是 -recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。 diff --git a/source/c09/c09_24.md b/source/c09/c09_24.md deleted file mode 100644 index 36ad998..0000000 --- a/source/c09/c09_24.md +++ /dev/null @@ -1,242 +0,0 @@ -# 9.24 超级详细的Go语言包管理方案演变及 go mod 入门 - -在以前,Go 语言的的包依赖管理一直都被大家所诟病,Go官方也在一直在努力为开发者提供更方便易用的包管理方案,从最初的 GOPATH 到 GO VENDOR,再到最新的 GO Modules,虽然走了不少的弯路,但最终还是拿出了 Go Modules 这样像样的解决方案。 - -目前最主流的包依赖管理方式是使用官方推荐的 Go Modules ,这不前段时间 Go 1.14 版本发布,官方正式放话,强烈推荐你使用 Go Modules,并且有自信可以用于生产中。 - -本文会大篇幅的讲解 Go Modules 的使用,但是在那之前,我仍然会简要介绍一下前两个解决方案 GOPATH 和 go vendor 到底是怎么回事?我认为这是有必要的,因为只有了解它的发展历程,才能知道 Go Modules 的到来是有多么的不容易,多么的意义非凡。 - -## 1. 最古老的 GOPATH - -GOPATH 应该很多人都很眼熟了,之前在配置环境的时候,都配置过吧? - -你可以将其理解为工作目录,在这个工作目录下,通常有如下的目录结构 - -![](http://image.python-online.cn/image-20200311220825614.png) - -每个目录存放的文件,都不相同 - -- bin:存放编译后生成的二进制可执行文件 -- pkg:存放编译后生成的 `.a` 文件 -- src:存放项目的源代码,可以是你自己写的代码,也可以是你 go get 下载的包 - -将你的包或者别人的包全部放在 `$GOPATH/src` 目录下进行管理的方式,我们称之为 GOPATH 模式。 - -在这个模式下,使用 go install 时,生成的可执行文件会放在 `$GOPATH/bin` 下 - -![](http://image.python-online.cn/image-20200312221011685.png) - -如果你安装的是一个库,则会生成 `.a` 文件到 `$GOPATH/pkg` 下对应的平台目录中(由 GOOS 和 GOARCH 组合而成),生成 `.a` 为后缀的文件。 - -![](http://image.python-online.cn/image-20200312221141028.png) - -GOOS,表示的是目标操作系统,有 darwin(Mac),linux,windows,android,netbsd,openbsd,solaris,plan9 等 - -而 GOARCH,表示目标架构,常见的有 arm,amd64 等 - -这两个都是 go env 里的变量,你可以通过 `go env 变量名` 进行查看 - -![](http://image.python-online.cn/image-20200314132614248.png) - -至此,你可能不会觉得上面的方案会产生什么样的问题,直到你开始真正使用 GOPATH 去开发程序,就不得不开始面临各种各样的问题,其中最严重的就是版本管理问题,因为 GOPATH 根本没有版本的概念。 - -以下几点是你使用 GOPATH 一定会碰到的问题: - -- 你无法在你的项目中,使用指定版本的包,因为不同版本的包的导入方法也都一样 -- 其他人运行你的开发的程序时,无法保证他下载的包版本是你所期望的版本,当对方使用了其他版本,有可能导致程序无法正常运行 -- 在本地,一个包只能保留一个版本,意味着你在本地开发的所有项目,都得用同一个版本的包,这几乎是不可能的。 - - - -## 2. go vendor 模式的过渡 - -为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 开始支持 vendor 。 - -以前使用 GOPATH 的时候,所有的项目都共享一个 GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH 模式下只能有一个版本的第三方库。 - -解决的思路就是,在每个项目下都创建一个 vendor 目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5 的 Go 在你设置了开启 `GO15VENDOREXPERIMENT=1` (注:这个变量在 v1.6 版本默认为1,但是在 v1.7 后,已去掉该环境变量,默认开启 `vendor` 特性,无需你手动设置)后,会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。 - -其搜索包的优先级顺序,由高到低是这样的 - -- 当前包下的 vendor 目录 -- 向上级目录查找,直到找到 src 下的 vendor 目录 -- 在 GOROOT 目录下查找 -- 在 GOPATH 下面查找依赖包 - -虽然这个方案解决了一些问题,但是解决得并不完美。 - -- 如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 - -- 并且如果要分享开源你的项目,你需要将你的所有的依赖包悉数上传,别人使用的时候,除了你的项目源码外,还有所有的依赖包全部下载下来,才能保证别人使用的时候,不会因为版本问题导致项目不能如你预期那样正常运行。 - -这些看似不是问题的问题,会给我们的开发使用过程变得非常难受,虽然我是初学者,还未使用过 go vendor,但能有很明显的预感,这个方案照样会另我崩溃。 - -## 3. go mod 的横空出世 - -go modules 在 v1.11 版本正式推出,在最新发布的 v1.14 版本中,官方正式发话,称其已经足够成熟,可以应用于生产上。 - -从 v1.11 开始,`go env` 多了个环境变量: `GO111MODULE` ,这里的 111,其实就是 v1.11 的象征标志, go 里好像很喜欢这样的命名方式,比如当初 vendor 出现的时候,也多了个 `GO15VENDOREXPERIMENT `环境变量,其中 15,表示的vendor 是在 v1.5 时才诞生的。 - -`GO111MODULE` 是一个开关,通过它可以开启或关闭 go mod 模式。 - -它有三个可选值:`off`、`on`、`auto`,默认值是`auto`。 - -1. `GO111MODULE=off`禁用模块支持,编译时会从`GOPATH`和`vendor`文件夹中查找包。 -2. `GO111MODULE=on`启用模块支持,编译时会忽略`GOPATH`和`vendor`文件夹,只根据 `go.mod`下载依赖。 -3. `GO111MODULE=auto`,当项目在`$GOPATH/src `外且项目根目录有`go.mod`文件时,自动开启模块支持。 - -go mod 出现后, GOPATH(肯定没人使用了) 和 GOVENDOR 将会且正在被逐步淘汰,但是若你的项目仍然要使用那些即将过时的包依赖管理方案,请注意将 GO111MODULE 置为 off。 - -具体怎么设置呢?可以使用 go env 的命令,如我要开启 go mod ,就使用这条命令 - -```shell -$ go env -w GO111MODULE="on" -``` - - - -## 4. go mod 依赖的管理 - -接下来,来演示一下 go modules 是如何来管理包依赖的。 - -go mod 不再依靠 $GOPATH,使得它可以脱离 GOPATH 来创建项目,于是我们在家目录下创建一个 go_test 的目录,用来创建我的项目,详细操作如下: - -![](http://image.python-online.cn/image-20200314000227914.png) - -接下来,进入项目目录,执行如下命令进行 go modules 的初始化 - -![](http://image.python-online.cn/image-20200314000940825.png) - -接下来很重要的一点,我们要看看 go install 把下载的包安装到哪里了? - -![](http://image.python-online.cn/image-20200314001426817.png) - -上面我们观察到,在使用 go modules 模式后,项目目录下会多生成两个文件也就是 `go.mod` 和 `go.sum` 。 - -这两个文件是 go modules 的核心所在,这里不得不好好介绍一下。 - -![](http://image.python-online.cn/image-20200314001708640.png) - -### go.mod 文件 - -go.mod 的内容比较容易理解 - -- 第一行:模块的引用路径 -- 第二行:项目使用的 go 版本 -- 第三行:项目所需的直接依赖包及其版本 - -在实际应用上,你会看见更复杂的 go.mod 文件,比如下面这样 - -``` -module github.com/BingmingWong/module-test - -go 1.14 - -require ( - example.com/apple v0.1.2 - example.com/banana v1.2.3 - example.com/banana/v2 v2.3.4 - example.com/pear // indirect - example.com/strawberry // incompatible -) - -exclude example.com/banana v1.2.4 -replace( - golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac - golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d - golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 -) -``` - -主要是多出了两个 flag: - -- `exclude`: 忽略指定版本的依赖包 -- `replace`:由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 - -### go.sum 文件 - -反观 go.sum 文件,就比较复杂了,密密麻麻的。 - -可以看到,内容虽然多,但是也不难理解 - -每一行都是由 `模块路径`,`模块版本`,`哈希检验值` 组成,其中哈希检验值是用来保证当前缓存的模块不会被篡改。hash 是以`h1:`开头的字符串,表示生成checksum的算法是第一版的hash算法(sha256)。 - -值得注意的是,为什么有的包只有一行 - -``` - /go.mod -``` - -而有的包却有两行呢 - -``` - - /go.mod -``` - -那些有两行的包,区别就在于 hash 值不一行,一个是 `h1:hash`,一个是 `go.mod h1:hash` - -而 `h1:hash` 和 `go.mod h1:hash`两者,要不就是同时存在,要不就是只存在 `go.mod h1:hash`。那什么情况下会不存在 `h1:hash` 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的` h1 hash`,就会出现不存在 `h1 hash`,只存在 `go.mod h1:hash` 的情况。[引用自 3] - -go.mod 和 go.sum 是 go modules 版本管理的指导性文件,因此 go.mod 和 go.sum 文件都应该提交到你的 Git 仓库中去,避免其他人使用你写项目时,重新生成的go.mod 和 go.sum 与你开发的基准版本的不一致。 - -## 5. go mod 命令的使用 - - -- `go mod init`:初始化go mod, 生成go.mod文件,后可接参数指定 module 名,上面已经演示过。 - -- `go mod download`:手动触发下载依赖包到本地cache(默认为`$GOPATH/pkg/mod`目录) - -- `go mod graph`: 打印项目的模块依赖结构 - -![](http://image.python-online.cn/image-20200314003442400.png) - -- `go mod tidy` :添加缺少的包,且删除无用的包 - -- `go mod verify` :校验模块是否被篡改过 - -- `go mod why`: 查看为什么需要依赖 - -- `go mod vendor` :导出项目所有依赖到vendor下 - -![](http://image.python-online.cn/image-20200314003913527.png) - - - `go mod edit` :编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 -require=golang.org/x/text 添加依赖,接 -droprequire=golang.org/x/text 删除依赖,详情可参考 `go help mod edit ` - -![](http://image.python-online.cn/image-20200314004643487.png) - -- `go list -m -json all`:以 json 的方式打印依赖详情 - -![](http://image.python-online.cn/image-20200314005924877.png) - - - - -如何给项目添加依赖(写进 go.mod)呢? - -有两种方法: - -- 你只要在项目中有 import,然后 go build 就会 go module 就会自动下载并添加。 -- 自己手工使用 go get 下载安装后,会自动写入 go.mod 。 - -![](http://image.python-online.cn/image-20200314005217447.png) - - - -## 7. 总结写在最后 - -如果让我用一段话来评价 GOPATH 和 go vendor,我会说 - -GOPATH 做为 Golang 的第一个包管理模式,只能保证你能用,但不保证好用,而 go vendor 解决了 GOPATH 忽视包版的本管理,保证好用,但是还不够好用,直到 go mod 的推出后,才使 Golang 包的依赖管理有了一个能让 Gopher 都统一比较满意的方案,达到了能用且好用的标准。 - -如果是刚开始学习 Golang ,那么 GOPATH 和 go vendor 可以做适当了解,不必深入研究,除非你要接手的项目由于一些历史原因仍然在使用 go vender 械管理,除此之外,任何 Gopher 应该从此刻就投入 go modules 的怀抱。 - -以上是我在这几天的学习总结,希望对还未入门阶段的你,有所帮助。另外,本篇文章如有写得不对的,请后台批评指正,以免误导其他朋友,非常感谢。 - -## 8. 推荐参考文章 - -- [Go语言之依赖管理](https://www.cnblogs.com/Dr-wei/p/11742253.html) -- [Go 包依赖管理工具 —— govendor](https://shockerli.net/post/go-package-manage-tool-govendor/) -- [Go Modules 终极入门](https://mp.weixin.qq.com/s/fNMXfpBhBC3UWTbYCnwIMg) (强烈推荐煎鱼大佬的文章) - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) diff --git a/source/c09/c09_24.rst b/source/c09/c09_24.rst deleted file mode 100644 index 066879d..0000000 --- a/source/c09/c09_24.rst +++ /dev/null @@ -1,323 +0,0 @@ -9.24 超级详细的Go语言包管理方案演变及 go mod 入门 -================================================= - -在以前,Go -语言的的包依赖管理一直都被大家所诟病,Go官方也在一直在努力为开发者提供更方便易用的包管理方案,从最初的 -GOPATH 到 GO VENDOR,再到最新的 GO -Modules,虽然走了不少的弯路,但最终还是拿出了 Go Modules -这样像样的解决方案。 - -目前最主流的包依赖管理方式是使用官方推荐的 Go Modules ,这不前段时间 Go -1.14 版本发布,官方正式放话,强烈推荐你使用 Go -Modules,并且有自信可以用于生产中。 - -本文会大篇幅的讲解 Go Modules -的使用,但是在那之前,我仍然会简要介绍一下前两个解决方案 GOPATH 和 go -vendor -到底是怎么回事?我认为这是有必要的,因为只有了解它的发展历程,才能知道 -Go Modules 的到来是有多么的不容易,多么的意义非凡。 - -1. 最古老的 GOPATH ------------------- - -GOPATH 应该很多人都很眼熟了,之前在配置环境的时候,都配置过吧? - -你可以将其理解为工作目录,在这个工作目录下,通常有如下的目录结构 - -|image0| - -每个目录存放的文件,都不相同 - -- bin:存放编译后生成的二进制可执行文件 -- pkg:存放编译后生成的 ``.a`` 文件 -- src:存放项目的源代码,可以是你自己写的代码,也可以是你 go get - 下载的包 - -将你的包或者别人的包全部放在 ``$GOPATH/src`` -目录下进行管理的方式,我们称之为 GOPATH 模式。 - -在这个模式下,使用 go install 时,生成的可执行文件会放在 ``$GOPATH/bin`` -下 - -|image1| - -如果你安装的是一个库,则会生成 ``.a`` 文件到 ``$GOPATH/pkg`` -下对应的平台目录中(由 GOOS 和 GOARCH 组合而成),生成 ``.a`` -为后缀的文件。 - -|image2| - -GOOS,表示的是目标操作系统,有 -darwin(Mac),linux,windows,android,netbsd,openbsd,solaris,plan9 -等 - -而 GOARCH,表示目标架构,常见的有 arm,amd64 等 - -这两个都是 go env 里的变量,你可以通过 ``go env 变量名`` 进行查看 - -|image3| - -至此,你可能不会觉得上面的方案会产生什么样的问题,直到你开始真正使用 -GOPATH -去开发程序,就不得不开始面临各种各样的问题,其中最严重的就是版本管理问题,因为 -GOPATH 根本没有版本的概念。 - -以下几点是你使用 GOPATH 一定会碰到的问题: - -- 你无法在你的项目中,使用指定版本的包,因为不同版本的包的导入方法也都一样 -- 其他人运行你的开发的程序时,无法保证他下载的包版本是你所期望的版本,当对方使用了其他版本,有可能导致程序无法正常运行 -- 在本地,一个包只能保留一个版本,意味着你在本地开发的所有项目,都得用同一个版本的包,这几乎是不可能的。 - -2. go vendor 模式的过渡 ------------------------ - -为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 -开始支持 vendor 。 - -以前使用 GOPATH 的时候,所有的项目都共享一个 -GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH -模式下只能有一个版本的第三方库。 - -解决的思路就是,在每个项目下都创建一个 vendor -目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5 -的 Go 在你设置了开启 ``GO15VENDOREXPERIMENT=1`` (注:这个变量在 v1.6 -版本默认为1,但是在 v1.7 后,已去掉该环境变量,默认开启 ``vendor`` -特性,无需你手动设置)后,会提升 vendor -目录的依赖包搜索路径的优先级(相较于 GOPATH)。 - -其搜索包的优先级顺序,由高到低是这样的 - -- 当前包下的 vendor 目录 -- 向上级目录查找,直到找到 src 下的 vendor 目录 -- 在 GOROOT 目录下查找 -- 在 GOPATH 下面查找依赖包 - -虽然这个方案解决了一些问题,但是解决得并不完美。 - -- 如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 - -- 并且如果要分享开源你的项目,你需要将你的所有的依赖包悉数上传,别人使用的时候,除了你的项目源码外,还有所有的依赖包全部下载下来,才能保证别人使用的时候,不会因为版本问题导致项目不能如你预期那样正常运行。 - -这些看似不是问题的问题,会给我们的开发使用过程变得非常难受,虽然我是初学者,还未使用过 -go vendor,但能有很明显的预感,这个方案照样会另我崩溃。 - -3. go mod 的横空出世 --------------------- - -go modules 在 v1.11 版本正式推出,在最新发布的 v1.14 -版本中,官方正式发话,称其已经足够成熟,可以应用于生产上。 - -从 v1.11 开始,\ ``go env`` 多了个环境变量: ``GO111MODULE`` ,这里的 -111,其实就是 v1.11 的象征标志, go 里好像很喜欢这样的命名方式,比如当初 -vendor 出现的时候,也多了个 ``GO15VENDOREXPERIMENT``\ 环境变量,其中 -15,表示的vendor 是在 v1.5 时才诞生的。 - -``GO111MODULE`` 是一个开关,通过它可以开启或关闭 go mod 模式。 - -它有三个可选值:\ ``off``\ 、\ ``on``\ 、\ ``auto``\ ,默认值是\ ``auto``\ 。 - -1. ``GO111MODULE=off``\ 禁用模块支持,编译时会从\ ``GOPATH``\ 和\ ``vendor``\ 文件夹中查找包。 -2. ``GO111MODULE=on``\ 启用模块支持,编译时会忽略\ ``GOPATH``\ 和\ ``vendor``\ 文件夹,只根据 - ``go.mod``\ 下载依赖。 -3. ``GO111MODULE=auto``\ ,当项目在\ ``$GOPATH/src``\ 外且项目根目录有\ ``go.mod``\ 文件时,自动开启模块支持。 - -go mod 出现后, GOPATH(肯定没人使用了) 和 GOVENDOR -将会且正在被逐步淘汰,但是若你的项目仍然要使用那些即将过时的包依赖管理方案,请注意将 -GO111MODULE 置为 off。 - -具体怎么设置呢?可以使用 go env 的命令,如我要开启 go mod -,就使用这条命令 - -.. code:: shell - - $ go env -w GO111MODULE="on" - -4. go mod 依赖的管理 --------------------- - -接下来,来演示一下 go modules 是如何来管理包依赖的。 - -go mod 不再依靠 $GOPATH,使得它可以脱离 GOPATH -来创建项目,于是我们在家目录下创建一个 go_test -的目录,用来创建我的项目,详细操作如下: - -|image4| - -接下来,进入项目目录,执行如下命令进行 go modules 的初始化 - -|image5| - -接下来很重要的一点,我们要看看 go install 把下载的包安装到哪里了? - -|image6| - -上面我们观察到,在使用 go modules -模式后,项目目录下会多生成两个文件也就是 ``go.mod`` 和 ``go.sum`` 。 - -这两个文件是 go modules 的核心所在,这里不得不好好介绍一下。 - -|image7| - -go.mod 文件 -~~~~~~~~~~~ - -go.mod 的内容比较容易理解 - -- 第一行:模块的引用路径 -- 第二行:项目使用的 go 版本 -- 第三行:项目所需的直接依赖包及其版本 - -在实际应用上,你会看见更复杂的 go.mod 文件,比如下面这样 - -:: - - module github.com/BingmingWong/module-test - - go 1.14 - - require ( - example.com/apple v0.1.2 - example.com/banana v1.2.3 - example.com/banana/v2 v2.3.4 - example.com/pear // indirect - example.com/strawberry // incompatible - ) - - exclude example.com/banana v1.2.4 - replace( - golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac - golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d - golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 - ) - -主要是多出了两个 flag: - -- ``exclude``\ : 忽略指定版本的依赖包 -- ``replace``\ :由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 - -go.sum 文件 -~~~~~~~~~~~ - -反观 go.sum 文件,就比较复杂了,密密麻麻的。 - -可以看到,内容虽然多,但是也不难理解 - -每一行都是由 ``模块路径``\ ,\ ``模块版本``\ ,\ ``哈希检验值`` -组成,其中哈希检验值是用来保证当前缓存的模块不会被篡改。hash -是以\ ``h1:``\ 开头的字符串,表示生成checksum的算法是第一版的hash算法(sha256)。 - -值得注意的是,为什么有的包只有一行 - -:: - - /go.mod - -而有的包却有两行呢 - -:: - - - /go.mod - -那些有两行的包,区别就在于 hash 值不一行,一个是 ``h1:hash``\ ,一个是 -``go.mod h1:hash`` - -而 ``h1:hash`` 和 -``go.mod h1:hash``\ 两者,要不就是同时存在,要不就是只存在 -``go.mod h1:hash``\ 。那什么情况下会不存在 ``h1:hash`` 呢,就是当 Go -认为肯定用不到某个模块版本的时候就会省略它的\ ``h1 hash``\ ,就会出现不存在 -``h1 hash``\ ,只存在 ``go.mod h1:hash`` 的情况。[引用自 3] - -go.mod 和 go.sum 是 go modules 版本管理的指导性文件,因此 go.mod 和 -go.sum 文件都应该提交到你的 Git -仓库中去,避免其他人使用你写项目时,重新生成的go.mod 和 go.sum -与你开发的基准版本的不一致。 - -5. go mod 命令的使用 --------------------- - -- ``go mod init``\ :初始化go mod, 生成go.mod文件,后可接参数指定 - module 名,上面已经演示过。 - -- ``go mod download``\ :手动触发下载依赖包到本地cache(默认为\ ``$GOPATH/pkg/mod``\ 目录) - -- ``go mod graph``\ : 打印项目的模块依赖结构 - -|image8| - -- ``go mod tidy`` :添加缺少的包,且删除无用的包 - -- ``go mod verify`` :校验模块是否被篡改过 - -- ``go mod why``\ : 查看为什么需要依赖 - -- ``go mod vendor`` :导出项目所有依赖到vendor下 - -|image9| - -- ``go mod edit`` :编辑go.mod文件,接 -fmt 参数格式化 go.mod 文件,接 - -require=golang.org/x/text 添加依赖,接 - -droprequire=golang.org/x/text 删除依赖,详情可参考 - ``go help mod edit`` - -|image10| - -- ``go list -m -json all``\ :以 json 的方式打印依赖详情 - -|image11| - -如何给项目添加依赖(写进 go.mod)呢? - -有两种方法: - -- 你只要在项目中有 import,然后 go build 就会 go module - 就会自动下载并添加。 -- 自己手工使用 go get 下载安装后,会自动写入 go.mod 。 - -|image12| - -7. 总结写在最后 ---------------- - -如果让我用一段话来评价 GOPATH 和 go vendor,我会说 - -GOPATH 做为 Golang 的第一个包管理模式,只能保证你能用,但不保证好用,而 -go vendor 解决了 GOPATH 忽视包版的本管理,保证好用,但是还不够好用,直到 -go mod 的推出后,才使 Golang 包的依赖管理有了一个能让 Gopher -都统一比较满意的方案,达到了能用且好用的标准。 - -如果是刚开始学习 Golang ,那么 GOPATH 和 go vendor -可以做适当了解,不必深入研究,除非你要接手的项目由于一些历史原因仍然在使用 -go vender 械管理,除此之外,任何 Gopher 应该从此刻就投入 go modules -的怀抱。 - -以上是我在这几天的学习总结,希望对还未入门阶段的你,有所帮助。另外,本篇文章如有写得不对的,请后台批评指正,以免误导其他朋友,非常感谢。 - -8. 推荐参考文章 ---------------- - -- `Go语言之依赖管理 `__ -- `Go 包依赖管理工具 —— - govendor `__ -- `Go Modules - 终极入门 `__ - (强烈推荐煎鱼大佬的文章) - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - - -.. |image0| image:: http://image.python-online.cn/image-20200311220825614.png -.. |image1| image:: http://image.python-online.cn/image-20200312221011685.png -.. |image2| image:: http://image.python-online.cn/image-20200312221141028.png -.. |image3| image:: http://image.python-online.cn/image-20200314132614248.png -.. |image4| image:: http://image.python-online.cn/image-20200314000227914.png -.. |image5| image:: http://image.python-online.cn/image-20200314000940825.png -.. |image6| image:: http://image.python-online.cn/image-20200314001426817.png -.. |image7| image:: http://image.python-online.cn/image-20200314001708640.png -.. |image8| image:: http://image.python-online.cn/image-20200314003442400.png -.. |image9| image:: http://image.python-online.cn/image-20200314003913527.png -.. |image10| image:: http://image.python-online.cn/image-20200314004643487.png -.. |image11| image:: http://image.python-online.cn/image-20200314005924877.png -.. |image12| image:: http://image.python-online.cn/image-20200314005217447.png - diff --git a/source/c09/c09_25.rst b/source/c09/c09_25.rst deleted file mode 100644 index 5b4bad2..0000000 --- a/source/c09/c09_25.rst +++ /dev/null @@ -1,187 +0,0 @@ -9.25 Go语言的包依赖管理 -======================= - -在以前,Go -语言的的包依赖管理一直都被大家所诟病,但最近几年,这个窘境开始得到缓解。 - -现在最主流的包依赖管理方式是使用官方推荐的 go module -的方式,如果你和我一样是刚开始学习Go语言的,建议也了解一下Go语言包依赖管理方案,是如何一步一步发展到今天的。 - -简单来说,Go语言的包依赖管理方案,可以分为三个阶段。 - -**第一阶段**\ :使用最古老的 GOPATH 进行管理 --------------------------------------------- - -使用 GOPATH -的话,你需要将你所有的第三方库都下载到本地,这本身没有什么问题,其他语言也都是这么做的,问题就在于,在本地只能保存一个版本的第三方库,如果你的机器上有多个项目,而这些项目是基于不同版本的库进行开发的,那就尴尬了。 - -**第二阶段**\ :使用 GOVENDOR 解决方案 --------------------------------------- - -为了解决 GOPATH 方案下不同项目下无法使用多个版本库的问题,Go v1.5 -开始支持 vendor 。 - -以前使用 GOPATH 的时候,所有的项目都共享一个 -GOPATH,需要导入依赖的时候,都来这里找,正所谓一山不容二虎,在 GOPATH -下只能有一个版本的第三方库。 - -解决的思路就是,在每个项目下都创建一个 vendor -目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包互不影响。在编译时,v1.5+ -的Go 会提升 vendor 目录的依赖包搜索路径的优先级(相较于 GOPATH)。如果在 -vendor 目录下没有找到,才会去 ``$GOAPTH/src`` 中去查找 - -在这个阶段,也催生出了各种第三方的管理包依赖插件:godep,dep,glide - -使用 godep 的开发流程是这样的: - -1. 先保证程序在本地能够正常编译 -2. 执行\ ``godep save``\ 保存当前项目的所有第三方依赖的版本信息和代码到 - ``Godeps/Godeps.json``\ 文件中 -3. 提交Godeps目录和vender目录到代码库。 -4. 如果要更新依赖的版本,可以直接修改\ ``Godeps.json``\ 文件中的对应项 - -虽然这个方案解决了一些问题,但是解决得并不完美。 - -如果多个项目用到了同一个包的同一个版本,这个包会存在于该机器上的不同目录下,不仅对磁盘空间是一种浪费,而且没法对第三方包进行集中式的管理(分散在各个角落)。 - -**第三阶段**\ :使用 GO MODULE 版本管理工具 -------------------------------------------- - -go module 在 v1.11 版本推出,并在 -v1.13版本中,成为官方默认的包依赖管理工具。 - -v1.11 开始,\ ``go env`` 多了个环境变量: -``GO111MODULE``\ ,这是一个开关,通过它可以开启或关闭模块支持,它有三个可选值:\ ``off``\ 、\ ``on``\ 、\ ``auto``\ ,默认值是\ ``auto``\ 。 - -1. ``GO111MODULE=off``\ 禁用模块支持,编译时会从\ ``GOPATH``\ 和\ ``vendor``\ 文件夹中查找包。 -2. ``GO111MODULE=on``\ 启用模块支持,编译时会忽略\ ``GOPATH``\ 和\ ``vendor``\ 文件夹,只根据 - ``go.mod``\ 下载依赖。 -3. ``GO111MODULE=auto``\ ,当项目在\ ``$GOPATH/src``\ 外且项目根目录有\ ``go.mod``\ 文件时,开启模块支持。 - -go mod 出现后, GOPATH 和 GOVENDOR -将被逐步淘汰,但是若你的项目仍然要使用那些 out -的包依赖管理方案,需要注意将 GO111MODULE 置为 off。 - -接下来,介绍一下,如何使用 go module 来管理依赖。 - -使用 go module -管理依赖后会在项目根目录下生成两个文件\ ``go.mod``\ 和\ ``go.sum``\ 。 - -``go.mod`` 文件记录了项目所有的依赖信息,其结构大致如下: - -:: - - module github.com/Q1mi/studygo/blogger - - go 1.12 - - require ( - github.com/DeanThompson/ginpprof v0.0.0-20190408063150-3be636683586 - github.com/gin-gonic/gin v1.4.0 - github.com/go-sql-driver/mysql v1.4.1 - github.com/jmoiron/sqlx v1.2.0 - github.com/satori/go.uuid v1.2.0 - google.golang.org/appengine v1.6.1 // indirect - ) - -其中, - -- ``module``\ 用来定义包名 -- ``require``\ 用来定义依赖包及版本 -- ``indirect``\ 表示间接引用 - -而 ``go.sum`` 记录该项目中每个依赖库的版本和哈希值。 - -使用 go module 需熟悉 go mod 的命令 - -:: - - go mod init 初始化当前文件夹, 创建go.mod文件,后可接参数指定 module 名 - go mod graph 打印模块依赖图 - go mod tidy 增加缺少的module,删除无用的module - go mod vendor 将依赖复制到vendor下 - go mod verify 校验依赖 - go mod why 解释为什么需要依赖 - go mod download 下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录) - - go mod edit 编辑go.mod文件 - 接 -fmt 参数格式化 go.mod 文件 - 接 -require=golang.org/x/text 添加依赖 - 接 -droprequire=golang.org/x/text 删除依赖 - 更加用法,参看 go help mod edit - -如何给项目添加依赖(下载包并将依赖写进go.mod文件)? - -有两种方法: - -- 你只要在项目中有 import,然后 go build 就会 go module - 就会自动下载并添加。 - -- 自己手工使用 go get 下载,支持语义化版本号 - -.. code:: shell - - # 拉取最新 - go get github.com/foo - - # 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) - go get -u github.com/foo - - # 升级到最新的修订版本 - go get -u=patch github.com/foo - - # 指定版本 - go get github.com/foo@v1.2.3 - - # 指定分支 - go get github.com/foo@master - - # 指定git提交的hash值 - go get github.com/foo@e3702bed2 - - # 指定版本 - go get github.com/foo@v1.11.0 - -使用以上方式添加完依赖后,在 go.mod 文件里会有你所依赖的包及其版本。 - -如果项目下已经有这个 go.mod 文件,但是包还没拉取,如何 -触发下载呢?两种方法 - -- 可以执行命令 ``go build ./...``\ ,go module 会自动下载 -- 使用 ``go mod download`` ,手动下载 - -如果你连这个 go.mod 文件都丢失了,那怎么生成?两种方法 - -- 使用 Goland 的话,可以点击 create go.mod 文件来生成。 -- 也可以在项目目录下执行这条命令 - -.. code:: shell - - $ go mod init - -由于在国内访问golang.org/x的各个包都需要翻墙,你可以在go.mod中使用replace替换成github上对应的库。 - -:: - - replace ( - golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac - golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d - golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 - ) - -以上几种解决方案,不同之处就在于它们的依赖包的搜索路径优先级不同 - -- 使用 go mod,只在 ``$GOPATH/pkg/mod`` 查找依赖包(GO111MODULE=on) -- 使用 GOVENDOR,优先在 vendor目录中查找,然后才去 ``$GOROOT/src`` 和 - ``$GOPATH/src``\ 查找 -- 使用 GOPATH,先去\ ``$GOROOT/src`` 查找 ,找不到再去 ``$GOPATH/src`` - 中查找 - -参考文章: ----------- - -- `Go语言之依赖管理 `__ - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_26.md b/source/c09/c09_26.md deleted file mode 100644 index b50f796..0000000 --- a/source/c09/c09_26.md +++ /dev/null @@ -1,177 +0,0 @@ -# 9.26 Go语言命名编码规范 - -每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 - -以下内容整理自:[Go语言(Golang)编码规范](https://www.bookstack.cn/books/go-code-convention) - -命名规范分为以下几点 - -**1. 文件命名** - -文件名应一律使用小写(因为Windows文件名不区分大小写?), 不同单词之间用下划线分割。 - -应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 MyBlog的入口应当为 myblog.go - -**2. 常量命名 ** - -- 常量均需使用全部大写字母组成,并使用下划线分词: - - ```go - const APP_VER = "0.7.0.1110 Beta" - ``` - -- 如果是枚举类型的常量,需要先创建相应类型: - - ```go - type Scheme string - const ( - HTTP Scheme = "http" - HTTPS Scheme = "https" - ) - ``` - -- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: - - ```go - type PullRequestStatus int - const ( - PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota - PULL_REQUEST_STATUS_CHECKING - PULL_REQUEST_STATUS_MERGEABLE - ) - ``` - -**3. 变量命名** - -使用驼峰命名法 - -- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u -- 若该变量为 bool 类型,则名称应以 `Has`, `Is`, `Can` 或 `Allow` 开头。例如:isExist ,hasConflict 。 -- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 startDate 。 -- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 `apiClient`。 -- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient - -下面列举了一些常见的特有名词: - -``` -// A GonicMapper that contains a list of common initialisms taken from golang/lint -var LintGonicMapper = GonicMapper{ - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SSH": true, - "TLS": true, - "TTL": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XSRF": true, - "XSS": true, -} -``` - - - -**接口命名** - -使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 - -```go -type helloWorld interface { - func Hello(); -} - -type SayHello helloWorld -``` - - - -**注释规范** - -单行注释使用 `//` ,多行注释使用 `/* comment */` - -```go -// go语言 - -/* -Go 语言 -Hello, World -*/ -``` - -- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 - -- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 - -- 包、函数、方法和类型的注释说明都是一个完整的句子。 - -- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 - -- 注释的单行长度不能超过 80 个字符。 - -- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 - -- 如果是特别复杂的包,可单独创建 doc.go 文件说明 - -- 类型的定义一般都以单数形式描述: - - ```go - // Request represents a request to run a command. type Request struct { ... - ``` - -- 如果为接口,则一般以以下形式描述: - - ```go - // FileInfo is the interface that describes a file and is returned by Stat and Lstat. - type FileInfo interface { ... - ``` - -- 函数与方法的注释需以函数或方法的名称作为开头: - - ```go - // Post returns *BeegoHttpRequest with POST method. - ``` - -- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: - - ```go - // Copy copies file from source to target path. - // It returns false and error when error occurs in underlying function calls. - ``` - -- 若函数或方法为判断类型(返回值主要为 `bool` 类型),则以 ` returns true if` 开头: - - ```go - // HasPrefix returns true if name has any string in given slice as prefix. - func HasPrefix(name string, prefixes []string) bool { ... - ``` - -特别注释 - -- TODE:提醒维护人员此部分代码待完成 -- FIXME:提醒维护人员此处有BUG待修复 -- NOTE:维护人员要关注的一些问题说明 - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_26.rst b/source/c09/c09_26.rst deleted file mode 100644 index cfab7c4..0000000 --- a/source/c09/c09_26.rst +++ /dev/null @@ -1,182 +0,0 @@ -9.26 Go语言命名编码规范 -======================= - -每个语言都有自己特色的命名规范,学习该语言的命名规范,能让你写出来的代码更加易读。 - -以下内容整理自:\ `Go语言(Golang)编码规范 `__ - -命名规范分为以下几点 - -**1. 文件命名** - -文件名应一律使用小写(因为Windows文件名不区分大小写?), -不同单词之间用下划线分割。 - -应用的主入口应当为 main.go ,或者为应用名的全小写形式,比如 -MyBlog的入口应当为 myblog.go - -**2. 常量命名** - -- 常量均需使用全部大写字母组成,并使用下划线分词: - - .. code:: go - - const APP_VER = "0.7.0.1110 Beta" - -- 如果是枚举类型的常量,需要先创建相应类型: - - .. code:: go - - type Scheme string - const ( - HTTP Scheme = "http" - HTTPS Scheme = "https" - ) - -- 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀: - - .. code:: go - - type PullRequestStatus int - const ( - PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota - PULL_REQUEST_STATUS_CHECKING - PULL_REQUEST_STATUS_MERGEABLE - ) - -**3. 变量命名** - -使用驼峰命名法 - -- 在相对简单的环境(对象数量少、针对性强)中,可以将完整单词简写为单个字母,例如:user写为u -- 若该变量为 bool 类型,则名称应以 ``Has``, ``Is``, ``Can`` 或 - ``Allow`` 开头。例如:isExist ,hasConflict 。 -- 其他一般情况下首单词全小写,其后各单词首字母大写。例如:numShips 和 - startDate 。 -- 若变量中有特有名词(以下列出),且变量为私有,则首单司还是使用全小写,如 - ``apiClient``\ 。 -- 若变量中有特有名词(以下列出),那首单词就要变成全大写。例如:APIClient - -下面列举了一些常见的特有名词: - -:: - - // A GonicMapper that contains a list of common initialisms taken from golang/lint - var LintGonicMapper = GonicMapper{ - "API": true, - "ASCII": true, - "CPU": true, - "CSS": true, - "DNS": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SSH": true, - "TLS": true, - "TTL": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "XML": true, - "XSRF": true, - "XSS": true, - } - -**接口命名** - -使用驼峰命名法,可以用 type alias 来定义大写开头的type 给包外访问。 - -.. code:: go - - type helloWorld interface { - func Hello(); - } - - type SayHello helloWorld - -**注释规范** - -单行注释使用 ``//`` ,多行注释使用 ``/* comment */`` - -.. code:: go - - // go语言 - - /* - Go 语言 - Hello, World - */ - -- 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。 - -- 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。 - -- 包、函数、方法和类型的注释说明都是一个完整的句子。 - -- 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。 - -- 注释的单行长度不能超过 80 个字符。 - -- 包级别的注释说明,只需要在一个源文件中注释即可,并且放在 package 之前 - -- 如果是特别复杂的包,可单独创建 doc.go 文件说明 - -- 类型的定义一般都以单数形式描述: - - .. code:: go - - // Request represents a request to run a command. type Request struct { ... - -- 如果为接口,则一般以以下形式描述: - - .. code:: go - - // FileInfo is the interface that describes a file and is returned by Stat and Lstat. - type FileInfo interface { ... - -- 函数与方法的注释需以函数或方法的名称作为开头: - - .. code:: go - - // Post returns *BeegoHttpRequest with POST method. - -- 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述: - - .. code:: go - - // Copy copies file from source to target path. - // It returns false and error when error occurs in underlying function calls. - -- 若函数或方法为判断类型(返回值主要为 ``bool`` 类型),则以 - `` returns true if`` 开头: - - .. code:: go - - // HasPrefix returns true if name has any string in given slice as prefix. - func HasPrefix(name string, prefixes []string) bool { ... - -特别注释 - -- TODE:提醒维护人员此部分代码待完成 -- FIXME:提醒维护人员此处有BUG待修复 -- NOTE:维护人员要关注的一些问题说明 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_27.md b/source/c09/c09_27.md deleted file mode 100644 index 872c19b..0000000 --- a/source/c09/c09_27.md +++ /dev/null @@ -1,197 +0,0 @@ -# 9.27 go 命令详解 - -## 1. 环境变量 - -`go env` - -![仅截取部分内容](/Users/MING/Library/Application Support/typora-user-images/image-20200311221418584.png) - - - -## 2. 执行 Go 程序 - -当前热门的编程语言 Python ,可以不用编译成 二进制文件,就可以直接运行。 - -但 Go 语言程序的执行,必须得先编译再执行。通常来说有如下两种方法 - -1. 先使用 go build 编译成二进制文件,再执行这个二进制文件 - - ![image-20200313222620374](/Users/MING/Library/Application Support/typora-user-images/image-20200313222620374.png) - -2. 使用 go run “直接”运行,这个命令还是会去编译,但是不会在当前目录下生成二进制文件,而是编译成临时文件后直接运行。 - - ![image-20200313222710998](/Users/MING/Library/Application Support/typora-user-images/image-20200313222710998.png) - -## 3. 编译文件 - -将 `.go` 文件编译成可执行文件,可以使用 `go build` - -如下图所示,helloworld 文件夹下,包含两个 `.go` 文件,它们都归属于同一个包。 - -当使用 `go build` 可指定包里所有的文件,就你下面这样,默认会以第一个文件(main.go)名生成可执行文件(main)。 - -![image-20200312201759541](/Users/MING/Library/Application Support/typora-user-images/image-20200312201759541.png) - -当然,你也可以不指定,此时生成的可执行文件是以 文件夹名命名 - -![image-20200312202032363](/Users/MING/Library/Application Support/typora-user-images/image-20200312202032363.png) - -当然你也可以手动指定这个可执行文件名 - -![image-20200312202520902](/Users/MING/Library/Application Support/typora-user-images/image-20200312202520902.png) - -以上是编译单个文件,当然也可以编译多个文件 - - - -## 4. 清除编译文件 - -使用 go install 或 go install 有可能会生成很多的文件,如可执行文件,归档文件等,它们的后缀名非常多,有 `.exe`, `.a`, `.test`,`.o`,`.so`,`.5` ,`.6`,`.8`,如果要手动一个一个去清理他们,可以说是相当麻烦的,这里你可以通过使用 `go clean` 一键清理。 - -![image-20200313224148510](/Users/MING/Library/Application Support/typora-user-images/image-20200313224148510.png) - -实际开发中`go clean`命令使用的可能不是很多,一般都是利用`go clean`命令清除编译文件,然后再将源码递交到 github 上,方便对于源码的管理。 - -go clean 有不少的参数: - -- `-i `:清除关联的安装的包和可运行文件,也就是通过`go install`安装的文件; -- `-n`: 把需要执行的清除命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的; -- `-r`: 循环的清除在 import 中引入的包; -- `-x`: 打印出来执行的详细命令,其实就是 -n 打印的执行版本; -- `-cache`: 删除所有`go build`命令的缓存 -- `-testcache`: 删除当前包所有的测试结果 - -## 4. 下载代码包 - -在 Golang 中,除了可以从官方网站(golang.org)下载包之外,还可以从一些代码仓库中下载,诸如 github.com,bitbucket.org 这样的代码托管网站。 - -`go get` 这条命令,你以后会最经常用到,它可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。整个过程就像安装一个 App 一样简单。 - -这个命令可以动态获取远程代码包,目前支持的有 BitBucket、GitHub、Google Code 和 Launchpad。在使用 go get 命令前,需要安装与远程包匹配的代码管理工具,如 Git、SVN等。 - -`go get` 会根据域名的不同,使用不同的工具去拉取代码包,具体可参考下图 - -![image-20200312203244402](/Users/MING/Library/Application Support/typora-user-images/image-20200312203244402.png) - -下载和安装,原本是两个动作,但使用 `go get` 后,它默认会将下载(源码包)和安装(go install)合并起来,当然你也可以通过参数指定将拆散它们。 - -在终端执行 `go help get`,会弹出 `go get ` 的帮助文档,我这里汉化总结一下,来帮助大家学习。 - -``` -go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages] -``` - -其中几个参数详解如下 - -- `-u`: - - 用于下载指定的路径包及其依赖包,默认情况下,不会下载本地已经存在的,只会下载本地不存在的代码包。就是口中常说的更新包 比如:go get -u github.com/jinzhu/gorm。会把最新的 gorm 包下载到你本地 - -- `-d`: - - 让命令程序只执行下载动作,而不执行安装动作。 - -- `-t` - - 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包 - -- `-fix` - - 命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。比如,我的代码是用1.7 开发的,现在go 版本已经是1.13 了,有些包已经发生了变化,那么我们在使用go get命令的时候可以加入-fix标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.7版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。 - -- `-v` - - 打印出那些下载的代码包的名字 - -- `-f` - - 仅在使用-u标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了 - -- `-x` - - 打印出整个过程使用了哪些命令 - -- `-insecure` - 允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。(记得 使用工具 git 时,有个版本就是 http 升级为了https) - - - -参数有点多,咱一个一个来。 - -指定 `-d`,只下载源码包而不进行安装 - -![image-20200312204335687](/Users/MING/Library/Application Support/typora-user-images/image-20200312204335687.png) - -由于此时,我们已经下载了 logging 包,当你再次执行 go get 时,并不会重复下载,只有当你指定 `-u` 时,不管你需不需要更新,都会触发重新下载强制更新。 - -![image-20200312204746007](/Users/MING/Library/Application Support/typora-user-images/image-20200312204746007.png) - -如果你想看,下载这个过程用到了哪几个命令,可以指定 `-x` 参数 - -![image-20200312205001161](/Users/MING/Library/Application Support/typora-user-images/image-20200312205001161.png) - -最后,你可能想说,为什么 golang 里的包含这么长,好难记呀,其实这个路径是有讲究的 - -![image-20200312210557326](/Users/MING/Library/Application Support/typora-user-images/image-20200312210557326.png) - - - -这样不同的人开发的包即使使用同一个名,也不会冲突了。 - -下载的包,可能有不同的版本,如何指定版本下载呢? - -```shell -# 拉取最新 -go get github.com/foo - -# 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) -go get -u github.com/foo - -# 升级到最新的修订版本 -go get -u=patch github.com/foo - -# 指定版本,若存在tag,则代行使用 -go get github.com/foo@v1.2.3 - -# 指定分支 -go get github.com/foo@master - -# 指定git提交的hash值 -go get github.com/foo@e3702bed2 -``` - - - -## 6. 安装代码包 - -`go install` 这个命令,如果你安装的是一个可执行文件(包名是 main),它会生成可执行文件到 bin 目录下。这点和 `go build` 很相似,不同的是,`go build` 编译生成的可执行文件放在当前目录,而 `go install` 会将可执行文件统一放至 `$GOPATH/bin` 目录下。 - -![image-20200312221011685](/Users/MING/Library/Application Support/typora-user-images/image-20200312221011685.png) - -如果你安装的是一个库,它会将这个库安装到 pkg 目录下,生成 `.a` 为后缀的文件。 - -![image-20200312221141028](/Users/MING/Library/Application Support/typora-user-images/image-20200312221141028.png) - - - -## 7. 格式化 go 文件 - -Go语言的开发团队制定了统一的官方代码风格,并且推出了 gofmt 工具(gofmt 或 go fmt)来帮助开发者格式化他们的代码到统一的风格。 - -gofmt 是一个 cli 程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有 .go 文件,如果不传参数,会格式化当前目录下的所有 .go 文件。 - -http://c.biancheng.net/view/4441.html - - - - - - - -## 参考文章 - -https://mp.weixin.qq.com/s/fNMXfpBhBC3UWTbYCnwIMg - -https://studygolang.com/articles/25658 - -https://juejin.im/post/5d0b865c6fb9a07f050a6f45 \ No newline at end of file diff --git a/source/c09/c09_27.rst b/source/c09/c09_27.rst deleted file mode 100644 index ec7ef42..0000000 --- a/source/c09/c09_27.rst +++ /dev/null @@ -1,264 +0,0 @@ -9.27 go 命令详解 -================ - -1. 环境变量 ------------ - -``go env`` - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200311221418584.png - :alt: 仅截取部分内容 - - 仅截取部分内容 - -2. 执行 Go 程序 ---------------- - -当前热门的编程语言 Python ,可以不用编译成 二进制文件,就可以直接运行。 - -但 Go 语言程序的执行,必须得先编译再执行。通常来说有如下两种方法 - -1. 先使用 go build 编译成二进制文件,再执行这个二进制文件 - - .. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200313222620374.png - :alt: image-20200313222620374 - - image-20200313222620374 - -2. 使用 go run - “直接”运行,这个命令还是会去编译,但是不会在当前目录下生成二进制文件,而是编译成临时文件后直接运行。 - - .. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200313222710998.png - :alt: image-20200313222710998 - - image-20200313222710998 - -3. 编译文件 ------------ - -将 ``.go`` 文件编译成可执行文件,可以使用 ``go build`` - -如下图所示,helloworld 文件夹下,包含两个 ``.go`` -文件,它们都归属于同一个包。 - -当使用 ``go build`` -可指定包里所有的文件,就你下面这样,默认会以第一个文件(main.go)名生成可执行文件(main)。 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312201759541.png - :alt: image-20200312201759541 - - image-20200312201759541 - -当然,你也可以不指定,此时生成的可执行文件是以 文件夹名命名 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312202032363.png - :alt: image-20200312202032363 - - image-20200312202032363 - -当然你也可以手动指定这个可执行文件名 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312202520902.png - :alt: image-20200312202520902 - - image-20200312202520902 - -以上是编译单个文件,当然也可以编译多个文件 - -4. 清除编译文件 ---------------- - -使用 go install 或 go install -有可能会生成很多的文件,如可执行文件,归档文件等,它们的后缀名非常多,有 -``.exe``\ , ``.a``\ , ``.test``\ ,\ ``.o``\ ,\ ``.so``\ ,\ ``.5`` -,\ ``.6``\ ,\ ``.8``\ ,如果要手动一个一个去清理他们,可以说是相当麻烦的,这里你可以通过使用 -``go clean`` 一键清理。 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200313224148510.png - :alt: image-20200313224148510 - - image-20200313224148510 - -实际开发中\ ``go clean``\ 命令使用的可能不是很多,一般都是利用\ ``go clean``\ 命令清除编译文件,然后再将源码递交到 -github 上,方便对于源码的管理。 - -go clean 有不少的参数: - -- ``-i``\ :清除关联的安装的包和可运行文件,也就是通过\ ``go install``\ 安装的文件; -- ``-n``\ : - 把需要执行的清除命令打印出来,但是不执行,这样就可以很容易的知道底层是如何运行的; -- ``-r``\ : 循环的清除在 import 中引入的包; -- ``-x``\ : 打印出来执行的详细命令,其实就是 -n 打印的执行版本; -- ``-cache``\ : 删除所有\ ``go build``\ 命令的缓存 -- ``-testcache``\ : 删除当前包所有的测试结果 - -4. 下载代码包 -------------- - -在 Golang -中,除了可以从官方网站(golang.org)下载包之外,还可以从一些代码仓库中下载,诸如 -github.com,bitbucket.org 这样的代码托管网站。 - -``go get`` -这条命令,你以后会最经常用到,它可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。整个过程就像安装一个 -App 一样简单。 - -这个命令可以动态获取远程代码包,目前支持的有 BitBucket、GitHub、Google -Code 和 Launchpad。在使用 go get -命令前,需要安装与远程包匹配的代码管理工具,如 Git、SVN等。 - -``go get`` 会根据域名的不同,使用不同的工具去拉取代码包,具体可参考下图 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312203244402.png - :alt: image-20200312203244402 - - image-20200312203244402 - -下载和安装,原本是两个动作,但使用 ``go get`` -后,它默认会将下载(源码包)和安装(go -install)合并起来,当然你也可以通过参数指定将拆散它们。 - -在终端执行 ``go help get``\ ,会弹出 ``go get`` -的帮助文档,我这里汉化总结一下,来帮助大家学习。 - -:: - - go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages] - -其中几个参数详解如下 - -- ``-u``\ : - - 用于下载指定的路径包及其依赖包,默认情况下,不会下载本地已经存在的,只会下载本地不存在的代码包。就是口中常说的更新包 - 比如:go get -u github.com/jinzhu/gorm。会把最新的 gorm - 包下载到你本地 - -- ``-d``\ : - - 让命令程序只执行下载动作,而不执行安装动作。 - -- ``-t`` - - 让命令程序同时下载并安装指定的代码包中的测试源码文件中依赖的代码包 - -- ``-fix`` - - 命令程序在下载代码包后先执行修正动作,而后再进行编译和安装。比如,我的代码是用1.7 - 开发的,现在go 版本已经是1.13 - 了,有些包已经发生了变化,那么我们在使用go - get命令的时候可以加入-fix标记。这个标记的作用是在检出代码包之后,先对该代码包中不符合Go语言1.7版本的语言规范的语法进行修正,然后再下载它的依赖包,最后再对它们进行编译和安装。 - -- ``-v`` - - 打印出那些下载的代码包的名字 - -- ``-f`` - - 仅在使用-u标记时才有效。该标记会让命令程序忽略掉对已下载代码包的导入路径的检查。如果下载并安装的代码包所属的项目是你从别人那里Fork过来的,那么这样做就尤为重要了 - -- ``-x`` - - 打印出整个过程使用了哪些命令 - -- ``-insecure`` - 允许命令程序使用非安全的scheme(如HTTP)去下载指定的代码包。如果你用的代码仓库(如公司内部的Gitlab)没有HTTPS支持,可以添加此标记。请在确定安全的情况下使用它。(记得 - 使用工具 git 时,有个版本就是 http 升级为了https) - -参数有点多,咱一个一个来。 - -指定 ``-d``\ ,只下载源码包而不进行安装 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312204335687.png - :alt: image-20200312204335687 - - image-20200312204335687 - -由于此时,我们已经下载了 logging 包,当你再次执行 go get -时,并不会重复下载,只有当你指定 ``-u`` -时,不管你需不需要更新,都会触发重新下载强制更新。 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312204746007.png - :alt: image-20200312204746007 - - image-20200312204746007 - -如果你想看,下载这个过程用到了哪几个命令,可以指定 ``-x`` 参数 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312205001161.png - :alt: image-20200312205001161 - - image-20200312205001161 - -最后,你可能想说,为什么 golang -里的包含这么长,好难记呀,其实这个路径是有讲究的 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312210557326.png - :alt: image-20200312210557326 - - image-20200312210557326 - -这样不同的人开发的包即使使用同一个名,也不会冲突了。 - -下载的包,可能有不同的版本,如何指定版本下载呢? - -.. code:: shell - - # 拉取最新 - go get github.com/foo - - # 最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) - go get -u github.com/foo - - # 升级到最新的修订版本 - go get -u=patch github.com/foo - - # 指定版本,若存在tag,则代行使用 - go get github.com/foo@v1.2.3 - - # 指定分支 - go get github.com/foo@master - - # 指定git提交的hash值 - go get github.com/foo@e3702bed2 - -6. 安装代码包 -------------- - -``go install`` 这个命令,如果你安装的是一个可执行文件(包名是 -main),它会生成可执行文件到 bin 目录下。这点和 ``go build`` -很相似,不同的是,\ ``go build`` 编译生成的可执行文件放在当前目录,而 -``go install`` 会将可执行文件统一放至 ``$GOPATH/bin`` 目录下。 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312221011685.png - :alt: image-20200312221011685 - - image-20200312221011685 - -如果你安装的是一个库,它会将这个库安装到 pkg 目录下,生成 ``.a`` -为后缀的文件。 - -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200312221141028.png - :alt: image-20200312221141028 - - image-20200312221141028 - -7. 格式化 go 文件 ------------------ - -Go语言的开发团队制定了统一的官方代码风格,并且推出了 gofmt 工具(gofmt -或 go fmt)来帮助开发者格式化他们的代码到统一的风格。 - -gofmt 是一个 cli -程序,会优先读取标准输入,如果传入了文件路径的话,会格式化这个文件,如果传入一个目录,会格式化目录中所有 -.go 文件,如果不传参数,会格式化当前目录下的所有 .go 文件。 - -http://c.biancheng.net/view/4441.html - -参考文章 --------- - -https://mp.weixin.qq.com/s/fNMXfpBhBC3UWTbYCnwIMg - -https://studygolang.com/articles/25658 - -https://juejin.im/post/5d0b865c6fb9a07f050a6f45 diff --git a/source/c09/c09_28.md b/source/c09/c09_28.md deleted file mode 100644 index a907fc5..0000000 --- a/source/c09/c09_28.md +++ /dev/null @@ -1,14 +0,0 @@ -# 9.28 函数式编程 - -函数是一等公民,它可以做为参数进行传递,可以做为函数返回值,也可以做为接收者。所以后来才有了高阶函数,闭包。 - - - -正统的函数式编程 - -- 不可变性:不能有状态,只能有函数和变量 -- 函数只能有一个参数 - -Go 语言不是正统 - -![image-20200131160151750](C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200131160151750.png) \ No newline at end of file diff --git a/source/c09/c09_28.rst b/source/c09/c09_28.rst deleted file mode 100644 index 39ba71b..0000000 --- a/source/c09/c09_28.rst +++ /dev/null @@ -1,16 +0,0 @@ -9.28 函数式编程 -=============== - -函数是一等公民,它可以做为参数进行传递,可以做为函数返回值,也可以做为接收者。所以后来才有了高阶函数,闭包。 - -正统的函数式编程 - -- 不可变性:不能有状态,只能有函数和变量 -- 函数只能有一个参数 - -Go 语言不是正统 - -.. figure:: C:\Users\wangbm\AppData\Roaming\Typora\typora-user-images\image-20200131160151750.png - :alt: image-20200131160151750 - - image-20200131160151750 diff --git a/source/c09/c09_29.md b/source/c09/c09_29.md deleted file mode 100644 index 777861c..0000000 --- a/source/c09/c09_29.md +++ /dev/null @@ -1,158 +0,0 @@ -# 9.29 学习一些常见的并发模型 - -本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 Golang 中的 协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 - -你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 - -## 0. 并发与并行 - -讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? - -这里我用两个例子来形象描述: - -- **并发**:当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 -- **并行**:你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 - -在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 - -而当你的机器有多个 CPU 核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 - -接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 - -但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? - -谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C 的活,然后再去做一会 A 的活,就这样不断的切换着,大家都很开心,其乐融融。 - -## 1. 并发编程的模型 - -在计算机的世界里,实现并发通常有几种方式: - -1. 多进程模型:创建新的线程处理请求 -2. 多线程模型:创建新的进程处理请求 -3. 使用线程池:线程/进程创建销毁开销大 -4. I/O 多路复用+单/多线程 - -## 2. 多进程与多线程 - -对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ ,一个微信,它们都是一个进程。 - -进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 - -在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 - -线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 - -而进程的切换,相比线程来说,会更加麻烦。 - -因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 - -此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams 等 - -说了这么多,好像都在说线程优于进程,也不尽然。 - -比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 - -同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 - - - -## 3. I/O多路复用 - -`I/O多路复用` ,英文全称为 `I/O multiplexing`,这个中文翻译和把 socket 翻译成 套接字一样,影响了我对其概念的理解。 - -在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 - -但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 - -终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O 多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 - -再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 select ,早已不能支撑用户请求了。 - -由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select 发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 - -select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 - -都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 - -由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 - -epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: - -- select 和 poll 无差别轮循fd,浪费资源,epool 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 -- select 和 poll 线程不安全,epool 线程安全 -- select 请求连接数的限制,epool 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) -- select 和 pool 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 - -虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 epool 出现了, select 就会被淘汰掉。 - -epool 关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool 的性能最好。 - -而 select 关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 - -另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool 是 Linux 所有的,其他平台上没有。 - - IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 - -- 一个线程的IO多路复用,比如 Redis -- 多个线程的IO多路复用,比如 goroutine - -IO多路复用 + 单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 - -## 4. 三种线程模型? - -实际上,goroutine 并非传统意义上的协程。 - -现在主流的线程模型分三种: - -- 内核级线程模型 -- 用户级线程模型 -- 两级线程模型(也称混合型线程模型) - -传统的协程库属于**用户级线程模型**,而 goroutine 和它的 `Go Scheduler` 在底层实现上其实是属于**两级线程模型**,因此,有时候为了方便理解可以简单把 goroutine 类比成协程,但心里一定要有个清晰的认知 — goroutine并不等同于协程。 - -关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 - - - -## 5. 协程的优势在哪? - -协程,可以认为是轻量级的“线程”。 - -对比线程,有如下几个明显的优势。 - -1. 协程的调度由 Go 的 runtime 管理,协程切换不需要经由操作系统内核,开销较小。 -2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 - -同时,在 Golang 里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 async def 而言),运行时只需要一个关键字 `go`,就可以轻松创建一个协程。 - - - -使用 -race 来检测数据 访问的冲突 - - - -协程什么时候会切换 - -1. I/O,select -2. channel -3. 等待锁 -4. 函数调用(有时 -5. runtime.Gosched() - - - - - -## 参考阅读: - -https://www.cnblogs.com/aspirant/p/9166944.html - -https://blog.csdn.net/snoweaglelord/article/details/99681179 - -https://www.jianshu.com/p/dfd940e7fca2 - -https://studygolang.com/articles/13344 - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c09/c09_29.rst b/source/c09/c09_29.rst deleted file mode 100644 index 2990769..0000000 --- a/source/c09/c09_29.rst +++ /dev/null @@ -1,187 +0,0 @@ -9.29 学习一些常见的并发模型 -=========================== - -本篇内容主要是了解下并发编程中的一些概念,及讲述一些常用的并发模型都是什么样的,从而理解 -Golang 中的 -协程在这些众多模型中是一种什么样的存在及地位。可能和本系列的初衷(零基础学Go)有所出入,因此你读不读本篇都不会对你学习Go有影响,尽管我个人觉得这是有必要了解的。 - -你可以自行选择,若你只想学习 Golang 有关的内容,完全可以跳过本篇。 - -0. 并发与并行 -------------- - -讲到并发,那不防先了解下什么是并发,与之相对的并行有什么区别? - -这里我用两个例子来形象描述: - -- **并发**\ :当你在跑步时,发现鞋带松,要停下来系鞋带,这时候跑步和系鞋带就是并发状态。 -- **并行**\ :你跑步时,可以同时听歌,那么跑步和听歌就是并行状态,谁也不影响谁。 - -在计算机的世界中,一个CPU核严格来说同一时刻只能做一件事,但由于CPU的频率实在太快了,人们根本感知不到其切换的过程,所以我们在编码的时候,实际上是可以在单核机器上写多进程的程序(但你要知道这是假象),这是相对意义上的并行。 - -而当你的机器有多个 CPU -核时,多个进程之间才能真正的实现并行,这是绝对意义上的并行。 - -接着来说并发,所谓的并发,就是多个任务之间可以在同一时间段里一起执行。 - -但是在单核CPU里,他同一时刻只能做一件事情 ,怎么办? - -谁都不能偏坦,我就先做一会 A 的活,再做一会B 的活,接着去做一会 C -的活,然后再去做一会 A -的活,就这样不断的切换着,大家都很开心,其乐融融。 - -1. 并发编程的模型 ------------------ - -在计算机的世界里,实现并发通常有几种方式: - -1. 多进程模型:创建新的线程处理请求 -2. 多线程模型:创建新的进程处理请求 -3. 使用线程池:线程/进程创建销毁开销大 -4. I/O 多路复用+单/多线程 - -2. 多进程与多线程 ------------------ - -对于普通的用户来说,进程是最熟悉的存在,比如一个 QQ -,一个微信,它们都是一个进程。 - -进程是计算机资源分配的最小单位,而线程是比进程更小的执行单元,它不能脱离于进程单独存在。 - -在一个进程里,至少有一个线程,那个线程叫主线程,同时你也可以创建多个线程,多个线程之间是可以并发执行的。 - -线程是调度的基本单位,在多线程里,在调度过程中,需要由 CPU 和 -内核层参与上下文的切换。如果你跑了A线程,然后切到B线程,内核调用开始,CPU需要对A线程的上下文保留,然后切到B线程,然后把控制权交给你的应用层调度。 - -而进程的切换,相比线程来说,会更加麻烦。 - -因为进程有自己的独立地址空间,多个进程之间的地址空间是相互隔离的,这和线程有很大的不同,单个进程内的多个线程 -共享进程中的数据的,使用相同的地址空间,所以CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 - -此外,由于同一进程下的线程共享全局变量、静态变量等数据,使得线程间的通信非常方便,相比之下,进程间的通信(IPC,InterProcess -Communication)就略显复杂,通常的进程间的通信方式有:管道,消息队列,信号量,Socket,Streams -等 - -说了这么多,好像都在说线程优于进程,也不尽然。 - -比如多线程更多用于有IO密集型的业务场景,而对于计算密集型的场景,应该优先选择多进程。 - -同时,多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 - -3. I/O多路复用 --------------- - -``I/O多路复用`` ,英文全称为 ``I/O multiplexing``\ ,这个中文翻译和把 -socket 翻译成 套接字一样,影响了我对其概念的理解。 - -在互联网早期,为了实现一个服务器可以处理多个客户端的连接,程序猿是这样做的。服务器得知来了一个请求后,就去创建一个线程处理这个请求,假如有10个客户请求,就创建10个线程,这在当时联网设备还比较匮乏的时代,是没有任何问题的。 - -但随着科技的发展,人们越来越富裕,都买得起电脑了,网民也越来越多了,由于一台机器的能开启的线程数是有限制的,当请求非常集中量大到一定量时,服务器的压力就巨大无比。 - -终于到了 1983年,人们意识到这种问题,提出了一种最早的 I/O -多路复用的模型(select实现),这种模型,对比之前最大的不同就是,处理请求的线程不再是根据请求来定,后端请求的进程只有一个。虽然这种模型在现在看来还是不行,但在当时已经大大减小了服务器系统的开销,可以解决服务器压力太大的问题,毕竟当时的电脑都是很珍贵的。 - -再后来,家家都有了电脑,手机互联网的时代也要开始来了,联网设备爆炸式增长,之前的 -select ,早已不能支撑用户请求了。 - -由于使用 select 最多只能接收 1024 个连接,后来程序猿们又改进了 select -发明了 pool,pool 使用的链表存储,没有最大连接数的限制。 - -select 和 pool ,除了解决了连接数的限制 ,其他似乎没有本质的区别。 - -都是服务器知道了有一个连接来了,由于并不知道是哪那几个流(可能有一个,多个,甚至全部),所以只能一个一个查过去(轮循),假如服务器上有几万个文件描述符(下称fd,file -descriptor),而你要处理一个请求,却要遍历几万个fd,这样是不是很浪费时间和资源。 - -由此程序员不得不持续改进 I/O多路复用的策略,这才有了后来的 epoll 方法。 - -epoll 解决了前期 select 和 poll 出现的一系列的尴尬问题,比如: - -- select 和 poll 无差别轮循fd,浪费资源,epool - 使用通知回调机制,有流发生 IO事件时就会主动触发回调函数 -- select 和 poll 线程不安全,epool 线程安全 -- select 请求连接数的限制,epool - 能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口) -- select 和 pool - 需要频繁地将fd复制到内核空间,开销大,epoll通过内核和用户空间共享一块内存来减少这方面的开销。 - -虽然 I/O 多路复用经历了三种实现:select -> pool -> epool,这也不是就说 -epool 出现了, select 就会被淘汰掉。 - -epool -关注的是活跃的连接数,当连接数非常多但活跃连接少的情况下(比如长连接数较多),epool -的性能最好。 - -而 select -关注的是连接总数,当连接数多而且大部分的连接都很活跃的情况下,选择 -select 会更好,因为 epool 的通知回调机制需要很多的函数回调。 - -另外还有一点是,select 是 POSIX 规定的,一般操作系统均有实现,而 epool -是 Linux 所有的,其他平台上没有。 - -IO多路复用除了以上三种不同的具体实现的区别外,还可以根据线程数的多少来分类 - -- 一个线程的IO多路复用,比如 Redis -- 多个线程的IO多路复用,比如 goroutine - -IO多路复用 + -单进(线)程有个好处,就是不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。 - -4. 三种线程模型? ------------------ - -实际上,goroutine 并非传统意义上的协程。 - -现在主流的线程模型分三种: - -- 内核级线程模型 -- 用户级线程模型 -- 两级线程模型(也称混合型线程模型) - -传统的协程库属于\ **用户级线程模型**\ ,而 goroutine 和它的 -``Go Scheduler`` -在底层实现上其实是属于\ **两级线程模型**\ ,因此,有时候为了方便理解可以简单把 -goroutine 类比成协程,但心里一定要有个清晰的认知 — -goroutine并不等同于协程。 - -关于这块,想详细了解的,可以前往:https://studygolang.com/articles/13344 - -5. 协程的优势在哪? -------------------- - -协程,可以认为是轻量级的“线程”。 - -对比线程,有如下几个明显的优势。 - -1. 协程的调度由 Go 的 runtime - 管理,协程切换不需要经由操作系统内核,开销较小。 -2. 单个协程的堆栈只有几个kb,可创建协程的数量远超线程数。 - -同时,在 Golang -里,我还体会到了这种现代化编程语言带来的优势,它考虑得面面俱到,让编码变得更加的傻瓜式,goroutine的定义不需要在定义时区分是否异步函数(相对Python的 -async def 而言),运行时只需要一个关键字 -``go``\ ,就可以轻松创建一个协程。 - -使用 -race 来检测数据 访问的冲突 - -协程什么时候会切换 - -1. I/O,select -2. channel -3. 等待锁 -4. 函数调用(有时 -5. runtime.Gosched() - -参考阅读: ----------- - -https://www.cnblogs.com/aspirant/p/9166944.html - -https://blog.csdn.net/snoweaglelord/article/details/99681179 - -https://www.jianshu.com/p/dfd940e7fca2 - -https://studygolang.com/articles/13344 - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - diff --git a/source/c09/c09_40.md b/source/c09/c09_40.md deleted file mode 100644 index 1da49a2..0000000 --- a/source/c09/c09_40.md +++ /dev/null @@ -1,9 +0,0 @@ -# 9.40 推荐几个Go学习网站 - -1、用小例子来学习 Go 语言 - -https://gobyexample-cn.github.io/ - -2、跟煎鱼学Go - -https://eddycjy.gitbook.io/golang/ \ No newline at end of file diff --git a/source/c09/c09_40.rst b/source/c09/c09_40.rst deleted file mode 100644 index d196b48..0000000 --- a/source/c09/c09_40.rst +++ /dev/null @@ -1,10 +0,0 @@ -9.40 推荐几个Go学习网站 -======================= - -1、用小例子来学习 Go 语言 - -https://gobyexample-cn.github.io/ - -2、跟煎鱼学Go - -https://eddycjy.gitbook.io/golang/ diff --git a/source/c10/c10_01.md b/source/c10/c10_01.md deleted file mode 100644 index 1d9e66a..0000000 --- a/source/c10/c10_01.md +++ /dev/null @@ -1,144 +0,0 @@ -# 10.1 情人节来了,教你使用 Python 来表白 - -**作者**:@明哥 -**公众号**:Python编程时光 - ---- - -2020年,这个看起来如此浪漫的年份,你还是一个人吗? - -2018年的时候,写过一篇介绍如何使用 Python 来表白的文章。 - -虽然创意和使用效果都不错,但有一缺点,这是那个exe文件,女神需要打开电脑,才有可能参与进来,进而被你成功"调戏”。 - -由于是很早期的文章了,应该有很多人没有看过。 - -没有看过的,你可以点击这里查看:[用Python写一个表白神器让你脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) - -提醒你一下,后天就是 2月14日了。什么?还是一条狗呢? - -行吧,那你赶上了,今天的文章,就是为你而写。 - -明哥今天来教你如何使用 Python 来向心中的女神表白。 - -前段时间,在微博上刷到了一条推荐。内容是这样的 - -![微博截图](http://image.python-online.cn/20200211211522.png) - -出于好奇,我点开了,放大再放大,emmm,有点意思吖… - -![img](http://image.python-online.cn/20200211211657.png) - -这四个字,对于像我这样腼腆的DS男来说,还真不好意思说,说出来,万一被拒绝了咋办? - -使用套路来表白,并观察对方的反应,你大概能清楚对方是否对你也有好感,先测试下自己有几成的把握再下手或许更稳妥。 - -今天就教大家一个这样的套路:如何使用 Python 来做出来这样的图,有点浪漫,又有点极客。能不能拿下你女神,就要靠你(命)了。(๑•́₃ •̀๑) - -首先,你得先找到一张你女神的高清图片(尽量分辨率高点的吧,效果会好点)。 - -这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 - -![](http://image.python-online.cn/20200214104413.png) - -使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? - -![](http://image.python-online.cn/save.jpeg) - -然后将这张图片发给你的女神,具体话术你自己想咯。 - ------- - -相比女神来说,你可能更在意这是如何实现的(**活该你单身**)。 - -其实原理很简单,代码也还不到20 行。 - -首先,来讲讲原理。 - -事实上,每一张图片都是由一个一个的像素点所组成的。而每个像素点,都有自己的颜色,其颜色可以用一个数组来表示:(a,b,c),其中每位数的取值范围都是 0-255。 - -比如(0,0,0)代表黑色色,(255,255,255)代表白色。 - -当像素点足够多的时候,这张照片就是我们所说的高清照片。 - -而如果当像素点太少,我们的肉眼就能感知到明显的锯齿感。 - -用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 - -![](http://image.python-online.cn/20200214104646.png) - -我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 - -有了思路,就可以开始我们的代码。 - -首先,使用 pillow.Image读取图像,并使用load函数获取到每一个像素值。 - -```python -img_raw = Image.open(img_path) -img_array = img_raw.load() -``` - -然后新建一张画布,并选好你要使用的字体和字体大小。 - -```python -img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) -draw = ImageDraw.Draw(img_new) -font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) -``` - -由于需要不断循环 “我喜欢你!”,这五个字符。所以这里可以while循环 yield 来实现一个生成器。 - -```python -def character_generator(text): - while True: - for i in range(len(text)): - yield text[i] -``` - -最后,要给这些字加上相应的颜色,写入新创建的画布中。 - -```python -for y in range(0, img_raw.size[1], font_size): - for x in range(0, img_raw.size[0], font_size): - draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) -``` - -最后将成品保存 - -```python -img_new.convert('RGB').save("F://gyy_save.jpeg") -``` - -完整代码如下,供你参考 - -```python -from PIL import Image, ImageDraw, ImageFont - -font_size = 7 -text = "我喜欢你!" -img_path = "F://gyy.jpeg" - -img_raw = Image.open(img_path) -img_array = img_raw.load() - -img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) -draw = ImageDraw.Draw(img_new) -font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) - -def character_generator(text): - while True: - for i in range(len(text)): - yield text[i] - -ch_gen = character_generator(text) - -for y in range(0, img_raw.size[1], font_size): - for x in range(0, img_raw.size[0], font_size): - draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) - -img_new.convert('RGB').save("F://save.jpeg") -``` - - - -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst deleted file mode 100644 index f0f8fce..0000000 --- a/source/c10/c10_01.rst +++ /dev/null @@ -1,160 +0,0 @@ -10.1 情人节来了,教你使用 Python 来表白 -======================================= - -**作者**\ :@明哥 **公众号**\ :Python编程时光 - --------------- - -2020年,这个看起来如此浪漫的年份,你还是一个人吗? - -2018年的时候,写过一篇介绍如何使用 Python 来表白的文章。 - -虽然创意和使用效果都不错,但有一缺点,这是那个exe文件,女神需要打开电脑,才有可能参与进来,进而被你成功“调戏”。 - -由于是很早期的文章了,应该有很多人没有看过。 - -没有看过的,你可以点击这里查看:\ `用Python写一个表白神器让你脱离单身 `__ - -提醒你一下,后天就是 2月14日了。什么?还是一条狗呢? - -行吧,那你赶上了,今天的文章,就是为你而写。 - -明哥今天来教你如何使用 Python 来向心中的女神表白。 - -前段时间,在微博上刷到了一条推荐。内容是这样的 - -.. figure:: http://image.python-online.cn/20200211211522.png - :alt: 微博截图 - - 微博截图 - -出于好奇,我点开了,放大再放大,emmm,有点意思吖… - -.. figure:: http://image.python-online.cn/20200211211657.png - :alt: img - - img - -这四个字,对于像我这样腼腆的DS男来说,还真不好意思说,说出来,万一被拒绝了咋办? - -使用套路来表白,并观察对方的反应,你大概能清楚对方是否对你也有好感,先测试下自己有几成的把握再下手或许更稳妥。 - -今天就教大家一个这样的套路:如何使用 Python -来做出来这样的图,有点浪漫,又有点极客。能不能拿下你女神,就要靠你(命)了。(๑•́₃ -•̀๑) - -首先,你得先找到一张你女神的高清图片(尽量分辨率高点的吧,效果会好点)。 - -这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 - -|image0| - -使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? - -|image1| - -然后将这张图片发给你的女神,具体话术你自己想咯。 - --------------- - -相比女神来说,你可能更在意这是如何实现的(\ **活该你单身**\ )。 - -其实原理很简单,代码也还不到20 行。 - -首先,来讲讲原理。 - -事实上,每一张图片都是由一个一个的像素点所组成的。而每个像素点,都有自己的颜色,其颜色可以用一个数组来表示:(a,b,c),其中每位数的取值范围都是 -0-255。 - -比如(0,0,0)代表黑色色,(255,255,255)代表白色。 - -当像素点足够多的时候,这张照片就是我们所说的高清照片。 - -而如果当像素点太少,我们的肉眼就能感知到明显的锯齿感。 - -用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 -5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 - -|image2| - -我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 - -有了思路,就可以开始我们的代码。 - -首先,使用 pillow.Image读取图像,并使用load函数获取到每一个像素值。 - -.. code:: python - - img_raw = Image.open(img_path) - img_array = img_raw.load() - -然后新建一张画布,并选好你要使用的字体和字体大小。 - -.. code:: python - - img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) - draw = ImageDraw.Draw(img_new) - font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) - -由于需要不断循环 “我喜欢你!”,这五个字符。所以这里可以while循环 yield -来实现一个生成器。 - -.. code:: python - - def character_generator(text): - while True: - for i in range(len(text)): - yield text[i] - -最后,要给这些字加上相应的颜色,写入新创建的画布中。 - -.. code:: python - - for y in range(0, img_raw.size[1], font_size): - for x in range(0, img_raw.size[0], font_size): - draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) - -最后将成品保存 - -.. code:: python - - img_new.convert('RGB').save("F://gyy_save.jpeg") - -完整代码如下,供你参考 - -.. code:: python - - from PIL import Image, ImageDraw, ImageFont - - font_size = 7 - text = "我喜欢你!" - img_path = "F://gyy.jpeg" - - img_raw = Image.open(img_path) - img_array = img_raw.load() - - img_new = Image.new("RGB", img_raw.size, (0, 0, 0)) - draw = ImageDraw.Draw(img_new) - font = ImageFont.truetype('C:/Windows/fonts/Dengl.ttf', font_size) - - def character_generator(text): - while True: - for i in range(len(text)): - yield text[i] - - ch_gen = character_generator(text) - - for y in range(0, img_raw.size[1], font_size): - for x in range(0, img_raw.size[0], font_size): - draw.text((x, y), next(ch_gen), font=font, fill=img_array[x, y], direction=None) - - img_new.convert('RGB').save("F://save.jpeg") - -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新干货! - - -.. |image0| image:: http://image.python-online.cn/20200214104413.png -.. |image1| image:: http://image.python-online.cn/save.jpeg -.. |image2| image:: http://image.python-online.cn/20200214104646.png - diff --git a/source/chapters/p01.rst b/source/chapters/p01.rst index fea4cdc..203bd32 100755 --- a/source/chapters/p01.rst +++ b/source/chapters/p01.rst @@ -23,3 +23,7 @@ :glob: ../c01/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p02.rst b/source/chapters/p02.rst index ef6dd05..6fa8859 100755 --- a/source/chapters/p02.rst +++ b/source/chapters/p02.rst @@ -15,3 +15,7 @@ :glob: ../c02/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p03.rst b/source/chapters/p03.rst index 21f4008..6899aad 100755 --- a/source/chapters/p03.rst +++ b/source/chapters/p03.rst @@ -15,3 +15,7 @@ :glob: ../c03/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p04.rst b/source/chapters/p04.rst index 2547e1b..82b4344 100755 --- a/source/chapters/p04.rst +++ b/source/chapters/p04.rst @@ -13,3 +13,7 @@ :glob: ../c04/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p05.rst b/source/chapters/p05.rst index c17ee87..d046a01 100755 --- a/source/chapters/p05.rst +++ b/source/chapters/p05.rst @@ -25,3 +25,7 @@ :glob: ../c05/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p06.rst b/source/chapters/p06.rst index 07042a9..357fc56 100755 --- a/source/chapters/p06.rst +++ b/source/chapters/p06.rst @@ -14,3 +14,7 @@ :glob: ../c06/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p07.rst b/source/chapters/p07.rst index 61e771c..578832c 100755 --- a/source/chapters/p07.rst +++ b/source/chapters/p07.rst @@ -13,3 +13,7 @@ :glob: ../c07/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p08.rst b/source/chapters/p08.rst index de04d43..06ac496 100755 --- a/source/chapters/p08.rst +++ b/source/chapters/p08.rst @@ -13,3 +13,7 @@ :glob: ../c08/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p09.rst b/source/chapters/p09.rst index d292f94..78ed63c 100644 --- a/source/chapters/p09.rst +++ b/source/chapters/p09.rst @@ -1,26 +1,10 @@ ============================= -第九章:Go 语言之路 +第十章:有趣的工具 ============================= -以前经常能看到有人盘点,有哪几个国外的大型网站是使用 Python 开发的,当小白看到这个榜单的时候,也许会想原来 Python 做网站也可以这么牛。爱了。 +Python 是一门可以快速开发的语言,使用它可以用最少的时间,做出符合预期,且效果还不错的产品。 -可是你可曾听到过有人盘点有哪些网站是用 Java 开发的?没有吧?印证了那句话:“一个人越炫耀什么,说明内心越缺少什么”。 - -有不少人都跟我说过,现在很多公司都更倾向于用 Go 来开发项目。然后好奇去了解一番,发现确实很优秀。 - -如果按照开发效率、编译与执行效率,编程语言可以大致分为三类: - -1. 执行速度快但是编译速度并不理想的语言(以 C++ 为代表) -2. 编译速度较快但执行效率不佳的语言(以 Java 为代表) -3. 开发难度较低但执行速度一般的动态语言呢?(以 Python 为代表) - -三类语言,各有利弊,让人难以选择。 - -而Go语言,优秀到什么程度? - -它在这 3 个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发。Go语言支持交叉编译,比如说你可以在运行 Linux 系统的计算机上开发可以在 Windows 上运行的应用程序。 - -这是第一门完全支持 UTF-8 的编程语言,这不仅体现在它可以处理使用 UTF-8 编码的字符串,就连它的源码文件格式都是使用的 UTF-8 编码。Go语言做到了真正的国际化! +这一章里,我将写一写我使用 Python 做出过哪一些好用的工具。 本章节,会持续更新,敬请关注… @@ -30,4 +14,8 @@ :maxdepth: 1 :glob: - ../c09/* + ../c10/* + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/chapters/p10.rst b/source/chapters/p10.rst deleted file mode 100644 index fab4ef4..0000000 --- a/source/chapters/p10.rst +++ /dev/null @@ -1,17 +0,0 @@ -============================= -第十章:有趣的工具 -============================= - -Python 是一门可以快速开发的语言,使用它可以用最少的时间,做出符合预期,且效果还不错的产品。 - -这一章里,我将写一写我使用 Python 做出过哪一些好用的工具。 - -本章节,会持续更新,敬请关注… - ----------------------------- - -.. toctree:: - :maxdepth: 1 - :glob: - - ../c10/* diff --git a/source/conf.py b/source/conf.py index ad9fb61..1b0c693 100755 --- a/source/conf.py +++ b/source/conf.py @@ -46,8 +46,8 @@ master_doc = 'index' # General information about the project. -project = 'MING\'s BLOG' -copyright = 'Python编程时光' +project = 'Python编程时光' +copyright = '王炳明' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -91,10 +91,10 @@ \XeTeXlinebreakskip = 0pt plus 1pt """} -man_pages = [ - ('index', 'python3-cookbook', '《Python编程时光》', - ['小明同学'], 1) -] +# man_pages = [ +# ('index', 'python3-cookbook', '《Python编程时光》', +# ['小明同学'], 1) +# ] # If true, show URL addresses after external links. #man_show_urls = False @@ -105,11 +105,11 @@ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) -texinfo_documents = [ - ('index', 'Python-Time', '《Python编程时光》', - '小明同学', 'Python-Time', '《Python编程时光》', - 'Miscellaneous'), -] +# texinfo_documents = [ +# ('index', 'Python-Time', '《Python编程时光》', +# '小明同学', 'Python-Time', '《Python编程时光》', +# 'Miscellaneous'), +# ] #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/source/go_mp_index.md b/source/go_mp_index.md deleted file mode 100644 index f620d0d..0000000 --- a/source/go_mp_index.md +++ /dev/null @@ -1,60 +0,0 @@ -# Go编程时光 - 学习索引 - -为了方便读者们能从本号真正学到一些有用的内容,我将 《Go编程时光》干货文章进行了整理,方便你学习。 - -### 系列导读: - -[01. 开发环境的搭建(Goland & VS Code)](http://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483659&idx=1&sn=a5c0836d8d3f2209979b28c7e93aca26&chksm=fc355b60cb42d2767ea13bdb53216be2aad6e4c06c2d0be49f1531a0651434d544b6558791ae&token=243502240&lang=zh_CN#rd) - -[02. 学习五种变量创建的方法 ](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483669&idx=2&sn=e70a1400c094e981f15b8da552bd8fbf&chksm=fc355b7ecb42d26824985163a3ef0c3567134975637c4efc42161751f54ab10343b485b36e23#rd) - -[03. 详解数据类型:整形与浮点型](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483669&idx=1&sn=9d8b29ed467195e4d02759fc136a2544&chksm=fc355b7ecb42d2686dbd5fa09f467439716939ea08917af2eb0ff9bbcc4f7b8479548c451710#rd) - -[04. 详解数据类型:byte、rune与string](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483673&idx=1&sn=09cc98b3f0b0e89e28c40a5f096c0d73&chksm=fc355b72cb42d26426bd4ae986ea46284b89d3bdf78465686a799684ddfc5f48f128382cc1ae&token=243502240&lang=zh_CN#rd) - -[05. 详解数据类型:数组与切片](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483677&idx=1&sn=99556dc39a349fac7c9ba2e39e78336f&chksm=fc355b76cb42d260a063c73cb6adffcd23613c131a8fad8d0049dda3378ebd6d4f444c53e540&token=243502240&lang=zh_CN#rd) - -[06. 详解数据类型:字典与布尔类型](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483696&idx=1&sn=44a7ace3a7a06c92f398ef4e35ab6dd1&chksm=fc355b5bcb42d24d6aded290ea3b5601aa8c89e5baf988c83e20d63b7ddd8c9cc1b5a48d1a31&token=243502240&lang=zh_CN#rd) - -[07. 详解数据类型:指针](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483697&idx=1&sn=e6612228a8d2df948069242269466263&chksm=fc355b5acb42d24caf65d1d5863c86c51b3faf3fa69b0b04546c50336544c20c9e8e2ca7165a&token=243502240&lang=zh_CN#rd) - -[08. 面向对象编程:结构体与继承](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483698&idx=1&sn=d12e9c0d5ee8bec8ba8cf1cd319dd783&chksm=fc355b59cb42d24ff61dd30dd5e68875170bd3953fa2cfc15a4b67c272688324cc6bc9525c74&token=243502240&lang=zh_CN#rd) - -[09. 一篇文章理解 Go 里的函数](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483702&idx=1&sn=58ed79a076381b14dd1b387783dd145b&chksm=fc355b5dcb42d24b9cee52c2b912d357e3f500e90111b68b63484d5dc8e127b2114530fb3880&token=243502240&lang=zh_CN#rd) - -[10. Go语言流程控制:if-else 条件语句](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483706&idx=1&sn=f7697b9ba8fbb1ed06c9ed9da64a87a1&chksm=fc355b51cb42d247e11263580fd5917409c1e6066ef297b6bb1ba10666c0d8eae6245c713239&token=243502240&lang=zh_CN#rd) - -[11. Go语言流程控制:switch-case 选择语句](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483710&idx=1&sn=56b4c4e0e6d12f9ba650e0d0aeb6ce70&chksm=fc355b55cb42d243b9f694b017ea57de8e843fbd46e13b002cf44d251fefc446ed49bb842e26&token=243502240&lang=zh_CN#rd) - -[12. Go语言流程控制:for 循环语句](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483714&idx=1&sn=07e2838aade6d5c21e36ec078c26628c&chksm=fc355b29cb42d23fcc4b5d4b8d99646e88a63fc29842092b67e7d2c0c6f120c782f2a9642fc5&token=243502240&lang=zh_CN#rd) - -[13. Go语言流程控制:goto 无条件跳转](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483718&idx=1&sn=63cf01a472c19f9e91a200fd6edd297a&chksm=fc355b2dcb42d23b83fe9792a92e08d19d0186dec443f53a0d342046e51e6a852b6da56db08d&token=243502240&lang=zh_CN#rd) - -[14. Go语言流程控制:defer 延迟调用](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483722&idx=1&sn=90ac47d68a5b0c3c443006d272671234&chksm=fc355b21cb42d237ad329f72670a3c6828862146d11e56c424bd0e26bb26de8c7a7895fe8de8&token=243502240&lang=zh_CN#rd) - -[15. 面向对象编程:接口与多态](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483724&idx=1&sn=660fdc0c90751e3306b19c3009965836&chksm=fc355b27cb42d2317b3b9e755ef1a9544cc1cfbfffef38082dbba0641b133d4776bfbe35df1d&token=243502240&lang=zh_CN#rd) - -[16. 关键字:make 和 new 的区别?](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483728&idx=1&sn=497137228dbd44bc9b9a4141fce8c473&chksm=fc355b3bcb42d22d5d5346051763b79633f1dbdd219bdf1f6cf967b65964d11b39dd2606637e&token=243502240&lang=zh_CN#rd) - -[17. 一篇文章理解 Go 里的语句块与作用域](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483732&idx=1&sn=10f8c1dcba40b83ade76288b499a77ea&chksm=fc355b3fcb42d229b24d7909663b8e7b7238aae333e51f4ad626bb9c1c7f3df86a7ef66fd156&token=243502240&lang=zh_CN#rd) - -[18. 学习 Go 协程:goroutine](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483736&idx=1&sn=1f54126378ca6811d618e0ccb49dfb42&chksm=fc355b33cb42d225363907ec1900d80f140b15a4a122514ac5e35509e8f2303fdf198c155e96&token=243502240&lang=zh_CN#rd) - -[19. 学习 Go 协程:详解信道/通道](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483741&idx=1&sn=4d4ccd8fdee404432f03447927ddb055&chksm=fc355b36cb42d2201e12b77085f7db5a5fed98674e369a5df7fd852abde09a1efad35ba28944&token=243502240&lang=zh_CN#rd) - -[20. 几个信道死锁经典错误案例详解](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483745&idx=1&sn=390492a25034a47519c43a839c5e73df&chksm=fc355b0acb42d21cbf064d9d8301e980127d8027803d498bf027281b4dc6a61584f81f3fcf3b&token=243502240&lang=zh_CN#rd) - -[21. 学习 Go 协程:WaitGroup](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483746&idx=1&sn=5fb55f41cd5b11d7e13959dd94b454d3&chksm=fc355b09cb42d21f2efdf871dbe611db62bcd9cbfd10d0bb27fa657246dfcb561aa228a61007&token=243502240&lang=zh_CN#rd) - -[22. 学习 Go 协程:互斥锁和读写锁](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483750&idx=1&sn=e3749252c7544d621b8327bd370f4deb&chksm=fc355b0dcb42d21b9ee41dff123714d743901b2a58738a479a8cf66138cb5827c223038a23cc&token=243502240&lang=zh_CN#rd) - -[23. Go 里的异常处理:panic 和 recover](https://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483755&idx=1&sn=aaa376a579ffb277d52239a64b7b5630&chksm=fc355b00cb42d216e14fd42b93cad5e8cc9a5659d3c3359bb95ef02df01266ea79ced2ec1282&token=243502240&lang=zh_CN#rd) - - - -还在持续更新中... - - - -![](http://image.python-online.cn/20200314203721.png) - diff --git a/source/index.rst b/source/index.rst index 6f9ce04..d83ab07 100755 --- a/source/index.rst +++ b/source/index.rst @@ -19,3 +19,7 @@ Contents: thanks aboutme roadmap + +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/preface.rst b/source/preface.rst index fc32a3f..fbd03fe 100755 --- a/source/preface.rst +++ b/source/preface.rst @@ -31,9 +31,6 @@ ------------------------------ -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注公众号,获取最新文章 - - +.. figure:: http://image.python-online.cn/20200315144434.png .. _博客构建教程: http://python-online.cn/zh_CN/latest/c04/c04_03.html diff --git a/source/py_mp_index.md b/source/py_mp_index.md index 7717453..4500b4b 100644 --- a/source/py_mp_index.md +++ b/source/py_mp_index.md @@ -436,4 +436,4 @@ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117155836.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file diff --git a/source/roadmap.rst b/source/roadmap.rst index 056be5a..c5fc9ab 100755 --- a/source/roadmap.rst +++ b/source/roadmap.rst @@ -10,3 +10,6 @@ Roadmap | 整个项目的框架完成 +-------------- + +.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file diff --git a/source/thanks.rst b/source/thanks.rst index bae1130..05fc94c 100644 --- a/source/thanks.rst +++ b/source/thanks.rst @@ -19,11 +19,10 @@ 贡献历史 ---------------------------------- -2019.7.4 +2019.7.4 @魏朝阳:提出在《并发编程》中三处笔误,现已更正! ------------------------------- +-------------- -.. figure:: http://image.python-online.cn/20191117155836.png - :alt: 关注明哥公众号 +.. figure:: http://image.python-online.cn/20200315144434.png From 59f80af4667657c22755f21d9033bd492451be21 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 15 Mar 2020 17:01:03 +0800 Subject: [PATCH 021/147] =?UTF-8?q?update:=20=E7=AC=AC=E5=8D=81=E7=AB=A0?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E7=AC=AC=E4=B9=9D=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c09/c09_01.md | 2 +- source/c09/c09_01.rst | 2 +- source/chapters/p09.rst | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index a9b3c9a..988cb23 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -1,4 +1,4 @@ -# 10.1 情人节来了,教你使用 Python 来表白 +# 9.1 情人节来了,教你使用 Python 来表白 **作者**:@明哥 **公众号**:Python编程时光 diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 50fed20..100c38a 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -1,4 +1,4 @@ -10.1 情人节来了,教你使用 Python 来表白 +9.1 情人节来了,教你使用 Python 来表白 ======================================= **作者**\ :@明哥 **公众号**\ :Python编程时光 diff --git a/source/chapters/p09.rst b/source/chapters/p09.rst index 78ed63c..bda6dfd 100644 --- a/source/chapters/p09.rst +++ b/source/chapters/p09.rst @@ -1,5 +1,5 @@ ============================= -第十章:有趣的工具 +第九章:有趣的工具 ============================= Python 是一门可以快速开发的语言,使用它可以用最少的时间,做出符合预期,且效果还不错的产品。 @@ -14,7 +14,7 @@ Python 是一门可以快速开发的语言,使用它可以用最少的时间 :maxdepth: 1 :glob: - ../c10/* + ../c09/* -------------- From c76deff264ec42d4b12e6416c6bc7e504bb7a23a Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 15 Mar 2020 17:04:31 +0800 Subject: [PATCH 022/147] Update readme --- README.md | 35 ++--------------------------------- source/c09/c09_01.rst | 2 +- source/chapters/p01.rst | 2 +- source/chapters/p02.rst | 2 +- source/chapters/p03.rst | 2 +- source/chapters/p04.rst | 2 +- source/chapters/p05.rst | 2 +- source/chapters/p06.rst | 2 +- source/chapters/p07.rst | 2 +- source/chapters/p08.rst | 2 +- source/chapters/p09.rst | 2 +- source/index.rst | 2 +- source/roadmap.rst | 2 +- 13 files changed, 14 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index c3a680e..c7e416c 100644 --- a/README.md +++ b/README.md @@ -135,39 +135,8 @@ - 8.14 [支持 IPv6以及多运营商](http://python-online.cn/zh_CN/latest/c08/c08_14.html) - 8.15 [Neutron 源码解读](http://python-online.cn/zh_CN/latest/c08/c08_15.html) -## 第九章:Go 语言之路 -- 9.1 [开发环境的搭建(Goland和VSCode)](http://python-online.cn/zh_CN/latest/c09/c09_01.html) -- 9.2 [五种变量创建的方法](http://python-online.cn/zh_CN/latest/c09/c09_02.html) -- 9.3 [详解数据类型:整型与浮点型](http://python-online.cn/zh_CN/latest/c09/c09_03.html) -- 9.4 [详解数据类型:byte、rune与字符串](http://python-online.cn/zh_CN/latest/c09/c09_04.html) -- 9.7 [详解数据类型:数组与切片](http://python-online.cn/zh_CN/latest/c09/c09_05.html) -- 9.6 [详解数据类型:字典与布尔类型](http://python-online.cn/zh_CN/latest/c09/c09_06.html) -- 9.7 [详解数据类型:指针](http://python-online.cn/zh_CN/latest/c09/c09_07.html) -- 9.8 [面向对象编程:结构体与继承](http://python-online.cn/zh_CN/latest/c09/c09_08.html) -- 9.9 [一篇文章理解 Go 里的函数](http://python-online.cn/zh_CN/latest/c09/c09_09.html) -- 9.10 [Go语言流程控制:if-else](http://python-online.cn/zh_CN/latest/c09/c09_10.html) -- 9.11 [Go语言流程控制:switch-case](http://python-online.cn/zh_CN/latest/c09/c09_11.html) -- 9.11 [Go语言流程控制:for](http://python-online.cn/zh_CN/latest/c09/c09_12.html) -- 9.13 [Go语言流程控制:goto 无条件跳转](http://python-online.cn/zh_CN/latest/c09/c09_13.html) -- 9.14 [Go语言流程控制:defer 延迟语句](http://python-online.cn/zh_CN/latest/c09/c09_14.html) -- 9.15 [面向对象编程:接口与多态](http://python-online.cn/zh_CN/latest/c09/c09_15.html) -- 9.16 [关键字:make 和 new 的区别?](http://python-online.cn/zh_CN/latest/c09/c09_16.html) -- 9.17 [理解语句块与作用域](http://python-online.cn/zh_CN/latest/c09/c09_17.html) -- 9.18 [理解 Go 协程:goroutine](http://python-online.cn/zh_CN/latest/c09/c09_18.html) -- 9.19 [学习 Go 协程:详解信道/通道](http://python-online.cn/zh_CN/latest/c09/c09_19.html) -- 9.20 [几个信道死锁经典错误案例详解](http://python-online.cn/zh_CN/latest/c09/c09_20.html) -- 9.21 [学习 Go 协程:WaitGroup](http://python-online.cn/zh_CN/latest/c09/c09_21.html) -- 9.22 [学习 Go 协程:互斥锁和读写锁](http://python-online.cn/zh_CN/latest/c09/c09_22.html) -- 9.23 [panic 和 recover](http://python-online.cn/zh_CN/latest/c09/c09_23.html) -- 9.24 [超级详细的Go语言包管理方案演变及 go mod 入门](http://python-online.cn/zh_CN/latest/c09/c09_24.html) -- 9.26 [Go语言命名编码规范](http://python-online.cn/zh_CN/latest/c09/c09_26.html) -- 9.27 [go 命令详解](http://python-online.cn/zh_CN/latest/c09/c09_27.html) -- 9.28 [函数式编程](http://python-online.cn/zh_CN/latest/c09/c09_28.html) -- 9.29 [学习一些常见的并发模型](http://python-online.cn/zh_CN/latest/c09/c09_29.html) -- 9.40 [推荐几个Go学习网站](http://python-online.cn/zh_CN/latest/c09/c09_40.html) - -## 第十章:有趣的工具 -- 10.1 [情人节来了,教你使用 Python 来表白](http://python-online.cn/zh_CN/latest/c10/c10_01.html) +## 第九章:有趣的工具 +- 9.1 [情人节来了,教你使用 Python 来表白](http://python-online.cn/zh_CN/latest/c09/c09_01.html) --- diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 100c38a..4528264 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -1,5 +1,5 @@ 9.1 情人节来了,教你使用 Python 来表白 -======================================= +====================================== **作者**\ :@明哥 **公众号**\ :Python编程时光 diff --git a/source/chapters/p01.rst b/source/chapters/p01.rst index 203bd32..e7b7e8d 100755 --- a/source/chapters/p01.rst +++ b/source/chapters/p01.rst @@ -26,4 +26,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p02.rst b/source/chapters/p02.rst index 6fa8859..fbd3909 100755 --- a/source/chapters/p02.rst +++ b/source/chapters/p02.rst @@ -18,4 +18,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p03.rst b/source/chapters/p03.rst index 6899aad..f87a845 100755 --- a/source/chapters/p03.rst +++ b/source/chapters/p03.rst @@ -18,4 +18,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p04.rst b/source/chapters/p04.rst index 82b4344..04f3a86 100755 --- a/source/chapters/p04.rst +++ b/source/chapters/p04.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p05.rst b/source/chapters/p05.rst index d046a01..6de09b5 100755 --- a/source/chapters/p05.rst +++ b/source/chapters/p05.rst @@ -28,4 +28,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p06.rst b/source/chapters/p06.rst index 357fc56..4a9c044 100755 --- a/source/chapters/p06.rst +++ b/source/chapters/p06.rst @@ -17,4 +17,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p07.rst b/source/chapters/p07.rst index 578832c..6456af0 100755 --- a/source/chapters/p07.rst +++ b/source/chapters/p07.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p08.rst b/source/chapters/p08.rst index 06ac496..66e6489 100755 --- a/source/chapters/p08.rst +++ b/source/chapters/p08.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/chapters/p09.rst b/source/chapters/p09.rst index bda6dfd..7fccba4 100644 --- a/source/chapters/p09.rst +++ b/source/chapters/p09.rst @@ -18,4 +18,4 @@ Python 是一门可以快速开发的语言,使用它可以用最少的时间 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/index.rst b/source/index.rst index d83ab07..6cc5eb4 100755 --- a/source/index.rst +++ b/source/index.rst @@ -22,4 +22,4 @@ Contents: -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png diff --git a/source/roadmap.rst b/source/roadmap.rst index c5fc9ab..ac9b50a 100755 --- a/source/roadmap.rst +++ b/source/roadmap.rst @@ -12,4 +12,4 @@ Roadmap -------------- -.. figure:: http://image.python-online.cn/20200315144434.png \ No newline at end of file +.. figure:: http://image.python-online.cn/20200315144434.png From cb3b8fe4a7aa6fb337aa90551174eb5dc3bd90f6 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 15 Mar 2020 18:56:59 +0800 Subject: [PATCH 023/147] =?UTF-8?q?Update:=20=E5=9F=9F=E5=90=8D=E8=BF=81?= =?UTF-8?q?=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 240 +++++++++++++++++++++++++++--------------------------- md2rst.py | 6 +- 2 files changed, 123 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index c7e416c..e815e8b 100644 --- a/README.md +++ b/README.md @@ -1,142 +1,142 @@ -这是我的个人博客( [MING's BLOG](http://python-online.cn/) ),主要写关于Python的一些思考总结。 +这是我的个人博客( [Python编程时光](http://python.iswbm.com/) ),主要写关于Python的一些思考总结。 -关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python-online.cn/zh_CN/latest/c04/c04_03.html) +关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python.iswbm.com/en/latest/c04/c04_03.html) ## 第一章:基础知识 -- 1.1 [13条Python2.x和3.x的区别?](http://python-online.cn/zh_CN/latest/c01/c01_01.html) -- 1.4 [什么是猴子补丁?](http://python-online.cn/zh_CN/latest/c01/c01_04.html) -- 1.5 [深入闭包与变量作用域](http://python-online.cn/zh_CN/latest/c01/c01_05.html) -- 1.6 [深入理解元组存在的意义](http://python-online.cn/zh_CN/latest/c01/c01_06.html) -- 1.7 [15个Pythonic的代码示例](http://python-online.cn/zh_CN/latest/c01/c01_07.html) -- 1.8 [新式类和经典类的区别?](http://python-online.cn/zh_CN/latest/c01/c01_08.html) -- 1.9 [多继承与Mixin设计模式](http://python-online.cn/zh_CN/latest/c01/c01_09.html) -- 1.10 [Python 冷知识 40 讲](http://python-online.cn/zh_CN/latest/c01/c01_10.html) -- 1.11 [正则表达式必知必会](http://python-online.cn/zh_CN/latest/c01/c01_11.html) -- 1.12 [搞懂字符编码的前世今生](http://python-online.cn/zh_CN/latest/c01/c01_12.html) -- 1.13 [Python几个高阶函数](http://python-online.cn/zh_CN/latest/c01/c01_13.html) -- 1.14 [with 与 上下文管理器](http://python-online.cn/zh_CN/latest/c01/c01_14.html) -- 1.15 [提升Python性能的7个习惯](http://python-online.cn/zh_CN/latest/c01/c01_15.html) -- 1.16 [泛型函数怎么写?](http://python-online.cn/zh_CN/latest/c01/c01_16.html) -- 1.17 [深入理解「描述符」](http://python-online.cn/zh_CN/latest/c01/c01_17.html) -- 1.18 [MySQL 使用总结](http://python-online.cn/zh_CN/latest/c01/c01_18.html) -- 1.20 [静态方法其实暗藏玄机](http://python-online.cn/zh_CN/latest/c01/c01_20.html) -- 1.21 [开发小技巧](http://python-online.cn/zh_CN/latest/c01/c01_21.html) -- 1.22 [如何修改 CentOS 6.x 上默认Python](http://python-online.cn/zh_CN/latest/c01/c01_22.html) -- 1.23 [Pythonista 学习 Js](http://python-online.cn/zh_CN/latest/c01/c01_23.html) -- 1.24 [深入探讨 Python 的 import 机制:实现远程导入模块](http://python-online.cn/zh_CN/latest/c01/c01_24.html) -- 1.25 [50% 的人不知道的Python 包与模块的知识盲区](http://python-online.cn/zh_CN/latest/c01/c01_25.html) -- 1.26 [C语言基础的学习](http://python-online.cn/zh_CN/latest/c01/c01_26.html) -- 1.27 [全面学习 Python 包:包的构建与分发](http://python-online.cn/zh_CN/latest/c01/c01_27.html) -- 1.27 [如何阅读 CPython源码?](http://python-online.cn/zh_CN/latest/c01/c01_29.html) -- 1.30 [学习编程的几大网站](http://python-online.cn/zh_CN/latest/c01/c01_30.html) -- 1.31 [学习 Pillow 笔记](http://python-online.cn/zh_CN/latest/c01/c01_31.html) -- 1.32 [在 CentOS 7.2 上安装 Python3.7](http://python-online.cn/zh_CN/latest/c01/c01_32.html) -- 1.33 [如何调试已经运行中的程序](http://python-online.cn/zh_CN/latest/c01/c01_33.html) -- 1.34 [每日一库:sh,最优雅的命令调用方式](http://python-online.cn/zh_CN/latest/c01/c01_34.html) -- 1.35 [使用 Python 远程登陆服务器的利器](http://python-online.cn/zh_CN/latest/c01/c01_35.html) -- 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python-online.cn/zh_CN/latest/c01/c01_36.html) +- 1.1 [13条Python2.x和3.x的区别?](http://python.iswbm.com/en/latest/c01/c01_01.html) +- 1.4 [什么是猴子补丁?](http://python.iswbm.com/en/latest/c01/c01_04.html) +- 1.5 [深入闭包与变量作用域](http://python.iswbm.com/en/latest/c01/c01_05.html) +- 1.6 [深入理解元组存在的意义](http://python.iswbm.com/en/latest/c01/c01_06.html) +- 1.7 [15个Pythonic的代码示例](http://python.iswbm.com/en/latest/c01/c01_07.html) +- 1.8 [新式类和经典类的区别?](http://python.iswbm.com/en/latest/c01/c01_08.html) +- 1.9 [多继承与Mixin设计模式](http://python.iswbm.com/en/latest/c01/c01_09.html) +- 1.10 [Python 冷知识 40 讲](http://python.iswbm.com/en/latest/c01/c01_10.html) +- 1.11 [正则表达式必知必会](http://python.iswbm.com/en/latest/c01/c01_11.html) +- 1.12 [搞懂字符编码的前世今生](http://python.iswbm.com/en/latest/c01/c01_12.html) +- 1.13 [Python几个高阶函数](http://python.iswbm.com/en/latest/c01/c01_13.html) +- 1.14 [with 与 上下文管理器](http://python.iswbm.com/en/latest/c01/c01_14.html) +- 1.15 [提升Python性能的7个习惯](http://python.iswbm.com/en/latest/c01/c01_15.html) +- 1.16 [泛型函数怎么写?](http://python.iswbm.com/en/latest/c01/c01_16.html) +- 1.17 [深入理解「描述符」](http://python.iswbm.com/en/latest/c01/c01_17.html) +- 1.18 [MySQL 使用总结](http://python.iswbm.com/en/latest/c01/c01_18.html) +- 1.20 [静态方法其实暗藏玄机](http://python.iswbm.com/en/latest/c01/c01_20.html) +- 1.21 [开发小技巧](http://python.iswbm.com/en/latest/c01/c01_21.html) +- 1.22 [如何修改 CentOS 6.x 上默认Python](http://python.iswbm.com/en/latest/c01/c01_22.html) +- 1.23 [Pythonista 学习 Js](http://python.iswbm.com/en/latest/c01/c01_23.html) +- 1.24 [深入探讨 Python 的 import 机制:实现远程导入模块](http://python.iswbm.com/en/latest/c01/c01_24.html) +- 1.25 [50% 的人不知道的Python 包与模块的知识盲区](http://python.iswbm.com/en/latest/c01/c01_25.html) +- 1.26 [C语言基础的学习](http://python.iswbm.com/en/latest/c01/c01_26.html) +- 1.27 [全面学习 Python 包:包的构建与分发](http://python.iswbm.com/en/latest/c01/c01_27.html) +- 1.27 [如何阅读 CPython源码?](http://python.iswbm.com/en/latest/c01/c01_29.html) +- 1.30 [学习编程的几大网站](http://python.iswbm.com/en/latest/c01/c01_30.html) +- 1.31 [学习 Pillow 笔记](http://python.iswbm.com/en/latest/c01/c01_31.html) +- 1.32 [在 CentOS 7.2 上安装 Python3.7](http://python.iswbm.com/en/latest/c01/c01_32.html) +- 1.33 [如何调试已经运行中的程序](http://python.iswbm.com/en/latest/c01/c01_33.html) +- 1.34 [每日一库:sh,最优雅的命令调用方式](http://python.iswbm.com/en/latest/c01/c01_34.html) +- 1.35 [使用 Python 远程登陆服务器的利器](http://python.iswbm.com/en/latest/c01/c01_35.html) +- 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python.iswbm.com/en/latest/c01/c01_36.html) ## 第二章:并发编程 -- 2.1 [从性能角度初探并发编程](http://python-online.cn/zh_CN/latest/c02/c02_01.html) -- 2.2 [创建多线程的几种方法](http://python-online.cn/zh_CN/latest/c02/c02_02.html) -- 2.3 [谈谈线程中的“锁机制”](http://python-online.cn/zh_CN/latest/c02/c02_03.html) -- 2.4 [线程消息通信机制](http://python-online.cn/zh_CN/latest/c02/c02_04.html) -- 2.5 [线程中的信息隔离](http://python-online.cn/zh_CN/latest/c02/c02_05.html) -- 2.6 [如何创建线程池](http://python-online.cn/zh_CN/latest/c02/c02_06.html) -- 2.7 [从生成器使用入门协程](http://python-online.cn/zh_CN/latest/c02/c02_07.html) -- 2.8 [深入理解yield from语法](http://python-online.cn/zh_CN/latest/c02/c02_08.html) -- 2.9 [初识异步IO框架:asyncio 上篇](http://python-online.cn/zh_CN/latest/c02/c02_09.html) -- 2.10 [深入异步IO框架:asyncio 中篇](http://python-online.cn/zh_CN/latest/c02/c02_10.html) -- 2.11 [实战异步IO框架:asyncio 下篇](http://python-online.cn/zh_CN/latest/c02/c02_11.html) -- 2.12 [生成器与协程,你分清了吗?](http://python-online.cn/zh_CN/latest/c02/c02_12.html) -- 2.13 [I/O多路复用:select/poll/epoll](http://python-online.cn/zh_CN/latest/c02/c02_13.html) +- 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) +- 2.2 [创建多线程的几种方法](http://python.iswbm.com/en/latest/c02/c02_02.html) +- 2.3 [谈谈线程中的“锁机制”](http://python.iswbm.com/en/latest/c02/c02_03.html) +- 2.4 [线程消息通信机制](http://python.iswbm.com/en/latest/c02/c02_04.html) +- 2.5 [线程中的信息隔离](http://python.iswbm.com/en/latest/c02/c02_05.html) +- 2.6 [如何创建线程池](http://python.iswbm.com/en/latest/c02/c02_06.html) +- 2.7 [从生成器使用入门协程](http://python.iswbm.com/en/latest/c02/c02_07.html) +- 2.8 [深入理解yield from语法](http://python.iswbm.com/en/latest/c02/c02_08.html) +- 2.9 [初识异步IO框架:asyncio 上篇](http://python.iswbm.com/en/latest/c02/c02_09.html) +- 2.10 [深入异步IO框架:asyncio 中篇](http://python.iswbm.com/en/latest/c02/c02_10.html) +- 2.11 [实战异步IO框架:asyncio 下篇](http://python.iswbm.com/en/latest/c02/c02_11.html) +- 2.12 [生成器与协程,你分清了吗?](http://python.iswbm.com/en/latest/c02/c02_12.html) +- 2.13 [I/O多路复用:select/poll/epoll](http://python.iswbm.com/en/latest/c02/c02_13.html) ## 第三章:高级编程 -- 3.1 [装饰器进阶用法详解](http://python-online.cn/zh_CN/latest/c03/c03_01.html) -- 3.2 [深入理解Python元类](http://python-online.cn/zh_CN/latest/c03/c03_02.html) -- 3.3 [Socket编程实现在线聊天](http://python-online.cn/zh_CN/latest/c03/c03_03.html) -- 3.4 [Django+Uwsgi部署网站](http://python-online.cn/zh_CN/latest/c03/c03_04.html) -- 3.5 [源码解读:Flask上下文与代理模式](http://python-online.cn/zh_CN/latest/c03/c03_05.html) -- 3.6 [Web开发者必看:理解WSGI](http://python-online.cn/zh_CN/latest/c03/c03_06.html) +- 3.1 [装饰器进阶用法详解](http://python.iswbm.com/en/latest/c03/c03_01.html) +- 3.2 [深入理解Python元类](http://python.iswbm.com/en/latest/c03/c03_02.html) +- 3.3 [Socket编程实现在线聊天](http://python.iswbm.com/en/latest/c03/c03_03.html) +- 3.4 [Django+Uwsgi部署网站](http://python.iswbm.com/en/latest/c03/c03_04.html) +- 3.5 [源码解读:Flask上下文与代理模式](http://python.iswbm.com/en/latest/c03/c03_05.html) +- 3.6 [Web开发者必看:理解WSGI](http://python.iswbm.com/en/latest/c03/c03_06.html) ## 第四章:开发工具 -- 4.1 [虚拟环境:virtualenv](http://python-online.cn/zh_CN/latest/c04/c04_01.html) -- 4.2 [Xshell的高效使用手册](http://python-online.cn/zh_CN/latest/c04/c04_02.html) -- 4.3 [30分钟教你搭建一个博客](http://python-online.cn/zh_CN/latest/c04/c04_03.html) -- 4.4 [Jupyter NoteBook 使用指南](http://python-online.cn/zh_CN/latest/c04/c04_04.html) -- 4.5 [Win10+Ubuntu 双系统安装教程](http://python-online.cn/zh_CN/latest/c04/c04_05.html) -- 4.6 [我的 Git 使用指南](http://python-online.cn/zh_CN/latest/c04/c04_06.html) -- 4.7 [Hexo 搭建博客教程](http://python-online.cn/zh_CN/latest/c04/c04_07.html) -- 4.8 [珍惜生命,远离鼠标](http://python-online.cn/zh_CN/latest/c04/c04_08.html) -- 4.9 [MySQL的基本使用](http://python-online.cn/zh_CN/latest/c04/c04_09.html) -- 4.10 [MySQL的高级进阶](http://python-online.cn/zh_CN/latest/c04/c04_10.html) -- 4.11 [不能不会的远程调试技巧](http://python-online.cn/zh_CN/latest/c04/c04_11.html) -- 4.12 [服务器调试神器:pdb](http://python-online.cn/zh_CN/latest/c04/c04_12.html) -- 4.13 [命令行解析工具:argparse](http://python-online.cn/zh_CN/latest/c04/c04_13.html) -- 4.14 [虚拟环境:Pipenv](http://python-online.cn/zh_CN/latest/c04/c04_14.html) -- 4.15 [30个 PyCharm 实用技巧](http://python-online.cn/zh_CN/latest/c04/c04_15.html) -- 4.16 [Python 开发技巧集合](http://python-online.cn/zh_CN/latest/c04/c04_16.html) -- 4.17 [详解 23 种设计模式](http://python-online.cn/zh_CN/latest/c04/c04_17.html) -- 4.18 [如何上手Mac ?](http://python-online.cn/zh_CN/latest/c04/c04_18.html) -- 4.19 [程序员编码必学:Vim](http://python-online.cn/zh_CN/latest/c04/c04_19.html) -- 4.20 [学会使用谷歌搜索引擎](http://python-online.cn/zh_CN/latest/c04/c04_20.html) -- 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python-online.cn/zh_CN/latest/c04/c04_21.html) -- 4.22 [用好 Chrome 必看](http://python-online.cn/zh_CN/latest/c04/c04_22.html) -- 4.23 [电脑使用技巧](http://python-online.cn/zh_CN/latest/c04/c04_23.html) +- 4.1 [虚拟环境:virtualenv](http://python.iswbm.com/en/latest/c04/c04_01.html) +- 4.2 [Xshell的高效使用手册](http://python.iswbm.com/en/latest/c04/c04_02.html) +- 4.3 [30分钟教你搭建一个博客](http://python.iswbm.com/en/latest/c04/c04_03.html) +- 4.4 [Jupyter NoteBook 使用指南](http://python.iswbm.com/en/latest/c04/c04_04.html) +- 4.5 [Win10+Ubuntu 双系统安装教程](http://python.iswbm.com/en/latest/c04/c04_05.html) +- 4.6 [我的 Git 使用指南](http://python.iswbm.com/en/latest/c04/c04_06.html) +- 4.7 [Hexo 搭建博客教程](http://python.iswbm.com/en/latest/c04/c04_07.html) +- 4.8 [珍惜生命,远离鼠标](http://python.iswbm.com/en/latest/c04/c04_08.html) +- 4.9 [MySQL的基本使用](http://python.iswbm.com/en/latest/c04/c04_09.html) +- 4.10 [MySQL的高级进阶](http://python.iswbm.com/en/latest/c04/c04_10.html) +- 4.11 [不能不会的远程调试技巧](http://python.iswbm.com/en/latest/c04/c04_11.html) +- 4.12 [服务器调试神器:pdb](http://python.iswbm.com/en/latest/c04/c04_12.html) +- 4.13 [命令行解析工具:argparse](http://python.iswbm.com/en/latest/c04/c04_13.html) +- 4.14 [虚拟环境:Pipenv](http://python.iswbm.com/en/latest/c04/c04_14.html) +- 4.15 [30个 PyCharm 实用技巧](http://python.iswbm.com/en/latest/c04/c04_15.html) +- 4.16 [Python 开发技巧集合](http://python.iswbm.com/en/latest/c04/c04_16.html) +- 4.17 [详解 23 种设计模式](http://python.iswbm.com/en/latest/c04/c04_17.html) +- 4.18 [如何上手Mac ?](http://python.iswbm.com/en/latest/c04/c04_18.html) +- 4.19 [程序员编码必学:Vim](http://python.iswbm.com/en/latest/c04/c04_19.html) +- 4.20 [学会使用谷歌搜索引擎](http://python.iswbm.com/en/latest/c04/c04_20.html) +- 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python.iswbm.com/en/latest/c04/c04_21.html) +- 4.22 [用好 Chrome 必看](http://python.iswbm.com/en/latest/c04/c04_22.html) +- 4.23 [电脑使用技巧](http://python.iswbm.com/en/latest/c04/c04_23.html) ## 第五章:算法教程 -- 5.1 [图解九大经典排序算法](http://python-online.cn/zh_CN/latest/c05/c05_01.html) -- 5.2 [递归算法:走楼梯会思考的题](http://python-online.cn/zh_CN/latest/c05/c05_02.html) -- 5.3 [哈希算法:安全方面的算法应用](http://python-online.cn/zh_CN/latest/c05/c05_03.html) +- 5.1 [图解九大经典排序算法](http://python.iswbm.com/en/latest/c05/c05_01.html) +- 5.2 [递归算法:走楼梯会思考的题](http://python.iswbm.com/en/latest/c05/c05_02.html) +- 5.3 [哈希算法:安全方面的算法应用](http://python.iswbm.com/en/latest/c05/c05_03.html) ## 第六章:数据分析 -- 6.1 [一张图带你入门matplotlib](http://python-online.cn/zh_CN/latest/c06/c06_01.html) -- 6.2 [详解六种可视化图表](http://python-online.cn/zh_CN/latest/c06/c06_02.html) -- 6.3 [如何绘制正余弦函数图象](http://python-online.cn/zh_CN/latest/c06/c06_03.html) -- 6.4 [子图与子区 难点突破](http://python-online.cn/zh_CN/latest/c06/c06_04.html) -- 6.5 [绘制酷炫的gif动态图](http://python-online.cn/zh_CN/latest/c06/c06_05.html) -- 6.6 [自动生成图像视频](http://python-online.cn/zh_CN/latest/c06/c06_06.html) +- 6.1 [一张图带你入门matplotlib](http://python.iswbm.com/en/latest/c06/c06_01.html) +- 6.2 [详解六种可视化图表](http://python.iswbm.com/en/latest/c06/c06_02.html) +- 6.3 [如何绘制正余弦函数图象](http://python.iswbm.com/en/latest/c06/c06_03.html) +- 6.4 [子图与子区 难点突破](http://python.iswbm.com/en/latest/c06/c06_04.html) +- 6.5 [绘制酷炫的gif动态图](http://python.iswbm.com/en/latest/c06/c06_05.html) +- 6.6 [自动生成图像视频](http://python.iswbm.com/en/latest/c06/c06_06.html) ## 第七章:运维人生 -- 7.1 [Linux 命令行的艺术](http://python-online.cn/zh_CN/latest/c07/c07_01.html) -- 7.2 [Zabbix 监控部署文档](http://python-online.cn/zh_CN/latest/c07/c07_02.html) -- 7.3 [Docker:Hello world](http://python-online.cn/zh_CN/latest/c07/c07_03.html) -- 7.4 [Docker:关于镜像](http://python-online.cn/zh_CN/latest/c07/c07_04.html) -- 7.5 [Docker:网络通信](http://python-online.cn/zh_CN/latest/c07/c07_05.html) -- 7.6 [Docker:存储与多主机](http://python-online.cn/zh_CN/latest/c07/c07_06.html) -- 7.7 [SaltStack 入门指南](http://python-online.cn/zh_CN/latest/c07/c07_07.html) -- 7.8 [Keepalived 部署文档](http://python-online.cn/zh_CN/latest/c07/c07_08.html) -- 7.9 [Ansible 入门指南使用手册](http://python-online.cn/zh_CN/latest/c07/c07_09.html) -- 7.10 [Ansible API 最全使用文档(中文)](http://python-online.cn/zh_CN/latest/c07/c07_10.html) -- 7.11 [K8S:基础入门](http://python-online.cn/zh_CN/latest/c07/c07_11.html) -- 7.12 [yum 的使用总结](http://python-online.cn/zh_CN/latest/c07/c07_12.html) -- 7.13 [基于 ansible-api 二次开发](http://python-online.cn/zh_CN/latest/c07/c07_13.html) -- 7.14 [Linux 如何写判断语句](http://python-online.cn/zh_CN/latest/c07/c07_14.html) -- 7.15 [Mariadb 与 Galera 集群总结](http://python-online.cn/zh_CN/latest/c07/c07_15.html) -- 7.16 [Linux 运维之路](http://python-online.cn/zh_CN/latest/c07/c07_16.html) -- 1.17 [ansible 自定义 Jinja2 过滤器](http://python-online.cn/zh_CN/latest/c07/c07_17.html) -- 7.18 [Shell中去除字符串前后空格的方法](http://python-online.cn/zh_CN/latest/c07/c07_18.html) -- 7.19 [Ansible 使用教程](http://python-online.cn/zh_CN/latest/c07/c07_19.html) +- 7.1 [Linux 命令行的艺术](http://python.iswbm.com/en/latest/c07/c07_01.html) +- 7.2 [Zabbix 监控部署文档](http://python.iswbm.com/en/latest/c07/c07_02.html) +- 7.3 [Docker:Hello world](http://python.iswbm.com/en/latest/c07/c07_03.html) +- 7.4 [Docker:关于镜像](http://python.iswbm.com/en/latest/c07/c07_04.html) +- 7.5 [Docker:网络通信](http://python.iswbm.com/en/latest/c07/c07_05.html) +- 7.6 [Docker:存储与多主机](http://python.iswbm.com/en/latest/c07/c07_06.html) +- 7.7 [SaltStack 入门指南](http://python.iswbm.com/en/latest/c07/c07_07.html) +- 7.8 [Keepalived 部署文档](http://python.iswbm.com/en/latest/c07/c07_08.html) +- 7.9 [Ansible 入门指南使用手册](http://python.iswbm.com/en/latest/c07/c07_09.html) +- 7.10 [Ansible API 最全使用文档(中文)](http://python.iswbm.com/en/latest/c07/c07_10.html) +- 7.11 [K8S:基础入门](http://python.iswbm.com/en/latest/c07/c07_11.html) +- 7.12 [yum 的使用总结](http://python.iswbm.com/en/latest/c07/c07_12.html) +- 7.13 [基于 ansible-api 二次开发](http://python.iswbm.com/en/latest/c07/c07_13.html) +- 7.14 [Linux 如何写判断语句](http://python.iswbm.com/en/latest/c07/c07_14.html) +- 7.15 [Mariadb 与 Galera 集群总结](http://python.iswbm.com/en/latest/c07/c07_15.html) +- 7.16 [Linux 运维之路](http://python.iswbm.com/en/latest/c07/c07_16.html) +- 1.17 [ansible 自定义 Jinja2 过滤器](http://python.iswbm.com/en/latest/c07/c07_17.html) +- 7.18 [Shell中去除字符串前后空格的方法](http://python.iswbm.com/en/latest/c07/c07_18.html) +- 7.19 [Ansible 使用教程](http://python.iswbm.com/en/latest/c07/c07_19.html) ## 第八章:OpenStack -- 8.1 [OpenStack 运维命令](http://python-online.cn/zh_CN/latest/c08/c08_01.html) -- 8.2 [OpenStack 部署SR-IOV](http://python-online.cn/zh_CN/latest/c08/c08_02.html) -- 8.3 [制作 OpenStack 镜像](http://python-online.cn/zh_CN/latest/c08/c08_03.html) -- 8.4 [云计算与虚拟化入门通识](http://python-online.cn/zh_CN/latest/c08/c08_04.html) -- 8.5 [OpenStack 源码剖析与改造](http://python-online.cn/zh_CN/latest/c08/c08_05.html) -- 8.6 [源码解读:Cloud-Init](http://python-online.cn/zh_CN/latest/c08/c08_06.html) -- 8.7 [OpenStack 实现GPU直通](http://python-online.cn/zh_CN/latest/c08/c08_07.html) -- 8.8 [OpenStack 如何使用DHCP?](http://python-online.cn/zh_CN/latest/c08/c08_08.html) -- 8.9 [从0到1:全面理解RPC远程调用](http://python-online.cn/zh_CN/latest/c08/c08_09.html) -- 8.16 [修改 KVM 镜像文件的三种方法](http://python-online.cn/zh_CN/latest/c08/c08_10.html) -- 8.11 [OpenStack 问题排查](http://python-online.cn/zh_CN/latest/c08/c08_11.html) -- 8.12 [OpenStack之主机调度](http://python-online.cn/zh_CN/latest/c08/c08_12.html) -- 8.13 [网络知识必知必会](http://python-online.cn/zh_CN/latest/c08/c08_13.html) -- 8.14 [支持 IPv6以及多运营商](http://python-online.cn/zh_CN/latest/c08/c08_14.html) -- 8.15 [Neutron 源码解读](http://python-online.cn/zh_CN/latest/c08/c08_15.html) +- 8.1 [OpenStack 运维命令](http://python.iswbm.com/en/latest/c08/c08_01.html) +- 8.2 [OpenStack 部署SR-IOV](http://python.iswbm.com/en/latest/c08/c08_02.html) +- 8.3 [制作 OpenStack 镜像](http://python.iswbm.com/en/latest/c08/c08_03.html) +- 8.4 [云计算与虚拟化入门通识](http://python.iswbm.com/en/latest/c08/c08_04.html) +- 8.5 [OpenStack 源码剖析与改造](http://python.iswbm.com/en/latest/c08/c08_05.html) +- 8.6 [源码解读:Cloud-Init](http://python.iswbm.com/en/latest/c08/c08_06.html) +- 8.7 [OpenStack 实现GPU直通](http://python.iswbm.com/en/latest/c08/c08_07.html) +- 8.8 [OpenStack 如何使用DHCP?](http://python.iswbm.com/en/latest/c08/c08_08.html) +- 8.9 [从0到1:全面理解RPC远程调用](http://python.iswbm.com/en/latest/c08/c08_09.html) +- 8.16 [修改 KVM 镜像文件的三种方法](http://python.iswbm.com/en/latest/c08/c08_10.html) +- 8.11 [OpenStack 问题排查](http://python.iswbm.com/en/latest/c08/c08_11.html) +- 8.12 [OpenStack之主机调度](http://python.iswbm.com/en/latest/c08/c08_12.html) +- 8.13 [网络知识必知必会](http://python.iswbm.com/en/latest/c08/c08_13.html) +- 8.14 [支持 IPv6以及多运营商](http://python.iswbm.com/en/latest/c08/c08_14.html) +- 8.15 [Neutron 源码解读](http://python.iswbm.com/en/latest/c08/c08_15.html) ## 第九章:有趣的工具 -- 9.1 [情人节来了,教你使用 Python 来表白](http://python-online.cn/zh_CN/latest/c09/c09_01.html) +- 9.1 [情人节来了,教你使用 Python 来表白](http://python.iswbm.com/en/latest/c09/c09_01.html) --- diff --git a/md2rst.py b/md2rst.py index 2f979d7..6c7280f 100644 --- a/md2rst.py +++ b/md2rst.py @@ -24,11 +24,11 @@ # 没有文件变更 os._exit(0) -base_link = "http://python-online.cn/zh_CN/latest/" +base_link = "http://python.iswbm.com/en/latest/" readme_header = ''' -这是我的个人博客( [MING's BLOG](http://python-online.cn/) ),主要写关于Python的一些思考总结。 +这是我的个人博客( [Python编程时光](http://python.iswbm.com/) ),主要写关于Python的一些思考总结。 -关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python-online.cn/zh_CN/latest/c04/c04_03.html) +关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python.iswbm.com/en/latest/c04/c04_03.html) ''' readme_tooter = ''' --- From 610da2e817cef38ae2941eb40a37536e4de7d2d7 Mon Sep 17 00:00:00 2001 From: wangbm Date: Wed, 18 Mar 2020 18:26:29 +0800 Subject: [PATCH 024/147] =?UTF-8?q?Update:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_13.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/source/c04/c04_13.md b/source/c04/c04_13.md index 84a8711..f988079 100644 --- a/source/c04/c04_13.md +++ b/source/c04/c04_13.md @@ -46,6 +46,37 @@ optional arguments: 已经可以使用了。 +来试一下加几个参数:`--foo` 和 `bar` + +其中 `metavar` 就相当于注释,在打印help的时候会显示 + +```python +>>> parser = argparse.ArgumentParser() +>>> parser.add_argument('--foo', metavar='YYY') +>>> parser.add_argument('bar', metavar='XXX') +>>> parser.parse_args('X --foo Y'.split()) +Namespace(bar='X', foo='Y') +>>> parser.print_help() +usage: [-h] [--foo YYY] XXX + +positional arguments: + XXX + +optional arguments: + -h, --help show this help message and exit + --foo YYY +``` + +**参数来源** + +通常参数都是通过 `sys.argv` 获得。 + +有时候也可以通过向 ` parser.parse_args()`函数传入一段字符串列表,来解析。 + +``` +parser.parse_args('test --name wangbm'.split()) +``` + ## 1. 入门配置 这里先讲一下,比较常用的参数配置。 @@ -376,4 +407,4 @@ single,"--name", '-n',--frequency,module_args,处理函数:main_single --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file From ab724b8e2c2fd936a9f4a1af2d27d14ccc066ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 20 Mar 2020 13:06:19 +0800 Subject: [PATCH 025/147] =?UTF-8?q?Update=EF=BC=9A=E5=85=AC=E4=BC=97?= =?UTF-8?q?=E5=8F=B7=E4=BA=8C=E7=BB=B4=E7=A0=81=E6=A0=B7=E5=BC=8F=E6=9B=B4?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- md2rst.py | 2 +- source/aboutme.rst | 2 +- source/c01/c01_01.md | 2 +- source/c01/c01_01.rst | 3 ++- source/c01/c01_04.rst | 1 + source/c01/c01_05.md | 2 +- source/c01/c01_05.rst | 3 ++- source/c01/c01_06.md | 2 +- source/c01/c01_06.rst | 3 ++- source/c01/c01_07.md | 2 +- source/c01/c01_07.rst | 3 ++- source/c01/c01_08.md | 2 +- source/c01/c01_08.rst | 3 ++- source/c01/c01_09.md | 2 +- source/c01/c01_09.rst | 3 ++- source/c01/c01_10.md | 2 +- source/c01/c01_10.rst | 3 ++- source/c01/c01_11.md | 2 +- source/c01/c01_11.rst | 3 ++- source/c01/c01_12.md | 2 +- source/c01/c01_12.rst | 3 ++- source/c01/c01_13.md | 2 +- source/c01/c01_13.rst | 3 ++- source/c01/c01_14.md | 2 +- source/c01/c01_14.rst | 3 ++- source/c01/c01_15.md | 2 +- source/c01/c01_15.rst | 3 ++- source/c01/c01_16.md | 2 +- source/c01/c01_16.rst | 3 ++- source/c01/c01_17.md | 2 +- source/c01/c01_17.rst | 3 ++- source/c01/c01_20.md | 2 +- source/c01/c01_20.rst | 3 ++- source/c01/c01_21.md | 2 +- source/c01/c01_21.rst | 3 ++- source/c01/c01_22.md | 2 +- source/c01/c01_22.rst | 3 ++- source/c01/c01_23.md | 2 +- source/c01/c01_23.rst | 3 ++- source/c01/c01_24.md | 2 +- source/c01/c01_24.rst | 3 ++- source/c01/c01_25.md | 2 +- source/c01/c01_25.rst | 3 ++- source/c01/c01_26.md | 2 +- source/c01/c01_26.rst | 3 ++- source/c01/c01_27.md | 2 +- source/c01/c01_27.rst | 3 ++- source/c01/c01_29.md | 2 +- source/c01/c01_29.rst | 3 ++- source/c01/c01_31.md | 2 +- source/c01/c01_31.rst | 3 ++- source/c01/c01_32.md | 2 +- source/c01/c01_32.rst | 3 ++- source/c01/c01_33.md | 2 +- source/c01/c01_33.rst | 3 ++- source/c01/c01_34.md | 2 +- source/c01/c01_34.rst | 3 ++- source/c01/c01_35.md | 2 +- source/c01/c01_35.rst | 3 ++- source/c02/c02_01.md | 2 +- source/c02/c02_01.rst | 3 ++- source/c02/c02_02.md | 2 +- source/c02/c02_02.rst | 3 ++- source/c02/c02_03.md | 2 +- source/c02/c02_03.rst | 3 ++- source/c02/c02_04.md | 2 +- source/c02/c02_04.rst | 3 ++- source/c02/c02_05.md | 2 +- source/c02/c02_05.rst | 3 ++- source/c02/c02_06.md | 2 +- source/c02/c02_06.rst | 3 ++- source/c02/c02_07.md | 2 +- source/c02/c02_07.rst | 3 ++- source/c02/c02_08.md | 2 +- source/c02/c02_08.rst | 3 ++- source/c02/c02_09.md | 2 +- source/c02/c02_09.rst | 3 ++- source/c02/c02_10.md | 2 +- source/c02/c02_10.rst | 3 ++- source/c02/c02_11.md | 2 +- source/c02/c02_11.rst | 3 ++- source/c02/c02_12.md | 2 +- source/c02/c02_12.rst | 3 ++- source/c03/c03_01.md | 2 +- source/c03/c03_01.rst | 3 ++- source/c03/c03_02.md | 2 +- source/c03/c03_02.rst | 3 ++- source/c03/c03_03.md | 2 +- source/c03/c03_03.rst | 3 ++- source/c03/c03_04.md | 2 +- source/c03/c03_04.rst | 3 ++- source/c03/c03_05.md | 2 +- source/c03/c03_05.rst | 3 ++- source/c03/c03_06.md | 2 +- source/c03/c03_06.rst | 3 ++- source/c04/c04_01.md | 2 +- source/c04/c04_01.rst | 3 ++- source/c04/c04_02.md | 2 +- source/c04/c04_02.rst | 3 ++- source/c04/c04_03.md | 2 +- source/c04/c04_03.rst | 3 ++- source/c04/c04_04.md | 2 +- source/c04/c04_04.rst | 3 ++- source/c04/c04_05.md | 2 +- source/c04/c04_05.rst | 3 ++- source/c04/c04_06.md | 2 +- source/c04/c04_06.rst | 3 ++- source/c04/c04_07.md | 2 +- source/c04/c04_07.rst | 3 ++- source/c04/c04_08.md | 2 +- source/c04/c04_08.rst | 3 ++- source/c04/c04_09.md | 2 +- source/c04/c04_09.rst | 3 ++- source/c04/c04_10.md | 2 +- source/c04/c04_10.rst | 3 ++- source/c04/c04_11.md | 2 +- source/c04/c04_11.rst | 3 ++- source/c04/c04_12.md | 2 +- source/c04/c04_12.rst | 3 ++- source/c04/c04_13.md | 2 +- source/c04/c04_13.rst | 35 ++++++++++++++++++++++++++++++++++- source/c04/c04_14.md | 2 +- source/c04/c04_14.rst | 3 ++- source/c04/c04_15.md | 2 +- source/c04/c04_15.rst | 3 ++- source/c04/c04_16.md | 2 +- source/c04/c04_16.rst | 3 ++- source/c04/c04_17.md | 2 +- source/c04/c04_17.rst | 3 ++- source/c04/c04_18.md | 2 +- source/c04/c04_18.rst | 3 ++- source/c04/c04_19.md | 2 +- source/c04/c04_19.rst | 3 ++- source/c04/c04_21.md | 2 +- source/c04/c04_21.rst | 3 ++- source/c05/c05_01.md | 2 +- source/c05/c05_01.rst | 3 ++- source/c05/c05_02.md | 2 +- source/c05/c05_02.rst | 3 ++- source/c05/c05_03.md | 2 +- source/c05/c05_03.rst | 3 ++- source/c06/c06_01.md | 2 +- source/c06/c06_01.rst | 3 ++- source/c06/c06_02.md | 2 +- source/c06/c06_02.rst | 3 ++- source/c06/c06_03.md | 2 +- source/c06/c06_03.rst | 3 ++- source/c06/c06_04.md | 2 +- source/c06/c06_04.rst | 3 ++- source/c06/c06_05.md | 2 +- source/c06/c06_05.rst | 3 ++- source/c06/c06_06.md | 2 +- source/c06/c06_06.rst | 3 ++- source/c07/C07_08.md | 2 +- source/c07/c07_01.md | 2 +- source/c07/c07_01.rst | 3 ++- source/c07/c07_02.md | 2 +- source/c07/c07_02.rst | 3 ++- source/c07/c07_03.md | 2 +- source/c07/c07_03.rst | 3 ++- source/c07/c07_04.md | 2 +- source/c07/c07_04.rst | 3 ++- source/c07/c07_05.md | 2 +- source/c07/c07_05.rst | 3 ++- source/c07/c07_06.md | 2 +- source/c07/c07_06.rst | 3 ++- source/c07/c07_07.md | 2 +- source/c07/c07_07.rst | 3 ++- source/c07/c07_08.rst | 3 ++- source/c07/c07_10.md | 2 +- source/c07/c07_10.rst | 3 ++- source/c08/c08_01.md | 2 +- source/c08/c08_01.rst | 3 ++- source/c08/c08_02.md | 2 +- source/c08/c08_02.rst | 3 ++- source/c08/c08_03.md | 2 +- source/c08/c08_03.rst | 3 ++- source/c08/c08_04.md | 2 +- source/c08/c08_04.rst | 3 ++- source/c08/c08_05.md | 2 +- source/c08/c08_05.rst | 3 ++- source/c08/c08_06.md | 2 +- source/c08/c08_06.rst | 3 ++- source/c08/c08_07.md | 2 +- source/c08/c08_07.rst | 3 ++- source/c08/c08_08.md | 2 +- source/c08/c08_08.rst | 3 ++- source/c08/c08_09.md | 2 +- source/c08/c08_09.rst | 3 ++- source/c08/c08_11.md | 2 +- source/c08/c08_11.rst | 3 ++- source/c08/c08_12.md | 2 +- source/c08/c08_12.rst | 3 ++- source/c08/c08_13.md | 2 +- source/c08/c08_13.rst | 3 ++- source/c08/c08_14.md | 2 +- source/c08/c08_14.rst | 3 ++- source/c09/c09_01.md | 2 +- source/c09/c09_01.rst | 3 ++- source/chapters/p01.rst | 2 +- source/chapters/p02.rst | 2 +- source/chapters/p03.rst | 2 +- source/chapters/p04.rst | 2 +- source/chapters/p05.rst | 2 +- source/chapters/p06.rst | 2 +- source/chapters/p07.rst | 2 +- source/chapters/p08.rst | 2 +- source/chapters/p09.rst | 2 +- source/index.rst | 2 +- source/preface.rst | 2 +- source/py_mp_index.md | 24 +++++++++++++++++++++++- source/roadmap.rst | 2 +- source/thanks.rst | 2 +- 214 files changed, 366 insertions(+), 213 deletions(-) diff --git a/README.md b/README.md index e815e8b..bddbe5a 100644 --- a/README.md +++ b/README.md @@ -140,5 +140,5 @@ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/md2rst.py b/md2rst.py index 6c7280f..3d53eaa 100644 --- a/md2rst.py +++ b/md2rst.py @@ -32,7 +32,7 @@ ''' readme_tooter = ''' --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) ''' diff --git a/source/aboutme.rst b/source/aboutme.rst index 3b60f98..8e3f65b 100755 --- a/source/aboutme.rst +++ b/source/aboutme.rst @@ -10,5 +10,5 @@ -------------------------------------------- -.. image:: http://image.python-online.cn/20200315144434.png +.. image:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/c01/c01_01.md b/source/c01/c01_01.md index 399af2b..938ffc8 100644 --- a/source/c01/c01_01.md +++ b/source/c01/c01_01.md @@ -271,4 +271,4 @@ class Person(metaclass=MetaPerson): -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 375e26a..21bbee0 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -314,9 +314,10 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165542.png .. |image1| image:: http://image.python-online.cn/20190511165551.png diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index 88358aa..5972b03 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -118,6 +118,7 @@ .. figure:: http://image.python-online.cn/20191117142849.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190404215330.png diff --git a/source/c01/c01_05.md b/source/c01/c01_05.md index b347191..febc146 100644 --- a/source/c01/c01_05.md +++ b/source/c01/c01_05.md @@ -166,4 +166,4 @@ foobar() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index e164494..e3bd115 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -177,6 +177,7 @@ locals() -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_06.md b/source/c01/c01_06.md index 09f489d..20bfbf6 100644 --- a/source/c01/c01_06.md +++ b/source/c01/c01_06.md @@ -99,4 +99,4 @@ print(Xiamen_dict) -------------- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index ed96ba6..a48254e 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -104,6 +104,7 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_07.md b/source/c01/c01_07.md index dc053bb..2c5e7f7 100644 --- a/source/c01/c01_07.md +++ b/source/c01/c01_07.md @@ -291,4 +291,4 @@ b = 2 if a > 2 else 1 - https://foofish.net/idiomatic_part2.html ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index 255870d..d9166e4 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -348,6 +348,7 @@ Pythonic -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_08.md b/source/c01/c01_08.md index 5e43213..619365f 100644 --- a/source/c01/c01_08.md +++ b/source/c01/c01_08.md @@ -250,4 +250,4 @@ A.__mro__ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index c2e77a4..4df2492 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -269,9 +269,10 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg .. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg diff --git a/source/c01/c01_09.md b/source/c01/c01_09.md index ae35b34..b9d825b 100644 --- a/source/c01/c01_09.md +++ b/source/c01/c01_09.md @@ -66,5 +66,5 @@ class Airplane(Vehicle, PlaneMixin): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index 2778631..32fe3bd 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -73,6 +73,7 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index b925039..970ebb7 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -1195,4 +1195,4 @@ linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 0945727..80cbd7f 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1310,9 +1310,10 @@ linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165650.png .. |image1| image:: http://image.python-online.cn/20190511165716.png diff --git a/source/c01/c01_11.md b/source/c01/c01_11.md index 95e3f7d..57d427d 100644 --- a/source/c01/c01_11.md +++ b/source/c01/c01_11.md @@ -322,4 +322,4 @@ match.span() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index 79d82b7..ca3cb4c 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -344,6 +344,7 @@ start(),end(),end() -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_12.md b/source/c01/c01_12.md index 6a5e8f3..0ed9f6f 100644 --- a/source/c01/c01_12.md +++ b/source/c01/c01_12.md @@ -134,4 +134,4 @@ GB 18030 与 GB 2312-1980 和 GBK 兼容,共收录汉字70244个。 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index fb8d5cb..b9736b4 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -154,9 +154,10 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/02/598168fe2b016.png .. |image1| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png diff --git a/source/c01/c01_13.md b/source/c01/c01_13.md index 7079093..aab1c8b 100644 --- a/source/c01/c01_13.md +++ b/source/c01/c01_13.md @@ -124,4 +124,4 @@ from functools import reduce --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index d201e64..2060038 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -157,9 +157,10 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg diff --git a/source/c01/c01_14.md b/source/c01/c01_14.md index 3c5cb01..cf90646 100644 --- a/source/c01/c01_14.md +++ b/source/c01/c01_14.md @@ -195,4 +195,4 @@ with open_func('/Users/MING/mytest.txt') as file_in: ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index 7ca5a49..263231c 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -209,9 +209,10 @@ open)的上下文管理器。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190310172800.png diff --git a/source/c01/c01_15.md b/source/c01/c01_15.md index cead07e..fa232e8 100644 --- a/source/c01/c01_15.md +++ b/source/c01/c01_15.md @@ -68,4 +68,4 @@ a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index 7be0fdf..d4a43ca 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -78,6 +78,7 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_16.md b/source/c01/c01_16.md index 2edf64d..576eedf 100644 --- a/source/c01/c01_16.md +++ b/source/c01/c01_16.md @@ -159,4 +159,4 @@ hello, world ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst index 31ff68a..ab6c40b 100644 --- a/source/c01/c01_16.rst +++ b/source/c01/c01_16.rst @@ -166,6 +166,7 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md index 70a2ae8..84c494e 100644 --- a/source/c01/c01_17.md +++ b/source/c01/c01_17.md @@ -525,4 +525,4 @@ class Student: --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index 02f0f1e..f8bf4ac 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -577,9 +577,10 @@ super 的实现原理,就交由你来自己完成。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190425221322.png .. |image1| image:: http://image.python-online.cn/20190425221322.png diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md index 3432fa6..f70e790 100644 --- a/source/c01/c01_20.md +++ b/source/c01/c01_20.md @@ -96,5 +96,5 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index bc63617..08c279b 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -105,9 +105,10 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod 写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190630111243.png .. |image1| image:: http://image.python-online.cn/20190630104956.png diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md index 5c7e18e..38d4e7c 100644 --- a/source/c01/c01_21.md +++ b/source/c01/c01_21.md @@ -83,4 +83,4 @@ with close_stdout(): -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index 4752247..aab9498 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -89,6 +89,7 @@ >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) 24 -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_22.md b/source/c01/c01_22.md index 7abd276..448951a 100644 --- a/source/c01/c01_22.md +++ b/source/c01/c01_22.md @@ -188,4 +188,4 @@ $ cloud-init init - https://www.cnblogs.com/stonehe/p/7944366.html -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index 82cbe3f..80b32d3 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -197,9 +197,10 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip - https://www.cnblogs.com/stonehe/p/7944366.html -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190831160317.png diff --git a/source/c01/c01_23.md b/source/c01/c01_23.md index 7ff0671..82e6a09 100644 --- a/source/c01/c01_23.md +++ b/source/c01/c01_23.md @@ -123,4 +123,4 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index bb9b54c..19f5b35 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -118,6 +118,7 @@ Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5 -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md index 1f23ffd..dc08028 100644 --- a/source/c01/c01_24.md +++ b/source/c01/c01_24.md @@ -720,4 +720,4 @@ ok -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index 5fb76d9..eca008a 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -793,9 +793,10 @@ sys.path)查找器 - https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc - https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191027192949.png diff --git a/source/c01/c01_25.md b/source/c01/c01_25.md index 106141f..dddcda6 100644 --- a/source/c01/c01_25.md +++ b/source/c01/c01_25.md @@ -106,4 +106,4 @@ AttributeError: 'module' object has no attribute '__file__' -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index e57fabd..846efa9 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -105,6 +105,7 @@ 因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md index c976d1a..c94c65c 100644 --- a/source/c01/c01_26.md +++ b/source/c01/c01_26.md @@ -259,4 +259,4 @@ int main(int argc, char const *argv[]) -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index 974c5eb..4f32dcb 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -282,6 +282,7 @@ getchar() & putchar() return 0; } -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index 8427273..f440996 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -640,4 +640,4 @@ $ python setup.py upload -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 9031274..3e69eb0 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -683,9 +683,10 @@ Index)上,它是 Python - http://blog.konghy.cn/2018/04/29/setup-dot-py/ - https://note.qidong.name/2018/01/python-setup-requires/ -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191218202833.png .. |image1| image:: http://image.python-online.cn/20191218203005.png diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md index 52ccd52..2657d2a 100644 --- a/source/c01/c01_29.md +++ b/source/c01/c01_29.md @@ -8,4 +8,4 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index 224b310..9004d6b 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -5,6 +5,7 @@ 基于 Python 3.6 的源码分析:https://he11olx.com/ -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_31.md b/source/c01/c01_31.md index 683e923..71656f0 100644 --- a/source/c01/c01_31.md +++ b/source/c01/c01_31.md @@ -20,4 +20,4 @@ RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G) -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index 4f4a1f5..5b12101 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -17,6 +17,7 @@ ARGB RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md index 3655bcb..77da3e4 100644 --- a/source/c01/c01_32.md +++ b/source/c01/c01_32.md @@ -34,4 +34,4 @@ python3 -m pip install --user requests aiohttp cryptography pymysql prettytable -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst index c384497..4cebca3 100644 --- a/source/c01/c01_32.rst +++ b/source/c01/c01_32.rst @@ -31,6 +31,7 @@ requests.get(“https://www.baidu.com”) python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_33.md b/source/c01/c01_33.md index fcacc16..e6f4756 100644 --- a/source/c01/c01_33.md +++ b/source/c01/c01_33.md @@ -46,7 +46,7 @@ sudo yum install gdb python-debuginfo -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_33.rst b/source/c01/c01_33.rst index 9725d42..bfe6e11 100644 --- a/source/c01/c01_33.rst +++ b/source/c01/c01_33.rst @@ -44,6 +44,7 @@ sudo yum install gdb python-debuginfo -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c01/c01_34.md b/source/c01/c01_34.md index 876acce..45a5b29 100644 --- a/source/c01/c01_34.md +++ b/source/c01/c01_34.md @@ -87,4 +87,4 @@ innodb: foreign key constraint system tables created -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index b0d8b0c..d1ecce9 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -88,9 +88,10 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 innodb: foreign key constraint system tables created -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200227201644.png diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md index 53917f8..8a16647 100644 --- a/source/c01/c01_35.md +++ b/source/c01/c01_35.md @@ -339,4 +339,4 @@ trans.close() -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index c71d54a..849e3fc 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -353,9 +353,10 @@ Windows,这里就有一件好事,一件坏事了,。 - http://docs.paramiko.org - https://www.liujiangblog.com/blog/15/ -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200228085749.png .. |image1| image:: http://image.python-online.cn/20200228093627.png diff --git a/source/c02/c02_01.md b/source/c02/c02_01.md index 59eeb90..3320f33 100644 --- a/source/c02/c02_01.md +++ b/source/c02/c02_01.md @@ -235,4 +235,4 @@ multi_process(io_simulation, type="模拟IO密集型") - 多进程虽然总是最快的,但是不一定是最优的选择,因为它需要CPU资源支持下才能体现优势 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index d8c3991..b9b9ce0 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -248,9 +248,10 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png .. |image1| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg diff --git a/source/c02/c02_02.md b/source/c02/c02_02.md index 72786b1..915af86 100644 --- a/source/c02/c02_02.md +++ b/source/c02/c02_02.md @@ -133,4 +133,4 @@ t.name = "My-Thread" 至此,Python线程基础知识,我们大概都介绍完了。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index a5acd36..526c577 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -144,6 +144,7 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_03.md b/source/c02/c02_03.md index 5a403a5..e195de4 100644 --- a/source/c02/c02_03.md +++ b/source/c02/c02_03.md @@ -327,4 +327,4 @@ t2.start() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index ea131d5..46b92cb 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -354,6 +354,7 @@ CPython,所以也就默许了Python具有GIL锁这个事。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index d9e0bbb..1f911ef 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -263,4 +263,4 @@ teacher.call('小亮') ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index d2c3e44..7fd675b 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -287,6 +287,7 @@ Condition和Event 是类似的,并没有多大区别。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_05.md b/source/c02/c02_05.md index 3955bf0..4c753e6 100644 --- a/source/c02/c02_05.md +++ b/source/c02/c02_05.md @@ -249,4 +249,4 @@ item5 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 4b9088c..daa59c4 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -259,6 +259,7 @@ Out),就是先进入队列的消息,将优先被消费。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index fca7bd3..9c4c8fb 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -100,4 +100,4 @@ running thread-12504:1 构建线程池的方法,是可以很灵活的,大家有空可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index b6f2450..374c2a5 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -109,6 +109,7 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_07.md b/source/c02/c02_07.md index 641a717..c61bc4d 100644 --- a/source/c02/c02_07.md +++ b/source/c02/c02_07.md @@ -364,4 +364,4 @@ if __name__ == '__main__': 下一章,我将讲一个Python3.5新引入的语法:`yield from`。篇幅也比较多,所以就单独拿出来讲。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index 0c3366d..a98ee67 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -390,9 +390,10 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190527123516.png .. |image1| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png diff --git a/source/c02/c02_08.md b/source/c02/c02_08.md index 38586c7..fd3ba5d 100644 --- a/source/c02/c02_08.md +++ b/source/c02/c02_08.md @@ -333,4 +333,4 @@ RESULT = _r --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index 39b1219..248cda8 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -359,6 +359,7 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_09.md b/source/c02/c02_09.md index 28e7f64..00c4519 100644 --- a/source/c02/c02_09.md +++ b/source/c02/c02_09.md @@ -220,4 +220,4 @@ emmm,和上面的结果是一样的。nice --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 871f816..2709d13 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -244,9 +244,10 @@ emmm,和上面的结果是一样的。nice -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |普通函数中 不能使用 await| image:: https://i.loli.net/2018/05/26/5b09794f45340.png .. |async 中 不能使用yield| image:: https://i.loli.net/2018/05/26/5b0978b646230.png diff --git a/source/c02/c02_10.md b/source/c02/c02_10.md index f88b0f9..d015315 100644 --- a/source/c02/c02_10.md +++ b/source/c02/c02_10.md @@ -458,4 +458,4 @@ loop.close() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index 23bbc0b..e7d699a 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -507,6 +507,7 @@ asyncio.gather -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c02/c02_11.md b/source/c02/c02_11.md index 928eb80..f4f9298 100644 --- a/source/c02/c02_11.md +++ b/source/c02/c02_11.md @@ -211,4 +211,4 @@ Thu May 31 23:42:48 2018 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 5e28cb5..842b33a 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -216,9 +216,10 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/06/03/5b13ba8525bcf.png .. |image1| image:: https://i.loli.net/2018/06/03/5b13ba9f66baa.png diff --git a/source/c02/c02_12.md b/source/c02/c02_12.md index a28379b..40ecd8f 100644 --- a/source/c02/c02_12.md +++ b/source/c02/c02_12.md @@ -108,4 +108,4 @@ MING.send('香肠') --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index 8f4e7b7..e7812e2 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -107,6 +107,7 @@ yield -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c03/c03_01.md b/source/c03/c03_01.md index 5f768e4..542646b 100644 --- a/source/c03/c03_01.md +++ b/source/c03/c03_01.md @@ -704,4 +704,4 @@ def timeout_limit(timeout_time): 我的文章更新频率是远低于其他 Python 技术号,但我仍然坚持自己,坚持原创,每周虽然只有一篇,但我能保证我的每一篇文章都是诚意之作。希望那些对你有帮助的文章能够多多帮忙转发分享。这也是我更新的一大动力。非常感谢。 -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 636b94c..aa4839a 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -773,9 +773,10 @@ property 我的文章更新频率是远低于其他 Python 技术号,但我仍然坚持自己,坚持原创,每周虽然只有一篇,但我能保证我的每一篇文章都是诚意之作。希望那些对你有帮助的文章能够多多帮忙转发分享。这也是我更新的一大动力。非常感谢。 -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190811100737.png .. |image1| image:: http://image.python-online.cn/20190512113917.png diff --git a/source/c03/c03_02.md b/source/c03/c03_02.md index ad24cb0..a5ef80c 100644 --- a/source/c03/c03_02.md +++ b/source/c03/c03_02.md @@ -344,4 +344,4 @@ in User ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index ca1ea02..8be7e21 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -374,6 +374,7 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c03/c03_03.md b/source/c03/c03_03.md index b260e98..c3f5ceb 100644 --- a/source/c03/c03_03.md +++ b/source/c03/c03_03.md @@ -257,4 +257,4 @@ if __name__ == '__main__': ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index 2d8ccf0..d8ecd86 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -292,9 +292,10 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png .. |image1| image:: https://i.loli.net/2018/04/30/5ae6c31b2d1c8.png diff --git a/source/c03/c03_04.md b/source/c03/c03_04.md index dd3de57..19cf25f 100644 --- a/source/c03/c03_04.md +++ b/source/c03/c03_04.md @@ -451,4 +451,4 @@ vim /etc/nginx/nginx.conf --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index f27729b..280a3a6 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -510,9 +510,10 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/08/20/599982f513b7e.png .. |image1| image:: https://i.loli.net/2017/08/20/59998b33265d9.png diff --git a/source/c03/c03_05.md b/source/c03/c03_05.md index c7991b0..9da86be 100644 --- a/source/c03/c03_05.md +++ b/source/c03/c03_05.md @@ -457,4 +457,4 @@ with app.app_context(): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index fa18b82..ce07cd8 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -498,9 +498,10 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX .. |image1| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup diff --git a/source/c03/c03_06.md b/source/c03/c03_06.md index 7d06c37..7f1f097 100644 --- a/source/c03/c03_06.md +++ b/source/c03/c03_06.md @@ -770,4 +770,4 @@ meth :>> parser = argparse.ArgumentParser() + >>> parser.add_argument('--foo', metavar='YYY') + >>> parser.add_argument('bar', metavar='XXX') + >>> parser.parse_args('X --foo Y'.split()) + Namespace(bar='X', foo='Y') + >>> parser.print_help() + usage: [-h] [--foo YYY] XXX + + positional arguments: + XXX + + optional arguments: + -h, --help show this help message and exit + --foo YYY + +**参数来源** + +通常参数都是通过 ``sys.argv`` 获得。 + +有时候也可以通过向 +``parser.parse_args()``\ 函数传入一段字符串列表,来解析。 + +:: + + parser.parse_args('test --name wangbm'.split()) + 1. 入门配置 ----------- @@ -399,6 +431,7 @@ arguments)。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c04/c04_14.md b/source/c04/c04_14.md index 4629a97..aa76f4a 100644 --- a/source/c04/c04_14.md +++ b/source/c04/c04_14.md @@ -157,4 +157,4 @@ $ pipenv check --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 364684c..94d2edf 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -168,9 +168,10 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn .. |image1| image:: http://image.python-online.cn/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX diff --git a/source/c04/c04_15.md b/source/c04/c04_15.md index 67d5021..4dd59c8 100644 --- a/source/c04/c04_15.md +++ b/source/c04/c04_15.md @@ -903,4 +903,4 @@ PyCharm 打开一个文件,就占用一个标签面。 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index 2e6bce5..bfdca54 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -1063,9 +1063,10 @@ Debug ,而远程调试需要不少前置步骤。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190323164120.png .. |image1| image:: http://image.python-online.cn/20190323211635.png diff --git a/source/c04/c04_16.md b/source/c04/c04_16.md index 5692951..0dbccaa 100644 --- a/source/c04/c04_16.md +++ b/source/c04/c04_16.md @@ -72,4 +72,4 @@ caller name: f1 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c04/c04_16.rst b/source/c04/c04_16.rst index f88cc2a..51c01a4 100644 --- a/source/c04/c04_16.rst +++ b/source/c04/c04_16.rst @@ -72,6 +72,7 @@ Python:如何在被调用方法中获取调用者的方法名? -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c04/c04_17.md b/source/c04/c04_17.md index 435625b..9f3dd10 100644 --- a/source/c04/c04_17.md +++ b/source/c04/c04_17.md @@ -464,4 +464,4 @@ for i in range(10): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c04/c04_17.rst b/source/c04/c04_17.rst index bb5a55e..018f952 100644 --- a/source/c04/c04_17.rst +++ b/source/c04/c04_17.rst @@ -472,9 +472,10 @@ Order -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190414144511.png .. |image1| image:: http://image.python-online.cn/20190512113846.png diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index a39ca02..d8c06d8 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -302,4 +302,4 @@ command + w --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 3bd79ba..2a1c60a 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -317,9 +317,10 @@ NewFileMenu:使得可以在访达中新建文件 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190810161513.png .. |image1| image:: http://image.python-online.cn/20190810162315.png diff --git a/source/c04/c04_19.md b/source/c04/c04_19.md index 1c2b93d..031f998 100644 --- a/source/c04/c04_19.md +++ b/source/c04/c04_19.md @@ -684,4 +684,4 @@ set cindent(cin) 设置C语言风格缩进 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c04/c04_19.rst b/source/c04/c04_19.rst index 7ab13fc..09a9255 100644 --- a/source/c04/c04_19.rst +++ b/source/c04/c04_19.rst @@ -719,6 +719,7 @@ n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c04/c04_21.md b/source/c04/c04_21.md index c863619..1e5e425 100644 --- a/source/c04/c04_21.md +++ b/source/c04/c04_21.md @@ -356,7 +356,7 @@ trusted-host=tsinghua.edu.cn -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index fc64e2e..b0a08a7 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -368,9 +368,10 @@ pip 的文件夹,若没有则创建之。 |image0| -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20191105200041.png diff --git a/source/c05/c05_01.md b/source/c05/c05_01.md index 1ce5978..5525f87 100644 --- a/source/c05/c05_01.md +++ b/source/c05/c05_01.md @@ -409,4 +409,4 @@ def radix_sort(array, reverse=False): -------------- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c05/c05_01.rst b/source/c05/c05_01.rst index c5c6bc1..8f77543 100755 --- a/source/c05/c05_01.rst +++ b/source/c05/c05_01.rst @@ -419,9 +419,10 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |快速排序| image:: http://image.python-online.cn/Fpj4DFN_YCtfmJwb_85QnsuIVLqk .. |\|冒泡排序\|| image:: http://image.python-online.cn/FvbrVECeq58hY8TptG4ilkL5Owcc diff --git a/source/c05/c05_02.md b/source/c05/c05_02.md index 7ab4332..fe6f528 100644 --- a/source/c05/c05_02.md +++ b/source/c05/c05_02.md @@ -112,4 +112,4 @@ calc_step_loop(40) --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c05/c05_02.rst b/source/c05/c05_02.rst index 05a55ea..c5abf2d 100755 --- a/source/c05/c05_02.rst +++ b/source/c05/c05_02.rst @@ -120,6 +120,7 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c05/c05_03.md b/source/c05/c05_03.md index 8d8b900..eb3dd2e 100644 --- a/source/c05/c05_03.md +++ b/source/c05/c05_03.md @@ -144,5 +144,5 @@ print(calc(365, 23)) # 0.500001752183 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c05/c05_03.rst b/source/c05/c05_03.rst index d424916..823304f 100755 --- a/source/c05/c05_03.rst +++ b/source/c05/c05_03.rst @@ -159,9 +159,10 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190112181126.png diff --git a/source/c06/c06_01.md b/source/c06/c06_01.md index dfc4f58..2176810 100644 --- a/source/c06/c06_01.md +++ b/source/c06/c06_01.md @@ -111,4 +111,4 @@ plt.show() ![](http://image.python-online.cn/20190511164650.png) ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c06/c06_01.rst b/source/c06/c06_01.rst index 316826a..8929cd0 100755 --- a/source/c06/c06_01.rst +++ b/source/c06/c06_01.rst @@ -131,9 +131,10 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png .. |image1| image:: http://image.python-online.cn/20190511164650.png diff --git a/source/c06/c06_02.md b/source/c06/c06_02.md index 8309e6b..6606e83 100644 --- a/source/c06/c06_02.md +++ b/source/c06/c06_02.md @@ -297,4 +297,4 @@ show image --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c06/c06_02.rst b/source/c06/c06_02.rst index 0a2873d..cc807e1 100755 --- a/source/c06/c06_02.rst +++ b/source/c06/c06_02.rst @@ -309,9 +309,10 @@ show image |image9| -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511164738.png .. |image1| image:: http://image.python-online.cn/20190511164753.png diff --git a/source/c06/c06_03.md b/source/c06/c06_03.md index fb3faa2..144f5f4 100644 --- a/source/c06/c06_03.md +++ b/source/c06/c06_03.md @@ -204,4 +204,4 @@ plt.show() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c06/c06_03.rst b/source/c06/c06_03.rst index 89f472c..1dc0793 100755 --- a/source/c06/c06_03.rst +++ b/source/c06/c06_03.rst @@ -205,9 +205,10 @@ show image |image4| -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511164936.png .. |image1| image:: http://image.python-online.cn/20190511164949.png diff --git a/source/c06/c06_04.md b/source/c06/c06_04.md index b7dbad1..4b437ea 100644 --- a/source/c06/c06_04.md +++ b/source/c06/c06_04.md @@ -188,4 +188,4 @@ plt.show() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c06/c06_04.rst b/source/c06/c06_04.rst index 7e38fa1..a21e883 100755 --- a/source/c06/c06_04.rst +++ b/source/c06/c06_04.rst @@ -199,9 +199,10 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165103.png .. |image1| image:: http://image.python-online.cn/20190511165132.png diff --git a/source/c06/c06_05.md b/source/c06/c06_05.md index f7f0a68..a1b502e 100644 --- a/source/c06/c06_05.md +++ b/source/c06/c06_05.md @@ -126,4 +126,4 @@ Image(url='./ming.gif') ![](https://i.loli.net/2018/12/25/5c2226078799b.gif) --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c06/c06_05.rst b/source/c06/c06_05.rst index 951dabb..f1883dd 100755 --- a/source/c06/c06_05.rst +++ b/source/c06/c06_05.rst @@ -141,9 +141,10 @@ matplotlib 给我们提供了一个函数,\ ``animation.FuncAnimation`` -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/12/25/5c2226078799b.gif diff --git a/source/c06/c06_06.md b/source/c06/c06_06.md index 81e89ec..8e8a594 100644 --- a/source/c06/c06_06.md +++ b/source/c06/c06_06.md @@ -121,4 +121,4 @@ HTML(ani.to_html5_video()) 我将这个小短片下载并上传至后台,你可以点击 [公众号原文](https://mp.weixin.qq.com/s/BU4DtJQxtxwEMhGZE8t3CQ) 感受一下。 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c06/c06_06.rst b/source/c06/c06_06.rst index 42bb33b..16a290a 100755 --- a/source/c06/c06_06.rst +++ b/source/c06/c06_06.rst @@ -137,9 +137,10 @@ Jupyter NoteBook 里观察整个变化的过程。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190511165315.png diff --git a/source/c07/C07_08.md b/source/c07/C07_08.md index 20cbd1b..6e96207 100644 --- a/source/c07/C07_08.md +++ b/source/c07/C07_08.md @@ -175,4 +175,4 @@ fi --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_01.md b/source/c07/c07_01.md index f93c745..4cb1d2c 100644 --- a/source/c07/c07_01.md +++ b/source/c07/c07_01.md @@ -1753,4 +1753,4 @@ yum makecache --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index 1202d5c..f1683a8 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -1908,9 +1908,10 @@ url** 即可。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-9-20/47469030.jpg .. |image1| image:: http://image.python-online.cn/20190705182629.png diff --git a/source/c07/c07_02.md b/source/c07/c07_02.md index 8ad6dc5..40e7226 100644 --- a/source/c07/c07_02.md +++ b/source/c07/c07_02.md @@ -636,4 +636,4 @@ update items set history='30d' where hostid in (select hostid from hosts where h --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_02.rst b/source/c07/c07_02.rst index e7daee7..a79bfc4 100755 --- a/source/c07/c07_02.rst +++ b/source/c07/c07_02.rst @@ -693,9 +693,10 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190404193811.png .. |image1| image:: http://image.python-online.cn/20190404194416.png diff --git a/source/c07/c07_03.md b/source/c07/c07_03.md index 9bc3342..26fb469 100644 --- a/source/c07/c07_03.md +++ b/source/c07/c07_03.md @@ -251,4 +251,4 @@ namespace 有下面六种 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_03.rst b/source/c07/c07_03.rst index 0800da9..785a324 100755 --- a/source/c07/c07_03.rst +++ b/source/c07/c07_03.rst @@ -287,9 +287,10 @@ namespace 有下面六种 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-12-23/44035514.jpg .. |image1| image:: http://image.python-online.cn/17-12-23/20133481.jpg diff --git a/source/c07/c07_04.md b/source/c07/c07_04.md index e2411e9..676a753 100644 --- a/source/c07/c07_04.md +++ b/source/c07/c07_04.md @@ -308,4 +308,4 @@ docker history image --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_04.rst b/source/c07/c07_04.rst index e65c2a7..f186d28 100755 --- a/source/c07/c07_04.rst +++ b/source/c07/c07_04.rst @@ -358,9 +358,10 @@ CMD:两个功能 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/17-12-23/49304868.jpg .. |image1| image:: http://image.python-online.cn/17-12-23/36753853.jpg diff --git a/source/c07/c07_05.md b/source/c07/c07_05.md index a4165eb..e8117dd 100644 --- a/source/c07/c07_05.md +++ b/source/c07/c07_05.md @@ -274,4 +274,4 @@ echo "nameserver 8.8.8.8" > /etc/netns/ns1/resolv.conf --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index 313b1d5..0667b18 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -317,9 +317,10 @@ Socket发些“奇怪”的数据。 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/18-1-28/92519416.jpg .. |image1| image:: http://image.python-online.cn/18-1-28/37395940.jpg diff --git a/source/c07/c07_06.md b/source/c07/c07_06.md index 2ea06df..2aece07 100644 --- a/source/c07/c07_06.md +++ b/source/c07/c07_06.md @@ -280,4 +280,4 @@ docker-machine scp bm-docker-01:/tmp/a bm-docker-02:/tmp/b --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_06.rst b/source/c07/c07_06.rst index 3e716dd..9a01a73 100755 --- a/source/c07/c07_06.rst +++ b/source/c07/c07_06.rst @@ -326,9 +326,10 @@ centos的配置文件路径如下,ubuntu的有所不同 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png .. |image1| image:: https://i.loli.net/2018/01/03/5a4ce6eeaff7d.png diff --git a/source/c07/c07_07.md b/source/c07/c07_07.md index fd6d728..948fab4 100644 --- a/source/c07/c07_07.md +++ b/source/c07/c07_07.md @@ -353,4 +353,4 @@ salt minion01 saltutil.sync_grains # 只同步grains --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_07.rst b/source/c07/c07_07.rst index e0cf0f6..7af2e7c 100755 --- a/source/c07/c07_07.rst +++ b/source/c07/c07_07.rst @@ -416,6 +416,7 @@ salt命令格式 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c07/c07_08.rst b/source/c07/c07_08.rst index c45882e..a0a1c35 100644 --- a/source/c07/c07_08.rst +++ b/source/c07/c07_08.rst @@ -173,6 +173,7 @@ chk_zabbix.sh -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c07/c07_10.md b/source/c07/c07_10.md index abe3257..5c9e3f9 100644 --- a/source/c07/c07_10.md +++ b/source/c07/c07_10.md @@ -171,5 +171,5 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index 5f59296..494ce9f 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -184,9 +184,10 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190716111523.png .. |image1| image:: http://image.python-online.cn/20190716112113.png diff --git a/source/c08/c08_01.md b/source/c08/c08_01.md index 22ed87a..694ef43 100644 --- a/source/c08/c08_01.md +++ b/source/c08/c08_01.md @@ -430,4 +430,4 @@ pkill -9 pacemaker;service pacemaker restart --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index fbace3b..90bb12e 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -444,6 +444,7 @@ aggregate管理 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! diff --git a/source/c08/c08_02.md b/source/c08/c08_02.md index 9a65181..2b88eda 100644 --- a/source/c08/c08_02.md +++ b/source/c08/c08_02.md @@ -264,4 +264,4 @@ select * from pci_devices where instance_uuid='5a1c1828-4190-43fa-8e05-ae51b0196 ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index 9e1de14..09c3722 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -303,9 +303,10 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/01/19/5a61bfa0ca66f.png .. |image1| image:: https://i.loli.net/2018/01/19/5a61bfd243111.png diff --git a/source/c08/c08_03.md b/source/c08/c08_03.md index a2e8aef..55fac94 100644 --- a/source/c08/c08_03.md +++ b/source/c08/c08_03.md @@ -484,4 +484,4 @@ $ virsh blockcommit ws_controller01 hda --active --verbose --pivot --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index caa18fc..229c25c 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -528,9 +528,10 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png .. |image1| image:: https://i.loli.net/2018/01/27/5a6c34b14c6ec.png diff --git a/source/c08/c08_04.md b/source/c08/c08_04.md index 5e9f7c1..0c25a83 100644 --- a/source/c08/c08_04.md +++ b/source/c08/c08_04.md @@ -433,4 +433,4 @@ nova boot \ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index cf9ca40..fa24ae1 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -483,9 +483,10 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190714161353.png .. |image1| image:: http://image.python-online.cn/20190716004341.png diff --git a/source/c08/c08_05.md b/source/c08/c08_05.md index 560347b..7bde76a 100644 --- a/source/c08/c08_05.md +++ b/source/c08/c08_05.md @@ -670,4 +670,4 @@ python -c 'import crypt,getpass;pw=getpass.getpass();print(crypt.crypt(pw,crypt. --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index 73503e7..8448872 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -730,9 +730,10 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190526144846.png .. |image1| image:: http://image.python-online.cn/20190529135942.png diff --git a/source/c08/c08_06.md b/source/c08/c08_06.md index 3f96d30..a4ca292 100644 --- a/source/c08/c08_06.md +++ b/source/c08/c08_06.md @@ -624,4 +624,4 @@ ip addr flush dev ens3 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index ed5230e..682d12d 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -705,9 +705,10 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190430204707.png .. |image1| image:: http://image.python-online.cn/20190430204933.png diff --git a/source/c08/c08_07.md b/source/c08/c08_07.md index 2025f33..b81e476 100644 --- a/source/c08/c08_07.md +++ b/source/c08/c08_07.md @@ -123,4 +123,4 @@ openstack image set --property img_hide_hypervisor_id=true --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index 46485f0..0b65413 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -148,9 +148,10 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190419144135.png .. |image1| image:: http://image.python-online.cn/20190419144044.png diff --git a/source/c08/c08_08.md b/source/c08/c08_08.md index 26ba22c..3acfe11 100644 --- a/source/c08/c08_08.md +++ b/source/c08/c08_08.md @@ -185,4 +185,4 @@ systemctl restart neutron-openvswitch-agent --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index 565c0fb..049617e 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -249,9 +249,10 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190514202013.png .. |image1| image:: http://image.python-online.cn/20190514202442.png diff --git a/source/c08/c08_09.md b/source/c08/c08_09.md index d2df23e..7c9cc13 100644 --- a/source/c08/c08_09.md +++ b/source/c08/c08_09.md @@ -676,4 +676,4 @@ server.wait() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index 9e929df..2c1d900 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -782,9 +782,10 @@ rpc server 和rpc client 的四个重要方法 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190623185008.png .. |image1| image:: http://image.python-online.cn/20190623165341.png diff --git a/source/c08/c08_11.md b/source/c08/c08_11.md index 1514a56..cecf80d 100644 --- a/source/c08/c08_11.md +++ b/source/c08/c08_11.md @@ -80,4 +80,4 @@ $ nova reboot --hard -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index ec5f8f1..44680f0 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -82,9 +82,10 @@ ConfigDrive 是一个 iso9660 格式的文件,只读。 # 硬重启,重新生成 xml,如果有必要的话 $ nova reboot --hard -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190530175817.png diff --git a/source/c08/c08_12.md b/source/c08/c08_12.md index a6228ab..aac31f2 100644 --- a/source/c08/c08_12.md +++ b/source/c08/c08_12.md @@ -68,4 +68,4 @@ LOG.debug("Selected host: %(host)s", {'host': chosen_host}) --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index 7a8297c..5057fe5 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -86,9 +86,10 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190424212211.png .. |image1| image:: http://image.python-online.cn/20190424213430.png diff --git a/source/c08/c08_13.md b/source/c08/c08_13.md index 585c8b1..7b715cc 100644 --- a/source/c08/c08_13.md +++ b/source/c08/c08_13.md @@ -312,4 +312,4 @@ ovs-vsctl set port vnet0 tag=4 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index 26cec6d..1633296 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -312,9 +312,10 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190706114314.png .. |image1| image:: http://image.python-online.cn/20190706093904.png diff --git a/source/c08/c08_14.md b/source/c08/c08_14.md index 48f2e9a..dd83ae8 100644 --- a/source/c08/c08_14.md +++ b/source/c08/c08_14.md @@ -329,4 +329,4 @@ sudo sysctl -p --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index 9db3710..bfd3507 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -323,9 +323,10 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20190716175250.png .. |image1| image:: http://image.python-online.cn/20190716180655.png diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index 988cb23..23d9ccd 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -141,4 +141,4 @@ img_new.convert('RGB').save("F://save.jpeg") -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 4528264..216c302 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -150,9 +150,10 @@ img_new.convert('RGB').save("F://save.jpeg") -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! + 关注公众号,获取最新干货! .. |image0| image:: http://image.python-online.cn/20200214104413.png .. |image1| image:: http://image.python-online.cn/save.jpeg diff --git a/source/chapters/p01.rst b/source/chapters/p01.rst index e7b7e8d..93de56a 100755 --- a/source/chapters/p01.rst +++ b/source/chapters/p01.rst @@ -26,4 +26,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p02.rst b/source/chapters/p02.rst index fbd3909..f0eeafa 100755 --- a/source/chapters/p02.rst +++ b/source/chapters/p02.rst @@ -18,4 +18,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p03.rst b/source/chapters/p03.rst index f87a845..054b6b7 100755 --- a/source/chapters/p03.rst +++ b/source/chapters/p03.rst @@ -18,4 +18,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p04.rst b/source/chapters/p04.rst index 04f3a86..e366137 100755 --- a/source/chapters/p04.rst +++ b/source/chapters/p04.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p05.rst b/source/chapters/p05.rst index 6de09b5..1a47f88 100755 --- a/source/chapters/p05.rst +++ b/source/chapters/p05.rst @@ -28,4 +28,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p06.rst b/source/chapters/p06.rst index 4a9c044..5d015f8 100755 --- a/source/chapters/p06.rst +++ b/source/chapters/p06.rst @@ -17,4 +17,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p07.rst b/source/chapters/p07.rst index 6456af0..2ae6b0b 100755 --- a/source/chapters/p07.rst +++ b/source/chapters/p07.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p08.rst b/source/chapters/p08.rst index 66e6489..9189ce8 100755 --- a/source/chapters/p08.rst +++ b/source/chapters/p08.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/chapters/p09.rst b/source/chapters/p09.rst index 7fccba4..4571ac5 100644 --- a/source/chapters/p09.rst +++ b/source/chapters/p09.rst @@ -18,4 +18,4 @@ Python 是一门可以快速开发的语言,使用它可以用最少的时间 -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/index.rst b/source/index.rst index 6cc5eb4..0071da4 100755 --- a/source/index.rst +++ b/source/index.rst @@ -22,4 +22,4 @@ Contents: -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/preface.rst b/source/preface.rst index fbd03fe..8ffbf73 100755 --- a/source/preface.rst +++ b/source/preface.rst @@ -31,6 +31,6 @@ ------------------------------ -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png .. _博客构建教程: http://python-online.cn/zh_CN/latest/c04/c04_03.html diff --git a/source/py_mp_index.md b/source/py_mp_index.md index 4500b4b..62880d9 100644 --- a/source/py_mp_index.md +++ b/source/py_mp_index.md @@ -56,6 +56,8 @@ 25、[ 别笑!Python 新手这五大坑你躲不过](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485573&idx=1&sn=362e7c5e73c27942e8250375257620e2&chksm=e8866867dff1e17140dc043b5dd33eec0e94e1616eab7e4b415bb767883d46d58a7243fb198f#rd) +26、[Python 3.9 发布,字典的合并操作符终于来了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485825&idx=1&sn=04075357936423097f692fa19857c3aa&chksm=e8866963dff1e075707b605fb1352f7760fb1b41040bdda4dec6b83370af37e009424d24f65e&token=2013245174&lang=zh_CN#rd) + ### 1.2 开发技巧 1、[让Python中类的属性具有惰性求值的能力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485759&idx=2&sn=e32b66c4666f96f84572738549a47a24&chksm=e88669dddff1e0cb0a4b6e6e965c9570b273236fa58a54351ae3b118d6a4bcbd52e452109a63&token=1148998814&lang=zh_CN#rd) @@ -190,6 +192,14 @@ 17、[为什么说线程是CPU调度的基本单位?](https://mp.weixin.qq.com/s/c3aZ-6UzZVD3_PvVhiDPWA) +18、[非常适合小白的 Asyncio 教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485842&idx=1&sn=ab85dab95cf47046ddcc35b60e633c40&chksm=e8866970dff1e066381c0ba53fe09e34b8f1ce52bbbfe568c1ee66b9cbad87f43b442895c770&token=2013245174&lang=zh_CN#rd) + +### 2.5 实战练习 + +1、[适合 Python 新手练习的绝佳项目](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485898&idx=2&sn=36a1b1a94dece0a290652da23679cff0&chksm=e8866928dff1e03e58b95b3cd24dbc23e368b6eb2c0599b2efe814924e21ec5c01935b2e0455&token=2013245174&lang=zh_CN#rd) + +2、[4个Python实战项目,让你瞬间读懂Python!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485852&idx=2&sn=eb3471a260cce835a755a780d6cc690d&chksm=e886697edff1e068bd0f9d456e26ee5ebe482580457951e42807e731d266c95f00c37de22fea&token=2013245174&lang=zh_CN#rd) + ## 03. 数据分析 ### 3.1 基础库 @@ -298,6 +308,12 @@ 7、[ 为了选出最合适的 http客户端,我做了个测评](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485651&idx=1&sn=647c22ac66e469ccc33478457dadf224&chksm=e8866831dff1e1279149ff1e3af84bef86d24c524b7b7c14e9c519cf7c4739fa8f10dcc4a8d0#rd) +8、[凭什么 FastAPI 火成这样?看这篇文章就知道了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485891&idx=1&sn=82bde3e83435ebb6beb7e3581f12f7b0&chksm=e8866921dff1e0376befb029e9f1d81f33b0fa55242182f8de20d2a6e6b0cce0b5efd02f9aef&token=2013245174&lang=zh_CN#rd) + +9、[要想 BUG 变得富有美感,这个库你一定要看](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485889&idx=1&sn=0258eb138dce2c71b533d340770d5d09&chksm=e8866923dff1e03548dee4f780b410c22b51e1c3ffde3ef814f6a9b94e49af941fc04c79a660&token=2013245174&lang=zh_CN#rd) + +10、[如何使用 Python 输出漂亮的表格?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485841&idx=2&sn=705c123abed5441a52f8640b65f3c400&chksm=e8866973dff1e0650e4b2e025d0e86b3aedeb7ea8f68ad560f37cb033f36660b6eee15fb3ce4&token=2013245174&lang=zh_CN#rd) + ## 05. 网络爬虫 1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect) @@ -354,10 +370,14 @@ 5、[ 神器!这款VSCode插件能填满Github绿色格子](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485595&idx=1&sn=65df21ab0b4042953dd8257bea8b1848&chksm=e8866879dff1e16fff97584467f2f1728f9c2da007894ae062e9201d53b82a30054036f8e81e#rd) +6、[Git 中冷门却又非常需要的高级用法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485890&idx=2&sn=528df28585ec16114324e391dbfebab4&chksm=e8866920dff1e036d5da7f5dfe3c944ef8b13d72f022d72b209117120c7e27ebb71036b99893&token=2013245174&lang=zh_CN#rd) + ### 7.3 MySQL 1、[写给程序员的 MySQL 面试高频 100 问](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485321&idx=1&sn=30432df24d4e7858a475192bcf233914&scene=21#wechat_redirect) +2、[开发人员必学的几点 SQL 优化点](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485892&idx=1&sn=e0b526002a5568391e75fb0cac60ab0b&chksm=e8866926dff1e0303f2717e95ff801c2550d97812c1a775cc232d48183af802db1be54f26105&token=2013245174&lang=zh_CN#rd) + ### 7.4 Chrome 1、[推荐 8个 超实用的 Chrome 插件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484925&idx=1&sn=110e4da78a20e8b5721f00bb36ba9d4c&scene=21#wechat_redirect) @@ -420,6 +440,8 @@ 2、[ 如何探测虚拟环境是物理机、虚拟机还是容器?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485401&idx=2&sn=8bfb6e1a71e0777a4d5e55f64b55ace5&chksm=e886673bdff1ee2d8acce291e402cd2ae0c7f64e455fdeea158eb53e535ee79b2aadc449935e#rd) +3、[超详细教你如何阅读 OpenStack 源码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485899&idx=1&sn=7a578a553c67e794ef6ec2f463864a46&chksm=e8866929dff1e03fa8152751967106e9262980db6afdff106fe9e39854d24d25494b3e8a3a4e&token=2013245174&lang=zh_CN#rd) + ## 11. 职场相关 1、[一个专科生的 Python 转行之路](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485012&idx=1&sn=751796e63a30d84be01486619b5fb806&scene=21#wechat_redirect) @@ -436,4 +458,4 @@ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20200315144434.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/roadmap.rst b/source/roadmap.rst index ac9b50a..a2f790b 100755 --- a/source/roadmap.rst +++ b/source/roadmap.rst @@ -12,4 +12,4 @@ Roadmap -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/thanks.rst b/source/thanks.rst index 05fc94c..d9b6b7a 100644 --- a/source/thanks.rst +++ b/source/thanks.rst @@ -25,4 +25,4 @@ -------------- -.. figure:: http://image.python-online.cn/20200315144434.png +.. figure:: http://image.python-online.cn/image-20200320125724880.png From 8e2ad106600539bb890c6e8a6e341e1885d09eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Wed, 25 Mar 2020 09:21:11 +0800 Subject: [PATCH 026/147] =?UTF-8?q?Update:=20=E6=9B=B4=E6=96=B0=20git=20?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_06.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source/c04/c04_06.md b/source/c04/c04_06.md index f6c81b5..383b341 100644 --- a/source/c04/c04_06.md +++ b/source/c04/c04_06.md @@ -171,6 +171,25 @@ $ git reflog # 显示所有修改的日志 ![](http://image.python-online.cn/20191217150942.png) +查看两个 commit 之间的修改 + +``` +$ git diff commit_id1 commit_id2 +$ git diff commit_id^! + +# 如果只想看改了哪些文件 +$ git diff commit_id1 commit_id2 --stat +``` + +查看某个 commit 的改动 + +``` +git show commit_id +git show --stat commit_id +``` + + + ## 三、状态回滚 往下看之前,请先理解这三个Git区域: `工作区` -> `暂存区(stage)` -> `版本库` From 634f56f098e22f2be264a5be41e2dfee2d359cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 27 Mar 2020 21:26:10 +0800 Subject: [PATCH 027/147] =?UTF-8?q?Add=EF=BC=9A=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E7=AB=A0<=E7=82=AB=E6=8A=80=E6=93=8D=E4=BD=9C=EF=BC=9A?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E8=AF=AD=E5=8F=A5=E7=9A=84=E4=B8=83=E7=A7=8D?= =?UTF-8?q?=E7=94=A8=E6=B3=95>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_37.md | 159 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 source/c01/c01_37.md diff --git a/source/c01/c01_37.md b/source/c01/c01_37.md new file mode 100644 index 0000000..11db288 --- /dev/null +++ b/source/c01/c01_37.md @@ -0,0 +1,159 @@ +# 1.37 Python 炫技操作:条件语句的七种写法 + +有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。 + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +在这个系列里,我将总结列举一下,我所见过的那些炫技操作,今天先来个热身,写一写很简单的条件判断语句里有哪些让人想骂街的炫技操作,在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧,但学习归学习,希望你区分场景使用。 + +## 原代码 + +这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。 +```python +if age > 18: + return "已成年" +else: + return "未成年" +``` + +下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外) + +## 第一种 + +语法: + +```python + if else +``` + +例子 + +```python +>>> age1 = 20 +>>> age2 = 17 +>>> +>>> +>>> msg1 = "已成年" if age1 > 18 else "未成年" +>>> print msg1 +已成年 +>>> +>>> msg2 = "已成年" if age2 > 18 else "未成年" +>>> print msg2 +未成年 +>>> +``` + +## 第二种 + +语法 + +```python + and or +``` + +例子 + +```python +>>> msg1 = age1 > 18 and "已成年" or "未成年" +>>> msg2 = "已成年" if age2 > 18 else "未成年" +>>> +>>> print(msg1) +已成年 +>>> +>>> print(msg2) +未成年 +``` + +## 第三种 + +语法 + +```python +(, )[condition] +``` + +例子 + +```python +>>> msg1 = ("未成年", "已成年")[age1 > 18] +>>> print(msg1) +已成年 +>>> +>>> +>>> msg2 = ("未成年", "已成年")[age2 > 18] +>>> print(msg2) +未成年 +``` + +## 第四种 + +语法 + +```python +(lambda: , lambda:)[]() +``` + +例子 + +```python +>>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() +>>> print(msg1) +已成年 +>>> +>>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() +>>> print(msg2) +未成年 +``` + +## 第五种 + +语法: + +``` +{True: , False: }[] +``` + +例子: + +```python +>>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] +>>> print(msg1) +已成年 +>>> +>>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] +>>> print(msg2) +未成年 +``` + +## 第六种 + +语法 + +``` +(() and (,) or (,))[0] +``` + +例子 + +```python +>>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] +>>> print(msg1) +已成年 +>>> +>>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] +>>> print(msg2) +未成年 +``` + +以上代码,都比较简单,注意看都能看懂,我就不做解释了。 + +看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From fb8147618fa9f0068407bbe63cc6fad75c7bbbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 27 Mar 2020 21:27:26 +0800 Subject: [PATCH 028/147] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_36.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md index 4757785..6b4f513 100644 --- a/source/c01/c01_36.md +++ b/source/c01/c01_36.md @@ -246,4 +246,6 @@ if __name__ == "__main__": -以上,就是我对 `pretty_errors` 的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,`pretty_errors` 会是一个不错的解决方案,明哥把它推荐给你。 \ No newline at end of file +以上,就是我对 `pretty_errors` 的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,`pretty_errors` 会是一个不错的解决方案,明哥把它推荐给你。 + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From 06fdcd0a1e0f10431c72da8db9604ec0f6087a7c Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 28 Mar 2020 09:29:34 +0800 Subject: [PATCH 029/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_37.md | 10 +-- source/c04/c04_07.md | 183 ++++++++++++++++++++++++++++--------------- source/c04/c04_18.md | 60 +++++++++++++- 3 files changed, 186 insertions(+), 67 deletions(-) diff --git a/source/c01/c01_37.md b/source/c01/c01_37.md index 11db288..ebdc0c0 100644 --- a/source/c01/c01_37.md +++ b/source/c01/c01_37.md @@ -30,7 +30,7 @@ else: 语法: ```python - if else + if else ``` 例子 @@ -55,7 +55,7 @@ else: 语法 ```python - and or + and or ``` 例子 @@ -116,7 +116,7 @@ else: 语法: -``` +```python {True: , False: }[] ``` @@ -136,7 +136,7 @@ else: 语法 -``` +```python (() and (,) or (,))[0] ``` @@ -152,7 +152,7 @@ else: 未成年 ``` -以上代码,都比较简单,注意看都能看懂,我就不做解释了。 +以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 diff --git a/source/c04/c04_07.md b/source/c04/c04_07.md index b3f5a18..226fd3c 100644 --- a/source/c04/c04_07.md +++ b/source/c04/c04_07.md @@ -2,24 +2,25 @@ --- -## 一、创建博客项目 +现在越来越多的人喜欢利用Github搭建静态网站,原因不外乎简单省钱。本人也利用hexo+github搭建了本博客,用于分享一些心得。在此过程中,折腾博客的各种配置以及功能占具了我一部分时间,在此详细记录下我是如何利用hexo+github搭建静态博客以及一些配置相关问题,以免过后遗忘,且当备份之用。 ---- +## 一、创建博客项目 ### 1.1 准备工作 -- 下载node.js并安装(官网下载安装),默认会安装npm。 +- 下载[node.js](http://nodejs.cn/download/)并安装(官网下载安装),默认会安装npm。 - 下载安装git(官网下载安装) -- 下载安装hexo。方法:打开cmd 运行*npm install -g hexo*(要翻墙) +- 下载安装hexo。方法:打开终端 运行`npm install -g hexo`(要翻墙) + +![](http://image.python-online.cn/image-20200321163152876.png) ### 1.2 初始化项目 -- 新创建一个目录,比如叫做 MyBlog -- 进入该文件夹内,右击运行 Git Bash,执行`hexo init` (初始化项目) +- 新创建一个目录,比如叫做 `iswbm-blog` +- 使用终端进入该文件夹内,执行`hexo init` (初始化项目) + +![](http://image.python-online.cn/image-20200321163746032.png) -```shell -$ hexo init -``` - 运行你的博客项目(用于调试),查看生成的文章是否符合自己的预期。 ```shell @@ -35,60 +36,64 @@ $ hexo s # 运行本地web服务器 按照下面的步骤来 -- 在Github上创建名字为XXX.github.io的项目,XXX为自己的github用户名。 +- 在Github上创建名字为XXX.github.io的项目,XXX为自己的github用户名。然后 clone 到你的本地电脑上。 -- 打开本地的MyBlog 文件夹内的`_config.yml`配置文件,将其中的type设置为git,其他配置如下 + ![](http://image.python-online.cn/image-20200321165634287.png) -``` +- 打开该文件夹内的`_config.yml`配置文件,将其中的type设置为git,其他配置如下 + +```yml # Deployment ## Docs: https://hexo.io/docs/deployment.html deploy: type: git - repository: https://github.com/BingmingWong/BingmingWong.github.io.git + repository: https://github.com/iswbm/iswbm.github.io.git branch: master ``` - 安装Git部署插件 -``` -npm install hexo-deployer-git --save +```shell +$ npm install hexo-deployer-git --save ``` - 重新生成静态文件,部署上线。 +```shell +$ hexo clean +$ hexo g +$ hexo d ``` -hexo clean -hexo g -hexo d -``` -一切完成之后,你就可以通过 来访问我的博客了。 +在第一次部署后,请到 github 后台,补充你的 网站信息,否则无法访问。 + +![](http://image.python-online.cn/image-20200321171008622.png) + +一切完成之后,你就可以通过上面的网址来访问我的博客了。 ### 1.4 绑定域名 做为一个个人博客,使用 github的域名,明显不够个性化。 -我们可以自己去阿里云购买一个域名,然后将其解析到你的博客地址上。 +我们可以自己去阿里云购买一个域名(我的是 iswbm.com),然后将其 CNAME 到你的博客地址上。 - 去阿里云 域名云解析 - 其中`151.101.73.147`是我的 `bingmingwong.github.io` 的ip,可以通过ping获得 - ![](http://image.python-online.cn/17-9-9/50205036.jpg) - ![](http://image.python-online.cn/17-9-9/5214564.jpg) - + ![](http://image.python-online.cn/image-20200321171939919.png) + - 然后到对应的GitHub仓库,绑定我的个性域名 - ![](http://image.python-online.cn/17-9-9/56478208.jpg) + ![](http://image.python-online.cn/image-20200321171821683.png) -- 然后再本地项目`blog/source/`下新建`CNAME`文件,内容就是我的域名 +- 然后再在你本地的项目里的 `source` 目录下新建 `CNAME`文件,内容就是我的域名 ``` - wongbingming.me + iswbm.com ``` - 然后重新部署,使配置生效。这样就可以使用域名访问。 - ``` - hexo clean - hexo g - hexo d + ```shell + $ hexo clean + $ hexo g + $ hexo d ``` ## 二、丰富博客页面 @@ -142,17 +147,19 @@ password: 然后使用 `hexo new`就可以一键生成新文章的头格式了,不用手动去搬运或者书写。相当方便。 +![image-20200321201555321](http://image.python-online.cn/image-20200321201555321.png) + ## 四、美化博客 --- ### 4.1 更换主题 -下载主题 +在 hexo 部署目录下,使用如下命令下载主题 ``` git clone https://github.com/iissnan/hexo-theme-next themes/next ``` -打开站点配置文件 +打开站点配置文件(部署代码根目录下的 `_config.yml`)选择刚刚下载的next主题 ``` # Extensions ## Plugins: https://hexo.io/plugins/ @@ -160,23 +167,22 @@ git clone https://github.com/iissnan/hexo-theme-next themes/next theme: next ``` -下面都是`..\themes\next/_config.yml`中设置 -选择主题样式(取消注释即可) +然后再编辑 `themes/next/_config.yml` 选择主题样式 + ``` scheme: Pisces ``` ### 4.2 ico缩略图 ``` -1. 制作icon图标,最好是32x32,可以在这里制作:http://www.faviconico.org/favicon +1. 制作icon图标,最好是32x32,可以在这里制作:https://tool.lu/favicon/ 2. 将制作的ico文件,放到next主题source/images目录下 -3. 配置ico文件路径。配置文件在 ..\themes\next\_config.yml - -# Put your favicon.ico into `hexo-site/source/` directory. -favicon: images/favicon.ico - +3. 配置ico文件路径。配置文件在 themes/next/_config.yml +favicon: + small: /images/favicon-16x16-next.ico + medium: /images/favicon-32x32-next.ico ``` ### 4.3 菜单栏和图标 @@ -198,7 +204,7 @@ menu_icons: ### 4.4 社交网络和图标 ``` social: - GitHub: https://github.com/BingmingWong || github + GitHub: https://github.com/iswbm || github E-Mail: mailto:wongbingming@163.com || envelope-o 微博: http://weibo.com/942663728 || weibo WeChat: http://image.python-online.cn/17-9-9/58657236.jpg || weixin @@ -217,6 +223,7 @@ social_icons: next主题集成leanCloud,我只需稍微配置下(在主题配置文件) 其中的id和key要去`LeanCloud`注册登录然后创建应用后,新建Class,名字一定要是`Counter`,然后查看id和key填入 + ``` post_meta: item_text: true @@ -226,9 +233,9 @@ leancloud_visitors: app_id: 你的id app_key: 你的key ``` -打开`/themes/next/layout/_macro/post.swig` +打开`themes/next/layout/_macro/post.swig` 在`”leancloud-visitors-count”>`标签后面添加℃。 -然后打开,/themes/next/languages/zh-Hans.yml,将visitors内容改为热度即可。 +然后打开,`themes/next/languages/zh-Hans.yml`,将visitors内容改为热度即可。 为什么不直接用不蒜子,因为首页的时候,无法显示。 如果也开了不蒜子的计数功能的话,可以直接把下面代码删掉 @@ -242,7 +249,26 @@ leancloud_visitors: {% endif %} ``` + + +如果你在前端看到了这个错误 + +``` +Code 504: The app is archived, please restore in console before use. +``` + +很好解决,前往 [LeanCloud](https://leancloud.cn/) 重新激活应用即可。 + +如果在前端又出现了这个错误 + +``` +Code 403: 访问被api域名白名单拒绝,请检查你的安全域名设置. +``` + +也很好解决,前往 [LeanCloud](https://leancloud.cn/) 绑定你的域名即可。不过要注意的是这个域名,你得备案,否则会绑定失败 + ### 4.7 分享插件JiaThis + 默认有好多分享平台,可以在jiathis.swig里删除不需要的 ``` jiathis: @@ -267,7 +293,7 @@ jiathis: {% endif %} --> ``` -### 4.8 Fork me on github +### 4.8 Fork me on Github 点击[这里](https://github.com/blog/273-github-ribbons)挑选自己喜欢的样式,并复制代码 然后粘贴刚才复制的代码到`themes/next/layout/_layout.swig`文件中(放在`
`的下面),并把`href`改为你的`github`地址 @@ -280,7 +306,7 @@ jiathis: ``` ### 4.10 添加版权 -修改`F:\MyBlog\themes\next\layout\_macro\post-copyright.swig`如下 +修改`themes/next/layout/_macro/post-copyright.swig`如下 ```
  • @@ -307,13 +333,13 @@ jiathis: 到主题配置文件,改`enable`为`True` ``` post_copyright: - enable: True + enable: true license: CC BY-NC-SA 3.0 license_url: https://creativecommons.org/licenses/by-nc-sa/3.0/ ``` ### 4.11 文章结尾页眉 -在路径`\themes\next\layout\_macro`中新建 `passage-end-tag.swig` 文件,并添加以下内容: +在路径`themes/next/layout/_macro`中新建 `passage-end-tag.swig` 文件,并添加以下内容: ```
    {% if not is_index %} @@ -321,9 +347,10 @@ post_copyright: {% endif %}
    ``` -接着打开`\themes\next\layout\_macro\post.swig`文件,添加如下下代码,注意位置 +接着打开`themes/next/layout/_macro/post.swig`文件,添加如下下代码,注意位置 ![](http://image.python-online.cn/17-9-9/63041495.jpg) 代码如下: + ```
    {% if not is_index %} @@ -360,7 +387,7 @@ passage_end_tag: 更改NexT容器宽度可以参考这个[常见问题](http://theme-next.iissnan.com/faqs.html) Pisces Scheme比较特殊。 -在`F:\MyBlog\themes\next\source\css\_schemes\Pisces\_layout.styl`最后面增加如下样式 +在`themes/next/source/css/_schemes/Pisces/_layout.styl`最后面增加如下样式 ``` .header{ @@ -400,13 +427,13 @@ Pisces Scheme比较特殊。 ``` # 设置图片不居中 -themes\next\source\css\_common\components\post\post-expand.styl +themes/next/source/css/_common/components/post/post-expand.styl .post-gallery-row .fancybox img { margin: 0 auto !important;} # 标题格式 -themes\next\source\css\_common\scaffolding\base.styl +themes/next/source/css/_common/scaffolding/base.styl h1 { font-size: 27px; @@ -422,12 +449,46 @@ h1 { font-family: cursive; border-radius: 15px 15px 15px 15px !important; } +``` + + + +### 4.16 添加头像 +将你的头像放置到 `themes/next/source/images/avatar.png` +在 `themes/next/_config.yml` 设置路径 + +```yaml +avatar: /images/avatar.png ``` +### 4.17 设置中文 + +在根目录的 `_config.yml` 里 把 `language` 改成 `zh-Hans`,这个值是要和 `themes/next/languages` 目录下的文件名保持一致。 + + + +## 4.18 不渲染 README + +在根目录创建 README.md 文件,内容由你决定。 + +然后在 `_config.yml` 修改配置 + +```yaml +skip_render: README.md +``` + + + +### 4.18 页面出现 %20 问题 + +Next 主题的配置文件有一个原生 bug,就是菜单项后面会多一个空格,这会导致你在页面访问 about,tags等页面时,会报 404,原因是地址后面多了个空格。 + +![](/Users/MING/Library/Application Support/typora-user-images/image-20200321210014963.png) + ## 五、其他实用功能 ### 5.1 给文章加密 @@ -559,8 +620,10 @@ top: 100 ``` 其中选择.post-body 是为了不影响标题,选择 p 是为了不影响首页“阅读全文”的显示样式,颜色可以自己定义。 - ### 5.8 归档设置分页数量 + +在根目录的 `_config.yml` + ``` index_generator: per_page: 5 @@ -575,7 +638,7 @@ tag_generator: ``` ### 5.9 设置永久链接 -站点配置文件 +在根目录的 `_config.yml` ``` permalink: :year/:i_month/:i_day/:title.html ``` @@ -586,14 +649,12 @@ permalink: Database-MySQL-Basic_usage -一个是,如何在多台电脑都能使用Hexo更新博客 - ## 六、多台电脑协同更新博客 --- -这个问题很普遍,上班后,工作都会给配置了一台电脑,而自己家里也有电脑,有时候我想使用家里电脑更新博客,而有时候我也想使用公司电脑更新博客。 +上班后,工作都会给配置了一台电脑,而自己家里也有电脑,有时候我想使用家里电脑更新博客,而有时候我也想使用公司电脑更新博客。 这就很蛋疼了,因为Markdown的原文只有一份,如何将两台电脑的原文保持一致呢。当然,容易想到的是代管在Github上。 @@ -607,14 +668,14 @@ permalink: Database-MySQL-Basic_usage 在web界面新建分支,命名为`hexo` 在web界面设置 `hexo` 为默认分支,因为我们只会在这个分支上进行操作。 -![](https://i.loli.net/2018/04/15/5ad3018e18cc7.png) +![](http://image.python-online.cn/image-20200321193444320.png) ### 6.2 本地PC操作 clone项目到本地: ``` -git clone git@github.com:BingmingWong/BingmingWong.github.io.git +git clone git@github.com:iswbm/iswbm.github.io.git ``` 使用 `git branch` 确认此时的分支是否为hexo,如果不是则上面设置默认分支有误,要重新设置。 @@ -689,7 +750,7 @@ npm install -g hexo ### 7.2 clone最新分支 ``` -git clone git@github.com:BingmingWong/BingmingWong.github.io.git +git clone git@github.com:iswbm/iswbm.github.io.git ``` 添加node_modules/文件夹 diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index d8c06d8..50a230f 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -177,7 +177,7 @@ ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 -Downie:网页视频下载,复制链接即可。 +Downie:网页视频下载,复制链接即可,支持优酷、YouTube 等 700+ 网站。 GoodSync:和 windows 平台同步文件 @@ -185,8 +185,32 @@ GoodSync:和 windows 平台同步文件 TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) +NTFS for Mac 助手,安装可以在 Mac 上读写 NTFS 格式的移动硬盘或U盘。 + NewFileMenu:使得可以在访达中新建文件 +ScreenFlow:视频录制 + +One Switch,多合一功能开关合集,一键隐藏桌面、保持屏幕常亮、切换夜间模式。 + +Fantastical,可能是 Mac 上最好用的日历工具。 + +TinyCal(小历),菜单栏日历小工具,有农历和假期。Mac App Store 购买下载。 + +iMazing,最佳 iOS 备份及管理软件,完美替代 iTunes。 + +Permute 3,精致小巧的视频格式转换工具。 + +PP 鸭,好用的多格式图片压缩软件。 + +Squash,优雅而强大的图片压缩工具。领取优惠购买链接: + +GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 + +PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 + +iText,精准的 OCR 文字识别工具。 + ## 4.18.6 brew 的使用 设置国内源 @@ -292,7 +316,41 @@ command + w +## 4.18.8 使用小鹤双拼 + +2018 款的 MBP 系统是 10.13.6 ,这个系统支持的双拼是自然码,若想使用小鹤双拼,可以使用如下命令 + +```shell +defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 +``` + +同样的,还有更多的方案,都可以使用命令来修改 + +```shell +全拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 0 + +智能 ABC:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 1 + +微软双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 2 + +紫光双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 3 + +小鹤双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 + +自然码:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 5 + +拼音加加:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 6 + +搜狗双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 7 +``` + +练习的话,可以使用这两个网站: + +练习单字:https://api.ihint.me/shuang/ + +练习文章:https://api.ihint.me/zi/ +对应的 github:https://github.com/BlueSky-07/Shuang ## 参考文章 From 07d30689b003e5d8ea7248c541b754f6859deb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Wed, 1 Apr 2020 19:34:25 +0800 Subject: [PATCH 030/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E4=B8=A4?= =?UTF-8?q?=E7=AF=87=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + md2rst.py | 6 +- source/c01/c01_37.rst | 179 ++++++++++++++++++++++++++++++++++++++++++ source/c01/c01_38.md | 59 ++++++++++++++ source/c01/c01_38.rst | 74 +++++++++++++++++ 5 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 source/c01/c01_37.rst create mode 100644 source/c01/c01_38.md create mode 100644 source/c01/c01_38.rst diff --git a/README.md b/README.md index bddbe5a..ccc5bcf 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ - 1.34 [每日一库:sh,最优雅的命令调用方式](http://python.iswbm.com/en/latest/c01/c01_34.html) - 1.35 [使用 Python 远程登陆服务器的利器](http://python.iswbm.com/en/latest/c01/c01_35.html) - 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python.iswbm.com/en/latest/c01/c01_36.html) +- 1.37 [Python 炫技操作:条件语句的七种写法](http://python.iswbm.com/en/latest/c01/c01_37.html) +- 1.38 [/usr/bin/env python 有什么用?](http://python.iswbm.com/en/latest/c01/c01_38.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) diff --git a/md2rst.py b/md2rst.py index 3d53eaa..4b94018 100644 --- a/md2rst.py +++ b/md2rst.py @@ -10,9 +10,9 @@ osName = platform.system() repo_path ='.' if (osName == 'Windows'): - repo_path = 'E:\\MING-Git\\06. PythonCodingTime' - blog_path = 'E:\\MING-Git\\06. PythonCodingTime\\source' - index_path = 'E:\\MING-Git\\06. PythonCodingTime\\README.md' + repo_path = 'E:\\MING-Git\\PythonCodingTime' + blog_path = 'E:\\MING-Git\\PythonCodingTime\\source' + index_path = 'E:\\MING-Git\\PythonCodingTime\\README.md' elif (osName == 'Darwin'): repo_path = '/Users/MING/Github/PythonCodingTime/' blog_path = '/Users/MING/Github/PythonCodingTime/source' diff --git a/source/c01/c01_37.rst b/source/c01/c01_37.rst new file mode 100644 index 0000000..af11db0 --- /dev/null +++ b/source/c01/c01_37.rst @@ -0,0 +1,179 @@ +1.37 Python 炫技操作:条件语句的七种写法 +======================================== + +有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。 + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python +发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: 1. +越简洁的代码,越清晰的逻辑,就越不容易出错; 2. +在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. +简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +在这个系列里,我将总结列举一下,我所见过的那些炫技操作,今天先来个热身,写一写很简单的条件判断语句里有哪些让人想骂街的炫技操作,在这里,如果你是 +Python +发烧友,你可以学到一些写出超酷的代码书写技巧,但学习归学习,希望你区分场景使用。 + +原代码 +------ + +这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 +Python 功力。 + +.. code:: python + + if age > 18: + return "已成年" + else: + return "未成年" + +下面我列举了六种这段代码的变异写法,一个比一个还 6 +,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?** +(除了第一种之外) + +第一种 +------ + +语法: + +.. code:: python + + if else + +例子 + +.. code:: python + + >>> age1 = 20 + >>> age2 = 17 + >>> + >>> + >>> msg1 = "已成年" if age1 > 18 else "未成年" + >>> print msg1 + 已成年 + >>> + >>> msg2 = "已成年" if age2 > 18 else "未成年" + >>> print msg2 + 未成年 + >>> + +第二种 +------ + +语法 + +.. code:: python + + and or + +例子 + +.. code:: python + + >>> msg1 = age1 > 18 and "已成年" or "未成年" + >>> msg2 = "已成年" if age2 > 18 else "未成年" + >>> + >>> print(msg1) + 已成年 + >>> + >>> print(msg2) + 未成年 + +第三种 +------ + +语法 + +.. code:: python + + (, )[condition] + +例子 + +.. code:: python + + >>> msg1 = ("未成年", "已成年")[age1 > 18] + >>> print(msg1) + 已成年 + >>> + >>> + >>> msg2 = ("未成年", "已成年")[age2 > 18] + >>> print(msg2) + 未成年 + +第四种 +------ + +语法 + +.. code:: python + + (lambda: , lambda:)[]() + +例子 + +.. code:: python + + >>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() + >>> print(msg1) + 已成年 + >>> + >>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() + >>> print(msg2) + 未成年 + +第五种 +------ + +语法: + +.. code:: python + + {True: , False: }[] + +例子: + +.. code:: python + + >>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] + >>> print(msg1) + 已成年 + >>> + >>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] + >>> print(msg2) + 未成年 + +第六种 +------ + +语法 + +.. code:: python + + (() and (,) or (,))[0] + +例子 + +.. code:: python + + >>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] + >>> print(msg1) + 已成年 + >>> + >>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] + >>> print(msg2) + 未成年 + +以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 + +看到这里,有没有涨姿势了,学了这么久的 Python +,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c01/c01_38.md b/source/c01/c01_38.md new file mode 100644 index 0000000..a40bf3e --- /dev/null +++ b/source/c01/c01_38.md @@ -0,0 +1,59 @@ +# 1.38 /usr/bin/env python 有什么用? + +我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 + +```json +#!/usr/bin/python +``` + +或者这样 + +```json +#!/usr/bin/env python +``` + +这两者有什么区别呢? + + + +稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python` + +![](http://image.python-online.cn/20200331184021.png) + +而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。 + +那么加和不加有什么区别呢? + +不加的话,你每次执行这个脚本时,都得这样: `python xx.py` , + +![](http://image.python-online.cn/20200331185034.png) + +有没有一种方式?可以省去每次都加 `python` 呢? + +当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 + +![](http://image.python-online.cn/20200331184755.png) + +明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ? + +当我执行 `env python` 时,自动进入了 python console 的模式。 + +![](http://image.python-online.cn/20200331185741.png) + +这是为什么?和 直接执行 python 好像没什么区别呀 + +当你执行 `env python` 时,它其实会去 `env | grep PATH` 里(也就是 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin )这几个路径里去依次查找名为python的可执行文件。 + +找到一个就直接执行,上面我们的 python 路径是在 `/usr/bin/python` 里,在 `PATH` 列表里倒数第二个目录下,所以当我在 `/usr/local/sbin` 下创建一个名字也为 python 的可执行文件时,就会执行 `/usr/bin/python` 了。 + +具体演示过程,你可以看下面。 + +![](http://image.python-online.cn/20200331190224.png) + +那么对于这两者,我们应该使用哪个呢? + +个人感觉应该优先使用 `#!/usr/bin/env python`,因为不是所有的机器的 python 解释器都是 `/usr/bin/python` 。 + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_38.rst b/source/c01/c01_38.rst new file mode 100644 index 0000000..a0c5871 --- /dev/null +++ b/source/c01/c01_38.rst @@ -0,0 +1,74 @@ +1.38 /usr/bin/env python 有什么用? +=================================== + +我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 + +.. code:: json + + #!/usr/bin/python + +或者这样 + +.. code:: json + + #!/usr/bin/env python + +这两者有什么区别呢? + +稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python`` +进入console 模式里的 ``python`` + +|image0| + +而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python`` +,意思就是说你得用哪个软件 (python)来执行这个文件。 + +那么加和不加有什么区别呢? + +不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` , + +|image1| + +有没有一种方式?可以省去每次都加 ``python`` 呢? + +当然有,你可以文件头里加上\ ``#!/usr/bin/python`` +,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 + +|image2| + +明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? + +当我执行 ``env python`` 时,自动进入了 python console 的模式。 + +|image3| + +这是为什么?和 直接执行 python 好像没什么区别呀 + +当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是 +/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin +)这几个路径里去依次查找名为python的可执行文件。 + +找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python`` +里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin`` +下创建一个名字也为 python 的可执行文件时,就会执行 ``/usr/bin/python`` +了。 + +具体演示过程,你可以看下面。 + +|image4| + +那么对于这两者,我们应该使用哪个呢? + +个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 +python 解释器都是 ``/usr/bin/python`` 。 + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.python-online.cn/20200331184021.png +.. |image1| image:: http://image.python-online.cn/20200331185034.png +.. |image2| image:: http://image.python-online.cn/20200331184755.png +.. |image3| image:: http://image.python-online.cn/20200331185741.png +.. |image4| image:: http://image.python-online.cn/20200331190224.png From 8baa1c09493e094aacbaad1ecec424fac9f5e70e Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 8 Apr 2020 21:19:14 +0800 Subject: [PATCH 031/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0<=E5=90=88=E5=B9=B6=E5=AD=97=E5=85=B8=E7=9A=84?= =?UTF-8?q?=E4=B8=83=E7=A7=8D=E6=96=B9=E6=B3=95>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_39.md | 218 ++++++++++++++++++++++++++++++++++++++++ source/c01/c01_39.rst | 228 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 446 insertions(+) create mode 100644 source/c01/c01_39.md create mode 100644 source/c01/c01_39.rst diff --git a/source/c01/c01_39.md b/source/c01/c01_39.md new file mode 100644 index 0000000..c66081f --- /dev/null +++ b/source/c01/c01_39.md @@ -0,0 +1,218 @@ +# 1.38 Python 炫技操作:合并字典的七种方法 + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: + +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +该篇是「**炫技系列**」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 + +## 1. 最简单的原地更新 + +字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> profile.update(ext_info) +>>> print(profile) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +如果想使用 update 这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> from copy import deepcopy +>>> +>>> full_profile = deepcopy(profile) +>>> full_profile.update(ext_info) +>>> +>>> print(full_profile) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> print(profile) +{"name": "xiaoming", "age": 27} +``` + + + +## 2. 先解包再合并字典 + +使用 `**` 可以解包字典,解包完后再使用 dict 或者 `{}` 就可以合并。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> full_profile01 = {**profile, **ext_info} +>>> print(full_profile01) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> +>>> full_profile02 = dict(**profile, **ext_info) +>>> print(full_profile02) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +若你不知道 `dict(**profile, **ext_info)` 做了啥,你可以将它等价于 + +```python +>>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +## 3. 借助 itertools + +在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +正好我们字典也是可迭代对象,自然就可以想到,可以使用 `itertools.chain()` 函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 dict 转成字典。 + +```python +>>> import itertools +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> +>>> dict(itertools.chain(profile.items(), ext_info.items())) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +## 4. 借助 ChainMap + +如果可以引入一个辅助包,那我就再提一个, `ChainMap` 也可以达到和 `itertools` 同样的效果。 + +```python +>>> from collections import ChainMap +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(ChainMap(profile, ext_info)) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +使用 ChainMap 有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 itertools 就不会有这个问题)。 + +```python +>>> from collections import ChainMap +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info={"age": 30} +>>> dict(ChainMap(profile, ext_info)) +{'name': 'xiaoming', 'age': 27} +``` + + + +## 5. 使用dict.items() 合并 + +在 Python 3.9 之前,其实就已经有 `|` 操作符了,只不过它通常用于对集合(set)取并集。 + +利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 + +你得先利用 `items` 方法将 dict 转成 dict_items,再对这两个 dict_items 取并集,最后利用 dict 函数,转成字典。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> full_profile = dict(profile.items() | ext_info.items()) +>>> full_profile +{'gender': 'male', 'age': 27, 'name': 'xiaoming'} +``` + + + +当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list 函数再合并(示例为 Python 3.x ) + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(list(profile.items()) + list(ext_info.items())) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +若你在 Python 2.x 下,可以直接省去 list 函数。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(profile.items() + ext_info.items()) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +## 6. 最酷炫的字典解析式 + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? + +当然可以,具体示例代码如下: + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> {k:v for d in [profile, ext_info] for k,v in d.items()} +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +## 7. Python 3.9 新特性 + +在 2 月份发布的 Python 3.9.04a 版本中,新增了一个抓眼球的新操作符操作符: `|`, PEP584 将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> profile | ext_info +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> +>>> ext_info | profile +{'gender': 'male', 'name': 'xiaoming', 'age': 27} +>>> +>>> +``` + +除了 `|` 操作符之外,还有另外一个操作符 `|=`,类似于原地更新。 + +```python +>>> ext_info |= profile +>>> ext_info +{'gender': 'male', 'name': 'xiaoming', 'age': 27} +>>> +>>> +>>> profile |= ext_info +>>> profile +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +看到这里,有没有涨姿势了,学了这么久的 Python ,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 7 种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 + + + +--- + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_39.rst b/source/c01/c01_39.rst new file mode 100644 index 0000000..f6b4b05 --- /dev/null +++ b/source/c01/c01_39.rst @@ -0,0 +1,228 @@ +1.38 Python 炫技操作:合并字典的七种方法 +======================================== + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python +发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: + +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +该篇是「\ **炫技系列**\ 」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 +Python +发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 + +1. 最简单的原地更新 +------------------- + +字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> profile.update(ext_info) + >>> print(profile) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +如果想使用 update +这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> from copy import deepcopy + >>> + >>> full_profile = deepcopy(profile) + >>> full_profile.update(ext_info) + >>> + >>> print(full_profile) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> print(profile) + {"name": "xiaoming", "age": 27} + +2. 先解包再合并字典 +------------------- + +使用 ``**`` 可以解包字典,解包完后再使用 dict 或者 ``{}`` 就可以合并。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> full_profile01 = {**profile, **ext_info} + >>> print(full_profile01) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> + >>> full_profile02 = dict(**profile, **ext_info) + >>> print(full_profile02) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +若你不知道 ``dict(**profile, **ext_info)`` 做了啥,你可以将它等价于 + +.. code:: python + + >>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +3. 借助 itertools +----------------- + +在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +正好我们字典也是可迭代对象,自然就可以想到,可以使用 +``itertools.chain()`` +函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 +dict 转成字典。 + +.. code:: python + + >>> import itertools + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> + >>> dict(itertools.chain(profile.items(), ext_info.items())) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +4. 借助 ChainMap +---------------- + +如果可以引入一个辅助包,那我就再提一个, ``ChainMap`` 也可以达到和 +``itertools`` 同样的效果。 + +.. code:: python + + >>> from collections import ChainMap + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(ChainMap(profile, ext_info)) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +使用 ChainMap +有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 +itertools 就不会有这个问题)。 + +.. code:: python + + >>> from collections import ChainMap + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info={"age": 30} + >>> dict(ChainMap(profile, ext_info)) + {'name': 'xiaoming', 'age': 27} + +5. 使用dict.items() 合并 +------------------------ + +在 Python 3.9 之前,其实就已经有 ``|`` +操作符了,只不过它通常用于对集合(set)取并集。 + +利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 + +你得先利用 ``items`` 方法将 dict 转成 dict_items,再对这两个 dict_items +取并集,最后利用 dict 函数,转成字典。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> full_profile = dict(profile.items() | ext_info.items()) + >>> full_profile + {'gender': 'male', 'age': 27, 'name': 'xiaoming'} + +当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list +函数再合并(示例为 Python 3.x ) + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(list(profile.items()) + list(ext_info.items())) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +若你在 Python 2.x 下,可以直接省去 list 函数。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(profile.items() + ext_info.items()) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +6. 最酷炫的字典解析式 +--------------------- + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python +发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? + +当然可以,具体示例代码如下: + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> {k:v for d in [profile, ext_info] for k,v in d.items()} + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +7. Python 3.9 新特性 +-------------------- + +在 2 月份发布的 Python 3.9.04a +版本中,新增了一个抓眼球的新操作符操作符: ``|``\ , PEP584 +将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> profile | ext_info + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> + >>> ext_info | profile + {'gender': 'male', 'name': 'xiaoming', 'age': 27} + >>> + >>> + +除了 ``|`` 操作符之外,还有另外一个操作符 ``|=``\ ,类似于原地更新。 + +.. code:: python + + >>> ext_info |= profile + >>> ext_info + {'gender': 'male', 'name': 'xiaoming', 'age': 27} + >>> + >>> + >>> profile |= ext_info + >>> profile + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +看到这里,有没有涨姿势了,学了这么久的 Python +,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 +7 +种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 + +-------------- + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! From e2caccf3d0bad4c606e536a84ef002f6e1a07990 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 8 Apr 2020 21:19:58 +0800 Subject: [PATCH 032/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_36.rst | 5 + source/c01/c01_38.rst | 1 + source/c04/c04_06.rst | 17 ++++ source/c04/c04_07.rst | 208 ++++++++++++++++++++++++++++-------------- source/c04/c04_18.rst | 66 +++++++++++++- 5 files changed, 228 insertions(+), 69 deletions(-) diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst index cd790dc..f137980 100644 --- a/source/c01/c01_36.rst +++ b/source/c01/c01_36.rst @@ -266,6 +266,11 @@ PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,\ ``pretty_errors`` 会是一个不错的解决方案,明哥把它推荐给你。 +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/image-20200307210853246.png .. |image1| image:: http://image.python-online.cn/image-20200307212823345.png .. |image2| image:: http://image.python-online.cn/image-20200307213534278.png diff --git a/source/c01/c01_38.rst b/source/c01/c01_38.rst index a0c5871..c0d5d1e 100644 --- a/source/c01/c01_38.rst +++ b/source/c01/c01_38.rst @@ -72,3 +72,4 @@ python 解释器都是 ``/usr/bin/python`` 。 .. |image2| image:: http://image.python-online.cn/20200331184755.png .. |image3| image:: http://image.python-online.cn/20200331185741.png .. |image4| image:: http://image.python-online.cn/20200331190224.png + diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 32b313d..d4a00df 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -179,6 +179,23 @@ add/commit 前撤销对文件的修改 |image0| +查看两个 commit 之间的修改 + +:: + + $ git diff commit_id1 commit_id2 + $ git diff commit_id^! + + # 如果只想看改了哪些文件 + $ git diff commit_id1 commit_id2 --stat + +查看某个 commit 的改动 + +:: + + git show commit_id + git show --stat commit_id + 三、状态回滚 ------------ diff --git a/source/c04/c04_07.rst b/source/c04/c04_07.rst index 527849f..e59ddf6 100755 --- a/source/c04/c04_07.rst +++ b/source/c04/c04_07.rst @@ -3,27 +3,28 @@ -------------- +现在越来越多的人喜欢利用Github搭建静态网站,原因不外乎简单省钱。本人也利用hexo+github搭建了本博客,用于分享一些心得。在此过程中,折腾博客的各种配置以及功能占具了我一部分时间,在此详细记录下我是如何利用hexo+github搭建静态博客以及一些配置相关问题,以免过后遗忘,且当备份之用。 + 一、创建博客项目 ---------------- --------------- - 1.1 准备工作 ~~~~~~~~~~~~ -- 下载node.js并安装(官网下载安装),默认会安装npm。 +- 下载\ `node.js `__\ 并安装(官网下载安装),默认会安装npm。 - 下载安装git(官网下载安装) -- 下载安装hexo。方法:打开cmd 运行\ *npm install -g hexo*\ (要翻墙) +- 下载安装hexo。方法:打开终端 + 运行\ ``npm install -g hexo``\ (要翻墙) + +|image0| 1.2 初始化项目 ~~~~~~~~~~~~~~ -- 新创建一个目录,比如叫做 MyBlog -- 进入该文件夹内,右击运行 Git Bash,执行\ ``hexo init`` (初始化项目) +- 新创建一个目录,比如叫做 ``iswbm-blog`` +- 使用终端进入该文件夹内,执行\ ``hexo init`` (初始化项目) -.. code:: shell - - $ hexo init +|image1| - 运行你的博客项目(用于调试),查看生成的文章是否符合自己的预期。 @@ -43,61 +44,68 @@ 按照下面的步骤来 -- 在Github上创建名字为XXX.github.io的项目,XXX为自己的github用户名。 +- 在Github上创建名字为XXX.github.io的项目,XXX为自己的github用户名。然后 + clone 到你的本地电脑上。 -- 打开本地的MyBlog - 文件夹内的\ ``_config.yml``\ 配置文件,将其中的type设置为git,其他配置如下 + |image2| -:: +- 打开该文件夹内的\ ``_config.yml``\ 配置文件,将其中的type设置为git,其他配置如下 + +.. code:: yml # Deployment ## Docs: https://hexo.io/docs/deployment.html deploy: type: git - repository: https://github.com/BingmingWong/BingmingWong.github.io.git + repository: https://github.com/iswbm/iswbm.github.io.git branch: master - 安装Git部署插件 -:: +.. code:: shell - npm install hexo-deployer-git --save + $ npm install hexo-deployer-git --save - 重新生成静态文件,部署上线。 -:: +.. code:: shell - hexo clean - hexo g - hexo d + $ hexo clean + $ hexo g + $ hexo d -一切完成之后,你就可以通过 来访问我的博客了。 +在第一次部署后,请到 github 后台,补充你的 网站信息,否则无法访问。 + +|image3| + +一切完成之后,你就可以通过上面的网址来访问我的博客了。 1.4 绑定域名 ~~~~~~~~~~~~ 做为一个个人博客,使用 github的域名,明显不够个性化。 -我们可以自己去阿里云购买一个域名,然后将其解析到你的博客地址上。 +我们可以自己去阿里云购买一个域名(我的是 iswbm.com),然后将其 CNAME +到你的博客地址上。 -- 去阿里云 域名云解析 其中\ ``151.101.73.147``\ 是我的 - ``bingmingwong.github.io`` 的ip,可以通过ping获得 |image0| |image1| +- 去阿里云 域名云解析 |image4| -- 然后到对应的GitHub仓库,绑定我的个性域名 |image2| +- 然后到对应的GitHub仓库,绑定我的个性域名 |image5| -- 然后再本地项目\ ``blog/source/``\ 下新建\ ``CNAME``\ 文件,内容就是我的域名 +- 然后再在你本地的项目里的 ``source`` 目录下新建 + ``CNAME``\ 文件,内容就是我的域名 :: - wongbingming.me + iswbm.com - 然后重新部署,使配置生效。这样就可以使用域名访问。 - :: + .. code:: shell - hexo clean - hexo g - hexo d + $ hexo clean + $ hexo g + $ hexo d 二、丰富博客页面 ---------------- @@ -135,7 +143,7 @@ Do not just seek happiness for yourself. Seek happiness for all. Through kindness. Through mercy. {% endblockquote %} -效果如下 |image3| +效果如下 |image6| 3.2 一键生成md头格式 ~~~~~~~~~~~~~~~~~~~~ @@ -158,6 +166,11 @@ 然后使用 ``hexo new``\ 就可以一键生成新文章的头格式了,不用手动去搬运或者书写。相当方便。 +.. figure:: http://image.python-online.cn/image-20200321201555321.png + :alt: image-20200321201555321 + + image-20200321201555321 + 四、美化博客 ------------ @@ -166,13 +179,14 @@ 4.1 更换主题 ~~~~~~~~~~~~ -下载主题 +在 hexo 部署目录下,使用如下命令下载主题 :: git clone https://github.com/iissnan/hexo-theme-next themes/next -打开站点配置文件 +打开站点配置文件(部署代码根目录下的 +``_config.yml``\ )选择刚刚下载的next主题 :: @@ -181,8 +195,7 @@ ## Themes: https://hexo.io/themes/ theme: next -下面都是\ ``..\themes\next/_config.yml``\ 中设置 -选择主题样式(取消注释即可) +然后再编辑 ``themes/next/_config.yml`` 选择主题样式 :: @@ -193,14 +206,14 @@ :: - 1. 制作icon图标,最好是32x32,可以在这里制作:http://www.faviconico.org/favicon + 1. 制作icon图标,最好是32x32,可以在这里制作:https://tool.lu/favicon/ 2. 将制作的ico文件,放到next主题source/images目录下 - 3. 配置ico文件路径。配置文件在 ..\themes\next\_config.yml - - # Put your favicon.ico into `hexo-site/source/` directory. - favicon: images/favicon.ico + 3. 配置ico文件路径。配置文件在 themes/next/_config.yml + favicon: + small: /images/favicon-16x16-next.ico + medium: /images/favicon-32x32-next.ico 4.3 菜单栏和图标 ~~~~~~~~~~~~~~~~ @@ -227,7 +240,7 @@ :: social: - GitHub: https://github.com/BingmingWong || github + GitHub: https://github.com/iswbm || github E-Mail: mailto:wongbingming@163.com || envelope-o 微博: http://weibo.com/942663728 || weibo WeChat: http://image.python-online.cn/17-9-9/58657236.jpg || weixin @@ -260,9 +273,9 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) app_id: 你的id app_key: 你的key -打开\ ``/themes/next/layout/_macro/post.swig`` +打开\ ``themes/next/layout/_macro/post.swig`` 在\ ``”leancloud-visitors-count”>``\ 标签后面添加℃。 -然后打开,/themes/next/languages/zh-Hans.yml,将visitors内容改为热度即可。 +然后打开,\ ``themes/next/languages/zh-Hans.yml``\ ,将visitors内容改为热度即可。 为什么不直接用不蒜子,因为首页的时候,无法显示。 如果也开了不蒜子的计数功能的话,可以直接把下面代码删掉 @@ -276,6 +289,23 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) {% endif %} +如果你在前端看到了这个错误 + +:: + + Code 504: The app is archived, please restore in console before use. + +很好解决,前往 `LeanCloud `__ 重新激活应用即可。 + +如果在前端又出现了这个错误 + +:: + + Code 403: 访问被api域名白名单拒绝,请检查你的安全域名设置. + +也很好解决,前往 `LeanCloud `__ +绑定你的域名即可。不过要注意的是这个域名,你得备案,否则会绑定失败 + 4.7 分享插件JiaThis ~~~~~~~~~~~~~~~~~~~ @@ -307,7 +337,7 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) {% endif %} --> -4.8 Fork me on github +4.8 Fork me on Github ~~~~~~~~~~~~~~~~~~~~~ 点击\ `这里 `__\ 挑选自己喜欢的样式,并复制代码 @@ -327,7 +357,7 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) 4.10 添加版权 ~~~~~~~~~~~~~ -修改\ ``F:\MyBlog\themes\next\layout\_macro\post-copyright.swig``\ 如下 +修改\ ``themes/next/layout/_macro/post-copyright.swig``\ 如下 :: @@ -358,14 +388,14 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) :: post_copyright: - enable: True + enable: true license: CC BY-NC-SA 3.0 license_url: https://creativecommons.org/licenses/by-nc-sa/3.0/ 4.11 文章结尾页眉 ~~~~~~~~~~~~~~~~~ -在路径\ ``\themes\next\layout\_macro``\ 中新建 ``passage-end-tag.swig`` +在路径\ ``themes/next/layout/_macro``\ 中新建 ``passage-end-tag.swig`` 文件,并添加以下内容: :: @@ -376,8 +406,8 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) {% endif %}
    -接着打开\ ``\themes\next\layout\_macro\post.swig``\ 文件,添加如下下代码,注意位置 -|image4| 代码如下: +接着打开\ ``themes/next/layout/_macro/post.swig``\ 文件,添加如下下代码,注意位置 +|image7| 代码如下: :: @@ -419,7 +449,7 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) 更改NexT容器宽度可以参考这个\ `常见问题 `__ Pisces Scheme比较特殊。 -在\ ``F:\MyBlog\themes\next\source\css\_schemes\Pisces\_layout.styl``\ 最后面增加如下样式 +在\ ``themes/next/source/css/_schemes/Pisces/_layout.styl``\ 最后面增加如下样式 :: @@ -461,13 +491,13 @@ Pisces Scheme比较特殊。 :: # 设置图片不居中 - themes\next\source\css\_common\components\post\post-expand.styl + themes/next/source/css/_common/components/post/post-expand.styl .post-gallery-row .fancybox img { margin: 0 auto !important;} # 标题格式 - themes\next\source\css\_common\scaffolding\base.styl + themes/next/source/css/_common/scaffolding/base.styl h1 { font-size: 27px; @@ -484,6 +514,44 @@ Pisces Scheme比较特殊。 border-radius: 15px 15px 15px 15px !important; } +4.16 添加头像 +~~~~~~~~~~~~~ + +将你的头像放置到 ``themes/next/source/images/avatar.png`` + +在 ``themes/next/_config.yml`` 设置路径 + +.. code:: yaml + + avatar: /images/avatar.png + +4.17 设置中文 +~~~~~~~~~~~~~ + +在根目录的 ``_config.yml`` 里 把 ``language`` 改成 +``zh-Hans``\ ,这个值是要和 ``themes/next/languages`` +目录下的文件名保持一致。 + +4.18 不渲染 README +------------------ + +在根目录创建 README.md 文件,内容由你决定。 + +然后在 ``_config.yml`` 修改配置 + +.. code:: yaml + + skip_render: README.md + +4.18 页面出现 %20 问题 +~~~~~~~~~~~~~~~~~~~~~~ + +Next 主题的配置文件有一个原生 +bug,就是菜单项后面会多一个空格,这会导致你在页面访问 +about,tags等页面时,会报 404,原因是地址后面多了个空格。 + +|image8| + 五、其他实用功能 ---------------- @@ -640,6 +708,8 @@ Pisces Scheme比较特殊。 5.8 归档设置分页数量 ~~~~~~~~~~~~~~~~~~~~ +在根目录的 ``_config.yml`` + :: index_generator: @@ -656,7 +726,7 @@ Pisces Scheme比较特殊。 5.9 设置永久链接 ~~~~~~~~~~~~~~~~ -站点配置文件 +在根目录的 ``_config.yml`` :: @@ -668,14 +738,12 @@ Pisces Scheme比较特殊。 permalink: Database-MySQL-Basic_usage -一个是,如何在多台电脑都能使用Hexo更新博客 - 六、多台电脑协同更新博客 ------------------------ -------------- -这个问题很普遍,上班后,工作都会给配置了一台电脑,而自己家里也有电脑,有时候我想使用家里电脑更新博客,而有时候我也想使用公司电脑更新博客。 +上班后,工作都会给配置了一台电脑,而自己家里也有电脑,有时候我想使用家里电脑更新博客,而有时候我也想使用公司电脑更新博客。 这就很蛋疼了,因为Markdown的原文只有一份,如何将两台电脑的原文保持一致呢。当然,容易想到的是代管在Github上。 @@ -688,7 +756,7 @@ Pisces Scheme比较特殊。 在web界面新建分支,命名为\ ``hexo`` 在web界面设置 ``hexo`` 为默认分支,因为我们只会在这个分支上进行操作。 -|image5| +|image9| 6.2 本地PC操作 ~~~~~~~~~~~~~~ @@ -697,7 +765,7 @@ clone项目到本地: :: - git clone git@github.com:BingmingWong/BingmingWong.github.io.git + git clone git@github.com:iswbm/iswbm.github.io.git 使用 ``git branch`` 确认此时的分支是否为hexo,如果不是则上面设置默认分支有误,要重新设置。 @@ -717,7 +785,7 @@ clone项目到本地: git push origin hexo 将博客原始文件添加进来,但是有一些是没必要放的,具体要放哪些文件,看图片。 -|image6| +|image10| :: @@ -785,7 +853,7 @@ bash窗口,不然会提示找不到npm命令) :: - git clone git@github.com:BingmingWong/BingmingWong.github.io.git + git clone git@github.com:iswbm/iswbm.github.io.git 添加node_modules/文件夹 本来这个文件夹,不需要拷过来的,但是怕安装 ``hexo-deployer-git`` 失败,所以这里我直接备份一份,可以拷贝覆盖上即可。 @@ -826,11 +894,15 @@ bash窗口,不然会提示找不到npm命令) 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/17-9-9/50205036.jpg -.. |image1| image:: http://image.python-online.cn/17-9-9/5214564.jpg -.. |image2| image:: http://image.python-online.cn/17-9-9/56478208.jpg -.. |image3| image:: http://image.python-online.cn/17-9-10/85269241.jpg -.. |image4| image:: http://image.python-online.cn/17-9-9/63041495.jpg -.. |image5| image:: https://i.loli.net/2018/04/15/5ad3018e18cc7.png -.. |image6| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png +.. |image0| image:: http://image.python-online.cn/image-20200321163152876.png +.. |image1| image:: http://image.python-online.cn/image-20200321163746032.png +.. |image2| image:: http://image.python-online.cn/image-20200321165634287.png +.. |image3| image:: http://image.python-online.cn/image-20200321171008622.png +.. |image4| image:: http://image.python-online.cn/image-20200321171939919.png +.. |image5| image:: http://image.python-online.cn/image-20200321171821683.png +.. |image6| image:: http://image.python-online.cn/17-9-10/85269241.jpg +.. |image7| image:: http://image.python-online.cn/17-9-9/63041495.jpg +.. |image8| image:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200321210014963.png +.. |image9| image:: http://image.python-online.cn/image-20200321193444320.png +.. |image10| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 2a1c60a..9af1afd 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -190,7 +190,7 @@ ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 -Downie:网页视频下载,复制链接即可。 +Downie:网页视频下载,复制链接即可,支持优酷、YouTube 等 700+ 网站。 GoodSync:和 windows 平台同步文件 @@ -198,8 +198,34 @@ GoodSync:和 windows 平台同步文件 TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) +NTFS for Mac 助手,安装可以在 Mac 上读写 NTFS 格式的移动硬盘或U盘。 + NewFileMenu:使得可以在访达中新建文件 +ScreenFlow:视频录制 + +One +Switch,多合一功能开关合集,一键隐藏桌面、保持屏幕常亮、切换夜间模式。 + +Fantastical,可能是 Mac 上最好用的日历工具。 + +TinyCal(小历),菜单栏日历小工具,有农历和假期。Mac App Store +购买下载。 + +iMazing,最佳 iOS 备份及管理软件,完美替代 iTunes。 + +Permute 3,精致小巧的视频格式转换工具。 + +PP 鸭,好用的多格式图片压缩软件。 + +Squash,优雅而强大的图片压缩工具。领取优惠购买链接: + +GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 + +PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 + +iText,精准的 OCR 文字识别工具。 + 4.18.6 brew 的使用 ------------------ @@ -305,6 +331,44 @@ NewFileMenu:使得可以在访达中新建文件 # 关闭访达标签页,如果是最后一个标签页,则关闭访达 command + w +4.18.8 使用小鹤双拼 +------------------- + +2018 款的 MBP 系统是 10.13.6 +,这个系统支持的双拼是自然码,若想使用小鹤双拼,可以使用如下命令 + +.. code:: shell + + defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 + +同样的,还有更多的方案,都可以使用命令来修改 + +.. code:: shell + + 全拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 0 + + 智能 ABC:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 1 + + 微软双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 2 + + 紫光双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 3 + + 小鹤双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 + + 自然码:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 5 + + 拼音加加:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 6 + + 搜狗双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 7 + +练习的话,可以使用这两个网站: + +练习单字:https://api.ihint.me/shuang/ + +练习文章:https://api.ihint.me/zi/ + +对应的 github:https://github.com/BlueSky-07/Shuang + 参考文章 -------- From daa76f119059a1d770618d09388e4cc744582f7b Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 8 Apr 2020 21:20:20 +0800 Subject: [PATCH 033/147] =?UTF-8?q?Update=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ccc5bcf..8be16a1 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ - 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python.iswbm.com/en/latest/c01/c01_36.html) - 1.37 [Python 炫技操作:条件语句的七种写法](http://python.iswbm.com/en/latest/c01/c01_37.html) - 1.38 [/usr/bin/env python 有什么用?](http://python.iswbm.com/en/latest/c01/c01_38.html) +- 1.38 [Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_39.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) From cc29947fcdc923a8241dcc952e5ee3fc5b795261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 10 Apr 2020 11:28:12 +0800 Subject: [PATCH 034/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_01.md | 39 ++++++++++++++++++++--- source/c07/c07_13.md | 74 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 94 insertions(+), 19 deletions(-) diff --git a/source/c07/c07_01.md b/source/c07/c07_01.md index 4cb1d2c..e5a82cd 100644 --- a/source/c07/c07_01.md +++ b/source/c07/c07_01.md @@ -308,27 +308,46 @@ s∶取代,可以直接进行取代的工作哩!通常这个 s 的动作可 # 以上,只是输出,源文件并不会替换,若要对源文件进行修改,需加 -i sed -i '1,2d' somefile - ``` +删除指定行的上一行或下一行 + +```shell +删除指定文件的上一行 +sed -i -e :a -e '$!N;s/.*n(.*directory)/1/;ta' -e 'P;D' server.xml +删除指定文件的下一行 +sed -i '/pattern="%/{n;d}' server.xml +``` + 显示某行 + ``` sed -n '1p' somefile #显示第一行 sed -n '$p' somefile #显示最后一行 - sed -n '1,2p' somefile #显示第一行到第二行 - sed -n '2,$p' somefile #显示第二行到最后一行 + sed -n '1,2p' somefile #显示第一行到第二行 + sed -n '2,$p' somefile #显示第二行到最后一行 ``` 使用模式进行查询 ``` sed -n '/ruby/p' somefile #查询包括关键字ruby所在所有行 sed -n '/\$/p' somefile #查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含义 ``` -插入行 +插入行(a 表示 append 追加,也可以使用 i,表示 insert,如果你要在第一行插入,就不得不使用 i 了) ``` sed '1a drink tea' somefile #第一行后增加字符串"drink tea" sed '1,3a drink tea' somefile #第一行到第三行后增加字符串"drink tea" sed '1a drink tea\nor coffee' somefile #第一行后增加多行,使用换行符\n ``` +在某一行后面加入一行(示例为第四行) + +``` +sed -i 'N;4addpdf' a.txt +sed -i 'N;4ieepdf' a.txt +``` + + + 替换行 + ``` sed '1c Hi' somefile #第一行代替为Hi sed '1,2c Hi' somefile #第一行到第二行代替为Hi @@ -339,6 +358,18 @@ sed 's/ruby/bird/g somefile' #替换ruby为bird,记住这个并不会更改 sed 's/ruby//g' somefile #删除ruby ``` +在某行的前一行或后一行添加内容,其中 `\` 可省略,但为了可读性,所以我一般不省略 + +```shell +# 匹配行前加 +sed -i '/2222222222/a\3333333333' test.txt + +# 匹配行前后 +sed -i '/2222222222/i\3333333333' test.txt +``` + + + 【注意】:以上对源文件都不做修改,若要修改,要加上`-i` 替换换行符 diff --git a/source/c07/c07_13.md b/source/c07/c07_13.md index 4e7d7e2..7489a74 100644 --- a/source/c07/c07_13.md +++ b/source/c07/c07_13.md @@ -5,7 +5,7 @@ 答案就是[ Ansible](https://www.ansible.com/)。和传统的自动化工具 (如 Puppet)相比,Ansible 尤其明显的优势: - 简单,是一种高级的脚本类语言,而非标准语言。 -- 不需要安装 agent, 分为管理节点和远程被管节点通过 SSH 认证。 +- 不需要安装 agent, 分为管理节点和远程被管节点,通过 SSH 认证。 - 纳管范围广泛,不仅仅是操作系统,还包括各种虚拟化、公有云,甚至网络设备。 用过 ansible 的人都知道,ansible 一般都是通过命令行执行的:ansible、ansible-playbook、ansible-doc、ansible-galaxy、ansible-console 等。用执行命令的方式,整个过程的所有信息都会实时的打印在终端屏幕。这样的好处是,反馈实时,可以观察整个部署过程。缺点就是信息过多,难以获取有效的信息。 @@ -16,41 +16,85 @@ 本想从官方文档中查得一些资料,结果发现官网中对于这块也没有很详细的介绍。 -后来想想,ansible、ansible-playbook、ansible-doc 这些命令不也是基于 ansible api 开发出来的吗? +后来想想,ansible、ansible-playbook、ansible-doc 这些命令不也是基于 ansible 的 api 开发出来的吗? 我不是可以模仿他们,来调用 ansible api 接口。 以下通过阅读 ansible cli 的源码,总结出来的。 +## 如何调用 Playbook +我习惯性地将调用 playbook 的代码封装为一个函数。 + +```python +def playbook_task(): + try: + # 执行 playbook 的各项参数 + cli = PlaybookCLI(args=["", "you_task.yml"]) + cli.parse() + super(PlaybookCLI, cli).run() + + loader, inventory, variable_manager = cli._play_prereqs(cli.options) + CLI.get_host_list(inventory, cli.options.subset) + pbex = PlaybookExecutor(playbooks=cli.args, inventory=inventory, + variable_manager=variable_manager, + loader=loader, + options=cli.options, + passwords=None) + # 【重点】指定回调实例 + pbex._tqm._stdout_callback = ResultCallback() + exit_code = pbex.run() + + except Exception as e: + LOG.exception('Failed to run playbook:{}'.format(e)) +``` + +以上的代码相对固定,你可以直接拷贝直接用。除了两个位置你要注意一下 + +1. 第一个注释处:按需要对应添加 playbook 的参数 +2. 第二个注释处:指定你自定义的回调实例 定义回调类 ```python -class ResultCallback(CallbackBase): - def v2_runner_on_ok(self, result, **kwargs): - pass +from ansible.plugins.callback.default import CallbackModule + +class ResultCallback(CallbackModule): + def __init__(self): + self.display_failed_stderr = False + self.display_ok_hosts = True + super(ResultCallback, self).__init__() - def v2_runner_on_failed(self, result, **kwargs): + def v2_runner_on_failed(self, result, ignore_errors=False, **kwargs): + super(ResultCallback, self).v2_runner_on_failed(result, ignore_errors, **kwargs) host = result._host.get_name() - self.runner_on_failed(host, result._result, False) - task_result[host]['status'] = 'fail' - task_result[host]['reason'] = result._result.get('msg', '').encode('utf-8') + self.runner_on_failed(host, result._result, ignore_errors) + if not ignore_errors: + if "ws" not in host: + host = "ws_" + host.replace("_host", "") + + if host in task_result: + task_result[host]["detailStatus"] = "FAIL" + task_result[host]["detailMsg"] = result.task_name + ": " + result._result.get("msg", "").encode("utf-8") + else: + for host, _ in task_result.items(): + task_result[host]["detailStatus"] = "FAIL" + task_result[host]["detailMsg"] = result.task_name + ": " + result._result.get("msg", "").encode("utf-8") def v2_runner_on_unreachable(self, result): + super(ResultCallback, self).v2_runner_on_unreachable(result) host = result._host.get_name() self.runner_on_unreachable(host, result._result) - task_result[host]['status'] = 'fail' - task_result[host]['reason'] = result._result.get('msg' , 'unreachable') + if "ws" not in host: + host = "ws_" + host.replace("_host", "") + task_result[host]["detailStatus"] = "FAIL" + task_result[host]["detailMsg"] = result.task_name + ": " + result._result.get("msg", "").encode("utf-8") def v2_runner_on_skipped(self, result): + super(ResultCallback, self).v2_runner_on_skipped(result) if C.DISPLAY_SKIPPED_HOSTS: host = result._host.get_name() self.runner_on_skipped(host, self._get_item(getattr(result._result, 'results', {}))) - - def v2_playbook_on_stats(self, stats): - global PLAYBOOK_ERROR - PLAYBOOK_ERROR = len(stats.dark)+len(stats.failures) ``` 并实现其中几个关键的方法 From 40a4aa702e2a6e45bca531b445137836712b7d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 10 Apr 2020 19:35:59 +0800 Subject: [PATCH 035/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=BA=8F=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_39.md | 2 +- source/c01/c01_39.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/c01/c01_39.md b/source/c01/c01_39.md index c66081f..e4eaccc 100644 --- a/source/c01/c01_39.md +++ b/source/c01/c01_39.md @@ -1,4 +1,4 @@ -# 1.38 Python 炫技操作:合并字典的七种方法 +# 1.39 Python 炫技操作:合并字典的七种方法 Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 diff --git a/source/c01/c01_39.rst b/source/c01/c01_39.rst index f6b4b05..3037283 100644 --- a/source/c01/c01_39.rst +++ b/source/c01/c01_39.rst @@ -1,4 +1,4 @@ -1.38 Python 炫技操作:合并字典的七种方法 +1.39 Python 炫技操作:合并字典的七种方法 ======================================== Python 语言里有许多(而且是越来越多)的高级特性,是 Python From 0d0e676beec200ec6c85710a456c6ba37081e9ba Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 13 Apr 2020 08:28:42 +0800 Subject: [PATCH 036/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E4=B8=A4?= =?UTF-8?q?=E7=AF=87=E6=96=87=E7=AB=A0<=E5=8C=85=E5=90=AB=E5=AD=90?= =?UTF-8?q?=E4=B8=B2=E5=92=8C=E8=BF=9E=E6=8E=A5=E5=88=97=E8=A1=A8>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- source/c01/c01_40.md | 141 +++++++++++++++++++++++++++++++++++++++ source/c01/c01_40.rst | 151 ++++++++++++++++++++++++++++++++++++++++++ source/c01/c01_41.md | 140 +++++++++++++++++++++++++++++++++++++++ source/c01/c01_41.rst | 134 +++++++++++++++++++++++++++++++++++++ 5 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 source/c01/c01_40.md create mode 100644 source/c01/c01_40.rst create mode 100644 source/c01/c01_41.md create mode 100644 source/c01/c01_41.rst diff --git a/README.md b/README.md index 8be16a1..9b6f461 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,9 @@ - 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python.iswbm.com/en/latest/c01/c01_36.html) - 1.37 [Python 炫技操作:条件语句的七种写法](http://python.iswbm.com/en/latest/c01/c01_37.html) - 1.38 [/usr/bin/env python 有什么用?](http://python.iswbm.com/en/latest/c01/c01_38.html) -- 1.38 [Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_39.html) +- 1.39 [Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_39.html) +- 1.40 [Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_40.html) +- 1.41 [Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_41.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) diff --git a/source/c01/c01_40.md b/source/c01/c01_40.md new file mode 100644 index 0000000..a70130c --- /dev/null +++ b/source/c01/c01_40.md @@ -0,0 +1,141 @@ +# 1.40 Python 炫技操作:判断是否包含子串的七种方法 + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: + +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 + +## 1. 使用 in 和 not in + +`in` 和 `not in` 在 Python 中是很常用的关键字,我们将它们归类为 `成员运算符`。 + +使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下: + +```python +>>> "llo" in "hello, python" +True +>>> +>>> "lol" in "hello, python" +False +``` + + + +## 2. 使用 find 方法 + +使用 字符串 对象的 find 方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回 `-1` + +```python +>>> "hello, python".find("llo") != -1 +True +>>> "hello, python".find("lol") != -1 +False +>> +``` + + + +## 3. 使用 index 方法 + +字符串对象有一个 index 方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。 + +```python +def is_in(full_str, sub_str): + try: + full_str.index(sub_str) + return True + except ValueError: + return False + +print(is_in("hello, python", "llo")) # True +print(is_in("hello, python", "lol")) # False +``` + + + +## 4. 使用 count 方法 + +利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。 + +只要判断结果大于 0 就说明子串存在于字符串中。 + +```python +def is_in(full_str, sub_str): + return full_str.count(sub_str) > 0 + +print(is_in("hello, python", "llo")) # True +print(is_in("hello, python", "lol")) # False +``` + + + +## 5. 通过魔法方法 + +在第一种方法中,我们使用 in 和 not in 判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in 时,Python 解释器会先去检查该对象是否有 `__contains__` 魔法方法。 + +若有就执行它,若没有,Python 就自动会迭代整个序列,只要找到了需要的一项就返回 True 。 + +示例如下; + +```python +>>> "hello, python".__contains__("llo") +True +>>> +>>> "hello, python".__contains__("lol") +False +>>> +``` + +这个用法与使用 in 和 not in 没有区别,但不排除有人会特意写成这样来增加代码的理解难度。 + +## 6. 借助 operator + +operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 python 代码快。 + +在 operator 中有一个方法 `contains` 可以很方便地判断子串是否在字符串中。 + +```python +>>> import operator +>>> +>>> operator.contains("hello, python", "llo") +True +>>> operator.contains("hello, python", "lol") +False +>>> +``` + + + +## 7. 使用正则匹配 + +说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。 + +对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。 + +```python +import re + +def is_in(full_str, sub_str): + if re.findall(sub_str, full_str): + return True + else: + return False + +print(is_in("hello, python", "llo")) # True +print(is_in("hello, python", "lol")) # False +``` + + + +--- + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_40.rst b/source/c01/c01_40.rst new file mode 100644 index 0000000..b662c06 --- /dev/null +++ b/source/c01/c01_40.rst @@ -0,0 +1,151 @@ +1.40 Python 炫技操作:判断是否包含子串的七种方法 +================================================ + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python +发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: + +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +该篇是「\ **炫技系列**\ 」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 +Python +发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 + +1. 使用 in 和 not in +-------------------- + +``in`` 和 ``not in`` 在 Python 中是很常用的关键字,我们将它们归类为 +``成员运算符``\ 。 + +使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下: + +.. code:: python + + >>> "llo" in "hello, python" + True + >>> + >>> "lol" in "hello, python" + False + +2. 使用 find 方法 +----------------- + +使用 字符串 对象的 find +方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回 +``-1`` + +.. code:: python + + >>> "hello, python".find("llo") != -1 + True + >>> "hello, python".find("lol") != -1 + False + >> + +3. 使用 index 方法 +------------------ + +字符串对象有一个 index +方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。 + +.. code:: python + + def is_in(full_str, sub_str): + try: + full_str.index(sub_str) + return True + except ValueError: + return False + + print(is_in("hello, python", "llo")) # True + print(is_in("hello, python", "lol")) # False + +4. 使用 count 方法 +------------------ + +利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。 + +只要判断结果大于 0 就说明子串存在于字符串中。 + +.. code:: python + + def is_in(full_str, sub_str): + return full_str.count(sub_str) > 0 + + print(is_in("hello, python", "llo")) # True + print(is_in("hello, python", "lol")) # False + +5. 通过魔法方法 +--------------- + +在第一种方法中,我们使用 in 和 not in +判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in +时,Python 解释器会先去检查该对象是否有 ``__contains__`` 魔法方法。 + +若有就执行它,若没有,Python +就自动会迭代整个序列,只要找到了需要的一项就返回 True 。 + +示例如下; + +.. code:: python + + >>> "hello, python".__contains__("llo") + True + >>> + >>> "hello, python".__contains__("lol") + False + >>> + +这个用法与使用 in 和 not in +没有区别,但不排除有人会特意写成这样来增加代码的理解难度。 + +6. 借助 operator +---------------- + +operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 +python 代码快。 + +在 operator 中有一个方法 ``contains`` +可以很方便地判断子串是否在字符串中。 + +.. code:: python + + >>> import operator + >>> + >>> operator.contains("hello, python", "llo") + True + >>> operator.contains("hello, python", "lol") + False + >>> + +7. 使用正则匹配 +--------------- + +说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。 + +对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。 + +.. code:: python + + import re + + def is_in(full_str, sub_str): + if re.findall(sub_str, full_str): + return True + else: + return False + + print(is_in("hello, python", "llo")) # True + print(is_in("hello, python", "lol")) # False + +-------------- + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c01/c01_41.md b/source/c01/c01_41.md new file mode 100644 index 0000000..5baf357 --- /dev/null +++ b/source/c01/c01_41.md @@ -0,0 +1,140 @@ +# 1.41 Python 炫技操作:连接列表的八种方法 + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: + +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 + + + +## 1. 最直观的相加 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> list01 + list02 + list03 +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +## 2. 借助 itertools + +```python +>>> from functools import chain +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> list(chain(list01, list02)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + +## 3. 使用 * 解包 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> [*list01, *list02] +[1, 2, 3, 4, 5, 6] +>>> +``` + + + +## 4. 使用 extend + +```python +>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> list01.extend(list02) +>>> list01 +[1, 2, 3, 4, 5, 6] +``` + +## 5. 使用列表推导式 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> [x for l in (list01, list02, list03) for x in l] +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +## 6. 使用 heapq + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> from heapq import merge +>>> +>>> list(merge(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +## 7. 借助魔法方法 + + + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> from functools import reduce +>>> reduce(list.__add__, (list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +## 8. 使用 yield from + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> def merge(*lists): +... for l in lists: +... yield from l +... +>>> list(merge(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +--- + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) + + + diff --git a/source/c01/c01_41.rst b/source/c01/c01_41.rst new file mode 100644 index 0000000..a2ccb92 --- /dev/null +++ b/source/c01/c01_41.rst @@ -0,0 +1,134 @@ +1.41 Python 炫技操作:连接列表的八种方法 +======================================== + +Python 语言里有许多(而且是越来越多)的高级特性,是 Python +发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: + +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +该篇是「\ **炫技系列**\ 」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 +Python +发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 + +1. 最直观的相加 +--------------- + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> list01 + list02 + list03 + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +2. 借助 itertools +----------------- + +.. code:: python + + >>> from functools import chain + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> list(chain(list01, list02)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +3. 使用 \* 解包 +--------------- + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> [*list01, *list02] + [1, 2, 3, 4, 5, 6] + >>> + +4. 使用 extend +-------------- + +.. code:: python + + >> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> list01.extend(list02) + >>> list01 + [1, 2, 3, 4, 5, 6] + +5. 使用列表推导式 +----------------- + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> [x for l in (list01, list02, list03) for x in l] + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +6. 使用 heapq +------------- + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> from heapq import merge + >>> + >>> list(merge(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +7. 借助魔法方法 +--------------- + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> from functools import reduce + >>> reduce(list.__add__, (list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +8. 使用 yield from +------------------ + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> def merge(*lists): + ... for l in lists: + ... yield from l + ... + >>> list(merge(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +-------------- + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! From c852e8bd7488c0ae088140d0b31fa92974bffc63 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 13 Apr 2020 08:29:09 +0800 Subject: [PATCH 037/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_18.md | 6 ++++ source/c04/c04_18.rst | 6 ++++ source/c07/c07_01.rst | 35 +++++++++++++++++--- source/c07/c07_13.rst | 77 ++++++++++++++++++++++++++++++++++--------- 4 files changed, 105 insertions(+), 19 deletions(-) diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 50a230f..8bce532 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -42,6 +42,12 @@ f11 # 在外接键盘下,请先设置 F1,F2 这些键为标准功能键, # 最大化窗口与取消 command + ctrl + f +# 最小化/隐藏窗口 +command + h + +# 隐藏除当前窗口外的其他所有窗口 +command + opthon + h + # 关闭除访达外的其他程序 command + q diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 9af1afd..fb4cd85 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -48,6 +48,12 @@ # 最大化窗口与取消 command + ctrl + f + # 最小化/隐藏窗口 + command + h + + # 隐藏除当前窗口外的其他所有窗口 + command + opthon + h + # 关闭除访达外的其他程序 command + q diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index f1683a8..0e49392 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -339,7 +339,15 @@ more、cat file \| head等。 # 以上,只是输出,源文件并不会替换,若要对源文件进行修改,需加 -i sed -i '1,2d' somefile - + +删除指定行的上一行或下一行 + +.. code:: shell + + 删除指定文件的上一行 + sed -i -e :a -e '$!N;s/.*n(.*directory)/1/;ta' -e 'P;D' server.xml + 删除指定文件的下一行 + sed -i '/pattern="%/{n;d}' server.xml 显示某行 @@ -347,8 +355,8 @@ more、cat file \| head等。 sed -n '1p' somefile #显示第一行 sed -n '$p' somefile #显示最后一行 - sed -n '1,2p' somefile #显示第一行到第二行 - sed -n '2,$p' somefile #显示第二行到最后一行 + sed -n '1,2p' somefile #显示第一行到第二行 + sed -n '2,$p' somefile #显示第二行到最后一行 使用模式进行查询 @@ -357,7 +365,8 @@ more、cat file \| head等。 sed -n '/ruby/p' somefile #查询包括关键字ruby所在所有行 sed -n '/\$/p' somefile #查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含义 -插入行 +插入行(a 表示 append 追加,也可以使用 i,表示 +insert,如果你要在第一行插入,就不得不使用 i 了) :: @@ -365,6 +374,13 @@ more、cat file \| head等。 sed '1,3a drink tea' somefile #第一行到第三行后增加字符串"drink tea" sed '1a drink tea\nor coffee' somefile #第一行后增加多行,使用换行符\n +在某一行后面加入一行(示例为第四行) + +:: + + sed -i 'N;4addpdf' a.txt + sed -i 'N;4ieepdf' a.txt + 替换行 :: @@ -379,6 +395,17 @@ more、cat file \| head等。 sed 's/ruby/bird/g somefile' #替换ruby为bird,记住这个并不会更改源文件,只是输出 sed 's/ruby//g' somefile #删除ruby +在某行的前一行或后一行添加内容,其中 ``\`` +可省略,但为了可读性,所以我一般不省略 + +.. code:: shell + + # 匹配行前加 + sed -i '/2222222222/a\3333333333' test.txt + + # 匹配行前后 + sed -i '/2222222222/i\3333333333' test.txt + 【注意】:以上对源文件都不做修改,若要修改,要加上\ ``-i`` 替换换行符 diff --git a/source/c07/c07_13.rst b/source/c07/c07_13.rst index a780eef..ff46eb3 100644 --- a/source/c07/c07_13.rst +++ b/source/c07/c07_13.rst @@ -8,7 +8,7 @@ (如 Puppet)相比,Ansible 尤其明显的优势: - 简单,是一种高级的脚本类语言,而非标准语言。 -- 不需要安装 agent, 分为管理节点和远程被管节点通过 SSH 认证。 +- 不需要安装 agent, 分为管理节点和远程被管节点,通过 SSH 认证。 - 纳管范围广泛,不仅仅是操作系统,还包括各种虚拟化、公有云,甚至网络设备。 用过 ansible 的人都知道,ansible @@ -25,41 +25,88 @@ 本想从官方文档中查得一些资料,结果发现官网中对于这块也没有很详细的介绍。 后来想想,ansible、ansible-playbook、ansible-doc 这些命令不也是基于 -ansible api 开发出来的吗? +ansible 的 api 开发出来的吗? 我不是可以模仿他们,来调用 ansible api 接口。 以下通过阅读 ansible cli 的源码,总结出来的。 +如何调用 Playbook +----------------- + +我习惯性地将调用 playbook 的代码封装为一个函数。 + +.. code:: python + + def playbook_task(): + try: + # 执行 playbook 的各项参数 + cli = PlaybookCLI(args=["", "you_task.yml"]) + cli.parse() + super(PlaybookCLI, cli).run() + + loader, inventory, variable_manager = cli._play_prereqs(cli.options) + CLI.get_host_list(inventory, cli.options.subset) + pbex = PlaybookExecutor(playbooks=cli.args, inventory=inventory, + variable_manager=variable_manager, + loader=loader, + options=cli.options, + passwords=None) + # 【重点】指定回调实例 + pbex._tqm._stdout_callback = ResultCallback() + exit_code = pbex.run() + + except Exception as e: + LOG.exception('Failed to run playbook:{}'.format(e)) + +以上的代码相对固定,你可以直接拷贝直接用。除了两个位置你要注意一下 + +1. 第一个注释处:按需要对应添加 playbook 的参数 +2. 第二个注释处:指定你自定义的回调实例 + 定义回调类 .. code:: python - class ResultCallback(CallbackBase): - def v2_runner_on_ok(self, result, **kwargs): - pass + from ansible.plugins.callback.default import CallbackModule - def v2_runner_on_failed(self, result, **kwargs): + class ResultCallback(CallbackModule): + def __init__(self): + self.display_failed_stderr = False + self.display_ok_hosts = True + super(ResultCallback, self).__init__() + + def v2_runner_on_failed(self, result, ignore_errors=False, **kwargs): + super(ResultCallback, self).v2_runner_on_failed(result, ignore_errors, **kwargs) host = result._host.get_name() - self.runner_on_failed(host, result._result, False) - task_result[host]['status'] = 'fail' - task_result[host]['reason'] = result._result.get('msg', '').encode('utf-8') + self.runner_on_failed(host, result._result, ignore_errors) + if not ignore_errors: + if "ws" not in host: + host = "ws_" + host.replace("_host", "") + + if host in task_result: + task_result[host]["detailStatus"] = "FAIL" + task_result[host]["detailMsg"] = result.task_name + ": " + result._result.get("msg", "").encode("utf-8") + else: + for host, _ in task_result.items(): + task_result[host]["detailStatus"] = "FAIL" + task_result[host]["detailMsg"] = result.task_name + ": " + result._result.get("msg", "").encode("utf-8") def v2_runner_on_unreachable(self, result): + super(ResultCallback, self).v2_runner_on_unreachable(result) host = result._host.get_name() self.runner_on_unreachable(host, result._result) - task_result[host]['status'] = 'fail' - task_result[host]['reason'] = result._result.get('msg' , 'unreachable') + if "ws" not in host: + host = "ws_" + host.replace("_host", "") + task_result[host]["detailStatus"] = "FAIL" + task_result[host]["detailMsg"] = result.task_name + ": " + result._result.get("msg", "").encode("utf-8") def v2_runner_on_skipped(self, result): + super(ResultCallback, self).v2_runner_on_skipped(result) if C.DISPLAY_SKIPPED_HOSTS: host = result._host.get_name() self.runner_on_skipped(host, self._get_item(getattr(result._result, 'results', {}))) - def v2_playbook_on_stats(self, stats): - global PLAYBOOK_ERROR - PLAYBOOK_ERROR = len(stats.dark)+len(stats.failures) - 并实现其中几个关键的方法 - v2_runner_on_ok:单个部署任务成功时,会调用 From 64261b64e3f4f8520a0edb8d44a05d8ae7de765b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 14 Apr 2020 21:00:01 +0800 Subject: [PATCH 038/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=86=B7=E7=9F=A5=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_10.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index 970ebb7..29de30d 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -1187,6 +1187,26 @@ linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ +## 39. argument 和 parameter 的区别 + +arguments 和 parameter 的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。 + +但若要严格再进行区分,它们实际上还有各自的叫法 + +- parameter:形参(**formal parameter**),体现在函数内部,作用域是这个函数体。 +- argument :实参(**actual parameter**),调用函数实际传递的参数。 + +举个例子,如下这段代码,`"error"` 为 argument,而 msg 为 `parameter`。 + +```python +def output_msg(msg): + print(msg) + +output_msg("error") +``` + + + ## 附录:参考文章 From 8d73f2bd9a4db70256f95c7b5ad725af6ab7e90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 14 Apr 2020 21:32:53 +0800 Subject: [PATCH 039/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_41.md | 65 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/source/c01/c01_41.md b/source/c01/c01_41.md index 5baf357..6c81160 100644 --- a/source/c01/c01_41.md +++ b/source/c01/c01_41.md @@ -12,10 +12,10 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python 该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - - ## 1. 最直观的相加 +使用 `+` 对多个列表进行相加,你应该懂,不多说了。 + ```python >>> list01 = [1,2,3] >>> list02 = [4,5,6] @@ -30,6 +30,12 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python ## 2. 借助 itertools +itertools 在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +在前面的文章中也介绍过,使用 `itertools.chain()` 函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 + +最后你再利用 list 将其转化为 列表。 + ```python >>> from functools import chain >>> list01 = [1,2,3] @@ -43,6 +49,8 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python ## 3. 使用 * 解包 +在【】这一节提到,使用 `**` 可解包字典,而使用 `*` 可以解包列表。 + ```python >>> list01 = [1,2,3] >>> list02 = [4,5,6] @@ -56,6 +64,8 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python ## 4. 使用 extend +在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。 + ```python >> list01 = [1,2,3] >>> list02 = [4,5,6] @@ -67,6 +77,12 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python ## 5. 使用列表推导式 +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? + +当然可以,具体示例代码如下: + ```python >>> list01 = [1,2,3] >>> list02 = [4,5,6] @@ -81,6 +97,8 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python ## 6. 使用 heapq + + ```python >>> list01 = [1,2,3] >>> list02 = [4,5,6] @@ -93,11 +111,48 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python >>> ``` +要注意的是,heapq.merge 除了合并多个列表外,它还会将合并后的最终的列表进行排序。 + +```python +>>> list01 = [2,5,3] +>>> list02 = [1,4,6] +>>> list03 = [7,9,8] +>>> +>>> from heapq import merge +>>> +>>> list(merge(list01, list02, list03)) +[1, 2, 4, 5, 3, 6, 7, 9, 8] +>>> +``` + +它的效果等价于下面这行代码: + +```python +sorted(itertools.chain(*iterables)) +``` +如果你希望得到一个始终有序的列表,那请第一时间想到 heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 ## 7. 借助魔法方法 +在 【】这一节里,把魔法方法介绍得很全,其中有一个魔法方法是 `__add__`,实际 上当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的. + +所以以下两种方法其实是等价的 + +``` +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> list01 + list02 +[1, 2, 3, 4, 5, 6] +>>> +>>> +>>> list01.__add__(list02) +[1, 2, 3, 4, 5, 6] +>>> +``` +借用这个魔法特性,我们可以 reduce 这个方法来对多个列表进行合并,示例代码如下 ```python >>> list01 = [1,2,3] @@ -114,6 +169,12 @@ Python 语言里有许多(而且是越来越多)的高级特性,是 Python ## 8. 使用 yield from +在【】这一节里,我很详细的介绍了 yield from 意义及使用方法。 + +在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 + +因此,我们可以像下面这样自定义一个合并列表的工具函数。 + ```python >>> list01 = [1,2,3] >>> list02 = [4,5,6] From e0ebed052f81a9885360d023716e17931d9f25b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Wed, 15 Apr 2020 20:17:37 +0800 Subject: [PATCH 040/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_41.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/source/c01/c01_41.md b/source/c01/c01_41.md index 6c81160..1b95514 100644 --- a/source/c01/c01_41.md +++ b/source/c01/c01_41.md @@ -49,7 +49,13 @@ itertools 在 Python 里有一个非常强大的内置模块,它专门用于 ## 3. 使用 * 解包 -在【】这一节提到,使用 `**` 可解包字典,而使用 `*` 可以解包列表。 +在 [Python 炫技操作(02):合并字典的七种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&token=688334198&lang=zh_CN#rd) 提到了使用 `**` 可解包字典。 + +与它相似的,使用 `*` 可以解包列表。 `*` 和 `**` 常用在函数定义时,设置可变参数。 + +现在我将它单独拿出来用在多个列表的合并。 + +示例如下: ```python >>> list01 = [1,2,3] @@ -67,7 +73,7 @@ itertools 在 Python 里有一个非常强大的内置模块,它专门用于 在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。 ```python ->> list01 = [1,2,3] +>>> list01 = [1,2,3] >>> list02 = [4,5,6] >>> >>> list01.extend(list02) @@ -97,7 +103,9 @@ Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的 ## 6. 使用 heapq +heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 +该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 ```python >>> list01 = [1,2,3] @@ -135,7 +143,13 @@ sorted(itertools.chain(*iterables)) ## 7. 借助魔法方法 -在 【】这一节里,把魔法方法介绍得很全,其中有一个魔法方法是 `__add__`,实际 上当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的. +在之前的文章里,把魔法方法介绍得很全。 + +[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd) + +[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd) + +其中有一个魔法方法是 `__add__`,实际 上当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的. 所以以下两种方法其实是等价的 @@ -169,7 +183,7 @@ sorted(itertools.chain(*iterables)) ## 8. 使用 yield from -在【】这一节里,我很详细的介绍了 yield from 意义及使用方法。 +在很早的一篇文章里([并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect)),我很详细的介绍了 yield from 意义及使用方法。 在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 @@ -198,4 +212,3 @@ sorted(itertools.chain(*iterables)) ![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) - From 99d87b985facc7a8ad118f2824d684b5ae9695fc Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 15 Apr 2020 20:45:37 +0800 Subject: [PATCH 041/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_10.rst | 22 ++++++++++ source/c01/c01_41.rst | 98 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 80cbd7f..c7ebb50 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1303,6 +1303,28 @@ Tips `__ 如果你手动安装python,它会直接使用目录site-packages。 linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ +39. argument 和 parameter 的区别 +-------------------------------- + +arguments 和 parameter +的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。 + +但若要严格再进行区分,它们实际上还有各自的叫法 + +- parameter:形参(\ **formal + parameter**\ ),体现在函数内部,作用域是这个函数体。 +- argument :实参(\ **actual parameter**\ ),调用函数实际传递的参数。 + +举个例子,如下这段代码,\ ``"error"`` 为 argument,而 msg 为 +``parameter``\ 。 + +.. code:: python + + def output_msg(msg): + print(msg) + + output_msg("error") + 附录:参考文章 -------------- diff --git a/source/c01/c01_41.rst b/source/c01/c01_41.rst index a2ccb92..2614191 100644 --- a/source/c01/c01_41.rst +++ b/source/c01/c01_41.rst @@ -19,6 +19,8 @@ Python 1. 最直观的相加 --------------- +使用 ``+`` 对多个列表进行相加,你应该懂,不多说了。 + .. code:: python >>> list01 = [1,2,3] @@ -32,6 +34,14 @@ Python 2. 借助 itertools ----------------- +itertools 在 Python +里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +在前面的文章中也介绍过,使用 ``itertools.chain()`` +函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 + +最后你再利用 list 将其转化为 列表。 + .. code:: python >>> from functools import chain @@ -46,6 +56,17 @@ Python 3. 使用 \* 解包 --------------- +在 `Python +炫技操作(02):合并字典的七种方法 `__ +提到了使用 ``**`` 可解包字典。 + +与它相似的,使用 ``*`` 可以解包列表。 ``*`` 和 ``**`` +常用在函数定义时,设置可变参数。 + +现在我将它单独拿出来用在多个列表的合并。 + +示例如下: + .. code:: python >>> list01 = [1,2,3] @@ -58,9 +79,12 @@ Python 4. 使用 extend -------------- +在字典中,使用 update 可实现原地更新,而在列表中,使用 extend +可实现列表的自我扩展。 + .. code:: python - >> list01 = [1,2,3] + >>> list01 = [1,2,3] >>> list02 = [4,5,6] >>> >>> list01.extend(list02) @@ -70,6 +94,13 @@ Python 5. 使用列表推导式 ----------------- +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python +发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? + +当然可以,具体示例代码如下: + .. code:: python >>> list01 = [1,2,3] @@ -83,6 +114,10 @@ Python 6. 使用 heapq ------------- +heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 + +该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 + .. code:: python >>> list01 = [1,2,3] @@ -95,9 +130,62 @@ Python [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> +要注意的是,heapq.merge +除了合并多个列表外,它还会将合并后的最终的列表进行排序。 + +.. code:: python + + >>> list01 = [2,5,3] + >>> list02 = [1,4,6] + >>> list03 = [7,9,8] + >>> + >>> from heapq import merge + >>> + >>> list(merge(list01, list02, list03)) + [1, 2, 4, 5, 3, 6, 7, 9, 8] + >>> + +它的效果等价于下面这行代码: + +.. code:: python + + sorted(itertools.chain(*iterables)) + +如果你希望得到一个始终有序的列表,那请第一时间想到 +heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 + 7. 借助魔法方法 --------------- +在之前的文章里,把魔法方法介绍得很全。 + +`非常全的通俗易懂 Python +魔法方法指南(上) `__ + +`非常全的通俗易懂 Python +魔法方法指南(下) `__ + +其中有一个魔法方法是 ``__add__``\ ,实际 上当我们使用第一种方法 list01 + +list02 的时候,内部实际上是作用在 ``__add__`` 这个魔法方法上的. + +所以以下两种方法其实是等价的 + +:: + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> list01 + list02 + [1, 2, 3, 4, 5, 6] + >>> + >>> + >>> list01.__add__(list02) + [1, 2, 3, 4, 5, 6] + >>> + +借用这个魔法特性,我们可以 reduce +这个方法来对多个列表进行合并,示例代码如下 + .. code:: python >>> list01 = [1,2,3] @@ -112,6 +200,14 @@ Python 8. 使用 yield from ------------------ +在很早的一篇文章里(\ `并发编程08|深入理解yield +from语法 `__\ ),我很详细的介绍了 +yield from 意义及使用方法。 + +在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 + +因此,我们可以像下面这样自定义一个合并列表的工具函数。 + .. code:: python >>> list01 = [1,2,3] From 30c2d0ba9268a526aa5af47776b4f0fe053b9529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 16 Apr 2020 10:33:34 +0800 Subject: [PATCH 042/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=AD=A3?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_41.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/c01/c01_41.md b/source/c01/c01_41.md index 1b95514..2135fd0 100644 --- a/source/c01/c01_41.md +++ b/source/c01/c01_41.md @@ -42,7 +42,7 @@ itertools 在 Python 里有一个非常强大的内置模块,它专门用于 >>> list02 = [4,5,6] >>> list03 = [7,8,9] >>> ->>> list(chain(list01, list02)) +>>> list(chain(list01, list02, list03)) [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> ``` From a6ffca1679271d88d57e2e133802cd584cfd2eb3 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 18 Apr 2020 14:03:14 +0800 Subject: [PATCH 043/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=AD=A3?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_41.md | 2 +- source/c01/c01_41.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/c01/c01_41.md b/source/c01/c01_41.md index 2135fd0..211fb5c 100644 --- a/source/c01/c01_41.md +++ b/source/c01/c01_41.md @@ -37,7 +37,7 @@ itertools 在 Python 里有一个非常强大的内置模块,它专门用于 最后你再利用 list 将其转化为 列表。 ```python ->>> from functools import chain +>>> from itertools import chain >>> list01 = [1,2,3] >>> list02 = [4,5,6] >>> list03 = [7,8,9] diff --git a/source/c01/c01_41.rst b/source/c01/c01_41.rst index 2614191..03af012 100644 --- a/source/c01/c01_41.rst +++ b/source/c01/c01_41.rst @@ -44,12 +44,12 @@ itertools 在 Python .. code:: python - >>> from functools import chain + >>> from itertools import chain >>> list01 = [1,2,3] >>> list02 = [4,5,6] >>> list03 = [7,8,9] >>> - >>> list(chain(list01, list02)) + >>> list(chain(list01, list02, list03)) [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> From 89eae014d0669b90897f0c2b927aaa6ac70fa666 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 18 Apr 2020 14:03:45 +0800 Subject: [PATCH 044/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E7=82=AB?= =?UTF-8?q?=E6=8A=80=E6=93=8D=E4=BD=9C=E6=96=87=E7=AB=A0<=E6=B5=B7?= =?UTF-8?q?=E8=B1=A1=E8=BF=90=E7=AE=97=E7=AC=A6>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + source/c01/c01_42.md | 173 +++++++++++++++++++++++++++++++++++++++ source/c01/c01_42.rst | 182 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 source/c01/c01_42.md create mode 100644 source/c01/c01_42.rst diff --git a/README.md b/README.md index 9b6f461..0cccd95 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ - 1.39 [Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_39.html) - 1.40 [Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_40.html) - 1.41 [Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_41.html) +- 1.42 [Python 炫技操作:海象运算符的三种用法](http://python.iswbm.com/en/latest/c01/c01_42.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) diff --git a/source/c01/c01_42.md b/source/c01/c01_42.md new file mode 100644 index 0000000..fbde99b --- /dev/null +++ b/source/c01/c01_42.md @@ -0,0 +1,173 @@ +# 1.42 Python 炫技操作:海象运算符的三种用法 + +Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 + +很多 Python 3.8 的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 + +海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 + +它的英文原名叫 `Assignment Expressions`,翻译过来也就是 `赋值表达式`,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 + +![](http://image.iswbm.com/image-20200418122739417.png) + +## 1. 第一个用法:if/else + +可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? + +在 Golang 中的条件语句可以直接在 if 中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 + +```go +import "fmt" + +func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } +} +``` + +若在 Python 3.8 之前,Python 必须得这样子写 + +```python +age = 20 +if age > 18: + print("已经成年了") +``` + +但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 Golang,那这里要注意,Golang 中的 `:=` 叫短变量声明,意思是声明并初始化,它和 Python 中的 `:=` 不是一个概念) + +```python +if (age:= 20) > 18: + print("已经成年了") +``` + + + +## 2. 第二个用法:while + +在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 + +```python +file = open("demo.txt", "r") +while True: + line = file.readline() + if not line: + break + print(line.strip()) +``` + +但有了海象运算符之后,你可以这样 + +```python +file = open("demo.txt", "r") +while (line := file.readline()): + print(line.strip()) +``` + +使用它替换以往的无限 while 循环写法更为惊艳 + +比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 + +```python +while True: + p = input("Enter the password: ") + if p == "youpassword": + break +``` + +有了海象运算符之后,这样子写更为舒服 + +```python +while (p := input("Enter the password: ")) != "youpassword": + continue +``` + + + +## 3. 第三个用法:推导式 + +这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 + +在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 + +如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 + +```python +members = [ + {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, + {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, + {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, +] + +count = 0 + +def get_bmi(info): + global count + count += 1 + + print(f"执行了 {count} 次") + + height = info["height"] + weight = info["weight"] + + return weight / (height**2) + +# 查出所有会员中过于肥胖的人的 bmi 指数 +fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] + +print(fat_bmis) +``` + +输出如下 + +``` +执行了 1 次 +执行了 2 次 +执行了 3 次 +执行了 4 次 +[25.88057063502083] +``` + +可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 + +如果所有会员都是过于肥胖的,那最终将执行 6 次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for 循环 + if 判断。 + +```python +fat_bmis = [] + +# 查出所有会员中过于肥胖的人的 bmi 指数 +for m in members: + bmi = get_bmi(m) + if bmi > 24: + fat_bmis.append(bmi) +``` + +在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 + +```python +# 查出所有会员中过于肥胖的人的 bmi 指数 +fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] +``` + +最终从输出结果可以看出,只执行了 3 次 + +``` +执行了 1 次 +执行了 2 次 +执行了 3 次 +[25.88057063502083] +``` + +这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 + + + +海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 + + + +--- + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_42.rst b/source/c01/c01_42.rst new file mode 100644 index 0000000..a92b178 --- /dev/null +++ b/source/c01/c01_42.rst @@ -0,0 +1,182 @@ +1.42 Python 炫技操作:海象运算符的三种用法 +========================================== + +Python 版本发展非常快,如今最新的版本已经是 Pyhton +3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 + +很多 Python 3.8 +的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 + +海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 + +它的英文原名叫 ``Assignment Expressions``\ ,翻译过来也就是 +``赋值表达式``\ ,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 + +|image0| + +1. 第一个用法:if/else +---------------------- + +可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? + +在 Golang 中的条件语句可以直接在 if +中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 + +.. code:: go + + import "fmt" + + func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } + } + +若在 Python 3.8 之前,Python 必须得这样子写 + +.. code:: python + + age = 20 + if age > 18: + print("已经成年了") + +但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 +Golang,那这里要注意,Golang 中的 ``:=`` +叫短变量声明,意思是声明并初始化,它和 Python 中的 ``:=`` 不是一个概念) + +.. code:: python + + if (age:= 20) > 18: + print("已经成年了") + +2. 第二个用法:while +-------------------- + +在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 + +.. code:: python + + file = open("demo.txt", "r") + while True: + line = file.readline() + if not line: + break + print(line.strip()) + +但有了海象运算符之后,你可以这样 + +.. code:: python + + file = open("demo.txt", "r") + while (line := file.readline()): + print(line.strip()) + +使用它替换以往的无限 while 循环写法更为惊艳 + +比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 + +.. code:: python + + while True: + p = input("Enter the password: ") + if p == "youpassword": + break + +有了海象运算符之后,这样子写更为舒服 + +.. code:: python + + while (p := input("Enter the password: ")) != "youpassword": + continue + +3. 第三个用法:推导式 +--------------------- + +这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 + +在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 + +如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 + +.. code:: python + + members = [ + {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, + {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, + {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, + ] + + count = 0 + + def get_bmi(info): + global count + count += 1 + + print(f"执行了 {count} 次") + + height = info["height"] + weight = info["weight"] + + return weight / (height**2) + + # 查出所有会员中过于肥胖的人的 bmi 指数 + fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] + + print(fat_bmis) + +输出如下 + +:: + + 执行了 1 次 + 执行了 2 次 + 执行了 3 次 + 执行了 4 次 + [25.88057063502083] + +可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 +次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 + +如果所有会员都是过于肥胖的,那最终将执行 6 +次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for +循环 + if 判断。 + +.. code:: python + + fat_bmis = [] + + # 查出所有会员中过于肥胖的人的 bmi 指数 + for m in members: + bmi = get_bmi(m) + if bmi > 24: + fat_bmis.append(bmi) + +在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 + +.. code:: python + + # 查出所有会员中过于肥胖的人的 bmi 指数 + fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] + +最终从输出结果可以看出,只执行了 3 次 + +:: + + 执行了 1 次 + 执行了 2 次 + 执行了 3 次 + [25.88057063502083] + +这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 + +海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 + +-------------- + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/image-20200418122739417.png + From 4a915bcc8e1a048991b28cbb02b8d17ebb5dfbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 23 Apr 2020 20:07:20 +0800 Subject: [PATCH 045/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0(=20Python=E7=BC=96=E7=A0=81=E9=97=AE=E9=A2=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_44.md | 260 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 source/c01/c01_44.md diff --git a/source/c01/c01_44.md b/source/c01/c01_44.md new file mode 100644 index 0000000..2e096bd --- /dev/null +++ b/source/c01/c01_44.md @@ -0,0 +1,260 @@ +# 1.44 详解 Python 中的编码问题 + +Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 Python 开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 + +一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式? + +反反复复,这个过程真是太痛苦了。 + +今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再 Google,收藏这篇文章就行。 + + + +## 1. Python 3 中 str 与 bytes + +在 Python3中,字符串有两种类型 ,str 和 bytes。 + +今天就来说一说这二者的区别: + +- `unicode string(str 类型)`:以 Unicode code points 形式存储,**人类认识的形式** +- `byte string(bytes 类型)`:以 byte 形式存储,**机器认识的形式** + +在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 `type` 和 `isinstance` 可以判别 + +```python +# python3 + +>>> str_obj = "你好" +>>> +>>> type(str_obj) + +>>> +>>> isinstance("你好", str) +True +>>> +>>> isinstance("你好", bytes) +False +>>> +``` + +而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个 `b`,就表示你要定义一个 bytes 类型的字符串对象。 + +```python +# python3 +>>> byte_obj = b"Hello World!" +>>> type(byte_obj) + +>>> +>>> isinstance(byte_obj, str) +False +>>> +>>> isinstance(byte_obj, bytes) +True +>>> +``` + +但是在定义中文字符串时,你就不能直接在前面加 `b` 了,而应该使用 `encode` 转一下。 + +```python +>>> byte_obj=b"你好" + File "", line 1 +SyntaxError: bytes can only contain ASCII literal characters. +>>> +>>> str_obj="你好" +>>> +>>> str_obj.encode("utf-8") +b'\xe4\xbd\xa0\xe5\xa5\xbd' +>>> +``` + +## 2. Python 2 中 str 与 unicode + +而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。 + +在 Python2 里,字符串也只有两种类型,unicode 和 str 。 + +只有 unicode object 和 非unicode object(其实应该叫 str object) 的区别: + +- `unicode string(unicode类型)`:以 Unicode code points 形式存储,**人类认识的形式** +- `byte string(str 类型)`:以 byte 形式存储,**机器认识的形式** + +当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str 字符串对象,比如这样 + +```python +# python2 + +>>> str_obj="你好" +>>> +>>> type(str_obj) + +>>> +>>> isinstance(str_obj, bytes) +True +>>> isinstance(str_obj, str) +True +>>> +``` + +而当我们在双引号或单引号前面加个 `u`,就表明我们定义的是 unicode 字符串对象,比如这样 + +```python +# python2 + +>>> unicode_obj = u"你好" +>>> +>>> type(unicode_obj) + +>>> +>>> isinstance(unicode_obj, bytes) +False +>>> isinstance(unicode_obj, str) +False +>>> +``` + + + +## 3. 如何检测对象的编码 + +所有的字符,在 unicode 字符集中都有对应的编码值(英文叫做:`code point`) + +而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312 等。 + +也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。 + +那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码? + +这时候就要介绍到一个 python 的库 -- `chardet` ,使用它之前 需要先安装 + +``` +python3 -m pip install chardet +``` + +chardet 有一个 detect 方法,可以 `预测`其其编码格式 + +```python +>>> import chardet +>>> chardet.detect('微信公众号:Python编程时光'.encode('gbk')) +{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} +``` + +为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence 字段,其表示预测的可信度,或者说成功率。 + +但是使用它时,若你的字符数较少,就有可能 “`误诊`”),比如只有 `中文` 两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成 KOI8-R 编码。 + +```python +>>> str_obj = "中文" +>>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes +>>> +>>> chardet.detect(byte_obj) +{'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'} +>>> +>>> str_obj2 = str(byte_obj, encoding='KOI8-R') +>>> str_obj2 +'жпнд' +``` + +所以为了编码诊断的准确,要尽量使用足够多的字符。 + +chardet 支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html) + +![](http://image.iswbm.com/20200423185819.png) + + + +## 4. 编码与解码的区别 + +编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2 已经远去,这里以及后面都只用 Python 3 举例) + +- **编码**:encode 方法,把字符串对象转化为二进制字节序列 + +- **解码**:decode 方法,把二进制字节序列转化为字符串对象 + +![](http://image.iswbm.com/20200423190331.png) + + + +那么假如我们真知道了其编码格式,如何来转成 unicode 呢? + +**有两种方法** + +**第一种**是,直接使用 decode 方法 + +```python +>>> byte_obj.decode('gbk') +'中文' +>>> +``` + +**第二种**是,使用 str 类来转 + +```python +>>> str_obj = str(byte_obj, encoding='gbk') +>>> str_obj +'中文' +>>> +``` + + + +## 5. 如何设置文件编码 + +在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python 2 的时候,如果你的 python 文件里有中文,运行是会报错的。 + +``` +SyntaxError: Non-ASCII character '\xe4' in file demo.py +``` + +原因就是 ASCII 编码表太小,无法解释中文。 + +而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。 + +对于这个问题,通常解决方法有两种: + +**第一种方法** + +在 python2 中,可以使用在头部指定 + +可以这样写,虽然很好看 + +``` +# -*- coding: utf-8 -*- +``` + +但这样写太麻烦了,我通常使用下面两种写法 + +``` +# coding:utf-8 +# coding=utf-8 +``` + + + +**第二种方法** + +``` +import sys + +reload(sys) +sys.setdefaultencoding('utf-8') +``` + +这里在调用sys.setdefaultencoding(‘utf-8’) 设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除 sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用 sys.setdefaultencoding 这个方法。 + + + +## 6. 参考文章 + + + +- [阮一峰老师文章的常识性错误之 Unicode 与 UTF-8](https://foofish.net/unicode_utf-8.html) +- [Strings, Bytes, and Unicode in Python 2 and 3](https://timothybramlett.com/Strings_Bytes_and_Unicode_in_Python_2_and_3.html) +- [字符编码笔记:ASCII,Unicode 和 UTF-8](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) + + + +--- + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From bcd087eaa68a84c1546baf216c6442ef3c44a62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 23 Apr 2020 20:07:47 +0800 Subject: [PATCH 046/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0git?= =?UTF-8?q?=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_06.md | 25 +++++++++++++++++++++++++ source/c04/c04_15.md | 20 ++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/source/c04/c04_06.md b/source/c04/c04_06.md index 383b341..5fbef1d 100644 --- a/source/c04/c04_06.md +++ b/source/c04/c04_06.md @@ -522,6 +522,31 @@ Git 的使用全都是命令行的,由于没有那么直观,所以很容易 如果想要用户名和密码登陆,可以将上面的 OpenSSH 改成 PuTTY/Plink就行了。 + + +## 八、Gitlab 使用 + +### 8.1 免密使用 + +新建一个配置文件:`/root/.gitconfig` + +```ini +[user] + email = wangbm@163.com + name = wangbm +[push] + default = matching +[gui] + recentrepo = /root/git/deployment + recentrepo = /root/git/vmp2.0 +[credential] + helper = store +``` + +使用再次 git pull,提示你输入密码后,再次输入后,git 会给你存储下来,下次就不需要你再输入密码。 + + + --- ![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c04/c04_15.md b/source/c04/c04_15.md index 4dd59c8..76c3140 100644 --- a/source/c04/c04_15.md +++ b/source/c04/c04_15.md @@ -895,6 +895,26 @@ PyCharm 打开一个文件,就占用一个标签面。 ![](http://image.python-online.cn/20191211143510.png) + + +## 4.15.36 快速查看 Git 的修改 + +如果你的项目在 git 的管理之下,在你修改了文件后,你会有很多种方法来查看自己到底修改了什么? + +第一种当然是使用 git diff + +![](http://image.iswbm.com/20200420085524.png) + +第二种是使用之前写的 show history + +![](http://image.iswbm.com/20200420090117.png) + +第三种,也是今天要介绍的,是最简便,也是直接的方法。 + +在有文本变动的位置,PyCharm 会有提示,如下红色箭头标识处,点击它就可以直接查看,还可以快速回滚。 + +![](http://image.iswbm.com/20200420090428.png) + ## 附录 - [PyCharm 快捷键 Mac 版 ](https://resources.jetbrains.com/storage/products/pycharm/docs/PyCharm_ReferenceCard_mac.pdf) From 57002a524f7572da17eaa617fe5dd1b8da8a9476 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Fri, 24 Apr 2020 00:29:50 +0800 Subject: [PATCH 047/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_44.md | 12 ++ source/c01/c01_44.rst | 294 ++++++++++++++++++++++++++++++++++++++++++ source/c04/c04_06.rst | 24 ++++ source/c04/c04_15.rst | 24 ++++ 4 files changed, 354 insertions(+) create mode 100644 source/c01/c01_44.rst diff --git a/source/c01/c01_44.md b/source/c01/c01_44.md index 2e096bd..a576798 100644 --- a/source/c01/c01_44.md +++ b/source/c01/c01_44.md @@ -88,11 +88,18 @@ b'\xe4\xbd\xa0\xe5\xa5\xbd' >>> type(str_obj) >>> +>>> str_obj +'\xe4\xbd\xa0\xe5\xa5\xbd' +>>> >>> isinstance(str_obj, bytes) True >>> isinstance(str_obj, str) True +>>> isinstance(str_obj, unicode) +False >>> +>>> str is bytes +True ``` 而当我们在双引号或单引号前面加个 `u`,就表明我们定义的是 unicode 字符串对象,比如这样 @@ -102,6 +109,9 @@ True >>> unicode_obj = u"你好" >>> +>>> unicode_obj +u'\u4f60\u597d' +>>> >>> type(unicode_obj) >>> @@ -110,6 +120,8 @@ False >>> isinstance(unicode_obj, str) False >>> +>>> isinstance(unicode_obj, unicode) +True ``` diff --git a/source/c01/c01_44.rst b/source/c01/c01_44.rst new file mode 100644 index 0000000..cac3270 --- /dev/null +++ b/source/c01/c01_44.rst @@ -0,0 +1,294 @@ +1.44 详解 Python 中的编码问题 +============================= + +Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 +Python +开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 + +一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 +unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 +里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式? + +反反复复,这个过程真是太痛苦了。 + +今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再 +Google,收藏这篇文章就行。 + +1. Python 3 中 str 与 bytes +--------------------------- + +在 Python3中,字符串有两种类型 ,str 和 bytes。 + +今天就来说一说这二者的区别: + +- ``unicode string(str 类型)``\ :以 Unicode code points + 形式存储,\ **人类认识的形式** +- ``byte string(bytes 类型)``\ :以 byte + 形式存储,\ **机器认识的形式** + +在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 ``type`` +和 ``isinstance`` 可以判别 + +.. code:: python + + # python3 + + >>> str_obj = "你好" + >>> + >>> type(str_obj) + + >>> + >>> isinstance("你好", str) + True + >>> + >>> isinstance("你好", bytes) + False + >>> + +而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个 +``b``\ ,就表示你要定义一个 bytes 类型的字符串对象。 + +.. code:: python + + # python3 + >>> byte_obj = b"Hello World!" + >>> type(byte_obj) + + >>> + >>> isinstance(byte_obj, str) + False + >>> + >>> isinstance(byte_obj, bytes) + True + >>> + +但是在定义中文字符串时,你就不能直接在前面加 ``b`` 了,而应该使用 +``encode`` 转一下。 + +.. code:: python + + >>> byte_obj=b"你好" + File "", line 1 + SyntaxError: bytes can only contain ASCII literal characters. + >>> + >>> str_obj="你好" + >>> + >>> str_obj.encode("utf-8") + b'\xe4\xbd\xa0\xe5\xa5\xbd' + >>> + +2. Python 2 中 str 与 unicode +----------------------------- + +而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。 + +在 Python2 里,字符串也只有两种类型,unicode 和 str 。 + +只有 unicode object 和 非unicode object(其实应该叫 str object) +的区别: + +- ``unicode string(unicode类型)``\ :以 Unicode code points + 形式存储,\ **人类认识的形式** +- ``byte string(str 类型)``\ :以 byte 形式存储,\ **机器认识的形式** + +当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str +字符串对象,比如这样 + +.. code:: python + + # python2 + + >>> str_obj="你好" + >>> + >>> type(str_obj) + + >>> + >>> str_obj + '\xe4\xbd\xa0\xe5\xa5\xbd' + >>> + >>> isinstance(str_obj, bytes) + True + >>> isinstance(str_obj, str) + True + >>> isinstance(str_obj, unicode) + False + >>> + >>> str is bytes + True + +而当我们在双引号或单引号前面加个 ``u``\ ,就表明我们定义的是 unicode +字符串对象,比如这样 + +.. code:: python + + # python2 + + >>> unicode_obj = u"你好" + >>> + >>> unicode_obj + u'\u4f60\u597d' + >>> + >>> type(unicode_obj) + + >>> + >>> isinstance(unicode_obj, bytes) + False + >>> isinstance(unicode_obj, str) + False + >>> + >>> isinstance(unicode_obj, unicode) + True + +3. 如何检测对象的编码 +--------------------- + +所有的字符,在 unicode +字符集中都有对应的编码值(英文叫做:\ ``code point``\ ) + +而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312 +等。 + +也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。 + +那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码? + +这时候就要介绍到一个 python 的库 – ``chardet`` ,使用它之前 需要先安装 + +:: + + python3 -m pip install chardet + +chardet 有一个 detect 方法,可以 ``预测``\ 其其编码格式 + +.. code:: python + + >>> import chardet + >>> chardet.detect('微信公众号:Python编程时光'.encode('gbk')) + {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} + +为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence +字段,其表示预测的可信度,或者说成功率。 + +但是使用它时,若你的字符数较少,就有可能 “``误诊``”),比如只有 ``中文`` +两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成 +KOI8-R 编码。 + +.. code:: python + + >>> str_obj = "中文" + >>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes + >>> + >>> chardet.detect(byte_obj) + {'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'} + >>> + >>> str_obj2 = str(byte_obj, encoding='KOI8-R') + >>> str_obj2 + 'жпнд' + +所以为了编码诊断的准确,要尽量使用足够多的字符。 + +chardet +支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html) + +|image0| + +4. 编码与解码的区别 +------------------- + +编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2 +已经远去,这里以及后面都只用 Python 3 举例) + +- **编码**\ :encode 方法,把字符串对象转化为二进制字节序列 + +- **解码**\ :decode 方法,把二进制字节序列转化为字符串对象 + +|image1| + +那么假如我们真知道了其编码格式,如何来转成 unicode 呢? + +**有两种方法** + +**第一种**\ 是,直接使用 decode 方法 + +.. code:: python + + >>> byte_obj.decode('gbk') + '中文' + >>> + +**第二种**\ 是,使用 str 类来转 + +.. code:: python + + >>> str_obj = str(byte_obj, encoding='gbk') + >>> str_obj + '中文' + >>> + +5. 如何设置文件编码 +------------------- + +在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python +2 的时候,如果你的 python 文件里有中文,运行是会报错的。 + +:: + + SyntaxError: Non-ASCII character '\xe4' in file demo.py + +原因就是 ASCII 编码表太小,无法解释中文。 + +而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。 + +对于这个问题,通常解决方法有两种: + +**第一种方法** + +在 python2 中,可以使用在头部指定 + +可以这样写,虽然很好看 + +:: + + # -*- coding: utf-8 -*- + +但这样写太麻烦了,我通常使用下面两种写法 + +:: + + # coding:utf-8 + # coding=utf-8 + +**第二种方法** + +:: + + import sys + + reload(sys) + sys.setdefaultencoding('utf-8') + +这里在调用sys.setdefaultencoding(‘utf-8’) +设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除 +sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用 +sys.setdefaultencoding 这个方法。 + +6. 参考文章 +----------- + +- `阮一峰老师文章的常识性错误之 Unicode 与 + UTF-8 `__ +- `Strings, Bytes, and Unicode in Python 2 and + 3 `__ +- `字符编码笔记:ASCII,Unicode 和 + UTF-8 `__ + +-------------- + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200423185819.png +.. |image1| image:: http://image.iswbm.com/20200423190331.png + diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index d4a00df..49364a7 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -552,6 +552,30 @@ Desktop真心觉得不好用)。 如果想要用户名和密码登陆,可以将上面的 OpenSSH 改成 PuTTY/Plink就行了。 +八、Gitlab 使用 +--------------- + +8.1 免密使用 +~~~~~~~~~~~~ + +新建一个配置文件:\ ``/root/.gitconfig`` + +.. code:: ini + + [user] + email = wangbm@163.com + name = wangbm + [push] + default = matching + [gui] + recentrepo = /root/git/deployment + recentrepo = /root/git/vmp2.0 + [credential] + helper = store + +使用再次 git pull,提示你输入密码后,再次输入后,git +会给你存储下来,下次就不需要你再输入密码。 + -------------- .. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index bfdca54..f7babd9 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -1052,6 +1052,27 @@ Debug ,而远程调试需要不少前置步骤。 |image99| +4.15.36 快速查看 Git 的修改 +--------------------------- + +如果你的项目在 git +的管理之下,在你修改了文件后,你会有很多种方法来查看自己到底修改了什么? + +第一种当然是使用 git diff + +|image100| + +第二种是使用之前写的 show history + +|image101| + +第三种,也是今天要介绍的,是最简便,也是直接的方法。 + +在有文本变动的位置,PyCharm +会有提示,如下红色箭头标识处,点击它就可以直接查看,还可以快速回滚。 + +|image102| + 附录 ---- @@ -1168,4 +1189,7 @@ Debug ,而远程调试需要不少前置步骤。 .. |image97| image:: http://image.python-online.cn/20191211102826.png .. |image98| image:: http://image.python-online.cn/20191211133836.png .. |image99| image:: http://image.python-online.cn/20191211143510.png +.. |image100| image:: http://image.iswbm.com/20200420085524.png +.. |image101| image:: http://image.iswbm.com/20200420090117.png +.. |image102| image:: http://image.iswbm.com/20200420090428.png From 6e3d97574eb559b0f2debf25599566f7a529757f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 24 Apr 2020 09:19:52 +0800 Subject: [PATCH 048/147] =?UTF-8?q?Update=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_11.md | 9 +-------- source/c07/c07_12.md | 45 +++++++++++++++++++++++++++++++++++++++++++- source/c07/c07_13.md | 3 +++ source/c07/c07_14.md | 1 + source/c07/c07_15.md | 4 +++- source/c07/c07_16.md | 5 +++++ source/c07/c07_17.md | 3 +++ source/c07/c07_18.md | 6 +++++- source/c07/c07_19.md | 3 +++ 9 files changed, 68 insertions(+), 11 deletions(-) diff --git a/source/c07/c07_11.md b/source/c07/c07_11.md index 678bebb..9d5f6ca 100644 --- a/source/c07/c07_11.md +++ b/source/c07/c07_11.md @@ -78,11 +78,4 @@ K8s 角色详解 - - - - - - - - +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md index 9f799d8..2ffe069 100644 --- a/source/c07/c07_12.md +++ b/source/c07/c07_12.md @@ -1,4 +1,6 @@ -# 7.12 yum 的使用总结 +# 7.12 Linux 包管理工具:yum 和 rpm + +## yum 只下载安装包 @@ -165,3 +167,44 @@ rpm -qpc rpm rpm -qpR xxx.rpm ``` + + +## rpm + +```shell +rpm -ivh xxx.rpm-------安装软件 + +   -e-----------------卸载指定软件,不能是安装包名称 + +   -q-----------------查询指定软件是否安装,跟软件名称 + +   -qi----------------查询已经安装的软件的信息 + +  -ql-----------------查询已经安装的软件中包含什么样的内容 + +  -qf /etc/fstab ----查询这个文件是由哪个安装包产生的 + +  -qc-----------------查询已经安装的软件中包含的配置文件 + +  -qd-----------------查询已经安装的软件中包含的doc文件 + +  -q --scripts ------查询软件的脚本内容 + +  -Uvh----------------升级软件 + +  -Fvh----------------刷新 + +  -p------------------对于未安装的软件包查询信息,需要额外加此选项 + +  -qip----------------查询一个尚款安装的安装包的信息 + +  -qpc----------------查询一个尚未安装的安装包的配置文件 + +  -qpd----------------查询一个尚未安装的安装包的doc文件 + +  -qpl----------------查询一个尚未安装的安装包包含的信息 +``` + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c07/c07_13.md b/source/c07/c07_13.md index 7489a74..bc9dba3 100644 --- a/source/c07/c07_13.md +++ b/source/c07/c07_13.md @@ -105,3 +105,6 @@ class ResultCallback(CallbackModule): - v2_runner_on_skipped:部署任务跳过时,会调用 - v2_playbook_on_stats:所有的部署任务完成时,调用 + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c07/c07_14.md b/source/c07/c07_14.md index c43a5eb..b0031db 100644 --- a/source/c07/c07_14.md +++ b/source/c07/c07_14.md @@ -38,3 +38,4 @@ filename1 -ot filename2 如果 filename1比 filename2旧,则为真。 +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c07/c07_15.md b/source/c07/c07_15.md index ca35bb2..0dbed61 100644 --- a/source/c07/c07_15.md +++ b/source/c07/c07_15.md @@ -212,4 +212,6 @@ show status like '%wsrep%'; - https://blog.csdn.net/weixin_42867972/article/details/84198696 - http://www.360doc.com/content/13/0817/15/834950_307820923.shtml -- http://blog.itpub.net/30126024/viewspace-2221483/ \ No newline at end of file +- http://blog.itpub.net/30126024/viewspace-2221483/ + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c07/c07_16.md b/source/c07/c07_16.md index acd014b..311b45e 100644 --- a/source/c07/c07_16.md +++ b/source/c07/c07_16.md @@ -48,3 +48,8 @@ ${command} 2>&1 ``` + + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c07/c07_17.md b/source/c07/c07_17.md index 8eda019..529ade8 100644 --- a/source/c07/c07_17.md +++ b/source/c07/c07_17.md @@ -16,3 +16,6 @@ https://www.jianshu.com/p/ae74f5f39828 + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c07/c07_18.md b/source/c07/c07_18.md index 34d8b66..f832863 100644 --- a/source/c07/c07_18.md +++ b/source/c07/c07_18.md @@ -54,4 +54,8 @@ ``` [root@linux ~]``# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1'``A BC -``` \ No newline at end of file +``` + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c07/c07_19.md b/source/c07/c07_19.md index 488bdbd..4d9ad8f 100644 --- a/source/c07/c07_19.md +++ b/source/c07/c07_19.md @@ -43,3 +43,6 @@ $ ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.20.20.1 $ ansible 172.20.20.1 -m ping ``` + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From 4dec67b185c7dc462b447d8a42805164920cdee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 28 Apr 2020 09:15:21 +0800 Subject: [PATCH 049/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0<=E7=94=A8=E6=88=B7=E7=8E=AF=E5=A2=83=E7=9A=84?= =?UTF-8?q?=E4=BD=BF=E7=94=A8>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_24.md | 169 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 source/c04/c04_24.md diff --git a/source/c04/c04_24.md b/source/c04/c04_24.md new file mode 100644 index 0000000..7ddfaf5 --- /dev/null +++ b/source/c04/c04_24.md @@ -0,0 +1,169 @@ +# 4.24 Python “用户环境”的一次完美应用 + +在之前写过一篇关于虚拟环境使用的文章 :[Python 虚拟环境使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&chksm=e886669bdff1ef8d82aae3a231ef0651f82d5e97cf1e64aceda00e686119900518c202dc9b1b&scene=21#wechat_redirect). + +但是还没有好好的介绍一下 Python 的用户环境,原因是自己一直没遇到要使用 `用户环境` 的使用场景,所以就一直懒得写。 + +恰巧这两天,自己遇到了一个使用用户环境的体验可以完爆虚拟环境的案例,就拿出来分享一下。 + +## 1. 我的使用背景 + +公司有数以万计的服务器,为了对实现对访问记录进行集中管理以及出于安全考虑,每台服务器都有访问限制,必须使用公司的跳板机才能登陆。 + +每个公司的员工在跳板机上都有自己的用户、 家目录,对于很多需要 root 权限的操作,是高度受限制的。 + +比如我现在我要在跳板机上实现远程登陆大批量的机器进行一些维护工作,当然我这里使用的还是 Python 来实现,这个 Python 脚本里有一些依赖库(比如 之前介绍过的 paramiko 这个神器),在跳板机上中并没有安装。 + +![](http://image.iswbm.com/20200427180207.png) + +做为普通用户的你,是没有权限安装第三方包的。 + +![](http://image.iswbm.com/20200427180042.png) + +问题就来了,我如何才能在跳板机中使用 paramiko 这个包呢? + +## 2. 为何不使用虚拟环境? + +既然不能对全局的 Python 环境进行更改,那我完全可以自己再创建一个环境,只要这个环境里事先装好 paramiko 这个包不就好了。 + +因此,使用虚拟环境是一种解决方案,但它并不是一个完美的解决方案。 + +**原因有以下几点**: + +1、 创建虚拟环境的过程,步骤较多,比较复杂。这里的复杂是相对于我后面要使用的用户环境而言。 + +2、 虚拟环境是包含一整个 Python 解释器,存在大量与系统重复的包,size比较大,并不轻便。 + +3、 使用 console 模式调试的话,进入很不方便 + +![](http://image.iswbm.com/20200427182334.png) + +就算你不使用 console 模式,你调用脚本的方式,也会很奇怪,你得这样 + +```python +$ zabbix_env/bin/python demo.py +``` + +如果你不想使用这样,可以给这个脚本加个可执行权限,并在脚本的第一行指定你的解释器,省去了一点点麻烦,可即便如此,我仍然感觉很别扭。 + +```shell +[wangbm@35ha02 ~]$ cat demo.py +#!/home/wangbm/zabbix_env/bin/python + +import zabbix_api +[wangbm@35ha02 ~]$ +[wangbm@35ha02 ~]$ +[wangbm@35ha02 ~]$ chmod +x demo.py +[wangbm@35ha02 ~]$ +[wangbm@35ha02 ~]$ ./demo.py # 可以执行,没有报错 +[wangbm@35ha02 ~]$ +``` + + + +你可能会问我:为什么不使用 virtualenv + virtualenvwrapper ,这样可以使用 workon 进入虚拟环境。 + +原因是跳板机里的都是很古老的包,你看上面的 Python 还是 2.7.5 呢,所以你所说的那些工具通通没有。 + + + +## 3. 用户环境原理 + +这里要介绍的这种方案(**用户环境**),可能很多人都没有使用过,甚至没有听过,它算是一个冷门但是非常好用的功能。 + +操作之前 ,先简单介绍一下它。 + +先提一个问题,Python 在查找导入包时,如果我们多个路径都有这个包,那 Python 如何确定应该从哪个路径进行导入呢? + +答案是, 搜索导入路径是有优先级的,你可以通过 sys.path 进行查看。 + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path) +['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] +>>> +``` + +可以看到路径 `/home/wangbm/.local/lib/python2.7/site-packages` 是优先于 `/usr/lib64/python2.7/site-packages` 路径的。 + +这就是 **用户环境** 的原理,只要我们将包装在自己家目录下,就可以优先于全局环境中进行查找。 + +使用起来,可以做到用户无感知,跟使用原生的全局环境并没有区别。 + +## 4. 具体操作方法 + +创建一个用户环境,并安装上你所需要的包,一条命令就能搞定,这可比虚拟环境简单方便多了。 + +那么怎么操作呢? + +只要你在使用 pip 安装包时,加上 `--user` 参数,pip 就会将其安装在当前用户的 `~/.local/lib/python2.x/site-packages` 下,而其他用户的 python 则不会受影响。 + +```shell +$ pip install --user pkg +``` + +这里要注意的是,不能使用这种方式,亲测它会将包装到全局环境下,具体原因我还没有深究。 + +```shell +$ python -m pip install --user pkg +``` + +为了让你理解这个过程,我这里来举个例子,并且验证其是否可以做到用户隔离。 + +```shell +# 在全局环境中未安装 requests +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ su - wangbm + +# 由于用户环境继承自全局环境,这里也未安装 +[wangbm@localhost ~]$ pip list | grep requests +[wangbm@localhost ~]$ pip install --user requests +[wangbm@localhost ~]$ pip list | grep requests +requests (2.22.0) +[wangbm@localhost ~]$ + +# 从 Location 属性可发现 requests 只安装在当前用户环境中 +[wangbm@localhost ~]$ pip show requests +--- +Metadata-Version: 2.1 +Name: requests +Version: 2.22.0 +Summary: Python HTTP for Humans. +Home-page: http://python-requests.org +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +Installer: pip +License: Apache 2.0 +Location: /home/wangbm/.local/lib/python2.7/site-packages +[wangbm@localhost ~]$ exit +logout + +# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ +``` + +有了这个思路,我就可以先在其他机器(前提自己必须拥有管理员权限 )上,创建一个用户环境,并且安装上 paramiko 这个包。 + +然后将这个用户环境,压缩拷贝至跳板机自己的家目录下的 `.local/lib` 目录下并解压。 + +然后直接使用 python 进入 console 模式,现在已经可以直接使用 paramiko 这个包了。 + +![](http://image.iswbm.com/20200427185854.png) + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From ed5da843f8d5c338fc1596cb42525bde0b21b586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 28 Apr 2020 09:16:02 +0800 Subject: [PATCH 050/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_35.md | 40 ++++++++++++++++++++++++++++++++++++++++ source/c07/c07_16.md | 21 +++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md index 8a16647..594e430 100644 --- a/source/c01/c01_35.md +++ b/source/c01/c01_35.md @@ -323,6 +323,45 @@ trans.close() ![](http://image.python-online.cn/20200228111654.png) +### 注意事项 + +使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 "踩坑" 后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点 + +就是在执行 `ssh.exec_command(cmd)` 时,这个命令并不是同步阻塞的。 + +比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s 后,再 执行 ssh.close() + +```python +import paramiko + +trans = paramiko.Transport(("172.20.42.1", 57891)) +trans.connect(username="root", password="youpassword") +ssh = paramiko.SSHClient() +ssh._transport = trans +stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") +ssh.close() +``` + + + +但是如果改成这样,加上一行 stdout.read(), paramiko 就知道,你需要这个执行的结果,就会在 read() 进行阻塞。 + +```python +import paramiko + +trans = paramiko.Transport(("172.20.42.1", 57891)) +trans.connect(username="root", password="youpassword") +ssh = paramiko.SSHClient() +ssh._transport = trans +stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") + +# 加上一行 read() +print(stdout.read()) +ssh.close() +``` + + + ## 4. 写在最后 经过了一番对比,和一些实例的展示,可以看出 Paramiko 是一个专业、让人省心的 ssh 利器,个人认为 Paramiko 模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh 到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。 @@ -336,6 +375,7 @@ trans.close() - https://github.com/paramiko/paramiko - http://docs.paramiko.org - https://www.liujiangblog.com/blog/15/ +- http://docs.paramiko.org/en/stable/ diff --git a/source/c07/c07_16.md b/source/c07/c07_16.md index 311b45e..0d4e915 100644 --- a/source/c07/c07_16.md +++ b/source/c07/c07_16.md @@ -50,6 +50,27 @@ ${command} 2>&1 +## 7.16.4 如何ssh多行执行命令 + +```shell +out=`ssh -l root -p$port $server_ip "mysql -h $server_ip -P $port -u $user -p$password -e \"mysql 命令\" "` + +for cluster_ip in ${out[*]}; +do + + echo '========' $cluster_ip '开始更新...' +sshpass -p $password ssh -p $port root@$cluster_ip -t sshpass -p "password" ssh root@172.20.20.253 -o StrictHostKeyChecking=no < Date: Tue, 28 Apr 2020 13:32:59 +0800 Subject: [PATCH 051/147] =?UTF-8?q?Update=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- source/c01/c01_35.rst | 40 +++++++++ source/c04/c04_24.rst | 196 ++++++++++++++++++++++++++++++++++++++++++ source/c07/c07_11.rst | 5 ++ source/c07/c07_12.rst | 49 ++++++++++- source/c07/c07_13.rst | 5 ++ source/c07/c07_14.rst | 5 ++ source/c07/c07_15.rst | 5 ++ source/c07/c07_16.rst | 25 ++++++ source/c07/c07_17.rst | 5 ++ source/c07/c07_18.rst | 5 ++ source/c07/c07_19.rst | 5 ++ 12 files changed, 346 insertions(+), 3 deletions(-) create mode 100644 source/c04/c04_24.rst diff --git a/README.md b/README.md index 0cccd95..7a8fe9f 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ - 1.40 [Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_40.html) - 1.41 [Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_41.html) - 1.42 [Python 炫技操作:海象运算符的三种用法](http://python.iswbm.com/en/latest/c01/c01_42.html) +- 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) @@ -89,6 +90,7 @@ - 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python.iswbm.com/en/latest/c04/c04_21.html) - 4.22 [用好 Chrome 必看](http://python.iswbm.com/en/latest/c04/c04_22.html) - 4.23 [电脑使用技巧](http://python.iswbm.com/en/latest/c04/c04_23.html) +- 4.24 [Python “用户环境”的一次完美应用](http://python.iswbm.com/en/latest/c04/c04_24.html) ## 第五章:算法教程 - 5.1 [图解九大经典排序算法](http://python.iswbm.com/en/latest/c05/c05_01.html) @@ -115,7 +117,7 @@ - 7.9 [Ansible 入门指南使用手册](http://python.iswbm.com/en/latest/c07/c07_09.html) - 7.10 [Ansible API 最全使用文档(中文)](http://python.iswbm.com/en/latest/c07/c07_10.html) - 7.11 [K8S:基础入门](http://python.iswbm.com/en/latest/c07/c07_11.html) -- 7.12 [yum 的使用总结](http://python.iswbm.com/en/latest/c07/c07_12.html) +- 7.12 [Linux 包管理工具:yum 和 rpm](http://python.iswbm.com/en/latest/c07/c07_12.html) - 7.13 [基于 ansible-api 二次开发](http://python.iswbm.com/en/latest/c07/c07_13.html) - 7.14 [Linux 如何写判断语句](http://python.iswbm.com/en/latest/c07/c07_14.html) - 7.15 [Mariadb 与 Galera 集群总结](http://python.iswbm.com/en/latest/c07/c07_15.html) diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index 849e3fc..0ed5503 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -336,6 +336,45 @@ Windows,这里就有一件好事,一件坏事了,。 |image2| +注意事项 +~~~~~~~~ + +使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 “踩坑” +后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点 + +就是在执行 ``ssh.exec_command(cmd)`` 时,这个命令并不是同步阻塞的。 + +比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s +后,再 执行 ssh.close() + +.. code:: python + + import paramiko + + trans = paramiko.Transport(("172.20.42.1", 57891)) + trans.connect(username="root", password="youpassword") + ssh = paramiko.SSHClient() + ssh._transport = trans + stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") + ssh.close() + +但是如果改成这样,加上一行 stdout.read(), paramiko +就知道,你需要这个执行的结果,就会在 read() 进行阻塞。 + +.. code:: python + + import paramiko + + trans = paramiko.Transport(("172.20.42.1", 57891)) + trans.connect(username="root", password="youpassword") + ssh = paramiko.SSHClient() + ssh._transport = trans + stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") + + # 加上一行 read() + print(stdout.read()) + ssh.close() + 4. 写在最后 ----------- @@ -352,6 +391,7 @@ Windows,这里就有一件好事,一件坏事了,。 - https://github.com/paramiko/paramiko - http://docs.paramiko.org - https://www.liujiangblog.com/blog/15/ +- http://docs.paramiko.org/en/stable/ .. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! diff --git a/source/c04/c04_24.rst b/source/c04/c04_24.rst new file mode 100644 index 0000000..c0804c8 --- /dev/null +++ b/source/c04/c04_24.rst @@ -0,0 +1,196 @@ +4.24 Python “用户环境”的一次完美应用 +==================================== + +在之前写过一篇关于虚拟环境使用的文章 :\ `Python +虚拟环境使用指南 `__. + +但是还没有好好的介绍一下 Python 的用户环境,原因是自己一直没遇到要使用 +``用户环境`` 的使用场景,所以就一直懒得写。 + +恰巧这两天,自己遇到了一个使用用户环境的体验可以完爆虚拟环境的案例,就拿出来分享一下。 + +1. 我的使用背景 +--------------- + +公司有数以万计的服务器,为了对实现对访问记录进行集中管理以及出于安全考虑,每台服务器都有访问限制,必须使用公司的跳板机才能登陆。 + +每个公司的员工在跳板机上都有自己的用户、 家目录,对于很多需要 root +权限的操作,是高度受限制的。 + +比如我现在我要在跳板机上实现远程登陆大批量的机器进行一些维护工作,当然我这里使用的还是 +Python 来实现,这个 Python 脚本里有一些依赖库(比如 之前介绍过的 +paramiko 这个神器),在跳板机上中并没有安装。 + +|image0| + +做为普通用户的你,是没有权限安装第三方包的。 + +|image1| + +问题就来了,我如何才能在跳板机中使用 paramiko 这个包呢? + +2. 为何不使用虚拟环境? +----------------------- + +既然不能对全局的 Python +环境进行更改,那我完全可以自己再创建一个环境,只要这个环境里事先装好 +paramiko 这个包不就好了。 + +因此,使用虚拟环境是一种解决方案,但它并不是一个完美的解决方案。 + +**原因有以下几点**\ : + +1、 +创建虚拟环境的过程,步骤较多,比较复杂。这里的复杂是相对于我后面要使用的用户环境而言。 + +2、 虚拟环境是包含一整个 Python +解释器,存在大量与系统重复的包,size比较大,并不轻便。 + +3、 使用 console 模式调试的话,进入很不方便 + +|image2| + +就算你不使用 console 模式,你调用脚本的方式,也会很奇怪,你得这样 + +.. code:: python + + $ zabbix_env/bin/python demo.py + +如果你不想使用这样,可以给这个脚本加个可执行权限,并在脚本的第一行指定你的解释器,省去了一点点麻烦,可即便如此,我仍然感觉很别扭。 + +.. code:: shell + + [wangbm@35ha02 ~]$ cat demo.py + #!/home/wangbm/zabbix_env/bin/python + + import zabbix_api + [wangbm@35ha02 ~]$ + [wangbm@35ha02 ~]$ + [wangbm@35ha02 ~]$ chmod +x demo.py + [wangbm@35ha02 ~]$ + [wangbm@35ha02 ~]$ ./demo.py # 可以执行,没有报错 + [wangbm@35ha02 ~]$ + +你可能会问我:为什么不使用 virtualenv + virtualenvwrapper ,这样可以使用 +workon 进入虚拟环境。 + +原因是跳板机里的都是很古老的包,你看上面的 Python 还是 2.7.5 +呢,所以你所说的那些工具通通没有。 + +3. 用户环境原理 +--------------- + +这里要介绍的这种方案(\ **用户环境**\ ),可能很多人都没有使用过,甚至没有听过,它算是一个冷门但是非常好用的功能。 + +操作之前 ,先简单介绍一下它。 + +先提一个问题,Python 在查找导入包时,如果我们多个路径都有这个包,那 +Python 如何确定应该从哪个路径进行导入呢? + +答案是, 搜索导入路径是有优先级的,你可以通过 sys.path 进行查看。 + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path) + ['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] + >>> + +可以看到路径 ``/home/wangbm/.local/lib/python2.7/site-packages`` +是优先于 ``/usr/lib64/python2.7/site-packages`` 路径的。 + +这就是 **用户环境** +的原理,只要我们将包装在自己家目录下,就可以优先于全局环境中进行查找。 + +使用起来,可以做到用户无感知,跟使用原生的全局环境并没有区别。 + +4. 具体操作方法 +--------------- + +创建一个用户环境,并安装上你所需要的包,一条命令就能搞定,这可比虚拟环境简单方便多了。 + +那么怎么操作呢? + +只要你在使用 pip 安装包时,加上 ``--user`` 参数,pip +就会将其安装在当前用户的 ``~/.local/lib/python2.x/site-packages`` +下,而其他用户的 python 则不会受影响。 + +.. code:: shell + + $ pip install --user pkg + +这里要注意的是,不能使用这种方式,亲测它会将包装到全局环境下,具体原因我还没有深究。 + +.. code:: shell + + $ python -m pip install --user pkg + +为了让你理解这个过程,我这里来举个例子,并且验证其是否可以做到用户隔离。 + +.. code:: shell + + # 在全局环境中未安装 requests + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ su - wangbm + + # 由于用户环境继承自全局环境,这里也未安装 + [wangbm@localhost ~]$ pip list | grep requests + [wangbm@localhost ~]$ pip install --user requests + [wangbm@localhost ~]$ pip list | grep requests + requests (2.22.0) + [wangbm@localhost ~]$ + + # 从 Location 属性可发现 requests 只安装在当前用户环境中 + [wangbm@localhost ~]$ pip show requests + --- + Metadata-Version: 2.1 + Name: requests + Version: 2.22.0 + Summary: Python HTTP for Humans. + Home-page: http://python-requests.org + Author: Kenneth Reitz + Author-email: me@kennethreitz.org + Installer: pip + License: Apache 2.0 + Location: /home/wangbm/.local/lib/python2.7/site-packages + [wangbm@localhost ~]$ exit + logout + + # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ + +有了这个思路,我就可以先在其他机器(前提自己必须拥有管理员权限 +)上,创建一个用户环境,并且安装上 paramiko 这个包。 + +然后将这个用户环境,压缩拷贝至跳板机自己的家目录下的 ``.local/lib`` +目录下并解压。 + +然后直接使用 python 进入 console 模式,现在已经可以直接使用 paramiko +这个包了。 + +|image3| + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200427180207.png +.. |image1| image:: http://image.iswbm.com/20200427180042.png +.. |image2| image:: http://image.iswbm.com/20200427182334.png +.. |image3| image:: http://image.iswbm.com/20200427185854.png + diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst index 19b278b..7951bb0 100644 --- a/source/c07/c07_11.rst +++ b/source/c07/c07_11.rst @@ -75,5 +75,10 @@ K8s 角色详解 里可以运行一个容器(最常用),也可以运行多个容器,若运行多个容器,那这几个容器的工作必定有着紧密的联系,而且需要直接 **共享资源**\ 。 +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/20190907162015.png diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index 8c0b99e..a2dce10 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -1,5 +1,8 @@ -7.12 yum 的使用总结 -=================== +7.12 Linux 包管理工具:yum 和 rpm +================================= + +yum +--- 只下载安装包 @@ -156,6 +159,48 @@ yum-utils 使用 # 查看软件包的依赖关系 rpm -qpR xxx.rpm +rpm +--- + +.. code:: shell + + rpm -ivh xxx.rpm-------安装软件 + +    -e-----------------卸载指定软件,不能是安装包名称 + +    -q-----------------查询指定软件是否安装,跟软件名称 + +    -qi----------------查询已经安装的软件的信息 + +   -ql-----------------查询已经安装的软件中包含什么样的内容 + +   -qf /etc/fstab ----查询这个文件是由哪个安装包产生的 + +   -qc-----------------查询已经安装的软件中包含的配置文件 + +   -qd-----------------查询已经安装的软件中包含的doc文件 + +   -q --scripts ------查询软件的脚本内容 + +   -Uvh----------------升级软件 + +   -Fvh----------------刷新 + +   -p------------------对于未安装的软件包查询信息,需要额外加此选项 + +   -qip----------------查询一个尚款安装的安装包的信息 + +   -qpc----------------查询一个尚未安装的安装包的配置文件 + +   -qpd----------------查询一个尚未安装的安装包的doc文件 + +   -qpl----------------查询一个尚未安装的安装包包含的信息 + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/20191219152328.png .. |image1| image:: http://image.python-online.cn/20191225173340.png .. |image2| image:: http://image.python-online.cn/20191225175350.png diff --git a/source/c07/c07_13.rst b/source/c07/c07_13.rst index ff46eb3..7bf2666 100644 --- a/source/c07/c07_13.rst +++ b/source/c07/c07_13.rst @@ -114,3 +114,8 @@ ansible 的 api 开发出来的吗? - v2_runner_on_unreachable:节点不可达时,会调用 - v2_runner_on_skipped:部署任务跳过时,会调用 - v2_playbook_on_stats:所有的部署任务完成时,调用 + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c07/c07_14.rst b/source/c07/c07_14.rst index 289dc83..7bc09f9 100644 --- a/source/c07/c07_14.rst +++ b/source/c07/c07_14.rst @@ -23,3 +23,8 @@ filename可读,则为真 ``-w filename``: 如果 filename可写,则为真 -eq :等于 -ne :不等于 -gt :大于 -ge :大于等于 -lt :小于 -le :小于等于 + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c07/c07_15.rst b/source/c07/c07_15.rst index a14de00..bbb1e93 100644 --- a/source/c07/c07_15.rst +++ b/source/c07/c07_15.rst @@ -217,5 +217,10 @@ wsrep_flow_control_sent 和 wsrep_local_recv_queue_avg - http://www.360doc.com/content/13/0817/15/834950_307820923.shtml - http://blog.itpub.net/30126024/viewspace-2221483/ +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + .. |image0| image:: http://image.python-online.cn/20191213162259.png diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index f20a6c2..911d75c 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -46,6 +46,31 @@ ${command} 2>&1 +7.16.4 如何ssh多行执行命令 +-------------------------- + +.. code:: shell + + out=`ssh -l root -p$port $server_ip "mysql -h $server_ip -P $port -u $user -p$password -e \"mysql 命令\" "` + + for cluster_ip in ${out[*]}; + do + + echo '========' $cluster_ip '开始更新...' + sshpass -p $password ssh -p $port root@$cluster_ip -t sshpass -p "password" ssh root@172.20.20.253 -o StrictHostKeyChecking=no < Date: Tue, 5 May 2020 09:42:04 +0800 Subject: [PATCH 052/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- source/c04/c04_18.md | 71 ++++++++++++++++++--------- source/c04/c04_18.rst | 108 ++++++++++++++++++++++++++++-------------- source/c07/c07_20.md | 47 ++++++++++++++++++ source/c07/c07_20.rst | 48 +++++++++++++++++++ 5 files changed, 218 insertions(+), 59 deletions(-) create mode 100644 source/c07/c07_20.md create mode 100644 source/c07/c07_20.rst diff --git a/README.md b/README.md index 7a8fe9f..7384a9f 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ - 4.15 [30个 PyCharm 实用技巧](http://python.iswbm.com/en/latest/c04/c04_15.html) - 4.16 [Python 开发技巧集合](http://python.iswbm.com/en/latest/c04/c04_16.html) - 4.17 [详解 23 种设计模式](http://python.iswbm.com/en/latest/c04/c04_17.html) -- 4.18 [如何上手Mac ?](http://python.iswbm.com/en/latest/c04/c04_18.html) +- 4.18 [详细的 Mac 使用指南](http://python.iswbm.com/en/latest/c04/c04_18.html) - 4.19 [程序员编码必学:Vim](http://python.iswbm.com/en/latest/c04/c04_19.html) - 4.20 [学会使用谷歌搜索引擎](http://python.iswbm.com/en/latest/c04/c04_20.html) - 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python.iswbm.com/en/latest/c04/c04_21.html) @@ -125,6 +125,7 @@ - 1.17 [ansible 自定义 Jinja2 过滤器](http://python.iswbm.com/en/latest/c07/c07_17.html) - 7.18 [Shell中去除字符串前后空格的方法](http://python.iswbm.com/en/latest/c07/c07_18.html) - 7.19 [Ansible 使用教程](http://python.iswbm.com/en/latest/c07/c07_19.html) +- 7.20 [使用 Shell 删除文件的多种方法](http://python.iswbm.com/en/latest/c07/c07_20.html) ## 第八章:OpenStack - 8.1 [OpenStack 运维命令](http://python.iswbm.com/en/latest/c08/c08_01.html) diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 8bce532..66a3509 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -1,4 +1,4 @@ -# 4.18 如何上手Mac ? +# 4.18 详细的 Mac 使用指南 ## 4.18.1 iTerm2 @@ -45,6 +45,9 @@ command + ctrl + f # 最小化/隐藏窗口 command + h +# 最小化除了当前active窗口之外的所有窗口 +comand + option + h + # 隐藏除当前窗口外的其他所有窗口 command + opthon + h @@ -251,9 +254,11 @@ brew cask install docker ## 4.18.7 访达使用技巧 -详细请看这篇文章([MacOS实用技巧之Finder(访达)的使用](https://www.jianshu.com/p/3666e6954e8a)),非常好的教程 +详细请看这篇文章([MacOS实用技巧之Finder(访达)的使用](https://www.jianshu.com/p/3666e6954e8a)),非常好的教程。 -``` +**跳转技巧** + +```shell # 快速打开访达:先打开搜索,再打开个人家目录 打开搜索:command + option(alt) + space 关闭标签页:command + shift + h @@ -264,6 +269,25 @@ command + ↑ # 进入文件夹 command + ↓ +# 前进 后退 +command + [ +comand + ] + +# 快速跳转至第一个文件或最后一个文件 +option + ↑ +option + ↓ + + +# 打开指定路径(前提访达得是激活状态的窗口) +# 注意在这里,可以使用 tab 补全 +shift + command + g +``` + + + +**操作文件与文件夹** + +```sh e llsh el # enter 重命名文件夹 @@ -275,13 +299,6 @@ command + ↓ 点击 -> 拖拽 如果想要取消选中,就 command + 点击 -# 打开指定路径(前提访达得是激活状态的窗口) -# 注意在这里,可以使用 tab 补全 -shift + command + g - -# 前进 后退 -command + [ -comand + ] # 打开最近使用过的文件夹 comand + shift + f @@ -292,18 +309,6 @@ command + shift + . # 查看文件/夹 详情 command + i -# 复制文件路径,有两种方法 -# 【第一种】:快捷键 -command + option + c -# 若你使用 alfred ,快捷键会冲突,解决方法:先右键,再 option,选择将 xx 拷贝为路径名称 - -# 第二种:使用服务 -参考 https://sspai.com/post/33422 - -# 快速跳转至第一个文件或最后一个文件 -option + ↑ -option + ↓ - # mac 中拷贝和复制不一样 command + c 拷贝 command + d 复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option @@ -320,6 +325,28 @@ command + shift + n command + w ``` +**定制服务(复制文件路径)** + +```shell +# 复制文件路径,有两种方法 +# 【第一种】:快捷键 +command + option + c +# 若你使用 alfred ,快捷键会冲突,解决方法:先右键,再 option,选择将 xx 拷贝为路径名称 + +# 第二种:使用服务 +参考 https://sspai.com/post/33422 +``` + +**在 iTerm2中打开访达** + +``` +# 在当前目录打开 +open . + +# 在指定目录打开 +open ~/Code +``` + ## 4.18.8 使用小鹤双拼 diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index fb4cd85..9759ec8 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -1,5 +1,5 @@ -4.18 如何上手Mac ? -=================== +4.18 详细的 Mac 使用指南 +======================== 4.18.1 iTerm2 ------------- @@ -51,6 +51,9 @@ # 最小化/隐藏窗口 command + h + # 最小化除了当前active窗口之外的所有窗口 + comand + option + h + # 隐藏除当前窗口外的其他所有窗口 command + opthon + h @@ -268,9 +271,11 @@ iText,精准的 OCR 文字识别工具。 4.18.7 访达使用技巧 ------------------- -详细请看这篇文章(\ `MacOS实用技巧之Finder(访达)的使用 `__\ ),非常好的教程 +详细请看这篇文章(\ `MacOS实用技巧之Finder(访达)的使用 `__\ ),非常好的教程。 -:: +**跳转技巧** + +.. code:: shell # 快速打开访达:先打开搜索,再打开个人家目录 打开搜索:command + option(alt) + space @@ -282,33 +287,73 @@ iText,精准的 OCR 文字识别工具。 # 进入文件夹 command + ↓ - # enter - 重命名文件夹 - - # 选中所有文件,并将这些文件归档入一个新的文件夹 - 右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 + # 前进 后退 + command + [ + comand + ] + # 快速跳转至第一个文件或最后一个文件 + option + ↑ + option + ↓ - # 选择 - 点击 -> 拖拽 - 如果想要取消选中,就 command + 点击 # 打开指定路径(前提访达得是激活状态的窗口) # 注意在这里,可以使用 tab 补全 shift + command + g - # 前进 后退 - command + [ - comand + ] +**操作文件与文件夹** + +\```sh e llsh el # enter 重命名文件夹 + +选中所有文件,并将这些文件归档入一个新的文件夹 +============================================== + +右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 + +选择 +==== + +点击 -> 拖拽 如果想要取消选中,就 command + 点击 - # 打开最近使用过的文件夹 - comand + shift + f +打开最近使用过的文件夹 +====================== - # 显示/隐藏文件 - command + shift + . +comand + shift + f - # 查看文件/夹 详情 - command + i +显示/隐藏文件 +============= + +command + shift + . + +查看文件/夹 详情 +================ + +command + i + +mac 中拷贝和复制不一样 +====================== + +command + c 拷贝 command + d +复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option command + v +粘贴 command + option + v 称动 ,或者使用鼠标拖动 + +可以设置搜索的范围 +================== + +command + f + +新建文件夹 +========== + +command + shift + n + +关闭访达标签页,如果是最后一个标签页,则关闭访达 +================================================ + +command + w \``\` + +**定制服务(复制文件路径)** + +.. code:: shell # 复制文件路径,有两种方法 # 【第一种】:快捷键 @@ -318,24 +363,15 @@ iText,精准的 OCR 文字识别工具。 # 第二种:使用服务 参考 https://sspai.com/post/33422 - # 快速跳转至第一个文件或最后一个文件 - option + ↑ - option + ↓ +**在 iTerm2中打开访达** - # mac 中拷贝和复制不一样 - command + c 拷贝 - command + d 复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option - command + v 粘贴 - command + option + v 称动 ,或者使用鼠标拖动 - - # 可以设置搜索的范围 - command + f +:: - # 新建文件夹 - command + shift + n + # 在当前目录打开 + open . - # 关闭访达标签页,如果是最后一个标签页,则关闭访达 - command + w + # 在指定目录打开 + open ~/Code 4.18.8 使用小鹤双拼 ------------------- diff --git a/source/c07/c07_20.md b/source/c07/c07_20.md new file mode 100644 index 0000000..7dc445f --- /dev/null +++ b/source/c07/c07_20.md @@ -0,0 +1,47 @@ +# 7.20 使用 Shell 删除文件的多种方法 + +## 1. 当前目录下的文件 + +最经典的方法,删除当前目录下的所有类型的文件 + +```shell +rm -f \* +``` + +用find命令查找普通文件并删除or用find命令的处理动作将其删除 + +```shell +find . -type f -delete + +# 或者 + +find . -type f -exec rm -f {} \ +``` + +用于参数列表过长;要删除的文件太多 + +```shell +find . -type f | xargs rm -f +``` + +删除全部普通文件 + +```shell +rm-f `find . -type f` +``` + +用for循环语句删除当前目录下的所有类型的文件 + +```shell +for delete in `ls -l`;do rm -f \* ;done +``` + + + +## 2. 指定目录下的文件 + +两种方法 + +1. 把上面的 `.` 全部改为指定的目录 +2. 在上面的命令之前,都加上 `cd ${dest_dir};` ,并且命令后,加上 `cd -` + diff --git a/source/c07/c07_20.rst b/source/c07/c07_20.rst new file mode 100644 index 0000000..fcb4ca0 --- /dev/null +++ b/source/c07/c07_20.rst @@ -0,0 +1,48 @@ +7.20 使用 Shell 删除文件的多种方法 +================================== + +1. 当前目录下的文件 +------------------- + +最经典的方法,删除当前目录下的所有类型的文件 + +.. code:: shell + + rm -f \* + +用find命令查找普通文件并删除or用find命令的处理动作将其删除 + +.. code:: shell + + find . -type f -delete + + # 或者 + + find . -type f -exec rm -f {} \ + +用于参数列表过长;要删除的文件太多 + +.. code:: shell + + find . -type f | xargs rm -f + +删除全部普通文件 + +.. code:: shell + + rm-f `find . -type f` + +用for循环语句删除当前目录下的所有类型的文件 + +.. code:: shell + + for delete in `ls -l`;do rm -f \* ;done + +2. 指定目录下的文件 +------------------- + +两种方法 + +1. 把上面的 ``.`` 全部改为指定的目录 +2. 在上面的命令之前,都加上 ``cd ${dest_dir};`` ,并且命令后,加上 + ``cd -`` From 0b340eea323fe4c9a57d9a049a9d5230f756ba86 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 5 May 2020 12:04:47 +0800 Subject: [PATCH 053/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_18.md | 4 +-- source/c04/c04_18.rst | 63 ++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 66a3509..a0a2339 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -287,7 +287,7 @@ shift + command + g **操作文件与文件夹** -```sh e llsh el +```shell # enter 重命名文件夹 @@ -339,7 +339,7 @@ command + option + c **在 iTerm2中打开访达** -``` +```shell # 在当前目录打开 open . diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 9759ec8..da93952 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -302,54 +302,43 @@ iText,精准的 OCR 文字识别工具。 **操作文件与文件夹** -\```sh e llsh el # enter 重命名文件夹 - -选中所有文件,并将这些文件归档入一个新的文件夹 -============================================== - -右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 - -选择 -==== - -点击 -> 拖拽 如果想要取消选中,就 command + 点击 - -打开最近使用过的文件夹 -====================== - -comand + shift + f +.. code:: shell -显示/隐藏文件 -============= + # enter + 重命名文件夹 -command + shift + . + # 选中所有文件,并将这些文件归档入一个新的文件夹 + 右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 -查看文件/夹 详情 -================ -command + i + # 选择 + 点击 -> 拖拽 + 如果想要取消选中,就 command + 点击 -mac 中拷贝和复制不一样 -====================== -command + c 拷贝 command + d -复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option command + v -粘贴 command + option + v 称动 ,或者使用鼠标拖动 + # 打开最近使用过的文件夹 + comand + shift + f -可以设置搜索的范围 -================== + # 显示/隐藏文件 + command + shift + . -command + f + # 查看文件/夹 详情 + command + i -新建文件夹 -========== + # mac 中拷贝和复制不一样 + command + c 拷贝 + command + d 复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option + command + v 粘贴 + command + option + v 称动 ,或者使用鼠标拖动 -command + shift + n + # 可以设置搜索的范围 + command + f -关闭访达标签页,如果是最后一个标签页,则关闭访达 -================================================ + # 新建文件夹 + command + shift + n -command + w \``\` + # 关闭访达标签页,如果是最后一个标签页,则关闭访达 + command + w **定制服务(复制文件路径)** @@ -365,7 +354,7 @@ command + w \``\` **在 iTerm2中打开访达** -:: +.. code:: shell # 在当前目录打开 open . From fba3fdc9585053712766aa0d8f408b6e84d728f9 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 6 May 2020 08:45:25 +0800 Subject: [PATCH 054/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0<=E7=BA=BF=E7=A8=8B=E5=AE=89=E5=85=A8>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + source/c02/c02_14.md | 181 +++++++++++++++++++++++++++++++++++++ source/c02/c02_14.rst | 201 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 source/c02/c02_14.md create mode 100644 source/c02/c02_14.rst diff --git a/README.md b/README.md index 7384a9f..6f84cad 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ - 2.11 [实战异步IO框架:asyncio 下篇](http://python.iswbm.com/en/latest/c02/c02_11.html) - 2.12 [生成器与协程,你分清了吗?](http://python.iswbm.com/en/latest/c02/c02_12.html) - 2.13 [I/O多路复用:select/poll/epoll](http://python.iswbm.com/en/latest/c02/c02_13.html) +- 2.14 [浅谈线程安全那些事儿](http://python.iswbm.com/en/latest/c02/c02_14.html) ## 第三章:高级编程 - 3.1 [装饰器进阶用法详解](http://python.iswbm.com/en/latest/c03/c03_01.html) diff --git a/source/c02/c02_14.md b/source/c02/c02_14.md new file mode 100644 index 0000000..a731121 --- /dev/null +++ b/source/c02/c02_14.md @@ -0,0 +1,181 @@ +# 2.14 浅谈线程安全那些事儿 + +在并发编程时,如果多个线程访问同一资源,我们需要保证访问的时候不会产生冲突,数据修改不会发生错误,这就是我们常说的 **线程安全** 。 + +那什么情况下,访问数据时是安全的?什么情况下,访问数据是不安全的?如何知道你的代码是否线程安全?要如何访问数据才能保证数据的安全? + +本篇文章会一一回答你的问题。 + +## 1. 线程不安全是怎样的? + +要搞清楚什么是线程安全,就要先了解线程不安全是什么样的。 + +比如下面这段代码,开启两个线程,对全局变量 number 各自增 10万次,每次自增 1。 + +```python +from threading import Thread, Lock + +number = 0 + +def target(): + global number + for _ in range(1000000): + number += 1 + +thread_01 = Thread(target=target) +thread_02 = Thread(target=target) +thread_01.start() +thread_02.start() + +thread_01.join() +thread_02.join() + +print(number) +``` + +正常我们的预期输出结果,一个线程自增100万,两个线程就自增 200 万嘛,输出肯定为 2000000 。 + +可事实却并不是你想的那样,不管你运行多少次,每次输出的结果都会不一样,而这些输出结果都有一个特点是,都小于 200 万。 + +以下是执行三次的结果 + +```python +1459782 +1379891 +1432921 +``` + +这种现象就是线程不安全,究其根因,其实是我们的操作 `number += 1` ,不是原子操作,才会导致的线程不安全。 + + + +## 2. 什么是原子操作? + +原子操作(**atomic operation**),指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会切换到其他线程。 + +它有点类似数据库中的 **事务**。 + +在 Python 的[官方文档](https://docs.python.org/3.5/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe)上,列出了一些常见原子操作 + +```python +L.append(x) +L1.extend(L2) +x = L[i] +x = L.pop() +L1[i:j] = L2 +L.sort() +x = y +x.field = y +D[x] = y +D1.update(D2) +D.keys() +``` + +而下面这些就不是原子操作 + +```python +i = i+1 +L.append(L[-1]) +L[i] = L[j] +D[x] = D[x] + 1 +``` + +像上面的我使用自增操作 `number += 1`,其实等价于 `number = number + 1`,可以看到这种可以拆分成多个步骤(先读取相加再赋值),并不属于原子操作。 + +这样就导致多个线程同时读取时,有可能读取到同一个 number 值,读取两次,却只加了一次,最终导致自增的次数小于预期。 + +当我们还是无法确定我们的代码是否具有原子性的时候,可以尝试通过 `dis` 模块里的 dis 函数来查看 + +![](http://image.iswbm.com/20200506080445.png) + +当我们执行这段代码时,可以看到 `number += 1` 这一行代码,由两条字节码实现。 + +- `BINARY_ADD` :将两个值相加 +- `STORE_GLOBAL`: 将相加后的值重新赋值 + +每一条字节码指令都是一个整体,无法分割,他实现的效果也就是我们所说的原子操作。 + +当一行代码被分成多条字节码指令的时候,就代表在线程线程切换时,有可能只执行了一条字节码指令,此时若这行代码里有被多个线程共享的变量或资源时,并且拆分的多条指令里有对于这个共享变量的写操作,就会发生数据的冲突,导致数据的不准确。 + +为了对比,我们从上面列表的原子操作拿一个出来也来试试,是不是真如官网所说的原子操作。 + +这里我拿字典的 update 操作举例,代码和执行过程如下图 + +![](http://image.iswbm.com/20200506081541.png) + +从截图里可以看到,`info.update(new)` 虽然也分为好几个操作 + +- `LOAD_GLOBAL`:加载全局变量 +- `LOAD_ATTR`: 加载属性,获取 update 方法 +- `LOAD_FAST`:加载 new 变量 +- `CALL_FUNCTION`:调用函数 +- `POP_TOP`:执行更新操作 + +但我们要知道真正会引导数据冲突的,其实不是读操作,而是写操作。 + +上面这么多字节码指令,写操作都只有一个(**POP_TOP**),因此字典的 update 方法是原子操作。 + +## 3. 实现人工原子操作 + +在多线程下,我们并不能保证我们的代码具有原子性,因此如何让我们的代码变得具有 “原子性” ,就是一件很重要的事。 + +方法也很简单,就是当你在访问一个多线程间共享的资源时,加锁可以实现类似原子操作的效果,一个代码要嘛不执行,执行了的话就要执行完毕,才能接受线程的调度。 + +因此,我们使用加锁的方法,对例子一进行一些修改,使其具备原子性。 + +```python +from threading import Thread, Lock + + +number = 0 +lock = Lock() + + +def target(): + global number + for _ in range(1000000): + with lock: + number += 1 + +thread_01 = Thread(target=target) +thread_02 = Thread(target=target) +thread_01.start() +thread_02.start() + +thread_01.join() +thread_02.join() + +print(number) +``` + +此时,不管你执行多少遍,输出都是 2000000. + + + +## 4. 为什么 Queue 是线程安全的? + +Python 的 threading 模块里的消息通信机制主要有如下三种: + +1. Event +2. Condition +3. Queue + +使用最多的是 Queue,而我们都知道它是线程安全的。当我们对它进行写入和提取的操作不会被中断而导致错误,这也是我们在使用队列时,不需要额外加锁的原因。 + +他是如何做到的呢? + +其根本原因就是 Queue 实现了锁原语,因此他能像第三节那样实现人工原子操作。 + +> 原语指由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性;即原语的执行必须是连续的,在执行过程中不允许被中断。 + + + +## 参考文章: + +https://zhuanlan.zhihu.com/p/34150765 + +https://juejin.im/post/5b129a1be51d45068a6c91d4#comment + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c02/c02_14.rst b/source/c02/c02_14.rst new file mode 100644 index 0000000..bfcf597 --- /dev/null +++ b/source/c02/c02_14.rst @@ -0,0 +1,201 @@ +2.14 浅谈线程安全那些事儿 +========================= + +在并发编程时,如果多个线程访问同一资源,我们需要保证访问的时候不会产生冲突,数据修改不会发生错误,这就是我们常说的 +**线程安全** 。 + +那什么情况下,访问数据时是安全的?什么情况下,访问数据是不安全的?如何知道你的代码是否线程安全?要如何访问数据才能保证数据的安全? + +本篇文章会一一回答你的问题。 + +1. 线程不安全是怎样的? +----------------------- + +要搞清楚什么是线程安全,就要先了解线程不安全是什么样的。 + +比如下面这段代码,开启两个线程,对全局变量 number 各自增 +10万次,每次自增 1。 + +.. code:: python + + from threading import Thread, Lock + + number = 0 + + def target(): + global number + for _ in range(1000000): + number += 1 + + thread_01 = Thread(target=target) + thread_02 = Thread(target=target) + thread_01.start() + thread_02.start() + + thread_01.join() + thread_02.join() + + print(number) + +正常我们的预期输出结果,一个线程自增100万,两个线程就自增 200 +万嘛,输出肯定为 2000000 。 + +可事实却并不是你想的那样,不管你运行多少次,每次输出的结果都会不一样,而这些输出结果都有一个特点是,都小于 +200 万。 + +以下是执行三次的结果 + +.. code:: python + + 1459782 + 1379891 + 1432921 + +这种现象就是线程不安全,究其根因,其实是我们的操作 ``number += 1`` +,不是原子操作,才会导致的线程不安全。 + +2. 什么是原子操作? +------------------- + +原子操作(\ **atomic +operation**\ ),指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会切换到其他线程。 + +它有点类似数据库中的 **事务**\ 。 + +在 Python +的\ `官方文档 `__\ 上,列出了一些常见原子操作 + +.. code:: python + + L.append(x) + L1.extend(L2) + x = L[i] + x = L.pop() + L1[i:j] = L2 + L.sort() + x = y + x.field = y + D[x] = y + D1.update(D2) + D.keys() + +而下面这些就不是原子操作 + +.. code:: python + + i = i+1 + L.append(L[-1]) + L[i] = L[j] + D[x] = D[x] + 1 + +像上面的我使用自增操作 ``number += 1``\ ,其实等价于 +``number = number + 1``\ ,可以看到这种可以拆分成多个步骤(先读取相加再赋值),并不属于原子操作。 + +这样就导致多个线程同时读取时,有可能读取到同一个 number +值,读取两次,却只加了一次,最终导致自增的次数小于预期。 + +当我们还是无法确定我们的代码是否具有原子性的时候,可以尝试通过 ``dis`` +模块里的 dis 函数来查看 + +|image0| + +当我们执行这段代码时,可以看到 ``number += 1`` +这一行代码,由两条字节码实现。 + +- ``BINARY_ADD`` :将两个值相加 +- ``STORE_GLOBAL``\ : 将相加后的值重新赋值 + +每一条字节码指令都是一个整体,无法分割,他实现的效果也就是我们所说的原子操作。 + +当一行代码被分成多条字节码指令的时候,就代表在线程线程切换时,有可能只执行了一条字节码指令,此时若这行代码里有被多个线程共享的变量或资源时,并且拆分的多条指令里有对于这个共享变量的写操作,就会发生数据的冲突,导致数据的不准确。 + +为了对比,我们从上面列表的原子操作拿一个出来也来试试,是不是真如官网所说的原子操作。 + +这里我拿字典的 update 操作举例,代码和执行过程如下图 + +|image1| + +从截图里可以看到,\ ``info.update(new)`` 虽然也分为好几个操作 + +- ``LOAD_GLOBAL``\ :加载全局变量 +- ``LOAD_ATTR``\ : 加载属性,获取 update 方法 +- ``LOAD_FAST``\ :加载 new 变量 +- ``CALL_FUNCTION``\ :调用函数 +- ``POP_TOP``\ :执行更新操作 + +但我们要知道真正会引导数据冲突的,其实不是读操作,而是写操作。 + +上面这么多字节码指令,写操作都只有一个(\ **POP_TOP**\ ),因此字典的 +update 方法是原子操作。 + +3. 实现人工原子操作 +------------------- + +在多线程下,我们并不能保证我们的代码具有原子性,因此如何让我们的代码变得具有 +“原子性” ,就是一件很重要的事。 + +方法也很简单,就是当你在访问一个多线程间共享的资源时,加锁可以实现类似原子操作的效果,一个代码要嘛不执行,执行了的话就要执行完毕,才能接受线程的调度。 + +因此,我们使用加锁的方法,对例子一进行一些修改,使其具备原子性。 + +.. code:: python + + from threading import Thread, Lock + + + number = 0 + lock = Lock() + + + def target(): + global number + for _ in range(1000000): + with lock: + number += 1 + + thread_01 = Thread(target=target) + thread_02 = Thread(target=target) + thread_01.start() + thread_02.start() + + thread_01.join() + thread_02.join() + + print(number) + +此时,不管你执行多少遍,输出都是 2000000. + +4. 为什么 Queue 是线程安全的? +------------------------------ + +Python 的 threading 模块里的消息通信机制主要有如下三种: + +1. Event +2. Condition +3. Queue + +使用最多的是 +Queue,而我们都知道它是线程安全的。当我们对它进行写入和提取的操作不会被中断而导致错误,这也是我们在使用队列时,不需要额外加锁的原因。 + +他是如何做到的呢? + +其根本原因就是 Queue +实现了锁原语,因此他能像第三节那样实现人工原子操作。 + + 原语指由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性;即原语的执行必须是连续的,在执行过程中不允许被中断。 + +参考文章: +---------- + +https://zhuanlan.zhihu.com/p/34150765 + +https://juejin.im/post/5b129a1be51d45068a6c91d4#comment + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200506080445.png +.. |image1| image:: http://image.iswbm.com/20200506081541.png + From 18baf1ff73ecab82f4981c5f707aa0cbf204e49b Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 6 May 2020 08:45:57 +0800 Subject: [PATCH 055/147] =?UTF-8?q?Update=EF=BC=9A=E6=B6=A6=E5=8C=96?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c02/c02_02.md | 40 +++++++++++++++---------------- source/c02/c02_02.rst | 55 ++++++++++++++++++++++--------------------- source/c02/c02_03.md | 41 +++++++++++++++----------------- source/c02/c02_03.rst | 52 ++++++++++++++++++++-------------------- source/c02/c02_04.md | 4 ++-- source/c02/c02_04.rst | 4 ++-- 6 files changed, 95 insertions(+), 101 deletions(-) diff --git a/source/c02/c02_02.md b/source/c02/c02_02.md index 915af86..56586f4 100644 --- a/source/c02/c02_02.md +++ b/source/c02/c02_02.md @@ -7,12 +7,13 @@ 经过总结,Python创建多线程主要有如下两种方法: -- 函数 + +- - 函数 - 类 接下来,我们就来揭开多线程的神秘面纱。 -## 2.2.1 用函数创建多线程 +## 1. 用函数创建多线程 在Python3中,Python提供了一个内置模块 `threading.Thread`,可以很方便地让我们创建多线程。 @@ -27,19 +28,19 @@ import time from threading import Thread # 自定义线程函数。 -def main(name="Python"): +def target(name="Python"): for i in range(2): print("hello", name) time.sleep(1) # 创建线程01,不指定参数 -thread_01 = Thread(target=main) +thread_01 = Thread(target=target) # 启动线程01 thread_01.start() # 创建线程02,指定参数,注意逗号 -thread_02 = Thread(target=main, args=("MING",)) +thread_02 = Thread(target=target, args=("MING",)) # 启动线程02 thread_02.start() ``` @@ -51,34 +52,34 @@ hello Python hello MING ``` -是不是超级简单呢?别急,下面也是一样简单。 - -## 2.2.2 用类创建多线程 +## 2. 用类创建多线程 相比较函数而言,使用类创建线程,会比较麻烦一点。 首先,我们要自定义一个类,对于这个类有两点要求, + - 必须继承 `threading.Thread` 这个父类; -- 必须覆写 `run` 方法。 +- 必须复写 `run` 方法。 这里的 `run` 方法,和我们上面`线程函数`的性质是一样的,可以写我们的业务逻辑程序。在 `start()` 后将会调用。 来看一下例子 为了方便对比,`run`函数我复用上面的`main`。 + ```python import time from threading import Thread class MyThread(Thread): - def __init__(self, name="Python"): - # 注意,super().__init__() 一定要写 - # 而且要写在最前面,否则会报错。 + def __init__(self, type="Python"): + # 注意:super().__init__() 必须写 + # 且最好写在第一行 super().__init__() - self.name=name + self.type=type def run(self): for i in range(2): - print("hello", self.name) + print("hello", self.type) time.sleep(1) if __name__ == '__main__': @@ -98,18 +99,15 @@ hello Python hello MING ``` -## 2.2.3 多线程函数必讲 - -学完了两种创建线程的方式,你一定会惊叹,咋么这么简单,一点难度都没有。 - -其实不然,上面我们的`线程函数` 为了方便理解,都使用的最简单的代码逻辑。而在实际使用当中,多线程运行期间,还会出现诸多问题,只是我们现在还没体会到它的复杂而已。 +## 3. 线程对象的方法 -不过,你也不必担心,在后面的章节中,我会带着大家一起来探讨一下,都有哪些难题,应该如何解决。 +上面介绍了当前 Python 中创建线程两种主要方法。 -磨刀不误吹柴工,我们首先得来认识一下,Python给我们提供的 `Thread` 都有哪些函数和属性,实现哪些功能。学习完这些,在后期的学习中,我们才能更加得以应手。 +创建线程是件很容易的事,但要想用好线程,还需要学习线程对象的几个函数。 经过我的总结,大约常用的方法有如下这些: ```python +# 如上所述,创建一个线程 t=Thread(target=func) # 启动子线程 diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index 526c577..eac8902 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -5,12 +5,18 @@ 今天的内容会比较基础,主要是为了让新手也能无障碍地阅读,所以还是要再巩固下基础。学完了基础,你们也就能很顺畅地跟着我的思路理解以后的文章。 -经过总结,Python创建多线程主要有如下两种方法: - 函数 - 类 +经过总结,Python创建多线程主要有如下两种方法: + +- + + - 函数 + +- 类 接下来,我们就来揭开多线程的神秘面纱。 -2.2.1 用函数创建多线程 ----------------------- +1. 用函数创建多线程 +------------------- 在Python3中,Python提供了一个内置模块 ``threading.Thread``\ ,可以很方便地让我们创建多线程。 @@ -28,19 +34,19 @@ from threading import Thread # 自定义线程函数。 - def main(name="Python"): + def target(name="Python"): for i in range(2): print("hello", name) time.sleep(1) # 创建线程01,不指定参数 - thread_01 = Thread(target=main) + thread_01 = Thread(target=target) # 启动线程01 thread_01.start() # 创建线程02,指定参数,注意逗号 - thread_02 = Thread(target=main, args=("MING",)) + thread_02 = Thread(target=target, args=("MING",)) # 启动线程02 thread_02.start() @@ -53,15 +59,15 @@ hello Python hello MING -是不是超级简单呢?别急,下面也是一样简单。 - -2.2.2 用类创建多线程 --------------------- +2. 用类创建多线程 +----------------- 相比较函数而言,使用类创建线程,会比较麻烦一点。 -首先,我们要自定义一个类,对于这个类有两点要求, - 必须继承 -``threading.Thread`` 这个父类; - 必须覆写 ``run`` 方法。 +首先,我们要自定义一个类,对于这个类有两点要求, + +- 必须继承 ``threading.Thread`` 这个父类; +- 必须复写 ``run`` 方法。 这里的 ``run`` 方法,和我们上面\ ``线程函数``\ 的性质是一样的,可以写我们的业务逻辑程序。在 @@ -75,15 +81,15 @@ from threading import Thread class MyThread(Thread): - def __init__(self, name="Python"): - # 注意,super().__init__() 一定要写 - # 而且要写在最前面,否则会报错。 + def __init__(self, type="Python"): + # 注意:super().__init__() 必须写 + # 且最好写在第一行 super().__init__() - self.name=name + self.type=type def run(self): for i in range(2): - print("hello", self.name) + print("hello", self.type) time.sleep(1) if __name__ == '__main__': @@ -104,23 +110,18 @@ hello Python hello MING -2.2.3 多线程函数必讲 --------------------- - -学完了两种创建线程的方式,你一定会惊叹,咋么这么简单,一点难度都没有。 - -其实不然,上面我们的\ ``线程函数`` -为了方便理解,都使用的最简单的代码逻辑。而在实际使用当中,多线程运行期间,还会出现诸多问题,只是我们现在还没体会到它的复杂而已。 +3. 线程对象的方法 +----------------- -不过,你也不必担心,在后面的章节中,我会带着大家一起来探讨一下,都有哪些难题,应该如何解决。 +上面介绍了当前 Python 中创建线程两种主要方法。 -磨刀不误吹柴工,我们首先得来认识一下,Python给我们提供的 ``Thread`` -都有哪些函数和属性,实现哪些功能。学习完这些,在后期的学习中,我们才能更加得以应手。 +创建线程是件很容易的事,但要想用好线程,还需要学习线程对象的几个函数。 经过我的总结,大约常用的方法有如下这些: .. code:: python + # 如上所述,创建一个线程 t=Thread(target=func) # 启动子线程 diff --git a/source/c02/c02_03.md b/source/c02/c02_03.md index e195de4..5414f41 100644 --- a/source/c02/c02_03.md +++ b/source/c02/c02_03.md @@ -2,25 +2,19 @@ --- -## 2.3.1 何为Lock( 锁 )? +## 1. 什么是锁? +在开发中,**锁** 可以理解为通行证。 -何为 `Lock`( 锁 ),在网上找了很久,也没有找到合适的定义。可能 `锁` 这个词已经足够直白了,不需要再解释了。 +当你对一段逻辑代码加锁时,意味着在同一时间有且仅能有一个线程在执行这段代码。 -但是,对于新手来说,我还是要说下我的理解。 +在 Python 中的锁可以分为两种: -我自己想了个生活中例子来看下。 +1. 互斥锁 +2. 可重入锁 -有一个奇葩的房东,他家里有两个房间想要出租。这个房东很抠门,家里有两个房间,但却只有一把锁,不想另外花钱是去买另一把锁,也不让租客自己加锁。这样租客只有,先租到的那个人才能分配到锁。X先生,率先租到了房子,并且拿到了锁。而后来者Y先生,由于锁已经已经被X取走了,自己拿不到锁,也不能自己加锁,Y就不愿意了。也就不租了,换作其他人也一样,没有人会租第二个房间,直到X先生退租,把锁还给房东,可以让其他房客来取。第二间房间才能租出去。 - -换句话说,就是房东同时只能出租一个房间,一但有人租了一个房间,拿走了唯一的锁,就没有人再在租另一间房了。 - -回到我们的线程中来,有两个线程A和B,A和B里的程序都加了同一个锁对象,当线程A率先执行到lock.acquire()(拿到全局唯一的锁后),线程B只能等到线程A释放锁lock.release()后(归还锁)才能运行lock.acquire()(拿到全局唯一的锁)并执行后面的代码。 -这个例子,是不是让你清楚了什么是锁呢? - - -## 2.3.2 如何使用Lock( 锁 )? +## 2. 互斥锁的使用 来简单看下代码,学习如何加锁,获取钥匙,释放锁。 ```python import threading @@ -50,7 +44,7 @@ with lock: ``` `with` 语句会在这个代码块执行前自动获取锁,在执行结束后自动释放锁。 -## 2.3.3 为何要使用锁? +## 3. 为何要使用锁? 你现在肯定还是一脸懵逼,这么麻烦,我不用锁不行吗?有的时候还真不行。 那么为了说明锁存在的意义。我们分别来看下,不用锁的情形有怎样的问题。 @@ -161,9 +155,9 @@ job2 110 为了避免大家忘记释放锁,后面的例子,我将都使用with上下文管理器来加锁。大家注意一下。 -## 2.3.4 可重入锁(RLock) +## 4. 可重入锁(RLock) -有时候在同一个线程中,我们可能会多次请求同一资源(就是,获取同一锁钥匙),俗称锁嵌套。 +有时候在同一个线程中,我们可能会多次请求同一资源,俗称锁嵌套。 如果还是按照常规的做法,会造成死锁的。比如,下面这段代码,你可以试着运行一下。会发现并没有输出结果。 ```python @@ -181,9 +175,12 @@ def main(): t1 = threading.Thread(target=main) t1.start() ``` -是因为,第二次获取锁时,发现锁已经被同一线程的人拿走了。自己也就理所当然,拿不到锁,程序就卡住了。 +是因为第二次获取锁(通行证)时,发现锁(通行证)已经被同一线程的人拿走了,拿东西总有个先来后到,别人拿走了,你要想用,你就得干等着,直到有人归还锁(通行证),假如别人一直不归还,那程序就会在这里一直阻塞。 + +上面的代码中,使用了嵌套锁,在锁还没有释放的时候,又再一次请求锁,这就当然会造成死锁了。 + +那么如何解决这个问题呢? -那么如何解决这个问题呢。 `threading`模块除了提供`Lock`锁之外,还提供了一种可重入锁`RLock`,专门来处理这个问题。 ```python @@ -215,14 +212,14 @@ t1.start() 9 10 ``` -需要注意的是,可重入锁,只在同一线程里,放松对锁钥匙的获取,其他与`Lock`并无二致。 +需要注意的是,可重入锁(RLock),只在同一线程里放松对锁(通行证)的获取,意思是,只要在同一线程里,程序就当你是同一个人,这个锁就可以复用,其他的话与`Lock`并无区别。 -## 2.3.5 防止死锁的加锁机制 +## 5. 防止死锁的加锁机制 在编写多线程程序时,可能无意中就会写了一个死锁。可以说,死锁的形式有多种多样,但是本质都是相同的,都是对资源不合理竞争的结果。 以本人的经验总结,死锁通常以下几种 -- 同一线程,嵌套获取同把锁,造成死锁。 +- 同一线程,嵌套获取同把互斥锁,造成死锁。 - 多个线程,不按顺序同时获取多个锁。造成死锁 对于第一种,上面已经说过了,使用可重入锁。 @@ -306,7 +303,7 @@ t2.start() 看到没有,表面上`thread_1`的先获取锁x,再获取锁`y`,而`thread_2`是先获取锁`y`,再获取`x`。 但是实际上,`acquire`函数,已经对`x`,`y`两个锁进行了排序。所以`thread_1`,`hread_2`都是以同一顺序来获取锁的,是不是造成死锁的。 -## 2.3.6 饱受争议的GIL(全局锁) +## 6. 饱受争议的GIL(全局锁) 在第一章的时候,我就和大家介绍到,多线程和多进程是不一样的。 diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index 46b92cb..6b7165b 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -3,25 +3,20 @@ -------------- -2.3.1 何为Lock( 锁 )? ------------------------- +1. 什么是锁? +------------- -何为 ``Lock``\ ( 锁 ),在网上找了很久,也没有找到合适的定义。可能 ``锁`` -这个词已经足够直白了,不需要再解释了。 +在开发中,\ **锁** 可以理解为通行证。 -但是,对于新手来说,我还是要说下我的理解。 +当你对一段逻辑代码加锁时,意味着在同一时间有且仅能有一个线程在执行这段代码。 -我自己想了个生活中例子来看下。 +在 Python 中的锁可以分为两种: -有一个奇葩的房东,他家里有两个房间想要出租。这个房东很抠门,家里有两个房间,但却只有一把锁,不想另外花钱是去买另一把锁,也不让租客自己加锁。这样租客只有,先租到的那个人才能分配到锁。X先生,率先租到了房子,并且拿到了锁。而后来者Y先生,由于锁已经已经被X取走了,自己拿不到锁,也不能自己加锁,Y就不愿意了。也就不租了,换作其他人也一样,没有人会租第二个房间,直到X先生退租,把锁还给房东,可以让其他房客来取。第二间房间才能租出去。 +1. 互斥锁 +2. 可重入锁 -换句话说,就是房东同时只能出租一个房间,一但有人租了一个房间,拿走了唯一的锁,就没有人再在租另一间房了。 - -回到我们的线程中来,有两个线程A和B,A和B里的程序都加了同一个锁对象,当线程A率先执行到lock.acquire()(拿到全局唯一的锁后),线程B只能等到线程A释放锁lock.release()后(归还锁)才能运行lock.acquire()(拿到全局唯一的锁)并执行后面的代码。 -这个例子,是不是让你清楚了什么是锁呢? - -2.3.2 如何使用Lock( 锁 )? ----------------------------- +2. 互斥锁的使用 +--------------- 来简单看下代码,学习如何加锁,获取钥匙,释放锁。 @@ -55,8 +50,8 @@ lock.release()必须成对出现。否则就有可能造成死锁。 ``with`` 语句会在这个代码块执行前自动获取锁,在执行结束后自动释放锁。 -2.3.3 为何要使用锁? --------------------- +3. 为何要使用锁? +----------------- 你现在肯定还是一脸懵逼,这么麻烦,我不用锁不行吗?有的时候还真不行。 @@ -178,10 +173,10 @@ lock.release()必须成对出现。否则就有可能造成死锁。 为了避免大家忘记释放锁,后面的例子,我将都使用with上下文管理器来加锁。大家注意一下。 -2.3.4 可重入锁(RLock) ------------------------ +4. 可重入锁(RLock) +-------------------- -有时候在同一个线程中,我们可能会多次请求同一资源(就是,获取同一锁钥匙),俗称锁嵌套。 +有时候在同一个线程中,我们可能会多次请求同一资源,俗称锁嵌套。 如果还是按照常规的做法,会造成死锁的。比如,下面这段代码,你可以试着运行一下。会发现并没有输出结果。 @@ -201,9 +196,12 @@ lock.release()必须成对出现。否则就有可能造成死锁。 t1 = threading.Thread(target=main) t1.start() -是因为,第二次获取锁时,发现锁已经被同一线程的人拿走了。自己也就理所当然,拿不到锁,程序就卡住了。 +是因为第二次获取锁(通行证)时,发现锁(通行证)已经被同一线程的人拿走了,拿东西总有个先来后到,别人拿走了,你要想用,你就得干等着,直到有人归还锁(通行证),假如别人一直不归还,那程序就会在这里一直阻塞。 + +上面的代码中,使用了嵌套锁,在锁还没有释放的时候,又再一次请求锁,这就当然会造成死锁了。 + +那么如何解决这个问题呢? -那么如何解决这个问题呢。 ``threading``\ 模块除了提供\ ``Lock``\ 锁之外,还提供了一种可重入锁\ ``RLock``\ ,专门来处理这个问题。 .. code:: python @@ -238,15 +236,15 @@ lock.release()必须成对出现。否则就有可能造成死锁。 9 10 -需要注意的是,可重入锁,只在同一线程里,放松对锁钥匙的获取,其他与\ ``Lock``\ 并无二致。 +需要注意的是,可重入锁(RLock),只在同一线程里放松对锁(通行证)的获取,意思是,只要在同一线程里,程序就当你是同一个人,这个锁就可以复用,其他的话与\ ``Lock``\ 并无区别。 -2.3.5 防止死锁的加锁机制 ------------------------- +5. 防止死锁的加锁机制 +--------------------- 在编写多线程程序时,可能无意中就会写了一个死锁。可以说,死锁的形式有多种多样,但是本质都是相同的,都是对资源不合理竞争的结果。 以本人的经验总结,死锁通常以下几种 - -同一线程,嵌套获取同把锁,造成死锁。 - +同一线程,嵌套获取同把互斥锁,造成死锁。 - 多个线程,不按顺序同时获取多个锁。造成死锁 对于第一种,上面已经说过了,使用可重入锁。 @@ -332,8 +330,8 @@ lock.release()必须成对出现。否则就有可能造成死锁。 看到没有,表面上\ ``thread_1``\ 的先获取锁x,再获取锁\ ``y``\ ,而\ ``thread_2``\ 是先获取锁\ ``y``\ ,再获取\ ``x``\ 。 但是实际上,\ ``acquire``\ 函数,已经对\ ``x``\ ,\ ``y``\ 两个锁进行了排序。所以\ ``thread_1``\ ,\ ``hread_2``\ 都是以同一顺序来获取锁的,是不是造成死锁的。 -2.3.6 饱受争议的GIL(全局锁) ------------------------------ +6. 饱受争议的GIL(全局锁) +-------------------------- 在第一章的时候,我就和大家介绍到,多线程和多进程是不一样的。 diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 1f911ef..a3d6d83 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -15,7 +15,7 @@ - threading.Condition - queue.Queue -先抛出结论,接下来我们来一一探讨下。 +接下来我们来一一探讨下。 --- @@ -167,7 +167,7 @@ seeker: 被你找到了,哎~~~ ## 2.4.3 Queue队列 -终于到了我们今天的主角了。 +最后一个,队列,它是本节的重点,因为它是我们日常开发中最使用频率最高的。 从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue 对象,这些线程通过使用`put()` 和 `get()` 操作来向队列中添加或者删除元素。 diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 7fd675b..183fd66 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -14,7 +14,7 @@ 经过我的总结,线程中通信方法大致有如下三种: - threading.Event - threading.Condition - queue.Queue -先抛出结论,接下来我们来一一探讨下。 +接下来我们来一一探讨下。 -------------- @@ -180,7 +180,7 @@ Condition和Event 是类似的,并没有多大区别。 2.4.3 Queue队列 --------------- -终于到了我们今天的主角了。 +最后一个,队列,它是本节的重点,因为它是我们日常开发中最使用频率最高的。 从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue From 01317f87bfd9801d5df2cbbcdc14cd78615c9f22 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 6 May 2020 22:48:15 +0800 Subject: [PATCH 056/147] =?UTF-8?q?Update=EF=BC=9A=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- source/c02/c02_04.md | 16 +++++ source/c02/c02_04.rst | 20 ++++++ source/c02/c02_06.md | 137 ++++++++++++++++++++++++++---------------- source/c02/c02_06.rst | 136 +++++++++++++++++++++++++---------------- source/c02/c02_14.md | 2 +- source/c02/c02_14.rst | 2 +- 7 files changed, 208 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 6f84cad..cc71b2d 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ - 2.3 [谈谈线程中的“锁机制”](http://python.iswbm.com/en/latest/c02/c02_03.html) - 2.4 [线程消息通信机制](http://python.iswbm.com/en/latest/c02/c02_04.html) - 2.5 [线程中的信息隔离](http://python.iswbm.com/en/latest/c02/c02_05.html) -- 2.6 [如何创建线程池](http://python.iswbm.com/en/latest/c02/c02_06.html) +- 2.6 [线程池与进程池的创建](http://python.iswbm.com/en/latest/c02/c02_06.html) - 2.7 [从生成器使用入门协程](http://python.iswbm.com/en/latest/c02/c02_07.html) - 2.8 [深入理解yield from语法](http://python.iswbm.com/en/latest/c02/c02_08.html) - 2.9 [初识异步IO框架:asyncio 上篇](http://python.iswbm.com/en/latest/c02/c02_09.html) diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index a3d6d83..5b90ec1 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -255,6 +255,22 @@ teacher.call('小亮') 小亮:到! ``` +其实 queue 还有一个很重要的方法,Queue.task_done() + +如果不明白它的原理,我们在写程序,就很有可能卡死。 + +当我们使用 Queue.get() 从队列取出数据后,这个数据有没有被正常消费,是很重要的。 + +如果数据没有被正常消费,那么Queue会认为这个任务还在执行中,此时你使用 Queue.join() 会一直阻塞,即使此时你的队列里已经没有消息了。 + +那么如何解决这种一直阻塞的问题呢? + +就是在我们正常消费完数据后,记得调用一下 Queue.task_done(),说明队列这个任务已经结束了。 + +当队列内部的任务计数器归于零时,调用 Queue.join() 就不会再阻塞了。 + +要理解这个过程,请参考 http://python.iswbm.com/en/latest/c02/c02_06.html 里自定义线程池的的例子。 + ## 2.4.4 总结一下 学习了以上三种通信方法,我们很容易就能发现`Event` 和 `Condition` 是threading模块原生提供的模块,原理简单,功能单一,它能发送 `True` 和 `False` 的指令,所以只能适用于某些简单的场景中。 diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 183fd66..3649ea0 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -276,6 +276,26 @@ Condition和Event 是类似的,并没有多大区别。 老师:小亮来了没? 小亮:到! +其实 queue 还有一个很重要的方法,Queue.task_done() + +如果不明白它的原理,我们在写程序,就很有可能卡死。 + +当我们使用 Queue.get() +从队列取出数据后,这个数据有没有被正常消费,是很重要的。 + +如果数据没有被正常消费,那么Queue会认为这个任务还在执行中,此时你使用 +Queue.join() 会一直阻塞,即使此时你的队列里已经没有消息了。 + +那么如何解决这种一直阻塞的问题呢? + +就是在我们正常消费完数据后,记得调用一下 +Queue.task_done(),说明队列这个任务已经结束了。 + +当队列内部的任务计数器归于零时,调用 Queue.join() 就不会再阻塞了。 + +要理解这个过程,请参考 http://python.iswbm.com/en/latest/c02/c02_06.html +里自定义线程池的的例子。 + 2.4.4 总结一下 -------------- diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index 9c4c8fb..743fddc 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -1,13 +1,15 @@ -# 2.6 如何创建线程池 +# 2.6 线程池与进程池的创建 --- >**友情提醒**: >本系列所有的代码均在Python3下编写。Python2中可能有所差异。 -## 2.6.1 使用第三方模块 +## 1. 线程池的创建 -在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个较为优化的数量的线程,让过来的任务立刻能够使用,就形成了线程池。 +### 使用内置模块 + +在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个合理数量的线程池,让过来的任务立刻能够使用,就形成了线程池。 在Python3中,创建线程池是通过`concurrent.futures`函数库中的`ThreadPoolExecutor`类来实现的。 @@ -16,37 +18,50 @@ import time import threading from concurrent.futures import ThreadPoolExecutor - def target(): for i in range(5): print('running thread-{}:{}'.format(threading.get_ident(), i)) time.sleep(1) -#: 生成线程池最大线程为5个 +# 创建一个最大容纳数量为5的线程池 pool = ThreadPoolExecutor(5) -for i in range(100): - pool.submit(target) # 往线程中提交,并运行 +for i in range(10): + # 往线程池上塞任务 + pool.submit(target) ``` -从结果来看,前面设置线程池最大线程数5个,有生效。 +创建线程池还可以使用更优雅的方式,就是使用上下文管理器 + ```python -running thread-11308:0 -running thread-12504:0 -running thread-5656:0 -running thread-12640:0 -running thread-7948:0 - -running thread-11308:1 -running thread-5656:1 -running thread-7948:1 -running thread-12640:1 -running thread-12504:1 +with ThreadPoolExecutor(5) as pool: + for i in range(100): + pool.submit(target) +``` -... -... +直接运行代码,从输出可以看出,前面我们设置线程池最大线程数,会保证“同时”仅有五个线程在工作。 + +```python +running thread-123145483767808:0 +running thread-123145489022976:0 +running thread-123145494278144:0 +running thread-123145499533312:0 +running thread-123145504788480:0 +running thread-123145483767808:1 +running thread-123145489022976:1 +running thread-123145499533312:1 +running thread-123145494278144:1 +running thread-123145504788480:1 +running thread-123145489022976:2 +running thread-123145499533312:2 +running thread-123145483767808:2 +running thread-123145504788480:2 +running thread-123145494278144:2 +.... ``` -## 2.6.2 自定义线程池 + + +### 自定义线程池 除了使用上述第三方模块的方法之外,我们还可以自己结合前面所学的消息队列来自定义线程池。 @@ -56,48 +71,68 @@ import time import threading from queue import Queue -def target(q): +def target(queue): while True: - msg = q.get() - for i in range(5): - print('running thread-{}:{}'.format(threading.get_ident(), i)) - time.sleep(1) - -def pool(workers,queue): - for n in range(workers): - t = threading.Thread(target=target, args=(queue,)) + task = queue.get() + if task == "stop": + queue.task_done() + break + + task() + queue.task_done() + +def do_task(): + for i in range(5): + print('running thread-{}:{}'.format(threading.get_ident(), i)) + time.sleep(1) + + +class MyQueue(Queue): + def close(self): + for i in range(self.maxsize): + self.put("stop") + +def custome_pool(task_func, max_workers): + queue = MyQueue(max_workers) + for n in range(max_workers): + t = threading.Thread(target=task_func, args=(queue,)) t.daemon = True t.start() -queue = Queue() -# 创建一个线程池:并设置线程数为5 -pool(5, queue) + return queue + + + +pool = custome_pool(task_func=target, max_workers=5) -for i in range(100): - queue.put("start") +for i in range(10): + pool.put(do_task) -# 消息都被消费才能结束 -queue.join() +pool.close() +pool.join() ``` 输出是和上面是完全一样的效果 ```python -running thread-11308:0 -running thread-12504:0 -running thread-5656:0 -running thread-12640:0 -running thread-7948:0 - -running thread-11308:1 -running thread-5656:1 -running thread-7948:1 -running thread-12640:1 -running thread-12504:1 - -... +running thread-123145469886464:0 +running thread-123145475141632:0 +running thread-123145485651968:0 +running thread-123145490907136:0 +running thread-123145480396800:0 +running thread-123145469886464:1 +running thread-123145480396800:1 +running thread-123145475141632:1 +running thread-123145490907136:1 +running thread-123145485651968:1 ... ``` 构建线程池的方法,是可以很灵活的,大家有空可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 + + +## 2. 进程池的创建 + + + ---- ![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 374c2a5..675d704 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -1,15 +1,18 @@ -2.6 如何创建线程池 -================== +2.6 线程池与进程池的创建 +======================== -------------- **友情提醒**\ : 本系列所有的代码均在Python3下编写。Python2中可能有所差异。 -2.6.1 使用第三方模块 --------------------- +1. 线程池的创建 +--------------- -在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个较为优化的数量的线程,让过来的任务立刻能够使用,就形成了线程池。 +使用内置模块 +~~~~~~~~~~~~ + +在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个合理数量的线程池,让过来的任务立刻能够使用,就形成了线程池。 在Python3中,创建线程池是通过\ ``concurrent.futures``\ 函数库中的\ ``ThreadPoolExecutor``\ 类来实现的。 @@ -19,39 +22,49 @@ import threading from concurrent.futures import ThreadPoolExecutor - def target(): for i in range(5): print('running thread-{}:{}'.format(threading.get_ident(), i)) time.sleep(1) - #: 生成线程池最大线程为5个 + # 创建一个最大容纳数量为5的线程池 pool = ThreadPoolExecutor(5) - for i in range(100): - pool.submit(target) # 往线程中提交,并运行 + for i in range(10): + # 往线程池上塞任务 + pool.submit(target) -从结果来看,前面设置线程池最大线程数5个,有生效。 +创建线程池还可以使用更优雅的方式,就是使用上下文管理器 .. code:: python - running thread-11308:0 - running thread-12504:0 - running thread-5656:0 - running thread-12640:0 - running thread-7948:0 + with ThreadPoolExecutor(5) as pool: + for i in range(100): + pool.submit(target) - running thread-11308:1 - running thread-5656:1 - running thread-7948:1 - running thread-12640:1 - running thread-12504:1 +直接运行代码,从输出可以看出,前面我们设置线程池最大线程数,会保证“同时”仅有五个线程在工作。 - ... - ... +.. code:: python -2.6.2 自定义线程池 ------------------- + running thread-123145483767808:0 + running thread-123145489022976:0 + running thread-123145494278144:0 + running thread-123145499533312:0 + running thread-123145504788480:0 + running thread-123145483767808:1 + running thread-123145489022976:1 + running thread-123145499533312:1 + running thread-123145494278144:1 + running thread-123145504788480:1 + running thread-123145489022976:2 + running thread-123145499533312:2 + running thread-123145483767808:2 + running thread-123145504788480:2 + running thread-123145494278144:2 + .... + +自定义线程池 +~~~~~~~~~~~~ 除了使用上述第三方模块的方法之外,我们还可以自己结合前面所学的消息队列来自定义线程池。 @@ -63,50 +76,67 @@ import threading from queue import Queue - def target(q): + def target(queue): while True: - msg = q.get() - for i in range(5): - print('running thread-{}:{}'.format(threading.get_ident(), i)) - time.sleep(1) - - def pool(workers,queue): - for n in range(workers): - t = threading.Thread(target=target, args=(queue,)) + task = queue.get() + if task == "stop": + queue.task_done() + break + + task() + queue.task_done() + + def do_task(): + for i in range(5): + print('running thread-{}:{}'.format(threading.get_ident(), i)) + time.sleep(1) + + + class MyQueue(Queue): + def close(self): + for i in range(self.maxsize): + self.put("stop") + + def custome_pool(task_func, max_workers): + queue = MyQueue(max_workers) + for n in range(max_workers): + t = threading.Thread(target=task_func, args=(queue,)) t.daemon = True t.start() - queue = Queue() - # 创建一个线程池:并设置线程数为5 - pool(5, queue) + return queue - for i in range(100): - queue.put("start") - # 消息都被消费才能结束 - queue.join() -输出是和上面是完全一样的效果 + pool = custome_pool(task_func=target, max_workers=5) -.. code:: python + for i in range(10): + pool.put(do_task) - running thread-11308:0 - running thread-12504:0 - running thread-5656:0 - running thread-12640:0 - running thread-7948:0 + pool.close() + pool.join() - running thread-11308:1 - running thread-5656:1 - running thread-7948:1 - running thread-12640:1 - running thread-12504:1 +输出是和上面是完全一样的效果 - ... +.. code:: python + + running thread-123145469886464:0 + running thread-123145475141632:0 + running thread-123145485651968:0 + running thread-123145490907136:0 + running thread-123145480396800:0 + running thread-123145469886464:1 + running thread-123145480396800:1 + running thread-123145475141632:1 + running thread-123145490907136:1 + running thread-123145485651968:1 ... 构建线程池的方法,是可以很灵活的,大家有空可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 +2. 进程池的创建 +--------------- + -------------- .. figure:: http://image.python-online.cn/image-20200320125724880.png diff --git a/source/c02/c02_14.md b/source/c02/c02_14.md index a731121..6a07cae 100644 --- a/source/c02/c02_14.md +++ b/source/c02/c02_14.md @@ -117,7 +117,7 @@ D[x] = D[x] + 1 ## 3. 实现人工原子操作 -在多线程下,我们并不能保证我们的代码具有原子性,因此如何让我们的代码变得具有 “原子性” ,就是一件很重要的事。 +在多线程下,我们并不能保证我们的代码都具有原子性,因此如何让我们的代码变得具有 “原子性” ,就是一件很重要的事。 方法也很简单,就是当你在访问一个多线程间共享的资源时,加锁可以实现类似原子操作的效果,一个代码要嘛不执行,执行了的话就要执行完毕,才能接受线程的调度。 diff --git a/source/c02/c02_14.rst b/source/c02/c02_14.rst index bfcf597..a92ab36 100644 --- a/source/c02/c02_14.rst +++ b/source/c02/c02_14.rst @@ -131,7 +131,7 @@ update 方法是原子操作。 3. 实现人工原子操作 ------------------- -在多线程下,我们并不能保证我们的代码具有原子性,因此如何让我们的代码变得具有 +在多线程下,我们并不能保证我们的代码都具有原子性,因此如何让我们的代码变得具有 “原子性” ,就是一件很重要的事。 方法也很简单,就是当你在访问一个多线程间共享的资源时,加锁可以实现类似原子操作的效果,一个代码要嘛不执行,执行了的话就要执行完毕,才能接受线程的调度。 From a19a5e4b1b233ccb44d292291c3ea26caf4a9843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 7 May 2020 20:43:35 +0800 Subject: [PATCH 057/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_43.md | 351 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 source/c01/c01_43.md diff --git a/source/c01/c01_43.md b/source/c01/c01_43.md new file mode 100644 index 0000000..8b8a5ed --- /dev/null +++ b/source/c01/c01_43.md @@ -0,0 +1,351 @@ +# 1.43 求你了,别再使用 pprint 打印字典了 + +## 1. 吐槽问题 + +pprint 你应该很熟悉了吧? + +随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货 。 + +比如这下面这个 json 字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。 + +```json +[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] +``` + +如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint 看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。 + +```python +>>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> from pprint import pprint +>>> pprint(info) +[{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] +``` + +好像有点效果,真的是 “神器”呀。 + +但是你告诉我, **\xe4\xbd\xa0\xe7\x9a** 这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。 + +好在我懂点 Python 2 的编码,知道 Python 2 中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte 存储的。 + +行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。 + +```python +>>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> from pprint import pprint +>>> pprint(info) +[{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': u'\u76ae\u7684\u561b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': u'\u4e0d\u5b58\u5728\u7684', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] +``` + +确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀? + +``` +u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f' +``` + +除此之外,我们知道 json 的严格要求必须使用 **双引号**,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是 **单引号**?我也太难了吧,我连自己的代码都无法控制了吗? + +到这里,我们知道了 pprint 带来的两个问题: + +1. 没法在 Python 2 下正常打印中文 +2. 没法输出 JSON 标准格式的格式化内容(双引号) + +## 2. 解决问题 + +### 打印中文 + +如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。 + +```python +# Python3.7 +>>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> from pprint import pprint +>>> pprint(info) +[{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '皮的嘛', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '不存在的', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] +>>> + +``` + +但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的 Python,本来我可以选择不用的,因为有更好的替代方案(**这个后面会讲**)。 + +但是我出于猎奇,正好前两天不是写过一篇关于 编码 的文章吗,我自认为自己对于 编码还是掌握比较熟练的,就想着来解决一下这个问题。 + +索性就来看下 pprint 的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。 + +**以下是我的解决方案,供你参考**: + +写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer) + +并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str 类型,而是 unicode 类型,就用 uft8 编码成 str 类型。 + +```python +# coding: utf-8 +from pprint import PrettyPrinter + +# 继承 PrettyPrinter,复写 format 方法 +class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + +info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + +MyPrettyPrinter().pprint(info) +``` + +输出如下,已经解决了中文的显示问题: + +![](http://image.iswbm.com/20200507171451.png) + +### 打印双引号 + +解决了中文问题后,再来看看如何让 pprint 打印双引号。 + +在实例化 PrettyPrinter 对象的时候,可以接收一个 stream 对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个 stream,也就是标准输出。 + +现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。 + +那我们完全可以自己定义一个 stream 类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。 + +有了思路,就可以开始写代码了,如下: + +```python +# coding: utf-8 +from pprint import PrettyPrinter + +class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + +class MyStream(): + def write(self, text): + print text.replace('\'', '"') + +info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +MyPrettyPrinter(stream=MyStream()).pprint(info) +``` + +尝试执行了下,我的天,怎么是这样子的。 + +```json +[ +{ +"des" +: +2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青 +, + "downloadUrl": +"app/com.renren.mobile.android/com.renren.mobile.android.apk" +, + "iconUrl": +"app/com.renren.mobile.android/icon.jpg" +, + "id": +1580615 +, + "name": +皮的嘛 +, + "packageName": +"com.renren.mobile.android" +, + "size": +21803987 +, + "stars": +2 +} +, + +{ +"des" +: +斗鱼271934走过路过不要错过,这里有最好的鸡儿 +, + "downloadUrl": +"app/com.ct.client/com.ct.client.apk" +, + "iconUrl": +"app/com.ct.client/icon.jpg" +, + "id": +1540629 +, + "name": +不存在的 +, + "packageName": +"com.ct.client" +, + "size": +4794202 +, + "stars": +2 +} +] +``` + +经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个 **换行符**。 + +那如何将使 print 函数打印的内容,不进行换行呢? + +方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个 **逗号** 就行。 + +就像下面这样。 + +![](http://image.iswbm.com/20200507174459.png) + +知道了问题所在,再修改下代码 + +```python +# coding: utf-8 +from pprint import PrettyPrinter + +class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + +class MyStream(): + def write(self, text): + print text.replace('\'', '"'), + +info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + +MyPrettyPrinter(stream=MyStream()).pprint(info) +``` + +终于成功了,太不容易了吧。 + +![](http://image.iswbm.com/20200507174802.png) + +## 3. 何必折腾 + +通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。 + +代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。 + +**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**。 + +为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python ,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗? + +如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用 pprint 了。 + +### 打印中文 + +其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。 + +但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint 简单得不要太多。 + +具体的代码示例如下: + +```python +>>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> import json +>>> +>>> +>>> print json.dumps(info, indent=4, ensure_ascii=False) +[ + { + "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk", + "iconUrl": "app/com.renren.mobile.android/icon.jpg", + "name": "皮的嘛", + "stars": 2, + "packageName": "com.renren.mobile.android", + "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青", + "id": 1580615, + "size": 21803987 + }, + { + "downloadUrl": "app/com.ct.client/com.ct.client.apk", + "iconUrl": "app/com.ct.client/icon.jpg", + "name": "不存在的", + "stars": 2, + "packageName": "com.ct.client", + "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿", + "id": 1540629, + "size": 4794202 + } +] +>>> +``` + +json.dumps 的关键参数有两个: + +- **indent=4**:以 4 个空格缩进单位 +- **ensure_ascii=False**:接收非 ASCII 编码的字符,这样才能使用中文 + +与 pprint 相比 json.dumps 可以说完胜: + +1. 两个参数就能实现所有我的需求(打印中文与双引号) +2. 就算在 Python 2 下,使用中文也不需要用 `u'中文'` 这种写法 +3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题 + +## 4. 总结一下 + +本来很简单的一个观点,我为了证明 pprint 实现那两个需求有多么困难,花了很多的时间去研究了 pprint 的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。 + +本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点 + +1. 核心观点:Python2 下不要再使用 pprint +2. 若真要使用,且有和一样的改造需求,可以参考我的实现 +3. Python 2 中的 print 语句后居然可以加 逗号 + +以上。希望此文能对你有帮助。 + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From 46438ff335bfe5d51e3d6d17ad832180e75dc899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 7 May 2020 20:44:08 +0800 Subject: [PATCH 058/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_06.md | 15 +++++++++++++++ source/c07/c07_09.md | 10 ++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/source/c04/c04_06.md b/source/c04/c04_06.md index 5fbef1d..8d7441f 100644 --- a/source/c04/c04_06.md +++ b/source/c04/c04_06.md @@ -188,6 +188,17 @@ git show commit_id git show --stat commit_id ``` +查看两个分支之间的差异 + +``` +# 查看两个分支有哪些文件发生改变了 +git diff stable/2.2.9 stable/2.3.0 --stat + +# 对比某一个文件在两个文件中的变化 +git diff stable/2.2.9 stable/2.3.0 -- README.md +git diff stable/2.2.9:README.md stable/2.3.0:README.md +``` + ## 三、状态回滚 @@ -277,7 +288,11 @@ $ git commit --amend # HEAD^^是上上个版本 # HEAD~100是前100个版本 +# 回到上一个版本 $ git reset --hard HEAD^ + +# 若本地不小心修改或删除了很多文件,一个一个恢复太麻烦,可以这样,回到上一个版本 +$ git reset --hard HEAD ``` 使用 `git revert`,间接回退,会生成新的提交 diff --git a/source/c07/c07_09.md b/source/c07/c07_09.md index 4c1b659..9b46279 100644 --- a/source/c07/c07_09.md +++ b/source/c07/c07_09.md @@ -1,6 +1,6 @@ # 7.9 Ansible 入门指南使用手册 -## 7.9.1 有用的小技巧 +## 1. 有用的小技巧 ### 1. 只输出错误的信息 @@ -79,7 +79,13 @@ ansible all -m setup /root/deployment/inventory/get_controller_v1.py --list ``` -## 7. playbook 参数 +### 7. ad-hoc 指定 run_once + +```shell +ansible compute[0] -m synchronize -a 'src=/etc/deny.list dest=/etc/deny.list mode=pull' +``` + +## 2. playbook 参数 ``` Options: From b3b499317c383b1f2408c9c8dc48000d8060d6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 8 May 2020 20:08:12 +0800 Subject: [PATCH 059/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_10.md | 154 +++++++++++++++++++++---------------------- source/c07/c07_16.md | 16 +++++ 2 files changed, 93 insertions(+), 77 deletions(-) diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index 29de30d..39f90d2 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -259,11 +259,11 @@ talk is cheap,show me the code. - 类,和函数的定义。 - 列表推导式,字典推导式,集合推导式,生成器表达式 -## 10. Py2 也可以使用 print() +## 10. Python2 也可以使用 print() -我相信应该有不少人,思维定式,觉得只有 Py3 才可以使用 print(),而 Py2 只能使用print ''。 +可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 只能使用print ""。 -今天,小明要为 Py2 正名一次。 +但是其实并不是这样的。 在Python 2.6之前,只支持 ```python @@ -1083,91 +1083,48 @@ with test_context('aaa'), test_context('bbb'): -## 36. 花样更新字典的技巧 +## 36. 可直接运行 zip 包 -常规的更新字典的方式是这样的 +我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。 -```python ->>> profile={'name': 'wangbm'} ->>> extra={'age': 25, 'gender': 'male'} ->>> profile.update(extra) ->>> profile -{'gender': 'male', 'age': 25, 'name': 'wangbm'} -``` - -今天明哥来集中讲下字典的更新方式 - -```python ->>> profile={'name': 'wangbm'} ->>> extra=[('age', 25), ('gender', 'male')] ->>> profile.update(extra) ->>> profile -{'gender': 'male', 'age': 25, 'name': 'wangbm'} -``` - -如果你见过上面这种,那接下来这种我保证大部分人都没用过 - -```python ->>> profile={'name': 'wangbm'} ->>> profile.update(age=25, gender='male') ->>> profile -{'gender': 'male', 'age': 25, 'name': 'wangbm'} -``` +这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。 -## 37. 被低估的 print +那么这个zip 是如何制作的呢,请看下面的示例。 -print 很多人只用来执行简单的打印功能。 +```shell +[root@localhost ~]# ls -l demo +total 8 +-rw-r--r-- 1 root root 30 May 8 19:27 calc.py +-rw-r--r-- 1 root root 35 May 8 19:33 __main__.py +[root@localhost ~]# +[root@localhost ~]# cat demo/__main__.py +import calc -print 作为一个函数,本身也附有各种各样的参数,只是很多人不知道。 - -你一定知道 join 这种很高效的字符串拼接方式。 - -```python ->>> alist = ['a', 'b', 'c'] ->>> ','.join(alist) -'a,b,c' -``` - -当如果 alist 里有非字符串的元素时,join 就会抛错。 - -```python ->>> alist = ['a', 'b', 3] ->>> ','.join(alist) -Traceback (most recent call last): - File "", line 1, in -TypeError: sequence item 2: expected str instance, int found +print(calc.add(2, 3)) +[root@localhost ~]# +[root@localhost ~]# cat demo/calc.py +def add(x, y): + return x+y +[root@localhost ~]# +[root@localhost ~]# python -m zipfile -c demo.zip demo/* +[root@localhost ~]# ``` -如果要解决这个问题,可能要提前将数值转化成字符串类型。 +制作完成后,我们可以执行用 python 去执行它 -```python ->>> alist = ['a', 'b', 3] ->>> blist = [str(i) for i in alist] ->>> blist -['a', 'b', '3'] ->>> ','.join(blist) -'a,b,3' +```shell +[root@localhost ~]# python demo.zip +5 +[root@localhost ~]# ``` -这里再介绍一种更简洁的方法,就是使用 print 来实现,但这种有局限,好像只能在命令行模式下使用,毕竟 print 标准输出,无法进行赋值。 - -下面我使用 `_` 来获取上一次的返回值,再赋值给 blist。 - -```python ->>> print(*alist, sep=',') -a,b,3 ->>> blist = _ ->>> blist -'a,b,3' -``` -既然说到了 print,那么再说一点print 的神技巧。 -很多初学者,喜欢使用 print 来进行调试追踪。 +## 37. print 输出到日志文件 -一点不好的地方是,普通的 print 是输出到终端屏幕,而不能将保持在文件中。 +Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。 -其实 print 是支持将输出重定向到文件中的,不过说实话,个人感觉还不如使用 logging 模块呢,或者直接 f.write()。 +比如今天要说的使用 print 将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。 ```python >>> with open('test.log', mode='w') as f: @@ -1178,12 +1135,12 @@ $ cat test.log hello, python ``` -以上两点,学习自董伟明的文章:[一些你不知道的Python Tips](https://mp.weixin.qq.com/s/KTLRwzCM7lOvBF4IEGuBPg) + ## 38. site 和 dist 的区别 -如果你手动安装python,它会直接使用目录site-packages。 -linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ +如果你是手动安装 Python,它会直接使用目录site-packages。 +linux系统自带的 Python,如果安装第三方库就存放到 dist-packages/ @@ -1207,6 +1164,49 @@ output_msg("error") +## 40. 逗号的妙用 + +逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 + +**第一个用法** + +元组的转化 + +```shell +[root@localhost ~]# cat demo.py +def func(): + return "ok", + +print(func()) +[root@localhost ~]# python3 demo.py +('ok',) +``` + +**第二个用法** + +print 的取消换行 + +```shell +[root@localhost ~]# cat demo.py +for i in range(3): + print i +[root@localhost ~]# +[root@localhost ~]# python demo.py +0 +1 +2 +[root@localhost ~]# +[root@localhost ~]# vim demo.py +[root@localhost ~]# +[root@localhost ~]# cat demo.py +for i in range(3): + print i, +[root@localhost ~]# +[root@localhost ~]# python demo.py +0 1 2 +[root@localhost ~]# +``` + ## 附录:参考文章 diff --git a/source/c07/c07_16.md b/source/c07/c07_16.md index 0d4e915..0aff82a 100644 --- a/source/c07/c07_16.md +++ b/source/c07/c07_16.md @@ -71,6 +71,22 @@ done +## 7.16.5 获取随机数 + +获取 1 - 60 的随机数 + +```shell +function random_range() { + local beg=$1 + local end=$2 + echo $((RANDOM % ($end - $beg) + $beg)) +} + +result=`random_range 1 60` +``` + + + ![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From 6de63860db3dc362250a2592b36fd672028ee7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 8 May 2020 20:17:59 +0800 Subject: [PATCH 060/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_30.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md index b72e802..09f808b 100644 --- a/source/c01/c01_30.md +++ b/source/c01/c01_30.md @@ -1,10 +1,25 @@ -# 1.30 学习编程的几大网站 +# 1.30 盘点程序员学习编程的那些网站 +## 书栈网 - [书栈网](https://www.bookstack.cn/rank?tab=popular) +**网站链接**:https://www.bookstack.cn/rank?tab=popular - ![](http://image.python-online.cn/20200104144109.png) +![](http://image.python-online.cn/20200104144109.png) - [魔法学院](http://www.nowamagic.net/academy/) +## 魔法学院 - ![](http://image.python-online.cn/20200112210558.png) \ No newline at end of file + **网站链接**:http://www.nowamagic.net/academy/ + +![](http://image.python-online.cn/20200112210558.png) + + + +## Python 3 标准库实例教程 + +**网站链接**:https://learnku.com/docs/pymotw + +![](http://image.iswbm.com/20200508201333.png) + + + +## \ No newline at end of file From 4392aad530b16d29546bb9bae009d768b04c9864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sat, 9 May 2020 18:11:34 +0800 Subject: [PATCH 061/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0<40=E4=B8=AA=20Python=20=E5=86=B7=E7=9F=A5?= =?UTF-8?q?=E8=AF=86>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_10.md | 868 +++++++++++++++++++++++++------------------ 1 file changed, 515 insertions(+), 353 deletions(-) diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index 39f90d2..040e3b8 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -1,9 +1,9 @@ -# 1.10 Python 冷知识 40 讲 +# 1.10 看到这 40个知识点,那些说精通 Python 的人脸都黑了 --- -## 01. 省略号也是对象 +## 01. 万象皆对象,包括省略号 `...` 这是省略号,在Python中,一切皆对象。它也不例外。 @@ -49,54 +49,296 @@ except ZeroDivisionError: ... ``` +## 02. 使用 end 增强代码逻辑? -## 02. 类首字母不一定是大写 +有不少编程语言,循环、判断代码块需要用 end 标明结束,这样一定程度上会使代码逻辑更加清晰一点。 -在正常情况下,我们所编写的所见到的代码,好像都默许了类名首字母大写,而实例用小写的这一准则。但这并不是强制性的,即使你反过来的也没有关系。 +但是其实在 Python 这种严格缩进的语言里并没有必要这样做。 -但有一些内置的类,首字母都是小写,而实例都是大写。 +如果你真的想用,也不是没有办法,具体你看下面这个例子。 -比如 bool 是类名,而 True,False 是其实例; -比如 ellipsis 是类名,Ellipsis是实例; -还有 int,string,float,list,tuple,dict 等一系列数据类型都是类名,它们都是小写。 +```python +__builtins__.end = None -## 03. 增量赋值的性能更好 -诸如 `+=` 和 `*=` 这些运算符,叫做 增量赋值运算符。 +def my_abs(x): + if x > 0: + return x + else: + return -x + end +end -这里使用用 += 举例,以下两种写法,在效果上是等价的。 +print(my_abs(10)) +print(my_abs(-10)) ``` -# 第一种 -a = 1 ; a += 1 -# 第二种 -a = 1; a = a + 1 +执行后,输出如下 + +```shell +[root@localhost ~]$ python demo.py +10 +10 ``` -`+=` 其背后使用的魔法方法是 \__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add__ 。 -这两种写法有什么区别呢? -用列表举例 a += b,使用 \__add__ 的话就像是使用了a.extend(b),如果使用 \__add__ 的话,则是 a = a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 -所以在能使用增量赋值的时候尽量使用它。 +## 03. 逗号也有它独特的用法 + +逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 + +**第一个用法** + +元组的转化 + +```shell +[root@localhost ~]# cat demo.py +def func(): + return "ok", + +print(func()) +[root@localhost ~]# python3 demo.py +('ok',) +``` + +**第二个用法** + +print 的取消换行 + +```shell +[root@localhost ~]# cat demo.py +for i in range(3): + print i +[root@localhost ~]# +[root@localhost ~]# python demo.py +0 +1 +2 +[root@localhost ~]# +[root@localhost ~]# vim demo.py +[root@localhost ~]# +[root@localhost ~]# cat demo.py +for i in range(3): + print i, +[root@localhost ~]# +[root@localhost ~]# python demo.py +0 1 2 +[root@localhost ~]# +``` + +## 04. 反斜杠的倔强: 不写最后 + +`\` 在 Python 中的用法主要有两种 + +**1、在行尾时,用做续行符** + + ```python +[root@localhost ~]$ cat demo.py +print("hello "\ + "world") +[root@localhost ~]$ +[root@localhost ~]$ python demo.py +hello world + ``` + + + +**2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。** + +```python +>>> str1='\nhello'  #换行 +>>> print(str1) + +hello +>>> str2='\thello'  #tab +>>> print(str2) + hello +``` + +但是如果你用单`\`结尾是会报语法错误的 + +```python +>>> str3="\" + File "", line 1 + str3="\" + ^ +SyntaxError: EOL while scanning string literal +``` + +就算你指定它是个 raw 字符串,也不行。 + +```python +>>> str3=r"\" + File "", line 1 + str3=r"\" + ^ +SyntaxError: EOL while scanning string literal +``` + +## 05. 单行实现 for 死循环如何写? + +如果让你在不借助 while ,只使用 for 来写一个死循环? + +**你会写吗?** + +**如果你还说简单,你可以自己试一下。** + +... + + + +如果你尝试后,仍然写不出来,那我给出自己的做法。 + +```python +for i in iter(int, 1):pass +``` + + + +**是不是傻了?iter 还有这种用法?这为啥是个死循环?** + + + +关于这个问题,你如果看中文网站,可能找不到相关资料。 + +还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 + +原来iter有两种使用方法。 + +- 通常我们的认知是第一种,将一个列表转化为一个迭代器。 + +- 而第二种方法,他接收一个 callable对象,和一个sentinel 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 + +那`int` 呢? + +这又是一个知识点,int 是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console 模式下输入 `int()` 看看是不是返回0。 + +由于int() 永远返回0,永远返回不了1,所以这个 for 循环会没有终点。一直运行下去。 + +## 06. 懒人必备技能:使用 “_” + +对于 `_` ,大家对于他的印象都是用于 **占位符**,省得为一个不需要用到的变量,绞尽脑汁的想变量名。 + +今天要介绍的是他的第二种用法,就是在 console 模式下的应用。 + +示例如下: + +```python +>>> 3 + 4 +7 +>>> _ +7 +>>> name='公众号: Python编程时光' +>>> name +'公众号: Python编程时光' +>>> _ +'公众号: Python编程时光' +``` + +它可以返回上一次的运行结果。 + +但是,如果是print函数打印出来的就不行了。 + +```python +>>> 3 + 4 +7 +>>> _ +7 +>>> print("公众号: Python编程时光") +ming +>>> _ +7 +``` + +我自己写了个例子,验证了下,用`__repr__`输出的内容可以被获取到的。 +首先,在我们的目录下,写一个文件 demo.py。内容如下 + +```python +# demo.py +class mytest(): + def __str__(self): + return "hello" + + def __repr__(self): + return "world" +``` + +然后在这个目录下进入交互式环境。 -## 04. and 和 or 的取值顺序 +```python +>>> import demo +>>> mt=demo.mytest() +>>> mt +world +>>> print(mt) +hello +>>> _ +world +``` -and 和 or 是我们再熟悉不过的两个逻辑运算符。而我们通常只用它来做判断,很少用它来取值。 +知道这两个魔法方法的人,一看就明白了,这里不再解释啦。 -如果一个or表达式中所有值都为真,Python会选择第一个值,而and表达式则会选择第二个。 +## 07. 最快查看包搜索路径的方式 +当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 查看。 + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path) +['', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages'] +>>> +``` + +那有没有更快的方式呢? + +我这有一种连 console 模式都不用进入的方法,一行命令即可解决 + +```shell +[wangbm@localhost ~]$ python3 -m site +sys.path = [ + '/home/wangbm', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages', +] +USER_BASE: '/home/wangbm/.local' (exists) +USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) +ENABLE_USER_SITE: True ``` + +从输出你可以发现,这个列的路径会比 sys.path 更全,它包含了用户环境的目录。 + +如果你不熟悉用户环境,可以参考我写的另一篇文章:[记 Python “用户环境”的一次完美应用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486191&idx=1&sn=e556df305ce1df4c4969d2fdbe884cfa&chksm=e8866a0ddff1e31b2ca8ca8fbbe0355fb8c24376d715224ef2176e613b5601764be0193e3aa4&token=1343865895&lang=zh_CN#rd) + +## 08. and 和 or 的取值顺序 + +and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它有妙用。 + +- 当一个 **or 表达式**中所有值都为真,Python会选择第一个值 + +- 当一个 **and 表达式** 所有值都为真,Python 会选择第二个值。 + +示例如下: + +```python >>>(2 or 3) * (5 and 7) 14 # 2*7 ``` -## 05. 如何修改解释器提示符 +## 09. 如何修改解释器提示符 这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。 正常情况下,我们在 终端下 执行Python 命令是这样的。 -``` +```python >>> for i in range(2): ... print (i) ... @@ -105,7 +347,7 @@ and 和 or 是我们再熟悉不过的两个逻辑运算符。而我们通常只 ``` 你是否想过 `>>>` 和 `...` 这两个提示符也是可以修改的呢? -``` +```python >>> import sys >>> sys.ps1 '>>> ' @@ -121,7 +363,44 @@ Python编程时光>>>for i in range(2): 1 ``` -## 06. 默认参数最好不为可变对象 +## 10. 可直接运行 zip 包 + +我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。 + +这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。 + +那么这个zip 是如何制作的呢,请看下面的示例。 + +```shell +[root@localhost ~]# ls -l demo +total 8 +-rw-r--r-- 1 root root 30 May 8 19:27 calc.py +-rw-r--r-- 1 root root 35 May 8 19:33 __main__.py +[root@localhost ~]# +[root@localhost ~]# cat demo/__main__.py +import calc + +print(calc.add(2, 3)) +[root@localhost ~]# +[root@localhost ~]# cat demo/calc.py +def add(x, y): + return x+y +[root@localhost ~]# +[root@localhost ~]# python -m zipfile -c demo.zip demo/* +[root@localhost ~]# +``` + +制作完成后,我们可以执行用 python 去执行它 + +```shell +[root@localhost ~]# python demo.zip +5 +[root@localhost ~]# +``` + + + +## 11. 默认参数最好不为可变对象 函数的参数分三种 - 可变参数 @@ -159,7 +438,7 @@ Python 中的 def 语句在每次执行的时候都初始化一个函数对象 ![](http://image.python-online.cn/20190511165650.png) -## 07. 访问类中的私有方法 +## 12. 访问类中的私有方法 大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。 @@ -196,7 +475,7 @@ ins._Kls__private() ins.call_private() ``` -## 08. 时有时无的切片异常 +## 13. 时有时无的切片异常 这是个简单例子 ```python @@ -219,7 +498,7 @@ my_list = [1, 2, 3] print(my_list[5:]) ``` -## 09. 哪些情况下不需要续行符 +## 14. 哪些情况下不需要续行符 在写代码时,为了代码的可读性,代码的排版是尤为重要的。 @@ -252,14 +531,7 @@ talk is cheap,show me the code. ... show me the code''' ``` -上面只举了一些简单的例子。 - -但你要学会举一反三。一样的,在以下这些场景也同样适用 - -- 类,和函数的定义。 -- 列表推导式,字典推导式,集合推导式,生成器表达式 - -## 10. Python2 也可以使用 print() +## 15. 2.x 下 也可以使用 print(“”) 可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 只能使用print ""。 @@ -283,13 +555,13 @@ print("hello") print ("hello") ``` +虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print ,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 python2.6+下使用 print() 后,就成了函数。 - -## 11. 奇怪的字符串 +## 16. 迷一样的字符串 示例一 -``` +```python # Python2.7 >>> a = "Hello_Python" >>> id(a) @@ -307,7 +579,7 @@ print ("hello") 示例二 -``` +```python >>> a = "MING" >>> b = "MING" >>> a is b @@ -326,7 +598,7 @@ False 示例三 -``` +```python # Python2.7 >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' True @@ -340,17 +612,19 @@ True True ``` -详细解释这么不多说了(比较多),感兴趣的朋友,可以加我微信交流。 -## 12. 两次 return -我们都知道,try…finally… 语句的用法,不管try里面是正常执行还是报异常,最终都能保证finally能够执行。 +## 17. return不一定都是函数的终点 -同时,我们又知道,一个函数里只要遇到 return 函数就会立马结束。 +众所周知,try…finally… 的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。 -基于以上这两点,我们来看看这个例子,到底运行过程是怎么样的? +同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。 -``` +那问题就来了,以上这两种规则,如果同时存在,Python 解释器会如何选择?哪个优先级更高? + +写个示例验证一下,就明白啦 + +```python >>> def func(): ... try: ... return 'try' @@ -361,45 +635,39 @@ True 'finally' ``` -惊奇的发现,在`try`里的return居然不起作用。 - -原因是,在try…finally…语句中,try中的return会被直接忽视,因为要保证finally能够执行。 +从输出中,我们可以发现:在try…finally…语句中,try中的 return 会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally 能够执行。 -## 13. for 死循环 +**如果 try 里的 return 真的是直接被忽视吗?** -for 循环可以说是 基础得不能再基础的知识点了。 +我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的 return 真的是直接被忽视,那当finally 下没有显式的 return 的时候,是不是会返回None呢? -但是如果让你用 for 写一个死循环,你会写吗?(问题来自群友 陈**) +还是写个 示例来验证一下: -这是个开放性的问题,在往下看之前,建议你先尝试自己思考,你会如何解答。 - -好了,如果你还没有思路,那就来看一下 一个海外 MIT 群友的回答: - -``` -for i in iter(int, 1):pass +```python +>>> def func(): +... try: +... return 'try' +... finally: +... print('finally') +... +>>> +>>> func() +finally +'try' +>>> ``` -是不是懵逼了。iter 还有这种用法?这为啥是个死循环? - -这真的是个冷知识,关于这个知识点,你如果看中文网站,可能找不到相关资料。 - -还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 - -原来iter有两种使用方法,通常我们的认知是第一种,将一个列表转化为一个迭代器。 +从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return 仍然还是有效的。 -而第二种方法,他接收一个 callable对象,和一个sentinel 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 +那结论就出来了,如果 finally 里有显式的 return,那么这个 return 会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 try 里的 return 仍然有效。 -那`int` 呢,这又是一个知识点,int 是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在终端上输入 `int()` 看看是不是返回0。 +## 18. 用户无感知的小整数池 -由于int() 永远返回0,永远返回不了1,所以这个 for 循环会没有终点。一直运行下去。 +为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。 -这些问题和答案都源自于群友的智慧。如果你也想加入我们的讨论中,请到公众号后台,添加我个人微信。 +以上代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE 的影响,效果会有所不同。 -## 14. 小整数池 - -先看例子。 - -``` +```python >>> a = -6 >>> b = -6 >>> a is b @@ -420,15 +688,11 @@ False True ``` -为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。 - -以上代码请在 终端Python环境下测试,如果你是在IDE中测试,并不是这样的效果。 - -那最后一个示例,为啥又是True? +**问题又来了:最后一个示例,为啥是True?** 因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两成的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。 -## 15. intern机制 +## 19. 神奇的 intern 机制 字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化. @@ -468,65 +732,9 @@ True False ``` -## 16. 交互式“_”操作符 - -对于 `_` ,我想很多人都非常熟悉。 - -给变量取名好艰难,用 `_`; -懒得长长的变量名,用 `_`; -无用的垃圾变量,用 `_`; -以上,我们都很熟悉了,今天要介绍的是他在交互式中使用。 -```python ->>> 3 + 4 -7 ->>> _ -7 ->>> name='ming' ->>> name -'ming' ->>> _ -'ming' -``` -它可以返回上一次的运行结果。 - -但是,如果是print函数打印出来的就不行了。 -```python ->>> 3 + 4 -7 ->>> _ -7 ->>> print("ming") -ming ->>> _ -7 -``` -我自己写了个例子,验证了下,用`__repr__`输出的内容可以被获取到的。 -首先,在我们的目录下,写一个文件 ming.py。内容如下 -```python -# ming.py -class mytest(): - def __str__(self): - return "hello" - - def __repr__(self): - return "world" -``` -然后在这个目录下进入交互式环境。 -```python ->>> import ming ->>> mt=ming.mytest() ->>> mt -world ->>> print(mt) -hello ->>> _ -world -``` -知道这两个魔法方法的人,一看就明白了。 - -## 17. 反转字符串/列表最优雅的方式 +## 20. 反转字符串/列表最优雅的方式 反转序列并不难,但是如何做到最优雅呢? @@ -567,7 +775,7 @@ def my_reverse(str): [3, 2, 1] ``` -## 18. 改变默认递归次数限制 +## 21. 改变默认递归次数限制 上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。 @@ -585,7 +793,7 @@ def my_reverse(str): 2000 ``` -## 19. 一行代码实现FTP服务器 +## 22. 一行代码实现FTP服务器 搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。 @@ -602,7 +810,7 @@ python3 -m http.server 8888 SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 -## 20. 让你晕头转向的 else 用法 +## 23. 让你晕头转向的 else 用法 if else 用法可以说最基础的语法表达式之一,但是今天不是讲这个的,一定要讲点不一样的。 @@ -660,12 +868,13 @@ test_try_else("ming") 总结一下,for else 和 try else 相同,只要代码正常走下去不被 break,不抛出异常,就可以走else。 -## 21. 空字符串计数 +## 24. 字符串里的缝隙是什么? -求一个字符串里,某子字符(串)出现的次数。在Python中使用 count() 函数,就可以轻松实现。 +在Python中求一个字符串里,某子字符(串)出现的次数。 -比如下面几个常规例子 -``` +大家都懂得使用 count() 函数,比如下面几个常规例子: + +```python >>> "aabb".count("a") 2 >>> "aabb".count("b") @@ -674,29 +883,42 @@ test_try_else("ming") 1 ``` -但是如果使用空字符串呢,你可能想不到会是这样的结果。 -``` +但是如果我想计算空字符串的个数呢? +```python >>> "aabb".count("") 5 ``` -具体原因,我不敢妄下结论。 +**奇怪了吧?** -由此我还衍生出另一个想法,实验了下。不知道空字符串,是一种什么样的存在,难道字母与字母之间 “缝隙” 也算吗? -``` +不是应该返回 0 吗?怎么会返回 5? + +实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。 + +因此 对于 `aabb` 这个字符串在 Python 来看应该是这样的 + +![](http://image.iswbm.com/20200509172331.png) + +理解了这个“**缝隙**” 的概念后,以下这些就好理解了。 + +```python +>>> (" " * 10).count("") +11 +>>> >>> "" in "" True ->>> "" in "ab" +>>> +>>> "" in "M" True ``` -有兴趣的可以去看看CPython的源码实现。如果有结论,还请后台回复一下。不胜感激。 -## 22. 负负得正 -从初中开始,我们就开始接触了`负数` 这个概念。知道了`负负得正`,这和武侠世界里的`以毒功毒`,有点神似。 +## 25. 正负得正,负负得正 -Python 作为一门高级语言,它的编写符合人类的思维逻辑,这其中也包括`负负得正`这个思想。 +从初中开始,我们就开始接触了`负数` ,并且都知道了`负负得正` 的思想。 + +Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 `负负得正` 。 ```python >>> 5-3 @@ -711,7 +933,7 @@ Python 作为一门高级语言,它的编写符合人类的思维逻辑,这 2 ``` -## 23. 数值与字符串的比较 +## 26. 数值与字符串的比较 在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。 ``` @@ -727,7 +949,7 @@ True TypeError: '<' not supported between instances of 'int' and 'str' ``` -## 24. 循环中的局部变量泄露 +## 27. 循环中的局部变量泄露 在Python 2中x的值在一个循环执行之后被改变了。 ``` @@ -748,124 +970,31 @@ TypeError: '<' not supported between instances of 'int' and 'str' 1 ``` -## 25. 字典可排序 +## 28. 字典居然是可以排序的? 字典不可排序的思想,似乎已经根深蒂固。 -``` + +```python # Python2.7.10 >>> mydict = {str(i):i for i in range(5)} >>> mydict {'1': 1, '0': 0, '3': 3, '2': 2, '4': 4} ``` +假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,这是真的。 + 在 Python3 中字典已经是有序的。 -``` + +```python # Python3.6.7 >>> mydict = {str(i):i for i in range(5)} >>> mydict {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} ``` -## 26. 链式比较 - -先给看一个示例 - -``` ->>> False == False == True -False -``` - -你知道这个表达式会返回 False 吗? -我再给你举个例子,你可能就懂了。 -``` -f 18 < age < 60: - print("young man") -``` - -如果还不明白,再给你整个等价写法。 - -``` ->>> False == False and False == True -False -``` - -## 27. 奇怪的字母 - -直接看下列例子。 - -在Python 2.x 中 - -``` ->>> value = 11 ->>> valuе = 32 - File "", line 1 - valuе = 32 - ^ -SyntaxError: invalid syntax -``` - -在Python 3.x 中 - -``` ->>> value = 11 ->>> valuе = 32 ->>> value -11 -``` - -我相信你一开始看到这里,一定是目瞪口呆。你可以在自己的电脑上尝试一下,你会发现你不管在哪个版本的 Python 里运行都没有问题。 - -如果你想重现我这个场景,你可能复制我上面的代码粘贴至自己的命令行中即可。 - -在这里,也不卖关子了,上面代码中第二行的 `е` 和 第一行的 `e` 是不一样的。 - -第二行的 `e` 是 Cyrillic(西里尔)字母,而不是我们熟悉的英文字母。 - -``` ->>> ord('е') # cyrillic 'e' (Ye) -1077 ->>> ord('e') # latin 'e', as used in English and typed using standard keyboard -101 ->>> 'е' == 'e' -False -``` - -细思恐极,平时可千万不要得罪同事们,万一辞职的时候,把你项目里的 `e` 全局替换成 `e`,到时候连错都不知道错哪了哈哈。 - -## 28. x == +x 吗? - -在大多数情况下,这个等式是成立的。 - -``` ->>> n1 = 10086 ->>> n2 = +n1 ->>> ->>> n1 == n2 -True -``` - -什么情况下,这个等式会不成立呢? - -由于Counter的机制,`+` 用于两个 Counter 实例相加,而相加的结果如果元素的个数 `<=` 0,就会被丢弃。 - -``` ->>> from collections import Counter ->>> ct = Counter('abcdbcaa') ->>> ct -Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) ->>> ct['c'] = 0 ->>> ct['d'] = -2 ->>> ->>> ct -Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) ->>> ->>> +ct -Counter({'a': 3, 'b': 2}) -``` - -## 29. 有趣的import +## 29. 有趣但没啥用的 import 用法 import 是 Python 导包的方式。 @@ -917,7 +1046,7 @@ Namespaces are one honking great idea -- let's do more of those! 就会自动打开一个网页。 ![](http://image.python-online.cn/20190511165735.png) -## 30. 局部/全局变量分不清? +## 30. 局部/全局变量傻傻分不清 在开始讲之前,你可以试着运行一下下面这小段代码。 @@ -956,43 +1085,64 @@ def func02(): func02() ``` +## 31. 字母也玩起了障眼法 +以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。 -## 31. 不能使用单\结尾 +**在Python 2.x 中** -`\` 是转义字符,可以将普通字符转化为有特殊含义的字符。 +``` +>>> valuе = 32 + File "", line 1 + valuе = 32 + ^ +SyntaxError: invalid syntax +``` -```python ->>> str1='\nhello'  #换行 ->>> print(str1) +**在Python 3.x 中** -hello ->>> str2='\thello'  #tab ->>> print(str2) - hello +``` +>>> valuе = 32 +>>> value +11 ``` -但是如果你用单`\`结尾是会报语法错误的 +什么?没有截图你不信? -```python ->>> str3="\" - File "", line 1 - str3="\" - ^ -SyntaxError: EOL while scanning string literal -``` +![](http://image.iswbm.com/20200509122954.png) -就算你指定它是个 raw 字符串,也不行。 -```python ->>> str3=r"\" - File "", line 1 - str3=r"\" - ^ -SyntaxError: EOL while scanning string literal + +如果你在自己的电脑上尝试一下,结果可能是这样的 + +![](http://image.iswbm.com/20200509123107.png) + + + +**怎么又好了呢?** + +如果你想复现的话,请复制我这边给出的代码:`valuе = 32` + + + +**这是为什么呢?** + +原因在于,我上面使用的 value 变量名里的 `е` 又不是我们熟悉的 `e`,它是 Cyrillic(西里尔)字母。 + +``` +>>> ord('е') # cyrillic 'e' (Ye) +1077 +>>> ord('e') # latin 'e', as used in English and typed using standard keyboard +101 +>>> 'е' == 'e' +False ``` -## 32. 字符串分割 +细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的 `e` 全局替换成 `e`,到时候你就哭去吧,肉眼根本看不出来嘛。 + +## 32. 字符串的分割技巧 + +当我们对字符串进行分割时,且分割符是 `\n`,有可能会出现这样一个窘境: ```python >>> str = "a\nb\n" @@ -1003,12 +1153,19 @@ b >>> str.split('\n') ['a', 'b', ''] >>> +``` ->>> +会在最后一行多出一个元素,为了应对这种情况,你可以会多加一步处理。 + +但我想说的是,完成没有必要,对于这个场景,你可以使用 `splitlines` + +```python >>> str.splitlines() ['a', 'b'] ``` + + ## 33. 嵌套上下文管理的另类写法 当我们要写一个嵌套的上下文管理器时,可能会这样写 @@ -1073,54 +1230,62 @@ with test_context('aaa'), test_context('bbb'): [1, 2, 3, 4, 5, 6, 7, 8] ``` +## 35. 增量赋值的性能更好 +诸如 `+=` 和 `*=` 这些运算符,叫做 增量赋值运算符。 -## 35. Python 里也可以有 end - -有不少编程语言,循环、判断代码块需要用 end 标明结束,这样一定程序上会使代码逻辑更加清晰一点,其实这种语法在 Python 里并没有必要,但如果你想用,也不是没有办法,具体你看下面这个例子。 +这里使用用 += 举例,以下两种写法,在效果上是等价的。 -![](http://image.python-online.cn/20190915213412.png) +``` +# 第一种 +a = 1 ; a += 1 +# 第二种 +a = 1; a = a + 1 +``` +`+=` 其背后使用的魔法方法是 \__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add__ 。 -## 36. 可直接运行 zip 包 +这两种写法有什么区别呢? -我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。 +用列表举例 a += b,使用 \__add__ 的话就像是使用了a.extend(b),如果使用 \__add__ 的话,则是 a = a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 -这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。 +所以在能使用增量赋值的时候尽量使用它。 -那么这个zip 是如何制作的呢,请看下面的示例。 +## 36. x == +x 吗? -```shell -[root@localhost ~]# ls -l demo -total 8 --rw-r--r-- 1 root root 30 May 8 19:27 calc.py --rw-r--r-- 1 root root 35 May 8 19:33 __main__.py -[root@localhost ~]# -[root@localhost ~]# cat demo/__main__.py -import calc +在大多数情况下,这个等式是成立的。 -print(calc.add(2, 3)) -[root@localhost ~]# -[root@localhost ~]# cat demo/calc.py -def add(x, y): - return x+y -[root@localhost ~]# -[root@localhost ~]# python -m zipfile -c demo.zip demo/* -[root@localhost ~]# +``` +>>> n1 = 10086 +>>> n2 = +n1 +>>> +>>> n1 == n2 +True ``` -制作完成后,我们可以执行用 python 去执行它 +什么情况下,这个等式会不成立呢? + +由于Counter的机制,`+` 用于两个 Counter 实例相加,而相加的结果如果元素的个数 `<=` 0,就会被丢弃。 -```shell -[root@localhost ~]# python demo.zip -5 -[root@localhost ~]# +``` +>>> from collections import Counter +>>> ct = Counter('abcdbcaa') +>>> ct +Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) +>>> ct['c'] = 0 +>>> ct['d'] = -2 +>>> +>>> ct +Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) +>>> +>>> +ct +Counter({'a': 3, 'b': 2}) ``` -## 37. print 输出到日志文件 +## 37. 如何将 print 内容输出到文件 Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。 @@ -1137,10 +1302,25 @@ hello, python -## 38. site 和 dist 的区别 +## 38. site-packages和 dist-packages + +如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages** 下,而有些包安装在 **dist-packages** 下。 + +**它们有什么区别呢?** + +一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在 这个目录下。 + +而 dist-packages 其实是 debian 系的 Linux 系统(如 Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用 dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在 site-packages 下。 + +Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。 -如果你是手动安装 Python,它会直接使用目录site-packages。 -linux系统自带的 Python,如果安装第三方库就存放到 dist-packages/ +如何查找 Python 安装目录 + +```python +>>> from distutils.sysconfig import get_python_lib +>>> print(get_python_lib()) +/usr/lib/python2.7/site-packages +``` @@ -1162,50 +1342,32 @@ def output_msg(msg): output_msg("error") ``` +## 40. 简洁而优雅的链式比较 +先给看一个示例 -## 40. 逗号的妙用 +``` +>>> False == False == True +False +``` -逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 +你知道这个表达式会返回 False 吗? -**第一个用法** +我再给你举个例子,你可能就懂了。 -元组的转化 +``` +f 18 < age < 60: + print("young man") +``` -```shell -[root@localhost ~]# cat demo.py -def func(): - return "ok", +如果还不明白,再给你整个等价写法。 -print(func()) -[root@localhost ~]# python3 demo.py -('ok',) +``` +>>> False == False and False == True +False ``` -**第二个用法** - -print 的取消换行 -```shell -[root@localhost ~]# cat demo.py -for i in range(3): - print i -[root@localhost ~]# -[root@localhost ~]# python demo.py -0 -1 -2 -[root@localhost ~]# -[root@localhost ~]# vim demo.py -[root@localhost ~]# -[root@localhost ~]# cat demo.py -for i in range(3): - print i, -[root@localhost ~]# -[root@localhost ~]# python demo.py -0 1 2 -[root@localhost ~]# -``` From 4f7e2ed37379918b079310222a6a350fc7d9a182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 12 May 2020 19:39:19 +0800 Subject: [PATCH 062/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0<=E5=AF=BC=E5=8C=85=E7=9A=84=E5=85=AB=E7=A7=8D?= =?UTF-8?q?=E6=96=B9=E6=B3=95>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_45.md | 277 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 source/c01/c01_45.md diff --git a/source/c01/c01_45.md b/source/c01/c01_45.md new file mode 100644 index 0000000..5d90ca9 --- /dev/null +++ b/source/c01/c01_45.md @@ -0,0 +1,277 @@ +# 1.45 Python炫技操作:花式导包的八种方法 + + + +## 1. 直接 import + +人尽皆知的方法,直接导入即可 + +```python +>>> import os +>>> os.getcwd() +'/home/wangbm' +``` + +与此类似的还有,不再细讲 + +```python +import ... +import ... as ... +from ... import ... +from ... import ... as ... +``` + +一般情况下,使用 `import` 语句导入模块已经够用的。 + +但是在一些特殊场景中,可能还需要其他的导入方式。 + +下面我会一一地给你介绍。 + +## 2. 使用 \__import__ + +`__import__` 函数可用于导入模块,import 语句也会调用函数。其定义为: + +``` +__import__(name[, globals[, locals[, fromlist[, level]]]]) +``` + +参数介绍: + +- name (required): 被加载 module 的名称 +- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 global() +- locals (optional): 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local() +- fromlist (Optional): 被导入的 submodule 名称 +- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 类似于 '.',2 类似于 '..'。 + +使用示例如下: + +```python +>>> os = __import__('os') +>>> os.getcwd() +'/home/wangbm' +``` + +如果要实现 `import xx as yy` 的效果,只要修改左值即可 + +如下示例,等价于 `import os as myos`: + +```python +>>> myos = __import__('os') +>>> myos.getcwd() +'/home/wangbm' +``` + +## 3. 使用 importlib 模块 + +importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。 + +它的简单示例: + +```python +>>> import importlib +>>> myos=importlib.import_module("os") +>>> myos.getcwd() +'/home/wangbm' +``` + +如果要实现 `import xx as yy`效果,可以这样 + +```python +>>> import importlib +>>> +>>> myos = importlib.import_module("os") +>>> myos.getcwd() +'/home/wangbm' +``` + + + +## 4. 使用 imp 模块 + +`imp` 模块提供了一些 import 语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的 `__import__` 函数功能: + +```python +>>> import imp +>>> file, pathname, desc = imp.find_module('os') +>>> myos = imp.load_module('sep', file, pathname, desc) +>>> myos + +>>> myos.getcwd() +'/home/wangbm' +``` + +从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib 模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib 模块的合集。 + + + +## 5. 使用 execfile + +在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。 + +语法如下: + +``` +execfile(filename[, globals[, locals]]) +``` + +参数有这么几个: + +- filename:文件名。 +- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。 +- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。 + +```python +>>> execfile("/usr/lib64/python2.7/os.py") +>>> +>>> getcwd() +'/home/wangbm' +``` + + + +## 6. 使用 exec 执行 + +`execfile` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。 + +但是原理值得借鉴,你可以使用 open ... read 读取文件内容,然后再用 exec 去执行模块。 + +示例如下: + +```python +>>> with open("/usr/lib64/python2.7/os.py", "r") as f: +... exec(f.read()) +... +>>> getcwd() +'/home/wangbm' +``` + + + +## 7. import_from_github_com + +有一个包叫做 **import_from_github_com**,从名字上很容易得知,它是一个可以从 github 下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip 先安装它。 + +```shell +$ python3 -m pip install import_from_github_com +``` + +这个包使用了PEP 302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。 + +pip 要保证是较新版本,如果不是请执行如下命令进行升级。 + +```shell +$ python3 -m pip install --upgrade pip +``` + +确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com + +示例如下 + +```python +>>> from github_com.zzzeek import sqlalchemy +Collecting git+https://github.com/zzzeek/sqlalchemy +Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build +Installing collected packages: SQLAlchemy +Running setup.py install for SQLAlchemy ... done +Successfully installed SQLAlchemy-1.1.0b1.dev0 +>>> locals() +{'__builtins__': , '__spec__': None, +'__package__': None, '__doc__': None, '__name__': '__main__', +'sqlalchemy': , +'__loader__': } +>>> +``` + +看了 import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 pip 来安装那些没有安装的包,然后使用Python的`__import__()`函数来引入新安装的模块。 + + + +## 8. 远程导入模块 + +我在这篇文章里([深入探讨 Python 的 import 机制:实现远程导入模块](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。 + +具体内容非常的多,你可以点击这个[链接](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)进行深入学习。 + +示例代码如下: + +```python +# 新建一个 py 文件(my_importer.py),内容如下 +import sys +import importlib +import urllib.request as urllib2 + +class UrlMetaFinder(importlib.abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return loader + except Exception: + return None + +class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + +def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) +``` + +并且在远程服务器上开启 http 服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的 python 文件,如果后面导入成功会打印 `ok`。 + +```shell +$ mkdir httpserver && cd httpserver +$ cat>my_info.py>> from my_importer import install_meta +>>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder +>>> import my_info # 打印ok,说明导入成功 +ok +>>> my_info.name # 验证可以取得到变量 +'wangbm' +``` + + + +好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import 这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习` __import__ `以及 importlib 是非常有必要的。 + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From c431d392cc1a07a256e6b85a92a977206a2b0417 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 12 May 2020 21:46:06 +0800 Subject: [PATCH 063/147] =?UTF-8?q?Update=EF=BC=9A=E7=94=9F=E6=88=90=20rst?= =?UTF-8?q?=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- source/c01/c01_10.md | 1073 +++++++++++++++++++--- source/c01/c01_10.rst | 1965 +++++++++++++++++++++++++++++++---------- source/c01/c01_30.rst | 22 +- source/c01/c01_43.rst | 390 ++++++++ source/c01/c01_45.rst | 297 +++++++ source/c02/c02_06.md | 12 + source/c02/c02_06.rst | 15 + source/c04/c04_06.rst | 15 + source/c07/c07_09.rst | 13 +- source/c07/c07_16.rst | 15 + 11 files changed, 3198 insertions(+), 625 deletions(-) create mode 100644 source/c01/c01_43.rst create mode 100644 source/c01/c01_45.rst diff --git a/README.md b/README.md index cc71b2d..51059df 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - 1.7 [15个Pythonic的代码示例](http://python.iswbm.com/en/latest/c01/c01_07.html) - 1.8 [新式类和经典类的区别?](http://python.iswbm.com/en/latest/c01/c01_08.html) - 1.9 [多继承与Mixin设计模式](http://python.iswbm.com/en/latest/c01/c01_09.html) -- 1.10 [Python 冷知识 40 讲](http://python.iswbm.com/en/latest/c01/c01_10.html) +- 1.10 [Python 黑魔法指南 50 例](http://python.iswbm.com/en/latest/c01/c01_10.html) - 1.11 [正则表达式必知必会](http://python.iswbm.com/en/latest/c01/c01_11.html) - 1.12 [搞懂字符编码的前世今生](http://python.iswbm.com/en/latest/c01/c01_12.html) - 1.13 [Python几个高阶函数](http://python.iswbm.com/en/latest/c01/c01_13.html) @@ -28,7 +28,7 @@ - 1.26 [C语言基础的学习](http://python.iswbm.com/en/latest/c01/c01_26.html) - 1.27 [全面学习 Python 包:包的构建与分发](http://python.iswbm.com/en/latest/c01/c01_27.html) - 1.27 [如何阅读 CPython源码?](http://python.iswbm.com/en/latest/c01/c01_29.html) -- 1.30 [学习编程的几大网站](http://python.iswbm.com/en/latest/c01/c01_30.html) +- 1.30 [盘点程序员学习编程的那些网站](http://python.iswbm.com/en/latest/c01/c01_30.html) - 1.31 [学习 Pillow 笔记](http://python.iswbm.com/en/latest/c01/c01_31.html) - 1.32 [在 CentOS 7.2 上安装 Python3.7](http://python.iswbm.com/en/latest/c01/c01_32.html) - 1.33 [如何调试已经运行中的程序](http://python.iswbm.com/en/latest/c01/c01_33.html) @@ -41,7 +41,9 @@ - 1.40 [Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_40.html) - 1.41 [Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_41.html) - 1.42 [Python 炫技操作:海象运算符的三种用法](http://python.iswbm.com/en/latest/c01/c01_42.html) +- 1.43 [求你了,别再使用 pprint 打印字典了](http://python.iswbm.com/en/latest/c01/c01_43.html) - 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html) +- 1.45 [Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_45.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index 040e3b8..3c5fc47 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -1,15 +1,15 @@ -# 1.10 看到这 40个知识点,那些说精通 Python 的人脸都黑了 +# 1.10 Python 黑魔法指南 50 例 --- +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 -## 01. 万象皆对象,包括省略号 -`...` 这是省略号,在Python中,一切皆对象。它也不例外。 +## 01. 默默无闻的省略号很好用 -在 Python 中,它叫做 Ellipsis 。 +在Python中,一切皆对象,省略号也不例外。 -在 Python 3 中你可以直接写...来得到这玩意。 +在 Python 3 中你可以直接写 `...` 来得到它 ```python >>> ... Ellipsis @@ -17,7 +17,7 @@ Ellipsis ``` -而在 2 中没有...这个语法,只能直接写Ellipsis来获取。 +而在 Python 2 中没有`...` 这个语法,只能直接写Ellipsis来获取。 ```python >>> Ellipsis Ellipsis @@ -39,17 +39,31 @@ True 4362672336 ``` -这东西有啥用呢?据说它是Numpy的语法糖,不玩 Numpy 的人,可以说是没啥用的。 +那这东西有啥用呢? -在网上只看到这个 用 `...` 代替 pass ,稍微有点用,但又不是必须使用的。 -``` -try: - 1/0 -except ZeroDivisionError: +1. 它是 Numpy 的一个语法糖 +2. 在 Python 3 中可以使用 ... 代替 pass + +```shell +$ cat demo.py +def func01(): ... + +def func02(): + pass + +func01() +func02() + +print("ok") + +$ python3 demo.py +ok ``` -## 02. 使用 end 增强代码逻辑? + + +## 02. 使用 end 来结束代码块 有不少编程语言,循环、判断代码块需要用 end 标明结束,这样一定程度上会使代码逻辑更加清晰一点。 @@ -81,48 +95,38 @@ print(my_abs(-10)) 10 ``` +## 03. 可直接运行的 zip 包 +我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。 -## 03. 逗号也有它独特的用法 - -逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 - -**第一个用法** +这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。 -元组的转化 +那么这个zip 是如何制作的呢,请看下面的示例。 ```shell -[root@localhost ~]# cat demo.py -def func(): - return "ok", +[root@localhost ~]# ls -l demo +total 8 +-rw-r--r-- 1 root root 30 May 8 19:27 calc.py +-rw-r--r-- 1 root root 35 May 8 19:33 __main__.py +[root@localhost ~]# +[root@localhost ~]# cat demo/__main__.py +import calc -print(func()) -[root@localhost ~]# python3 demo.py -('ok',) +print(calc.add(2, 3)) +[root@localhost ~]# +[root@localhost ~]# cat demo/calc.py +def add(x, y): + return x+y +[root@localhost ~]# +[root@localhost ~]# python -m zipfile -c demo.zip demo/* +[root@localhost ~]# ``` -**第二个用法** - -print 的取消换行 +制作完成后,我们可以执行用 python 去执行它 ```shell -[root@localhost ~]# cat demo.py -for i in range(3): - print i -[root@localhost ~]# -[root@localhost ~]# python demo.py -0 -1 -2 -[root@localhost ~]# -[root@localhost ~]# vim demo.py -[root@localhost ~]# -[root@localhost ~]# cat demo.py -for i in range(3): - print i, -[root@localhost ~]# -[root@localhost ~]# python demo.py -0 1 2 +[root@localhost ~]# python demo.zip +5 [root@localhost ~]# ``` @@ -185,8 +189,6 @@ SyntaxError: EOL while scanning string literal ... - - 如果你尝试后,仍然写不出来,那我给出自己的做法。 ```python @@ -197,8 +199,6 @@ for i in iter(int, 1):pass **是不是傻了?iter 还有这种用法?这为啥是个死循环?** - - 关于这个问题,你如果看中文网站,可能找不到相关资料。 还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 @@ -215,6 +215,8 @@ for i in iter(int, 1):pass 由于int() 永远返回0,永远返回不了1,所以这个 for 循环会没有终点。一直运行下去。 +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + ## 06. 懒人必备技能:使用 “_” 对于 `_` ,大家对于他的印象都是用于 **占位符**,省得为一个不需要用到的变量,绞尽脑汁的想变量名。 @@ -316,8 +318,6 @@ ENABLE_USER_SITE: True 从输出你可以发现,这个列的路径会比 sys.path 更全,它包含了用户环境的目录。 -如果你不熟悉用户环境,可以参考我写的另一篇文章:[记 Python “用户环境”的一次完美应用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486191&idx=1&sn=e556df305ce1df4c4969d2fdbe884cfa&chksm=e8866a0ddff1e31b2ca8ca8fbbe0355fb8c24376d715224ef2176e613b5601764be0193e3aa4&token=1343865895&lang=zh_CN#rd) - ## 08. and 和 or 的取值顺序 and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它有妙用。 @@ -363,43 +363,53 @@ Python编程时光>>>for i in range(2): 1 ``` -## 10. 可直接运行 zip 包 +## 10. 逗号也有它独特的用法 -我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。 +逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 -这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。 +**第一个用法** -那么这个zip 是如何制作的呢,请看下面的示例。 +元组的转化 ```shell -[root@localhost ~]# ls -l demo -total 8 --rw-r--r-- 1 root root 30 May 8 19:27 calc.py --rw-r--r-- 1 root root 35 May 8 19:33 __main__.py -[root@localhost ~]# -[root@localhost ~]# cat demo/__main__.py -import calc +[root@localhost ~]# cat demo.py +def func(): + return "ok", -print(calc.add(2, 3)) -[root@localhost ~]# -[root@localhost ~]# cat demo/calc.py -def add(x, y): - return x+y -[root@localhost ~]# -[root@localhost ~]# python -m zipfile -c demo.zip demo/* -[root@localhost ~]# +print(func()) +[root@localhost ~]# python3 demo.py +('ok',) ``` -制作完成后,我们可以执行用 python 去执行它 +**第二个用法** + +print 的取消换行 ```shell -[root@localhost ~]# python demo.zip -5 +[root@localhost ~]# cat demo.py +for i in range(3): + print i +[root@localhost ~]# +[root@localhost ~]# python demo.py +0 +1 +2 +[root@localhost ~]# +[root@localhost ~]# vim demo.py +[root@localhost ~]# +[root@localhost ~]# cat demo.py +for i in range(3): + print i, +[root@localhost ~]# +[root@localhost ~]# python demo.py +0 1 2 [root@localhost ~]# ``` +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + ## 11. 默认参数最好不为可变对象 函数的参数分三种 @@ -407,12 +417,10 @@ def add(x, y): - 默认参数 - 关键字参数 -这三者的具体区别,和使用方法在 廖雪峰的教程 里会详细的解释。这里就不搬运了。 - -今天要说的是,传递默认参数时,新手很容易踩雷的一个坑。 +当你在传递默认参数时,有新手很容易踩雷的一个坑。 先来看一个示例 -``` +```python def func(item, item_list=[]): item_list.append(item) print(item_list) @@ -443,7 +451,7 @@ Python 中的 def 语句在每次执行的时候都初始化一个函数对象 大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。 这里先看一下例子 -``` +```python class Kls(): def public(self): print('Hello public world!') @@ -477,28 +485,24 @@ ins.call_private() ## 13. 时有时无的切片异常 -这是个简单例子 +这是个简单例子,alist 只有5 个元素,当你取第 6 个元素时,会抛出索引异常。这与我们的认知一致。 ```python -my_list = [1, 2, 3, 4, 5] -print(my_list[5]) -``` -执行一下,和我们预期的一样,会抛出索引异常。 -``` +>>> alist = [0, 1, 2, 3, 4] +>>> alist[5] Traceback (most recent call last): - File "F:/Python Script/test.py", line 2, in - print(my_list[5]) + File "", line 1, in IndexError: list index out of range ``` - -但是今天要说的肯定不是这个,而是一个你可能会不知道的冷知识。 - -来看看,如下这种写法就不会报索引异常,执行my_list[5:],会返回一个新list:[]。 +但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 6个元素,也不抛出异常,而是会返回一个新的列表。 ```python -my_list = [1, 2, 3] -print(my_list[5:]) +>>> alist = [0, 1, 2, 3, 4] +>>> alist[5:] +[] +>>> alist[100:] +[] ``` -## 14. 哪些情况下不需要续行符 +## 14. 哪些情况下不需要续行符? 在写代码时,为了代码的可读性,代码的排版是尤为重要的。 @@ -525,15 +529,15 @@ talk is cheap,show me the code. >>> my_dict={"name": "MING", ... "gender": "male"} ``` -另外还有,在多行文本注释中 `'''` ,续行符也是可以不写的。 +另外还有,在多行文本注释中 `'''` ,续行符也是可以不写的。 ``` >>> text = '''talk is cheap, ... show me the code''' ``` -## 15. 2.x 下 也可以使用 print(“”) +## 15. Python2下 也能使用 print(“”) -可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 只能使用print ""。 +可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 只能使用`print ""`。 但是其实并不是这样的。 @@ -557,6 +561,8 @@ print ("hello") 虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print ,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 python2.6+下使用 print() 后,就成了函数。 +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + ## 16. 迷一样的字符串 示例一 @@ -775,6 +781,8 @@ def my_reverse(str): [3, 2, 1] ``` +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + ## 21. 改变默认递归次数限制 上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。 @@ -933,26 +941,28 @@ Python 作为一门高级语言,它的编写符合人类的思维逻辑,包 2 ``` +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + ## 26. 数值与字符串的比较 在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。 -``` +```Python >>> 100000000 < "" True ->>> 100000000 < "ming" +>>> 100000000 < "hello" True ``` 但在 Python3 中,却不行。 -``` +```python >>> 100000000 < "" TypeError: '<' not supported between instances of 'int' and 'str' ``` ## 27. 循环中的局部变量泄露 -在Python 2中x的值在一个循环执行之后被改变了。 -``` +在Python 2中 x 的值在一个循环执行之后被改变了。 +```python # Python2 >>> x = 1 >>> [x for x in range(5)] @@ -961,7 +971,7 @@ TypeError: '<' not supported between instances of 'int' and 'str' 4 ``` 不过在Python3 中这个问题已经得到解决了。 -``` +```python # Python3 >>> x = 1 >>> [x for x in range(5)] @@ -972,7 +982,7 @@ TypeError: '<' not supported between instances of 'int' and 'str' ## 28. 字典居然是可以排序的? -字典不可排序的思想,似乎已经根深蒂固。 +在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。 ```python # Python2.7.10 @@ -981,9 +991,9 @@ TypeError: '<' not supported between instances of 'int' and 'str' {'1': 1, '0': 0, '3': 3, '2': 2, '4': 4} ``` -假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,这是真的。 +假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的 -在 Python3 中字典已经是有序的。 +在 Python3.6 + 中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。 ```python # Python3.6.7 @@ -1050,41 +1060,53 @@ Namespaces are one honking great idea -- let's do more of those! 在开始讲之前,你可以试着运行一下下面这小段代码。 -``` +```python +# demo.py a = 1 -def func01(): +def add(): a += 1 - -func01() +add() ``` -看似没有毛病,但实则已经犯了一个很基础的问题,这个报错相当常见吧? +看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下: ```python ->>> func01() +$ python demo.py Traceback (most recent call last): - File "", line 1, in - File "", line 2, in func01 + File "demo.py", line 6, in + add() + File "demo.py", line 4, in add + a += 1 UnboundLocalError: local variable 'a' referenced before assignment ``` 回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。 -当程序运行到 `a += 1` 时,Python 解释器就认为在函数内部要给 `a` 这个变量赋值,当然就把 `a` 当做局部变量了,报错是理所应当的。 +当程序运行到 `a += 1` 时,Python 解释器就认为在函数内部要给 `a` 这个变量赋值,当然就把 `a` 当做局部变量了,但是做为局部变量的 a 还没有被还没被定义。 + +因此报错是正常的。 理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢? -``` +```python +$ cat demo.py a = 1 -def func02(): +def output(): print(a) - -func02() + +output() + +$ python demo.py +1 ``` + + +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + ## 31. 字母也玩起了障眼法 以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。 @@ -1252,6 +1274,8 @@ a = 1; a = a + 1 所以在能使用增量赋值的时候尽量使用它。 +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + ## 36. x == +x 吗? 在大多数情况下,这个等式是成立的。 @@ -1322,8 +1346,6 @@ Debian 这么设计的原因,是为了减少不同来源的 Python 之间产 /usr/lib/python2.7/site-packages ``` - - ## 39. argument 和 parameter 的区别 arguments 和 parameter 的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。 @@ -1344,36 +1366,825 @@ output_msg("error") ## 40. 简洁而优雅的链式比较 -先给看一个示例 +先给你看一个示例: -``` +```python >>> False == False == True False ``` -你知道这个表达式会返回 False 吗? +你知道这个表达式为什么会会返回 False 吗? -我再给你举个例子,你可能就懂了。 +它的运行原理与下面这个类似,是不是有点头绪了: -``` -f 18 < age < 60: - print("young man") +```python +if 80 < score <= 90: + print("成绩良好") ``` -如果还不明白,再给你整个等价写法。 +如果你还是不明白,那我再给你整个第一个例子的等价写法。 -``` +```python >>> False == False and False == True False ``` +这个用法叫做链式比较。 + + + +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +## 41. 连接多个列表最极客的方式 + +```python +>>> a = [1,2] +>>> b = [3,4] +>>> c = [5,6] +>>> +>>> sum((a,b,c), []) +[1, 2, 3, 4, 5, 6] +``` + +## 42. 另外 8 种连接列表的方式 + +**1. 最直观的相加** + +使用 `+` 对多个列表进行相加,你应该懂,不多说了。 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> list01 + list02 + list03 +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +**2. 借助 itertools** + +itertools 在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +在前面的文章中也介绍过,使用 `itertools.chain()` 函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 + +最后你再利用 list 将其转化为 列表。 + +```python +>>> from itertools import chain +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> list(chain(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + +**3. 使用 * 解包** + +使用 `*` 可以解包列表,解包后再合并。 + +示例如下: + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> [*list01, *list02] +[1, 2, 3, 4, 5, 6] +>>> +``` + + + +**4. 使用 extend** + +在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> list01.extend(list02) +>>> list01 +[1, 2, 3, 4, 5, 6] +``` + +**5. 使用列表推导式** + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? + +当然可以,具体示例代码如下: + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> [x for l in (list01, list02, list03) for x in l] +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +**6. 使用 heapq** + +heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 + +该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> from heapq import merge +>>> +>>> list(merge(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + +要注意的是,heapq.merge 除了合并多个列表外,它还会将合并后的最终的列表进行排序。 + +```python +>>> list01 = [2,5,3] +>>> list02 = [1,4,6] +>>> list03 = [7,9,8] +>>> +>>> from heapq import merge +>>> +>>> list(merge(list01, list02, list03)) +[1, 2, 4, 5, 3, 6, 7, 9, 8] +>>> +``` + +它的效果等价于下面这行代码: + +```python +sorted(itertools.chain(*iterables)) +``` + +如果你希望得到一个始终有序的列表,那请第一时间想到 heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 + +**7. 借助魔法方法** +有一个魔法方法叫 `__add__`,当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的。 + +所以以下两种方法其实是等价的 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> list01 + list02 +[1, 2, 3, 4, 5, 6] +>>> +>>> +>>> list01.__add__(list02) +[1, 2, 3, 4, 5, 6] +>>> +``` +借用这个魔法特性,我们可以 reduce 这个方法来对多个列表进行合并,示例代码如下 +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> from functools import reduce +>>> reduce(list.__add__, (list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +**8. 使用 yield from** + +在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 + +因此,我们可以像下面这样自定义一个合并列表的工具函数。 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> def merge(*lists): +... for l in lists: +... yield from l +... +>>> list(merge(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +## 43. 在程序退出前执行代码的技巧 + +使用 atexit 这个内置模块,可以很方便的注册退出函数。 + +不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 + +示例如下 + +![](http://image.iswbm.com/20200510112133.png) + +如果`clean()`函数有参数,那么你可以不用装饰器,而是直接调用`atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')`。 + +可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit 来得优雅,来得方便,并且它很容易扩展。 + +但是使用 atexit 仍然有一些局限性,比如: + +- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 +- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 +- 如果你手动调用了`os._exit()`,你注册的函数无法正常执行。 + +## 44. 合并字典的 8 种方法 + +**1. 最简单的原地更新** + +字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> profile.update(ext_info) +>>> print(profile) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +如果想使用 update 这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> from copy import deepcopy +>>> +>>> full_profile = deepcopy(profile) +>>> full_profile.update(ext_info) +>>> +>>> print(full_profile) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> print(profile) +{"name": "xiaoming", "age": 27} +``` + + + +**2. 先解包再合并字典** + +使用 `**` 可以解包字典,解包完后再使用 dict 或者 `{}` 就可以合并。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> full_profile01 = {**profile, **ext_info} +>>> print(full_profile01) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> +>>> full_profile02 = dict(**profile, **ext_info) +>>> print(full_profile02) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +若你不知道 `dict(**profile, **ext_info)` 做了啥,你可以将它等价于 + +```python +>>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +**3. 借助 itertools** + +在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +正好我们字典也是可迭代对象,自然就可以想到,可以使用 `itertools.chain()` 函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 dict 转成字典。 + +```python +>>> import itertools +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> +>>> dict(itertools.chain(profile.items(), ext_info.items())) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +**4. 借助 ChainMap** + +如果可以引入一个辅助包,那我就再提一个, `ChainMap` 也可以达到和 `itertools` 同样的效果。 + +```python +>>> from collections import ChainMap +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(ChainMap(profile, ext_info)) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +使用 ChainMap 有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 itertools 就不会有这个问题)。 + +```python +>>> from collections import ChainMap +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info={"age": 30} +>>> dict(ChainMap(profile, ext_info)) +{'name': 'xiaoming', 'age': 27} +``` + + + +**5. 使用dict.items() 合并** + +在 Python 3.9 之前,其实就已经有 `|` 操作符了,只不过它通常用于对集合(set)取并集。 + +利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 + +你得先利用 `items` 方法将 dict 转成 dict_items,再对这两个 dict_items 取并集,最后利用 dict 函数,转成字典。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> full_profile = dict(profile.items() | ext_info.items()) +>>> full_profile +{'gender': 'male', 'age': 27, 'name': 'xiaoming'} +``` + + + +当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list 函数再合并(示例为 Python 3.x ) + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(list(profile.items()) + list(ext_info.items())) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + +若你在 Python 2.x 下,可以直接省去 list 函数。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(profile.items() + ext_info.items()) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +**6. 最酷炫的字典解析式** + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? + +当然可以,具体示例代码如下: + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> {k:v for d in [profile, ext_info] for k,v in d.items()} +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +**7. Python 3.9 新特性** + +在 2 月份发布的 Python 3.9.04a 版本中,新增了一个抓眼球的新操作符操作符: `|`, PEP584 将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> profile | ext_info +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> +>>> ext_info | profile +{'gender': 'male', 'name': 'xiaoming', 'age': 27} +>>> +>>> +``` + +除了 `|` 操作符之外,还有另外一个操作符 `|=`,类似于原地更新。 + +```python +>>> ext_info |= profile +>>> ext_info +{'gender': 'male', 'name': 'xiaoming', 'age': 27} +>>> +>>> +>>> profile |= ext_info +>>> profile +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` + + + +看到这里,有没有涨姿势了,学了这么久的 Python ,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 7 种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 + + + +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +## 45. 条件语句的七种写法 + +**第一种:原代码** + +这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。 + +```python +if age > 18: + return "已成年" +else: + return "未成年" +``` + +下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外) + +**第二种** + +语法: + +```python + if else +``` + +例子 + +```python +>>> age1 = 20 +>>> age2 = 17 +>>> +>>> +>>> msg1 = "已成年" if age1 > 18 else "未成年" +>>> print msg1 +已成年 +>>> +>>> msg2 = "已成年" if age2 > 18 else "未成年" +>>> print msg2 +未成年 +>>> +``` + +**第三种** + +语法 + +```python + and or +``` + +例子 + +```python +>>> msg1 = age1 > 18 and "已成年" or "未成年" +>>> msg2 = "已成年" if age2 > 18 else "未成年" +>>> +>>> print(msg1) +已成年 +>>> +>>> print(msg2) +未成年 +``` + +**第四种** + +语法 + +```python +(, )[condition] +``` + +例子 + +```python +>>> msg1 = ("未成年", "已成年")[age1 > 18] +>>> print(msg1) +已成年 +>>> +>>> +>>> msg2 = ("未成年", "已成年")[age2 > 18] +>>> print(msg2) +未成年 +``` + +**第五种** + +语法 + +```python +(lambda: , lambda:)[]() +``` + +例子 + +```python +>>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() +>>> print(msg1) +已成年 +>>> +>>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() +>>> print(msg2) +未成年 +``` + +**第六种** + +语法: + +```python +{True: , False: }[] +``` + +例子: + +```python +>>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] +>>> print(msg1) +已成年 +>>> +>>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] +>>> print(msg2) +未成年 +``` + +**第七种** + +语法 + +```python +(() and (,) or (,))[0] +``` + +例子 + +```python +>>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] +>>> print(msg1) +已成年 +>>> +>>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] +>>> print(msg2) +未成年 +``` + +以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 + +看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 + +> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +## 46. /usr/bin/env python 有什么用? + +我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 + +```shell +#!/usr/bin/python +``` + +或者这样 + +```sh e llsh el +#!/usr/bin/env python +``` + +这两者有什么区别呢? + +稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python` + +![](http://image.python-online.cn/20200331184021.png) + +而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。 + +那么加和不加有什么区别呢? + +不加的话,你每次执行这个脚本时,都得这样: `python xx.py` , + +![](http://image.python-online.cn/20200331185034.png) + +有没有一种方式?可以省去每次都加 `python` 呢? + +当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 + +![](http://image.python-online.cn/20200331184755.png) + +明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ? + +当我执行 `env python` 时,自动进入了 python console 的模式。 + +![](http://image.python-online.cn/20200331185741.png) + +这是为什么?和 直接执行 python 好像没什么区别呀 + +当你执行 `env python` 时,它其实会去 `env | grep PATH` 里(也就是 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin )这几个路径里去依次查找名为python的可执行文件。 + +找到一个就直接执行,上面我们的 python 路径是在 `/usr/bin/python` 里,在 `PATH` 列表里倒数第二个目录下,所以当我在 `/usr/local/sbin` 下创建一个名字也为 python 的可执行文件时,就会执行 `/usr/bin/python` 了。 + +具体演示过程,你可以看下面。 + +![](http://image.python-online.cn/20200331190224.png) + +那么对于这两者,我们应该使用哪个呢? + +个人感觉应该优先使用 `#!/usr/bin/env python`,因为不是所有的机器的 python 解释器都是 `/usr/bin/python` 。 + +## 47. 让我爱不释手的用户环境 + +当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢? + +可以使用 `pip install --user pkg` 将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。 + +```shell +# 在全局环境中未安装 requests +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ su - wangbm + +# 由于用户环境继承自全局环境,这里也未安装 +[wangbm@localhost ~]$ pip list | grep requests +[wangbm@localhost ~]$ pip install --user requests +[wangbm@localhost ~]$ pip list | grep requests +requests (2.22.0) +[wangbm@localhost ~]$ + +# 从 Location 属性可发现 requests 只安装在当前用户环境中 +[wangbm@localhost ~]$ pip show requests +--- +Metadata-Version: 2.1 +Name: requests +Version: 2.22.0 +Summary: Python HTTP for Humans. +Home-page: http://python-requests.org +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +Installer: pip +License: Apache 2.0 +Location: /home/wangbm/.local/lib/python2.7/site-packages +[wangbm@localhost ~]$ exit +logout + +# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ +``` + + + +## 48. 实现类似 defer 的延迟调用 + +在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 + +```go +import "fmt" + +func myfunc() { + fmt.Println("B") +} + +func main() { + defer myfunc() + fmt.Println("A") +} +``` + +输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 的调用写在函数的第一行,这就是延迟调用。 + +``` +A +B +``` + +那么在 Python 中否有这种机制呢? + +当然也有,只不过并没有 Golang 这种简便。 + +在 Python 可以使用 **上下文管理器** 达到这种效果 + +```python +import contextlib + +def callback(): + print('B') + +with contextlib.ExitStack() as stack: + stack.callback(callback) + print('A') +``` + +输出如下 + +``` +A +B +``` + +## 49. 自带的缓存机制不用白不用 + +缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 + +数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 + +为了实现这个需求,Python 3.2 + 中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。 + +这个机制实现于 functool 模块中的 lru_cache 装饰器。 + +```python +@functools.lru_cache(maxsize=None, typed=False) +``` + +参数解读: + +- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 2 的幂时,性能最佳 +- typed:若为 True,则不同参数类型的调用将分别缓存。 + +举个例子 + +```python +from functools import lru_cache + +@lru_cache(None) +def add(x, y): + print("calculating: %s + %s" % (x, y)) + return x + y + +print(add(1, 2)) +print(add(1, 2)) +print(add(2, 3)) +``` + +输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果 + +```shell +calculating: 1 + 2 +3 +3 +calculating: 2 + 3 +5 +``` + +## 50. 重定向标准输出到日志 + +假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。 + +检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。 + +如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。 + +由于最初约定的脚本返回方式是以 JSON 的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。 + +如何避免这种情况的发生呢? + +我们可以这样做,把你的标准错误输出到日志文件中。 + +```python +import contextlib + +log_file="/var/log/you.log" + +def you_task(): + pass + +@contextlib.contextmanager +def close_stdout(): + raw_stdout = sys.stdout + file = open(log_file, 'a+') + sys.stdout = file + + yield + + sys.stdout = raw_stdout + file.close() + +with close_stdout(): + you_task() +``` -## 附录:参考文章 -- [wtfpython](https://github.com/satwikkansal/wtfpython) --- diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index c7ebb50..cf3d866 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1,16 +1,16 @@ -1.10 Python 冷知识 40 讲 -======================== +1.10 Python 黑魔法指南 50 例 +============================ -------------- -01. 省略号也是对象 ------------------- + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 -``...`` 这是省略号,在Python中,一切皆对象。它也不例外。 +01. 默默无闻的省略号很好用 +-------------------------- -在 Python 中,它叫做 Ellipsis 。 +在Python中,一切皆对象,省略号也不例外。 -在 Python 3 中你可以直接写…来得到这玩意。 +在 Python 3 中你可以直接写 ``...`` 来得到它 .. code:: python @@ -19,7 +19,7 @@ >>> type(...) -而在 2 中没有…这个语法,只能直接写Ellipsis来获取。 +而在 Python 2 中没有\ ``...`` 这个语法,只能直接写Ellipsis来获取。 .. code:: python @@ -45,76 +45,320 @@ >>> id(...) 4362672336 -这东西有啥用呢?据说它是Numpy的语法糖,不玩 Numpy -的人,可以说是没啥用的。 +那这东西有啥用呢? -在网上只看到这个 用 ``...`` 代替 pass ,稍微有点用,但又不是必须使用的。 +1. 它是 Numpy 的一个语法糖 +2. 在 Python 3 中可以使用 … 代替 pass -:: +.. code:: shell - try: - 1/0 - except ZeroDivisionError: + $ cat demo.py + def func01(): ... -02. 类首字母不一定是大写 ------------------------- + def func02(): + pass -在正常情况下,我们所编写的所见到的代码,好像都默许了类名首字母大写,而实例用小写的这一准则。但这并不是强制性的,即使你反过来的也没有关系。 + func01() + func02() -但有一些内置的类,首字母都是小写,而实例都是大写。 + print("ok") -比如 bool 是类名,而 True,False 是其实例; 比如 ellipsis -是类名,Ellipsis是实例; 还有 int,string,float,list,tuple,dict -等一系列数据类型都是类名,它们都是小写。 + $ python3 demo.py + ok -03. 增量赋值的性能更好 ----------------------- +02. 使用 end 来结束代码块 +------------------------- -诸如 ``+=`` 和 ``*=`` 这些运算符,叫做 增量赋值运算符。 +有不少编程语言,循环、判断代码块需要用 end +标明结束,这样一定程度上会使代码逻辑更加清晰一点。 -这里使用用 += 举例,以下两种写法,在效果上是等价的。 +但是其实在 Python 这种严格缩进的语言里并没有必要这样做。 -:: +如果你真的想用,也不是没有办法,具体你看下面这个例子。 - # 第一种 - a = 1 ; a += 1 +.. code:: python - # 第二种 - a = 1; a = a + 1 + __builtins__.end = None -``+=`` 其背后使用的魔法方法是 -\__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add_\_ 。 -这两种写法有什么区别呢? + def my_abs(x): + if x > 0: + return x + else: + return -x + end + end -用列表举例 a += b,使用 \__add_\_ 的话就像是使用了a.extend(b),如果使用 -\__add_\_ 的话,则是 a = -a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 + print(my_abs(10)) + print(my_abs(-10)) -所以在能使用增量赋值的时候尽量使用它。 +执行后,输出如下 + +.. code:: shell + + [root@localhost ~]$ python demo.py + 10 + 10 + +03. 可直接运行的 zip 包 +----------------------- + +我们可以经常看到有 Python 包,居然可以以 zip +包进行发布,并且可以不用解压直接使用。 + +这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 +是 egg,要嘛是whl 格式。 + +那么这个zip 是如何制作的呢,请看下面的示例。 + +.. code:: shell + + [root@localhost ~]# ls -l demo + total 8 + -rw-r--r-- 1 root root 30 May 8 19:27 calc.py + -rw-r--r-- 1 root root 35 May 8 19:33 __main__.py + [root@localhost ~]# + [root@localhost ~]# cat demo/__main__.py + import calc + + print(calc.add(2, 3)) + [root@localhost ~]# + [root@localhost ~]# cat demo/calc.py + def add(x, y): + return x+y + [root@localhost ~]# + [root@localhost ~]# python -m zipfile -c demo.zip demo/* + [root@localhost ~]# + +制作完成后,我们可以执行用 python 去执行它 + +.. code:: shell + + [root@localhost ~]# python demo.zip + 5 + [root@localhost ~]# + +04. 反斜杠的倔强: 不写最后 +-------------------------- + +``\`` 在 Python 中的用法主要有两种 + +**1、在行尾时,用做续行符** + +.. code:: python + + [root@localhost ~]$ cat demo.py + print("hello "\ + "world") + [root@localhost ~]$ + [root@localhost ~]$ python demo.py + hello world + +**2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。** + +.. code:: python + + >>> str1='\nhello'  #换行 + >>> print(str1) + + hello + >>> str2='\thello'  #tab + >>> print(str2) + hello + +但是如果你用单\ ``\``\ 结尾是会报语法错误的 + +.. code:: python + + >>> str3="\" + File "", line 1 + str3="\" + ^ + SyntaxError: EOL while scanning string literal + +就算你指定它是个 raw 字符串,也不行。 + +.. code:: python + + >>> str3=r"\" + File "", line 1 + str3=r"\" + ^ + SyntaxError: EOL while scanning string literal + +05. 单行实现 for 死循环如何写? +------------------------------- + +如果让你在不借助 while ,只使用 for 来写一个死循环? + +**你会写吗?** + +**如果你还说简单,你可以自己试一下。** + +… + +如果你尝试后,仍然写不出来,那我给出自己的做法。 + +.. code:: python + + for i in iter(int, 1):pass + +**是不是傻了?iter 还有这种用法?这为啥是个死循环?** + +关于这个问题,你如果看中文网站,可能找不到相关资料。 + +还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 + +原来iter有两种使用方法。 + +- 通常我们的认知是第一种,将一个列表转化为一个迭代器。 + +- 而第二种方法,他接收一个 callable对象,和一个sentinel + 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 + +那\ ``int`` 呢? + +这又是一个知识点,int +是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console +模式下输入 ``int()`` 看看是不是返回0。 + +由于int() 永远返回0,永远返回不了1,所以这个 for +循环会没有终点。一直运行下去。 + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +06. 懒人必备技能:使用 “_” +-------------------------- + +对于 ``_`` ,大家对于他的印象都是用于 +**占位符**\ ,省得为一个不需要用到的变量,绞尽脑汁的想变量名。 + +今天要介绍的是他的第二种用法,就是在 console 模式下的应用。 + +示例如下: + +.. code:: python + + >>> 3 + 4 + 7 + >>> _ + 7 + >>> name='公众号: Python编程时光' + >>> name + '公众号: Python编程时光' + >>> _ + '公众号: Python编程时光' + +它可以返回上一次的运行结果。 + +但是,如果是print函数打印出来的就不行了。 + +.. code:: python + + >>> 3 + 4 + 7 + >>> _ + 7 + >>> print("公众号: Python编程时光") + ming + >>> _ + 7 + +我自己写了个例子,验证了下,用\ ``__repr__``\ 输出的内容可以被获取到的。 +首先,在我们的目录下,写一个文件 demo.py。内容如下 + +.. code:: python + + # demo.py + class mytest(): + def __str__(self): + return "hello" + + def __repr__(self): + return "world" + +然后在这个目录下进入交互式环境。 -04. and 和 or 的取值顺序 +.. code:: python + + >>> import demo + >>> mt=demo.mytest() + >>> mt + world + >>> print(mt) + hello + >>> _ + world + +知道这两个魔法方法的人,一看就明白了,这里不再解释啦。 + +07. 最快查看包搜索路径的方式 +---------------------------- + +当你使用 import 导入一个包或模块时,Python +会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path +查看。 + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path) + ['', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages'] + >>> + +那有没有更快的方式呢? + +我这有一种连 console 模式都不用进入的方法,一行命令即可解决 + +.. code:: shell + + [wangbm@localhost ~]$ python3 -m site + sys.path = [ + '/home/wangbm', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages', + ] + USER_BASE: '/home/wangbm/.local' (exists) + USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) + ENABLE_USER_SITE: True + +从输出你可以发现,这个列的路径会比 sys.path +更全,它包含了用户环境的目录。 + +08. and 和 or 的取值顺序 ------------------------ -and 和 or -是我们再熟悉不过的两个逻辑运算符。而我们通常只用它来做判断,很少用它来取值。 +and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它有妙用。 -如果一个or表达式中所有值都为真,Python会选择第一个值,而and表达式则会选择第二个。 +- 当一个 **or 表达式**\ 中所有值都为真,Python会选择第一个值 -:: +- 当一个 **and 表达式** 所有值都为真,Python 会选择第二个值。 + +示例如下: + +.. code:: python >>>(2 or 3) * (5 and 7) 14 # 2*7 -05. 如何修改解释器提示符 +09. 如何修改解释器提示符 ------------------------ 这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。 正常情况下,我们在 终端下 执行Python 命令是这样的。 -:: +.. code:: python >>> for i in range(2): ... print (i) @@ -124,7 +368,7 @@ and 和 or 你是否想过 ``>>>`` 和 ``...`` 这两个提示符也是可以修改的呢? -:: +.. code:: python >>> import sys >>> sys.ps1 @@ -140,19 +384,64 @@ and 和 or 0 1 -06. 默认参数最好不为可变对象 +10. 逗号也有它独特的用法 +------------------------ + +逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 + +**第一个用法** + +元组的转化 + +.. code:: shell + + [root@localhost ~]# cat demo.py + def func(): + return "ok", + + print(func()) + [root@localhost ~]# python3 demo.py + ('ok',) + +**第二个用法** + +print 的取消换行 + +.. code:: shell + + [root@localhost ~]# cat demo.py + for i in range(3): + print i + [root@localhost ~]# + [root@localhost ~]# python demo.py + 0 + 1 + 2 + [root@localhost ~]# + [root@localhost ~]# vim demo.py + [root@localhost ~]# + [root@localhost ~]# cat demo.py + for i in range(3): + print i, + [root@localhost ~]# + [root@localhost ~]# python demo.py + 0 1 2 + [root@localhost ~]# + +.. + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +11. 默认参数最好不为可变对象 ---------------------------- 函数的参数分三种 - 可变参数 - 默认参数 - 关键字参数 -这三者的具体区别,和使用方法在 廖雪峰的教程 -里会详细的解释。这里就不搬运了。 - -今天要说的是,传递默认参数时,新手很容易踩雷的一个坑。 +当你在传递默认参数时,有新手很容易踩雷的一个坑。 先来看一个示例 -:: +.. code:: python def func(item, item_list=[]): item_list.append(item) @@ -184,14 +473,14 @@ Python 中的 def |image0| -07. 访问类中的私有方法 +12. 访问类中的私有方法 ---------------------- 大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。 这里先看一下例子 -:: +.. code:: python class Kls(): def public(self): @@ -224,36 +513,33 @@ Python 中的 def ins._Kls__private() ins.call_private() -08. 时有时无的切片异常 +13. 时有时无的切片异常 ---------------------- -这是个简单例子 +这是个简单例子,alist 只有5 个元素,当你取第 6 +个元素时,会抛出索引异常。这与我们的认知一致。 .. code:: python - my_list = [1, 2, 3, 4, 5] - print(my_list[5]) - -执行一下,和我们预期的一样,会抛出索引异常。 - -:: - + >>> alist = [0, 1, 2, 3, 4] + >>> alist[5] Traceback (most recent call last): - File "F:/Python Script/test.py", line 2, in - print(my_list[5]) + File "", line 1, in IndexError: list index out of range -但是今天要说的肯定不是这个,而是一个你可能会不知道的冷知识。 - -来看看,如下这种写法就不会报索引异常,执行my_list[5:],会返回一个新list:[]。 +但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 +6个元素,也不抛出异常,而是会返回一个新的列表。 .. code:: python - my_list = [1, 2, 3] - print(my_list[5:]) + >>> alist = [0, 1, 2, 3, 4] + >>> alist[5:] + [] + >>> alist[100:] + [] -09. 哪些情况下不需要续行符 --------------------------- +14. 哪些情况下不需要续行符? +---------------------------- 在写代码时,为了代码的可读性,代码的排版是尤为重要的。 @@ -289,20 +575,13 @@ Python 中的 def >>> text = '''talk is cheap, ... show me the code''' -上面只举了一些简单的例子。 - -但你要学会举一反三。一样的,在以下这些场景也同样适用 - -- 类,和函数的定义。 -- 列表推导式,字典推导式,集合推导式,生成器表达式 - -10. Py2 也可以使用 print() --------------------------- +15. Python2下 也能使用 print(“”) +-------------------------------- -我相信应该有不少人,思维定式,觉得只有 Py3 才可以使用 print(),而 Py2 -只能使用print ’’。 +可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 +只能使用\ ``print ""``\ 。 -今天,小明要为 Py2 正名一次。 +但是其实并不是这样的。 在Python 2.6之前,只支持 @@ -325,12 +604,18 @@ Python 中的 def print("hello") print ("hello") -11. 奇怪的字符串 ----------------- +虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print +,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 +python2.6+下使用 print() 后,就成了函数。 + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +16. 迷一样的字符串 +------------------ 示例一 -:: +.. code:: python # Python2.7 >>> a = "Hello_Python" @@ -348,7 +633,7 @@ Python 中的 def 示例二 -:: +.. code:: python >>> a = "MING" >>> b = "MING" @@ -367,7 +652,7 @@ Python 中的 def 示例三 -:: +.. code:: python # Python2.7 >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' @@ -381,19 +666,20 @@ Python 中的 def >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' True -详细解释这么不多说了(比较多),感兴趣的朋友,可以加我微信交流。 +17. return不一定都是函数的终点 +------------------------------ -12. 两次 return ---------------- +众所周知,try…finally… +的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。 -我们都知道,try…finally… -语句的用法,不管try里面是正常执行还是报异常,最终都能保证finally能够执行。 +同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。 -同时,我们又知道,一个函数里只要遇到 return 函数就会立马结束。 +那问题就来了,以上这两种规则,如果同时存在,Python +解释器会如何选择?哪个优先级更高? -基于以上这两点,我们来看看这个例子,到底运行过程是怎么样的? +写个示例验证一下,就明白啦 -:: +.. code:: python >>> def func(): ... try: @@ -404,51 +690,49 @@ Python 中的 def >>> func() 'finally' -惊奇的发现,在\ ``try``\ 里的return居然不起作用。 - -原因是,在try…finally…语句中,try中的return会被直接忽视,因为要保证finally能够执行。 - -13. for 死循环 --------------- - -for 循环可以说是 基础得不能再基础的知识点了。 +从输出中,我们可以发现:在try…finally…语句中,try中的 return +会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally +能够执行。 -但是如果让你用 for 写一个死循环,你会写吗?(问题来自群友 陈**) +**如果 try 里的 return 真的是直接被忽视吗?** -这是个开放性的问题,在往下看之前,建议你先尝试自己思考,你会如何解答。 +我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的 +return 真的是直接被忽视,那当finally 下没有显式的 return +的时候,是不是会返回None呢? -好了,如果你还没有思路,那就来看一下 一个海外 MIT 群友的回答: - -:: - - for i in iter(int, 1):pass - -是不是懵逼了。iter 还有这种用法?这为啥是个死循环? - -这真的是个冷知识,关于这个知识点,你如果看中文网站,可能找不到相关资料。 - -还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 +还是写个 示例来验证一下: -原来iter有两种使用方法,通常我们的认知是第一种,将一个列表转化为一个迭代器。 +.. code:: python -而第二种方法,他接收一个 callable对象,和一个sentinel -参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 + >>> def func(): + ... try: + ... return 'try' + ... finally: + ... print('finally') + ... + >>> + >>> func() + finally + 'try' + >>> -那\ ``int`` 呢,这又是一个知识点,int -是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在终端上输入 -``int()`` 看看是不是返回0。 +从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return +仍然还是有效的。 -由于int() 永远返回0,永远返回不了1,所以这个 for -循环会没有终点。一直运行下去。 +那结论就出来了,如果 finally 里有显式的 return,那么这个 return +会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 +try 里的 return 仍然有效。 -这些问题和答案都源自于群友的智慧。如果你也想加入我们的讨论中,请到公众号后台,添加我个人微信。 +18. 用户无感知的小整数池 +------------------------ -14. 小整数池 ------------- +为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] +这些整数对象是提前建立好的,不会被垃圾回收。 -先看例子。 +以上代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE +的影响,效果会有所不同。 -:: +.. code:: python >>> a = -6 >>> b = -6 @@ -469,18 +753,12 @@ for 循环可以说是 基础得不能再基础的知识点了。 >>> a is b True -为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] -这些整数对象是提前建立好的,不会被垃圾回收。 - -以上代码请在 -终端Python环境下测试,如果你是在IDE中测试,并不是这样的效果。 - -那最后一个示例,为啥又是True? +**问题又来了:最后一个示例,为啥是True?** 因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两成的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。 -15. intern机制 --------------- +19. 神奇的 intern 机制 +---------------------- 字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化. @@ -521,79 +799,14 @@ intern(字符串驻留)的技术来提高字符串效率,什么是intern >>> s1 is s2 False -16. 交互式“_”操作符 -------------------- +20. 反转字符串/列表最优雅的方式 +------------------------------- -对于 ``_`` ,我想很多人都非常熟悉。 +反转序列并不难,但是如何做到最优雅呢? -给变量取名好艰难,用 ``_``\ ; 懒得长长的变量名,用 ``_``\ ; -无用的垃圾变量,用 ``_``\ ; +先来看看,正常是如何反转的。 -以上,我们都很熟悉了,今天要介绍的是他在交互式中使用。 - -.. code:: python - - >>> 3 + 4 - 7 - >>> _ - 7 - >>> name='ming' - >>> name - 'ming' - >>> _ - 'ming' - -它可以返回上一次的运行结果。 - -但是,如果是print函数打印出来的就不行了。 - -.. code:: python - - >>> 3 + 4 - 7 - >>> _ - 7 - >>> print("ming") - ming - >>> _ - 7 - -我自己写了个例子,验证了下,用\ ``__repr__``\ 输出的内容可以被获取到的。 -首先,在我们的目录下,写一个文件 ming.py。内容如下 - -.. code:: python - - # ming.py - class mytest(): - def __str__(self): - return "hello" - - def __repr__(self): - return "world" - -然后在这个目录下进入交互式环境。 - -.. code:: python - - >>> import ming - >>> mt=ming.mytest() - >>> mt - world - >>> print(mt) - hello - >>> _ - world - -知道这两个魔法方法的人,一看就明白了。 - -17. 反转字符串/列表最优雅的方式 -------------------------------- - -反转序列并不难,但是如何做到最优雅呢? - -先来看看,正常是如何反转的。 - -最简单的方法是使用列表自带的reverse()方法。 +最简单的方法是使用列表自带的reverse()方法。 .. code:: python @@ -632,7 +845,11 @@ intern(字符串驻留)的技术来提高字符串效率,什么是intern >>> ml[::-1] [3, 2, 1] -18. 改变默认递归次数限制 +.. + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +21. 改变默认递归次数限制 ------------------------ 上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。 @@ -653,7 +870,7 @@ intern(字符串驻留)的技术来提高字符串效率,什么是intern >>> sys.getrecursionlimit() 2000 -19. 一行代码实现FTP服务器 +22. 一行代码实现FTP服务器 ------------------------- 搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。 @@ -674,7 +891,7 @@ SimpleHTTPServer是Python SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 -20. 让你晕头转向的 else 用法 +23. 让你晕头转向的 else 用法 ---------------------------- if else @@ -742,15 +959,14 @@ break,还是没有break? 总结一下,for else 和 try else 相同,只要代码正常走下去不被 break,不抛出异常,就可以走else。 -21. 空字符串计数 ----------------- +24. 字符串里的缝隙是什么? +-------------------------- -求一个字符串里,某子字符(串)出现的次数。在Python中使用 count() -函数,就可以轻松实现。 +在Python中求一个字符串里,某子字符(串)出现的次数。 -比如下面几个常规例子 +大家都懂得使用 count() 函数,比如下面几个常规例子: -:: +.. code:: python >>> "aabb".count("a") 2 @@ -759,35 +975,44 @@ break,不抛出异常,就可以走else。 >>> "aabb".count("ab") 1 -但是如果使用空字符串呢,你可能想不到会是这样的结果。 +但是如果我想计算空字符串的个数呢? -:: +.. code:: python >>> "aabb".count("") 5 -具体原因,我不敢妄下结论。 +**奇怪了吧?** -由此我还衍生出另一个想法,实验了下。不知道空字符串,是一种什么样的存在,难道字母与字母之间 -“缝隙” 也算吗? +不是应该返回 0 吗?怎么会返回 5? -:: +实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。 + +因此 对于 ``aabb`` 这个字符串在 Python 来看应该是这样的 + +|image2| +理解了这个“**缝隙**” 的概念后,以下这些就好理解了。 + +.. code:: python + + >>> (" " * 10).count("") + 11 + >>> >>> "" in "" True - >>> "" in "ab" + >>> + >>> "" in "M" True -有兴趣的可以去看看CPython的源码实现。如果有结论,还请后台回复一下。不胜感激。 - -22. 负负得正 ------------- +25. 正负得正,负负得正 +---------------------- -从初中开始,我们就开始接触了\ ``负数`` -这个概念。知道了\ ``负负得正``\ ,这和武侠世界里的\ ``以毒功毒``\ ,有点神似。 +从初中开始,我们就开始接触了\ ``负数`` ,并且都知道了\ ``负负得正`` +的思想。 -Python -作为一门高级语言,它的编写符合人类的思维逻辑,这其中也包括\ ``负负得正``\ 这个思想。 +Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 ``负负得正`` +。 .. code:: python @@ -802,31 +1027,35 @@ Python >>> 5---3 2 -23. 数值与字符串的比较 +.. + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +26. 数值与字符串的比较 ---------------------- 在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。 -:: +.. code:: python >>> 100000000 < "" True - >>> 100000000 < "ming" + >>> 100000000 < "hello" True 但在 Python3 中,却不行。 -:: +.. code:: python >>> 100000000 < "" TypeError: '<' not supported between instances of 'int' and 'str' -24. 循环中的局部变量泄露 +27. 循环中的局部变量泄露 ------------------------ -在Python 2中x的值在一个循环执行之后被改变了。 +在Python 2中 x 的值在一个循环执行之后被改变了。 -:: +.. code:: python # Python2 >>> x = 1 @@ -837,7 +1066,7 @@ Python 不过在Python3 中这个问题已经得到解决了。 -:: +.. code:: python # Python3 >>> x = 1 @@ -846,135 +1075,32 @@ Python >>> x 1 -25. 字典可排序 --------------- +28. 字典居然是可以排序的? +-------------------------- -字典不可排序的思想,似乎已经根深蒂固。 +在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。 -:: +.. code:: python # Python2.7.10 >>> mydict = {str(i):i for i in range(5)} >>> mydict {'1': 1, '0': 0, '3': 3, '2': 2, '4': 4} -在 Python3 中字典已经是有序的。 +假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的 -:: +在 Python3.6 + +中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。 + +.. code:: python # Python3.6.7 >>> mydict = {str(i):i for i in range(5)} >>> mydict {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} -26. 链式比较 ------------- - -先给看一个示例 - -:: - - >>> False == False == True - False - -你知道这个表达式会返回 False 吗? - -我再给你举个例子,你可能就懂了。 - -:: - - f 18 < age < 60: - print("young man") - -如果还不明白,再给你整个等价写法。 - -:: - - >>> False == False and False == True - False - -27. 奇怪的字母 --------------- - -直接看下列例子。 - -在Python 2.x 中 - -:: - - >>> value = 11 - >>> valuе = 32 - File "", line 1 - valuе = 32 - ^ - SyntaxError: invalid syntax - -在Python 3.x 中 - -:: - - >>> value = 11 - >>> valuе = 32 - >>> value - 11 - -我相信你一开始看到这里,一定是目瞪口呆。你可以在自己的电脑上尝试一下,你会发现你不管在哪个版本的 -Python 里运行都没有问题。 - -如果你想重现我这个场景,你可能复制我上面的代码粘贴至自己的命令行中即可。 - -在这里,也不卖关子了,上面代码中第二行的 ``е`` 和 第一行的 ``e`` -是不一样的。 - -第二行的 ``e`` 是 Cyrillic(西里尔)字母,而不是我们熟悉的英文字母。 - -:: - - >>> ord('е') # cyrillic 'e' (Ye) - 1077 - >>> ord('e') # latin 'e', as used in English and typed using standard keyboard - 101 - >>> 'е' == 'e' - False - -细思恐极,平时可千万不要得罪同事们,万一辞职的时候,把你项目里的 ``e`` -全局替换成 ``e``\ ,到时候连错都不知道错哪了哈哈。 - -28. x == +x 吗? ----------------- - -在大多数情况下,这个等式是成立的。 - -:: - - >>> n1 = 10086 - >>> n2 = +n1 - >>> - >>> n1 == n2 - True - -什么情况下,这个等式会不成立呢? - -由于Counter的机制,\ ``+`` 用于两个 Counter -实例相加,而相加的结果如果元素的个数 ``<=`` 0,就会被丢弃。 - -:: - - >>> from collections import Counter - >>> ct = Counter('abcdbcaa') - >>> ct - Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) - >>> ct['c'] = 0 - >>> ct['d'] = -2 - >>> - >>> ct - Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) - >>> - >>> +ct - Counter({'a': 3, 'b': 2}) - -29. 有趣的import ----------------- +29. 有趣但没啥用的 import 用法 +------------------------------ import 是 Python 导包的方式。 @@ -1023,85 +1149,119 @@ import 是 Python 导包的方式。 >>> import antigravity -就会自动打开一个网页。 |image2| +就会自动打开一个网页。 |image3| -30. 局部/全局变量分不清? -------------------------- +30. 局部/全局变量傻傻分不清 +--------------------------- 在开始讲之前,你可以试着运行一下下面这小段代码。 -:: +.. code:: python + # demo.py a = 1 - def func01(): + def add(): a += 1 - - func01() -看似没有毛病,但实则已经犯了一个很基础的问题,这个报错相当常见吧? + add() + +看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下: .. code:: python - >>> func01() + $ python demo.py Traceback (most recent call last): - File "", line 1, in - File "", line 2, in func01 + File "demo.py", line 6, in + add() + File "demo.py", line 4, in add + a += 1 UnboundLocalError: local variable 'a' referenced before assignment 回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。 当程序运行到 ``a += 1`` 时,Python 解释器就认为在函数内部要给 ``a`` -这个变量赋值,当然就把 ``a`` 当做局部变量了,报错是理所应当的。 +这个变量赋值,当然就把 ``a`` 当做局部变量了,但是做为局部变量的 a +还没有被还没被定义。 + +因此报错是正常的。 理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢? -:: +.. code:: python + $ cat demo.py a = 1 - def func02(): + def output(): print(a) - - func02() -31. 不能使用单:raw-latex:`\结尾` --------------------------------- + output() -``\`` 是转义字符,可以将普通字符转化为有特殊含义的字符。 + $ python demo.py + 1 -.. code:: python +.. - >>> str1='\nhello'  #换行 - >>> print(str1) + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - hello - >>> str2='\thello'  #tab - >>> print(str2) - hello +31. 字母也玩起了障眼法 +---------------------- -但是如果你用单\ ``\``\ 结尾是会报语法错误的 +以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。 -.. code:: python +**在Python 2.x 中** - >>> str3="\" +:: + + >>> valuе = 32 File "", line 1 - str3="\" - ^ - SyntaxError: EOL while scanning string literal + valuе = 32 + ^ + SyntaxError: invalid syntax -就算你指定它是个 raw 字符串,也不行。 +**在Python 3.x 中** -.. code:: python +:: - >>> str3=r"\" - File "", line 1 - str3=r"\" - ^ - SyntaxError: EOL while scanning string literal + >>> valuе = 32 + >>> value + 11 -32. 字符串分割 --------------- +什么?没有截图你不信? + +|image4| + +如果你在自己的电脑上尝试一下,结果可能是这样的 + +|image5| + +**怎么又好了呢?** + +如果你想复现的话,请复制我这边给出的代码:\ ``valuе = 32`` + +**这是为什么呢?** + +原因在于,我上面使用的 value 变量名里的 ``е`` 又不是我们熟悉的 +``e``\ ,它是 Cyrillic(西里尔)字母。 + +:: + + >>> ord('е') # cyrillic 'e' (Ye) + 1077 + >>> ord('e') # latin 'e', as used in English and typed using standard keyboard + 101 + >>> 'е' == 'e' + False + +细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的 +``e`` 全局替换成 ``e``\ ,到时候你就哭去吧,肉眼根本看不出来嘛。 + +32. 字符串的分割技巧 +-------------------- + +当我们对字符串进行分割时,且分割符是 +``\n``\ ,有可能会出现这样一个窘境: .. code:: python @@ -1114,7 +1274,12 @@ import 是 Python 导包的方式。 ['a', 'b', ''] >>> - >>> +会在最后一行多出一个元素,为了应对这种情况,你可以会多加一步处理。 + +但我想说的是,完成没有必要,对于这个场景,你可以使用 ``splitlines`` + +.. code:: python + >>> str.splitlines() ['a', 'b'] @@ -1185,123 +1350,110 @@ import 是 Python 导包的方式。 >>> b [1, 2, 3, 4, 5, 6, 7, 8] -35. Python 里也可以有 end -------------------------- - -有不少编程语言,循环、判断代码块需要用 end -标明结束,这样一定程序上会使代码逻辑更加清晰一点,其实这种语法在 Python -里并没有必要,但如果你想用,也不是没有办法,具体你看下面这个例子。 - -|image3| - -36. 花样更新字典的技巧 +35. 增量赋值的性能更好 ---------------------- -常规的更新字典的方式是这样的 +诸如 ``+=`` 和 ``*=`` 这些运算符,叫做 增量赋值运算符。 -.. code:: python +这里使用用 += 举例,以下两种写法,在效果上是等价的。 - >>> profile={'name': 'wangbm'} - >>> extra={'age': 25, 'gender': 'male'} - >>> profile.update(extra) - >>> profile - {'gender': 'male', 'age': 25, 'name': 'wangbm'} +:: -今天明哥来集中讲下字典的更新方式 + # 第一种 + a = 1 ; a += 1 -.. code:: python + # 第二种 + a = 1; a = a + 1 - >>> profile={'name': 'wangbm'} - >>> extra=[('age', 25), ('gender', 'male')] - >>> profile.update(extra) - >>> profile - {'gender': 'male', 'age': 25, 'name': 'wangbm'} +``+=`` 其背后使用的魔法方法是 +\__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add_\_ 。 -如果你见过上面这种,那接下来这种我保证大部分人都没用过 +这两种写法有什么区别呢? -.. code:: python - - >>> profile={'name': 'wangbm'} - >>> profile.update(age=25, gender='male') - >>> profile - {'gender': 'male', 'age': 25, 'name': 'wangbm'} - -37. 被低估的 print ------------------- +用列表举例 a += b,使用 \__add_\_ 的话就像是使用了a.extend(b),如果使用 +\__add_\_ 的话,则是 a = +a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 -print 很多人只用来执行简单的打印功能。 +所以在能使用增量赋值的时候尽量使用它。 -print 作为一个函数,本身也附有各种各样的参数,只是很多人不知道。 + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 -你一定知道 join 这种很高效的字符串拼接方式。 +36. x == +x 吗? +---------------- -.. code:: python +在大多数情况下,这个等式是成立的。 - >>> alist = ['a', 'b', 'c'] - >>> ','.join(alist) - 'a,b,c' +:: -当如果 alist 里有非字符串的元素时,join 就会抛错。 + >>> n1 = 10086 + >>> n2 = +n1 + >>> + >>> n1 == n2 + True -.. code:: python +什么情况下,这个等式会不成立呢? - >>> alist = ['a', 'b', 3] - >>> ','.join(alist) - Traceback (most recent call last): - File "", line 1, in - TypeError: sequence item 2: expected str instance, int found +由于Counter的机制,\ ``+`` 用于两个 Counter +实例相加,而相加的结果如果元素的个数 ``<=`` 0,就会被丢弃。 -如果要解决这个问题,可能要提前将数值转化成字符串类型。 +:: -.. code:: python + >>> from collections import Counter + >>> ct = Counter('abcdbcaa') + >>> ct + Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) + >>> ct['c'] = 0 + >>> ct['d'] = -2 + >>> + >>> ct + Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) + >>> + >>> +ct + Counter({'a': 3, 'b': 2}) - >>> alist = ['a', 'b', 3] - >>> blist = [str(i) for i in alist] - >>> blist - ['a', 'b', '3'] - >>> ','.join(blist) - 'a,b,3' +37. 如何将 print 内容输出到文件 +------------------------------- -这里再介绍一种更简洁的方法,就是使用 print -来实现,但这种有局限,好像只能在命令行模式下使用,毕竟 print -标准输出,无法进行赋值。 +Python 3 中的 print +作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。 -下面我使用 ``_`` 来获取上一次的返回值,再赋值给 blist。 +比如今天要说的使用 print +将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。 .. code:: python - >>> print(*alist, sep=',') - a,b,3 - >>> blist = _ - >>> blist - 'a,b,3' + >>> with open('test.log', mode='w') as f: + ... print('hello, python', file=f, flush=True) + >>> exit() -既然说到了 print,那么再说一点print 的神技巧。 + $ cat test.log + hello, python -很多初学者,喜欢使用 print 来进行调试追踪。 +38. site-packages和 dist-packages +--------------------------------- -一点不好的地方是,普通的 print 是输出到终端屏幕,而不能将保持在文件中。 +如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages** +下,而有些包安装在 **dist-packages** 下。 -其实 print 是支持将输出重定向到文件中的,不过说实话,个人感觉还不如使用 -logging 模块呢,或者直接 f.write()。 +**它们有什么区别呢?** -.. code:: python +一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在 +这个目录下。 - >>> with open('test.log', mode='w') as f: - ... print('hello, python', file=f, flush=True) - >>> exit() +而 dist-packages 其实是 debian 系的 Linux 系统(如 +Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用 +dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在 +site-packages 下。 - $ cat test.log - hello, python +Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。 -以上两点,学习自董伟明的文章:\ `一些你不知道的Python -Tips `__ +如何查找 Python 安装目录 -38. site 和 dist 的区别 ------------------------ +.. code:: python -如果你手动安装python,它会直接使用目录site-packages。 -linux系统自带的Python,如果安装第三方库就存放到 dist-packages/ + >>> from distutils.sysconfig import get_python_lib + >>> print(get_python_lib()) + /usr/lib/python2.7/site-packages 39. argument 和 parameter 的区别 -------------------------------- @@ -1325,10 +1477,845 @@ arguments 和 parameter output_msg("error") -附录:参考文章 --------------- +40. 简洁而优雅的链式比较 +------------------------ + +先给你看一个示例: + +.. code:: python + + >>> False == False == True + False + +你知道这个表达式为什么会会返回 False 吗? + +它的运行原理与下面这个类似,是不是有点头绪了: + +.. code:: python + + if 80 < score <= 90: + print("成绩良好") + +如果你还是不明白,那我再给你整个第一个例子的等价写法。 + +.. code:: python + + >>> False == False and False == True + False + +这个用法叫做链式比较。 + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +41. 连接多个列表最极客的方式 +---------------------------- + +.. code:: python + + >>> a = [1,2] + >>> b = [3,4] + >>> c = [5,6] + >>> + >>> sum((a,b,c), []) + [1, 2, 3, 4, 5, 6] + +42. 另外 8 种连接列表的方式 +--------------------------- + +**1. 最直观的相加** + +使用 ``+`` 对多个列表进行相加,你应该懂,不多说了。 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> list01 + list02 + list03 + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +**2. 借助 itertools** + +itertools 在 Python +里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +在前面的文章中也介绍过,使用 ``itertools.chain()`` +函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 + +最后你再利用 list 将其转化为 列表。 + +.. code:: python + + >>> from itertools import chain + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> list(chain(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +**3. 使用 \* 解包** + +使用 ``*`` 可以解包列表,解包后再合并。 + +示例如下: + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> [*list01, *list02] + [1, 2, 3, 4, 5, 6] + >>> + +**4. 使用 extend** + +在字典中,使用 update 可实现原地更新,而在列表中,使用 extend +可实现列表的自我扩展。 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> list01.extend(list02) + >>> list01 + [1, 2, 3, 4, 5, 6] + +**5. 使用列表推导式** + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python +发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? + +当然可以,具体示例代码如下: + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> [x for l in (list01, list02, list03) for x in l] + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +**6. 使用 heapq** + +heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 + +该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> from heapq import merge + >>> + >>> list(merge(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +要注意的是,heapq.merge +除了合并多个列表外,它还会将合并后的最终的列表进行排序。 + +.. code:: python + + >>> list01 = [2,5,3] + >>> list02 = [1,4,6] + >>> list03 = [7,9,8] + >>> + >>> from heapq import merge + >>> + >>> list(merge(list01, list02, list03)) + [1, 2, 4, 5, 3, 6, 7, 9, 8] + >>> + +它的效果等价于下面这行代码: + +.. code:: python + + sorted(itertools.chain(*iterables)) + +如果你希望得到一个始终有序的列表,那请第一时间想到 +heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 + +**7. 借助魔法方法** + +有一个魔法方法叫 ``__add__``\ ,当我们使用第一种方法 list01 + list02 +的时候,内部实际上是作用在 ``__add__`` 这个魔法方法上的。 + +所以以下两种方法其实是等价的 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> list01 + list02 + [1, 2, 3, 4, 5, 6] + >>> + >>> + >>> list01.__add__(list02) + [1, 2, 3, 4, 5, 6] + >>> + +借用这个魔法特性,我们可以 reduce +这个方法来对多个列表进行合并,示例代码如下 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> from functools import reduce + >>> reduce(list.__add__, (list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +**8. 使用 yield from** + +在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 + +因此,我们可以像下面这样自定义一个合并列表的工具函数。 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> def merge(*lists): + ... for l in lists: + ... yield from l + ... + >>> list(merge(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +43. 在程序退出前执行代码的技巧 +------------------------------ + +使用 atexit 这个内置模块,可以很方便的注册退出函数。 + +不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 + +示例如下 + +|image6| + +如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。 + +可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit +来得优雅,来得方便,并且它很容易扩展。 + +但是使用 atexit 仍然有一些局限性,比如: + +- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 +- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 +- 如果你手动调用了\ ``os._exit()``\ ,你注册的函数无法正常执行。 + +44. 合并字典的 8 种方法 +----------------------- + +**1. 最简单的原地更新** + +字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> profile.update(ext_info) + >>> print(profile) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +如果想使用 update +这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> from copy import deepcopy + >>> + >>> full_profile = deepcopy(profile) + >>> full_profile.update(ext_info) + >>> + >>> print(full_profile) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> print(profile) + {"name": "xiaoming", "age": 27} + +**2. 先解包再合并字典** + +使用 ``**`` 可以解包字典,解包完后再使用 dict 或者 ``{}`` 就可以合并。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> full_profile01 = {**profile, **ext_info} + >>> print(full_profile01) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> + >>> full_profile02 = dict(**profile, **ext_info) + >>> print(full_profile02) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +若你不知道 ``dict(**profile, **ext_info)`` 做了啥,你可以将它等价于 + +.. code:: python + + >>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +**3. 借助 itertools** + +在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +正好我们字典也是可迭代对象,自然就可以想到,可以使用 +``itertools.chain()`` +函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 +dict 转成字典。 + +.. code:: python + + >>> import itertools + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> + >>> dict(itertools.chain(profile.items(), ext_info.items())) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +**4. 借助 ChainMap** + +如果可以引入一个辅助包,那我就再提一个, ``ChainMap`` 也可以达到和 +``itertools`` 同样的效果。 + +.. code:: python + + >>> from collections import ChainMap + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(ChainMap(profile, ext_info)) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +使用 ChainMap +有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 +itertools 就不会有这个问题)。 + +.. code:: python -- `wtfpython `__ + >>> from collections import ChainMap + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info={"age": 30} + >>> dict(ChainMap(profile, ext_info)) + {'name': 'xiaoming', 'age': 27} + +**5. 使用dict.items() 合并** + +在 Python 3.9 之前,其实就已经有 ``|`` +操作符了,只不过它通常用于对集合(set)取并集。 + +利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 + +你得先利用 ``items`` 方法将 dict 转成 dict_items,再对这两个 dict_items +取并集,最后利用 dict 函数,转成字典。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> full_profile = dict(profile.items() | ext_info.items()) + >>> full_profile + {'gender': 'male', 'age': 27, 'name': 'xiaoming'} + +当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list +函数再合并(示例为 Python 3.x ) + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(list(profile.items()) + list(ext_info.items())) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +若你在 Python 2.x 下,可以直接省去 list 函数。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(profile.items() + ext_info.items()) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +**6. 最酷炫的字典解析式** + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python +发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? + +当然可以,具体示例代码如下: + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> {k:v for d in [profile, ext_info] for k,v in d.items()} + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +**7. Python 3.9 新特性** + +在 2 月份发布的 Python 3.9.04a +版本中,新增了一个抓眼球的新操作符操作符: ``|``\ , PEP584 +将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 + +.. code:: python + + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> profile | ext_info + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> + >>> ext_info | profile + {'gender': 'male', 'name': 'xiaoming', 'age': 27} + >>> + >>> + +除了 ``|`` 操作符之外,还有另外一个操作符 ``|=``\ ,类似于原地更新。 + +.. code:: python + + >>> ext_info |= profile + >>> ext_info + {'gender': 'male', 'name': 'xiaoming', 'age': 27} + >>> + >>> + >>> profile |= ext_info + >>> profile + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +看到这里,有没有涨姿势了,学了这么久的 Python +,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 +7 +种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +45. 条件语句的七种写法 +---------------------- + +**第一种:原代码** + +这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 +Python 功力。 + +.. code:: python + + if age > 18: + return "已成年" + else: + return "未成年" + +下面我列举了六种这段代码的变异写法,一个比一个还 6 +,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?** +(除了第一种之外) + +**第二种** + +语法: + +.. code:: python + + if else + +例子 + +.. code:: python + + >>> age1 = 20 + >>> age2 = 17 + >>> + >>> + >>> msg1 = "已成年" if age1 > 18 else "未成年" + >>> print msg1 + 已成年 + >>> + >>> msg2 = "已成年" if age2 > 18 else "未成年" + >>> print msg2 + 未成年 + >>> + +**第三种** + +语法 + +.. code:: python + + and or + +例子 + +.. code:: python + + >>> msg1 = age1 > 18 and "已成年" or "未成年" + >>> msg2 = "已成年" if age2 > 18 else "未成年" + >>> + >>> print(msg1) + 已成年 + >>> + >>> print(msg2) + 未成年 + +**第四种** + +语法 + +.. code:: python + + (, )[condition] + +例子 + +.. code:: python + + >>> msg1 = ("未成年", "已成年")[age1 > 18] + >>> print(msg1) + 已成年 + >>> + >>> + >>> msg2 = ("未成年", "已成年")[age2 > 18] + >>> print(msg2) + 未成年 + +**第五种** + +语法 + +.. code:: python + + (lambda: , lambda:)[]() + +例子 + +.. code:: python + + >>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() + >>> print(msg1) + 已成年 + >>> + >>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() + >>> print(msg2) + 未成年 + +**第六种** + +语法: + +.. code:: python + + {True: , False: }[] + +例子: + +.. code:: python + + >>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] + >>> print(msg1) + 已成年 + >>> + >>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] + >>> print(msg2) + 未成年 + +**第七种** + +语法 + +.. code:: python + + (() and (,) or (,))[0] + +例子 + +.. code:: python + + >>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] + >>> print(msg1) + 已成年 + >>> + >>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] + >>> print(msg2) + 未成年 + +以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 + +看到这里,有没有涨姿势了,学了这么久的 Python +,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 + + 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 + +46. /usr/bin/env python 有什么用? +---------------------------------- + +我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 + +.. code:: shell + + #!/usr/bin/python + +或者这样 + +``sh e llsh el #!/usr/bin/env python`` + +这两者有什么区别呢? + +稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python`` +进入console 模式里的 ``python`` + +|image7| + +而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python`` +,意思就是说你得用哪个软件 (python)来执行这个文件。 + +那么加和不加有什么区别呢? + +不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` , + +|image8| + +有没有一种方式?可以省去每次都加 ``python`` 呢? + +当然有,你可以文件头里加上\ ``#!/usr/bin/python`` +,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 + +|image9| + +明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? + +当我执行 ``env python`` 时,自动进入了 python console 的模式。 + +|image10| + +这是为什么?和 直接执行 python 好像没什么区别呀 + +当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是 +/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin +)这几个路径里去依次查找名为python的可执行文件。 + +找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python`` +里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin`` +下创建一个名字也为 python 的可执行文件时,就会执行 ``/usr/bin/python`` +了。 + +具体演示过程,你可以看下面。 + +|image11| + +那么对于这两者,我们应该使用哪个呢? + +个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 +python 解释器都是 ``/usr/bin/python`` 。 + +47. 让我爱不释手的用户环境 +-------------------------- + +当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢? + +可以使用 ``pip install --user pkg`` +将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。 + +.. code:: shell + + # 在全局环境中未安装 requests + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ su - wangbm + + # 由于用户环境继承自全局环境,这里也未安装 + [wangbm@localhost ~]$ pip list | grep requests + [wangbm@localhost ~]$ pip install --user requests + [wangbm@localhost ~]$ pip list | grep requests + requests (2.22.0) + [wangbm@localhost ~]$ + + # 从 Location 属性可发现 requests 只安装在当前用户环境中 + [wangbm@localhost ~]$ pip show requests + --- + Metadata-Version: 2.1 + Name: requests + Version: 2.22.0 + Summary: Python HTTP for Humans. + Home-page: http://python-requests.org + Author: Kenneth Reitz + Author-email: me@kennethreitz.org + Installer: pip + License: Apache 2.0 + Location: /home/wangbm/.local/lib/python2.7/site-packages + [wangbm@localhost ~]$ exit + logout + + # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ + +48. 实现类似 defer 的延迟调用 +----------------------------- + +在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 + +.. code:: go + + import "fmt" + + func myfunc() { + fmt.Println("B") + } + + func main() { + defer myfunc() + fmt.Println("A") + } + +输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc +的调用写在函数的第一行,这就是延迟调用。 + +:: + + A + B + +那么在 Python 中否有这种机制呢? + +当然也有,只不过并没有 Golang 这种简便。 + +在 Python 可以使用 **上下文管理器** 达到这种效果 + +.. code:: python + + import contextlib + + def callback(): + print('B') + + with contextlib.ExitStack() as stack: + stack.callback(callback) + print('A') + +输出如下 + +:: + + A + B + +49. 自带的缓存机制不用白不用 +---------------------------- + +缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 + +数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 + +为了实现这个需求,Python 3.2 + +中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。 + +这个机制实现于 functool 模块中的 lru_cache 装饰器。 + +.. code:: python + + @functools.lru_cache(maxsize=None, typed=False) + +参数解读: + +- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 + 2 的幂时,性能最佳 +- typed:若为 True,则不同参数类型的调用将分别缓存。 + +举个例子 + +.. code:: python + + from functools import lru_cache + + @lru_cache(None) + def add(x, y): + print("calculating: %s + %s" % (x, y)) + return x + y + + print(add(1, 2)) + print(add(1, 2)) + print(add(2, 3)) + +输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果 + +.. code:: shell + + calculating: 1 + 2 + 3 + 3 + calculating: 2 + 3 + 5 + +50. 重定向标准输出到日志 +------------------------ + +假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。 + +检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。 + +如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。 + +由于最初约定的脚本返回方式是以 JSON +的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。 + +如何避免这种情况的发生呢? + +我们可以这样做,把你的标准错误输出到日志文件中。 + +.. code:: python + + import contextlib + + log_file="/var/log/you.log" + + def you_task(): + pass + + @contextlib.contextmanager + def close_stdout(): + raw_stdout = sys.stdout + file = open(log_file, 'a+') + sys.stdout = file + + yield + + sys.stdout = raw_stdout + file.close() + + with close_stdout(): + you_task() -------------- @@ -1339,6 +2326,14 @@ arguments 和 parameter .. |image0| image:: http://image.python-online.cn/20190511165650.png .. |image1| image:: http://image.python-online.cn/20190511165716.png -.. |image2| image:: http://image.python-online.cn/20190511165735.png -.. |image3| image:: http://image.python-online.cn/20190915213412.png +.. |image2| image:: http://image.iswbm.com/20200509172331.png +.. |image3| image:: http://image.python-online.cn/20190511165735.png +.. |image4| image:: http://image.iswbm.com/20200509122954.png +.. |image5| image:: http://image.iswbm.com/20200509123107.png +.. |image6| image:: http://image.iswbm.com/20200510112133.png +.. |image7| image:: http://image.python-online.cn/20200331184021.png +.. |image8| image:: http://image.python-online.cn/20200331185034.png +.. |image9| image:: http://image.python-online.cn/20200331184755.png +.. |image10| image:: http://image.python-online.cn/20200331185741.png +.. |image11| image:: http://image.python-online.cn/20200331190224.png diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index f1bc2a5..58d20a0 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -1,14 +1,28 @@ -1.30 学习编程的几大网站 -======================= +1.30 盘点程序员学习编程的那些网站 +================================= -`书栈网 `__ +书栈网 +------ + +**网站链接**\ :https://www.bookstack.cn/rank?tab=popular |image0| -`魔法学院 `__ +魔法学院 +-------- + +**网站链接**\ :http://www.nowamagic.net/academy/ |image1| +Python 3 标准库实例教程 +----------------------- + +**网站链接**\ :https://learnku.com/docs/pymotw + +|image2| + .. |image0| image:: http://image.python-online.cn/20200104144109.png .. |image1| image:: http://image.python-online.cn/20200112210558.png +.. |image2| image:: http://image.iswbm.com/20200508201333.png diff --git a/source/c01/c01_43.rst b/source/c01/c01_43.rst new file mode 100644 index 0000000..dd26fab --- /dev/null +++ b/source/c01/c01_43.rst @@ -0,0 +1,390 @@ +1.43 求你了,别再使用 pprint 打印字典了 +======================================= + +1. 吐槽问题 +----------- + +pprint 你应该很熟悉了吧? + +随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货 +。 + +比如这下面这个 json +字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。 + +.. code:: json + + [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] + +如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint +看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。 + +.. code:: python + + >>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> from pprint import pprint + >>> pprint(info) + [{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] + +好像有点效果,真的是 “神器”呀。 + +但是你告诉我, +**:raw-latex:`\xe`4:raw-latex:`\xbd`:raw-latex:`\xa`0:raw-latex:`\xe`7:raw-latex:`\x`9a** +这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。 + +好在我懂点 Python 2 的编码,知道 Python 2 +中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte +存储的。 + +行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。 + +.. code:: python + + >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> from pprint import pprint + >>> pprint(info) + [{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': u'\u76ae\u7684\u561b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': u'\u4e0d\u5b58\u5728\u7684', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] + +确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀? + +:: + + u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f' + +除此之外,我们知道 json 的严格要求必须使用 +**双引号**\ ,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是 +**单引号**\ ?我也太难了吧,我连自己的代码都无法控制了吗? + +到这里,我们知道了 pprint 带来的两个问题: + +1. 没法在 Python 2 下正常打印中文 +2. 没法输出 JSON 标准格式的格式化内容(双引号) + +2. 解决问题 +----------- + +打印中文 +~~~~~~~~ + +如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。 + +.. code:: python + + # Python3.7 + >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> from pprint import pprint + >>> pprint(info) + [{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '皮的嘛', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '不存在的', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] + >>> + +但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的 +Python,本来我可以选择不用的,因为有更好的替代方案(\ **这个后面会讲**\ )。 + +但是我出于猎奇,正好前两天不是写过一篇关于 编码 +的文章吗,我自认为自己对于 +编码还是掌握比较熟练的,就想着来解决一下这个问题。 + +索性就来看下 pprint +的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。 + +**以下是我的解决方案,供你参考**\ : + +写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer) + +并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str +类型,而是 unicode 类型,就用 uft8 编码成 str 类型。 + +.. code:: python + + # coding: utf-8 + from pprint import PrettyPrinter + + # 继承 PrettyPrinter,复写 format 方法 + class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + + info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + + MyPrettyPrinter().pprint(info) + +输出如下,已经解决了中文的显示问题: + +|image0| + +打印双引号 +~~~~~~~~~~ + +解决了中文问题后,再来看看如何让 pprint 打印双引号。 + +在实例化 PrettyPrinter 对象的时候,可以接收一个 stream +对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个 +stream,也就是标准输出。 + +现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。 + +那我们完全可以自己定义一个 stream +类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。 + +有了思路,就可以开始写代码了,如下: + +.. code:: python + + # coding: utf-8 + from pprint import PrettyPrinter + + class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + + class MyStream(): + def write(self, text): + print text.replace('\'', '"') + + info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + MyPrettyPrinter(stream=MyStream()).pprint(info) + +尝试执行了下,我的天,怎么是这样子的。 + +.. code:: json + + [ + { + "des" + : + 2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青 + , + "downloadUrl": + "app/com.renren.mobile.android/com.renren.mobile.android.apk" + , + "iconUrl": + "app/com.renren.mobile.android/icon.jpg" + , + "id": + 1580615 + , + "name": + 皮的嘛 + , + "packageName": + "com.renren.mobile.android" + , + "size": + 21803987 + , + "stars": + 2 + } + , + + { + "des" + : + 斗鱼271934走过路过不要错过,这里有最好的鸡儿 + , + "downloadUrl": + "app/com.ct.client/com.ct.client.apk" + , + "iconUrl": + "app/com.ct.client/icon.jpg" + , + "id": + 1540629 + , + "name": + 不存在的 + , + "packageName": + "com.ct.client" + , + "size": + 4794202 + , + "stars": + 2 + } + ] + +经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个 +**换行符**\ 。 + +那如何将使 print 函数打印的内容,不进行换行呢? + +方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个 +**逗号** 就行。 + +就像下面这样。 + +|image1| + +知道了问题所在,再修改下代码 + +.. code:: python + + # coding: utf-8 + from pprint import PrettyPrinter + + class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + + class MyStream(): + def write(self, text): + print text.replace('\'', '"'), + + info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + + MyPrettyPrinter(stream=MyStream()).pprint(info) + +终于成功了,太不容易了吧。 + +|image2| + +3. 何必折腾 +----------- + +通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。 + +代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。 + +**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**\ 。 + +为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python +,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗? + +如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用 +pprint 了。 + +.. _打印中文-1: + +打印中文 +~~~~~~~~ + +其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。 + +但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint +简单得不要太多。 + +具体的代码示例如下: + +.. code:: python + + >>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> import json + >>> + >>> + >>> print json.dumps(info, indent=4, ensure_ascii=False) + [ + { + "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk", + "iconUrl": "app/com.renren.mobile.android/icon.jpg", + "name": "皮的嘛", + "stars": 2, + "packageName": "com.renren.mobile.android", + "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青", + "id": 1580615, + "size": 21803987 + }, + { + "downloadUrl": "app/com.ct.client/com.ct.client.apk", + "iconUrl": "app/com.ct.client/icon.jpg", + "name": "不存在的", + "stars": 2, + "packageName": "com.ct.client", + "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿", + "id": 1540629, + "size": 4794202 + } + ] + >>> + +json.dumps 的关键参数有两个: + +- **indent=4**\ :以 4 个空格缩进单位 +- **ensure_ascii=False**\ :接收非 ASCII 编码的字符,这样才能使用中文 + +与 pprint 相比 json.dumps 可以说完胜: + +1. 两个参数就能实现所有我的需求(打印中文与双引号) +2. 就算在 Python 2 下,使用中文也不需要用 ``u'中文'`` 这种写法 +3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题 + +4. 总结一下 +----------- + +本来很简单的一个观点,我为了证明 pprint +实现那两个需求有多么困难,花了很多的时间去研究了 pprint +的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。 + +本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点 + +1. 核心观点:Python2 下不要再使用 pprint +2. 若真要使用,且有和一样的改造需求,可以参考我的实现 +3. Python 2 中的 print 语句后居然可以加 逗号 + +以上。希望此文能对你有帮助。 + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200507171451.png +.. |image1| image:: http://image.iswbm.com/20200507174459.png +.. |image2| image:: http://image.iswbm.com/20200507174802.png + diff --git a/source/c01/c01_45.rst b/source/c01/c01_45.rst new file mode 100644 index 0000000..8d4feac --- /dev/null +++ b/source/c01/c01_45.rst @@ -0,0 +1,297 @@ +1.45 Python炫技操作:花式导包的八种方法 +======================================= + +1. 直接 import +-------------- + +人尽皆知的方法,直接导入即可 + +.. code:: python + + >>> import os + >>> os.getcwd() + '/home/wangbm' + +与此类似的还有,不再细讲 + +.. code:: python + + import ... + import ... as ... + from ... import ... + from ... import ... as ... + +一般情况下,使用 ``import`` 语句导入模块已经够用的。 + +但是在一些特殊场景中,可能还需要其他的导入方式。 + +下面我会一一地给你介绍。 + +2. 使用 \__import_\_ +-------------------- + +``__import__`` 函数可用于导入模块,import 语句也会调用函数。其定义为: + +:: + + __import__(name[, globals[, locals[, fromlist[, level]]]]) + +参数介绍: + +- name (required): 被加载 module 的名称 +- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 + global() +- locals (optional): + 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local() +- fromlist (Optional): 被导入的 submodule 名称 +- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 + absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 + absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 + 类似于 ‘.’,2 类似于 ‘..’。 + +使用示例如下: + +.. code:: python + + >>> os = __import__('os') + >>> os.getcwd() + '/home/wangbm' + +如果要实现 ``import xx as yy`` 的效果,只要修改左值即可 + +如下示例,等价于 ``import os as myos``\ : + +.. code:: python + + >>> myos = __import__('os') + >>> myos.getcwd() + '/home/wangbm' + +3. 使用 importlib 模块 +---------------------- + +importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。 + +它的简单示例: + +.. code:: python + + >>> import importlib + >>> myos=importlib.import_module("os") + >>> myos.getcwd() + '/home/wangbm' + +如果要实现 ``import xx as yy``\ 效果,可以这样 + +.. code:: python + + >>> import importlib + >>> + >>> myos = importlib.import_module("os") + >>> myos.getcwd() + '/home/wangbm' + +4. 使用 imp 模块 +---------------- + +``imp`` 模块提供了一些 import +语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的 +``__import__`` 函数功能: + +.. code:: python + + >>> import imp + >>> file, pathname, desc = imp.find_module('os') + >>> myos = imp.load_module('sep', file, pathname, desc) + >>> myos + + >>> myos.getcwd() + '/home/wangbm' + +从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 +开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib +模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib +模块的合集。 + +5. 使用 execfile +---------------- + +在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。 + +语法如下: + +:: + + execfile(filename[, globals[, locals]]) + +参数有这么几个: + +- filename:文件名。 +- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。 +- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。 + +.. code:: python + + >>> execfile("/usr/lib64/python2.7/os.py") + >>> + >>> getcwd() + '/home/wangbm' + +6. 使用 exec 执行 +----------------- + +``execfile`` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。 + +但是原理值得借鉴,你可以使用 open … read 读取文件内容,然后再用 exec +去执行模块。 + +示例如下: + +.. code:: python + + >>> with open("/usr/lib64/python2.7/os.py", "r") as f: + ... exec(f.read()) + ... + >>> getcwd() + '/home/wangbm' + +7. import_from_github_com +------------------------- + +有一个包叫做 +**import_from_github_com**\ ,从名字上很容易得知,它是一个可以从 github +下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip +先安装它。 + +.. code:: shell + + $ python3 -m pip install import_from_github_com + +这个包使用了PEP +302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 +Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。 + +pip 要保证是较新版本,如果不是请执行如下命令进行升级。 + +.. code:: shell + + $ python3 -m pip install --upgrade pip + +确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com + +示例如下 + +.. code:: python + + >>> from github_com.zzzeek import sqlalchemy + Collecting git+https://github.com/zzzeek/sqlalchemy + Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build + Installing collected packages: SQLAlchemy + Running setup.py install for SQLAlchemy ... done + Successfully installed SQLAlchemy-1.1.0b1.dev0 + >>> locals() + {'__builtins__': , '__spec__': None, + '__package__': None, '__doc__': None, '__name__': '__main__', + 'sqlalchemy': , + '__loader__': } + >>> + +看了 +import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 +pip +来安装那些没有安装的包,然后使用Python的\ ``__import__()``\ 函数来引入新安装的模块。 + +8. 远程导入模块 +--------------- + +我在这篇文章里(\ `深入探讨 Python 的 import +机制:实现远程导入模块 `__\ ),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。 + +具体内容非常的多,你可以点击这个\ `链接 `__\ 进行深入学习。 + +示例代码如下: + +.. code:: python + + # 新建一个 py 文件(my_importer.py),内容如下 + import sys + import importlib + import urllib.request as urllib2 + + class UrlMetaFinder(importlib.abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return loader + except Exception: + return None + + class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + + def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) + +并且在远程服务器上开启 http +服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的 +python 文件,如果后面导入成功会打印 ``ok``\ 。 + +.. code:: shell + + $ mkdir httpserver && cd httpserver + $ cat>my_info.py>> from my_importer import install_meta + >>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder + >>> import my_info # 打印ok,说明导入成功 + ok + >>> my_info.name # 验证可以取得到变量 + 'wangbm' + +好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import +这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习\ ``__import__``\ 以及 +importlib 是非常有必要的。 + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index 743fddc..4083c47 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -61,6 +61,18 @@ running thread-123145494278144:2 +示例完毕,来说明一下: + +1. 使用 with 语句 ,通过 ThreadPoolExecutor 构造实例,同时传入 max_workers 参数来设置线程池中最多能同时运行的线程数目。 + +2. 使用 submit 函数来提交线程需要执行的任务到线程池中,并返回该任务的句柄(类似于文件、画图),注意 submit() 不是阻塞的,而是立即返回。 + +3. 通过使用 done() 方法判断该任务是否结束。上面的例子可以看出,提交任务后立即判断任务状态,显示四个任务都未完成。在延时2.5后,task1 和 task2 执行完毕,task3 仍在执行中。 + +4. 使用 result() 方法可以获取任务的返回值。 + + + ### 自定义线程池 除了使用上述第三方模块的方法之外,我们还可以自己结合前面所学的消息队列来自定义线程池。 diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 675d704..6d314fc 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -63,6 +63,21 @@ running thread-123145494278144:2 .... +示例完毕,来说明一下: + +1. 使用 with 语句 ,通过 ThreadPoolExecutor 构造实例,同时传入 + max_workers 参数来设置线程池中最多能同时运行的线程数目。 + +2. 使用 submit + 函数来提交线程需要执行的任务到线程池中,并返回该任务的句柄(类似于文件、画图),注意 + submit() 不是阻塞的,而是立即返回。 + +3. 通过使用 done() + 方法判断该任务是否结束。上面的例子可以看出,提交任务后立即判断任务状态,显示四个任务都未完成。在延时2.5后,task1 + 和 task2 执行完毕,task3 仍在执行中。 + +4. 使用 result() 方法可以获取任务的返回值。 + 自定义线程池 ~~~~~~~~~~~~ diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 49364a7..5c4d314 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -196,6 +196,17 @@ add/commit 前撤销对文件的修改 git show commit_id git show --stat commit_id +查看两个分支之间的差异 + +:: + + # 查看两个分支有哪些文件发生改变了 + git diff stable/2.2.9 stable/2.3.0 --stat + + # 对比某一个文件在两个文件中的变化 + git diff stable/2.2.9 stable/2.3.0 -- README.md + git diff stable/2.2.9:README.md stable/2.3.0:README.md + 三、状态回滚 ------------ @@ -284,8 +295,12 @@ add/commit 前撤销对文件的修改 # HEAD^^是上上个版本 # HEAD~100是前100个版本 + # 回到上一个版本 $ git reset --hard HEAD^ + # 若本地不小心修改或删除了很多文件,一个一个恢复太麻烦,可以这样,回到上一个版本 + $ git reset --hard HEAD + 使用 ``git revert``\ ,间接回退,会生成新的提交 .. code:: shell diff --git a/source/c07/c07_09.rst b/source/c07/c07_09.rst index c69a010..d263d02 100644 --- a/source/c07/c07_09.rst +++ b/source/c07/c07_09.rst @@ -1,8 +1,8 @@ 7.9 Ansible 入门指南使用手册 ============================ -7.9.1 有用的小技巧 ------------------- +1. 有用的小技巧 +--------------- 1. 只输出错误的信息 ~~~~~~~~~~~~~~~~~~~ @@ -85,7 +85,14 @@ /root/deployment/inventory/get_controller_v1.py --list -7. playbook 参数 +7. ad-hoc 指定 run_once +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: shell + + ansible compute[0] -m synchronize -a 'src=/etc/deny.list dest=/etc/deny.list mode=pull' + +2. playbook 参数 ---------------- :: diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index 911d75c..9edd7f0 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -66,6 +66,21 @@ done +7.16.5 获取随机数 +----------------- + +获取 1 - 60 的随机数 + +.. code:: shell + + function random_range() { + local beg=$1 + local end=$2 + echo $((RANDOM % ($end - $beg) + $beg)) + } + + result=`random_range 1 60` + .. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! From 9852597ea14d15877505203475a2d1ba567518a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 14 May 2020 09:42:40 +0800 Subject: [PATCH 064/147] =?UTF-8?q?Update=EF=BC=9A=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_45.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/c01/c01_45.md b/source/c01/c01_45.md index 5d90ca9..1ae10d4 100644 --- a/source/c01/c01_45.md +++ b/source/c01/c01_45.md @@ -61,6 +61,17 @@ __import__(name[, globals[, locals[, fromlist[, level]]]]) '/home/wangbm' ``` + + +上面说过的 `__import__` 是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于 `__buildins__` 中,因此我们还可以这样导入 os 的模块: + +```python +>>> __builtins__.__dict__['__import__']('os').getcwd() +'/home/wangbm' +``` + + + ## 3. 使用 importlib 模块 importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。 From 6984f3837f28443b8b6d0a2e90e978daa7990837 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 27 May 2020 00:31:25 +0800 Subject: [PATCH 065/147] =?UTF-8?q?Add=EF=BC=9A=E5=A2=9E=E5=8A=A0=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_47.md | 141 +++++++++++++++++++++++++++++++++++++++ source/c01/c01_47.rst | 152 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 293 insertions(+) create mode 100644 source/c01/c01_47.md create mode 100644 source/c01/c01_47.rst diff --git a/source/c01/c01_47.md b/source/c01/c01_47.md new file mode 100644 index 0000000..d9ec1ea --- /dev/null +++ b/source/c01/c01_47.md @@ -0,0 +1,141 @@ +# 1.47 通俗理解OSI七层模型 + +OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 + +![img](https://pic4.zhimg.com/80/v2-854e3df8ea850c977c30cb1deb1f64db_1440w.jpg) + +为什么要有这个模型呢? + +主要是为了实现开放系统环境中的互连性、互操作性和应用的可移植性。 + +有了这个模型,各个层的实现就可以独立开发,复用性强,增加自由度的同时,也提高了生产效率。 + +## 数据的发送过程 + +数据包的发起方,一定都是从高层发起,然后从上往下传输数据,下层为上层提供传输服务,下方不关心传输内容,上层不关心下层如何传输。 + +就如下面这张图所示 + +![](http://image.iswbm.com/20200526233356.png) + + + +## 应用层 + +代表协议有:HTTP,HTTPS,FTP,SMTP,TELNET + +由于日常开发中,我们接触的基本都是 HTTP,这里就以 HTTP 为例。 + +这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页(www.baidu.com),由于你请求的是域名,域名首先要被解析成真正的ip,才能知道你要往哪个服务器上获取首页。 + +假设这里已经获得了ip,浏览器会将这些请求的信息,封装成一个 HTTP 数据包,这个数据包头会包含一些头信息。然后交由传输层(TCP)。 + +## 表示层 + +代表协议有:ASCII,SSL/TLS 等 + +这一层的作用是,将应用处理的信息转换为适合网络传输的格式。 + +比如我只会说中文,而日本友人只会说日文,那么我们两个是无法交流的。但如果我们都会说英文,交流时我先在心里想好要说的话是什么,再用英语说出来,日本友人听到英文,在心里转换为日语,他就能弄懂我的意思,此时表示层就是各自在心里转化语言。 + +当浏览器请求回一堆数据,是解析成文本还是图片,就由表示层决定。数据的压缩、加密、打包等功能也都在这层完成。 + +## 会话层 + +代表协议有: `ADSP`、`RPC` 等。 + +负责建立和断开通信连接(数据流动的逻辑通路),以及数据的分割等数据传输相关的管理。 + +比如,何时建立连接,何时断开连接以及保持多久的连接,都是由会话层来进行管理的。 + + + +## 传输层 + +代表协议有 :TCP,UDP等 + +**传输层起着可靠传输的作用。** + +对于 DNS 域名解析,传输层协议是 UDP + +而对于 HTTP 请求,传输层协议是 TCP + +**那这两种协议有什么区别呢?** + +`TCP` 协议提供可靠的通信传输,简单说就是确认目标能通信的情况下才会传输数据(因此需要三次握手),传输过程如果丢了数据,也会重发。而 `UDP` 协议则不然,不会确认目标能否通信,只会根据协议发到对方地址的端口。至于对方收不收到,丢不丢包,一概不管。 + +无论是哪种协议,其协议头信息都会包含本地端口号,和目标端口号(还有报文长度)。 + +加上这层头信息后,封装好的数据包再交由网络层。 + +## 网络层 + +代表协议有:IP,ICMP 协议等,其也叫IP层,主要应用是路由器(并非我们家中的路由器,扩展阅读:https://www.zhihu.com/question/52176116)。 + +**网络层负责将数据传输到目标地址。** + +网络层将数据从发送端的主机发送到接收端的主机,两台主机间可能会存在很多数据链路,但网络层就是负责找出一条相对顺畅的通路将数据传递过去。传输的地址使用的是IP地址。 + +IP地址和我们的住址有点相似,我们的住址可以从省到市再到街逐步缩小范围,直至我们住址。IP地址也有这样的能力,通过不断转发到更近的IP地址,最终可以到达目标地址。如何选择这条路,就看网络层了。 + +这好比是快递公司的路线规划者。快递公司有很多集散中心,根据集散中心的情况(是否拥堵),找出一条经过n个集散中心的路径将货物(数据)沿路运过去。 + +在这层里,会给数据包再加一些头信息,包括本地IP地址,目标IP地址(还有数据包的生存空间TTL),同时还会有一个Protocol字段 表示上层到底是 UDP还是TCP协议,这个是用于对端在接收到这个数据包后知道如何解析。 + +加上这些头信息后,再把**数据报**(或者说**分组**、**报文**)传递给链路层。 + + + +## 链路层 + +代表协议有:HDLC,PPP,SLIP 等,其也叫 MAC 层,主要应用是交换机,网桥。 + +**该层负责物理层面(一个以太网相连)上互连的节点之间的通信传输。** + +数据链路层会将0、1序列划分为具有意义的数据帧传送给对端(数据帧的生成与接收)。举个例子可能会更好理解,暂且把需要传输的数据看作为不同来源的水,如果直接倒入池子中时,是无法重新分辨出不同来源的水的。但如果将不同来源的灌入瓶子中并打上记号,那就能区分出不同来源的水。这也就是为什么要划分为具有意义的数据帧传送给对端。 + +数据链路层可以看作是快递公司的司机,他们驾驶着汽车,将打包好的货物(数据帧)从一个城市(物理节点)运输到另一个城市。 + +在这层里,会给数据包再加一些头信息,包括本地的mac地址,目标mac地址,同时还有一个type字段表示上层协议是使用的是IP协议,还是其他什么协议,也是用于对端在接收到这个数据包后知道如何解析。 + +需要注意的是,数据链路层只负责将数据运送给物理相连的两端,并不负责直接发送到最终地址。 + +加上这些头信息后,再把**数据帧**传递给物理层。 + + + +这一层主要解决两个问题: + +**第一个问题**:网络包是发给谁的,由谁来接收 + +链路层协议头会包含目标MAC和源MAC + +**第二个问题**:大家都在发包,谁先发,谁后发? + +对于这个问题,有多种算法可以解决 + +方法一:信道划分,就跟马路上分多个车道一样,你走你的,我走我的,互不影响 + +方法二:轮流协议,还是以交通举例,今天单号出行,明天双号出行,轮着来 + +方法三:随机接入协议,不管三七二十一,有事儿先出门,发现特堵,就回去。错过高峰再出。 + + + +## 物理层 + +代表协议有: `RS 232C`、`RS 449/422/423`、`V.24` 和 `X.21`、`X.21bis` 等。 + +物理层负责0、1比特流(0、1序列)与电压高低、光的闪灭之间的互换。 + +物理层其实就是我们日常能接触的物理介质,比如光纤、电缆、还有空气(还有集线器、中继器、调制解调器),根据这些传输介质的不同,二进制流(0和1)会相应地转化成光信号,电信号,电磁波信号。 + +物理层是 `OSI` 七层模型的物理基础,没有它就谈不上数据传输了。 + + + + + +## 参考文章 + +- https://juejin.im/post/59eb06b1f265da430f313c7f \ No newline at end of file diff --git a/source/c01/c01_47.rst b/source/c01/c01_47.rst new file mode 100644 index 0000000..70fd816 --- /dev/null +++ b/source/c01/c01_47.rst @@ -0,0 +1,152 @@ +1.47 通俗理解OSI七层模型 +======================== + +OSI (Open System +Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 + +.. figure:: https://pic4.zhimg.com/80/v2-854e3df8ea850c977c30cb1deb1f64db_1440w.jpg + :alt: img + + img + +为什么要有这个模型呢? + +主要是为了实现开放系统环境中的互连性、互操作性和应用的可移植性。 + +有了这个模型,各个层的实现就可以独立开发,复用性强,增加自由度的同时,也提高了生产效率。 + +数据的发送过程 +-------------- + +数据包的发起方,一定都是从高层发起,然后从上往下传输数据,下层为上层提供传输服务,下方不关心传输内容,上层不关心下层如何传输。 + +就如下面这张图所示 + +|image0| + +应用层 +------ + +代表协议有:HTTP,HTTPS,FTP,SMTP,TELNET + +由于日常开发中,我们接触的基本都是 HTTP,这里就以 HTTP 为例。 + +这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页(www.baidu.com),由于你请求的是域名,域名首先要被解析成真正的ip,才能知道你要往哪个服务器上获取首页。 + +假设这里已经获得了ip,浏览器会将这些请求的信息,封装成一个 HTTP +数据包,这个数据包头会包含一些头信息。然后交由传输层(TCP)。 + +表示层 +------ + +代表协议有:ASCII,SSL/TLS 等 + +这一层的作用是,将应用处理的信息转换为适合网络传输的格式。 + +比如我只会说中文,而日本友人只会说日文,那么我们两个是无法交流的。但如果我们都会说英文,交流时我先在心里想好要说的话是什么,再用英语说出来,日本友人听到英文,在心里转换为日语,他就能弄懂我的意思,此时表示层就是各自在心里转化语言。 + +当浏览器请求回一堆数据,是解析成文本还是图片,就由表示层决定。数据的压缩、加密、打包等功能也都在这层完成。 + +会话层 +------ + +代表协议有: ``ADSP``\ 、\ ``RPC`` 等。 + +负责建立和断开通信连接(数据流动的逻辑通路),以及数据的分割等数据传输相关的管理。 + +比如,何时建立连接,何时断开连接以及保持多久的连接,都是由会话层来进行管理的。 + +传输层 +------ + +代表协议有 :TCP,UDP等 + +**传输层起着可靠传输的作用。** + +对于 DNS 域名解析,传输层协议是 UDP + +而对于 HTTP 请求,传输层协议是 TCP + +**那这两种协议有什么区别呢?** + +``TCP`` +协议提供可靠的通信传输,简单说就是确认目标能通信的情况下才会传输数据(因此需要三次握手),传输过程如果丢了数据,也会重发。而 +``UDP`` +协议则不然,不会确认目标能否通信,只会根据协议发到对方地址的端口。至于对方收不收到,丢不丢包,一概不管。 + +无论是哪种协议,其协议头信息都会包含本地端口号,和目标端口号(还有报文长度)。 + +加上这层头信息后,封装好的数据包再交由网络层。 + +网络层 +------ + +代表协议有:IP,ICMP +协议等,其也叫IP层,主要应用是路由器(并非我们家中的路由器,扩展阅读:https://www.zhihu.com/question/52176116)。 + +**网络层负责将数据传输到目标地址。** + +网络层将数据从发送端的主机发送到接收端的主机,两台主机间可能会存在很多数据链路,但网络层就是负责找出一条相对顺畅的通路将数据传递过去。传输的地址使用的是IP地址。 + +IP地址和我们的住址有点相似,我们的住址可以从省到市再到街逐步缩小范围,直至我们住址。IP地址也有这样的能力,通过不断转发到更近的IP地址,最终可以到达目标地址。如何选择这条路,就看网络层了。 + +这好比是快递公司的路线规划者。快递公司有很多集散中心,根据集散中心的情况(是否拥堵),找出一条经过n个集散中心的路径将货物(数据)沿路运过去。 + +在这层里,会给数据包再加一些头信息,包括本地IP地址,目标IP地址(还有数据包的生存空间TTL),同时还会有一个Protocol字段 +表示上层到底是 +UDP还是TCP协议,这个是用于对端在接收到这个数据包后知道如何解析。 + +加上这些头信息后,再把\ **数据报**\ (或者说\ **分组**\ 、\ **报文**\ )传递给链路层。 + +链路层 +------ + +代表协议有:HDLC,PPP,SLIP 等,其也叫 MAC 层,主要应用是交换机,网桥。 + +**该层负责物理层面(一个以太网相连)上互连的节点之间的通信传输。** + +数据链路层会将0、1序列划分为具有意义的数据帧传送给对端(数据帧的生成与接收)。举个例子可能会更好理解,暂且把需要传输的数据看作为不同来源的水,如果直接倒入池子中时,是无法重新分辨出不同来源的水的。但如果将不同来源的灌入瓶子中并打上记号,那就能区分出不同来源的水。这也就是为什么要划分为具有意义的数据帧传送给对端。 + +数据链路层可以看作是快递公司的司机,他们驾驶着汽车,将打包好的货物(数据帧)从一个城市(物理节点)运输到另一个城市。 + +在这层里,会给数据包再加一些头信息,包括本地的mac地址,目标mac地址,同时还有一个type字段表示上层协议是使用的是IP协议,还是其他什么协议,也是用于对端在接收到这个数据包后知道如何解析。 + +需要注意的是,数据链路层只负责将数据运送给物理相连的两端,并不负责直接发送到最终地址。 + +加上这些头信息后,再把\ **数据帧**\ 传递给物理层。 + +这一层主要解决两个问题: + +**第一个问题**\ :网络包是发给谁的,由谁来接收 + +链路层协议头会包含目标MAC和源MAC + +**第二个问题**\ :大家都在发包,谁先发,谁后发? + +对于这个问题,有多种算法可以解决 + +方法一:信道划分,就跟马路上分多个车道一样,你走你的,我走我的,互不影响 + +方法二:轮流协议,还是以交通举例,今天单号出行,明天双号出行,轮着来 + +方法三:随机接入协议,不管三七二十一,有事儿先出门,发现特堵,就回去。错过高峰再出。 + +物理层 +------ + +代表协议有: ``RS 232C``\ 、\ ``RS 449/422/423``\ 、\ ``V.24`` 和 +``X.21``\ 、\ ``X.21bis`` 等。 + +物理层负责0、1比特流(0、1序列)与电压高低、光的闪灭之间的互换。 + +物理层其实就是我们日常能接触的物理介质,比如光纤、电缆、还有空气(还有集线器、中继器、调制解调器),根据这些传输介质的不同,二进制流(0和1)会相应地转化成光信号,电信号,电磁波信号。 + +物理层是 ``OSI`` 七层模型的物理基础,没有它就谈不上数据传输了。 + +参考文章 +-------- + +- https://juejin.im/post/59eb06b1f265da430f313c7f + +.. |image0| image:: http://image.iswbm.com/20200526233356.png + From cb06c6d6de2cddb81da7efabf1deec56d255d42f Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 27 May 2020 00:32:34 +0800 Subject: [PATCH 066/147] Update --- README.md | 3 + source/c01/c01_30.md | 12 +++- source/c01/c01_30.rst | 15 ++++ source/c01/c01_45.rst | 9 +++ source/c04/c04_18.md | 121 +++++-------------------------- source/c04/c04_18.rst | 127 ++++++--------------------------- source/c09/c09_02.md | 155 ++++++++++++++++++++++++++++++++++++++++ source/c09/c09_02.rst | 161 ++++++++++++++++++++++++++++++++++++++++++ source/c09/c09_03.md | 48 +++++++++++++ source/c09/c09_03.rst | 42 +++++++++++ 10 files changed, 484 insertions(+), 209 deletions(-) create mode 100644 source/c09/c09_02.md create mode 100644 source/c09/c09_02.rst create mode 100644 source/c09/c09_03.md create mode 100644 source/c09/c09_03.rst diff --git a/README.md b/README.md index 51059df..b31a7c8 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ - 1.43 [求你了,别再使用 pprint 打印字典了](http://python.iswbm.com/en/latest/c01/c01_43.html) - 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html) - 1.45 [Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_45.html) +- 1.47 [通俗理解OSI七层模型](http://python.iswbm.com/en/latest/c01/c01_47.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) @@ -149,6 +150,8 @@ ## 第九章:有趣的工具 - 9.1 [情人节来了,教你使用 Python 来表白](http://python.iswbm.com/en/latest/c09/c09_01.html) +- 9.2 [没有这 50 个APP,我的 Mac 将只是一块铁](http://python.iswbm.com/en/latest/c09/c09_02.html) +- 9.3 [明哥的在线工具集](http://python.iswbm.com/en/latest/c09/c09_03.html) --- diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md index 09f808b..f319646 100644 --- a/source/c01/c01_30.md +++ b/source/c01/c01_30.md @@ -22,4 +22,14 @@ -## \ No newline at end of file +## Django Web 框架 + +网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django + +该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django) + +![](http://image.iswbm.com/20200525080531.png) + +在服务端网页编程里,重点介绍了 Django + +![](http://image.iswbm.com/20200525080715.png) \ No newline at end of file diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index 58d20a0..0e34aaf 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -22,7 +22,22 @@ Python 3 标准库实例教程 |image2| +Django Web 框架 +--------------- + +网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django + +该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django) + +|image3| + +在服务端网页编程里,重点介绍了 Django + +|image4| + .. |image0| image:: http://image.python-online.cn/20200104144109.png .. |image1| image:: http://image.python-online.cn/20200112210558.png .. |image2| image:: http://image.iswbm.com/20200508201333.png +.. |image3| image:: http://image.iswbm.com/20200525080531.png +.. |image4| image:: http://image.iswbm.com/20200525080715.png diff --git a/source/c01/c01_45.rst b/source/c01/c01_45.rst index 8d4feac..79ea785 100644 --- a/source/c01/c01_45.rst +++ b/source/c01/c01_45.rst @@ -67,6 +67,15 @@ >>> myos.getcwd() '/home/wangbm' +上面说过的 ``__import__`` +是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于 +``__buildins__`` 中,因此我们还可以这样导入 os 的模块: + +.. code:: python + + >>> __builtins__.__dict__['__import__']('os').getcwd() + '/home/wangbm' + 3. 使用 importlib 模块 ---------------------- diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index a0a2339..d8b0542 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -121,106 +121,7 @@ finder的显示 风扇转得快了,散热自然也快了,但是风扇声音也更大了。建议只在非常烫(超过60℃?)的时候开启。 - -## 4.18.5 软件推荐 - -Alfred 3:快捷神器 - -iTerm2:终端神器 - -New File Menu:右键新建特定格式的文件。 - -Caffenie:讲PPT时,控制不息屏。 - -Tickeys:键盘模拟音效。 - -Magnet/Moon:窗口控制 - -Bartender 3:状态栏管理 - -SourceTree:Git可视化管理 - -FreeDownloadManagger:下载管理 - -PicGo:图床上传 - -Typora/Bear:Markdown写作工具 - -滴答清单:待办事项管理 - -Capture Gif:Gif 录制(不推荐) - -Kap Beta:录屏开源免费软件,支持GIF导出,快捷键:`Command Shift 5` - -TeamViewer:远程控制工具 - -iStat Menus:系统指标仪表盘 - -CheatSheet:快捷键帮助菜单 - -CCleaner:系统清理、软件卸载 - -印象笔记:笔记 - -WPS:Office套件 - -Snipaste:截图工具 - -Macs Fan Control:控制风扇转速,加快散热 - -ShortCat:在系统栏也可以搜索聚焦 - -Coffee Buzz:阻止电脑休眠,三种模式:永不休眠、屏幕熄灭但电脑不休眠、跟随系统节能设置。(限免已拿到) - -Pretty Regular Expressions:正则表达式测试工具(限免已拿到) - -Vicinity:环境白噪音(限免已拿到) - -QSpace:[finder 的增强版](https://mp.weixin.qq.com/s/BRBZZfx0bGc8X8WueS37Xg),可分屏整理文件(1块钱购买) - -eZip:与QSpace同一开发者。集所有同类产品所长的解压缩工具([官网可下](https://ezip.awehunt.com/)) - -Keta:解压缩软件 - -ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 - -PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 - -Downie:网页视频下载,复制链接即可,支持优酷、YouTube 等 700+ 网站。 - -GoodSync:和 windows 平台同步文件 - -[IINA](https://iina.io/):万能的视频播放器,一个就够 - -TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) - -NTFS for Mac 助手,安装可以在 Mac 上读写 NTFS 格式的移动硬盘或U盘。 - -NewFileMenu:使得可以在访达中新建文件 - -ScreenFlow:视频录制 - -One Switch,多合一功能开关合集,一键隐藏桌面、保持屏幕常亮、切换夜间模式。 - -Fantastical,可能是 Mac 上最好用的日历工具。 - -TinyCal(小历),菜单栏日历小工具,有农历和假期。Mac App Store 购买下载。 - -iMazing,最佳 iOS 备份及管理软件,完美替代 iTunes。 - -Permute 3,精致小巧的视频格式转换工具。 - -PP 鸭,好用的多格式图片压缩软件。 - -Squash,优雅而强大的图片压缩工具。领取优惠购买链接: - -GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 - -PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 - -iText,精准的 OCR 文字识别工具。 - -## 4.18.6 brew 的使用 +## 4.18.5 brew 的使用 设置国内源 @@ -252,7 +153,7 @@ brew update brew cask install docker ``` -## 4.18.7 访达使用技巧 +## 4.18.6 访达使用技巧 详细请看这篇文章([MacOS实用技巧之Finder(访达)的使用](https://www.jianshu.com/p/3666e6954e8a)),非常好的教程。 @@ -349,7 +250,7 @@ open ~/Code -## 4.18.8 使用小鹤双拼 +## 4.18.7 使用小鹤双拼 2018 款的 MBP 系统是 10.13.6 ,这个系统支持的双拼是自然码,若想使用小鹤双拼,可以使用如下命令 @@ -385,6 +286,22 @@ defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 对应的 github:https://github.com/BlueSky-07/Shuang +## 4.18.8 输入法切换 BUG + +问题是由 `TISwitcher` 引起的。 + +当你按住 `control` 键,不停的敲 `空格` 就可了看到这个进程的面貌了。 + +对于使用 `command + space` 切换输入法的, `control` 换成 `command` 即可。 `TISwitcher` 干掉没有任何影响。顶多就是切换输入法时,看不到切换状态而已。 + +解决方法如下: + +1. 打开 `Activity Monitor` +2. 找到 `TISwitcher` 这个进程,干掉就 OK 了 +3. 为了防止重启后,这个进程再次启动, 直接删掉 `/System/Library/CoreServices/Menu Extras/TextInput.menu/Contents/SharedSupport/TISwitcher.app` + + + ## 参考文章 1. [Mac 上值得推荐的录屏软件](https://mp.weixin.qq.com/s/cvS6BLI53JFQY2P3rvg9Xw) diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index da93952..340d81e 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -132,110 +132,7 @@ finder的显示 风扇转得快了,散热自然也快了,但是风扇声音也更大了。建议只在非常烫(超过60℃?)的时候开启。 -4.18.5 软件推荐 ---------------- - -Alfred 3:快捷神器 - -iTerm2:终端神器 - -New File Menu:右键新建特定格式的文件。 - -Caffenie:讲PPT时,控制不息屏。 - -Tickeys:键盘模拟音效。 - -Magnet/Moon:窗口控制 - -Bartender 3:状态栏管理 - -SourceTree:Git可视化管理 - -FreeDownloadManagger:下载管理 - -PicGo:图床上传 - -Typora/Bear:Markdown写作工具 - -滴答清单:待办事项管理 - -Capture Gif:Gif 录制(不推荐) - -Kap Beta:录屏开源免费软件,支持GIF导出,快捷键:\ ``Command Shift 5`` - -TeamViewer:远程控制工具 - -iStat Menus:系统指标仪表盘 - -CheatSheet:快捷键帮助菜单 - -CCleaner:系统清理、软件卸载 - -印象笔记:笔记 - -WPS:Office套件 - -Snipaste:截图工具 - -Macs Fan Control:控制风扇转速,加快散热 - -ShortCat:在系统栏也可以搜索聚焦 - -Coffee -Buzz:阻止电脑休眠,三种模式:永不休眠、屏幕熄灭但电脑不休眠、跟随系统节能设置。(限免已拿到) - -Pretty Regular Expressions:正则表达式测试工具(限免已拿到) - -Vicinity:环境白噪音(限免已拿到) - -QSpace:\ `finder -的增强版 `__\ ,可分屏整理文件(1块钱购买) - -eZip:与QSpace同一开发者。集所有同类产品所长的解压缩工具(\ `官网可下 `__\ ) - -Keta:解压缩软件 - -ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 - -PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 - -Downie:网页视频下载,复制链接即可,支持优酷、YouTube 等 700+ 网站。 - -GoodSync:和 windows 平台同步文件 - -`IINA `__\ :万能的视频播放器,一个就够 - -TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) - -NTFS for Mac 助手,安装可以在 Mac 上读写 NTFS 格式的移动硬盘或U盘。 - -NewFileMenu:使得可以在访达中新建文件 - -ScreenFlow:视频录制 - -One -Switch,多合一功能开关合集,一键隐藏桌面、保持屏幕常亮、切换夜间模式。 - -Fantastical,可能是 Mac 上最好用的日历工具。 - -TinyCal(小历),菜单栏日历小工具,有农历和假期。Mac App Store -购买下载。 - -iMazing,最佳 iOS 备份及管理软件,完美替代 iTunes。 - -Permute 3,精致小巧的视频格式转换工具。 - -PP 鸭,好用的多格式图片压缩软件。 - -Squash,优雅而强大的图片压缩工具。领取优惠购买链接: - -GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 - -PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 - -iText,精准的 OCR 文字识别工具。 - -4.18.6 brew 的使用 +4.18.5 brew 的使用 ------------------ 设置国内源 @@ -268,7 +165,7 @@ iText,精准的 OCR 文字识别工具。 brew cask install docker -4.18.7 访达使用技巧 +4.18.6 访达使用技巧 ------------------- 详细请看这篇文章(\ `MacOS实用技巧之Finder(访达)的使用 `__\ ),非常好的教程。 @@ -362,7 +259,7 @@ iText,精准的 OCR 文字识别工具。 # 在指定目录打开 open ~/Code -4.18.8 使用小鹤双拼 +4.18.7 使用小鹤双拼 ------------------- 2018 款的 MBP 系统是 10.13.6 @@ -400,6 +297,24 @@ iText,精准的 OCR 文字识别工具。 对应的 github:https://github.com/BlueSky-07/Shuang +4.18.8 输入法切换 BUG +--------------------- + +问题是由 ``TISwitcher`` 引起的。 + +当你按住 ``control`` 键,不停的敲 ``空格`` 就可了看到这个进程的面貌了。 + +对于使用 ``command + space`` 切换输入法的, ``control`` 换成 ``command`` +即可。 ``TISwitcher`` +干掉没有任何影响。顶多就是切换输入法时,看不到切换状态而已。 + +解决方法如下: + +1. 打开 ``Activity Monitor`` +2. 找到 ``TISwitcher`` 这个进程,干掉就 OK 了 +3. 为了防止重启后,这个进程再次启动, 直接删掉 + ``/System/Library/CoreServices/Menu Extras/TextInput.menu/Contents/SharedSupport/TISwitcher.app`` + 参考文章 -------- diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md new file mode 100644 index 0000000..7ef71ad --- /dev/null +++ b/source/c09/c09_02.md @@ -0,0 +1,155 @@ +# 9.2 没有这 50 个APP,我的 Mac 将只是一块铁 + +## 1. 效率神器 + +### 1. Alfred 3 + +Mac 中必不可少的APP,没有它 Mac 基本也就『残废』了。 + +目前我非常依赖它的功能有: + +1. 快速百度/谷歌搜索 +2. 快速打开某个链接/书签:使用 `bm +关键字` +3. 快速打开/切换指定APP +4. 访问剪切板记录:快捷键 Command + Option + c +5. 快速查找文件:使用 `find + 关键字` +6. 自定义大量个人常用短语,比如我要填写邮箱,只要输入`!mail`,就会自动转成 `wongbingming@163.com` + + + +### 2. NewFileMenu + +在访达下,你无法使用右键菜单直接新建文件,比如我们使用的 Excel,World 等 + +NewFileMenu 可以解决你的烦恼 + +![](http://image.iswbm.com/image-20200524183640630.png) + + + +### 3. Magnet/Moon + +分屏工作几乎是办公必备技能。 + + + +### 4. 快贴 + +https://clipber.com/ + +用于多设备间剪切板同步共享 + +### GoodSync + +和 windows 平台同步文件 + +One Switch,多合一功能开关合集,一键隐藏桌面、保持屏幕常亮、切换夜间模式。 + +CheatSheet:快捷键帮助菜单 + +## 2. 开发必备 + +iTerm2:终端神器 + +PyCharm:写 Python 代码 + +Goland:写 Golang 代码 + +SourceTree:Git可视化管理 + +Pretty Regular Expressions:正则表达式测试工具(限免已拿到) + + + +## 3. 优化体验 + +Bartender 3:状态栏管理 + +Caffenie:讲PPT时,控制不息屏。 + +Tickeys:键盘模拟音效。 + +Vicinity:环境白噪音(限免已拿到) + +Fantastical,可能是 Mac 上最好用的日历工具。 + +TinyCal(小历),菜单栏日历小工具,有农历和假期。Mac App Store 购买下载。 + +Coffee Buzz:阻止电脑休眠,三种模式:永不休眠、屏幕熄灭但电脑不休眠、跟随系统节能设置。(限免已拿到) + +ShortCat:在系统栏也可以搜索聚焦 + +## 4. 图片影音 + +Snipaste:截图工具 + +Capture Gif:Gif 录制(不推荐) + +Kap Beta:录屏开源免费软件,支持GIF导出,快捷键:`Command Shift 5` + +ScreenFlow:视频录制 + +ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 + +[IINA](https://iina.io/):万能的视频播放器,一个就够 + +PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 + +Downie:网页视频下载,复制链接即可,支持优酷、YouTube 等 700+ 网站。 + +Permute 3,精致小巧的视频格式转换工具。 + +PP 鸭,好用的多格式图片压缩软件。 + +GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 + +PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 + + + +## 5. 文件管理 + +FreeDownloadManagger:下载管理 + +滴答清单:待办事项管理 + +QSpace:[finder 的增强版](https://mp.weixin.qq.com/s/BRBZZfx0bGc8X8WueS37Xg),可分屏整理文件(1块钱购买) + +eZip:与QSpace同一开发者。集所有同类产品所长的解压缩工具([官网可下](https://ezip.awehunt.com/)) + +NTFS for Mac 助手,安装可以在 Mac 上读写 NTFS 格式的移动硬盘或U盘。 + +iStat Menus:系统指标仪表盘 + +Squash,优雅而强大的图片压缩工具。 + +Keta:解压缩软件 + +## 6. 写作必备 + +印象笔记:笔记 + +PicGo:图床上传 + +Typora/Bear:Markdown写作工具 + +WPS:Office套件 + +TeamViewer:远程控制工具 + +iText,精准的 OCR 文字识别工具。 + +## 7. 系统管理 + +CCleaner:系统清理、软件卸载 + +TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) + +iMazing,最佳 iOS 备份及管理软件,完美替代 iTunes。 + +Macs Fan Control:控制风扇转速,加快散热 + + + + + diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst new file mode 100644 index 0000000..a31b463 --- /dev/null +++ b/source/c09/c09_02.rst @@ -0,0 +1,161 @@ +9.2 没有这 50 个APP,我的 Mac 将只是一块铁 +========================================== + +1. 效率神器 +----------- + +1. Alfred 3 +~~~~~~~~~~~ + +Mac 中必不可少的APP,没有它 Mac 基本也就『残废』了。 + +目前我非常依赖它的功能有: + +1. 快速百度/谷歌搜索 +2. 快速打开某个链接/书签:使用 ``bm +关键字`` +3. 快速打开/切换指定APP +4. 访问剪切板记录:快捷键 Command + Option + c +5. 快速查找文件:使用 ``find + 关键字`` +6. 自定义大量个人常用短语,比如我要填写邮箱,只要输入\ ``!mail``\ ,就会自动转成 + ``wongbingming@163.com`` + +2. NewFileMenu +~~~~~~~~~~~~~~ + +在访达下,你无法使用右键菜单直接新建文件,比如我们使用的 Excel,World 等 + +NewFileMenu 可以解决你的烦恼 + +|image0| + +3. Magnet/Moon +~~~~~~~~~~~~~~ + +分屏工作几乎是办公必备技能。 + +4. 快贴 +~~~~~~~ + +https://clipber.com/ + +用于多设备间剪切板同步共享 + +GoodSync +~~~~~~~~ + +和 windows 平台同步文件 + +One +Switch,多合一功能开关合集,一键隐藏桌面、保持屏幕常亮、切换夜间模式。 + +CheatSheet:快捷键帮助菜单 + +2. 开发必备 +----------- + +iTerm2:终端神器 + +PyCharm:写 Python 代码 + +Goland:写 Golang 代码 + +SourceTree:Git可视化管理 + +Pretty Regular Expressions:正则表达式测试工具(限免已拿到) + +3. 优化体验 +----------- + +Bartender 3:状态栏管理 + +Caffenie:讲PPT时,控制不息屏。 + +Tickeys:键盘模拟音效。 + +Vicinity:环境白噪音(限免已拿到) + +Fantastical,可能是 Mac 上最好用的日历工具。 + +TinyCal(小历),菜单栏日历小工具,有农历和假期。Mac App Store +购买下载。 + +Coffee +Buzz:阻止电脑休眠,三种模式:永不休眠、屏幕熄灭但电脑不休眠、跟随系统节能设置。(限免已拿到) + +ShortCat:在系统栏也可以搜索聚焦 + +4. 图片影音 +----------- + +Snipaste:截图工具 + +Capture Gif:Gif 录制(不推荐) + +Kap Beta:录屏开源免费软件,支持GIF导出,快捷键:\ ``Command Shift 5`` + +ScreenFlow:视频录制 + +ArcTime Pro:免费给视频自动加字幕,依赖 java 环境 + +`IINA `__\ :万能的视频播放器,一个就够 + +PPDuck3:优秀的图片压缩软件,体积减小但是画质肉眼观察不出变化,压缩完自动替换原图。免费使用一次仅能压缩10张,需要退出重进。 + +Downie:网页视频下载,复制链接即可,支持优酷、YouTube 等 700+ 网站。 + +Permute 3,精致小巧的视频格式转换工具。 + +PP 鸭,好用的多格式图片压缩软件。 + +GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 + +PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 + +5. 文件管理 +----------- + +FreeDownloadManagger:下载管理 + +滴答清单:待办事项管理 + +QSpace:\ `finder +的增强版 `__\ ,可分屏整理文件(1块钱购买) + +eZip:与QSpace同一开发者。集所有同类产品所长的解压缩工具(\ `官网可下 `__\ ) + +NTFS for Mac 助手,安装可以在 Mac 上读写 NTFS 格式的移动硬盘或U盘。 + +iStat Menus:系统指标仪表盘 + +Squash,优雅而强大的图片压缩工具。 + +Keta:解压缩软件 + +6. 写作必备 +----------- + +印象笔记:笔记 + +PicGo:图床上传 + +Typora/Bear:Markdown写作工具 + +WPS:Office套件 + +TeamViewer:远程控制工具 + +iText,精准的 OCR 文字识别工具。 + +7. 系统管理 +----------- + +CCleaner:系统清理、软件卸载 + +TUXERA:使得插入的 NTFS 磁盘格式,能写入(不装只能读取) + +iMazing,最佳 iOS 备份及管理软件,完美替代 iTunes。 + +Macs Fan Control:控制风扇转速,加快散热 + +.. |image0| image:: http://image.iswbm.com/image-20200524183640630.png + diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md new file mode 100644 index 0000000..64a21df --- /dev/null +++ b/source/c09/c09_03.md @@ -0,0 +1,48 @@ +# 9.3 明哥的在线工具集 + + + + + +## 文档 + +### 格式转换 + +Smallpdf:https://smallpdf.com/cn + +iLovePDF:https://www.ilovepdf.com/zh-cn + +CleverPDF:https://www.cleverpdf.com/cn + + + +### 添加水印 + +**CleverPDF** + +支持中英文及图片水印,文本大小可调,字体有不少可选项,20M内免费 + +https://www.cleverpdf.com/cn + + + + + +### 文本翻译 + +DeepL:https://www.deepl.com/translator + + + +## 图片 + +ColouriseSG(给黑白照片上色):https://colourise.sg/ + +img8bit(将图片转成8-bit样式):https://img8bit.com/ + +ai-art(自拍照生成为名画艺术品):https://ai-art.tokyo/en + +## 工具集 + +码力全开:https://www.maliquankai.com/ + diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst new file mode 100644 index 0000000..0e13613 --- /dev/null +++ b/source/c09/c09_03.rst @@ -0,0 +1,42 @@ +9.3 明哥的在线工具集 +==================== + +文档 +---- + +格式转换 +~~~~~~~~ + +Smallpdf:https://smallpdf.com/cn + +iLovePDF:https://www.ilovepdf.com/zh-cn + +CleverPDF:https://www.cleverpdf.com/cn + +添加水印 +~~~~~~~~ + +**CleverPDF** + +支持中英文及图片水印,文本大小可调,字体有不少可选项,20M内免费 + +https://www.cleverpdf.com/cn + +文本翻译 +~~~~~~~~ + +DeepL:https://www.deepl.com/translator + +图片 +---- + +ColouriseSG(给黑白照片上色):https://colourise.sg/ + +img8bit(将图片转成8-bit样式):https://img8bit.com/ + +ai-art(自拍照生成为名画艺术品):https://ai-art.tokyo/en + +工具集 +------ + +码力全开:https://www.maliquankai.com/ From 8c9dd7ad1248b30747144ea992eb05d6bb975233 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 27 May 2020 00:38:34 +0800 Subject: [PATCH 067/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_47.md | 6 ++---- source/c01/c01_47.rst | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/source/c01/c01_47.md b/source/c01/c01_47.md index d9ec1ea..dcd2550 100644 --- a/source/c01/c01_47.md +++ b/source/c01/c01_47.md @@ -26,9 +26,9 @@ OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI 由于日常开发中,我们接触的基本都是 HTTP,这里就以 HTTP 为例。 -这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页(www.baidu.com),由于你请求的是域名,域名首先要被解析成真正的ip,才能知道你要往哪个服务器上获取首页。 +这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页。 -假设这里已经获得了ip,浏览器会将这些请求的信息,封装成一个 HTTP 数据包,这个数据包头会包含一些头信息。然后交由传输层(TCP)。 +览器会将你请求的信息,封装成一个 HTTP 数据包,这个数据包头会包含一些基本的头部信息及请求体(如果有的话)。然后交由下一层处理。 ## 表示层 @@ -134,8 +134,6 @@ IP地址和我们的住址有点相似,我们的住址可以从省到市再到 - - ## 参考文章 - https://juejin.im/post/59eb06b1f265da430f313c7f \ No newline at end of file diff --git a/source/c01/c01_47.rst b/source/c01/c01_47.rst index 70fd816..ea512fe 100644 --- a/source/c01/c01_47.rst +++ b/source/c01/c01_47.rst @@ -31,10 +31,10 @@ Interconnect),即开放式系统互联。一般都叫OSI参考模型,是IS 由于日常开发中,我们接触的基本都是 HTTP,这里就以 HTTP 为例。 -这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页(www.baidu.com),由于你请求的是域名,域名首先要被解析成真正的ip,才能知道你要往哪个服务器上获取首页。 +这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页。 -假设这里已经获得了ip,浏览器会将这些请求的信息,封装成一个 HTTP -数据包,这个数据包头会包含一些头信息。然后交由传输层(TCP)。 +览器会将你请求的信息,封装成一个 HTTP +数据包,这个数据包头会包含一些基本的头部信息及请求体(如果有的话)。然后交由下一层处理。 表示层 ------ From 13e0d39a9ddb781d33ba9bc3b290dbd57125b980 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 31 May 2020 18:36:18 +0800 Subject: [PATCH 068/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + source/c01/c01_48.md | 275 +++++++++++++++++++++++++++++++++ source/c01/c01_48.rst | 352 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 628 insertions(+) create mode 100644 source/c01/c01_48.md create mode 100644 source/c01/c01_48.rst diff --git a/README.md b/README.md index b31a7c8..faed639 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ - 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html) - 1.45 [Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_45.html) - 1.47 [通俗理解OSI七层模型](http://python.iswbm.com/en/latest/c01/c01_47.html) +- 1.48 [详细解读 DNS 解析过程](http://python.iswbm.com/en/latest/c01/c01_48.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) diff --git a/source/c01/c01_48.md b/source/c01/c01_48.md new file mode 100644 index 0000000..111ad45 --- /dev/null +++ b/source/c01/c01_48.md @@ -0,0 +1,275 @@ +# 1.48 详细解读 DNS 解析过程 + +## 1. DNS 是什么? + +DNS (Domain Name System 的缩写)的作用非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。 + +举例来说,如果你要访问域名`math.stackexchange.com`,首先要通过DNS查出它的IP地址是`151.101.129.69`。 + +## 2. 域名的层级 + +由于后面我会讲到 DNS 的解析过程,因此需要你对域名的层级有一些了解 + +- 根域名 :`.root` 或者 `.` ,通常是省略的 +- 顶级域名,如 `.com`,`.cn` 等 +- 次级域名,如 `baidu.com` 里的 `baidu`,这个用户是可以注册购买的 +- 主机域名,比如 `baike.baidu.com` 里的`baike`,这个用户是可分配的 + +``` +主机名.次级域名.顶级域名.根域名 + +baike.baidu.com.root +``` + + + +## 3. DNS 解析过程 + +咱们以访问 `www.163.com` 这个域名为例,来看一看当你访问 www.163.com 时,会发生哪些事: + +1. 先查找本地 DNS 缓存(自己的电脑上),有则返回,没有则进入下一步 +2. 查看本地 hosts 文件有没有相应的映射记录,有则返回,没有则进入下一步 +3. 向本地 DNS 服务器(一般都是你的网络接入服务器商提供,比如中国电信,中国移动)发送请求进行查询,本地DNS服务器收到请求后,会先查下自己的缓存记录,如果查到了直接返回就结束了,如果没有查到,本地DNS服务器就会向DNS的根域名服务器发起查询请求:请问老大, `www.163.com` 的ip是啥? +4. 根域名服务器收到请求后,看到这是个 `.com` 的域名,就回信说:这个域名是由 `.com` 老弟管理的,你去问他好了,这是`.com`老弟的联系方式(ip1)。 +5. 本地 DNS 服务器接收到回信后,照着老大哥给的联系方式(ip1),马上给 `.com` 这个顶级域名服务器发起请求:请问 `.com` 大大,`www.163.com` 的ip 是啥? +6. `.com` 顶级域名服务器接收到请求后,看到这是 `163.com` 的域名,就回信说:这个域名是 `.163.com` 老弟管理的,你就去问他就行了,这是他的联系方式(ip2) +7. 本地 DNS 服务器接收到回信后,按照前辈的指引(ip2),又向 `.163.com` 这个权威域名服务器发起请求:请问 `163.com` 大大,请问 `www.163.com` 的ip是啥? +8. `163.com` 权威域名服务器接收到请求后,确认了是自己管理的域名,马上查了下自己的小本本,把 `www.163.com` 的ip告诉了 本地DNS服务器。 +9. 本地DNS服务器接收到回信后,非常地开心,这下总算拿到了`www.163.com`的ip了,马上把这个消息告诉了要求查询的客户(就是你的电脑)。由于这个过程比较漫长,本地DNS服务器为了节省时间,也为了尽量不去打扰各位老大哥,就把这个查询结果偷偷地记在了自己的小本本上,方便下次有人来查询时,可以快速回应。 + +总结起来就是三句话 + +1. 从"根域名服务器"查到"顶级域名服务器"的NS记录和A记录(IP地址) +2. 从"顶级域名服务器"查到"次级域名服务器"的NS记录和A记录(IP地址) +3. 从"次级域名服务器"查出"主机名"的IP地址 + +![](http://image.iswbm.com/464291-20170703113844956-354755333.jpg) + +## 4. DNS的缓存时间 + +上面的几个步骤里,可以看到有两个地方会缓存 DNS 的查询记录,有了缓存,在一定程度上会提高查询效率,但同时在准确率上会有所损失。 + +因此我们在配置 DNS 解析的时候,会有一个 TTL 参数(Time To Live),意思就是这个缓存可以存活多长时间,过了这个时间,本地 DNS 就会删除这条记录,删除了缓存后,你再访问,就要重新走一遍上面的流程,获取最新的地址。 + +![](http://image.iswbm.com/image-20200531141521689.png) + + + +## 5. DNS 的记录类型 + +当我们在阿里云买了一个域名后,可以配置我们主机域名解析规则,也就是 **记录**。 + +![阿里云 域名云解析](http://image.iswbm.com/image-20200531170212224.png) + +常见的 DNS 记录类型如下 + +- `A`:地址记录(Address),返回域名指向的IP地址。 + +- `NS`:域名服务器记录(Name Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址。 +- `MX`:邮件记录(Mail eXchange),返回接收电子邮件的服务器地址。 +- `CNAME`:规范名称记录(Canonical Name),返回另一个域名,即当前查询的域名是另一个域名的跳转,详见下文。 +- `PTR`:逆向查询记录(Pointer Record),只用于从IP地址查询域名,详见下文。 + + + +## 6. DNS 报文结构 + +后面我将使用 wireshark 抓取 DNS 的数据包,但是在开始之前 ,得先了解一下 DNS 的报文结构 + +![](http://image.iswbm.com/image-20200531152824672.png) + +- 事务 ID:DNS 报文的 ID 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。通过它可以区分 DNS 应答报文是对哪个请求进行响应的。 +- 标志:DNS 报文中的标志字段。 +- 问题计数:DNS 查询请求的数目。 +- 回答资源记录数:DNS 响应的数目。 +- 权威名称服务器计数:权威名称服务器的数目。 +- 附加资源记录数:额外的记录数目(权威名称服务器对应 IP 地址的数目)。 + +## 7. Wireshark抓包实战 + +打开 Wireshark 后,使用 `ping 163.com` 来发起 DNS 解析请求,使用 `DNS` 关键字在Wireshark 过滤。 + +从抓取的报文整体来看,我们可以粗略获取几个信息 + +1. DNS 是应用层协议,传输层协议使用的是 UDP +2. DNS 默认端口是 53 + +![](http://image.iswbm.com/20200531175736.png) + +请求和应答的报文的截图我放在了下面,接下来我将逐个分析。 + +**请求** + +![](http://image.iswbm.com/20200531175811.png) + +**应答** + +![](http://image.iswbm.com/image-20200531153110621.png) + + + +### Transaction ID + +请求和应答的事务ID应当是一个:0xd0d7 + +### Flags + +标志字段里的内容比较多,每个字段的含义如下 + +- QR(Response):查询请求/响应的标志信息。查询请求时,值为 0;响应时,值为 1。 +- Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 表示服务器状态请求。 +- AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。 +- TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。 +- RD(Recursion Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。 +- RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 1 时,表示服务器支持递归查询。 +- Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。 +- rcode(Reply code):返回码字段,表示响应的差错状态。当值为 0 时,表示没有错误;当值为 1 时,表示报文格式错误(Format error),服务器不能理解请求的报文;当值为 2 时,表示域名服务器失败(Server failure),因为服务器的原因导致没办法处理这个请求;当值为 3 时,表示名字错误(Name Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为 4 时,表示查询类型不支持(Not Implemented),即域名服务器不支持查询类型;当值为 5 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。 + +### Answer RRs + +回答资源记录数,在应答包里为 2,说明返回了两条查询结果,你可以在 Answer 字段里看到。 + +### Authority RRs + +权威名称服务器计数 + +### Additionnal RRs + +附加资源记录数 + +### Answers + +应答的主要内容,这里返回两条结果,每条结果里的字段有 + +```shell +Name: 查询的域名 +Type: A表示IPv4,AAAA 表示IPv6 +Class: 表示Internet,几乎总是它 +Time to live: 生存时间 +Data length: 数据长度 +Address: 查询到的 IP 地址 +``` + + + +## 8. DNS 劫持 与 HTTP 劫持 + +通过上面的讲解,我们都知道了,DNS 完成了一次域名到 IP 的映射查询,当你在访问 www.baidu.com 时,能正确返回给你 百度首页的 ip。 + +但如果此时 DNS 解析出现了一些问题,当你想要访问 www.baidu.com 时,却返回给你 www.google.com 的ip,这就是我们常说的 DNS 劫持。 + +与之容易混淆的有 HTTP 劫持。 + +那什么是 HTTP 劫持呢? + +你一定见过当你在访问 某个网站时,右下角也突然弹出了一个扎眼的广告弹窗。这就是 HTTP 劫持。 + +借助别人文章里的例子,它们俩的区别就好比是 + +- DNS劫持是你想去机场的时候,把你给丢到火车站。 + +- HTTP劫持是你去机场途中,有人给你塞小广告。 + +**那么 DNS劫持 是如何产生的呢?** + +下面大概说几种DNS劫持方法: + +**1.本机DNS劫持** + +攻击者通过某些手段使用户的计算机感染上木马病毒,或者恶意软件之后,恶意修改本地DNS配置,比如修改本地hosts文件,缓存等 + +**2. 路由DNS劫持** + +很多用户默认路由器的默认密码,攻击者可以侵入到路由管理员账号中,修改路由器的默认配置 + +**3.攻击DNS服务器** + +直接攻击DNS服务器,例如对DNS服务器进行DDOS攻击,可以是DNS服务器宕机,出现异常请求,还可以利用某些手段感染dns服务器的缓存,使给用户返回来的是恶意的ip地址 + +## 9. 工具的使用 + +### dig 命令 + +dig是一个在类Unix命令行模式下查询DNS包括NS记录,A记录,MX记录等相关信息的工具。 + +通过 dig (参数:`+trace`)命令,我们可以看到上面描述的 DNS 解析的详细过程 + +![](http://image.iswbm.com/image-20200531162810531.png) + +从返回的结果,我们可以看得出几点信息 + +1. 我们的本地 DNS 服务器 ip 为 192.168.1.1,端口为53,你可以在 /etc/resolv.conf 里看到这个配置 +2. 根域名服务器目前全球一共只有十三台,从a.root-servers.net. `到`m.root-servers.net. ,它们对应的ip地址,已经内置在本地DNS服务器中。 + +如果你只想看到结果,可以使用 `+short` 参数,可以直接返回 www.163.com 对应着哪几个ip + +![](http://image.iswbm.com/image-20200531164525384.png) + +你也可以加个 `@` 参数 ,指定从某个 DNS 服务器进行查询 + +![](http://image.iswbm.com/image-20200531170427834.png) + +如果你只想查看指定的记录类型 + +![](http://image.iswbm.com/image-20200531170543250.png) + + + +### host 命令 + +`host` 命令 可以看作`dig`命令的简化版本,返回当前请求域名的各种记录。 + +![](http://image.iswbm.com/image-20200531171610902.png) + + + +### whois命令 + +`whois`命令用来查看域名的注册情况。 + +![](http://image.iswbm.com/image-20200531171905345.png) + +### nslookup命令 + +nslookup也是常用的一个查询 DNS 解析结果的工具 + +```shell +$ nslookup [查询的域名] [指定DNS服务器] +``` + +![](http://image.iswbm.com/image-20200531145109182.png) + +你也可以指定公网的域名服务器进行查询,比如常见的 114.114.114.114 + +![](http://image.iswbm.com/image-20200531145449577.png) + + + +## 10. 手动清理本地缓存 + +MacOS + +```shell +$ sudo dscacheutil -flushcache +$ sudo killall -HUP mDNSResponder +``` + +Windows + +```shell +$ ipconfig /flushdns +``` + +Linux + +```shell +# 使用NSCD的DNS缓存 +$ sudo /etc/init.d/nscd restart + +# 服务器或者路由器使用DNSMASQ +$ sudo dnsmasq restart +``` + + + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file diff --git a/source/c01/c01_48.rst b/source/c01/c01_48.rst new file mode 100644 index 0000000..62d0503 --- /dev/null +++ b/source/c01/c01_48.rst @@ -0,0 +1,352 @@ +1.48 详细解读 DNS 解析过程 +========================== + +1. DNS 是什么? +--------------- + +DNS (Domain Name System +的缩写)的作用非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。 + +举例来说,如果你要访问域名\ ``math.stackexchange.com``\ ,首先要通过DNS查出它的IP地址是\ ``151.101.129.69``\ 。 + +2. 域名的层级 +------------- + +由于后面我会讲到 DNS 的解析过程,因此需要你对域名的层级有一些了解 + +- 根域名 :\ ``.root`` 或者 ``.`` ,通常是省略的 +- 顶级域名,如 ``.com``\ ,\ ``.cn`` 等 +- 次级域名,如 ``baidu.com`` 里的 ``baidu``\ ,这个用户是可以注册购买的 +- 主机域名,比如 ``baike.baidu.com`` + 里的\ ``baike``\ ,这个用户是可分配的 + +:: + + 主机名.次级域名.顶级域名.根域名 + + baike.baidu.com.root + +3. DNS 解析过程 +--------------- + +咱们以访问 ``www.163.com`` 这个域名为例,来看一看当你访问 www.163.com +时,会发生哪些事: + +1. 先查找本地 DNS 缓存(自己的电脑上),有则返回,没有则进入下一步 +2. 查看本地 hosts 文件有没有相应的映射记录,有则返回,没有则进入下一步 +3. 向本地 DNS + 服务器(一般都是你的网络接入服务器商提供,比如中国电信,中国移动)发送请求进行查询,本地DNS服务器收到请求后,会先查下自己的缓存记录,如果查到了直接返回就结束了,如果没有查到,本地DNS服务器就会向DNS的根域名服务器发起查询请求:请问老大, + ``www.163.com`` 的ip是啥? +4. 根域名服务器收到请求后,看到这是个 ``.com`` + 的域名,就回信说:这个域名是由 ``.com`` + 老弟管理的,你去问他好了,这是\ ``.com``\ 老弟的联系方式(ip1)。 +5. 本地 DNS 服务器接收到回信后,照着老大哥给的联系方式(ip1),马上给 + ``.com`` 这个顶级域名服务器发起请求:请问 ``.com`` + 大大,\ ``www.163.com`` 的ip 是啥? +6. ``.com`` 顶级域名服务器接收到请求后,看到这是 ``163.com`` + 的域名,就回信说:这个域名是 ``.163.com`` + 老弟管理的,你就去问他就行了,这是他的联系方式(ip2) +7. 本地 DNS 服务器接收到回信后,按照前辈的指引(ip2),又向 ``.163.com`` + 这个权威域名服务器发起请求:请问 ``163.com`` 大大,请问 + ``www.163.com`` 的ip是啥? +8. ``163.com`` + 权威域名服务器接收到请求后,确认了是自己管理的域名,马上查了下自己的小本本,把 + ``www.163.com`` 的ip告诉了 本地DNS服务器。 +9. 本地DNS服务器接收到回信后,非常地开心,这下总算拿到了\ ``www.163.com``\ 的ip了,马上把这个消息告诉了要求查询的客户(就是你的电脑)。由于这个过程比较漫长,本地DNS服务器为了节省时间,也为了尽量不去打扰各位老大哥,就把这个查询结果偷偷地记在了自己的小本本上,方便下次有人来查询时,可以快速回应。 + +总结起来就是三句话 + +1. 从“根域名服务器”查到“顶级域名服务器”的NS记录和A记录(IP地址) +2. 从“顶级域名服务器”查到“次级域名服务器”的NS记录和A记录(IP地址) +3. 从“次级域名服务器”查出“主机名”的IP地址 + +|image0| + +4. DNS的缓存时间 +---------------- + +上面的几个步骤里,可以看到有两个地方会缓存 DNS +的查询记录,有了缓存,在一定程度上会提高查询效率,但同时在准确率上会有所损失。 + +因此我们在配置 DNS 解析的时候,会有一个 TTL 参数(Time To +Live),意思就是这个缓存可以存活多长时间,过了这个时间,本地 DNS +就会删除这条记录,删除了缓存后,你再访问,就要重新走一遍上面的流程,获取最新的地址。 + +|image1| + +5. DNS 的记录类型 +----------------- + +当我们在阿里云买了一个域名后,可以配置我们主机域名解析规则,也就是 +**记录**\ 。 + +.. figure:: http://image.iswbm.com/image-20200531170212224.png + :alt: 阿里云 域名云解析 + + 阿里云 域名云解析 + +常见的 DNS 记录类型如下 + +- ``A``\ :地址记录(Address),返回域名指向的IP地址。 + +- ``NS``\ :域名服务器记录(Name + Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址。 +- ``MX``\ :邮件记录(Mail eXchange),返回接收电子邮件的服务器地址。 +- ``CNAME``\ :规范名称记录(Canonical + Name),返回另一个域名,即当前查询的域名是另一个域名的跳转,详见下文。 +- ``PTR``\ :逆向查询记录(Pointer + Record),只用于从IP地址查询域名,详见下文。 + +6. DNS 报文结构 +--------------- + +后面我将使用 wireshark 抓取 DNS 的数据包,但是在开始之前 ,得先了解一下 +DNS 的报文结构 + +|image2| + +- 事务 ID:DNS 报文的 ID + 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。通过它可以区分 + DNS 应答报文是对哪个请求进行响应的。 +- 标志:DNS 报文中的标志字段。 +- 问题计数:DNS 查询请求的数目。 +- 回答资源记录数:DNS 响应的数目。 +- 权威名称服务器计数:权威名称服务器的数目。 +- 附加资源记录数:额外的记录数目(权威名称服务器对应 IP 地址的数目)。 + +7. Wireshark抓包实战 +-------------------- + +打开 Wireshark 后,使用 ``ping 163.com`` 来发起 DNS 解析请求,使用 +``DNS`` 关键字在Wireshark 过滤。 + +从抓取的报文整体来看,我们可以粗略获取几个信息 + +1. DNS 是应用层协议,传输层协议使用的是 UDP +2. DNS 默认端口是 53 + +|image3| + +请求和应答的报文的截图我放在了下面,接下来我将逐个分析。 + +**请求** + +|image4| + +**应答** + +|image5| + +Transaction ID +~~~~~~~~~~~~~~ + +请求和应答的事务ID应当是一个:0xd0d7 + +Flags +~~~~~ + +标志字段里的内容比较多,每个字段的含义如下 + +- QR(Response):查询请求/响应的标志信息。查询请求时,值为 + 0;响应时,值为 1。 +- Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 + 表示服务器状态请求。 +- AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 + 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。 +- TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 + 字节并已被截断,只返回前 512 个字节。 +- RD(Recursion + Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 + 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。 +- RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 + 1 时,表示服务器支持递归查询。 +- Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。 +- rcode(Reply code):返回码字段,表示响应的差错状态。当值为 0 + 时,表示没有错误;当值为 1 时,表示报文格式错误(Format + error),服务器不能理解请求的报文;当值为 2 + 时,表示域名服务器失败(Server + failure),因为服务器的原因导致没办法处理这个请求;当值为 3 + 时,表示名字错误(Name + Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为 + 4 时,表示查询类型不支持(Not + Implemented),即域名服务器不支持查询类型;当值为 5 + 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。 + +Answer RRs +~~~~~~~~~~ + +回答资源记录数,在应答包里为 2,说明返回了两条查询结果,你可以在 Answer +字段里看到。 + +Authority RRs +~~~~~~~~~~~~~ + +权威名称服务器计数 + +Additionnal RRs +~~~~~~~~~~~~~~~ + +附加资源记录数 + +Answers +~~~~~~~ + +应答的主要内容,这里返回两条结果,每条结果里的字段有 + +.. code:: shell + + Name: 查询的域名 + Type: A表示IPv4,AAAA 表示IPv6 + Class: 表示Internet,几乎总是它 + Time to live: 生存时间 + Data length: 数据长度 + Address: 查询到的 IP 地址 + +8. DNS 劫持 与 HTTP 劫持 +------------------------ + +通过上面的讲解,我们都知道了,DNS 完成了一次域名到 IP +的映射查询,当你在访问 www.baidu.com 时,能正确返回给你 百度首页的 ip。 + +但如果此时 DNS 解析出现了一些问题,当你想要访问 www.baidu.com +时,却返回给你 www.google.com 的ip,这就是我们常说的 DNS 劫持。 + +与之容易混淆的有 HTTP 劫持。 + +那什么是 HTTP 劫持呢? + +你一定见过当你在访问 +某个网站时,右下角也突然弹出了一个扎眼的广告弹窗。这就是 HTTP 劫持。 + +借助别人文章里的例子,它们俩的区别就好比是 + +- DNS劫持是你想去机场的时候,把你给丢到火车站。 + +- HTTP劫持是你去机场途中,有人给你塞小广告。 + +**那么 DNS劫持 是如何产生的呢?** + +下面大概说几种DNS劫持方法: + +**1.本机DNS劫持** + +攻击者通过某些手段使用户的计算机感染上木马病毒,或者恶意软件之后,恶意修改本地DNS配置,比如修改本地hosts文件,缓存等 + +**2. 路由DNS劫持** + +很多用户默认路由器的默认密码,攻击者可以侵入到路由管理员账号中,修改路由器的默认配置 + +**3.攻击DNS服务器** + +直接攻击DNS服务器,例如对DNS服务器进行DDOS攻击,可以是DNS服务器宕机,出现异常请求,还可以利用某些手段感染dns服务器的缓存,使给用户返回来的是恶意的ip地址 + +9. 工具的使用 +------------- + +dig 命令 +~~~~~~~~ + +dig是一个在类Unix命令行模式下查询DNS包括NS记录,A记录,MX记录等相关信息的工具。 + +通过 dig (参数:\ ``+trace``\ )命令,我们可以看到上面描述的 DNS +解析的详细过程 + +|image6| + +从返回的结果,我们可以看得出几点信息 + +1. 我们的本地 DNS 服务器 ip 为 192.168.1.1,端口为53,你可以在 + /etc/resolv.conf 里看到这个配置 +2. 根域名服务器目前全球一共只有十三台,从a.root-servers.net. + ``到``\ m.root-servers.net. + ,它们对应的ip地址,已经内置在本地DNS服务器中。 + +如果你只想看到结果,可以使用 ``+short`` 参数,可以直接返回 www.163.com +对应着哪几个ip + +|image7| + +你也可以加个 ``@`` 参数 ,指定从某个 DNS 服务器进行查询 + +|image8| + +如果你只想查看指定的记录类型 + +|image9| + +host 命令 +~~~~~~~~~ + +``host`` 命令 +可以看作\ ``dig``\ 命令的简化版本,返回当前请求域名的各种记录。 + +|image10| + +whois命令 +~~~~~~~~~ + +``whois``\ 命令用来查看域名的注册情况。 + +|image11| + +nslookup命令 +~~~~~~~~~~~~ + +nslookup也是常用的一个查询 DNS 解析结果的工具 + +.. code:: shell + + $ nslookup [查询的域名] [指定DNS服务器] + +|image12| + +你也可以指定公网的域名服务器进行查询,比如常见的 114.114.114.114 + +|image13| + +10. 手动清理本地缓存 +-------------------- + +MacOS + +.. code:: shell + + $ sudo dscacheutil -flushcache + $ sudo killall -HUP mDNSResponder + +Windows + +.. code:: shell + + $ ipconfig /flushdns + +Linux + +.. code:: shell + + # 使用NSCD的DNS缓存 + $ sudo /etc/init.d/nscd restart + + # 服务器或者路由器使用DNSMASQ + $ sudo dnsmasq restart + +.. figure:: http://image.python-online.cn/image-20200320125724880.png + :alt: 关注公众号,获取最新干货! + + 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/464291-20170703113844956-354755333.jpg +.. |image1| image:: http://image.iswbm.com/image-20200531141521689.png +.. |image2| image:: http://image.iswbm.com/image-20200531152824672.png +.. |image3| image:: http://image.iswbm.com/20200531175736.png +.. |image4| image:: http://image.iswbm.com/20200531175811.png +.. |image5| image:: http://image.iswbm.com/image-20200531153110621.png +.. |image6| image:: http://image.iswbm.com/image-20200531162810531.png +.. |image7| image:: http://image.iswbm.com/image-20200531164525384.png +.. |image8| image:: http://image.iswbm.com/image-20200531170427834.png +.. |image9| image:: http://image.iswbm.com/image-20200531170543250.png +.. |image10| image:: http://image.iswbm.com/image-20200531171610902.png +.. |image11| image:: http://image.iswbm.com/image-20200531171905345.png +.. |image12| image:: http://image.iswbm.com/image-20200531145109182.png +.. |image13| image:: http://image.iswbm.com/image-20200531145449577.png + From 01f295b2dc219383e183c1ba1145006a759bebed Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 31 May 2020 19:04:12 +0800 Subject: [PATCH 069/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E7=AB=A0?= =?UTF-8?q?=E8=8A=82<=E7=BD=91=E7=BB=9C=E5=9F=BA=E7=A1=80>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- source/{c01/c01_48.md => c10/c10_01.md} | 4 ++-- source/{c01/c01_48.rst => c10/c10_01.rst} | 4 ++-- source/chapters/p10.rst | 23 +++++++++++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) rename source/{c01/c01_48.md => c10/c10_01.md} (99%) rename source/{c01/c01_48.rst => c10/c10_01.rst} (99%) create mode 100755 source/chapters/p10.rst diff --git a/README.md b/README.md index faed639..2145384 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ - 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html) - 1.45 [Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_45.html) - 1.47 [通俗理解OSI七层模型](http://python.iswbm.com/en/latest/c01/c01_47.html) -- 1.48 [详细解读 DNS 解析过程](http://python.iswbm.com/en/latest/c01/c01_48.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) @@ -154,6 +153,9 @@ - 9.2 [没有这 50 个APP,我的 Mac 将只是一块铁](http://python.iswbm.com/en/latest/c09/c09_02.html) - 9.3 [明哥的在线工具集](http://python.iswbm.com/en/latest/c09/c09_03.html) +## 第十章:网络基础 +- 10.1 [网络知识扫盲:一篇文章搞懂 DNS](http://python.iswbm.com/en/latest/c10/c10_01.html) + --- ![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_48.md b/source/c10/c10_01.md similarity index 99% rename from source/c01/c01_48.md rename to source/c10/c10_01.md index 111ad45..3ec17c4 100644 --- a/source/c01/c01_48.md +++ b/source/c10/c10_01.md @@ -1,4 +1,4 @@ -# 1.48 详细解读 DNS 解析过程 +# 10.1 网络知识扫盲:一篇文章搞懂 DNS ## 1. DNS 是什么? @@ -272,4 +272,4 @@ $ sudo dnsmasq restart -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_48.rst b/source/c10/c10_01.rst similarity index 99% rename from source/c01/c01_48.rst rename to source/c10/c10_01.rst index 62d0503..9e52d2d 100644 --- a/source/c01/c01_48.rst +++ b/source/c10/c10_01.rst @@ -1,5 +1,5 @@ -1.48 详细解读 DNS 解析过程 -========================== +10.1 网络知识扫盲:一篇文章搞懂 DNS +=================================== 1. DNS 是什么? --------------- diff --git a/source/chapters/p10.rst b/source/chapters/p10.rst new file mode 100755 index 0000000..9f0594a --- /dev/null +++ b/source/chapters/p10.rst @@ -0,0 +1,23 @@ +============================= +第十章:网络基础 +============================= + +在找工作面试的过程中,面试官非常喜欢考察基础知识,除了数据结构与算法之外,网络知识也是一个非常重要的考察对象。 + +而网络知识,通常是很抽象,不容易理解的,有很多同学就在这里裁了跟头。 + +正好以前没在这里分享过有关网络的内容,所以打算重新梳理有关网络的一些知识,这些内容在大家面试的时候可能能用得上。 + +本章节,会持续更新,敬请关注… + +---------------------------- + +.. toctree:: + :maxdepth: 1 + :glob: + + ../c10/* + +-------------- + +.. figure:: http://image.python-online.cn/image-20200320125724880.png From 3401700a8a6fb860ea1c268e3c07e046ba6367fd Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 1 Jun 2020 08:22:39 +0800 Subject: [PATCH 070/147] =?UTF-8?q?Update=EF=BC=9A=E6=95=B4=E7=90=86?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- source/{c01/c01_47.md => c10/c10_02.md} | 2 +- source/{c01/c01_47.rst => c10/c10_02.rst} | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) rename source/{c01/c01_47.md => c10/c10_02.md} (99%) rename source/{c01/c01_47.rst => c10/c10_02.rst} (99%) diff --git a/README.md b/README.md index 2145384..37a4fa1 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ - 1.43 [求你了,别再使用 pprint 打印字典了](http://python.iswbm.com/en/latest/c01/c01_43.html) - 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html) - 1.45 [Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_45.html) -- 1.47 [通俗理解OSI七层模型](http://python.iswbm.com/en/latest/c01/c01_47.html) ## 第二章:并发编程 - 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) @@ -155,6 +154,8 @@ ## 第十章:网络基础 - 10.1 [网络知识扫盲:一篇文章搞懂 DNS](http://python.iswbm.com/en/latest/c10/c10_01.html) +- 10.2 [通俗理解OSI七层模型](http://python.iswbm.com/en/latest/c10/c10_02.html) +- 10.3 [一篇文章理解 HTTP](http://python.iswbm.com/en/latest/c10/c10_03.html) --- diff --git a/source/c01/c01_47.md b/source/c10/c10_02.md similarity index 99% rename from source/c01/c01_47.md rename to source/c10/c10_02.md index dcd2550..5be5151 100644 --- a/source/c01/c01_47.md +++ b/source/c10/c10_02.md @@ -1,4 +1,4 @@ -# 1.47 通俗理解OSI七层模型 +# 10.2 通俗理解OSI七层模型 OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 diff --git a/source/c01/c01_47.rst b/source/c10/c10_02.rst similarity index 99% rename from source/c01/c01_47.rst rename to source/c10/c10_02.rst index ea512fe..a1b456d 100644 --- a/source/c01/c01_47.rst +++ b/source/c10/c10_02.rst @@ -1,4 +1,4 @@ -1.47 通俗理解OSI七层模型 +10.2 通俗理解OSI七层模型 ======================== OSI (Open System From e783765f6d02ffd16b6abe6ae378b756d86a22bc Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 1 Jun 2020 08:26:52 +0800 Subject: [PATCH 071/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=A0=87=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- source/c10/c10_02.md | 2 +- source/c10/c10_02.rst | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 37a4fa1..09c7213 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ ## 第十章:网络基础 - 10.1 [网络知识扫盲:一篇文章搞懂 DNS](http://python.iswbm.com/en/latest/c10/c10_01.html) -- 10.2 [通俗理解OSI七层模型](http://python.iswbm.com/en/latest/c10/c10_02.html) +- 10.2 [网络知识扫盲:如何理解 OSI七层模型](http://python.iswbm.com/en/latest/c10/c10_02.html) - 10.3 [一篇文章理解 HTTP](http://python.iswbm.com/en/latest/c10/c10_03.html) diff --git a/source/c10/c10_02.md b/source/c10/c10_02.md index 5be5151..13805cb 100644 --- a/source/c10/c10_02.md +++ b/source/c10/c10_02.md @@ -1,4 +1,4 @@ -# 10.2 通俗理解OSI七层模型 +# 10.2 网络知识扫盲:如何理解 OSI七层模型 OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 diff --git a/source/c10/c10_02.rst b/source/c10/c10_02.rst index a1b456d..841ba73 100644 --- a/source/c10/c10_02.rst +++ b/source/c10/c10_02.rst @@ -1,5 +1,5 @@ -10.2 通俗理解OSI七层模型 -======================== +10.2 网络知识扫盲:如何理解 OSI七层模型 +======================================= OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 From ccd040d053f91f88868a73add9adc3d296ecf079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 2 Jun 2020 13:53:01 +0800 Subject: [PATCH 072/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0<=E5=BF=AB=E9=80=9F=E5=88=9B=E5=BB=BA=E5=A4=A7?= =?UTF-8?q?=E6=96=87=E4=BB=B6>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_21.md | 122 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 source/c07/c07_21.md diff --git a/source/c07/c07_21.md b/source/c07/c07_21.md new file mode 100644 index 0000000..2ded6d3 --- /dev/null +++ b/source/c07/c07_21.md @@ -0,0 +1,122 @@ +# 7.21 如何快速创建超大文件? + +![](http://image.iswbm.com/20200602135014.png) + +常规的创建文件方式有: + +1. touch +2. vi(m) +3. tee +4. `>` 或 `>>` + +但是这几种都只适合创建小的文本文件,某些情况下出于测试的需要,你需要快速创建一个超大的文件,可能要 上百G。这时候要使用上面几个命令,你可能要等一天的时间,效率非常低。 + +接下来介绍几种我常用的方法 + +## 1. dd + +dd命令,可以从标准输入或指定的文件中读取数据,拷贝至新文件。 + +```shell +$ dd if=/dev/zero of=big_file count=10 bs=1G +``` + +使用 time 命令,可以算出创建一个 10G的文件需要耗时多久? + +```shell +$ time dd if=/dev/zero of=big_file count=10 bs=1G +10+0 records in +10+0 records out +10737418240 bytes (11 GB) copied, 7.93627 s, 1.4 GB/s + +real 0m7.941s +user 0m0.000s +sys 0m7.935s +``` + +花了将近8秒的时间 + +```shell +$ ls -lh big_file +-rw-r--r-- 1 root root 10G Jun 2 10:57 big_file +``` + + + +## 2. fallocate + +fallocate命令可以为文件预分配物理空间。`-l`后接空间大小,默认单位为字节。也可后跟k、m、g、t、p、e来指定单位,分别代表KB、MB、GB、TB、PB、EB。 + +```shell +$ fallocate -l 10G big_file +``` + +使用 time 命令,可以算出创建一个 10G的文件需要耗时多久? + +```shell +$ time fallocate -l 10G big_file +real 0m0.002s +user 0m0.000s +sys 0m0.001s +``` + +居然只有 0.001秒。 + +```shell +$ ls -lh big_file +-rw-r--r-- 1 root root 10G Jun 2 11:01 big_file +``` + +使用 du 命令,查看是否是真正创建了一个 10G大小的文件?从结果来看,是的。 + +```shell +$ du -sh big_file +10G big_file +``` + +## 3. truncate + +truncate命令可以将文件缩减或扩展为指定大小,使用`-s`参数设置大小。 + +```shell +$ truncate -s 10G big_file +``` + +使用 time 命令,可以算出创建一个 10G的文件需要耗时多久? + +```shell +$ time truncate -s 10G big_file +real 0m0.001s +user 0m0.000s +sys 0m0.001s +``` + +也是只有 0.001 秒。 + +不过与 fallocate 不同的是,allocate 创建的文件是真实大小,而 truncate 创建的并不是。 + +使用 ls 和 du 命令,查看的结果是不一样的。 + +```shell +$ ls -lh big_file +-rw-r--r-- 1 root root 10G Jun 2 11:11 big_file + +$ du -sh big_file +0 big_file +``` + +由此可见 truncate 的作用是指定一个文件的大小,如果该文件不存在,就会创建该文件。如果指定文件的大小小于原先的大小,会丢失内容。 + +这个命令指定的文件大小其实是虚拟的。只是显示出来的大小。如果你指定一个非常大的文件。其实服务器剩余空间并不会减少。 + +--- + +以上,是我常用的几种创建大文件的方法,可以根据不同的使用场景进行选择,我一般使用的是 fallocate,速度够快,也是也会真实地占用磁盘空间,符合真实的测试场景。 + + + +--- + + + +![](http://image.iswbm.com/20200602133847.png) \ No newline at end of file From 89b2d9d5fb6eee445695e0a2bae80ebb33088c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 2 Jun 2020 13:54:02 +0800 Subject: [PATCH 073/147] =?UTF-8?q?Update=EF=BC=9A=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=AC=94=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_18.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/source/c07/c07_18.md b/source/c07/c07_18.md index f832863..c13615f 100644 --- a/source/c07/c07_18.md +++ b/source/c07/c07_18.md @@ -17,44 +17,49 @@ **如果不用awk命令,也可以使用eval命令来达到相同的目的** ``` -[root@``local` `~]``# echo " A BC "`` ``A BC``[root@``local` `~]``# eval echo " A BC "``A BC +[root@local ~]# echo " A BC " + A BC +[root@local ~]# eval echo " A BC " +A BC ``` 或者 ``` -[root@linux ~]``# echo ' A BC ' | python -c "s=raw_input();print(s.strip())"``A BC +[root@linux ~]# echo ' A BC ' | python -c "s=raw_input();print(s.strip())" +A BC ``` 或者 ``` -[root@linux ~]``# s=`echo " A BC "```[root@linux ~]``# echo $s``A BC +[root@linux ~]# s=`echo " A BC "` +[root@linux ~]# echo $s +A BC ``` -或者 +或者(**最简单易记的方法**) ``` -[root@linux ~]``# echo ' A BC ' | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g'``A BC +[root@linux ~]# echo " A BC " | awk '$1=$1' +A BC ``` 或者 ``` -[root@linux ~]``# echo " A BC " | awk '$1=$1'``A BC +[root@linux ~]# echo " A BC " | sed -r 's/^[ \t]+(.*)[ \t]+$//g' +A BC ``` 或者 ``` -[root@linux ~]``# echo " A BC " | sed -r 's/^[ \t]+(.*)[ \t]+$//g'``A BC +[root@linux ~]# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1' +A BC ``` -或者 -``` -[root@linux ~]``# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1'``A BC -``` From f7a2bcfaee8c76a6853921db2d899ed8c80fd671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 5 Jun 2020 17:26:42 +0800 Subject: [PATCH 074/147] =?UTF-8?q?Update=EF=BC=9A=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E9=87=8D=E5=BB=BA=20rpmdb=20=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_12.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md index 2ffe069..708666a 100644 --- a/source/c07/c07_12.md +++ b/source/c07/c07_12.md @@ -205,6 +205,18 @@ rpm -ivh xxx.rpm-------安装软件   -qpl----------------查询一个尚未安装的安装包包含的信息 ``` +重建 rpmdb + +```shell +# 删除rpm数据文件 +rm -f /var/lib/rpm/__db.00* + +# 重建rpm数据文件 +rpm –rebuilddb +``` + + + ![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file From d691f43563ede7fb1538d2acee1e0d473ef7e47d Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Fri, 5 Jun 2020 21:06:46 +0800 Subject: [PATCH 075/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- source/c07/c07_12.rst | 10 + source/c07/c07_18.rst | 29 +- source/c07/c07_21.rst | 128 ++++++++ source/c10/c10_03.md | 569 ++++++++++++++++++++++++++++++++++ source/c10/c10_03.rst | 690 ++++++++++++++++++++++++++++++++++++++++++ source/c10/c10_05.md | 34 +++ source/c10/c10_05.rst | 33 ++ source/c10/c10_06.md | 37 +++ source/c10/c10_06.rst | 37 +++ 10 files changed, 1559 insertions(+), 14 deletions(-) create mode 100644 source/c07/c07_21.rst create mode 100644 source/c10/c10_03.md create mode 100644 source/c10/c10_03.rst create mode 100644 source/c10/c10_05.md create mode 100644 source/c10/c10_05.rst create mode 100644 source/c10/c10_06.md create mode 100644 source/c10/c10_06.rst diff --git a/README.md b/README.md index 09c7213..7abe11f 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ - 7.18 [Shell中去除字符串前后空格的方法](http://python.iswbm.com/en/latest/c07/c07_18.html) - 7.19 [Ansible 使用教程](http://python.iswbm.com/en/latest/c07/c07_19.html) - 7.20 [使用 Shell 删除文件的多种方法](http://python.iswbm.com/en/latest/c07/c07_20.html) +- 7.21 [如何快速创建超大文件?](http://python.iswbm.com/en/latest/c07/c07_21.html) ## 第八章:OpenStack - 8.1 [OpenStack 运维命令](http://python.iswbm.com/en/latest/c08/c08_01.html) @@ -155,7 +156,10 @@ ## 第十章:网络基础 - 10.1 [网络知识扫盲:一篇文章搞懂 DNS](http://python.iswbm.com/en/latest/c10/c10_01.html) - 10.2 [网络知识扫盲:如何理解 OSI七层模型](http://python.iswbm.com/en/latest/c10/c10_02.html) -- 10.3 [一篇文章理解 HTTP](http://python.iswbm.com/en/latest/c10/c10_03.html) +- 10.3 [网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质](http://python.iswbm.com/en/latest/c10/c10_03.html) +- 10.5 [Wireshark 抓包教程](http://python.iswbm.com/en/latest/c10/c10_05.html) +- 10.6 [通过比较,学习 TCP 与 UDP](http://python.iswbm.com/en/latest/c10/c10_06.html) +- 10.7 [网络知识扫盲:一篇文章理解 HTTP](http://python.iswbm.com/en/latest/c10/c10_07.html) --- diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index a2dce10..d37d5dd 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -196,6 +196,16 @@ rpm   -qpl----------------查询一个尚未安装的安装包包含的信息 +重建 rpmdb + +.. code:: shell + + # 删除rpm数据文件 + rm -f /var/lib/rpm/__db.00* + + # 重建rpm数据文件 + rpm –rebuilddb + .. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! diff --git a/source/c07/c07_18.rst b/source/c07/c07_18.rst index 512c97d..d90a701 100644 --- a/source/c07/c07_18.rst +++ b/source/c07/c07_18.rst @@ -19,43 +19,46 @@ :: - [root@``local` `~]``# echo " A BC "`` ``A BC``[root@``local` `~]``# eval echo " A BC "``A BC + [root@local ~]# echo " A BC " + A BC + [root@local ~]# eval echo " A BC " + A BC 或者 :: - [root@linux ~]``# echo ' A BC ' | python -c "s=raw_input();print(s.strip())"``A BC + [root@linux ~]# echo ' A BC ' | python -c "s=raw_input();print(s.strip())" + A BC 或者 :: - [root@linux ~]``# s=`echo " A BC "```[root@linux ~]``# echo $s``A BC + [root@linux ~]# s=`echo " A BC "` + [root@linux ~]# echo $s + A BC -或者 - -:: - - [root@linux ~]``# echo ' A BC ' | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g'``A BC - -或者 +或者(\ **最简单易记的方法**\ ) :: - [root@linux ~]``# echo " A BC " | awk '$1=$1'``A BC + [root@linux ~]# echo " A BC " | awk '$1=$1' + A BC 或者 :: - [root@linux ~]``# echo " A BC " | sed -r 's/^[ \t]+(.*)[ \t]+$//g'``A BC + [root@linux ~]# echo " A BC " | sed -r 's/^[ \t]+(.*)[ \t]+$//g' + A BC 或者 :: - [root@linux ~]``# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1'``A BC + [root@linux ~]# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1' + A BC .. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! diff --git a/source/c07/c07_21.rst b/source/c07/c07_21.rst new file mode 100644 index 0000000..b8189e8 --- /dev/null +++ b/source/c07/c07_21.rst @@ -0,0 +1,128 @@ +7.21 如何快速创建超大文件? +=========================== + +|image0| + +常规的创建文件方式有: + +1. touch +2. vi(m) +3. tee +4. ``>`` 或 ``>>`` + +但是这几种都只适合创建小的文本文件,某些情况下出于测试的需要,你需要快速创建一个超大的文件,可能要 +上百G。这时候要使用上面几个命令,你可能要等一天的时间,效率非常低。 + +接下来介绍几种我常用的方法 + +1. dd +----- + +dd命令,可以从标准输入或指定的文件中读取数据,拷贝至新文件。 + +.. code:: shell + + $ dd if=/dev/zero of=big_file count=10 bs=1G + +使用 time 命令,可以算出创建一个 10G的文件需要耗时多久? + +.. code:: shell + + $ time dd if=/dev/zero of=big_file count=10 bs=1G + 10+0 records in + 10+0 records out + 10737418240 bytes (11 GB) copied, 7.93627 s, 1.4 GB/s + + real 0m7.941s + user 0m0.000s + sys 0m7.935s + +花了将近8秒的时间 + +.. code:: shell + + $ ls -lh big_file + -rw-r--r-- 1 root root 10G Jun 2 10:57 big_file + +2. fallocate +------------ + +fallocate命令可以为文件预分配物理空间。\ ``-l``\ 后接空间大小,默认单位为字节。也可后跟k、m、g、t、p、e来指定单位,分别代表KB、MB、GB、TB、PB、EB。 + +.. code:: shell + + $ fallocate -l 10G big_file + +使用 time 命令,可以算出创建一个 10G的文件需要耗时多久? + +.. code:: shell + + $ time fallocate -l 10G big_file + real 0m0.002s + user 0m0.000s + sys 0m0.001s + +居然只有 0.001秒。 + +.. code:: shell + + $ ls -lh big_file + -rw-r--r-- 1 root root 10G Jun 2 11:01 big_file + +使用 du 命令,查看是否是真正创建了一个 10G大小的文件?从结果来看,是的。 + +.. code:: shell + + $ du -sh big_file + 10G big_file + +3. truncate +----------- + +truncate命令可以将文件缩减或扩展为指定大小,使用\ ``-s``\ 参数设置大小。 + +.. code:: shell + + $ truncate -s 10G big_file + +使用 time 命令,可以算出创建一个 10G的文件需要耗时多久? + +.. code:: shell + + $ time truncate -s 10G big_file + real 0m0.001s + user 0m0.000s + sys 0m0.001s + +也是只有 0.001 秒。 + +不过与 fallocate 不同的是,allocate 创建的文件是真实大小,而 truncate +创建的并不是。 + +使用 ls 和 du 命令,查看的结果是不一样的。 + +.. code:: shell + + $ ls -lh big_file + -rw-r--r-- 1 root root 10G Jun 2 11:11 big_file + + $ du -sh big_file + 0 big_file + +由此可见 truncate +的作用是指定一个文件的大小,如果该文件不存在,就会创建该文件。如果指定文件的大小小于原先的大小,会丢失内容。 + +这个命令指定的文件大小其实是虚拟的。只是显示出来的大小。如果你指定一个非常大的文件。其实服务器剩余空间并不会减少。 + +-------------- + +以上,是我常用的几种创建大文件的方法,可以根据不同的使用场景进行选择,我一般使用的是 +fallocate,速度够快,也是也会真实地占用磁盘空间,符合真实的测试场景。 + +-------------- + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200602133847.png + diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md new file mode 100644 index 0000000..222d00d --- /dev/null +++ b/source/c10/c10_03.md @@ -0,0 +1,569 @@ +# 10.3 网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质 + +## 1. TCP 协议是什么? + +TCP 是 Transmission Control Protocol 的缩写,意思是传输控制协议。一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。 + +从这个定义里,有很多初学就首先懵了。 + +什么是面向连接? + +什么是可靠的通信协议? + +什么是面向字节流的? + + + +为了让你对 TCP 有个初步的了解,我打算先从这个定义入手。 + +### 什么是面向连接? + +面向连接,是相对于另一个传输层协议 UDP 而言的(后面会单独介绍)。 + +TCP 是面向连接的,所以在开始传输数据前要先经历三次握手建立连接。 + +而 UDP 即刻就可以传输数据,并不需要先三次握手来建立连接。 + +一个更可靠,而一个更开放。 + +就好比,你去医院看病,如果是专家号,一般要提前预约,对只要预约(三次握手建立了连接)上了,你去了就不会看不上病。这是 TCP 。 + +而如果你没有预约,就直接跑过去,那不好意思,你只能看普通门诊,而普通门诊等的人很多,你就不一定能看得上病了。这是 UDP。 + +既然是连接,必然是一对一的,就像绳子的两端。所以 TCP 是一对一发送消息。 + +而 UDP 协议不需要连接,可以一对一,也可以一对多,也可以多对多发送消息。 + +### 什么是可靠的通信协议? + +可不可靠,也是相对于 UDP 而言的。 + +TCP 自身有三次握手和超时重传等机制,所以无论网络如何变化,主要不是主机宕机等原因都可以保证一个报文可以到达目标主机。 + +与之对比, UDP 就比较不负责任了,不管你收不收得到,反正我就无脑发,网络拥堵我也发,它的职责是发出去。 + +### 什么是面向字节流的? + +与面向字节流相对的是,UDP 的面向报文。 + +面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 + +面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。 + + +## 2. 完整解读 TCP 报文格式 + +搞懂一个通信协议,了解它的报文格式是必经之路。 + +TCP 的报文段结构,可以从下面这张图中非常清晰的看到。 + + + +![TCP 报文首部](http://image.iswbm.com/20200605203659.png) + +接下来,我会一个一个讲解这些字段的内容。 + +**源端口** 和 **目标端口**:各占 2 个字节。2 个字节,也就是 16个 bit,这应该也能说明为什么计算机端口的范围是 1-65535 (0 不使用,2^16=65536,最大位65536不使用)了吧?有了源端口和目标端口,加上 IP 首部里的源IP和目标IP,就可以唯一确定一个连接。 + +**序列号**:共占用 4个字节。说明序列号的范围是 [0, 2^32-1],也就是 [0, 4294967296]。当序号增加到 4294967296 后,下一个序号将回到0重新开始。在建立连接时由计算机生成的随机数作为其初始值(ISN,即Initial Sequence Number,初始序列号),通过 SYN 包传给接收端主机,每发送一次数据,就**累加**一次该「数据字节数」的大小(其中要注意的是 SYN 和 FIN 包的 seq 也要消耗一个序号)。**用来解决网络包乱序问题。** + +**确认号**:共占用 4个字节。说明确认号的范围是 [0, 2^32-1],也就是 [0, 4294967296]。它表示**期望**收到对方下一次数据的序列号(所以 ack 一般都是上次接收成功的数据字节序号加1),发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。**用来解决不丢包的问题**。TCP在接收到数据后 200ms 才会发送ACK包,这种设定是为了等待是否有数据可以一起发送的。 + +**数据偏移**:共占 4 个bit,它表示的是TCP报文的数据起始处距离TCP报文起始处的距离有多远。实际生活中我们说距离多远,我们的单位通常是米,而这里距离有多远,单位是 4 个字节(也就是 32bit)。由于 4 个bit,能表示的最大整数是 15,也就说明 TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15)。这意味着 TCP 的首部(除数据外的都叫首部)长度是 20-60 个字节。 + +**窗口**:共占 16 个bit,因此最大的窗口大小为 2^16-1 = 65535 = 64k。这是早期的设计,对于现在的网络应用,可能会不太够,因此可以在选项里加一个 **窗口扩大选项**,来传输更多的数据。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。 + +**保留**:占 6个bit,保留为今后使用,目前应置为0。 + +**紧急指针**:占16个bit。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) 。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,**即使窗口为0时也可以发送紧急数据**。 + +**标志位:** + +- **SYN**(SYNchronization): 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。 +- **ACK**(ACKnowledgment):仅当ACK = 1时确认号字段才有效,当ACK = 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1,如果你可以看下我后面 wireshark 抓的包里除了 最初建立连接的 SYN 包之外,其他的包也都有 ACK 标志。 +- **RST**(ReSet):当 RST=1 时,表示 TCP 连接中出现异常(如主机崩溃或其他原因)必须强制断开连接,然后再重新建立连接进行传输。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。 +- **FIN**(Finish):当FIN=1 时,表示今后不会再有数据发送,希望断开连接。 +- **PSH**(Push) 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。 +- **URG**(Urgent):当URG=1时,表明开户了urgent mode,紧急指针就开始生效了。 + +**选项**:长度可变,最长可达40个字节。当没有使用“选项”时,TCP的首部长度是20字节。 + +- MSS 选项:TCP报文段中的数据字段的最大长度,后面有详解。 +- 窗口扩大选项:占用三个字节,使得接收端可接收更多的数据,由 2^16-1 扩充到 2^(16+14)-1,其中这个14是窗口移位记数器的最大值。详情请参见:TCP/IP详解 卷1 协议 P262 +- 时间戳选项:共占 10 个字节,其中最主要的字段是时间戳字段(4字节)和时间戳回送回答字段(4字节)。 + + + +## 3. 如何模拟 TCP 连接? + +只搞懂报文格式,没有实战的话,就永远只停留在字面上,无法深刻地理解它。 + +所以接下来我会使用 wireshark 进行对三次握手、数据传输、四次挥手进行一次抓包并分析这个过程。 + +但是在开始之前 ,首先要学会模拟建立一个 tcp 连接,好能让我们轻松使用过滤器来显示结果。 + +为此我使用 Python 写了两个小脚本 + +**1、服务端** + +监听 13200 端口,如果有客户端连接就发送 hello 字符串 + +```python +# tcp_server.py + +import socket # 导入 socket 模块 +import time + +s = socket.socket() # 创建 socket 对象 +host = socket.gethostname() # 获取本地主机名 +port = 13200 # 设置端口 +s.bind((host, port)) # 绑定端口 + +s.listen(5) # 等待客户端连接 +while True: + c, addr = s.accept() # 建立客户端连接 + c.send('hello'.encode("utf-8")) + c.send('world'.encode("utf-8")) + time.sleep(1) + c.close() # 关闭连接 +``` + +运行后,可以使用 lsof 命令查看 13200 端口是否处于监听中 + +![](http://image.iswbm.com/image-20200601221524846.png) + +**2、客户端** + +连接 13200 端口,并接收并打印服务端发送的内容 + +```python +# tcp_client.py + +import socket # 导入 socket 模块 +import time + +s = socket.socket() # 创建 socket 对象 +host = socket.gethostname() # 获取本地主机名 +port = 13200 # 设置端口号 + +s.connect((host, port)) +print(s.recv(1024)) +time.sleep(2) +s.close() +``` + + + +## 4. Wireshark 抓包实战分析 + +一切准备就绪后,打开我们的 wireshark ,并设置捕获过滤器 port=13200 + +![](http://image.iswbm.com/image-20200601222110435.png) + +然后开启抓包,最后执行上面的 客户端代码` tcp_client.py`,就可以在 wireshark 上看到如下内容。 + +![](http://image.iswbm.com/image-20200602234904143.png) + + + +### 三次握手 + +三次握手的过程可以参考下面这张图来帮助理解 + +![](http://image.iswbm.com/20200605130951.png) + +使用 wireshark 抓到的三次握手的包如下所示 + +![wireshare 三次握手](http://image.iswbm.com/image-20200603003018160.png) + +客户端要连接上服务端,首先要发送一个 SYN 包表示请求连接。这个SYN 包的 seq 为0。这是第一次握手。 + +当服务端接收这个 SYN 包时,知道了有人要连接自己,就发了一个 ACK 包说: 你要连接这件事,我已经知道啦。但是连接是双方的事情,我也要连接客户端呀,因此 服务端实际上也会发送一个 SYN 包给客户端,请求连接。此时 ACK 和 SYN 如果分开发,服务端觉得太麻烦了,于是就把这两个包合并在一起发,所以实际上只发一个 SYN+ACK 的包。这一点说重要也不重要,说不重要也重要,因为面试的时候经常会问到,**为什么不是四次握手呢?**答案就在这里,**因为一个包可以解决的事情没必要发两个包**。**这是第二次握手。** + +当客户端接收到服务端发送的 SYN+ACK 包时,知道服务端同意了自己的请求,并且也要求连接自己,有来就有往,客户端连忙回了个 ACK 包表示同意。**这就是第三次握手。** + + + +### 数据传输 + +在上面的 Python 代码中,服务端会向客户端发送了两次数据: `hello` 和 `world` + +那么这个数据是在哪里发送的呢? + +仔细看 wireshark 抓到的包,有两个 PSH 的包,意思就是有数据传输的意思。 + +打开这两个包分析一下 + +首先是第一个包 + +![](http://image.iswbm.com/image-20200602235431620.png) + +然后是第二个包 + +这里需要你理解的有两点 + +**1、为什么这里的 seq 为6呢?** + +因为第一次的 seq 为1,len=5,一共发了5个字节,所以第二次发送,要从6开始计数啦。 + +**2、为什么第一次 ack 为1,而第二次ack还是1呢?** + +因为客户端没有向服务端发送数据,所以 ack 将始终为1,直到客户端要向服务端发送数据。 + +![](http://image.iswbm.com/image-20200602235723214.png) + +### 四次挥手 + +四次挥手的过程可以参考下面这张图来帮助理解 + +![](http://image.iswbm.com/20200605192855.png) + +使用 wireshark 抓到的四次挥手的包如下所示 + +![wireshark 四次挥手](http://image.iswbm.com/image-20200603001339731.png) + +在服务端发送完两次数据后,调用一次了 close 方法,发送了一个 FIN 包请求关闭连接,**这是第一次挥手**,这个 FIN 包里的 seq 为11,是两次发送的数据长度+1,很容易理解,ack 始终为 1,上面讲过了也好理解。 + +当客户端收到了服务端发来的 FIN 包后,知道了服务端要关闭连接了,于是就回了一个 ACK 的应答包(**这是第二次挥手**),告诉服务端:恩,我知道了。但由于客户端这边还有一些事情要做(可能是还有数据要发送之类的,在 Python 代码里我通过 time.sleep 来模拟),所以要晚点才能关闭连接。这里的 ACK 包,seq 号 是取第一次挥手的 ack 号,而 ack 号是取 第一次挥手的 seq +1. + +等客户端事情也做完了(time.sleep 结束),也会主动发送一个 FIN 包(代码里是通过调用 close 方法实现)告诉服务端:我这边也结束了,可以关闭连接啦。这是第三次挥手。这个 FIN 包里的 seq 号还是取第一次挥手的 ack 号,而 ack 号也是取 第一次挥手的 seq +1,这和第二次挥手时是一样的。 + +既然是一样的,那为什么不一起发送呢? + +这个问题很好。当服务端数据都发送完了要关闭连接,而客户端自己也没什么事情 要做了也要关闭连接,确实是可以一起发送。这时候就四次挥手就变成了三次挥手,所以挥手并不总是四次的。 + +上面解析了三次挥手,还差最后一次。 + +最后一次挥手,就是服务端接收到客户端的 FIN 包后,知道了客户端要关闭连接了,就回了一个 ACK 应答包。此时的 seq 为第三次挥手的 ack,而 ack 为 第三次挥手的 seq +1。 + +至此,四次挥手全部完成。 + +## 5. 拷问灵魂的四个问题 + +### 问题1:为什么要三次握手? + +在建立连接前要经历三次握手,几乎是人尽皆知的事情。 + +但是为什么需要三次握手,这是一个值得思考的问题。 + +在大多数的文章里面,讲到三次握手都会用形象的比喻来跟你解释,比如和女朋友打电话的场景。 + +```shell +她:“你可以听到了吗?” +我:“可以呀,你呢,你可以听到我的吗?” +她:“我也可以听到了。” # 确认对应可以听到了再对话 +我:“你吃饭了吗?“ +她:“吃啦。“ +``` + +从这个例子里,可以提炼出一点,就是三次握手就是在确保连接的双方都能发送且接收到对方的消息。 + +这个例子是好的,但是只讲这个例子又是不够的。 + +这会让读者对三次握手停留在表层,导致无法真正去学习 TCP 的精髓之处。 + +接下来,我会说说我对 TCP 的理解。 + +关于 为什么需要握手(注意:这里还没开始讨论为什么要三次握手),我认为应该有两个理由: + +1. 同步起始序列号,为后续数据传输做准备 +2. 保证双方都可能发送数据且能接收数据 + +关于第一点,其实两次握手就可以,客户端把自己的 seq 通过 SYN 包告诉服务端,而服务端把自己的 seq 通过 SYN+ACK 包告诉客户端。 + +而第二点呢,必须要三次握手才能保证,这个大家应该能够理解,不再赘述。 + + + +**除此之外,在网络上,你会经常看到还有第三个理由** + +他们的论据是在 RFC 793 中可以找出下面这句话 + +> The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion. + +翻译一下,就是三次握手的最主要原因是为了防止旧的重复连接初始化造成混乱。 + +怎么理解这句话呢?举个例子吧 + +由于网络环境是错综复杂的,当我们发送了一个SYN包 a 后,很有可能过了很久还没有到达目标机器,此时,客户端会重新发送一个 SYN 包 b重新请求连接。 + +![](http://image.iswbm.com/20200605200027.png) + +b 包比 a 包先到达了目标机器(即使a包是先发的),当目标机器收到了 b 包,就会回复给源机器一个回包,当后面 a 包也到达了目标机器后,对于目标机器来说,虽然a 和 b 是来源于同一机器 同一端口,但是它才不管是不是重复连接,因为对于目标机器来说,只要来请求连接我都欢迎,收一个我回一个,至于哪个才是最新的连接,哪个是重复的?它不管,它把这个职责交还给了客户端,毕竟哪个包才是最新的,它最清楚了。 + +那问题就来了,源机器是如何决定 a 包过期的呢? + +源机器 收到了来自目标机器 对 a 包的 ACK 回应后,通过自身的上下文信息,知道了这是一个历史连接(序列号过期或超时),那么客户端就会发送 `RST` 报文给服务端,表示中止这一次连接。 + +由此,我们可以看到,三次握手可以解决这个重复连接的问题。 + +这里请注意,我说的是 **可以解决**,而不是说 **因此我们需要三次握手**。 + +没有第三次握手会有多个重复连接导致浪费资源,是建立在三次请求才会建立连接的基础上才会出现的问题,这不是设计三次请求的原因。只是三次握手刚好也解决了这个问题,这个逻辑要搞清楚。 + +### 问题2:为什么不是握手两次? + +这个问题可以转换成『只握手两次就建立连接会出现什么样的问题?』 + +还是用给女朋友打电话这个例子,男朋友如果没有跟女朋友确认对方是否可以听到自己的话,就自己一直在说说说,最后只能尴尬收场。这就是我们所说的不可靠的连接,只是单向,而不是双向。 + +```shell +她:“你可以听到了吗?” +我:“可以呀” # 没有向对方确认是否可以听到自己就开始一直说说说 +我:“你吃饭了吗?“ +我:“人呢?“ +我:“喂?“ +我:“去哪啦?“ +``` + +在实际应用上,其实只握手两次还会出现更严重的问题,那就是资源浪费。 + +还是上面那个例子,a 包由于网络拥堵,迟迟没有发到目标机器 ,由于超时源机器会重新发送一个 SYN 包 b,如果只进行了两次握手,目标机器就建立了连接,那么当 b 包到达后,目标机器又会创建一个连接,而这个连接是无用的、多余的。 + +![](http://image.iswbm.com/20200605201138.png) + +这里仅仅假设只超时重发一次就成功了,如果超时重发了 10 次,甚至更多呢?本来TCP 传输只需要一个连接就行了,现在服务端却创建了 n 个 连接,对于服务器资源来说无疑是非常浪费的。 + +### 问题3:为什么不是握手四次? + +看到这里,你应该很清楚 三次握手的流程了。 + +那么握手四次是什么样的呢? + +还是以给女朋友打电话的例子来说明 + +```shell +她:“你可以听到了吗?” +我:“可以呀!” +我:“你呢,你可以听到我的吗?” +她:“我也可以听到了。” +``` + +和三次握手相对比,其实就是把原来第二次握手的内容拆分成两次发送。 + +![](http://image.iswbm.com/20200605202450.png) + +所以为什么不握手四次? + +因为三次握手就可以完成的事,为什么要四次握手呢?没必要。 + +### 问题4:为什么不握手五次或更多? + +这个问题有点迷,你可能还不太清楚,还是以跟女朋友打电话为例 + +```shell +她:“你可以听到了吗?” +我:“可以呀,你呢,你可以听到我的吗?” +她:“恩,我也可以听到了。你呢,现在还可以听到吗?” +我:“可以呀,现在你那边还听到我的吗?” +她:“是的,可以,你呢,可以听到我现在说的吗” +我:“可以听到,那你呢?” +... +... +``` + +在每一次跟确认可以听到对方的声音时,还生怕这个消息对方收不到这个消息,所以两个人就一直在确认,跟个zz一样。 + +所以你问我,为什么不握手五次或更多? + +因为三次是基本保障,再多一个,就是多余,容易死循环。 + + + +## 6. MTU 和 MSS 是什么? + +### MTU + +Maximum Transmission Unit,最大传输单元。 + +在TCP/IP协议族中,指的是**IP数据报**能经过一个**物理网络**的**最大报文长度**,其中包括了IP首部(从20个字节到60个字节不等)。 + +由此我们知道,MTU 为多大跟链路层的介质有关,我们接触最多的以太网的 MTU 设为1500字节。 + +其他的你可以参考 下面这张图(摘自维基百科) + +![](http://image.iswbm.com/image-20200604204657243.png) + +如果上层协议(如 TCP)交给IP协议的内容实在是太多,使得 IP 报文的大小超过了 MTU ,以以太网为例,如果 IP 报文大小超过了1500 Bytes ,那么**IP报文就必须要分片传输**,到达目的主机或目的路由器之后由其进行重组分片。 + +IP分片发生在IP层,不仅源端主机会进行分片,中间的路由器也有可能分片,因为不同的网络的MTU是不一样的,如果传输路径上的某个网络的MTU比源端网络的MTU要小,路由器就可能对IP数据报再次进行分片。而分片数据的重组只会发生在目的端的IP层。 + +### MSS + +Maximum Segment Size ,它表示的是 TCP 报文段中的数据字段的最大长度。 + +数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是整个TCP报文段的最大长度,而是“TCP报文段长度减去TCP首部长度”。 + +MSS 和 MTU 的关系是: + +MSS = MTU - IP首部大小 - TCP首部大小 + +![](http://image.iswbm.com/tcp_pdus.png) + + + +**那为什么要规定一个最大报文长度MSS呢?** + +这并不是考虑接受方的接收缓存可能存放不下TCP报文段中的数据。实际上,MSS与接收窗口值没有关系。我们知道,TCP报文段的数据部分,至少要加上40字节的首部(TCP首部20字节和IP首部20字节,这里还没有考虑首部中的可选部分)才能组装成一个IP数据报。 + +若选择较小的MSS长度,网络的利用率就降低。设想在极端情况下,当TCP报文段只含有1字节的数据时,在IP层传输的数据报的开销至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,对网络的利用率就不会超过1/41。到了数据链路层还要加上一些开销。 + +但反过来,若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片组成成原来的TCP报文段,当传输出错时还要进行重传。 + +IP层是没有超时重传机制的,如果IP层对一个数据包进行了分片,只要有一个分片丢失了,只能依赖于传输层进行重传,结果是所有的分片都要重传一遍,这个代价有点大。 + +因此,MSS应尽可能大些,只要在IP层传输时不需要分片就行。由于IP数据报所经历的路径是动态变化的,因此在这条路径上确定的不需要的分片的MSS,如果改走另一条路径就可能需要进行分片。**因此最佳的MSS是很难确定的**。 + +在连接过程中,双方都把自己能够支持的MSS写入这一字段,以后就按照这个数值传输数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节长。因此,所有在互联网上的主机都应该接受的报文段长度是536+20(固定首部长度)=556字节。 + + + +## 7. 网络编程的常规步骤 + +上面为了方便抓包,我使用了 Python 写了一个服务器和客户端程序进行通信。 + +这里有必要说一下,面向 TCP 进行网络编程的常规步骤 + +![](http://image.iswbm.com/20200605204727.png) + +如果是服务端: + +1. 用函数socket() 创建一个socket; + +2. 用函数setsockopt() 设置socket属性; **可选步骤** + +3. 用函数bind() 绑定IP地址、端口等信息到socket上; + +4. 用函数listen() 开启监听; + +5. 用函数accept() 接收客户端上来的连接; + +6. 用函数send()和recv() 或者 read()和write() 收发数据; + +7. 关闭网络连接; + +8. 关闭监听; + +而如果是客户端: + +1. 用函数socket() 创建一个socket; + +2. 用函数setsockopt() 设置socket属性 ;**可选步骤** + +3. 用函数bind() 绑定IP地址、端口等信息到socket上; **可选步骤** +4. 用函数connect() 对方的IP地址和端口连接服务器 ; +5. 用函数send()和recv() 或者 read()和write() 收发数据; +6. 关闭网络连接; + +其中最主要、最关键的有三个函数: + +### connect() + +它是一个阻塞函数,通过 TCP 三次握手与服务器建立连接。 + +一般的情况下 客户端的connect函数 默认是阻塞行为 直到三次握手阶段成功为止。 + +### listen() + +不是一个阻塞函数: 它会将套接字 和 套接字对应队列的长度告诉Linux内核 + + 他是被动连接的 一直监听来自不同客户端的请求 listen函数只要 作用将socketfd 变成被动的连接监听socket 其中参数backlog作用 设置内核中队列的长度 。 + +### accpet() + +是一个阻塞函数,它会从处于 established 状态的队列中取出完成的连接。 + +当队列中没有完成连接时候就会阻塞,直到取出队列中已完成连接的用户连接为止。 + +那如果服务器没有及时调用 accept 函数取走完成连接的队列怎么办呢? + +服务器的连接队列满掉后,服务器不会对再对建立新连接的 SYN 进行应答,所以客户端的 connect 就会返回 ETIMEDOUT。 + +## 8. 注意事项 + +### ack 和 ACK 有区别吗? + +上面的分析三次握手和四次挥手时,有一个细节问题,可能不是那么重要,但是需要你搞清楚。 + +就是 ack 和 ACK 是否一致?答案是否定的 + +如果是 大写的 ACK ,表示的是标志位里的 flag,除了最初建立连接时的 SYN 包之外,后续的所有包此位都会被置为 1。 + +如果是 小写的 ack,表示的是希望确认号,表示的是希望接收到对方下一次数据的序列号, ack 一般都是上次接收成功的数据字节序号加1。 + +### TCP 包最多可传输多少数据? + +对于TCP协议来说,整个包的最大长度是由最大传输大小(MSS,Maxitum Segment Size)决定,MSS就是TCP数据包每次能够传输的最大数据分段。 + +为了达到最佳的传输效能 TCP协议在建立连接的时候通常要协商双方的MSS值。 + +通讯双方会根据双方提供的 MSS值的较小值来确定为这次连接的 MSS值。 + +在以太网中,MTU 为 1500 Bytes,减去IP数据包包头的大小20Bytes 和 TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 + + + +## 9. 异常情况分析 + +### 试图与一个不存在的端口建立连接(主机正常) + +这里的不存在的端口是指在服务器端没有程序监听在该端口。我们的客户端就调用connect,试图与其建立连接。这时会发生什么呢? + +这种情况下我们在客户端通常会收到如下异常内容: + +```python +Traceback (most recent call last): + File "/Users/MING/Code/Python/tcp_client.py", line 8, in + s.connect((host, port)) +ConnectionRefusedError: [Errno 61] Connection refused +``` + +试想一下,服务端本来就没有程序监听在这个接口,因此在服务端是无法完成连接的建立过程的。我们参考三次握手的流程可以知道当客户端的SYNC包到达服务端时,TCP协议没有找到监听的套接字,就会向客户端发送一个错误的报文,告诉客户端产生了错误。而该错误报文就是一个包含RST的报文。这种异常情况也很容易模拟,我们只需要写一个小程序,连接服务器上没有监听的端口即可。如下是通过wireshark捕获的数据包,可以看到红色部分的RST报文。 + +![](http://image.iswbm.com/image-20200604223625787.png) + + + +### 试图与一个某端口建立连接但该主机已经宕机(主机宕机) + +这也是一种比较常见的情况,当某台服务器主机宕机了,而客户端并不知道,因此会重复发送SYNC数据包. + +如下图所示,可以看到客户端每隔一段时间就会向服务端发送一个SYNC数据包。这里面具体的时间是跟TCP协议相关的,具体时间不同的操作系统实现可能稍有不同。 + +![](http://image.iswbm.com/image-20200604224127512.png) + + + +### 建立连接时,服务器应用被阻塞(或者僵死) + +还有一种异常情况是,客户端建立连接的过程中服务端应用处于僵死状态,这种情况在实际中也会经常出现(我们假设仅仅应用程序僵死,而内核没有僵死)。 + +对于TCP的服务端来说,当它收到SYN数据包时,就会创建一个套接字的数据结构并给客户端回复ACK,再次收到客户端的ACK时会将套接字数据结构的状态转换为ESTABLISHED,并将其加入就绪队列。 + +当上面的套接字处于就绪队列时,accept函数才被唤醒了,可以从套接字中读取数据。 + +在 accept 返回之前,客户端也是可以发送数据的,因为数据的发送与接收都是在内核态进行的。客户端发送数据后,服务端的网卡会先接收,然后通过中断通知IP层,再上传到TCP层。TCP层根据目的端口和地址将数据存入关联的缓冲区。 + +到此,可以得出几点结论。 + +1. 在 accept 返回之前,三次握手已经完成。 +2. TCP的客户端是否可以发送数据与服务端程序是否工作没有关系。 + +但是如果内核也处于僵死状态,那情况可就完全不一样了。 + +此时由于机器完全卡死,TCP服务端无法接受任何消息,自然也无法给客户端发送任何应答报文,也不会有后续发送数据的环节了。 + + + +## 10. 参考文章 + +[TCP报文段的首部格式](https://blog.csdn.net/qq_32998153/article/details/79680704) + +[TCP 、UDP、IP包的最大长度](https://www.cnblogs.com/jiangzhaowei/p/9273854.html) + +[理解了这些异常现象才敢说真正懂了TCP协议](https://network.51cto.com/art/201905/596543.htm) + +[近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题](https://mp.weixin.qq.com/s/tH8RFmjrveOmgLvk9hmrkw) + + + diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst new file mode 100644 index 0000000..36a03aa --- /dev/null +++ b/source/c10/c10_03.rst @@ -0,0 +1,690 @@ +10.3 网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质 +======================================================= + +1. TCP 协议是什么? +------------------- + +TCP 是 Transmission Control Protocol +的缩写,意思是传输控制协议。一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC +793 定义。 + +从这个定义里,有很多初学就首先懵了。 + +什么是面向连接? + +什么是可靠的通信协议? + +什么是面向字节流的? + +为了让你对 TCP 有个初步的了解,我打算先从这个定义入手。 + +什么是面向连接? +~~~~~~~~~~~~~~~~ + +面向连接,是相对于另一个传输层协议 UDP 而言的(后面会单独介绍)。 + +TCP 是面向连接的,所以在开始传输数据前要先经历三次握手建立连接。 + +而 UDP 即刻就可以传输数据,并不需要先三次握手来建立连接。 + +一个更可靠,而一个更开放。 + +就好比,你去医院看病,如果是专家号,一般要提前预约,对只要预约(三次握手建立了连接)上了,你去了就不会看不上病。这是 +TCP 。 + +而如果你没有预约,就直接跑过去,那不好意思,你只能看普通门诊,而普通门诊等的人很多,你就不一定能看得上病了。这是 +UDP。 + +既然是连接,必然是一对一的,就像绳子的两端。所以 TCP 是一对一发送消息。 + +而 UDP 协议不需要连接,可以一对一,也可以一对多,也可以多对多发送消息。 + +什么是可靠的通信协议? +~~~~~~~~~~~~~~~~~~~~~~ + +可不可靠,也是相对于 UDP 而言的。 + +TCP +自身有三次握手和超时重传等机制,所以无论网络如何变化,主要不是主机宕机等原因都可以保证一个报文可以到达目标主机。 + +与之对比, UDP +就比较不负责任了,不管你收不收得到,反正我就无脑发,网络拥堵我也发,它的职责是发出去。 + +什么是面向字节流的? +~~~~~~~~~~~~~~~~~~~~ + +与面向字节流相对的是,UDP 的面向报文。 + +面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 + +面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。 + +2. 完整解读 TCP 报文格式 +------------------------ + +搞懂一个通信协议,了解它的报文格式是必经之路。 + +TCP 的报文段结构,可以从下面这张图中非常清晰的看到。 + +.. figure:: http://image.iswbm.com/20200605203659.png + :alt: TCP 报文首部 + + TCP 报文首部 + +接下来,我会一个一个讲解这些字段的内容。 + +**源端口** 和 **目标端口**\ :各占 2 个字节。2 个字节,也就是 16个 +bit,这应该也能说明为什么计算机端口的范围是 1-65535 (0 +不使用,2^16=65536,最大位65536不使用)了吧?有了源端口和目标端口,加上 +IP 首部里的源IP和目标IP,就可以唯一确定一个连接。 + +**序列号**\ :共占用 4个字节。说明序列号的范围是 [0, 2^32-1],也就是 [0, +4294967296]。当序号增加到 4294967296 +后,下一个序号将回到0重新开始。在建立连接时由计算机生成的随机数作为其初始值(ISN,即Initial +Sequence Number,初始序列号),通过 SYN +包传给接收端主机,每发送一次数据,就\ **累加**\ 一次该「数据字节数」的大小(其中要注意的是 +SYN 和 FIN 包的 seq 也要消耗一个序号)。\ **用来解决网络包乱序问题。** + +**确认号**\ :共占用 4个字节。说明确认号的范围是 [0, 2^32-1],也就是 [0, +4294967296]。它表示\ **期望**\ 收到对方下一次数据的序列号(所以 ack +一般都是上次接收成功的数据字节序号加1),发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。\ **用来解决不丢包的问题**\ 。TCP在接收到数据后 +200ms 才会发送ACK包,这种设定是为了等待是否有数据可以一起发送的。 + +**数据偏移**\ :共占 4 +个bit,它表示的是TCP报文的数据起始处距离TCP报文起始处的距离有多远。实际生活中我们说距离多远,我们的单位通常是米,而这里距离有多远,单位是 +4 个字节(也就是 32bit)。由于 4 个bit,能表示的最大整数是 15,也就说明 +TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15)。这意味着 TCP +的首部(除数据外的都叫首部)长度是 20-60 个字节。 + +**窗口**\ :共占 16 个bit,因此最大的窗口大小为 2^16-1 = 65535 = +64k。这是早期的设计,对于现在的网络应用,可能会不太够,因此可以在选项里加一个 +**窗口扩大选项**\ ,来传输更多的数据。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。 + +**保留**\ :占 6个bit,保留为今后使用,目前应置为0。 + +**紧急指针**\ :占16个bit。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) +。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,\ **即使窗口为0时也可以发送紧急数据**\ 。 + +**标志位:** + +- **SYN**\ (SYNchronization): + 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。 +- **ACK**\ (ACKnowledgment):仅当ACK = 1时确认号字段才有效,当ACK = + 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1,如果你可以看下我后面 + wireshark 抓的包里除了 最初建立连接的 SYN 包之外,其他的包也都有 ACK + 标志。 +- **RST**\ (ReSet):当 RST=1 时,表示 TCP + 连接中出现异常(如主机崩溃或其他原因)必须强制断开连接,然后再重新建立连接进行传输。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。 +- **FIN**\ (Finish):当FIN=1 + 时,表示今后不会再有数据发送,希望断开连接。 +- **PSH**\ (Push) + 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。 +- **URG**\ (Urgent):当URG=1时,表明开户了urgent + mode,紧急指针就开始生效了。 + +**选项**\ :长度可变,最长可达40个字节。当没有使用“选项”时,TCP的首部长度是20字节。 + +- MSS 选项:TCP报文段中的数据字段的最大长度,后面有详解。 +- 窗口扩大选项:占用三个字节,使得接收端可接收更多的数据,由 2^16-1 + 扩充到 + 2^(16+14)-1,其中这个14是窗口移位记数器的最大值。详情请参见:TCP/IP详解 + 卷1 协议 P262 +- 时间戳选项:共占 10 + 个字节,其中最主要的字段是时间戳字段(4字节)和时间戳回送回答字段(4字节)。 + +3. 如何模拟 TCP 连接? +---------------------- + +只搞懂报文格式,没有实战的话,就永远只停留在字面上,无法深刻地理解它。 + +所以接下来我会使用 wireshark +进行对三次握手、数据传输、四次挥手进行一次抓包并分析这个过程。 + +但是在开始之前 ,首先要学会模拟建立一个 tcp +连接,好能让我们轻松使用过滤器来显示结果。 + +为此我使用 Python 写了两个小脚本 + +**1、服务端** + +监听 13200 端口,如果有客户端连接就发送 hello 字符串 + +.. code:: python + + # tcp_server.py + + import socket # 导入 socket 模块 + import time + + s = socket.socket() # 创建 socket 对象 + host = socket.gethostname() # 获取本地主机名 + port = 13200 # 设置端口 + s.bind((host, port)) # 绑定端口 + + s.listen(5) # 等待客户端连接 + while True: + c, addr = s.accept() # 建立客户端连接 + c.send('hello'.encode("utf-8")) + c.send('world'.encode("utf-8")) + time.sleep(1) + c.close() # 关闭连接 + +运行后,可以使用 lsof 命令查看 13200 端口是否处于监听中 + +|image0| + +**2、客户端** + +连接 13200 端口,并接收并打印服务端发送的内容 + +.. code:: python + + # tcp_client.py + + import socket # 导入 socket 模块 + import time + + s = socket.socket() # 创建 socket 对象 + host = socket.gethostname() # 获取本地主机名 + port = 13200 # 设置端口号 + + s.connect((host, port)) + print(s.recv(1024)) + time.sleep(2) + s.close() + +4. Wireshark 抓包实战分析 +------------------------- + +一切准备就绪后,打开我们的 wireshark ,并设置捕获过滤器 port=13200 + +|image1| + +然后开启抓包,最后执行上面的 客户端代码\ ``tcp_client.py``\ ,就可以在 +wireshark 上看到如下内容。 + +|image2| + +三次握手 +~~~~~~~~ + +三次握手的过程可以参考下面这张图来帮助理解 + +|image3| + +使用 wireshark 抓到的三次握手的包如下所示 + +.. figure:: http://image.iswbm.com/image-20200603003018160.png + :alt: wireshare 三次握手 + + wireshare 三次握手 + +客户端要连接上服务端,首先要发送一个 SYN 包表示请求连接。这个SYN 包的 +seq 为0。这是第一次握手。 + +当服务端接收这个 SYN 包时,知道了有人要连接自己,就发了一个 ACK 包说: +你要连接这件事,我已经知道啦。但是连接是双方的事情,我也要连接客户端呀,因此 +服务端实际上也会发送一个 SYN 包给客户端,请求连接。此时 ACK 和 SYN +如果分开发,服务端觉得太麻烦了,于是就把这两个包合并在一起发,所以实际上只发一个 +SYN+ACK +的包。这一点说重要也不重要,说不重要也重要,因为面试的时候经常会问到,\ **为什么不是四次握手呢?**\ 答案就在这里,\ **因为一个包可以解决的事情没必要发两个包**\ 。\ **这是第二次握手。** + +当客户端接收到服务端发送的 SYN+ACK +包时,知道服务端同意了自己的请求,并且也要求连接自己,有来就有往,客户端连忙回了个 +ACK 包表示同意。\ **这就是第三次握手。** + +数据传输 +~~~~~~~~ + +在上面的 Python 代码中,服务端会向客户端发送了两次数据: ``hello`` 和 +``world`` + +那么这个数据是在哪里发送的呢? + +仔细看 wireshark 抓到的包,有两个 PSH 的包,意思就是有数据传输的意思。 + +打开这两个包分析一下 + +首先是第一个包 + +|image4| + +然后是第二个包 + +这里需要你理解的有两点 + +**1、为什么这里的 seq 为6呢?** + +因为第一次的 seq +为1,len=5,一共发了5个字节,所以第二次发送,要从6开始计数啦。 + +**2、为什么第一次 ack 为1,而第二次ack还是1呢?** + +因为客户端没有向服务端发送数据,所以 ack +将始终为1,直到客户端要向服务端发送数据。 + +|image5| + +四次挥手 +~~~~~~~~ + +四次挥手的过程可以参考下面这张图来帮助理解 + +|image6| + +使用 wireshark 抓到的四次挥手的包如下所示 + +.. figure:: http://image.iswbm.com/image-20200603001339731.png + :alt: wireshark 四次挥手 + + wireshark 四次挥手 + +在服务端发送完两次数据后,调用一次了 close 方法,发送了一个 FIN +包请求关闭连接,\ **这是第一次挥手**\ ,这个 FIN 包里的 seq +为11,是两次发送的数据长度+1,很容易理解,ack 始终为 +1,上面讲过了也好理解。 + +当客户端收到了服务端发来的 FIN +包后,知道了服务端要关闭连接了,于是就回了一个 ACK +的应答包(\ **这是第二次挥手**\ ),告诉服务端:恩,我知道了。但由于客户端这边还有一些事情要做(可能是还有数据要发送之类的,在 +Python 代码里我通过 time.sleep 来模拟),所以要晚点才能关闭连接。这里的 +ACK 包,seq 号 是取第一次挥手的 ack 号,而 ack 号是取 第一次挥手的 seq ++1. + +等客户端事情也做完了(time.sleep 结束),也会主动发送一个 FIN +包(代码里是通过调用 close +方法实现)告诉服务端:我这边也结束了,可以关闭连接啦。这是第三次挥手。这个 +FIN 包里的 seq 号还是取第一次挥手的 ack 号,而 ack 号也是取 第一次挥手的 +seq +1,这和第二次挥手时是一样的。 + +既然是一样的,那为什么不一起发送呢? + +这个问题很好。当服务端数据都发送完了要关闭连接,而客户端自己也没什么事情 +要做了也要关闭连接,确实是可以一起发送。这时候就四次挥手就变成了三次挥手,所以挥手并不总是四次的。 + +上面解析了三次挥手,还差最后一次。 + +最后一次挥手,就是服务端接收到客户端的 FIN +包后,知道了客户端要关闭连接了,就回了一个 ACK 应答包。此时的 seq +为第三次挥手的 ack,而 ack 为 第三次挥手的 seq +1。 + +至此,四次挥手全部完成。 + +5. 拷问灵魂的四个问题 +--------------------- + +问题1:为什么要三次握手? +~~~~~~~~~~~~~~~~~~~~~~~~~ + +在建立连接前要经历三次握手,几乎是人尽皆知的事情。 + +但是为什么需要三次握手,这是一个值得思考的问题。 + +在大多数的文章里面,讲到三次握手都会用形象的比喻来跟你解释,比如和女朋友打电话的场景。 + +.. code:: shell + + 她:“你可以听到了吗?” + 我:“可以呀,你呢,你可以听到我的吗?” + 她:“我也可以听到了。” # 确认对应可以听到了再对话 + 我:“你吃饭了吗?“ + 她:“吃啦。“ + +从这个例子里,可以提炼出一点,就是三次握手就是在确保连接的双方都能发送且接收到对方的消息。 + +这个例子是好的,但是只讲这个例子又是不够的。 + +这会让读者对三次握手停留在表层,导致无法真正去学习 TCP 的精髓之处。 + +接下来,我会说说我对 TCP 的理解。 + +关于 +为什么需要握手(注意:这里还没开始讨论为什么要三次握手),我认为应该有两个理由: + +1. 同步起始序列号,为后续数据传输做准备 +2. 保证双方都可能发送数据且能接收数据 + +关于第一点,其实两次握手就可以,客户端把自己的 seq 通过 SYN +包告诉服务端,而服务端把自己的 seq 通过 SYN+ACK 包告诉客户端。 + +而第二点呢,必须要三次握手才能保证,这个大家应该能够理解,不再赘述。 + +**除此之外,在网络上,你会经常看到还有第三个理由** + +他们的论据是在 RFC 793 中可以找出下面这句话 + + The principle reason for the three-way handshake is to prevent old + duplicate connection initiations from causing confusion. + +翻译一下,就是三次握手的最主要原因是为了防止旧的重复连接初始化造成混乱。 + +怎么理解这句话呢?举个例子吧 + +由于网络环境是错综复杂的,当我们发送了一个SYN包 a +后,很有可能过了很久还没有到达目标机器,此时,客户端会重新发送一个 SYN +包 b重新请求连接。 + +|image7| + +b 包比 a 包先到达了目标机器(即使a包是先发的),当目标机器收到了 b +包,就会回复给源机器一个回包,当后面 a +包也到达了目标机器后,对于目标机器来说,虽然a 和 b 是来源于同一机器 +同一端口,但是它才不管是不是重复连接,因为对于目标机器来说,只要来请求连接我都欢迎,收一个我回一个,至于哪个才是最新的连接,哪个是重复的?它不管,它把这个职责交还给了客户端,毕竟哪个包才是最新的,它最清楚了。 + +那问题就来了,源机器是如何决定 a 包过期的呢? + +源机器 收到了来自目标机器 对 a 包的 ACK +回应后,通过自身的上下文信息,知道了这是一个历史连接(序列号过期或超时),那么客户端就会发送 +``RST`` 报文给服务端,表示中止这一次连接。 + +由此,我们可以看到,三次握手可以解决这个重复连接的问题。 + +这里请注意,我说的是 **可以解决**\ ,而不是说 +**因此我们需要三次握手**\ 。 + +没有第三次握手会有多个重复连接导致浪费资源,是建立在三次请求才会建立连接的基础上才会出现的问题,这不是设计三次请求的原因。只是三次握手刚好也解决了这个问题,这个逻辑要搞清楚。 + +问题2:为什么不是握手两次? +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +这个问题可以转换成『只握手两次就建立连接会出现什么样的问题?』 + +还是用给女朋友打电话这个例子,男朋友如果没有跟女朋友确认对方是否可以听到自己的话,就自己一直在说说说,最后只能尴尬收场。这就是我们所说的不可靠的连接,只是单向,而不是双向。 + +.. code:: shell + + 她:“你可以听到了吗?” + 我:“可以呀” # 没有向对方确认是否可以听到自己就开始一直说说说 + 我:“你吃饭了吗?“ + 我:“人呢?“ + 我:“喂?“ + 我:“去哪啦?“ + +在实际应用上,其实只握手两次还会出现更严重的问题,那就是资源浪费。 + +还是上面那个例子,a 包由于网络拥堵,迟迟没有发到目标机器 +,由于超时源机器会重新发送一个 SYN 包 +b,如果只进行了两次握手,目标机器就建立了连接,那么当 b +包到达后,目标机器又会创建一个连接,而这个连接是无用的、多余的。 + +|image8| + +这里仅仅假设只超时重发一次就成功了,如果超时重发了 10 +次,甚至更多呢?本来TCP 传输只需要一个连接就行了,现在服务端却创建了 n +个 连接,对于服务器资源来说无疑是非常浪费的。 + +问题3:为什么不是握手四次? +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +看到这里,你应该很清楚 三次握手的流程了。 + +那么握手四次是什么样的呢? + +还是以给女朋友打电话的例子来说明 + +.. code:: shell + + 她:“你可以听到了吗?” + 我:“可以呀!” + 我:“你呢,你可以听到我的吗?” + 她:“我也可以听到了。” + +和三次握手相对比,其实就是把原来第二次握手的内容拆分成两次发送。 + +|image9| + +所以为什么不握手四次? + +因为三次握手就可以完成的事,为什么要四次握手呢?没必要。 + +问题4:为什么不握手五次或更多? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +这个问题有点迷,你可能还不太清楚,还是以跟女朋友打电话为例 + +.. code:: shell + + 她:“你可以听到了吗?” + 我:“可以呀,你呢,你可以听到我的吗?” + 她:“恩,我也可以听到了。你呢,现在还可以听到吗?” + 我:“可以呀,现在你那边还听到我的吗?” + 她:“是的,可以,你呢,可以听到我现在说的吗” + 我:“可以听到,那你呢?” + ... + ... + +在每一次跟确认可以听到对方的声音时,还生怕这个消息对方收不到这个消息,所以两个人就一直在确认,跟个zz一样。 + +所以你问我,为什么不握手五次或更多? + +因为三次是基本保障,再多一个,就是多余,容易死循环。 + +6. MTU 和 MSS 是什么? +---------------------- + +MTU +~~~ + +Maximum Transmission Unit,最大传输单元。 + +在TCP/IP协议族中,指的是\ **IP数据报**\ 能经过一个\ **物理网络**\ 的\ **最大报文长度**\ ,其中包括了IP首部(从20个字节到60个字节不等)。 + +由此我们知道,MTU 为多大跟链路层的介质有关,我们接触最多的以太网的 MTU +设为1500字节。 + +其他的你可以参考 下面这张图(摘自维基百科) + +|image10| + +如果上层协议(如 TCP)交给IP协议的内容实在是太多,使得 IP +报文的大小超过了 MTU ,以以太网为例,如果 IP 报文大小超过了1500 Bytes +,那么\ **IP报文就必须要分片传输**\ ,到达目的主机或目的路由器之后由其进行重组分片。 + +IP分片发生在IP层,不仅源端主机会进行分片,中间的路由器也有可能分片,因为不同的网络的MTU是不一样的,如果传输路径上的某个网络的MTU比源端网络的MTU要小,路由器就可能对IP数据报再次进行分片。而分片数据的重组只会发生在目的端的IP层。 + +MSS +~~~ + +Maximum Segment Size ,它表示的是 TCP 报文段中的数据字段的最大长度。 + +数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是整个TCP报文段的最大长度,而是“TCP报文段长度减去TCP首部长度”。 + +MSS 和 MTU 的关系是: + +MSS = MTU - IP首部大小 - TCP首部大小 + +|image11| + +**那为什么要规定一个最大报文长度MSS呢?** + +这并不是考虑接受方的接收缓存可能存放不下TCP报文段中的数据。实际上,MSS与接收窗口值没有关系。我们知道,TCP报文段的数据部分,至少要加上40字节的首部(TCP首部20字节和IP首部20字节,这里还没有考虑首部中的可选部分)才能组装成一个IP数据报。 + +若选择较小的MSS长度,网络的利用率就降低。设想在极端情况下,当TCP报文段只含有1字节的数据时,在IP层传输的数据报的开销至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,对网络的利用率就不会超过1/41。到了数据链路层还要加上一些开销。 + +但反过来,若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片组成成原来的TCP报文段,当传输出错时还要进行重传。 + +IP层是没有超时重传机制的,如果IP层对一个数据包进行了分片,只要有一个分片丢失了,只能依赖于传输层进行重传,结果是所有的分片都要重传一遍,这个代价有点大。 + +因此,MSS应尽可能大些,只要在IP层传输时不需要分片就行。由于IP数据报所经历的路径是动态变化的,因此在这条路径上确定的不需要的分片的MSS,如果改走另一条路径就可能需要进行分片。\ **因此最佳的MSS是很难确定的**\ 。 + +在连接过程中,双方都把自己能够支持的MSS写入这一字段,以后就按照这个数值传输数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节长。因此,所有在互联网上的主机都应该接受的报文段长度是536+20(固定首部长度)=556字节。 + +7. 网络编程的常规步骤 +--------------------- + +上面为了方便抓包,我使用了 Python 写了一个服务器和客户端程序进行通信。 + +这里有必要说一下,面向 TCP 进行网络编程的常规步骤 + +|image12| + +如果是服务端: + +1. 用函数socket() 创建一个socket; + +2. 用函数setsockopt() 设置socket属性; **可选步骤** + +3. 用函数bind() 绑定IP地址、端口等信息到socket上; + +4. 用函数listen() 开启监听; + +5. 用函数accept() 接收客户端上来的连接; + +6. 用函数send()和recv() 或者 read()和write() 收发数据; + +7. 关闭网络连接; + +8. 关闭监听; + +而如果是客户端: + +1. 用函数socket() 创建一个socket; + +2. 用函数setsockopt() 设置socket属性 ;\ **可选步骤** + +3. 用函数bind() 绑定IP地址、端口等信息到socket上; **可选步骤** +4. 用函数connect() 对方的IP地址和端口连接服务器 ; +5. 用函数send()和recv() 或者 read()和write() 收发数据; +6. 关闭网络连接; + +其中最主要、最关键的有三个函数: + +connect() +~~~~~~~~~ + +它是一个阻塞函数,通过 TCP 三次握手与服务器建立连接。 + +一般的情况下 客户端的connect函数 默认是阻塞行为 +直到三次握手阶段成功为止。 + +listen() +~~~~~~~~ + +不是一个阻塞函数: 它会将套接字 和 套接字对应队列的长度告诉Linux内核 + +他是被动连接的 一直监听来自不同客户端的请求 listen函数只要 +作用将socketfd 变成被动的连接监听socket 其中参数backlog作用 +设置内核中队列的长度 。 + +accpet() +~~~~~~~~ + +是一个阻塞函数,它会从处于 established 状态的队列中取出完成的连接。 + +当队列中没有完成连接时候就会阻塞,直到取出队列中已完成连接的用户连接为止。 + +那如果服务器没有及时调用 accept 函数取走完成连接的队列怎么办呢? + +服务器的连接队列满掉后,服务器不会对再对建立新连接的 SYN +进行应答,所以客户端的 connect 就会返回 ETIMEDOUT。 + +8. 注意事项 +----------- + +ack 和 ACK 有区别吗? +~~~~~~~~~~~~~~~~~~~~~ + +上面的分析三次握手和四次挥手时,有一个细节问题,可能不是那么重要,但是需要你搞清楚。 + +就是 ack 和 ACK 是否一致?答案是否定的 + +如果是 大写的 ACK ,表示的是标志位里的 flag,除了最初建立连接时的 SYN +包之外,后续的所有包此位都会被置为 1。 + +如果是 小写的 +ack,表示的是希望确认号,表示的是希望接收到对方下一次数据的序列号, ack +一般都是上次接收成功的数据字节序号加1。 + +TCP 包最多可传输多少数据? +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +对于TCP协议来说,整个包的最大长度是由最大传输大小(MSS,Maxitum Segment +Size)决定,MSS就是TCP数据包每次能够传输的最大数据分段。 + +为了达到最佳的传输效能 TCP协议在建立连接的时候通常要协商双方的MSS值。 + +通讯双方会根据双方提供的 MSS值的较小值来确定为这次连接的 MSS值。 + +在以太网中,MTU 为 1500 Bytes,减去IP数据包包头的大小20Bytes 和 +TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 + +9. 异常情况分析 +--------------- + +试图与一个不存在的端口建立连接(主机正常) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +这里的不存在的端口是指在服务器端没有程序监听在该端口。我们的客户端就调用connect,试图与其建立连接。这时会发生什么呢? + +这种情况下我们在客户端通常会收到如下异常内容: + +.. code:: python + + Traceback (most recent call last): + File "/Users/MING/Code/Python/tcp_client.py", line 8, in + s.connect((host, port)) + ConnectionRefusedError: [Errno 61] Connection refused + +试想一下,服务端本来就没有程序监听在这个接口,因此在服务端是无法完成连接的建立过程的。我们参考三次握手的流程可以知道当客户端的SYNC包到达服务端时,TCP协议没有找到监听的套接字,就会向客户端发送一个错误的报文,告诉客户端产生了错误。而该错误报文就是一个包含RST的报文。这种异常情况也很容易模拟,我们只需要写一个小程序,连接服务器上没有监听的端口即可。如下是通过wireshark捕获的数据包,可以看到红色部分的RST报文。 + +|image13| + +试图与一个某端口建立连接但该主机已经宕机(主机宕机) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +这也是一种比较常见的情况,当某台服务器主机宕机了,而客户端并不知道,因此会重复发送SYNC数据包. + +如下图所示,可以看到客户端每隔一段时间就会向服务端发送一个SYNC数据包。这里面具体的时间是跟TCP协议相关的,具体时间不同的操作系统实现可能稍有不同。 + +|image14| + +建立连接时,服务器应用被阻塞(或者僵死) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +还有一种异常情况是,客户端建立连接的过程中服务端应用处于僵死状态,这种情况在实际中也会经常出现(我们假设仅仅应用程序僵死,而内核没有僵死)。 + +对于TCP的服务端来说,当它收到SYN数据包时,就会创建一个套接字的数据结构并给客户端回复ACK,再次收到客户端的ACK时会将套接字数据结构的状态转换为ESTABLISHED,并将其加入就绪队列。 + +当上面的套接字处于就绪队列时,accept函数才被唤醒了,可以从套接字中读取数据。 + +在 accept +返回之前,客户端也是可以发送数据的,因为数据的发送与接收都是在内核态进行的。客户端发送数据后,服务端的网卡会先接收,然后通过中断通知IP层,再上传到TCP层。TCP层根据目的端口和地址将数据存入关联的缓冲区。 + +到此,可以得出几点结论。 + +1. 在 accept 返回之前,三次握手已经完成。 +2. TCP的客户端是否可以发送数据与服务端程序是否工作没有关系。 + +但是如果内核也处于僵死状态,那情况可就完全不一样了。 + +此时由于机器完全卡死,TCP服务端无法接受任何消息,自然也无法给客户端发送任何应答报文,也不会有后续发送数据的环节了。 + +10. 参考文章 +------------ + +`TCP报文段的首部格式 `__ + +`TCP +、UDP、IP包的最大长度 `__ + +`理解了这些异常现象才敢说真正懂了TCP协议 `__ + +`近 40 张图解被问千百遍的 TCP +三次握手和四次挥手面试题 `__ + +.. |image0| image:: http://image.iswbm.com/image-20200601221524846.png +.. |image1| image:: http://image.iswbm.com/image-20200601222110435.png +.. |image2| image:: http://image.iswbm.com/image-20200602234904143.png +.. |image3| image:: http://image.iswbm.com/20200605130951.png +.. |image4| image:: http://image.iswbm.com/image-20200602235431620.png +.. |image5| image:: http://image.iswbm.com/image-20200602235723214.png +.. |image6| image:: http://image.iswbm.com/20200605192855.png +.. |image7| image:: http://image.iswbm.com/20200605200027.png +.. |image8| image:: http://image.iswbm.com/20200605201138.png +.. |image9| image:: http://image.iswbm.com/20200605202450.png +.. |image10| image:: http://image.iswbm.com/image-20200604204657243.png +.. |image11| image:: http://image.iswbm.com/tcp_pdus.png +.. |image12| image:: http://image.iswbm.com/20200605204727.png +.. |image13| image:: http://image.iswbm.com/image-20200604223625787.png +.. |image14| image:: http://image.iswbm.com/image-20200604224127512.png + diff --git a/source/c10/c10_05.md b/source/c10/c10_05.md new file mode 100644 index 0000000..86ee149 --- /dev/null +++ b/source/c10/c10_05.md @@ -0,0 +1,34 @@ +# 10.5 Wireshark 抓包教程 + + + +## 过滤器 + +过滤器按照过滤器的使用时间点不同,可以分为捕获过滤器和显示过滤器。 + +**1. 网络协议过滤** + +比如 TCP,只显示 TCP 协议,HTTP 只显示 HTTP 协议等。在过滤器输入框中直接输入协议名称即可,不区分大小写。 + +**2. IP 地址过滤** + +如 ip.src == 192.168.1.102 显示源地址为 `192.168.1.102`, +而 ip.dst == 192.168.1.102, 目标地址为 `192.168.1.102`。 + +**3. 端口过滤** + +tcp.port == 80, 端口为 80 的 + +tcp.srcport == 80, 只显示 TCP 协议的原端口为 80 的。 + +**4. Http 模式过滤** + +http.request.method == “GET”,只显示 HTTP GET 方法的。 + +**5. 结合逻辑运算符 AND/OR 组成复杂的表达式** + +AND/OR 也可以写成 `&&` / `||` + + + +`Wireshark` 只能查看封包,而不能修改封包的内容,或者发送封包。 \ No newline at end of file diff --git a/source/c10/c10_05.rst b/source/c10/c10_05.rst new file mode 100644 index 0000000..d805256 --- /dev/null +++ b/source/c10/c10_05.rst @@ -0,0 +1,33 @@ +10.5 Wireshark 抓包教程 +======================= + +过滤器 +------ + +过滤器按照过滤器的使用时间点不同,可以分为捕获过滤器和显示过滤器。 + +**1. 网络协议过滤** + +比如 TCP,只显示 TCP 协议,HTTP 只显示 HTTP +协议等。在过滤器输入框中直接输入协议名称即可,不区分大小写。 + +**2. IP 地址过滤** + +如 ip.src == 192.168.1.102 显示源地址为 ``192.168.1.102``\ , 而 ip.dst +== 192.168.1.102, 目标地址为 ``192.168.1.102``\ 。 + +**3. 端口过滤** + +tcp.port == 80, 端口为 80 的 + +tcp.srcport == 80, 只显示 TCP 协议的原端口为 80 的。 + +**4. Http 模式过滤** + +http.request.method == “GET”,只显示 HTTP GET 方法的。 + +**5. 结合逻辑运算符 AND/OR 组成复杂的表达式** + +AND/OR 也可以写成 ``&&`` / ``||`` + +``Wireshark`` 只能查看封包,而不能修改封包的内容,或者发送封包。 diff --git a/source/c10/c10_06.md b/source/c10/c10_06.md new file mode 100644 index 0000000..38cc080 --- /dev/null +++ b/source/c10/c10_06.md @@ -0,0 +1,37 @@ +# 10.6 通过比较,学习 TCP 与 UDP + +## 1. TCP 与 UDP 的区别 + +**1. 连接** + +TCP 是面向连接的,传输数据前要先经历三次握手建立连接 + +UDP 不需要连接,即刻就可以传输数据 + +**2. 可靠性** + +TCP 能够保证数据可靠地,完整地,无重复的到达对端。 + +而 UDP 并不保证,有可能会丢包 + +**3. 连接场景** + +TCP 的连接,就跟线一样,只有两端,所以只能一对一 通信。 + +UDP 则支持一对一,一对多,多对多的通信。 + +**4. 传输控制** + +TCP 有丢包重传,拥塞控制,流量控制的机制 + +UDP 则没有,就算网络非常拥堵,也不会影响 UDP 的发送速率 + +**5. 首部开销** + +TCP 首部长度较长,且是可变长的,会有一定的开销。 + +UDP 首部固定只有 8 个字节,开销较小。 + + + +[面向报文(UDP)和面向字节流(TCP)的区别](https://blog.csdn.net/ce123_zhouwei/article/details/8976006) \ No newline at end of file diff --git a/source/c10/c10_06.rst b/source/c10/c10_06.rst new file mode 100644 index 0000000..c895104 --- /dev/null +++ b/source/c10/c10_06.rst @@ -0,0 +1,37 @@ +10.6 通过比较,学习 TCP 与 UDP +============================== + +1. TCP 与 UDP 的区别 +-------------------- + +**1. 连接** + +TCP 是面向连接的,传输数据前要先经历三次握手建立连接 + +UDP 不需要连接,即刻就可以传输数据 + +**2. 可靠性** + +TCP 能够保证数据可靠地,完整地,无重复的到达对端。 + +而 UDP 并不保证,有可能会丢包 + +**3. 连接场景** + +TCP 的连接,就跟线一样,只有两端,所以只能一对一 通信。 + +UDP 则支持一对一,一对多,多对多的通信。 + +**4. 传输控制** + +TCP 有丢包重传,拥塞控制,流量控制的机制 + +UDP 则没有,就算网络非常拥堵,也不会影响 UDP 的发送速率 + +**5. 首部开销** + +TCP 首部长度较长,且是可变长的,会有一定的开销。 + +UDP 首部固定只有 8 个字节,开销较小。 + +`面向报文(UDP)和面向字节流(TCP)的区别 `__ From b57c0d21e2aa696facb6370c7f54db0f995337bf Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 6 Jun 2020 10:00:28 +0800 Subject: [PATCH 076/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- source/c10/c10_03.md | 10 +++++----- source/c10/c10_03.rst | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7abe11f..4a307ec 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ ## 第十章:网络基础 - 10.1 [网络知识扫盲:一篇文章搞懂 DNS](http://python.iswbm.com/en/latest/c10/c10_01.html) - 10.2 [网络知识扫盲:如何理解 OSI七层模型](http://python.iswbm.com/en/latest/c10/c10_02.html) -- 10.3 [网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质](http://python.iswbm.com/en/latest/c10/c10_03.html) +- 10.3 [网络知识扫盲:详解TCP的三次握手与四次挥手](http://python.iswbm.com/en/latest/c10/c10_03.html) - 10.5 [Wireshark 抓包教程](http://python.iswbm.com/en/latest/c10/c10_05.html) - 10.6 [通过比较,学习 TCP 与 UDP](http://python.iswbm.com/en/latest/c10/c10_06.html) - 10.7 [网络知识扫盲:一篇文章理解 HTTP](http://python.iswbm.com/en/latest/c10/c10_07.html) diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md index 222d00d..0dd6260 100644 --- a/source/c10/c10_03.md +++ b/source/c10/c10_03.md @@ -1,4 +1,6 @@ -# 10.3 网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质 +# 10.3 网络知识扫盲:详解TCP的三次握手与四次挥手 + + ## 1. TCP 协议是什么? @@ -57,9 +59,7 @@ TCP 自身有三次握手和超时重传等机制,所以无论网络如何变 TCP 的报文段结构,可以从下面这张图中非常清晰的看到。 - - -![TCP 报文首部](http://image.iswbm.com/20200605203659.png) +![TCP 报文首部](http://image.iswbm.com/20200606095627.png) 接下来,我会一个一个讲解这些字段的内容。 @@ -69,7 +69,7 @@ TCP 的报文段结构,可以从下面这张图中非常清晰的看到。 **确认号**:共占用 4个字节。说明确认号的范围是 [0, 2^32-1],也就是 [0, 4294967296]。它表示**期望**收到对方下一次数据的序列号(所以 ack 一般都是上次接收成功的数据字节序号加1),发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。**用来解决不丢包的问题**。TCP在接收到数据后 200ms 才会发送ACK包,这种设定是为了等待是否有数据可以一起发送的。 -**数据偏移**:共占 4 个bit,它表示的是TCP报文的数据起始处距离TCP报文起始处的距离有多远。实际生活中我们说距离多远,我们的单位通常是米,而这里距离有多远,单位是 4 个字节(也就是 32bit)。由于 4 个bit,能表示的最大整数是 15,也就说明 TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15)。这意味着 TCP 的首部(除数据外的都叫首部)长度是 20-60 个字节。 +**数据偏移(图中为报文首部)**:共占 4 个bit,它表示的是TCP报文的数据起始处距离TCP报文起始处的距离有多远。实际生活中我们说距离多远,我们的单位通常是米,而这里距离有多远,单位是 4 个字节(也就是 32bit)。由于 4 个bit,能表示的最大整数是 15,也就说明 TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15)。这意味着 TCP 的首部(除数据外的都叫首部)长度是 20-60 个字节。 **窗口**:共占 16 个bit,因此最大的窗口大小为 2^16-1 = 65535 = 64k。这是早期的设计,对于现在的网络应用,可能会不太够,因此可以在选项里加一个 **窗口扩大选项**,来传输更多的数据。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。 diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst index 36a03aa..5ff4511 100644 --- a/source/c10/c10_03.rst +++ b/source/c10/c10_03.rst @@ -1,5 +1,5 @@ -10.3 网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质 -======================================================= +10.3 网络知识扫盲:详解TCP的三次握手与四次挥手 +============================================== 1. TCP 协议是什么? ------------------- @@ -66,7 +66,7 @@ TCP TCP 的报文段结构,可以从下面这张图中非常清晰的看到。 -.. figure:: http://image.iswbm.com/20200605203659.png +.. figure:: http://image.iswbm.com/20200606095627.png :alt: TCP 报文首部 TCP 报文首部 @@ -90,7 +90,7 @@ SYN 和 FIN 包的 seq 也要消耗一个序号)。\ **用来解决网络包 一般都是上次接收成功的数据字节序号加1),发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。\ **用来解决不丢包的问题**\ 。TCP在接收到数据后 200ms 才会发送ACK包,这种设定是为了等待是否有数据可以一起发送的。 -**数据偏移**\ :共占 4 +**数据偏移(图中为报文首部)**\ :共占 4 个bit,它表示的是TCP报文的数据起始处距离TCP报文起始处的距离有多远。实际生活中我们说距离多远,我们的单位通常是米,而这里距离有多远,单位是 4 个字节(也就是 32bit)。由于 4 个bit,能表示的最大整数是 15,也就说明 TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15)。这意味着 TCP From f29071b886ea0514f5df24b0e6f1586587aff633 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 6 Jun 2020 10:57:44 +0800 Subject: [PATCH 077/147] =?UTF-8?q?Update=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=A6=81=E6=AD=A2=E8=BD=AC=E8=BD=BD=E5=A3=B0=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_01.md | 2 + source/c01/c01_01.rst | 11 +- source/c01/c01_04.md | 2 + source/c01/c01_04.rst | 7 +- source/c01/c01_05.md | 340 ++-- source/c01/c01_05.rst | 5 + source/c01/c01_06.md | 206 +-- source/c01/c01_06.rst | 5 + source/c01/c01_07.md | 590 +++---- source/c01/c01_07.rst | 5 + source/c01/c01_08.md | 2 + source/c01/c01_08.rst | 25 +- source/c01/c01_09.md | 2 + source/c01/c01_09.rst | 5 + source/c01/c01_10.md | 2 + source/c01/c01_10.rst | 51 +- source/c01/c01_11.md | 2 + source/c01/c01_11.rst | 5 + source/c01/c01_12.md | 2 + source/c01/c01_12.rst | 11 +- source/c01/c01_13.md | 2 + source/c01/c01_13.rst | 3 + source/c01/c01_14.md | 2 + source/c01/c01_14.rst | 7 +- source/c01/c01_15.md | 2 + source/c01/c01_15.rst | 5 + source/c01/c01_16.md | 2 + source/c01/c01_16.rst | 5 + source/c01/c01_17.md | 2 + source/c01/c01_17.rst | 17 +- source/c01/c01_18.md | 2 + source/c01/c01_18.rst | 31 +- source/c01/c01_20.md | 2 + source/c01/c01_20.rst | 19 +- source/c01/c01_21.md | 2 + source/c01/c01_21.rst | 5 + source/c01/c01_22.md | 2 + source/c01/c01_22.rst | 7 +- source/c01/c01_23.md | 2 + source/c01/c01_23.rst | 5 + source/c01/c01_24.md | 2 + source/c01/c01_24.rst | 7 +- source/c01/c01_25.md | 2 + source/c01/c01_25.rst | 5 + source/c01/c01_26.md | 2 + source/c01/c01_26.rst | 5 + source/c01/c01_27.md | 2 + source/c01/c01_27.rst | 19 +- source/c01/c01_29.md | 2 + source/c01/c01_29.rst | 5 + source/c01/c01_30.md | 2 + source/c01/c01_30.rst | 23 +- source/c01/c01_31.md | 2 + source/c01/c01_31.rst | 5 + source/c01/c01_32.md | 2 + source/c01/c01_32.rst | 5 + source/c01/c01_33.md | 2 + source/c01/c01_33.rst | 5 + source/c01/c01_34.md | 2 + source/c01/c01_34.rst | 7 +- source/c01/c01_35.md | 2 + source/c01/c01_35.rst | 15 +- source/c01/c01_36.md | 2 + source/c01/c01_36.rst | 47 +- source/c01/c01_37.md | 2 + source/c01/c01_37.rst | 5 + source/c01/c01_38.md | 2 + source/c01/c01_38.rst | 23 +- source/c01/c01_39.md | 2 + source/c01/c01_39.rst | 5 + source/c01/c01_40.md | 2 + source/c01/c01_40.rst | 5 + source/c01/c01_41.md | 2 + source/c01/c01_41.rst | 5 + source/c01/c01_42.md | 2 + source/c01/c01_42.rst | 7 +- source/c01/c01_43.md | 2 + source/c01/c01_43.rst | 15 +- source/c01/c01_44.md | 2 + source/c01/c01_44.rst | 11 +- source/c01/c01_45.md | 2 + source/c01/c01_45.rst | 5 + source/c02/c02_01.md | 2 + source/c02/c02_01.rst | 19 +- source/c02/c02_02.md | 2 + source/c02/c02_02.rst | 5 + source/c02/c02_03.md | 2 + source/c02/c02_03.rst | 5 + source/c02/c02_04.md | 2 + source/c02/c02_04.rst | 5 + source/c02/c02_05.md | 2 + source/c02/c02_05.rst | 5 + source/c02/c02_06.md | 2 + source/c02/c02_06.rst | 5 + source/c02/c02_07.md | 2 + source/c02/c02_07.rst | 11 +- source/c02/c02_08.md | 2 + source/c02/c02_08.rst | 5 + source/c02/c02_09.md | 2 + source/c02/c02_09.rst | 3 + source/c02/c02_10.md | 2 + source/c02/c02_10.rst | 5 + source/c02/c02_11.md | 2 + source/c02/c02_11.rst | 17 +- source/c02/c02_12.md | 2 + source/c02/c02_12.rst | 5 + source/c02/c02_13.md | 2 + source/c02/c02_13.rst | 5 + source/c02/c02_14.md | 2 + source/c02/c02_14.rst | 11 +- source/c03/c03_01.md | 2 + source/c03/c03_01.rst | 11 +- source/c03/c03_02.md | 2 + source/c03/c03_02.rst | 5 + source/c03/c03_03.md | 2 + source/c03/c03_03.rst | 11 +- source/c03/c03_04.md | 2 + source/c03/c03_04.rst | 27 +- source/c03/c03_05.md | 2 + source/c03/c03_05.rst | 15 +- source/c03/c03_06.md | 2 + source/c03/c03_06.rst | 95 +- source/c04/c04_01.md | 2 + source/c04/c04_01.rst | 23 +- source/c04/c04_02.md | 2 + source/c04/c04_02.rst | 23 +- source/c04/c04_03.md | 2 + source/c04/c04_03.rst | 75 +- source/c04/c04_04.md | 2 + source/c04/c04_04.rst | 37 +- source/c04/c04_05.md | 2 + source/c04/c04_05.rst | 67 +- source/c04/c04_06.md | 2 + source/c04/c04_06.rst | 31 +- source/c04/c04_07.md | 2 + source/c04/c04_07.rst | 47 +- source/c04/c04_08.md | 2 + source/c04/c04_08.rst | 5 + source/c04/c04_09.md | 2 + source/c04/c04_09.rst | 141 +- source/c04/c04_10.md | 2 + source/c04/c04_10.rst | 97 +- source/c04/c04_11.md | 2 + source/c04/c04_11.rst | 71 +- source/c04/c04_12.md | 2 + source/c04/c04_12.rst | 23 +- source/c04/c04_13.md | 2 + source/c04/c04_13.rst | 5 + source/c04/c04_14.md | 2 + source/c04/c04_14.rst | 35 +- source/c04/c04_15.md | 2 + source/c04/c04_15.rst | 415 ++--- source/c04/c04_16.md | 2 + source/c04/c04_16.rst | 5 + source/c04/c04_17.md | 2 + source/c04/c04_17.rst | 19 +- source/c04/c04_18.md | 2 + source/c04/c04_18.rst | 11 +- source/c04/c04_19.md | 2 + source/c04/c04_19.rst | 5 + source/c04/c04_20.md | 2 + source/c04/c04_20.rst | 5 + source/c04/c04_21.md | 2 + source/c04/c04_21.rst | 7 +- source/c04/c04_22.md | 2 + source/c04/c04_22.rst | 7 +- source/c04/c04_23.md | 2 + source/c04/c04_23.rst | 7 +- source/c04/c04_24.md | 2 + source/c04/c04_24.rst | 19 +- source/c05/c05_01.md | 2 + source/c05/c05_01.rst | 19 +- source/c05/c05_02.md | 2 + source/c05/c05_02.rst | 5 + source/c05/c05_03.md | 2 + source/c05/c05_03.rst | 7 +- source/c06/c06_01.md | 2 + source/c06/c06_01.rst | 11 +- source/c06/c06_02.md | 2 + source/c06/c06_02.rst | 43 +- source/c06/c06_03.md | 2 + source/c06/c06_03.rst | 23 +- source/c06/c06_04.md | 2 + source/c06/c06_04.rst | 31 +- source/c06/c06_05.md | 2 + source/c06/c06_05.rst | 7 +- source/c06/c06_06.md | 2 + source/c06/c06_06.rst | 7 +- source/c07/C07_08.md | 2 + source/c07/c07_01.md | 3576 +++++++++++++++++++++-------------------- source/c07/c07_01.rst | 15 +- source/c07/c07_02.md | 2 + source/c07/c07_02.rst | 91 +- source/c07/c07_03.md | 2 + source/c07/c07_03.rst | 9 +- source/c07/c07_04.md | 2 + source/c07/c07_04.rst | 39 +- source/c07/c07_05.md | 2 + source/c07/c07_05.rst | 19 +- source/c07/c07_06.md | 2 + source/c07/c07_06.rst | 15 +- source/c07/c07_07.md | 2 + source/c07/c07_07.rst | 5 + source/c07/c07_08.rst | 5 + source/c07/c07_09.md | 2 + source/c07/c07_09.rst | 5 + source/c07/c07_10.md | 2 + source/c07/c07_10.rst | 19 +- source/c07/c07_11.md | 2 + source/c07/c07_11.rst | 7 +- source/c07/c07_12.md | 2 + source/c07/c07_12.rst | 15 +- source/c07/c07_13.md | 2 + source/c07/c07_13.rst | 5 + source/c07/c07_14.md | 2 + source/c07/c07_14.rst | 5 + source/c07/c07_15.md | 2 + source/c07/c07_15.rst | 7 +- source/c07/c07_16.md | 2 + source/c07/c07_16.rst | 19 +- source/c07/c07_17.md | 2 + source/c07/c07_17.rst | 5 + source/c07/c07_18.md | 2 + source/c07/c07_18.rst | 5 + source/c07/c07_19.md | 2 + source/c07/c07_19.rst | 5 + source/c07/c07_20.md | 2 + source/c07/c07_20.rst | 5 + source/c08/c08_01.md | 2 + source/c08/c08_01.rst | 5 + source/c08/c08_02.md | 2 + source/c08/c08_02.rst | 33 +- source/c08/c08_03.md | 2 + source/c08/c08_03.rst | 23 +- source/c08/c08_04.md | 2 + source/c08/c08_04.rst | 27 +- source/c08/c08_05.md | 2 + source/c08/c08_05.rst | 243 +-- source/c08/c08_06.md | 2 + source/c08/c08_06.rst | 167 +- source/c08/c08_07.md | 2 + source/c08/c08_07.rst | 47 +- source/c08/c08_08.md | 2 + source/c08/c08_08.rst | 31 +- source/c08/c08_09.md | 2 + source/c08/c08_09.rst | 87 +- source/c08/c08_10.md | 2 + source/c08/c08_10.rst | 15 +- source/c08/c08_11.md | 2 + source/c08/c08_11.rst | 7 +- source/c08/c08_12.md | 2 + source/c08/c08_12.rst | 35 +- source/c08/c08_13.md | 2 + source/c08/c08_13.rst | 19 +- source/c08/c08_14.md | 2 + source/c08/c08_14.rst | 59 +- source/c08/c08_15.md | 2 + source/c08/c08_15.rst | 51 +- source/c09/c09_01.md | 2 + source/c09/c09_01.rst | 15 +- source/c09/c09_02.md | 2 + source/c09/c09_02.rst | 7 +- source/c09/c09_03.md | 2 + source/c09/c09_03.rst | 5 + source/c10/c10_01.md | 2 + source/c10/c10_01.rst | 59 +- source/c10/c10_02.md | 2 + source/c10/c10_02.rst | 7 +- source/c10/c10_03.md | 2 + source/c10/c10_03.rst | 63 +- source/c10/c10_05.md | 2 + source/c10/c10_05.rst | 5 + source/c10/c10_06.md | 2 + source/c10/c10_06.rst | 5 + 274 files changed, 4571 insertions(+), 3788 deletions(-) diff --git a/source/c01/c01_01.md b/source/c01/c01_01.md index 938ffc8..1739c71 100644 --- a/source/c01/c01_01.md +++ b/source/c01/c01_01.md @@ -1,5 +1,7 @@ # 1.1 13条Python2.x和3.x的区别? +![](http://image.iswbm.com/20200602135014.png) + --- 从今天开始,小明将和你一起过一下,那些在面试「Python开发」岗位时面试官喜欢问的问题。内容基础,但是你不一定会噢。 diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 21bbee0..307c91c 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -1,6 +1,8 @@ 1.1 13条Python2.x和3.x的区别? ============================== +|image0| + -------------- 从今天开始,小明将和你一起过一下,那些在面试「Python开发」岗位时面试官喜欢问的问题。内容基础,但是你不一定会噢。 @@ -23,10 +25,10 @@ 那当然是官网啦:https://www.python.org/downloads/ 这个地址里,有所有Python历史版本(2.0+)。 点击左边,Release Version栏目 -对应的版本。 |image0| +对应的版本。 |image1| 进入对应详情页后,找到如图 ``what's new in Python xx`` -就可以查看此版本的新特性。 |image1| +就可以查看此版本的新特性。 |image2| 网页是全英文的,需要你有一定的英文阅读能力。快去感觉一下吧。 @@ -319,6 +321,7 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511165542.png -.. |image1| image:: http://image.python-online.cn/20190511165551.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511165542.png +.. |image2| image:: http://image.python-online.cn/20190511165551.png diff --git a/source/c01/c01_04.md b/source/c01/c01_04.md index 0cef6a9..713a621 100644 --- a/source/c01/c01_04.md +++ b/source/c01/c01_04.md @@ -1,5 +1,7 @@ # 1.4 什么是猴子补丁? +![](http://image.iswbm.com/20200602135014.png) + 也许你没用过猴子补丁,但是你必须知道,在Python/Ruby 这类脚本语言中,有一种用法叫 Monkey Patch 。 这名字挺好玩的哈。在网上,关于这个名称的来源,大致有如下两种说法: diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index 5972b03..9813668 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -1,6 +1,8 @@ 1.4 什么是猴子补丁? =================== +|image0| + 也许你没用过猴子补丁,但是你必须知道,在Python/Ruby 这类脚本语言中,有一种用法叫 Monkey Patch 。 @@ -62,7 +64,7 @@ 在openstack中的例子 -|image0| +|image1| 还有就是gevent中也有用到。 @@ -120,5 +122,6 @@ 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190404215330.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190404215330.png diff --git a/source/c01/c01_05.md b/source/c01/c01_05.md index febc146..79e75fe 100644 --- a/source/c01/c01_05.md +++ b/source/c01/c01_05.md @@ -1,169 +1,171 @@ -# 1.5 深入闭包与变量作用域 - ---- - -## 1.5.1 作用域 - -Python的作用域可以分为四种: -- L (Local) 局部作用域 -- E (Enclosing) 闭包函数外的函数中 -- G (Global) 全局作用域 -- B (Built-in) 内建作用域 - -变量/函数 的查找顺序: -L –> E –> G –>B - -意思是,在局部找不到的,便去局部外的局部作用域找(例如 闭包),再找不到的就去全局作业域里找,再找不到就去内建作业域中找。 - -会影响 变量/函数 作用范围的有 -- 函数:def 或 lambda -- 类:class -- 关键字:global noglobal -- 文件:*py -- 推导式:[],{},()等,仅限Py3.x中,Py2.x会出现变量泄露。 - -1、赋值在前,引用在后 -```python -# ------同作用域内------ -name = "MING" -print(name) - -# ------不同作用域内------ -name = "MING" -def main(): - print(name) -``` -2、引用在前,赋值在后(同一作用域内) -```python -print(name) -name = "MING" - -# UnboundLocalError: local variable 'name' referenced before assignment -``` -3、赋值在低层,引用在高层 -```python -# L -> E -> G -> B -# 从左到右,由低层到高层 -def main(): - name = "MING" - -print(name) -# NameError: name 'name' is not defined -``` - - -## 1.5.2 闭包 - -闭包这个概念很重要噢。你一定要掌握。 ->在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。其实装饰函数,很多都是闭包。 - -好像并不难理解,为什么初学者会觉得闭包难以理解呢? - -我解释一下,你就明白了。 - -一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。 - -你可以看下面这段代码,就构成了闭包。在内函数里可以引用外函数的变量。 -```python -def deco(): - name = "MING" - def wrapper(): - print(name) - return wrapper - -deco()() -# 输出:MING -``` - -## 1.5.3 改变作用域 - -变量的作用域,与其定义(或赋值)的位置有关,但不是绝对相关。 -因为我们可以在某种程度上去改变`向上`的作用范围。 - -- 关键字:global -将 局部变量 变为全局变量 - -- 关键字:nonlocal -可以在闭包函数中,引用并使用闭包外部函数的变量(非全局的噢) - -global好理解,这里只讲下nonlocal。 - -先来看个例子 -```python -def deco(): - age = 10 - def wrapper(): - age += 1 - return wrapper - -deco()() -``` -运行一下,会报错。 -``` -# UnboundLocalError: local variable 'age' referenced before assignment -``` -但是这样就OK -``` -def deco(): - age = 10 - def wrapper(): - nonlocal age - age += 1 - return wrapper - -deco()() -# 输出:11 -``` -其实,你如果不使用 `+=`、`-=`等一类的操作,不加nonlocal也没有关系。这就展示了闭包的特性。 -``` -def deco(): - age = 10 - def wrapper(): - print(age) - return wrapper - -deco()() -# 输出:10 -``` - - -## 1.5.4 变量集合 - -在Python中,有两个内建函数,你可能用不到,但是需要掌握它们。 -- globals() :以dict的方式存储所有全局变量 -- locals():以dict的方式存储所有局部变量 - -globals() -```python -def foo(): - print("I am a func") - -def bar(): - foo="I am a string" - foo_dup = globals().get("foo") - foo_dup() - -bar() -# 输出 -# I am a func -``` - -locals() -```python -other = "test" - -def foobar(): - name = "MING" - gender = "male" - for key,value in locals().items(): - print(key, "=", value) - -foobar() -# 输出 -# name = MING -# gender = male -``` - ----- - -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +# 1.5 深入闭包与变量作用域 + +![](http://image.iswbm.com/20200602135014.png) + +--- + +## 1.5.1 作用域 + +Python的作用域可以分为四种: +- L (Local) 局部作用域 +- E (Enclosing) 闭包函数外的函数中 +- G (Global) 全局作用域 +- B (Built-in) 内建作用域 + +变量/函数 的查找顺序: +L –> E –> G –>B + +意思是,在局部找不到的,便去局部外的局部作用域找(例如 闭包),再找不到的就去全局作业域里找,再找不到就去内建作业域中找。 + +会影响 变量/函数 作用范围的有 +- 函数:def 或 lambda +- 类:class +- 关键字:global noglobal +- 文件:*py +- 推导式:[],{},()等,仅限Py3.x中,Py2.x会出现变量泄露。 + +1、赋值在前,引用在后 +```python +# ------同作用域内------ +name = "MING" +print(name) + +# ------不同作用域内------ +name = "MING" +def main(): + print(name) +``` +2、引用在前,赋值在后(同一作用域内) +```python +print(name) +name = "MING" + +# UnboundLocalError: local variable 'name' referenced before assignment +``` +3、赋值在低层,引用在高层 +```python +# L -> E -> G -> B +# 从左到右,由低层到高层 +def main(): + name = "MING" + +print(name) +# NameError: name 'name' is not defined +``` + + +## 1.5.2 闭包 + +闭包这个概念很重要噢。你一定要掌握。 +>在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。其实装饰函数,很多都是闭包。 + +好像并不难理解,为什么初学者会觉得闭包难以理解呢? + +我解释一下,你就明白了。 + +一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。 + +你可以看下面这段代码,就构成了闭包。在内函数里可以引用外函数的变量。 +```python +def deco(): + name = "MING" + def wrapper(): + print(name) + return wrapper + +deco()() +# 输出:MING +``` + +## 1.5.3 改变作用域 + +变量的作用域,与其定义(或赋值)的位置有关,但不是绝对相关。 +因为我们可以在某种程度上去改变`向上`的作用范围。 + +- 关键字:global +将 局部变量 变为全局变量 + +- 关键字:nonlocal +可以在闭包函数中,引用并使用闭包外部函数的变量(非全局的噢) + +global好理解,这里只讲下nonlocal。 + +先来看个例子 +```python +def deco(): + age = 10 + def wrapper(): + age += 1 + return wrapper + +deco()() +``` +运行一下,会报错。 +``` +# UnboundLocalError: local variable 'age' referenced before assignment +``` +但是这样就OK +``` +def deco(): + age = 10 + def wrapper(): + nonlocal age + age += 1 + return wrapper + +deco()() +# 输出:11 +``` +其实,你如果不使用 `+=`、`-=`等一类的操作,不加nonlocal也没有关系。这就展示了闭包的特性。 +``` +def deco(): + age = 10 + def wrapper(): + print(age) + return wrapper + +deco()() +# 输出:10 +``` + + +## 1.5.4 变量集合 + +在Python中,有两个内建函数,你可能用不到,但是需要掌握它们。 +- globals() :以dict的方式存储所有全局变量 +- locals():以dict的方式存储所有局部变量 + +globals() +```python +def foo(): + print("I am a func") + +def bar(): + foo="I am a string" + foo_dup = globals().get("foo") + foo_dup() + +bar() +# 输出 +# I am a func +``` + +locals() +```python +other = "test" + +def foobar(): + name = "MING" + gender = "male" + for key,value in locals().items(): + print(key, "=", value) + +foobar() +# 输出 +# name = MING +# gender = male +``` + +---- + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index e3bd115..124fedf 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -1,6 +1,8 @@ 1.5 深入闭包与变量作用域 ======================== +|image0| + -------------- 1.5.1 作用域 @@ -181,3 +183,6 @@ locals() :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_06.md b/source/c01/c01_06.md index 20bfbf6..e6048ca 100644 --- a/source/c01/c01_06.md +++ b/source/c01/c01_06.md @@ -1,102 +1,104 @@ -# 1.6 深入理解元组存在的意义 - ---- - -Python中有一个基础的数据结构,叫做元组(tuple),但是一般挺少有人会去用它的,因为在开发过程中,列表(list)基本已经能够满足我们的需求。 - -即使是这样,你也千万不要就此认为元组是多余的。不然在面试中也不会经常被人问,元组和列表有啥区别?为什么需要元组? - -以下两点,第一点是大家所熟知的,而第二点可能只有老司机才会知道,只有学习了第二点,才算真正理解了元组存在的价值和意义。 - -## 2.6.1 不可变列表 - -这是`元组`区别于`列表`最显著的特征。 - -- list:可变的序列 -- tuple:不可变的序列 - -那什么是不可变的序列呢? - -那就是在元组对象生成后,诸如列表的`插入元素`、`删除元素`、`添加元素`、`清空元素`、`修改元素`等功能,在元组中通通没有,你是无法对其进行修改的。 - -由于元组是不可变的,所以其方法也是很有限的。这里罗列一下。 - -``` -# s1和s2都是元组 -s1=(1,2,3) -s2=(4,5,6) - -# 拼接生成新元组 -s1+s2 -s1.__add__(s2) - -# 是否包含 -2 in s1 -s1.__contains__(2) - -# 统计元素包含的次数 -s1.count(2) - -# 获取元素 -s1[0] -s1.__getitem__(0) - -# 找到2第一次出现的索引 -s1.index(2) - -# 获取长度 -len(s1) - -# 重复拼接 -s1*n -``` - - -## 2.6.2 具名元组 - -这个特性,我个人认为,才是元组存在的意义所在。 - -只讲 具名元组,可能不太好理解。如果称之为 `带字段名的记录`,你可能就清楚了。 - -这里举个例子,但是实现带字段名,需要一个库(collections)的支持,你需要导入它。 -```python -from collections import namedtuple -# 生成一个City类 -City = namedtuple("City", "name country polulation coordinates") -# 实例化 -tokyo = City("Tokyo", 'JP', '36.93', ('35.68','139,69')) - -print(tokyo) -# City(name='Tokyo', country='JP', polulation='36.93', coordinates=('35.68', '139,69')) - -print(tokyo.name) -# Tokyo -``` - -看着有点像字典,是不是,但是他不是字典(获取数值的方法也与字典不同),字典是可变。元组在创建后,就无法再对其进行修改。这在某个程度上说明元组适合存放那些无需修改的数据。比如上面的,地名,国家,经纬度。 - -除了上面的用法之处,这里还要介绍一些元组自己专有的属性。 -```python -# 打印字段名 -print(City._fields) -('name', 'country', 'polulation', 'coordinates') - -# 生成新实例 -LatLong = namedtuple('LatLong', 'lat long') -Xiamen_tuple = ('Xiemen', 'China', '40,54', LatLong(24.26,118.03)) -Xiamen = City._make(Xiamen_tuple) - -print(Xiamen) -# City(name='Xiemen', country='China', polulation='40,54', coordinates=(24.26, 118.03)) - -# 将具名元组转为OrderDict -Xiamen_dict = Xiamen._asdict() -print(Xiamen_dict) -# OrderedDict([('name', 'Xiemen'), ('country', 'China'), ('polulation', '40,54'), ('coordinates', LatLong(lat=24.26, long=118.03))]) -``` - -总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。 - --------------- - -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +# 1.6 深入理解元组存在的意义 + +![](http://image.iswbm.com/20200602135014.png) + +--- + +Python中有一个基础的数据结构,叫做元组(tuple),但是一般挺少有人会去用它的,因为在开发过程中,列表(list)基本已经能够满足我们的需求。 + +即使是这样,你也千万不要就此认为元组是多余的。不然在面试中也不会经常被人问,元组和列表有啥区别?为什么需要元组? + +以下两点,第一点是大家所熟知的,而第二点可能只有老司机才会知道,只有学习了第二点,才算真正理解了元组存在的价值和意义。 + +## 2.6.1 不可变列表 + +这是`元组`区别于`列表`最显著的特征。 + +- list:可变的序列 +- tuple:不可变的序列 + +那什么是不可变的序列呢? + +那就是在元组对象生成后,诸如列表的`插入元素`、`删除元素`、`添加元素`、`清空元素`、`修改元素`等功能,在元组中通通没有,你是无法对其进行修改的。 + +由于元组是不可变的,所以其方法也是很有限的。这里罗列一下。 + +``` +# s1和s2都是元组 +s1=(1,2,3) +s2=(4,5,6) + +# 拼接生成新元组 +s1+s2 +s1.__add__(s2) + +# 是否包含 +2 in s1 +s1.__contains__(2) + +# 统计元素包含的次数 +s1.count(2) + +# 获取元素 +s1[0] +s1.__getitem__(0) + +# 找到2第一次出现的索引 +s1.index(2) + +# 获取长度 +len(s1) + +# 重复拼接 +s1*n +``` + + +## 2.6.2 具名元组 + +这个特性,我个人认为,才是元组存在的意义所在。 + +只讲 具名元组,可能不太好理解。如果称之为 `带字段名的记录`,你可能就清楚了。 + +这里举个例子,但是实现带字段名,需要一个库(collections)的支持,你需要导入它。 +```python +from collections import namedtuple +# 生成一个City类 +City = namedtuple("City", "name country polulation coordinates") +# 实例化 +tokyo = City("Tokyo", 'JP', '36.93', ('35.68','139,69')) + +print(tokyo) +# City(name='Tokyo', country='JP', polulation='36.93', coordinates=('35.68', '139,69')) + +print(tokyo.name) +# Tokyo +``` + +看着有点像字典,是不是,但是他不是字典(获取数值的方法也与字典不同),字典是可变。元组在创建后,就无法再对其进行修改。这在某个程度上说明元组适合存放那些无需修改的数据。比如上面的,地名,国家,经纬度。 + +除了上面的用法之处,这里还要介绍一些元组自己专有的属性。 +```python +# 打印字段名 +print(City._fields) +('name', 'country', 'polulation', 'coordinates') + +# 生成新实例 +LatLong = namedtuple('LatLong', 'lat long') +Xiamen_tuple = ('Xiemen', 'China', '40,54', LatLong(24.26,118.03)) +Xiamen = City._make(Xiamen_tuple) + +print(Xiamen) +# City(name='Xiemen', country='China', polulation='40,54', coordinates=(24.26, 118.03)) + +# 将具名元组转为OrderDict +Xiamen_dict = Xiamen._asdict() +print(Xiamen_dict) +# OrderedDict([('name', 'Xiemen'), ('country', 'China'), ('polulation', '40,54'), ('coordinates', LatLong(lat=24.26, long=118.03))]) +``` + +总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。 + +-------------- + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index a48254e..2e6fc0e 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -1,6 +1,8 @@ 1.6 深入理解元组存在的意义 ========================== +|image0| + -------------- Python中有一个基础的数据结构,叫做元组(tuple),但是一般挺少有人会去用它的,因为在开发过程中,列表(list)基本已经能够满足我们的需求。 @@ -108,3 +110,6 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_07.md b/source/c01/c01_07.md index 2c5e7f7..4c5647b 100644 --- a/source/c01/c01_07.md +++ b/source/c01/c01_07.md @@ -1,294 +1,296 @@ -# 1.7 15个Pythonic的代码示例 - ---- - -Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。 - -要写出 Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,Github 上有很多非常优秀的源代码值得阅读,比如:requests、flask、tornado,这里小明收集了一些常见的 Pythonic 写法,帮助你养成写优秀代码的习惯。 - -## 01. 变量交换 - -Bad -```python -tmp = a -a = b -b = tmp -``` -Pythonic -```python -a,b = b,a -``` - -## 02. 列表推导 - -Bad -```python -my_list = [] -for i in range(10): - my_list.append(i*2) -``` -Pythonic -```python -my_list = [i*2 for i in range(10)] -``` - -## 03. 单行表达式 - -虽然列表推导式由于其简洁性及表达性,被广受推崇。 - -但是有许多可以写成单行的表达式,并不是好的做法。 - -Bad -```python -print 'one'; print 'two' - -if x == 1: print 'one' - -if and : - # do something -``` - -Pythonic -```python -print 'one' -print 'two' - -if x == 1: - print 'one' - -cond1 = -cond2 = -if cond1 and cond2: - # do something -``` - -## 04. 带索引遍历 - -Bad -```python -for i in range(len(my_list)): - print(i, "-->", my_list[i]) -``` -Pythonic -```python -for i,item in enumerate(my_list): - print(i, "-->",item) -``` - -## 05. 序列解包 - -Pythonic -```python -a, *rest = [1, 2, 3] -# a = 1, rest = [2, 3] - -a, *middle, c = [1, 2, 3, 4] -# a = 1, middle = [2, 3], c = 4 -``` - -## 06. 字符串拼接 - -Bad -```python -letters = ['s', 'p', 'a', 'm'] -s="" -for let in letters: - s += let -``` - -Pythonic -```python -letters = ['s', 'p', 'a', 'm'] -word = ''.join(letters) -``` - -## 07. 真假判断 - -Bad -```python -if attr == True: - print 'True!' - -if attr == None: - print 'attr is None!' -``` - -Pythonic -```python -if attr: - print 'attr is truthy!' - -if not attr: - print 'attr is falsey!' - -if attr is None: - print 'attr is None!' -``` - - -## 08. 访问字典元素 - -Bad -```python -d = {'hello': 'world'} -if d.has_key('hello'): - print d['hello'] # prints 'world' -else: - print 'default_value' -``` - -Pythonic -```python -d = {'hello': 'world'} - -print d.get('hello', 'default_value') # prints 'world' -print d.get('thingy', 'default_value') # prints 'default_value' - -# Or: -if 'hello' in d: - print d['hello'] -``` - -## 09. 操作列表 - -Bad -```python -a = [3, 4, 5] -b = [] -for i in a: - if i > 4: - b.append(i) -``` - -Pythonic -```python -a = [3, 4, 5] -b = [i for i in a if i > 4] -# Or: -b = filter(lambda x: x > 4, a) -``` - -Bad -```python -a = [3, 4, 5] -for i in range(len(a)): - a[i] += 3 -``` - -Pythonic -```python -a = [3, 4, 5] -a = [i + 3 for i in a] -# Or: -a = map(lambda i: i + 3, a) -``` - -## 10. 文件读取 -Bad -```python -f = open('file.txt') -a = f.read() -print a -f.close() -``` - -Pythonic -```python -with open('file.txt') as f: - for line in f: - print line -``` - -## 11. 代码续行 -Bad -```python -my_very_big_string = """For a long time I used to go to bed early. Sometimes, \ - when I had put out my candle, my eyes would close so quickly that I had not even \ - time to say “I’m going to sleep.”""" - -from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \ - yet_another_nice_function -``` - -Pythonic -```python -my_very_big_string = ( - "For a long time I used to go to bed early. Sometimes, " - "when I had put out my candle, my eyes would close so quickly " - "that I had not even time to say “I’m going to sleep.”" -) - -from some.deep.module.inside.a.module import ( - a_nice_function, another_nice_function, yet_another_nice_function) -``` - -## 12. 显式代码 -Bad -```python -def make_complex(*args): - x, y = args - return dict(**locals()) -``` - -Pythonic -```python -def make_complex(x, y): - return {'x': x, 'y': y} -``` - -## 13. 使用占位符 - -Pythonic -```python -filename = 'foobar.txt' -basename, _, ext = filename.rpartition('.') -``` - -## 14. 链式比较 - -Bad -```python -if age > 18 and age < 60: - print("young man") -``` - -Pythonic -```python -if 18 < age < 60: - print("young man") -``` - -理解了链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False -``` ->>> False == False == True -False -``` - -## 15. 三目运算 - -这个保留意见。随使用习惯就好。 - -Bad -```python -if a > 2: - b = 2 -else: - b = 1 -#b = 2 - -``` -Pythonic -```python -a = 3 - -b = 2 if a > 2 else 1 -#b = 2 -``` - -## 参考文档 - -- http://docs.python-guide.org/en/latest/writing/style/ -- https://foofish.net/idiomatic_part2.html - ----- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +# 1.7 15个Pythonic的代码示例 + +![](http://image.iswbm.com/20200602135014.png) + +--- + +Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。 + +要写出 Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,Github 上有很多非常优秀的源代码值得阅读,比如:requests、flask、tornado,这里小明收集了一些常见的 Pythonic 写法,帮助你养成写优秀代码的习惯。 + +## 01. 变量交换 + +Bad +```python +tmp = a +a = b +b = tmp +``` +Pythonic +```python +a,b = b,a +``` + +## 02. 列表推导 + +Bad +```python +my_list = [] +for i in range(10): + my_list.append(i*2) +``` +Pythonic +```python +my_list = [i*2 for i in range(10)] +``` + +## 03. 单行表达式 + +虽然列表推导式由于其简洁性及表达性,被广受推崇。 + +但是有许多可以写成单行的表达式,并不是好的做法。 + +Bad +```python +print 'one'; print 'two' + +if x == 1: print 'one' + +if and : + # do something +``` + +Pythonic +```python +print 'one' +print 'two' + +if x == 1: + print 'one' + +cond1 = +cond2 = +if cond1 and cond2: + # do something +``` + +## 04. 带索引遍历 + +Bad +```python +for i in range(len(my_list)): + print(i, "-->", my_list[i]) +``` +Pythonic +```python +for i,item in enumerate(my_list): + print(i, "-->",item) +``` + +## 05. 序列解包 + +Pythonic +```python +a, *rest = [1, 2, 3] +# a = 1, rest = [2, 3] + +a, *middle, c = [1, 2, 3, 4] +# a = 1, middle = [2, 3], c = 4 +``` + +## 06. 字符串拼接 + +Bad +```python +letters = ['s', 'p', 'a', 'm'] +s="" +for let in letters: + s += let +``` + +Pythonic +```python +letters = ['s', 'p', 'a', 'm'] +word = ''.join(letters) +``` + +## 07. 真假判断 + +Bad +```python +if attr == True: + print 'True!' + +if attr == None: + print 'attr is None!' +``` + +Pythonic +```python +if attr: + print 'attr is truthy!' + +if not attr: + print 'attr is falsey!' + +if attr is None: + print 'attr is None!' +``` + + +## 08. 访问字典元素 + +Bad +```python +d = {'hello': 'world'} +if d.has_key('hello'): + print d['hello'] # prints 'world' +else: + print 'default_value' +``` + +Pythonic +```python +d = {'hello': 'world'} + +print d.get('hello', 'default_value') # prints 'world' +print d.get('thingy', 'default_value') # prints 'default_value' + +# Or: +if 'hello' in d: + print d['hello'] +``` + +## 09. 操作列表 + +Bad +```python +a = [3, 4, 5] +b = [] +for i in a: + if i > 4: + b.append(i) +``` + +Pythonic +```python +a = [3, 4, 5] +b = [i for i in a if i > 4] +# Or: +b = filter(lambda x: x > 4, a) +``` + +Bad +```python +a = [3, 4, 5] +for i in range(len(a)): + a[i] += 3 +``` + +Pythonic +```python +a = [3, 4, 5] +a = [i + 3 for i in a] +# Or: +a = map(lambda i: i + 3, a) +``` + +## 10. 文件读取 +Bad +```python +f = open('file.txt') +a = f.read() +print a +f.close() +``` + +Pythonic +```python +with open('file.txt') as f: + for line in f: + print line +``` + +## 11. 代码续行 +Bad +```python +my_very_big_string = """For a long time I used to go to bed early. Sometimes, \ + when I had put out my candle, my eyes would close so quickly that I had not even \ + time to say “I’m going to sleep.”""" + +from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \ + yet_another_nice_function +``` + +Pythonic +```python +my_very_big_string = ( + "For a long time I used to go to bed early. Sometimes, " + "when I had put out my candle, my eyes would close so quickly " + "that I had not even time to say “I’m going to sleep.”" +) + +from some.deep.module.inside.a.module import ( + a_nice_function, another_nice_function, yet_another_nice_function) +``` + +## 12. 显式代码 +Bad +```python +def make_complex(*args): + x, y = args + return dict(**locals()) +``` + +Pythonic +```python +def make_complex(x, y): + return {'x': x, 'y': y} +``` + +## 13. 使用占位符 + +Pythonic +```python +filename = 'foobar.txt' +basename, _, ext = filename.rpartition('.') +``` + +## 14. 链式比较 + +Bad +```python +if age > 18 and age < 60: + print("young man") +``` + +Pythonic +```python +if 18 < age < 60: + print("young man") +``` + +理解了链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False +``` +>>> False == False == True +False +``` + +## 15. 三目运算 + +这个保留意见。随使用习惯就好。 + +Bad +```python +if a > 2: + b = 2 +else: + b = 1 +#b = 2 + +``` +Pythonic +```python +a = 3 + +b = 2 if a > 2 else 1 +#b = 2 +``` + +## 参考文档 + +- http://docs.python-guide.org/en/latest/writing/style/ +- https://foofish.net/idiomatic_part2.html + +---- +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index d9166e4..61a0ef8 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -1,6 +1,8 @@ 1.7 15个Pythonic的代码示例 ========================== +|image0| + -------------- Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。 @@ -352,3 +354,6 @@ Pythonic :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_08.md b/source/c01/c01_08.md index 619365f..30a9956 100644 --- a/source/c01/c01_08.md +++ b/source/c01/c01_08.md @@ -1,5 +1,7 @@ # 1.8 新式类和经典类的区别? +![](http://image.iswbm.com/20200602135014.png) + --- ## 2.8.1 版本支持 / 写法差异 diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 4df2492..2b0036a 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -1,6 +1,8 @@ 1.8 新式类和经典类的区别? ========================== +|image0| + -------------- 2.8.1 版本支持 / 写法差异 @@ -46,7 +48,7 @@ 2.8.2 使用方法 / 独特属性 ------------------------- -经典类无法使用super() |image0| 经典类的类型是 classobj |image1| +经典类无法使用super() |image1| 经典类的类型是 classobj |image2| 新式类的类型是 type,保持class与type的统一。 @@ -114,7 +116,7 @@ 非常好理解,但是在菱形继承时,方法的调用会出现问题。 -|image2| +|image3| 假设 d 是 D 的一个实例,那么执行 d.show()是调用 A.show() 呢 还是调用 C.show()呢? @@ -132,7 +134,7 @@ A.show(),这显然是不合理的。所以这才有了后来的一步一步优 Python 2.2 的新式类 MRO 计算方式和经典类 MRO 的计算方式非常相似:它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。重新考虑上面「菱形继承」的例子: -|image3| +|image4| 同样地,我们也来验证一下。另说明,在新式类中,除用inspect外,可以直接通过__mro__属性获取类的 MRO。 @@ -163,7 +165,7 @@ MRO。 再来看一个复杂一点的例子。 -|image4| +|image5| 如果只依靠上面的算法,我们来一起算下,其继承关系是怎样的。 @@ -209,7 +211,7 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 例如下面这张图。 -|image5| +|image6| 计算过程,会采用一种 merge算法。它的基本思想如下: @@ -274,10 +276,11 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 关注公众号,获取最新干货! -.. |image0| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg -.. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg -.. |image2| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi77urc3lj206108n74e.jpg -.. |image3| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg -.. |image4| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg -.. |image5| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg +.. |image2| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg +.. |image3| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi77urc3lj206108n74e.jpg +.. |image4| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg +.. |image5| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg +.. |image6| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg diff --git a/source/c01/c01_09.md b/source/c01/c01_09.md index b9d825b..2bd4784 100644 --- a/source/c01/c01_09.md +++ b/source/c01/c01_09.md @@ -1,5 +1,7 @@ # 1.9 多继承与Mixin设计模式 +![](http://image.iswbm.com/20200602135014.png) + --- 类的单继承,是我们再熟悉不过的,写起来也毫不费力。而多继承呢,见得很多,写得很少。在很多的项目代码里,你还会见到一种很奇怪的类,他们有一个命名上的共同点,就是在类名的结尾,都喜欢用 Mixin。 diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index 32fe3bd..2ff4861 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -1,6 +1,8 @@ 1.9 多继承与Mixin设计模式 ========================= +|image0| + -------------- 类的单继承,是我们再熟悉不过的,写起来也毫不费力。而多继承呢,见得很多,写得很少。在很多的项目代码里,你还会见到一种很奇怪的类,他们有一个命名上的共同点,就是在类名的结尾,都喜欢用 @@ -77,3 +79,6 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index 3c5fc47..97ce7f3 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -1,5 +1,7 @@ # 1.10 Python 黑魔法指南 50 例 +![](http://image.iswbm.com/20200602135014.png) + --- > 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index cf3d866..1365f15 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1,6 +1,8 @@ 1.10 Python 黑魔法指南 50 例 ============================ +|image0| + -------------- 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 @@ -471,7 +473,7 @@ Python 中的 def 的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list 会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。 -|image0| +|image1| 12. 访问类中的私有方法 ---------------------- @@ -887,7 +889,7 @@ SimpleHTTPServer是Python # python3 python3 -m http.server 8888 -|image1| +|image2| SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 @@ -990,7 +992,7 @@ break,不抛出异常,就可以走else。 因此 对于 ``aabb`` 这个字符串在 Python 来看应该是这样的 -|image2| +|image3| 理解了这个“**缝隙**” 的概念后,以下这些就好理解了。 @@ -1149,7 +1151,7 @@ import 是 Python 导包的方式。 >>> import antigravity -就会自动打开一个网页。 |image3| +就会自动打开一个网页。 |image4| 30. 局部/全局变量傻傻分不清 --------------------------- @@ -1230,11 +1232,11 @@ import 是 Python 导包的方式。 什么?没有截图你不信? -|image4| +|image5| 如果你在自己的电脑上尝试一下,结果可能是这样的 -|image5| +|image6| **怎么又好了呢?** @@ -1710,7 +1712,7 @@ heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得 示例如下 -|image6| +|image7| 如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。 @@ -2096,7 +2098,7 @@ Python 功力。 稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python`` 进入console 模式里的 ``python`` -|image7| +|image8| 而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python`` ,意思就是说你得用哪个软件 (python)来执行这个文件。 @@ -2105,20 +2107,20 @@ Python 功力。 不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` , -|image8| +|image9| 有没有一种方式?可以省去每次都加 ``python`` 呢? 当然有,你可以文件头里加上\ ``#!/usr/bin/python`` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 -|image9| +|image10| 明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? 当我执行 ``env python`` 时,自动进入了 python console 的模式。 -|image10| +|image11| 这是为什么?和 直接执行 python 好像没什么区别呀 @@ -2133,7 +2135,7 @@ Python 功力。 具体演示过程,你可以看下面。 -|image11| +|image12| 那么对于这两者,我们应该使用哪个呢? @@ -2324,16 +2326,17 @@ python 解释器都是 ``/usr/bin/python`` 。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511165650.png -.. |image1| image:: http://image.python-online.cn/20190511165716.png -.. |image2| image:: http://image.iswbm.com/20200509172331.png -.. |image3| image:: http://image.python-online.cn/20190511165735.png -.. |image4| image:: http://image.iswbm.com/20200509122954.png -.. |image5| image:: http://image.iswbm.com/20200509123107.png -.. |image6| image:: http://image.iswbm.com/20200510112133.png -.. |image7| image:: http://image.python-online.cn/20200331184021.png -.. |image8| image:: http://image.python-online.cn/20200331185034.png -.. |image9| image:: http://image.python-online.cn/20200331184755.png -.. |image10| image:: http://image.python-online.cn/20200331185741.png -.. |image11| image:: http://image.python-online.cn/20200331190224.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511165650.png +.. |image2| image:: http://image.python-online.cn/20190511165716.png +.. |image3| image:: http://image.iswbm.com/20200509172331.png +.. |image4| image:: http://image.python-online.cn/20190511165735.png +.. |image5| image:: http://image.iswbm.com/20200509122954.png +.. |image6| image:: http://image.iswbm.com/20200509123107.png +.. |image7| image:: http://image.iswbm.com/20200510112133.png +.. |image8| image:: http://image.python-online.cn/20200331184021.png +.. |image9| image:: http://image.python-online.cn/20200331185034.png +.. |image10| image:: http://image.python-online.cn/20200331184755.png +.. |image11| image:: http://image.python-online.cn/20200331185741.png +.. |image12| image:: http://image.python-online.cn/20200331190224.png diff --git a/source/c01/c01_11.md b/source/c01/c01_11.md index 57d427d..a934919 100644 --- a/source/c01/c01_11.md +++ b/source/c01/c01_11.md @@ -1,5 +1,7 @@ # 1.11 正则表达式必知必会 +![](http://image.iswbm.com/20200602135014.png) + --- ## 一、正则表达式先导 diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index ca3cb4c..1b79c43 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -1,6 +1,8 @@ 1.11 正则表达式必知必会 ======================= +|image0| + -------------- 一、正则表达式先导 @@ -348,3 +350,6 @@ start(),end(),end() :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_12.md b/source/c01/c01_12.md index 0ed9f6f..e8a368c 100644 --- a/source/c01/c01_12.md +++ b/source/c01/c01_12.md @@ -1,5 +1,7 @@ # 1.12 搞懂字符编码的前世今生 +![](http://image.iswbm.com/20200602135014.png) + --- 初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码? 这些内容是在去年整理的,现在重新整理下,发布在博客,搞懂字符串编码,这一篇文章足矣 diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index b9736b4..85837f5 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -1,6 +1,8 @@ 1.12 搞懂字符编码的前世今生 =========================== +|image0| + -------------- 初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码? @@ -95,11 +97,11 @@ TransferFormat,即把Unicode转做某种格式的意思)应运而生 **扩展问题** - UTF-8有用一个字节表示,有用两个字节表示,读取数据,如何识别是几个字节表示一个字符? - |image0| + |image1| - ‘联通’显示乱码 当在txt输入输入’联通’,保存再次打开就乱码,输入’你好联通’就不会出现这个情况。 - |image1| + |image2| 1.12.6 编码之于Python --------------------- @@ -159,6 +161,7 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2017/08/02/598168fe2b016.png -.. |image1| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2017/08/02/598168fe2b016.png +.. |image2| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png diff --git a/source/c01/c01_13.md b/source/c01/c01_13.md index aab1c8b..c56cf93 100644 --- a/source/c01/c01_13.md +++ b/source/c01/c01_13.md @@ -1,5 +1,7 @@ # 1.13 Python几个高阶函数 +![](http://image.iswbm.com/20200602135014.png) + --- ## 1.13.1 lambda 表达式 diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index 2060038..bfdd509 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -1,6 +1,8 @@ 1.13 Python几个高阶函数 ======================= +|image0| + -------------- 1.13.1 lambda 表达式 @@ -162,5 +164,6 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 关注公众号,获取最新干货! +.. |image0| image:: http://image.iswbm.com/20200602135014.png .. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg diff --git a/source/c01/c01_14.md b/source/c01/c01_14.md index cf90646..4b0fa4f 100644 --- a/source/c01/c01_14.md +++ b/source/c01/c01_14.md @@ -1,5 +1,7 @@ # 1.14 with 与 上下文管理器 +![](http://image.iswbm.com/20200602135014.png) + > 提示:前面的内容较为基础,重点知识在后半段。 `with` 这个关键字,对于每一学习Python的人,都不会陌生。 diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index 263231c..518b8bc 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -1,6 +1,8 @@ 1.14 with 与 上下文管理器 ========================= +|image0| + 提示:前面的内容较为基础,重点知识在后半段。 ``with`` 这个关键字,对于每一学习Python的人,都不会陌生。 @@ -199,7 +201,7 @@ open)的上下文管理器。 代码是这样的 -|image0| +|image1| 总结起来,使用上下文管理器有三个好处: @@ -214,5 +216,6 @@ open)的上下文管理器。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190310172800.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190310172800.png diff --git a/source/c01/c01_15.md b/source/c01/c01_15.md index fa232e8..2fa8af3 100644 --- a/source/c01/c01_15.md +++ b/source/c01/c01_15.md @@ -1,5 +1,7 @@ # 1.15 提升Python性能的7个习惯 +![](http://image.iswbm.com/20200602135014.png) + --- > 转载自:https://zhuanlan.zhihu.com/p/38160586 diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index d4a43ca..6071011 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -1,6 +1,8 @@ 1.15 提升Python性能的7个习惯 ============================ +|image0| + -------------- 转载自:https://zhuanlan.zhihu.com/p/38160586 @@ -82,3 +84,6 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_16.md b/source/c01/c01_16.md index 576eedf..687de84 100644 --- a/source/c01/c01_16.md +++ b/source/c01/c01_16.md @@ -1,5 +1,7 @@ # 1.16 泛型函数怎么写? +![](http://image.iswbm.com/20200602135014.png) + 泛型,如果你尝过java,应该对他不陌生吧。但你可能不知道在 Python 中(3.4+ ),也可以实现 简单的泛型函数。 在Python中只能实现基于单个(第一个)参数的数据类型来选择具体的实现方式,官方名称 是 `single-dispatch`。你或许听不懂,说人话,就是可以实现第一个参数的数据类型不同,其调用的函数也就不同。 diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst index ab6c40b..54e51e1 100644 --- a/source/c01/c01_16.rst +++ b/source/c01/c01_16.rst @@ -1,6 +1,8 @@ 1.16 泛型函数怎么写? ===================== +|image0| + 泛型,如果你尝过java,应该对他不陌生吧。但你可能不知道在 Python 中(3.4+ ),也可以实现 简单的泛型函数。 @@ -170,3 +172,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md index 84c494e..1b9ca39 100644 --- a/source/c01/c01_17.md +++ b/source/c01/c01_17.md @@ -1,5 +1,7 @@ # 1.17 深入理解「描述符」 +![](http://image.iswbm.com/20200602135014.png) + 学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Descriptor(描述符)特性可以排得上号。 描述符 是Python 语言独有的特性,它不仅在应用层使用,在语言的基础设施中也有涉及。 diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index f8bf4ac..d394bd4 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -1,6 +1,8 @@ 1.17 深入理解「描述符」 ======================= +|image0| + 学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Descriptor(描述符)特性可以排得上号。 @@ -72,7 +74,7 @@ Python 给我们带来的便利与优雅。 这下程序稍微有点人工智能了,能够自己明辨是非了。 -|image0| +|image1| 程序是智能了,但在\ ``__init__``\ 里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过 Property @@ -127,7 +129,7 @@ Property 程序还是一样的人工智能,非常好。 -|image1| +|image2| 你以为你写的代码,已经非常优秀,无懈可击了。 @@ -197,7 +199,7 @@ math、chinese、english这三个属性的时候,都会经过 Score 实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等) -|image2| +|image3| 以上,我举了下具体的实例,从最原始的编码风格到 Property ,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 @@ -468,7 +470,7 @@ property 其实就相当于一个描述符类,而\ ``myfunc`` 在此刻变成了一个描述符。关于 ``staticmethod`` 的实现,你可以参照下面这段我自己写的代码,加以理解。 -|image3| +|image4| 调用这个方法可以知道,每调用一次,它都会经过描述符类的 ``__get__`` 。 @@ -582,8 +584,9 @@ super 的实现原理,就交由你来自己完成。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190425221322.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190425221322.png -.. |image2| image:: http://image.python-online.cn/20190425221233.png -.. |image3| image:: http://image.python-online.cn/20190519001930.png +.. |image2| image:: http://image.python-online.cn/20190425221322.png +.. |image3| image:: http://image.python-online.cn/20190425221233.png +.. |image4| image:: http://image.python-online.cn/20190519001930.png diff --git a/source/c01/c01_18.md b/source/c01/c01_18.md index 1a63ec8..d56d934 100644 --- a/source/c01/c01_18.md +++ b/source/c01/c01_18.md @@ -1,5 +1,7 @@ # 1.18 MySQL 使用总结 +![](http://image.iswbm.com/20200602135014.png) + ## 1.18.1 安装MySQL-python MySQL-python 这玩意实在是太难装了,为了以防后面再踩坑,这里还是记录一下吧。 diff --git a/source/c01/c01_18.rst b/source/c01/c01_18.rst index 0ba5e6b..0b18f82 100644 --- a/source/c01/c01_18.rst +++ b/source/c01/c01_18.rst @@ -1,6 +1,8 @@ 1.18 MySQL 使用总结 =================== +|image0| + 1.18.1 安装MySQL-python ----------------------- @@ -58,26 +60,26 @@ mysql,这时也请将其卸载再重新安装吧。 经过漫长的等待后,mysql 终于安装成功 -|image0| +|image1| 这时候,再 执行 pip install MySQL-python,发现还是报错。 -|image1| +|image2| 有经验的我,立马知道了 ``mysql_config`` 这个文件的路径可能没有在环境变量中。 -|image2| +|image3| 然后,我又重新执行 ``pip install MySQL-python`` ,发现还是报错。 -|image3| +|image4| 但是这个错误相对比较明显,明眼人一看就知道是权限不足。 那我就以 root 权限去安装好了。 -|image4| +|image5| 终于安装成功,折腾了两个晚上(主要是网速慢)。 @@ -111,7 +113,7 @@ mysql,这时也请将其卸载再重新安装吧。 选择密码强度,视情况而写,我这边选最强的,长度大于8,有数字,有大小写,有特殊字符。 -|image5| +|image6| 接下来还会问你,是否删除其他匿名用户,是否删除 test 数据库,是否允许远程使用root登陆(安全起见我选不允许)。 @@ -152,13 +154,14 @@ mysql,这时也请将其卸载再重新安装吧。 1.18.4 命令行使用技巧 --------------------- -|image6| +|image7| -.. |image0| image:: http://image.python-online.cn/20190615001340.png -.. |image1| image:: http://image.python-online.cn/20190615001414.png -.. |image2| image:: http://image.python-online.cn/20190615001633.png -.. |image3| image:: http://image.python-online.cn/20190615001706.png -.. |image4| image:: http://image.python-online.cn/20190615001908.png -.. |image5| image:: http://image.python-online.cn/20190615112422.png -.. |image6| image:: http://image.python-online.cn/20190705225651.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190615001340.png +.. |image2| image:: http://image.python-online.cn/20190615001414.png +.. |image3| image:: http://image.python-online.cn/20190615001633.png +.. |image4| image:: http://image.python-online.cn/20190615001706.png +.. |image5| image:: http://image.python-online.cn/20190615001908.png +.. |image6| image:: http://image.python-online.cn/20190615112422.png +.. |image7| image:: http://image.python-online.cn/20190705225651.png diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md index f70e790..e36aa12 100644 --- a/source/c01/c01_20.md +++ b/source/c01/c01_20.md @@ -1,5 +1,7 @@ # 1.20 静态方法其实暗藏玄机 +![](http://image.iswbm.com/20200602135014.png) + 这个标题「**静态方法其实暗藏玄机**」其实只是该文章的一个知识点。或许有些标题党,但没有关系,我相信有不少人对此并没有深入研究他们,不信我问你三个问题,你看能否答上来。 1、Python2.x和3.x中,函数和方法的区分有什么不同? diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index 08c279b..117c9ec 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -1,6 +1,8 @@ 1.20 静态方法其实暗藏玄机 ========================= +|image0| + 这个标题「\ **静态方法其实暗藏玄机**\ 」其实只是该文章的一个知识点。或许有些标题党,但没有关系,我相信有不少人对此并没有深入研究他们,不信我问你三个问题,你看能否答上来。 1、Python2.x和3.x中,函数和方法的区分有什么不同? @@ -18,7 +20,7 @@ Python3中,我却发现完全又是另一套准则。 首先先来 Python2 的(以下在 Python2.7中测试通过) -|image0| +|image1| 可以得出结论: @@ -57,7 +59,7 @@ Python3,我反而觉得Python3里方法和函数的区分似乎更加合理。 还是刚刚那段代码,我更改了解释器为Python3.6(以下在 Python3.6中测试通过) -|image1| +|image2| 和Python2的唯一区别是,\ ``People.jump`` 在Python3 中变成了函数。 @@ -69,14 +71,14 @@ Python3.6中测试通过) 执行People.jump(‘hello’),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。 -|image2| +|image3| **在 Python3中** 你可以发现,这里的jump的首参不再要求是 People 的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。 -|image3| +|image4| 也就是说,当你往jump中传入的首参为People的实例时,jump 就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。 @@ -110,8 +112,9 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190630111243.png -.. |image1| image:: http://image.python-online.cn/20190630104956.png -.. |image2| image:: http://image.python-online.cn/20190630105735.png -.. |image3| image:: http://image.python-online.cn/20190630105600.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190630111243.png +.. |image2| image:: http://image.python-online.cn/20190630104956.png +.. |image3| image:: http://image.python-online.cn/20190630105735.png +.. |image4| image:: http://image.python-online.cn/20190630105600.png diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md index 38d4e7c..1ebc32e 100644 --- a/source/c01/c01_21.md +++ b/source/c01/c01_21.md @@ -1,5 +1,7 @@ # 1.21 开发小技巧 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 解决网页鼠标限制 ``` diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index aab9498..fea0d27 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -1,6 +1,8 @@ 1.21 开发小技巧 =============== +|image0| + 1. 解决网页鼠标限制 ------------------- @@ -93,3 +95,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_22.md b/source/c01/c01_22.md index 448951a..6f66b59 100644 --- a/source/c01/c01_22.md +++ b/source/c01/c01_22.md @@ -1,5 +1,7 @@ # 1.22 如何修改 CentOS 6.x 上默认Python +![](http://image.iswbm.com/20200602135014.png) + 最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 CentOS 6.x,有的是 CentOS 7.x 。 需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x 上的 Python 版本是 2.7.x 的,这意味着我要实现的功能要适配这两种版本的系统。 diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index 80b32d3..09a4c53 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -1,6 +1,8 @@ 1.22 如何修改 CentOS 6.x 上默认Python ===================================== +|image0| + 最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 CentOS 6.x,有的是 CentOS 7.x 。 @@ -168,7 +170,7 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip 的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ 目录下 -|image0| +|image1| 然后安装一些 cloudinit 的依赖包。 @@ -202,5 +204,6 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190831160317.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190831160317.png diff --git a/source/c01/c01_23.md b/source/c01/c01_23.md index 82e6a09..b1f01b4 100644 --- a/source/c01/c01_23.md +++ b/source/c01/c01_23.md @@ -1,5 +1,7 @@ # 1.23 Pythonista 学习 Js +![](http://image.iswbm.com/20200602135014.png) + 1. JavaScript的设计者希望用`null`表示一个空的值,而`undefined`表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用`null`。`undefined`仅仅在判断函数参数是否传递的情况下有用。 diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index 19f5b35..49bd71c 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -1,6 +1,8 @@ 1.23 Pythonista 学习 Js ======================= +|image0| + 1. JavaScript的设计者希望用\ ``null``\ 表示一个空的值,而\ ``undefined``\ 表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用\ ``null``\ 。\ ``undefined``\ 仅仅在判断函数参数是否传递的情况下有用。 2. 在JavaScript中,使用等号\ ``=``\ 对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用\ ``var``\ 申明一次。 @@ -122,3 +124,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md index dc08028..9a48f7c 100644 --- a/source/c01/c01_24.md +++ b/source/c01/c01_24.md @@ -1,5 +1,7 @@ # 1.24 深入探讨 Python 的 import 机制:实现远程导入模块 +![](http://image.iswbm.com/20200602135014.png) + 所谓的模块导入( `import` ),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。 在 Python 中使用 import 关键字来实现这个操作,但不是唯一的方法,还有 `importlib.import_module()` 和 `__import__()` 等。 diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index eca008a..b24758a 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -1,6 +1,8 @@ 1.24 深入探讨 Python 的 import 机制:实现远程导入模块 ===================================================== +|image0| + 所谓的模块导入( ``import`` ),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。 @@ -14,7 +16,7 @@ 当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。 -|image0| +|image1| 1. 导入系统的基础 ----------------- @@ -798,5 +800,6 @@ sys.path)查找器 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20191027192949.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191027192949.png diff --git a/source/c01/c01_25.md b/source/c01/c01_25.md index dddcda6..7f7a1bf 100644 --- a/source/c01/c01_25.md +++ b/source/c01/c01_25.md @@ -1,5 +1,7 @@ # 1.25 50% 的人不知道的Python 包与模块的知识盲区 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 使用 \__all__ 控制可被导入的变量 使用 `from module import *` 默认情况下会导入 module 里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用 `__all__` 来控制想要被其他模块导入的变量。 diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index 846efa9..f9d887c 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -1,6 +1,8 @@ 1.25 50% 的人不知道的Python 包与模块的知识盲区 ============================================== +|image0| + 1. 使用 \__all_\_ 控制可被导入的变量 ------------------------------------ @@ -109,3 +111,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md index c94c65c..8096211 100644 --- a/source/c01/c01_26.md +++ b/source/c01/c01_26.md @@ -1,5 +1,7 @@ # 1.26 C语言基础的学习 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 安装编译器 C 语言编译器用于把源代码编译成最终的可执行程序。这里假设您已经对编程语言编译器有基本的了解了。 diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index 4f32dcb..878caea 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -1,6 +1,8 @@ 1.26 C语言基础的学习 ==================== +|image0| + 1. 安装编译器 ------------- @@ -286,3 +288,6 @@ getchar() & putchar() :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index f440996..a0d4f7c 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -1,5 +1,7 @@ # 1.27 全面学习 Python 包:包的构建与分发 +![](http://image.iswbm.com/20200602135014.png) + > 首发于公众号:Python编程时光 diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 3e69eb0..f5604c8 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -1,6 +1,8 @@ 1.27 全面学习 Python 包:包的构建与分发 ======================================= +|image0| + 首发于公众号:Python编程时光 1. 为什么需要对项目分发打包? @@ -163,7 +165,7 @@ Python 包的分发可以分为两种: 源码包的本质是一个压缩包,其常见的格式有: -|image0| +|image1| 2. 以二进制包形式发布 @@ -173,7 +175,7 @@ Python 包的分发可以分为两种: 二进制包的常见格式有: -|image1| +|image2| 6. eggs 与 wheels 有什么区别? ------------------------------ @@ -478,7 +480,7 @@ setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数: -|image2| +|image3| 更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html @@ -572,7 +574,7 @@ pypi.python.org/simple/ 创建一个压缩的tarball和一个zip文件。可用格式为: -|image3| +|image4| 对以上的格式,有几点需要注意一下: @@ -688,8 +690,9 @@ Index)上,它是 Python 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20191218202833.png -.. |image1| image:: http://image.python-online.cn/20191218203005.png -.. |image2| image:: http://image.python-online.cn/20191218203255.png -.. |image3| image:: http://image.python-online.cn/20191218203517.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191218202833.png +.. |image2| image:: http://image.python-online.cn/20191218203005.png +.. |image3| image:: http://image.python-online.cn/20191218203255.png +.. |image4| image:: http://image.python-online.cn/20191218203517.png diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md index 2657d2a..264d2c7 100644 --- a/source/c01/c01_29.md +++ b/source/c01/c01_29.md @@ -1,5 +1,7 @@ # 1.27 如何阅读 CPython源码? +![](http://image.iswbm.com/20200602135014.png) + 参考学习地址:https://realpython.com/cpython-source-code-guide/ diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index 9004d6b..71180f4 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -1,6 +1,8 @@ 1.27 如何阅读 CPython源码? =========================== +|image0| + 参考学习地址:https://realpython.com/cpython-source-code-guide/ 基于 Python 3.6 的源码分析:https://he11olx.com/ @@ -9,3 +11,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md index f319646..27e122a 100644 --- a/source/c01/c01_30.md +++ b/source/c01/c01_30.md @@ -1,5 +1,7 @@ # 1.30 盘点程序员学习编程的那些网站 +![](http://image.iswbm.com/20200602135014.png) + ## 书栈网 **网站链接**:https://www.bookstack.cn/rank?tab=popular diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index 0e34aaf..84ea716 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -1,26 +1,28 @@ 1.30 盘点程序员学习编程的那些网站 ================================= +|image0| + 书栈网 ------ **网站链接**\ :https://www.bookstack.cn/rank?tab=popular -|image0| +|image1| 魔法学院 -------- **网站链接**\ :http://www.nowamagic.net/academy/ -|image1| +|image2| Python 3 标准库实例教程 ----------------------- **网站链接**\ :https://learnku.com/docs/pymotw -|image2| +|image3| Django Web 框架 --------------- @@ -29,15 +31,16 @@ Django Web 框架 该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django) -|image3| +|image4| 在服务端网页编程里,重点介绍了 Django -|image4| +|image5| -.. |image0| image:: http://image.python-online.cn/20200104144109.png -.. |image1| image:: http://image.python-online.cn/20200112210558.png -.. |image2| image:: http://image.iswbm.com/20200508201333.png -.. |image3| image:: http://image.iswbm.com/20200525080531.png -.. |image4| image:: http://image.iswbm.com/20200525080715.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20200104144109.png +.. |image2| image:: http://image.python-online.cn/20200112210558.png +.. |image3| image:: http://image.iswbm.com/20200508201333.png +.. |image4| image:: http://image.iswbm.com/20200525080531.png +.. |image5| image:: http://image.iswbm.com/20200525080715.png diff --git a/source/c01/c01_31.md b/source/c01/c01_31.md index 71656f0..ca530b8 100644 --- a/source/c01/c01_31.md +++ b/source/c01/c01_31.md @@ -1,5 +1,7 @@ # 1.31 学习 Pillow 笔记 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 安装 pillow diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index 5b12101..a84d9b5 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -1,6 +1,8 @@ 1.31 学习 Pillow 笔记 ===================== +|image0| + 1. 安装 pillow -------------- @@ -21,3 +23,6 @@ RGB :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md index 77da3e4..f3471bb 100644 --- a/source/c01/c01_32.md +++ b/source/c01/c01_32.md @@ -1,5 +1,7 @@ # 1.32 在 CentOS 7.2 上安装 Python3.7 +![](http://image.iswbm.com/20200602135014.png) + 首先下载 python3.7的源码包,然后解压 ```shell diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst index 4cebca3..d3e5c46 100644 --- a/source/c01/c01_32.rst +++ b/source/c01/c01_32.rst @@ -1,6 +1,8 @@ 1.32 在 CentOS 7.2 上安装 Python3.7 =================================== +|image0| + 首先下载 python3.7的源码包,然后解压 .. code:: shell @@ -35,3 +37,6 @@ requests.get(“https://www.baidu.com”) :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_33.md b/source/c01/c01_33.md index e6f4756..96fbc6b 100644 --- a/source/c01/c01_33.md +++ b/source/c01/c01_33.md @@ -1,5 +1,7 @@ # 1.33 如何调试已经运行中的程序 +![](http://image.iswbm.com/20200602135014.png) + 官方原始wiki:https://wiki.python.org/moin/DebuggingWithGdb 在CentOS 下,安装包过程,官方给的不够详细。这里记录一下 diff --git a/source/c01/c01_33.rst b/source/c01/c01_33.rst index bfe6e11..9fec603 100644 --- a/source/c01/c01_33.rst +++ b/source/c01/c01_33.rst @@ -1,6 +1,8 @@ 1.33 如何调试已经运行中的程序 ============================= +|image0| + 官方原始wiki:https://wiki.python.org/moin/DebuggingWithGdb 在CentOS 下,安装包过程,官方给的不够详细。这里记录一下 @@ -48,3 +50,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_34.md b/source/c01/c01_34.md index 45a5b29..5a47f3e 100644 --- a/source/c01/c01_34.md +++ b/source/c01/c01_34.md @@ -1,5 +1,7 @@ # 1.34 每日一库:sh,最优雅的命令调用方式 +![](http://image.iswbm.com/20200602135014.png) + 在编写 Python 脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 os.popen,os.system,commands,还有 subprocess。 今天明哥要介绍一种更加优雅的方法,就是 `sh` 这个第三方库,它能让你像调用方法那样去调用系统中的命令。 diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index d1ecce9..761febe 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -1,6 +1,8 @@ 1.34 每日一库:sh,最优雅的命令调用方式 ======================================= +|image0| + 在编写 Python 脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 os.popen,os.system,commands,还有 subprocess。 @@ -21,7 +23,7 @@ Python 3 为例。 Windows 使用,它推荐你使用它的 兄弟库 - ``pbs`` (https://pypi.org/project/pbs/)。 -|image0| +|image1| 安装完成后,就可以直接使用它了,以下几个示例,非常简单,简单到我感觉只要 demo ,而不需要任何的中文解释就可以让你知道他是如何使用的。 @@ -93,5 +95,6 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20200227201644.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20200227201644.png diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md index 594e430..68322ec 100644 --- a/source/c01/c01_35.md +++ b/source/c01/c01_35.md @@ -1,5 +1,7 @@ # 1.35 使用 Python 远程登陆服务器的利器 +![](http://image.iswbm.com/20200602135014.png) + 在使用 Python 写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 在 shell 环境中,我们是这样子做的。 diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index 0ed5503..6b5048a 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -1,6 +1,8 @@ 1.35 使用 Python 远程登陆服务器的利器 ===================================== +|image0| + 在使用 Python 写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 @@ -132,7 +134,7 @@ os.popen,os.system,commands,subprocess 等一些命令执行库来间接 2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 ``sh.ssh`` 的人并不多,于是我又“追问”了下,期望能得到回复。 -|image0| +|image1| 以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 @@ -169,7 +171,7 @@ top 命令看到已连接的终端的变化,会先 ``+1`` 再 你得使用它的兄弟库 - ``pbs`` ,然后我又去 pypi 看了一眼 `pbs `__\ ,已经 “年久失修”,没人维护了。 -|image1| +|image2| 至此,我离 “卒”,就差最后一根稻草了。 @@ -334,7 +336,7 @@ Windows,这里就有一件好事,一件坏事了,。 坏事就是:你需要做很多复杂的准备,你可 google 解决,但是我建议你直接放弃,坑太深了。 -|image2| +|image3| 注意事项 ~~~~~~~~ @@ -398,7 +400,8 @@ Windows,这里就有一件好事,一件坏事了,。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20200228085749.png -.. |image1| image:: http://image.python-online.cn/20200228093627.png -.. |image2| image:: http://image.python-online.cn/20200228111654.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20200228085749.png +.. |image2| image:: http://image.python-online.cn/20200228093627.png +.. |image3| image:: http://image.python-online.cn/20200228111654.png diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md index 6b4f513..e3d3536 100644 --- a/source/c01/c01_36.md +++ b/source/c01/c01_36.md @@ -1,5 +1,7 @@ # 1.36 每日一库:pretty_errors 解决bug 洁癖 +![](http://image.iswbm.com/20200602135014.png) + 当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 就像这样子,天呐,密集恐惧症要犯了都 diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst index f137980..c9cd9a7 100644 --- a/source/c01/c01_36.rst +++ b/source/c01/c01_36.rst @@ -1,12 +1,14 @@ 1.36 每日一库:pretty_errors 解决bug 洁癖 ========================================= +|image0| + 当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 就像这样子,天呐,密集恐惧症要犯了都 -|image0| +|image1| 上面这段 traceback @@ -45,18 +47,18 @@ 随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 -|image1| +|image2| 而使用了 pretty_errors 后,报错信息被美化成这样了。 -|image2| +|image3| 是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? 当然这段代码少,你可能还没感受到,那就来看下 该项目在 Github上的一张效果对比图吧 -|image3| +|image4| 3. 配置全局可用 --------------- @@ -79,23 +81,23 @@ traceback 输出都自动美化。 $ python3 -m pretty_errors -|image4| +|image5| 配置完成后,你再运行任何脚本,traceback 都会自动美化了。 不仅是在我的 iTerm 终端下 -|image5| +|image6| 在 PyCharm 中也会 -|image6| +|image7| 唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 ``文件路径`` 直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 下面自定义配置的方案解决这个问题(下面会讲到,参数是:\ ``display_link``\ )。 -|image7| +|image8| 因此,有些情况下,你并不想设置 ``pretty_errors`` 全局可用。 @@ -103,7 +105,7 @@ traceback 输出都自动美化。 只需要再次输出 ``python -m pretty_errors``\ ,输出入 ``C`` 即可清除。 -|image8| +|image9| 4. 单文件中使用 --------------- @@ -177,7 +179,7 @@ traceback 输出都自动美化。 在你像上面这样使用 ``pretty_errrs.configure`` 进行配置时,抛出的的异常信息就变成这样了。 -|image9| +|image10| 当然了,\ ``pretty_errors.configure()`` 还可以接收很多的参数,你可以根据你自己的需要进行配置。 @@ -210,7 +212,7 @@ traceback 输出都自动美化。 其中,\ ``_BACKGROUND`` 用于设置背景色,举个例子如下。 -|image10| +|image11| 5.2 设置显示内容 ~~~~~~~~~~~~~~~~ @@ -271,15 +273,16 @@ PEP8 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/image-20200307210853246.png -.. |image1| image:: http://image.python-online.cn/image-20200307212823345.png -.. |image2| image:: http://image.python-online.cn/image-20200307213534278.png -.. |image3| image:: https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67 -.. |image4| image:: http://image.python-online.cn/image-20200307214742135.png -.. |image5| image:: http://image.python-online.cn/image-20200307213534278.png -.. |image6| image:: http://image.python-online.cn/image-20200307215530270.png -.. |image7| image:: http://image.python-online.cn/image-20200307215834623.png -.. |image8| image:: http://image.python-online.cn/image-20200307214600749.png -.. |image9| image:: http://image.python-online.cn/image-20200308121949011.png -.. |image10| image:: http://image.python-online.cn/image-20200308125431779.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/image-20200307210853246.png +.. |image2| image:: http://image.python-online.cn/image-20200307212823345.png +.. |image3| image:: http://image.python-online.cn/image-20200307213534278.png +.. |image4| image:: https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67 +.. |image5| image:: http://image.python-online.cn/image-20200307214742135.png +.. |image6| image:: http://image.python-online.cn/image-20200307213534278.png +.. |image7| image:: http://image.python-online.cn/image-20200307215530270.png +.. |image8| image:: http://image.python-online.cn/image-20200307215834623.png +.. |image9| image:: http://image.python-online.cn/image-20200307214600749.png +.. |image10| image:: http://image.python-online.cn/image-20200308121949011.png +.. |image11| image:: http://image.python-online.cn/image-20200308125431779.png diff --git a/source/c01/c01_37.md b/source/c01/c01_37.md index ebdc0c0..8454bb8 100644 --- a/source/c01/c01_37.md +++ b/source/c01/c01_37.md @@ -1,5 +1,7 @@ # 1.37 Python 炫技操作:条件语句的七种写法 +![](http://image.iswbm.com/20200602135014.png) + 有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。 Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 diff --git a/source/c01/c01_37.rst b/source/c01/c01_37.rst index af11db0..0e09b0d 100644 --- a/source/c01/c01_37.rst +++ b/source/c01/c01_37.rst @@ -1,6 +1,8 @@ 1.37 Python 炫技操作:条件语句的七种写法 ======================================== +|image0| + 有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。 Python 语言里有许多(而且是越来越多)的高级特性,是 Python @@ -177,3 +179,6 @@ Python 功力。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_38.md b/source/c01/c01_38.md index a40bf3e..6bf3ed5 100644 --- a/source/c01/c01_38.md +++ b/source/c01/c01_38.md @@ -1,5 +1,7 @@ # 1.38 /usr/bin/env python 有什么用? +![](http://image.iswbm.com/20200602135014.png) + 我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 ```json diff --git a/source/c01/c01_38.rst b/source/c01/c01_38.rst index c0d5d1e..1d59e40 100644 --- a/source/c01/c01_38.rst +++ b/source/c01/c01_38.rst @@ -1,6 +1,8 @@ 1.38 /usr/bin/env python 有什么用? =================================== +|image0| + 我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 .. code:: json @@ -18,7 +20,7 @@ 稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python`` 进入console 模式里的 ``python`` -|image0| +|image1| 而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python`` ,意思就是说你得用哪个软件 (python)来执行这个文件。 @@ -27,20 +29,20 @@ 不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` , -|image1| +|image2| 有没有一种方式?可以省去每次都加 ``python`` 呢? 当然有,你可以文件头里加上\ ``#!/usr/bin/python`` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 -|image2| +|image3| 明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? 当我执行 ``env python`` 时,自动进入了 python console 的模式。 -|image3| +|image4| 这是为什么?和 直接执行 python 好像没什么区别呀 @@ -55,7 +57,7 @@ 具体演示过程,你可以看下面。 -|image4| +|image5| 那么对于这两者,我们应该使用哪个呢? @@ -67,9 +69,10 @@ python 解释器都是 ``/usr/bin/python`` 。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20200331184021.png -.. |image1| image:: http://image.python-online.cn/20200331185034.png -.. |image2| image:: http://image.python-online.cn/20200331184755.png -.. |image3| image:: http://image.python-online.cn/20200331185741.png -.. |image4| image:: http://image.python-online.cn/20200331190224.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20200331184021.png +.. |image2| image:: http://image.python-online.cn/20200331185034.png +.. |image3| image:: http://image.python-online.cn/20200331184755.png +.. |image4| image:: http://image.python-online.cn/20200331185741.png +.. |image5| image:: http://image.python-online.cn/20200331190224.png diff --git a/source/c01/c01_39.md b/source/c01/c01_39.md index e4eaccc..2fc848d 100644 --- a/source/c01/c01_39.md +++ b/source/c01/c01_39.md @@ -1,5 +1,7 @@ # 1.39 Python 炫技操作:合并字典的七种方法 +![](http://image.iswbm.com/20200602135014.png) + Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 但你要知道,在团队合作里,炫技是大忌。 diff --git a/source/c01/c01_39.rst b/source/c01/c01_39.rst index 3037283..840c335 100644 --- a/source/c01/c01_39.rst +++ b/source/c01/c01_39.rst @@ -1,6 +1,8 @@ 1.39 Python 炫技操作:合并字典的七种方法 ======================================== +|image0| + Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 @@ -226,3 +228,6 @@ Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_40.md b/source/c01/c01_40.md index a70130c..e605731 100644 --- a/source/c01/c01_40.md +++ b/source/c01/c01_40.md @@ -1,5 +1,7 @@ # 1.40 Python 炫技操作:判断是否包含子串的七种方法 +![](http://image.iswbm.com/20200602135014.png) + Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 但你要知道,在团队合作里,炫技是大忌。 diff --git a/source/c01/c01_40.rst b/source/c01/c01_40.rst index b662c06..dabb092 100644 --- a/source/c01/c01_40.rst +++ b/source/c01/c01_40.rst @@ -1,6 +1,8 @@ 1.40 Python 炫技操作:判断是否包含子串的七种方法 ================================================ +|image0| + Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 @@ -149,3 +151,6 @@ python 代码快。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_41.md b/source/c01/c01_41.md index 211fb5c..7ccfad5 100644 --- a/source/c01/c01_41.md +++ b/source/c01/c01_41.md @@ -1,5 +1,7 @@ # 1.41 Python 炫技操作:连接列表的八种方法 +![](http://image.iswbm.com/20200602135014.png) + Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 但你要知道,在团队合作里,炫技是大忌。 diff --git a/source/c01/c01_41.rst b/source/c01/c01_41.rst index 03af012..16df3b4 100644 --- a/source/c01/c01_41.rst +++ b/source/c01/c01_41.rst @@ -1,6 +1,8 @@ 1.41 Python 炫技操作:连接列表的八种方法 ======================================== +|image0| + Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 @@ -228,3 +230,6 @@ yield from 意义及使用方法。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_42.md b/source/c01/c01_42.md index fbde99b..956ef33 100644 --- a/source/c01/c01_42.md +++ b/source/c01/c01_42.md @@ -1,5 +1,7 @@ # 1.42 Python 炫技操作:海象运算符的三种用法 +![](http://image.iswbm.com/20200602135014.png) + Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 很多 Python 3.8 的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 diff --git a/source/c01/c01_42.rst b/source/c01/c01_42.rst index a92b178..47bc5b1 100644 --- a/source/c01/c01_42.rst +++ b/source/c01/c01_42.rst @@ -1,6 +1,8 @@ 1.42 Python 炫技操作:海象运算符的三种用法 ========================================== +|image0| + Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 @@ -12,7 +14,7 @@ Python 版本发展非常快,如今最新的版本已经是 Pyhton 它的英文原名叫 ``Assignment Expressions``\ ,翻译过来也就是 ``赋值表达式``\ ,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 -|image0| +|image1| 1. 第一个用法:if/else ---------------------- @@ -178,5 +180,6 @@ Golang,那这里要注意,Golang 中的 ``:=`` 关注公众号,获取最新干货! -.. |image0| image:: http://image.iswbm.com/image-20200418122739417.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/image-20200418122739417.png diff --git a/source/c01/c01_43.md b/source/c01/c01_43.md index 8b8a5ed..08da1b7 100644 --- a/source/c01/c01_43.md +++ b/source/c01/c01_43.md @@ -1,5 +1,7 @@ # 1.43 求你了,别再使用 pprint 打印字典了 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 吐槽问题 pprint 你应该很熟悉了吧? diff --git a/source/c01/c01_43.rst b/source/c01/c01_43.rst index dd26fab..affda76 100644 --- a/source/c01/c01_43.rst +++ b/source/c01/c01_43.rst @@ -1,6 +1,8 @@ 1.43 求你了,别再使用 pprint 打印字典了 ======================================= +|image0| + 1. 吐槽问题 ----------- @@ -160,7 +162,7 @@ Python,本来我可以选择不用的,因为有更好的替代方案(\ ** 输出如下,已经解决了中文的显示问题: -|image0| +|image1| 打印双引号 ~~~~~~~~~~ @@ -267,7 +269,7 @@ stream,也就是标准输出。 就像下面这样。 -|image1| +|image2| 知道了问题所在,再修改下代码 @@ -292,7 +294,7 @@ stream,也就是标准输出。 终于成功了,太不容易了吧。 -|image2| +|image3| 3. 何必折腾 ----------- @@ -384,7 +386,8 @@ json.dumps 的关键参数有两个: 关注公众号,获取最新干货! -.. |image0| image:: http://image.iswbm.com/20200507171451.png -.. |image1| image:: http://image.iswbm.com/20200507174459.png -.. |image2| image:: http://image.iswbm.com/20200507174802.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200507171451.png +.. |image2| image:: http://image.iswbm.com/20200507174459.png +.. |image3| image:: http://image.iswbm.com/20200507174802.png diff --git a/source/c01/c01_44.md b/source/c01/c01_44.md index a576798..9d8f50a 100644 --- a/source/c01/c01_44.md +++ b/source/c01/c01_44.md @@ -1,5 +1,7 @@ # 1.44 详解 Python 中的编码问题 +![](http://image.iswbm.com/20200602135014.png) + Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 Python 开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式? diff --git a/source/c01/c01_44.rst b/source/c01/c01_44.rst index cac3270..3b92e50 100644 --- a/source/c01/c01_44.rst +++ b/source/c01/c01_44.rst @@ -1,6 +1,8 @@ 1.44 详解 Python 中的编码问题 ============================= +|image0| + Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 Python 开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 @@ -190,7 +192,7 @@ KOI8-R 编码。 chardet 支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html) -|image0| +|image1| 4. 编码与解码的区别 ------------------- @@ -202,7 +204,7 @@ chardet - **解码**\ :decode 方法,把二进制字节序列转化为字符串对象 -|image1| +|image2| 那么假如我们真知道了其编码格式,如何来转成 unicode 呢? @@ -289,6 +291,7 @@ sys.setdefaultencoding 这个方法。 关注公众号,获取最新干货! -.. |image0| image:: http://image.iswbm.com/20200423185819.png -.. |image1| image:: http://image.iswbm.com/20200423190331.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200423185819.png +.. |image2| image:: http://image.iswbm.com/20200423190331.png diff --git a/source/c01/c01_45.md b/source/c01/c01_45.md index 1ae10d4..b9e9e2f 100644 --- a/source/c01/c01_45.md +++ b/source/c01/c01_45.md @@ -1,5 +1,7 @@ # 1.45 Python炫技操作:花式导包的八种方法 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 直接 import diff --git a/source/c01/c01_45.rst b/source/c01/c01_45.rst index 79ea785..4629688 100644 --- a/source/c01/c01_45.rst +++ b/source/c01/c01_45.rst @@ -1,6 +1,8 @@ 1.45 Python炫技操作:花式导包的八种方法 ======================================= +|image0| + 1. 直接 import -------------- @@ -304,3 +306,6 @@ importlib 是非常有必要的。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_01.md b/source/c02/c02_01.md index 3320f33..fd3a1dd 100644 --- a/source/c02/c02_01.md +++ b/source/c02/c02_01.md @@ -1,5 +1,7 @@ # 2.1 从性能角度初探并发编程 +![](http://image.iswbm.com/20200602135014.png) + --- 作为进阶系列的一个分支「`并发编程`」,我觉得这是每个程序员都应该会的。 diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index b9b9ce0..57b79d8 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -1,6 +1,8 @@ 2.1 从性能角度初探并发编程 ========================== +|image0| + -------------- 作为进阶系列的一个分支「\ ``并发编程``\ 」,我觉得这是每个程序员都应该会的。 @@ -39,11 +41,11 @@ - ``多线程``\ ,交替执行,另一种意义上的串行。 -.\ |image1| +.\ |image2| - ``多进程``\ ,并行执行,真正意义上的并发。 -.\ |image2| +.\ |image3| 2.1.2 单线程VS多线程VS多进程 ---------------------------- @@ -52,7 +54,7 @@ 首先,我的实验环境配置如下 -|image3| +|image4| **注意** 以下代码,若要理解,对小白有如下知识点要求: @@ -230,7 +232,7 @@ 将结果汇总一下,制成表格。 -|image4| +|image5| 我们来分析下这个表格。 @@ -253,9 +255,10 @@ 关注公众号,获取最新干货! +.. |image0| image:: http://image.iswbm.com/20200602135014.png .. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png -.. |image1| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg -.. |image2| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg -.. |image3| image:: http://image.python-online.cn/20190112205155.png -.. |image4| image:: http://image.python-online.cn/20190112204930.png +.. |image2| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg +.. |image3| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg +.. |image4| image:: http://image.python-online.cn/20190112205155.png +.. |image5| image:: http://image.python-online.cn/20190112204930.png diff --git a/source/c02/c02_02.md b/source/c02/c02_02.md index 56586f4..08a1dcb 100644 --- a/source/c02/c02_02.md +++ b/source/c02/c02_02.md @@ -1,5 +1,7 @@ # 2.2 创建多线程的几种方法 +![](http://image.iswbm.com/20200602135014.png) + --- diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index eac8902..ba358a6 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -1,6 +1,8 @@ 2.2 创建多线程的几种方法 ======================== +|image0| + -------------- 今天的内容会比较基础,主要是为了让新手也能无障碍地阅读,所以还是要再巩固下基础。学完了基础,你们也就能很顺畅地跟着我的思路理解以后的文章。 @@ -149,3 +151,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_03.md b/source/c02/c02_03.md index 5414f41..8541826 100644 --- a/source/c02/c02_03.md +++ b/source/c02/c02_03.md @@ -1,5 +1,7 @@ # 2.3 谈谈线程中的“锁机制” +![](http://image.iswbm.com/20200602135014.png) + --- ## 1. 什么是锁? diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index 6b7165b..f936804 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -1,6 +1,8 @@ 2.3 谈谈线程中的“锁机制” ======================== +|image0| + -------------- 1. 什么是锁? @@ -356,3 +358,6 @@ CPython,所以也就默许了Python具有GIL锁这个事。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 5b90ec1..cd18c0e 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -1,5 +1,7 @@ # 2.4 线程消息通信机制 +![](http://image.iswbm.com/20200602135014.png) + --- 前面我已经向大家介绍了,如何使用创建线程,启动线程。相信大家都会有这样一个想法,线程无非就是创建一下,然后再`start()`下,实在是太简单了。 diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 3649ea0..533e1e8 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -1,6 +1,8 @@ 2.4 线程消息通信机制 ==================== +|image0| + -------------- 前面我已经向大家介绍了,如何使用创建线程,启动线程。相信大家都会有这样一个想法,线程无非就是创建一下,然后再\ ``start()``\ 下,实在是太简单了。 @@ -311,3 +313,6 @@ Queue.task_done(),说明队列这个任务已经结束了。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_05.md b/source/c02/c02_05.md index 4c753e6..cf71504 100644 --- a/source/c02/c02_05.md +++ b/source/c02/c02_05.md @@ -1,5 +1,7 @@ # 2.5 线程中的信息隔离 +![](http://image.iswbm.com/20200602135014.png) + --- 上一篇我们说,线程与线程之间要通过消息通信来控制程序的执行。 diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index daa59c4..1c45439 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -1,6 +1,8 @@ 2.5 线程中的信息隔离 ==================== +|image0| + -------------- 上一篇我们说,线程与线程之间要通过消息通信来控制程序的执行。 @@ -263,3 +265,6 @@ Out),就是先进入队列的消息,将优先被消费。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index 4083c47..ceaf744 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -1,5 +1,7 @@ # 2.6 线程池与进程池的创建 +![](http://image.iswbm.com/20200602135014.png) + --- >**友情提醒**: diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 6d314fc..c03c6db 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -1,6 +1,8 @@ 2.6 线程池与进程池的创建 ======================== +|image0| + -------------- **友情提醒**\ : @@ -158,3 +160,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_07.md b/source/c02/c02_07.md index c61bc4d..b0b3aec 100644 --- a/source/c02/c02_07.md +++ b/source/c02/c02_07.md @@ -1,5 +1,7 @@ # 2.7 从生成器使用入门协程 +![](http://image.iswbm.com/20200602135014.png) + --- 从今天开始,我们将开始进入Python的难点,那就是`协程`。 diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index a98ee67..e6ea410 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -1,6 +1,8 @@ 2.7 从生成器使用入门协程 ======================== +|image0| + -------------- 从今天开始,我们将开始进入Python的难点,那就是\ ``协程``\ 。 @@ -96,7 +98,7 @@ 3. 所以,最好的判断方法应该是通过 ``for循环``\ 或者\ ``iter()`` 去真实运行。 -|image0| +|image1| 接下来是,\ ``迭代器``\ 。 对比可迭代对象,\ ``迭代器``\ 其实就只是多了一个函数而已。就是\ ``__next__()``\ ,我们可以不再使用\ ``for``\ 循环来间断获取元素值。而可以直接使用next()方法来实现。 @@ -315,7 +317,7 @@ 抛出异常(\ ``StopIteration``\ )。 通过列表生成式构建的生成器,其内部已经自动帮我们实现了抛出异常这一步。不信我们来看一下。 -|image1| +|image2| 所以我们在自己定义一个生成器的时候,我们也应该在不满足生成元素条件的时候,抛出异常。 拿上面的代码来修改一下。 @@ -395,6 +397,7 @@ 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190527123516.png -.. |image1| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190527123516.png +.. |image2| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png diff --git a/source/c02/c02_08.md b/source/c02/c02_08.md index fd3ba5d..9456748 100644 --- a/source/c02/c02_08.md +++ b/source/c02/c02_08.md @@ -1,5 +1,7 @@ # 2.8 深入理解yield from语法 +![](http://image.iswbm.com/20200602135014.png) + --- 直到上一篇,我们终于迎来了Python并发编程中,最高级、最重要、当然也是最难的知识点--`协程`。 diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index 248cda8..a801241 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -1,6 +1,8 @@ 2.8 深入理解yield from语法 ========================== +|image0| + -------------- 直到上一篇,我们终于迎来了Python并发编程中,最高级、最重要、当然也是最难的知识点–\ ``协程``\ 。 @@ -363,3 +365,6 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_09.md b/source/c02/c02_09.md index 00c4519..1d3d363 100644 --- a/source/c02/c02_09.md +++ b/source/c02/c02_09.md @@ -1,5 +1,7 @@ # 2.9 初识异步IO框架:asyncio 上篇 +![](http://image.iswbm.com/20200602135014.png) + --- 通过前两节的铺垫(关于协程的使用),今天我们终于可以来介绍我们整个系列的重点 -- `asyncio`。 diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 2709d13..202c42a 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -1,6 +1,8 @@ 2.9 初识异步IO框架:asyncio 上篇 ================================ +|image0| + -------------- 通过前两节的铺垫(关于协程的使用),今天我们终于可以来介绍我们整个系列的重点 @@ -249,6 +251,7 @@ emmm,和上面的结果是一样的。nice 关注公众号,获取最新干货! +.. |image0| image:: http://image.iswbm.com/20200602135014.png .. |普通函数中 不能使用 await| image:: https://i.loli.net/2018/05/26/5b09794f45340.png .. |async 中 不能使用yield| image:: https://i.loli.net/2018/05/26/5b0978b646230.png .. |验证通过| image:: https://i.loli.net/2018/05/26/5b09814dc4714.png diff --git a/source/c02/c02_10.md b/source/c02/c02_10.md index d015315..b22ab09 100644 --- a/source/c02/c02_10.md +++ b/source/c02/c02_10.md @@ -1,5 +1,7 @@ # 2.10 深入异步IO框架:asyncio 中篇 +![](http://image.iswbm.com/20200602135014.png) + --- 今天的内容其实还挺多的,我准备了三天,到今天才整理完毕。希望大家看完,有所收获的,能给小明一个赞。这就是对小明最大的鼓励了。 diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index e7d699a..dc4381b 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -1,6 +1,8 @@ 2.10 深入异步IO框架:asyncio 中篇 ================================= +|image0| + -------------- 今天的内容其实还挺多的,我准备了三天,到今天才整理完毕。希望大家看完,有所收获的,能给小明一个赞。这就是对小明最大的鼓励了。 @@ -511,3 +513,6 @@ asyncio.gather :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_11.md b/source/c02/c02_11.md index f4f9298..f8e5879 100644 --- a/source/c02/c02_11.md +++ b/source/c02/c02_11.md @@ -1,5 +1,7 @@ # 2.11 实战异步IO框架:asyncio 下篇 +![](http://image.iswbm.com/20200602135014.png) + --- 前面两节,我们讲了协程中的单任务和多任务 diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 842b33a..12f71aa 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -1,6 +1,8 @@ 2.11 实战异步IO框架:asyncio 下篇 ================================= +|image0| + -------------- 前面两节,我们讲了协程中的单任务和多任务 @@ -125,11 +127,11 @@ 为了简单起见,并且协程更适合单线程的方式,我们的主线程用来监听队列,子线程用于处理队列。这里使用redis的队列。主线程中有一个是无限循环,用户消费队列。 先安装Redis 到 https://github.com/MicrosoftArchive/redis/releases 下载 -|image0| 解压到你的路径。 |image1| +|image1| 解压到你的路径。 |image2| -然后,在当前路径运行cmd,运行redis的服务端。 |image2| +然后,在当前路径运行cmd,运行redis的服务端。 |image3| 服务开启后,我们就可以运行我们的客户端了。 -并依次输入key=queue,value=5,3,1的消息。 |image3| +并依次输入key=queue,value=5,3,1的消息。 |image4| 一切准备就绪之后,我们就可以运行我们的代码了。 @@ -221,8 +223,9 @@ 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2018/06/03/5b13ba8525bcf.png -.. |image1| image:: https://i.loli.net/2018/06/03/5b13ba9f66baa.png -.. |image2| image:: https://i.loli.net/2018/06/03/5b13bab682a32.png -.. |image3| image:: https://i.loli.net/2018/06/03/5b13bad79f5ce.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2018/06/03/5b13ba8525bcf.png +.. |image2| image:: https://i.loli.net/2018/06/03/5b13ba9f66baa.png +.. |image3| image:: https://i.loli.net/2018/06/03/5b13bab682a32.png +.. |image4| image:: https://i.loli.net/2018/06/03/5b13bad79f5ce.png diff --git a/source/c02/c02_12.md b/source/c02/c02_12.md index 40ecd8f..2ca157d 100644 --- a/source/c02/c02_12.md +++ b/source/c02/c02_12.md @@ -1,5 +1,7 @@ # 2.12 生成器与协程,你分清了吗? +![](http://image.iswbm.com/20200602135014.png) + 之前写的 `并发编程` 系列文章,是我迄今务止,得到的反馈最好的一个系列,也是因为它我才能认识这么多优秀的朋友。 diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index e7812e2..fe84d8b 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -1,6 +1,8 @@ 2.12 生成器与协程,你分清了吗? =============================== +|image0| + 之前写的 ``并发编程`` 系列文章,是我迄今务止,得到的反馈最好的一个系列,也是因为它我才能认识这么多优秀的朋友。 @@ -111,3 +113,6 @@ yield :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_13.md b/source/c02/c02_13.md index 17c3df8..7a3f71a 100644 --- a/source/c02/c02_13.md +++ b/source/c02/c02_13.md @@ -1,5 +1,7 @@ # 2.13 I/O多路复用:select/poll/epoll +![](http://image.iswbm.com/20200602135014.png) + 在讲解 select/poll/epoll 之前 ,先来了解一下什么是 `I/O多路复用` `I/O多路复用` ,英文全称为 `I/O multiplexing`,这个中文翻译和把 socket 翻译成 套接字一样,影响了我们对其概念的理解。 diff --git a/source/c02/c02_13.rst b/source/c02/c02_13.rst index 3c53390..3656b5d 100644 --- a/source/c02/c02_13.rst +++ b/source/c02/c02_13.rst @@ -1,6 +1,8 @@ 2.13 I/O多路复用:select/poll/epoll =================================== +|image0| + 在讲解 select/poll/epoll 之前 ,先来了解一下什么是 ``I/O多路复用`` ``I/O多路复用`` ,英文全称为 ``I/O multiplexing``\ ,这个中文翻译和把 @@ -12,3 +14,6 @@ socket 翻译成 套接字一样,影响了我们对其概念的理解。 ---------------------- 举个现实生活中的例子。 + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c02/c02_14.md b/source/c02/c02_14.md index 6a07cae..f8af4af 100644 --- a/source/c02/c02_14.md +++ b/source/c02/c02_14.md @@ -1,5 +1,7 @@ # 2.14 浅谈线程安全那些事儿 +![](http://image.iswbm.com/20200602135014.png) + 在并发编程时,如果多个线程访问同一资源,我们需要保证访问的时候不会产生冲突,数据修改不会发生错误,这就是我们常说的 **线程安全** 。 那什么情况下,访问数据时是安全的?什么情况下,访问数据是不安全的?如何知道你的代码是否线程安全?要如何访问数据才能保证数据的安全? diff --git a/source/c02/c02_14.rst b/source/c02/c02_14.rst index a92ab36..79ab29d 100644 --- a/source/c02/c02_14.rst +++ b/source/c02/c02_14.rst @@ -1,6 +1,8 @@ 2.14 浅谈线程安全那些事儿 ========================= +|image0| + 在并发编程时,如果多个线程访问同一资源,我们需要保证访问的时候不会产生冲突,数据修改不会发生错误,这就是我们常说的 **线程安全** 。 @@ -97,7 +99,7 @@ operation**\ ),指不会被线程调度机制打断的操作,这种操作 当我们还是无法确定我们的代码是否具有原子性的时候,可以尝试通过 ``dis`` 模块里的 dis 函数来查看 -|image0| +|image1| 当我们执行这段代码时,可以看到 ``number += 1`` 这一行代码,由两条字节码实现。 @@ -113,7 +115,7 @@ operation**\ ),指不会被线程调度机制打断的操作,这种操作 这里我拿字典的 update 操作举例,代码和执行过程如下图 -|image1| +|image2| 从截图里可以看到,\ ``info.update(new)`` 虽然也分为好几个操作 @@ -196,6 +198,7 @@ https://juejin.im/post/5b129a1be51d45068a6c91d4#comment 关注公众号,获取最新干货! -.. |image0| image:: http://image.iswbm.com/20200506080445.png -.. |image1| image:: http://image.iswbm.com/20200506081541.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200506080445.png +.. |image2| image:: http://image.iswbm.com/20200506081541.png diff --git a/source/c03/c03_01.md b/source/c03/c03_01.md index 542646b..716091f 100644 --- a/source/c03/c03_01.md +++ b/source/c03/c03_01.md @@ -1,5 +1,7 @@ # 3.1 装饰器进阶用法详解 +![](http://image.iswbm.com/20200602135014.png) + --- 对于每一个学习 Python 的同学,想必对 `@` 符号一定不陌生了,正如你所知, @ 符号是装饰器的语法糖,@符号后面的函数就是我们本文的主角:**装饰器**。 diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index aa4839a..9559d36 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -1,6 +1,8 @@ 3.1 装饰器进阶用法详解 ====================== +|image0| + -------------- 对于每一个学习 Python 的同学,想必对 ``@`` @@ -20,7 +22,7 @@ 当时带着这两个问题,我就开始系统的学习装饰器的所有内容。这些一直整理在自己的博客中,今天对其进行了大量的补充和勘误,发表在这里分享给大家。希望对刚入门以及进阶的朋友可以提供一些参考。 -|image0| +|image1| 3.1.1 Hello,装饰器 ------------------- @@ -388,7 +390,7 @@ Python工匠:使用装饰器的小技巧) 其实例化的过程,你可以参考我这里的调试过程,加以理解。 -|image1| +|image2| 3.1.9 wraps 装饰器有啥用? -------------------------- @@ -778,6 +780,7 @@ property 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190811100737.png -.. |image1| image:: http://image.python-online.cn/20190512113917.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190811100737.png +.. |image2| image:: http://image.python-online.cn/20190512113917.png diff --git a/source/c03/c03_02.md b/source/c03/c03_02.md index a5ef80c..bed0f4c 100644 --- a/source/c03/c03_02.md +++ b/source/c03/c03_02.md @@ -1,5 +1,7 @@ # 3.2 深入理解Python元类 +![](http://image.iswbm.com/20200602135014.png) + --- ## 3.2.1 类是如何产生的 diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index 8be7e21..f03f4d4 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -1,6 +1,8 @@ 3.2 深入理解Python元类 ====================== +|image0| + -------------- 3.2.1 类是如何产生的 @@ -378,3 +380,6 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c03/c03_03.md b/source/c03/c03_03.md index c3f5ceb..33c1814 100644 --- a/source/c03/c03_03.md +++ b/source/c03/c03_03.md @@ -1,5 +1,7 @@ # 3.3 Socket编程实现在线聊天 +![](http://image.iswbm.com/20200602135014.png) + --- ## 3.3.1 什么是socket? diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index d8ecd86..abd5879 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -1,6 +1,8 @@ 3.3 Socket编程实现在线聊天 ========================== +|image0| + -------------- 3.3.1 什么是socket? @@ -104,7 +106,7 @@ 3.3.4 socket工作流程 -------------------- -|image0| +|image1| 3.3.5 socket公共函数汇总 ------------------------ @@ -288,7 +290,7 @@ cs = ChatServer(port=9999) cs.main() -将服务端程序跑起来,然后运行客户端,看下效果。 |image1| +将服务端程序跑起来,然后运行客户端,看下效果。 |image2| -------------- @@ -297,6 +299,7 @@ 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png -.. |image1| image:: https://i.loli.net/2018/04/30/5ae6c31b2d1c8.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png +.. |image2| image:: https://i.loli.net/2018/04/30/5ae6c31b2d1c8.png diff --git a/source/c03/c03_04.md b/source/c03/c03_04.md index 19cf25f..d425cda 100644 --- a/source/c03/c03_04.md +++ b/source/c03/c03_04.md @@ -1,5 +1,7 @@ # 3.4 Django+Uwsgi部署网站 +![](http://image.iswbm.com/20200602135014.png) + 本文是先前自己使用django写过一个个人博客时的部署过程记录。 涉及的工具有:Django+Nginx+Uwsgi+Supervisor diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index 280a3a6..1bcc0f4 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -1,6 +1,8 @@ 3.4 Django+Uwsgi部署网站 ======================== +|image0| + 本文是先前自己使用django写过一个个人博客时的部署过程记录。 涉及的工具有:Django+Nginx+Uwsgi+Supervisor @@ -27,7 +29,7 @@ sudo apt-get update sudo apt-get upgrade -更新时,会让你选择是否安装新版软件,输入Y安装 |image0| +更新时,会让你选择是否安装新版软件,输入Y安装 |image1| 更新安装完成后,系统就会给我们预装了Python2.7.12和Python3.5.2 @@ -149,17 +151,17 @@ **外网连接** 使用navicat进行测试连接 -|image1| +|image2| 连接上后就可以新建数据库 -|image2| +|image3| 如果有备份数据要还原,可以使用数据传输。 数据传输功能,不能确保一次就能将数据传输完整,要多传输几次。 实在无法完整传输,就要导出导入的方式。 -|image3| +|image4| 三、项目上传 ------------ @@ -391,7 +393,7 @@ vim /etc/supervisord.conf -直接跳到最后,填入红框内容 |image4| +直接跳到最后,填入红框内容 |image5| 5.3 启动项目 ~~~~~~~~~~~~ @@ -406,7 +408,7 @@ myblog: stopped myblog: updated process group -通过\ ``supervisorctl status``\ 查看启动状态 |image5| +通过\ ``supervisorctl status``\ 查看启动状态 |image6| 5.4 supervisor其他命令 ~~~~~~~~~~~~~~~~~~~~~~ @@ -515,10 +517,11 @@ 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2017/08/20/599982f513b7e.png -.. |image1| image:: https://i.loli.net/2017/08/20/59998b33265d9.png -.. |image2| image:: https://i.loli.net/2017/08/20/59998b91d16dd.png -.. |image3| image:: https://i.loli.net/2017/08/20/59998bdf36c79.png -.. |image4| image:: https://i.loli.net/2017/08/25/59a0236def497.png -.. |image5| image:: https://i.loli.net/2017/08/25/59a0244d239f0.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2017/08/20/599982f513b7e.png +.. |image2| image:: https://i.loli.net/2017/08/20/59998b33265d9.png +.. |image3| image:: https://i.loli.net/2017/08/20/59998b91d16dd.png +.. |image4| image:: https://i.loli.net/2017/08/20/59998bdf36c79.png +.. |image5| image:: https://i.loli.net/2017/08/25/59a0236def497.png +.. |image6| image:: https://i.loli.net/2017/08/25/59a0244d239f0.png diff --git a/source/c03/c03_05.md b/source/c03/c03_05.md index 9da86be..9bb787e 100644 --- a/source/c03/c03_05.md +++ b/source/c03/c03_05.md @@ -1,5 +1,7 @@ # 3.5 源码解读:Flask上下文与代理模式 +![](http://image.iswbm.com/20200602135014.png) + 在上一节中,我跟大家一起深入了解了一下Python的「上下文管理器 」。而今天呢,还是和上下文有关的话题。只不过这里的上下文和上一节的内容有点不一样,上下文管理器是管理代码块级别的上下文,而今天要讲的上下文是工程项目中的上下文。 可能你现在对**上下文**这个概念,还是不太清楚。这里再简单说明一下 diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index ce07cd8..a58ba7a 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -1,6 +1,8 @@ 3.5 源码解读:Flask上下文与代理模式 =================================== +|image0| + 在上一节中,我跟大家一起深入了解了一下Python的「上下文管理器 」。而今天呢,还是和上下文有关的话题。只不过这里的上下文和上一节的内容有点不一样,上下文管理器是管理代码块级别的上下文,而今天要讲的上下文是工程项目中的上下文。 @@ -139,11 +141,11 @@ wangbm,然后开始一个线程,做的事就是将这个 name 属性改为 w 如果要用图来表示,最开始的Local对象就是一个空盒子 -|image0| +|image1| 当有不同的线程往里写数据时,Local 对象为每个线程分配了一个 micro-box。 -|image1| +|image2| local 是需要被 ``localmanager`` 管理的,在请求结束后,会调用 ``localmanager.cleanup()`` 函数,其实是调用 ``local.__release_local__`` @@ -216,7 +218,7 @@ local 是需要被 ``localmanager`` 管理的,在请求结束后,会调用 同样用一张图来表示 -|image2| +|image3| 栈结构的特性,无非就是后进先出。这里就不说了,这里的重点是线程隔离的特性如何体现,还是以上面的例子,稍微做了下修改。 @@ -503,7 +505,8 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX -.. |image1| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup -.. |image2| image:: http://image.python-online.cn/FimULzWaeZWS2KJx_EQLAK_yRZ4A +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX +.. |image2| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup +.. |image3| image:: http://image.python-online.cn/FimULzWaeZWS2KJx_EQLAK_yRZ4A diff --git a/source/c03/c03_06.md b/source/c03/c03_06.md index 7f1f097..664a654 100644 --- a/source/c03/c03_06.md +++ b/source/c03/c03_06.md @@ -1,5 +1,7 @@ # 3.6 Web开发者必看:理解WSGI +![](http://image.iswbm.com/20200602135014.png) + 在 三百六十行,行行转 IT 的现状下,很多来自各行各业的同学,都选择 Python 这门胶水语言做为踏入互联网大门的第一块敲门砖,在这些人里,又有相当大比例的同学选择了 Web 开发这个方向(包括我)。而从事 web 开发,绕不过一个知识点,就是 WSGI。 不管你是否是这些如上同学中的一员,都应该好好地学习一下这个知识点。 diff --git a/source/c03/c03_06.rst b/source/c03/c03_06.rst index 791f586..f36f61e 100644 --- a/source/c03/c03_06.rst +++ b/source/c03/c03_06.rst @@ -1,6 +1,8 @@ 3.6 Web开发者必看:理解WSGI =========================== +|image0| + 在 三百六十行,行行转 IT 的现状下,很多来自各行各业的同学,都选择 Python 这门胶水语言做为踏入互联网大门的第一块敲门砖,在这些人里,又有相当大比例的同学选择了 Web 开发这个方向(包括我)。而从事 web 开发,绕不过一个知识点,就是 @@ -25,7 +27,7 @@ OpenStack 代码。 一个HTTP请求的过程可以分为两个阶段,第一阶段是从客户端到WSGI Server,第二阶段是从WSGI Server 到WSGI Application -|image0| +|image1| 今天主要是讲第二阶段,主要内容有以下几点: @@ -106,7 +108,7 @@ web 服务器 和 web 框架,分工不同,职责不同(web 我根据其架构组成的不同将这个过程的实现分为两种: -|image1| +|image2| **1、两级结构** 在这种结构里,uWSGI作为服务器,它用到了HTTP协议以及wsgi协议,flask应用作为application,实现了wsgi协议。当有客户端发来请求,uWSGI接受请求,调用flask @@ -147,7 +149,7 @@ nginx可以做负载均衡(前提是有多个服务器),保护了实际的web 使用 lsof 命令可以查到确实开启了这个端口 -|image2| +|image3| 以上使用 wsgiref 写了一个demo,让你对wsgi有个初步的了解。其由于只适合在学习测试使用,在生产环境中应该另寻他道。 @@ -173,26 +175,26 @@ nova-api,nova-compute,nova-conductor,nova-scheduler 等等。 从 Service 文件可以得知 nova-api 的入口是 ``nova.cmd.api:main()`` -|image3| - |image4| +|image5| + 打开\ ``nova.cmd.api:main()`` ,一起看看是 OpenStack Nova 的代码。 在如下的黄框里,可以看到在这里使用了service.WSGIService 启动了一个 server,就是我们所说的的 wsgi server -|image5| +|image6| 那这里的 WSGI Server 是依靠什么实现的呢?让我们继续深入源代码。 -|image6| +|image7| wsgi.py 可以看到这里使用了 eventlet 这个网络并发框架,它先开启了一个绿色线程池,从配置里可以看到这个服务器可以接收的请求并发量是 1000 。 -|image7| +|image8| 可是我们还没有看到 WSGI Server 的身影,上面使用eventlet 开启了线程池,那线程池里的每个线程应该都是一个服务器吧?它是如何接收请求的? @@ -229,7 +231,7 @@ Server,还是使用的 eventlet。 # 孵化协程 self._server = utils.spawn(**wsgi_kwargs) -|image8| +|image9| 就这样,nova 开启了一个可以接受1000个绿色协程并发的 WSGI Server。 @@ -273,7 +275,7 @@ PasteDeploy 到底是做什么的呢? 具体可以,看下nova的实现。 -|image9| +|image10| 通过打印的 DEBUG 内容得知 config_url 和 app name 的值 @@ -448,7 +450,7 @@ loadapp 函数可以接收两个实参: applications 是URLMap 对象。 -|image10| +|image11| 完善并整合第二步和第三步的内容,写成一个 Python 文件(wsgi_server.py)。内容如下 @@ -509,7 +511,7 @@ applications 是URLMap 对象。 一切都准备好后,在终端执行 ``python wsgi_server.py``\ 来启动 web server -|image11| +|image12| 如果像上图一样一切正常,那么打开浏览器 @@ -529,7 +531,7 @@ applications 是URLMap 对象。 ``nova.api.openstack.compute:APIRouterV21.factory`` 这个 application 的入口,看代码知道它其实返回了 APIRouterV21 类的一个实例。 -|image12| +|image13| WSGI规定 application 必须是一个 callable 的对象,函数、方法、类、实例,若是一个类实例,就要求这个实例所属的类实现 @@ -538,7 +540,7 @@ WSGI规定 application 必须是一个 callable APIRouterV21 本身没有实现 ``__call__`` ,但它的父类 Router实现了 ``__call__`` -|image13| +|image14| 我们知道,application 必须遵丛 WSGI 的规范 @@ -551,7 +553,7 @@ response,而只是返回另一个 callable 的对象,就这样我们的视线被一次又一次的转移,但没有关系,这些\ ``__call__``\ 都是外衣,只要扒掉这些外衣,我们就能看到核心app。 而负责扒掉这层外衣的,就是其头上的装饰器 ``@webob.dec.wsgify`` ,wsgify -是一个类,其 ``__call__`` 源码实现如下:\ |image14| +是一个类,其 ``__call__`` 源码实现如下:\ |image15| 可以看出,wsgify 在这里,会将 req 这个原始请求(dict对象)封装成 Request 对象(就是规范1里提到的 @@ -577,7 +579,7 @@ RoutesMiddleware对象)是如何找到真正的 application呢? 在文章最开始处,我们给大家画了一张图。 -|image15| +|image16| 这张图把一个 HTTP 请求粗略简单地划分为两个过程。但事实上,整个过程远比这个过程要复杂得多。 @@ -601,7 +603,7 @@ URL Routing。 进行路由匹配,并将匹配的结果存入request请求的环境变量\ ``['wsgiorg.routing_args']``\ ,最后会调用\ ``self._dispatch``\ (dispatch返回真正的application)返回response,最后会将这个response返回给 WSGI Server。 -|image16| +|image17| 这个中间件的原理,看起来是挺简单的。并没有很复杂的逻辑。 @@ -611,7 +613,7 @@ WSGI Server。 app,controller 这几个很重要的字眼,其是否是我苦苦追寻的 application 对象呢? -|image17| +|image18| 要搞明白这个问题,只要看清 match 到是什么东西? @@ -643,7 +645,7 @@ Resource,同时将这些 Resource 交由 routes.Mapper 从 Nova 代码中看出每个Resource 对应一个 Controller 对象,因为 Controller 对象本身就是对一种资源的操作集合。 -|image18| +|image19| 通过日志的打印,可以发现 nova 管理的 Resource 对象有多么的多而杂 @@ -825,7 +827,7 @@ Controller在构建的过程中会由于MetaClass的影响将其所有action类 图示地方,会从 environ 里获取中看到获取 action 的具体代码 -|image19| +|image20| 我将这边的 action_args打印出来 @@ -837,7 +839,7 @@ Controller在构建的过程中会由于MetaClass的影响将其所有action类 在 ``__call__`` 的最后,会 调用 ``_process_stack`` 方法 -|image20| +|image21| 在图标处,get_method 会根据 action(函数名) 取得处理函数对象。 @@ -848,12 +850,12 @@ Controller在构建的过程中会由于MetaClass的影响将其所有action类 最后,再执行这个函数,取得 action_result,在 ``_process_stack`` 会对 response 进行初步封装。 -|image21| +|image22| 然后将 response 再返回到 wsgify ,由这个专业的工具函数,进行 response 的最后封装和返回给客户端。 -|image22| +|image23| 至此,一个请求从发出到响应就结束了。 @@ -887,27 +889,28 @@ response 进行初步封装。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190607131728.png -.. |image1| image:: http://image.python-online.cn/20190607191954.png -.. |image2| image:: http://image.python-online.cn/20190607134310.png -.. |image3| image:: http://image.python-online.cn/20190607140817.png -.. |image4| image:: http://image.python-online.cn/20190607140922.png -.. |image5| image:: http://image.python-online.cn/20190530212557.png -.. |image6| image:: http://image.python-online.cn/20190530212753.png -.. |image7| image:: http://image.python-online.cn/20190530212956.png -.. |image8| image:: http://image.python-online.cn/20190530214820.png -.. |image9| image:: http://image.python-online.cn/20190530221101.png -.. |image10| image:: http://image.python-online.cn/20190607154119.png -.. |image11| image:: http://image.python-online.cn/20190607155432.png -.. |image12| image:: http://image.python-online.cn/20190602173212.png -.. |image13| image:: http://image.python-online.cn/20190602173956.png -.. |image14| image:: http://image.python-online.cn/20190605203016.png -.. |image15| image:: http://image.python-online.cn/20190607131728.png -.. |image16| image:: http://image.python-online.cn/20190608211233.png -.. |image17| image:: http://image.python-online.cn/20190531211542.png -.. |image18| image:: http://image.python-online.cn/20190531225529.png -.. |image19| image:: http://image.python-online.cn/20190602220246.png -.. |image20| image:: http://image.python-online.cn/20190602220511.png -.. |image21| image:: http://image.python-online.cn/20190602220700.png -.. |image22| image:: http://image.python-online.cn/20190605203016.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190607131728.png +.. |image2| image:: http://image.python-online.cn/20190607191954.png +.. |image3| image:: http://image.python-online.cn/20190607134310.png +.. |image4| image:: http://image.python-online.cn/20190607140817.png +.. |image5| image:: http://image.python-online.cn/20190607140922.png +.. |image6| image:: http://image.python-online.cn/20190530212557.png +.. |image7| image:: http://image.python-online.cn/20190530212753.png +.. |image8| image:: http://image.python-online.cn/20190530212956.png +.. |image9| image:: http://image.python-online.cn/20190530214820.png +.. |image10| image:: http://image.python-online.cn/20190530221101.png +.. |image11| image:: http://image.python-online.cn/20190607154119.png +.. |image12| image:: http://image.python-online.cn/20190607155432.png +.. |image13| image:: http://image.python-online.cn/20190602173212.png +.. |image14| image:: http://image.python-online.cn/20190602173956.png +.. |image15| image:: http://image.python-online.cn/20190605203016.png +.. |image16| image:: http://image.python-online.cn/20190607131728.png +.. |image17| image:: http://image.python-online.cn/20190608211233.png +.. |image18| image:: http://image.python-online.cn/20190531211542.png +.. |image19| image:: http://image.python-online.cn/20190531225529.png +.. |image20| image:: http://image.python-online.cn/20190602220246.png +.. |image21| image:: http://image.python-online.cn/20190602220511.png +.. |image22| image:: http://image.python-online.cn/20190602220700.png +.. |image23| image:: http://image.python-online.cn/20190605203016.png diff --git a/source/c04/c04_01.md b/source/c04/c04_01.md index d99c596..3cae439 100644 --- a/source/c04/c04_01.md +++ b/source/c04/c04_01.md @@ -1,5 +1,7 @@ # 4.1 虚拟环境:virtualenv +![](http://image.iswbm.com/20200602135014.png) + --- diff --git a/source/c04/c04_01.rst b/source/c04/c04_01.rst index ac4b062..77dd316 100755 --- a/source/c04/c04_01.rst +++ b/source/c04/c04_01.rst @@ -1,6 +1,8 @@ 4.1 虚拟环境:virtualenv ======================== +|image0| + -------------- 1.0 什么是虚拟环境? @@ -146,7 +148,7 @@ virtualenv 虽然已经相当好用了,可是功能还是不够完善。 若是 windows 则新增环境变量:\ ``WORKON_HOME`` -|image0| +|image1| **基本语法**\ : @@ -221,7 +223,7 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html 先对比下,全局环境和虚拟环境的区别,全局环境中有requests包,而虚拟环境中并未安装。 当我们敲入 ``workon my_env01``\ ,前面有\ ``my_env01``\ 的标识,说明我们已经处在虚拟环境中。后面所有的操作,都将在虚拟环境下执行。 -|image1| +|image2| 4.2 工程项目中 ~~~~~~~~~~~~~~ @@ -254,14 +256,14 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html $ ./ming.py -发现和预期一样,真的报错了。说明我们指定的虚拟环境有效果。 |image2| +发现和预期一样,真的报错了。说明我们指定的虚拟环境有效果。 |image3| 4.3 PyCharm中 ~~~~~~~~~~~~~ -点击 File - Settings - Project - Interpreter |image3| +点击 File - Settings - Project - Interpreter |image4| 点击小齿轮。如图点击添加,按提示添加一个虚拟环境。然后点 OK -就可以使用这个虚拟环境,之后的项目都会在这个虚拟环境下运行。 |image4| +就可以使用这个虚拟环境,之后的项目都会在这个虚拟环境下运行。 |image5| -------------- @@ -270,9 +272,10 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20200209161935.png -.. |image1| image:: https://i.loli.net/2018/06/11/5b1e7d36ce8ad.png -.. |image2| image:: https://i.loli.net/2018/06/11/5b1e7f140be6a.png -.. |image3| image:: https://i.loli.net/2018/06/11/5b1e805c996c8.png -.. |image4| image:: https://i.loli.net/2018/06/11/5b1e812db603f.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20200209161935.png +.. |image2| image:: https://i.loli.net/2018/06/11/5b1e7d36ce8ad.png +.. |image3| image:: https://i.loli.net/2018/06/11/5b1e7f140be6a.png +.. |image4| image:: https://i.loli.net/2018/06/11/5b1e805c996c8.png +.. |image5| image:: https://i.loli.net/2018/06/11/5b1e812db603f.png diff --git a/source/c04/c04_02.md b/source/c04/c04_02.md index bf0a8b8..f575a5d 100644 --- a/source/c04/c04_02.md +++ b/source/c04/c04_02.md @@ -1,5 +1,7 @@ # 4.2 Xshell的高效使用手册 +![](http://image.iswbm.com/20200602135014.png) + --- ![](http://image.python-online.cn/20190511162815.png) diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index f312ac8..7e475d4 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -1,9 +1,11 @@ 4.2 Xshell的高效使用手册 ======================== +|image0| + -------------- -|image0| +|image1| 做为一名开发人员,我们难免都会与服务器打交道。 @@ -25,11 +27,11 @@ 查看 -> 快速命令 -双击底部自定义快速命令。 |image1| +双击底部自定义快速命令。 |image2| **使用收藏栏** -点击最左侧按按钮添加收藏。 |image2| +点击最左侧按按钮添加收藏。 |image3| **快捷设置** @@ -37,13 +39,13 @@ ① 右键粘贴 ② 双击分隔符 ③ 选中即复制 -|image3| +|image4| **设置Meta键** 文件 -> 属性 -> 键盘 -一定要打钩,这是后面诸多快捷使用的前提。 |image4| +一定要打钩,这是后面诸多快捷使用的前提。 |image5| 移动光标 -------- @@ -171,9 +173,10 @@ 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511162815.png -.. |image1| image:: http://image.python-online.cn/20190511162524.png -.. |image2| image:: http://image.python-online.cn/20190511162607.png -.. |image3| image:: http://image.python-online.cn/20190511162716.png -.. |image4| image:: http://image.python-online.cn/20190511162730.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511162815.png +.. |image2| image:: http://image.python-online.cn/20190511162524.png +.. |image3| image:: http://image.python-online.cn/20190511162607.png +.. |image4| image:: http://image.python-online.cn/20190511162716.png +.. |image5| image:: http://image.python-online.cn/20190511162730.png diff --git a/source/c04/c04_03.md b/source/c04/c04_03.md index 5113a94..876a8a3 100644 --- a/source/c04/c04_03.md +++ b/source/c04/c04_03.md @@ -1,5 +1,7 @@ # 4.3 30分钟教你搭建一个博客 +![](http://image.iswbm.com/20200602135014.png) + --- 10个优秀的程序员里,有9个人都有写博客的习惯。这是非常好的习惯,值得每个程序员,投入时间和精力去坚持做下去。 diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 8698ed8..4f6b59c 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -1,6 +1,8 @@ 4.3 30分钟教你搭建一个博客 ========================== +|image0| + -------------- 10个优秀的程序员里,有9个人都有写博客的习惯。这是非常好的习惯,值得每个程序员,投入时间和精力去坚持做下去。 @@ -28,15 +30,15 @@ 以我的博客(\ ``python-online.cn``)为例,先给大家展示一下。 -这是首页。显示了你所有的文章索引。 |image0| +这是首页。显示了你所有的文章索引。 |image1| -这是我的导航栏。是不是结构很清晰,很方便索引。 |image1| +这是我的导航栏。是不是结构很清晰,很方便索引。 |image2| -点击文章后,还可以很方便查看标题,跳转。 |image2| +点击文章后,还可以很方便查看标题,跳转。 |image3| 体验下搜索功能,速度很快。 -|image3| +|image4| 看完这些你是不是也很想拥有这样一个博客呢? @@ -213,9 +215,9 @@ source:raw-latex:`\index`.rst,千万要注意中间的空行不可忽略。 | 我们点进去 build:raw-latex:`\html` 目录,使用浏览器打开index.html文件。 -| |image4| +| |image5| -真棒,已经完成了一半了。点击 我们刚写的 暴富指南。 |image5| +真棒,已经完成了一半了。点击 我们刚写的 暴富指南。 |image6| 4.3.5 托管项目 -------------- @@ -243,17 +245,17 @@ source:raw-latex:`\index`.rst,千万要注意中间的空行不可忽略。 你需要先去 Read the Docs 注册下帐号。 -关联一下GitHub |image6| +关联一下GitHub |image7| -|image7| +|image8| -导入代码库。填好与你对应的信息。 |image8| +导入代码库。填好与你对应的信息。 |image9| -|image9| +|image10| 构建网页后。右下方,你可以看见你的在线地址。 -|image10| +|image11| 这里要提醒一下的是,Sphinx的文档格式,默认是 rst 格式,如果你习惯了使用Markdown来写文章,可以使用 Pandoc @@ -298,7 +300,7 @@ WordPress 真的是没法比,因为这两种产品定位本身就不一样。 游客无法阅读博客的全部内容,因为会有一半的内容会被隐藏,就像这样。 -|image11| +|image12| 如想要浏览完整内容,需要点击 “阅读全文” 进行解锁: @@ -306,7 +308,7 @@ WordPress 真的是没法比,因为这两种产品定位本身就不一样。 2. 发送 ``more`` ,获取到的验证码; 3. 在如下文本框中输入验证码。 -这样就可以永久解锁本博客的所有干货文章。 |image12| +这样就可以永久解锁本博客的所有干货文章。 |image13| 思路有了,那么如何实现呢? @@ -396,7 +398,7 @@ WordPress 真的是没法比,因为这两种产品定位本身就不一样。 - 指定我们的本地生成的 requirements.txt(使用 pip freeze >requirements.txt) -|image13| +|image14| 同时你如果之前是看过我写的教程,使用过我的中文检索插件,那你要注意了。 @@ -428,11 +430,11 @@ Python 3.x ,所以这里的代码也要对应修改。 然后在网站列表新增一个你的网站,我的信息如下: -|image14| +|image15| 填写完成,就可以获取一段属于你的网站的专属 js 代码(下面第一步)。 -|image15| +|image16| 第二步内容,是教你如何安装这段 js 代码。 @@ -461,11 +463,11 @@ Python 3.x ,所以这里的代码也要对应修改。 构建完成后,去执行第三步,代码安装检查。像我下面这样,就是安装完成了。 -|image16| +|image17| 这个插件安装完成后,如果你的网站有流量,可以过个一个小时,点击一下查看报告查看你网站的详细访问数据。 -|image17| +|image18| 数据真的非常全面,你可以知道,访客都是从哪里访问(直接访问,Google等),每篇文章的点击量(你就知道哪篇是爆款?),每天有多少老访问客,多少新访客等等,更多维度的数据你可以自己去体验一下。 @@ -483,22 +485,23 @@ Python 3.x ,所以这里的代码也要对应修改。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511160523.png -.. |image1| image:: http://image.python-online.cn/20190511161056.png -.. |image2| image:: http://image.python-online.cn/20190511161130.png -.. |image3| image:: http://image.python-online.cn/20190511161147.png -.. |image4| image:: http://image.python-online.cn/20190511161212.png -.. |image5| image:: http://image.python-online.cn/20190511161240.png -.. |image6| image:: http://image.python-online.cn/20190511161255.png -.. |image7| image:: http://image.python-online.cn/20190511161311.png -.. |image8| image:: http://image.python-online.cn/20190511161334.png -.. |image9| image:: http://image.python-online.cn/20190511161414.png -.. |image10| image:: http://image.python-online.cn/20190511161426.png -.. |image11| image:: http://image.python-online.cn/20191015230346.png -.. |image12| image:: http://image.python-online.cn/20191015230502.png -.. |image13| image:: http://image.python-online.cn/20191015234452.png -.. |image14| image:: http://image.python-online.cn/20191016205336.png -.. |image15| image:: http://image.python-online.cn/20191016205653.png -.. |image16| image:: http://image.python-online.cn/20191015225652.png -.. |image17| image:: http://image.python-online.cn/20191016211012.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511160523.png +.. |image2| image:: http://image.python-online.cn/20190511161056.png +.. |image3| image:: http://image.python-online.cn/20190511161130.png +.. |image4| image:: http://image.python-online.cn/20190511161147.png +.. |image5| image:: http://image.python-online.cn/20190511161212.png +.. |image6| image:: http://image.python-online.cn/20190511161240.png +.. |image7| image:: http://image.python-online.cn/20190511161255.png +.. |image8| image:: http://image.python-online.cn/20190511161311.png +.. |image9| image:: http://image.python-online.cn/20190511161334.png +.. |image10| image:: http://image.python-online.cn/20190511161414.png +.. |image11| image:: http://image.python-online.cn/20190511161426.png +.. |image12| image:: http://image.python-online.cn/20191015230346.png +.. |image13| image:: http://image.python-online.cn/20191015230502.png +.. |image14| image:: http://image.python-online.cn/20191015234452.png +.. |image15| image:: http://image.python-online.cn/20191016205336.png +.. |image16| image:: http://image.python-online.cn/20191016205653.png +.. |image17| image:: http://image.python-online.cn/20191015225652.png +.. |image18| image:: http://image.python-online.cn/20191016211012.png diff --git a/source/c04/c04_04.md b/source/c04/c04_04.md index b3f6ba6..54b3d17 100644 --- a/source/c04/c04_04.md +++ b/source/c04/c04_04.md @@ -1,5 +1,7 @@ # 4.4 Jupyter NoteBook 使用指南 +![](http://image.iswbm.com/20200602135014.png) + --- diff --git a/source/c04/c04_04.rst b/source/c04/c04_04.rst index 43240a9..5707dab 100755 --- a/source/c04/c04_04.rst +++ b/source/c04/c04_04.rst @@ -1,6 +1,8 @@ 4.4 Jupyter NoteBook 使用指南 ============================= +|image0| + -------------- 今天来安利一款 Python开发 @@ -38,7 +40,7 @@ Anaconda 4.4.1 下载安装 -------------- -到官网下载 `Anaconda `__ |image0| +到官网下载 `Anaconda `__ |image1| 我这里选择下载 Py3.6,下载下来是 exe 文件(600多M)。安装即可。 安装完成后,还会提示你是否安装 VS Code,自行选择。不安装直接 Skip 就好。 @@ -47,18 +49,18 @@ Anaconda -------------- 点击打开 Anaconda 。会出现这个界面。第一眼,我看到了很多工具,包含 -NoteBook,qtconsole,VS Ccode等。 |image1| +NoteBook,qtconsole,VS Ccode等。 |image2| 这里就介绍一下,我最经常使用的 NoteBook 就好了。其他的大家自行尝试。 -选择 NoteBook,点击 Launch,会启动浏览器打开一个 web 页面。 |image2| +选择 NoteBook,点击 Launch,会启动浏览器打开一个 web 页面。 |image3| -点击右上角的new,就可以进入 Python3 的交互界面。 |image3| +点击右上角的new,就可以进入 Python3 的交互界面。 |image4| 来感受一下,如何使用这个工具。 每一行的命令,都在一个可编辑的输入框。即使你的命令输入有误,你也不用从头写代码,直接编辑后重新执行即可。 -|image4| +|image5| 还有一点,命令的执行是可间断的,某个命令执行错误,不会导致整个程序中断,这将很方便我们调试代码,只要改完代码,再重新执行该行代码即可,而不用重新执行全部代码。 @@ -220,18 +222,18 @@ Markdown下快捷键 其实以上快捷键,在非编辑模式下,按 h 就会出现快捷键帮助菜单。 -|image5| |image6| +|image6| |image7| 4.4.4 导出笔记文件 ------------------ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学习笔记。 它提供多种常用的文件格式,md,rst,pdf等。如果你希望再次编辑,可以保存为ipynb,这是Jupyter的文件格式,可以再次打开进行编辑。 -|image7| +|image8| 以前我学习 Pandas 的时候,也曾经使用它做过笔记,输出的是PDF文件,可以按目录导航,相当方便。 -|image8| +|image9| 好了,大概就是这些内容。 @@ -242,13 +244,14 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511163102.png -.. |image1| image:: http://image.python-online.cn/20190511163123.png -.. |image2| image:: http://image.python-online.cn/20190511163137.png -.. |image3| image:: http://image.python-online.cn/20190511163145.png -.. |image4| image:: http://image.python-online.cn/20190511163200.png -.. |image5| image:: http://image.python-online.cn/20190511163245.png -.. |image6| image:: http://image.python-online.cn/20190511163253.png -.. |image7| image:: http://image.python-online.cn/20190511163304.png -.. |image8| image:: http://image.python-online.cn/20190511163311.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511163102.png +.. |image2| image:: http://image.python-online.cn/20190511163123.png +.. |image3| image:: http://image.python-online.cn/20190511163137.png +.. |image4| image:: http://image.python-online.cn/20190511163145.png +.. |image5| image:: http://image.python-online.cn/20190511163200.png +.. |image6| image:: http://image.python-online.cn/20190511163245.png +.. |image7| image:: http://image.python-online.cn/20190511163253.png +.. |image8| image:: http://image.python-online.cn/20190511163304.png +.. |image9| image:: http://image.python-online.cn/20190511163311.png diff --git a/source/c04/c04_05.md b/source/c04/c04_05.md index 51be5bc..9086f04 100644 --- a/source/c04/c04_05.md +++ b/source/c04/c04_05.md @@ -1,5 +1,7 @@ # 4.5 Win10+Ubuntu 双系统安装教程 +![](http://image.iswbm.com/20200602135014.png) + --- 在大多数情况下,对于一个程序开发人员,电脑的操作系统的最佳选择不应该是 Windows,而是 Mac 或者 Linux。 diff --git a/source/c04/c04_05.rst b/source/c04/c04_05.rst index 86b863c..9d1a11e 100755 --- a/source/c04/c04_05.rst +++ b/source/c04/c04_05.rst @@ -1,6 +1,8 @@ 4.5 Win10+Ubuntu 双系统安装教程 =============================== +|image0| + -------------- 在大多数情况下,对于一个程序开发人员,电脑的操作系统的最佳选择不应该是 @@ -55,15 +57,15 @@ Windows。 这里上一张我安装硬盘的记念图。 -|image0| +|image1| 你没有看错,我的是台式机。说起这台电脑,还是前年我自己搜罗配置单,自己从各大电商平台,有京东,淘宝,还有天猫买了所有的配件,然后自己一件一件组装起来的。还是挺有感情的,虽然也是渣渣配置,但是这个过程还是很愉快的。在有了每一次装体验后,后面我还给别人装过好几台,如果说高三是我知识储备最高的时期,那前年就是我动手能力最强的时期的。组装过将近十台的电脑。这都是题外话了。 第一次装好硬盘后呢,要进入原先的 Windows 系统,检查一下,我们安装的硬盘有没有装成功。由于我装了工具,开始键和你们的可能不一样(不过真的是Win10),你也可以右击桌面 -「我的电脑」,再点击「管理」。 |image1| +「我的电脑」,再点击「管理」。 |image2| 如果硬盘安装成功,这里会有一块未分配的盘,如图中的 硬盘0。 -第一次使用,需要初始化硬盘,记得选 GPT。 |image2| +第一次使用,需要初始化硬盘,记得选 GPT。 |image3| 这里可以不用急着分区(在后面安装系统时会让你分的),如果你要提前分好(使用DiskGenius),也没有关系。 @@ -85,11 +87,11 @@ iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 首先,打开软件,点击 文件 - 打开,选择你所下载的 iso 文件。出现如下界面 -|image3| +|image4| 再点击 启动 - 写入硬盘映像 - 写入 -|image4| +|image5| 如一切顺利,U盘就制作完成,一般 99.99% 都不会在这地方出错。 @@ -101,7 +103,7 @@ iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 但在这里,必须关闭 win10的快速启动功能。方法如下:取消勾选「启用快速启动」,点击保存修改。然后就可以正常关机了。 -|image5| +|image6| 4.5.5 安装Ubuntu ---------------- @@ -113,7 +115,7 @@ Bios,更改顺序。但是我这里不想这么麻烦,因为安装完后又 查找完后,你可以对电脑开机了,按住你的快捷键,选择启动方式。由于我们要从U盘启动,所以这里选择 -这里一定要注意,选择最后一个,自定义选项。 |image6| +这里一定要注意,选择最后一个,自定义选项。 |image7| 终于到了分区的这一步了,这是最关键的一步。对于Linux不熟悉的人,到这里可能会懵逼。不用怕,这里给你提供一个最简单的分区配置。具体为什么这么分,我想你并不关心吧? @@ -125,36 +127,36 @@ Bios,更改顺序。但是我这里不想这么麻烦,因为安装完后又 /home,/usr等。 分区完成后,一定要注意如下这二个红框,选择安装启动引导器的设备为 -我们刚刚设置的efi分区。检查无误后,就可以点击「现在安装」。 |image7| +我们刚刚设置的efi分区。检查无误后,就可以点击「现在安装」。 |image8| -接下来就是 选择时区 - 配置键盘。 |image8| +接下来就是 选择时区 - 配置键盘。 |image9| -|image9| +|image10| -到了这里,你应该可以长舒第一口气了。成功了一半了。 |image10| +到了这里,你应该可以长舒第一口气了。成功了一半了。 |image11| 如果你和我一样使用 SSD ,应该不出5分钟系统就可以安装完毕。弹出如下界面。点击现在重启。 -|image11| +|image12| 重启的过程,记住还是一样按住你的快捷键,我这里仍然是 F11,看到没有,已经有一个叫 ubuntu的启动设备。就它了,选择进入系统。接下来,就是选择要以哪种模式进入ubuntu,你根据需要去选吧。 -|image12| +|image13| 4.5.6 效果展示 -------------- 由于默认的Ubuntu主题也是丑得可以,经过一个晚上的美化,它变成如下这般帅气逼人。 -|image13| - |image14| |image15| |image16| +|image17| + -------------- .. figure:: http://image.python-online.cn/image-20200320125724880.png @@ -162,21 +164,22 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511163441.png -.. |image1| image:: http://image.python-online.cn/20190511163457.png -.. |image2| image:: http://image.python-online.cn/20190511163510.png -.. |image3| image:: http://image.python-online.cn/20190511163520.png -.. |image4| image:: http://image.python-online.cn/20190511163531.png -.. |image5| image:: http://image.python-online.cn/20190511163542.png -.. |image6| image:: http://image.python-online.cn/20190511163550.png -.. |image7| image:: http://image.python-online.cn/20190511163559.png -.. |image8| image:: http://image.python-online.cn/20190511163612.png -.. |image9| image:: http://image.python-online.cn/20190511163633.png -.. |image10| image:: http://image.python-online.cn/20190511163700.png -.. |image11| image:: http://image.python-online.cn/20190511163711.png -.. |image12| image:: http://image.python-online.cn/20190511163722.png -.. |image13| image:: http://image.python-online.cn/20190511163731.png -.. |image14| image:: http://image.python-online.cn/20190511163750.png -.. |image15| image:: http://image.python-online.cn/20190511163757.png -.. |image16| image:: http://image.python-online.cn/20190511163805.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511163441.png +.. |image2| image:: http://image.python-online.cn/20190511163457.png +.. |image3| image:: http://image.python-online.cn/20190511163510.png +.. |image4| image:: http://image.python-online.cn/20190511163520.png +.. |image5| image:: http://image.python-online.cn/20190511163531.png +.. |image6| image:: http://image.python-online.cn/20190511163542.png +.. |image7| image:: http://image.python-online.cn/20190511163550.png +.. |image8| image:: http://image.python-online.cn/20190511163559.png +.. |image9| image:: http://image.python-online.cn/20190511163612.png +.. |image10| image:: http://image.python-online.cn/20190511163633.png +.. |image11| image:: http://image.python-online.cn/20190511163700.png +.. |image12| image:: http://image.python-online.cn/20190511163711.png +.. |image13| image:: http://image.python-online.cn/20190511163722.png +.. |image14| image:: http://image.python-online.cn/20190511163731.png +.. |image15| image:: http://image.python-online.cn/20190511163750.png +.. |image16| image:: http://image.python-online.cn/20190511163757.png +.. |image17| image:: http://image.python-online.cn/20190511163805.png diff --git a/source/c04/c04_06.md b/source/c04/c04_06.md index 8d7441f..bce9eac 100644 --- a/source/c04/c04_06.md +++ b/source/c04/c04_06.md @@ -1,5 +1,7 @@ # 4.6 我的 Git 使用指南 +![](http://image.iswbm.com/20200602135014.png) + --- ## 一、Hello world diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 5c4d314..1210844 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -1,6 +1,8 @@ 4.6 我的 Git 使用指南 ===================== +|image0| + -------------- 一、Hello world @@ -177,7 +179,7 @@ add/commit 前撤销对文件的修改 使用 diff 进行查看 -|image0| +|image1| 查看两个 commit 之间的修改 @@ -350,11 +352,11 @@ add/commit 前撤销对文件的修改 很简单,先使用 ``git reflog`` 找到你的 reset 的 commit id -|image1| +|image2| 然后再次使用 ``git reset`` 指定 commit id 回到一次修改add前的状态 -|image2| +|image3| 3.6 回退远程提交 ~~~~~~~~~~~~~~~~ @@ -512,10 +514,10 @@ add/commit 前撤销对文件的修改 ssh-keygen -t rsa -C "wongbingming@163.com" -|image3| 生成的私钥 ``/c/Users/wangbm/.ssh/id_rsa``\ ,由本机电脑保存。 +|image4| 生成的私钥 ``/c/Users/wangbm/.ssh/id_rsa``\ ,由本机电脑保存。 生成的仅钥 ``/c/Users/wangbm/.ssh/id_rsa.pub``\ ,需要在Github上添加。 -在github上添加ssh keys方法如下: |image4| +在github上添加ssh keys方法如下: |image5| 添加完后,可以使用这个命令测试一下。 @@ -539,7 +541,7 @@ add/commit 前撤销对文件的修改 git config --global user.name 'BingmingWong' git config --global user.email wongbingming@163.com -配置完后我们就可以进行clone线上代码了。 |image5| +配置完后我们就可以进行clone线上代码了。 |image6| 7.3 使用 SourceTree ~~~~~~~~~~~~~~~~~~~ @@ -563,7 +565,7 @@ Desktop真心觉得不好用)。 需要注意的是,使用它需要你进行一步操作。在 7.2 章节,我在机器上生成了一对私钥和公钥,在这里需要配置一下才能正常访问和提交我们的Github仓库。 -|image6| +|image7| 如果想要用户名和密码登陆,可以将上面的 OpenSSH 改成 PuTTY/Plink就行了。 @@ -598,11 +600,12 @@ Desktop真心觉得不好用)。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20191217150942.png -.. |image1| image:: http://image.python-online.cn/20191231165152.png -.. |image2| image:: http://image.python-online.cn/20191231165239.png -.. |image3| image:: https://i.loli.net/2018/04/15/5ad2c06e8893d.png -.. |image4| image:: http://image.python-online.cn/20190511163855.png -.. |image5| image:: https://i.loli.net/2018/04/15/5ad2c2a9813b9.png -.. |image6| image:: http://image.python-online.cn/20190430235625.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191217150942.png +.. |image2| image:: http://image.python-online.cn/20191231165152.png +.. |image3| image:: http://image.python-online.cn/20191231165239.png +.. |image4| image:: https://i.loli.net/2018/04/15/5ad2c06e8893d.png +.. |image5| image:: http://image.python-online.cn/20190511163855.png +.. |image6| image:: https://i.loli.net/2018/04/15/5ad2c2a9813b9.png +.. |image7| image:: http://image.python-online.cn/20190430235625.png diff --git a/source/c04/c04_07.md b/source/c04/c04_07.md index 226fd3c..e6b3d2e 100644 --- a/source/c04/c04_07.md +++ b/source/c04/c04_07.md @@ -1,5 +1,7 @@ # 4.7 Hexo 搭建博客教程 +![](http://image.iswbm.com/20200602135014.png) + --- 现在越来越多的人喜欢利用Github搭建静态网站,原因不外乎简单省钱。本人也利用hexo+github搭建了本博客,用于分享一些心得。在此过程中,折腾博客的各种配置以及功能占具了我一部分时间,在此详细记录下我是如何利用hexo+github搭建静态博客以及一些配置相关问题,以免过后遗忘,且当备份之用。 diff --git a/source/c04/c04_07.rst b/source/c04/c04_07.rst index e59ddf6..d1971e9 100755 --- a/source/c04/c04_07.rst +++ b/source/c04/c04_07.rst @@ -1,6 +1,8 @@ 4.7 Hexo 搭建博客教程 ===================== +|image0| + -------------- 现在越来越多的人喜欢利用Github搭建静态网站,原因不外乎简单省钱。本人也利用hexo+github搭建了本博客,用于分享一些心得。在此过程中,折腾博客的各种配置以及功能占具了我一部分时间,在此详细记录下我是如何利用hexo+github搭建静态博客以及一些配置相关问题,以免过后遗忘,且当备份之用。 @@ -16,7 +18,7 @@ - 下载安装hexo。方法:打开终端 运行\ ``npm install -g hexo``\ (要翻墙) -|image0| +|image1| 1.2 初始化项目 ~~~~~~~~~~~~~~ @@ -24,7 +26,7 @@ - 新创建一个目录,比如叫做 ``iswbm-blog`` - 使用终端进入该文件夹内,执行\ ``hexo init`` (初始化项目) -|image1| +|image2| - 运行你的博客项目(用于调试),查看生成的文章是否符合自己的预期。 @@ -47,7 +49,7 @@ - 在Github上创建名字为XXX.github.io的项目,XXX为自己的github用户名。然后 clone 到你的本地电脑上。 - |image2| + |image3| - 打开该文件夹内的\ ``_config.yml``\ 配置文件,将其中的type设置为git,其他配置如下 @@ -76,7 +78,7 @@ 在第一次部署后,请到 github 后台,补充你的 网站信息,否则无法访问。 -|image3| +|image4| 一切完成之后,你就可以通过上面的网址来访问我的博客了。 @@ -88,9 +90,9 @@ 我们可以自己去阿里云购买一个域名(我的是 iswbm.com),然后将其 CNAME 到你的博客地址上。 -- 去阿里云 域名云解析 |image4| +- 去阿里云 域名云解析 |image5| -- 然后到对应的GitHub仓库,绑定我的个性域名 |image5| +- 然后到对应的GitHub仓库,绑定我的个性域名 |image6| - 然后再在你本地的项目里的 ``source`` 目录下新建 ``CNAME``\ 文件,内容就是我的域名 @@ -143,7 +145,7 @@ Do not just seek happiness for yourself. Seek happiness for all. Through kindness. Through mercy. {% endblockquote %} -效果如下 |image6| +效果如下 |image7| 3.2 一键生成md头格式 ~~~~~~~~~~~~~~~~~~~~ @@ -407,7 +409,7 @@ next主题集成leanCloud,我只需稍微配置下(在主题配置文件) 接着打开\ ``themes/next/layout/_macro/post.swig``\ 文件,添加如下下代码,注意位置 -|image7| 代码如下: +|image8| 代码如下: :: @@ -550,7 +552,7 @@ Next 主题的配置文件有一个原生 bug,就是菜单项后面会多一个空格,这会导致你在页面访问 about,tags等页面时,会报 404,原因是地址后面多了个空格。 -|image8| +|image9| 五、其他实用功能 ---------------- @@ -756,7 +758,7 @@ about,tags等页面时,会报 404,原因是地址后面多了个空格。 在web界面新建分支,命名为\ ``hexo`` 在web界面设置 ``hexo`` 为默认分支,因为我们只会在这个分支上进行操作。 -|image9| +|image10| 6.2 本地PC操作 ~~~~~~~~~~~~~~ @@ -785,7 +787,7 @@ clone项目到本地: git push origin hexo 将博客原始文件添加进来,但是有一些是没必要放的,具体要放哪些文件,看图片。 -|image10| +|image11| :: @@ -894,15 +896,16 @@ bash窗口,不然会提示找不到npm命令) 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/image-20200321163152876.png -.. |image1| image:: http://image.python-online.cn/image-20200321163746032.png -.. |image2| image:: http://image.python-online.cn/image-20200321165634287.png -.. |image3| image:: http://image.python-online.cn/image-20200321171008622.png -.. |image4| image:: http://image.python-online.cn/image-20200321171939919.png -.. |image5| image:: http://image.python-online.cn/image-20200321171821683.png -.. |image6| image:: http://image.python-online.cn/17-9-10/85269241.jpg -.. |image7| image:: http://image.python-online.cn/17-9-9/63041495.jpg -.. |image8| image:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200321210014963.png -.. |image9| image:: http://image.python-online.cn/image-20200321193444320.png -.. |image10| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/image-20200321163152876.png +.. |image2| image:: http://image.python-online.cn/image-20200321163746032.png +.. |image3| image:: http://image.python-online.cn/image-20200321165634287.png +.. |image4| image:: http://image.python-online.cn/image-20200321171008622.png +.. |image5| image:: http://image.python-online.cn/image-20200321171939919.png +.. |image6| image:: http://image.python-online.cn/image-20200321171821683.png +.. |image7| image:: http://image.python-online.cn/17-9-10/85269241.jpg +.. |image8| image:: http://image.python-online.cn/17-9-9/63041495.jpg +.. |image9| image:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200321210014963.png +.. |image10| image:: http://image.python-online.cn/image-20200321193444320.png +.. |image11| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png diff --git a/source/c04/c04_08.md b/source/c04/c04_08.md index 3d2347a..d036b72 100644 --- a/source/c04/c04_08.md +++ b/source/c04/c04_08.md @@ -1,5 +1,7 @@ # 4.8 珍惜生命,远离鼠标 +![](http://image.iswbm.com/20200602135014.png) + --- 珍惜生命,远离鼠标。能用快捷键解决的事情,绝对不要用鼠标来做。 diff --git a/source/c04/c04_08.rst b/source/c04/c04_08.rst index 095af96..40626a4 100755 --- a/source/c04/c04_08.rst +++ b/source/c04/c04_08.rst @@ -1,6 +1,8 @@ 4.8 珍惜生命,远离鼠标 ====================== +|image0| + -------------- 珍惜生命,远离鼠标。能用快捷键解决的事情,绝对不要用鼠标来做。 @@ -432,3 +434,6 @@ URL相关 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c04/c04_09.md b/source/c04/c04_09.md index cb03b79..dc401b9 100644 --- a/source/c04/c04_09.md +++ b/source/c04/c04_09.md @@ -1,5 +1,7 @@ # 4.9 MySQL的基本使用 +![](http://image.iswbm.com/20200602135014.png) + --- 数据库是每个后端程序员都必须扎实掌握的一项基本技能,而做为最主流的关系型数据库 MySQL,上手也极为容易,这里是我几年前自学 MySQL 时做下的笔记,现整理出来,一共两篇,这是第一篇。 diff --git a/source/c04/c04_09.rst b/source/c04/c04_09.rst index 1ac04d2..c6cb316 100755 --- a/source/c04/c04_09.rst +++ b/source/c04/c04_09.rst @@ -1,6 +1,8 @@ 4.9 MySQL的基本使用 =================== +|image0| + -------------- 数据库是每个后端程序员都必须扎实掌握的一项基本技能,而做为最主流的关系型数据库 @@ -226,7 +228,7 @@ MySQL,上手也极为容易,这里是我几年前自学 MySQL SQL中将数据类型分成了三大类: ``数值类型``, ``字符串类型``\ 和\ ``时间日期类型`` -|image0| +|image1| 4.1 数值型 ~~~~~~~~~~ @@ -236,13 +238,13 @@ SQL中将数据类型分成了三大类: ``数值类型``, 整数型有符号之分(正负) -|image1| +|image2| 创建表或新增字段的时候,如未指定,默认是有符号的。 | 那么如何指定呢? | 只要在建表或新增字段的时候,指定\ ``unsigned`` -| |image2| +| |image3| | **显示宽度** | 显示宽度,最终显示的位数。 @@ -250,7 +252,7 @@ SQL中将数据类型分成了三大类: ``数值类型``, | 零填充+显示宽度的意义: 保证数据格式 | 不足显示宽度的话,需要在前面增加前导0来满足宽度(需要设定zerofill) - |image3| + |image4| 4.1.2 小数型 ^^^^^^^^^^^^ @@ -268,7 +270,7 @@ SQL中将数据类型分成了三大类: ``数值类型``, | 分为两种精度 | ``Float``: 单精度, 占用4个字节存储数据, 精度范围大概为7位左右 | ``Double``: 双精度,占用8个字节存储数据, 精度范围大概为15位左右 -| |image4| +| |image5| 浮点的使用方式 @@ -298,16 +300,16 @@ SQL中将数据类型分成了三大类: ``数值类型``, HH:ii:ss格式与datetime完全一致 | ``Year``: 年份,两种形式, year(2)和year(4): 1901-2156 -|image5| +|image6| -timestamp默认是自动更新当前时间的(在记录创建或更新时更新时间) |image6| +timestamp默认是自动更新当前时间的(在记录创建或更新时更新时间) |image7| **插入数据** - time:可以是负数,而且可以是很大的负数 - year:可以使用2位数插入(>=70的为1970-1999,<=69的为2000-2069),也可以使用4位数 -|image7| +|image8| 4.3 字符串类型 ~~~~~~~~~~~~~~ @@ -336,7 +338,7 @@ timestamp默认是自动更新当前时间的(在记录创建或更新时更 ``varchar(10)``: 的确存了10个汉字, utf8环境, 10 \* 3 + 1 = 31(bytes),存储了3个汉字: 3 \* 3 + 1 = 10(bytes) -| |image8| +| |image9| 从上图来看,如果长度比较固定,譬如身份证,手机号码等,还是选用定长,因为定长相对变长效率高。 | 如果长度是浮动的,那么就要选择变长,可以在一定长度节省空间。 @@ -360,10 +362,10 @@ timestamp默认是自动更新当前时间的(在记录创建或更新时更 //如enum(‘男’,’女’,’不男不女’,’保密’); | ``使用``: 存储数据,只能存储上面定义好的数据 -|image9| +|image10| | 插入数据 -| |image10| +| |image11| **作用之一** @@ -376,7 +378,7 @@ timestamp默认是自动更新当前时间的(在记录创建或更新时更 | ``证明字段存储的数据是数值``: 将数据取出来 + 0 就可以判断出原来的数据存的到底是字符串还是数值: 如果是字符串最终结果永远为0, 否则就是其他值. -| |image11| 因为枚举实际存储的是数值,所以可以直接插入数值. |image12| +| |image12| 因为枚举实际存储的是数值,所以可以直接插入数值. |image13| 4.5 集合字符串 ~~~~~~~~~~~~~~ @@ -386,13 +388,13 @@ timestamp默认是自动更新当前时间的(在记录创建或更新时更 | ``集合使用方式``: 定义: Set(元素列表) | 使用: 可以使用元素列表中的元素(多个), 使用逗号分隔 -创建集合字段 |image13| 插入数据:可以使用多个元素字符串组合, -也可以直接插入数值 |image14| 查询结果 |image15| +创建集合字段 |image14| 插入数据:可以使用多个元素字符串组合, +也可以直接插入数值 |image15| 查询结果 |image16| 为什么会很这样? 98是什么东西?3为什么表示(篮球,足球)? | 原来在数据库内部,set是用二进制表示的。每个元素都对应一个二进制位。 -| |image16| +| |image17| 五、列属性 ---------- @@ -408,7 +410,7 @@ auto_increment,comment 两个值: NULL(默认的)和NOT NULL(不为空) -|image17| +|image18| 在实际应用过程中,应尽量保证数据不为空,空是没有任何意义的。并且不能参与运算。很有可能会出错。 5.2 列描述 @@ -417,7 +419,7 @@ auto_increment,comment | 列描述: comment, 描述, 没有实际含义: 是专门用来描述字段,会根据表创建语句保存: 用来给程序猿(数据库管理员)来进行了解的. -| |image18| +| |image19| 5.3 默认值 ~~~~~~~~~~ @@ -427,9 +429,9 @@ auto_increment,comment 默认值关键字: default -生效:只要插入数据的时候,不给值,就会自动赋予默认值 |image19| +生效:只要插入数据的时候,不给值,就会自动赋予默认值 |image20| 如果是全字段插入数据,那我们没法跳过,且又不知道默认值是什么?就可以使用default -|image20| +|image21| 5.4 主键 ~~~~~~~~ @@ -442,12 +444,12 @@ auto_increment,comment 优点:简单直接 -缺点:只能使用一个字段作为主键 |image21| +缺点:只能使用一个字段作为主键 |image22| **方案二** 在创建表的时候, 在所有的字段之后, 使用primary key(主键字段列表)来创建主键(如果有多个字段作为主键,可以是复合主键) -|image22| +|image23| **方案三** @@ -456,7 +458,7 @@ key(主键字段列表)来创建主键(如果有多个字段作为主键,可以 Alter table 表名 add primary key(字段列表); -``前提``: 表中字段对应的数据本身是独立的(不重复) |image23| +``前提``: 表中字段对应的数据本身是独立的(不重复) |image24| 5.4.2删除主键 ^^^^^^^^^^^^^ @@ -465,7 +467,7 @@ Alter table 表名 add primary key(字段列表); alter table 表名 drop primary key; -|image24| +|image25| 5.4.3 更新主键 ^^^^^^^^^^^^^^ @@ -490,10 +492,10 @@ Alter table 表名 add primary key(字段列表); 5.5.2 如何触发自增长 ^^^^^^^^^^^^^^^^^^^^ -|image25| +|image26| | 如何确定下一次是什么自增长呢? 可以通过查看表创建语句看到. -| |image26| +| |image27| 5.5.3 修改自增长 ^^^^^^^^^^^^^^^^ @@ -501,7 +503,7 @@ Alter table 表名 add primary key(字段列表); 由于一张表只能有一个自增长字段,所以要改变自增长字段,需先删除再增加。 | 修改下次自增长的值。必须大于当前自增长数字的最大值,小于不生效。 -| |image27| +| |image28| **修改起始值和步长** 查看自增长对应的变量: @@ -513,7 +515,7 @@ Alter table 表名 add primary key(字段列表); $ set auto_increment_increment = 5 $ set auto_increment_offset = 10 -|image28| +|image29| 5.5.4 删除自增长 ^^^^^^^^^^^^^^^^ @@ -525,7 +527,7 @@ Alter table 表名 add primary key(字段列表); alter table 表名 modify 字段 字段类型; # 不写自增长属性就行 -|image29| +|image30| 5.6 唯一键 ~~~~~~~~~~ @@ -573,14 +575,14 @@ key)就可以解决表中有多个字段需要唯一性约束的问题. 创建表的时候增加外键: 在所有的表字段之后,使用 foreign key(外键字段) references 外部表(主键字段) -|image30| +|image31| :: 在新增表之后增加外键: 修改表结构 Alter table 表名 add [constraint 外键名字] foreign key(外键字段) references 父表(主键字段); -|image31| +|image32| 5.7.2 更新/删除外键 ^^^^^^^^^^^^^^^^^^^ @@ -637,10 +639,10 @@ key)就可以解决表中有多个字段需要唯一性约束的问题. alter table 表名 drop foreign key 外键名; alter table 表名 add foreign key 外键名 references 父表(主键字段) on delete set null on update cascade; -|image32| +|image33| 来实例操作一下,级联模式和置空模式是怎样的。 ``更新操作: 级联更新`` -|image33| ``删除操作: 置空`` |image34| +|image34| ``删除操作: 置空`` |image35| 5.8 索引 ~~~~~~~~ @@ -692,7 +694,7 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 show variables like 'character_set%'; -|image35| +|image36| 图上这个字符集编码,就不会出错,我们可以正常的插入中文数据。 如果发现\ ``character_set_client``\ 和\ ``character_set_results``\ 是utf8,那很有可能会出错。 @@ -715,40 +717,41 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2017/08/25/599feabef31a6.png -.. |image1| image:: https://i.loli.net/2017/08/25/59a0345577dc0.png -.. |image2| image:: https://i.loli.net/2017/08/25/59a037907f5ee.png -.. |image3| image:: https://i.loli.net/2017/08/25/59a03b9be4226.png -.. |image4| image:: https://i.loli.net/2017/08/25/59a03e48e42b6.png -.. |image5| image:: https://i.loli.net/2017/08/25/59a040f5eb751.png -.. |image6| image:: https://i.loli.net/2017/08/25/59a0418497d8f.png -.. |image7| image:: https://i.loli.net/2017/08/25/59a041fe483c2.png -.. |image8| image:: https://i.loli.net/2017/08/26/59a0cbe8b2790.png -.. |image9| image:: https://i.loli.net/2017/08/26/59a0ce69d1c60.png -.. |image10| image:: https://i.loli.net/2017/08/26/59a0cebd074ab.png -.. |image11| image:: https://i.loli.net/2017/08/26/59a0cf774e2d4.png -.. |image12| image:: https://i.loli.net/2017/08/26/59a0cfddc52da.png -.. |image13| image:: https://i.loli.net/2017/08/26/59a0d14772b49.png -.. |image14| image:: https://i.loli.net/2017/08/26/59a0d17313a77.png -.. |image15| image:: https://i.loli.net/2017/08/26/59a0d19d4b54f.png -.. |image16| image:: https://i.loli.net/2017/08/26/59a0d20543ba3.png -.. |image17| image:: https://i.loli.net/2017/08/26/59a0d2ed1ecf2.png -.. |image18| image:: https://i.loli.net/2017/08/26/59a0d35dd3a86.png -.. |image19| image:: https://i.loli.net/2017/08/26/59a0d3d6b85e4.png -.. |image20| image:: https://i.loli.net/2017/08/26/59a0d464bb8f1.png -.. |image21| image:: https://i.loli.net/2017/08/26/59a0f8aa02375.png -.. |image22| image:: https://i.loli.net/2017/08/26/59a0f9141a909.png -.. |image23| image:: https://i.loli.net/2017/08/26/59a0f9be5b003.png -.. |image24| image:: https://i.loli.net/2017/08/26/59a0fa9a27f35.png -.. |image25| image:: https://i.loli.net/2017/08/26/59a0fc0cbb82e.png -.. |image26| image:: https://i.loli.net/2017/08/26/59a0fc5759662.png -.. |image27| image:: https://i.loli.net/2017/08/26/59a0fd26410ed.png -.. |image28| image:: https://i.loli.net/2017/08/26/59a0fd9bc76f9.png -.. |image29| image:: https://i.loli.net/2017/08/26/59a101add19bb.png -.. |image30| image:: https://i.loli.net/2017/08/27/59a25b5ba7837.png -.. |image31| image:: https://i.loli.net/2017/08/27/59a25be55ad8e.png -.. |image32| image:: https://i.loli.net/2017/08/27/59a25ecf889b5.png -.. |image33| image:: https://i.loli.net/2017/08/27/59a261734e896.png -.. |image34| image:: https://i.loli.net/2017/08/27/59a261734ff8b.png -.. |image35| image:: https://i.loli.net/2017/08/25/599fc9aa85094.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2017/08/25/599feabef31a6.png +.. |image2| image:: https://i.loli.net/2017/08/25/59a0345577dc0.png +.. |image3| image:: https://i.loli.net/2017/08/25/59a037907f5ee.png +.. |image4| image:: https://i.loli.net/2017/08/25/59a03b9be4226.png +.. |image5| image:: https://i.loli.net/2017/08/25/59a03e48e42b6.png +.. |image6| image:: https://i.loli.net/2017/08/25/59a040f5eb751.png +.. |image7| image:: https://i.loli.net/2017/08/25/59a0418497d8f.png +.. |image8| image:: https://i.loli.net/2017/08/25/59a041fe483c2.png +.. |image9| image:: https://i.loli.net/2017/08/26/59a0cbe8b2790.png +.. |image10| image:: https://i.loli.net/2017/08/26/59a0ce69d1c60.png +.. |image11| image:: https://i.loli.net/2017/08/26/59a0cebd074ab.png +.. |image12| image:: https://i.loli.net/2017/08/26/59a0cf774e2d4.png +.. |image13| image:: https://i.loli.net/2017/08/26/59a0cfddc52da.png +.. |image14| image:: https://i.loli.net/2017/08/26/59a0d14772b49.png +.. |image15| image:: https://i.loli.net/2017/08/26/59a0d17313a77.png +.. |image16| image:: https://i.loli.net/2017/08/26/59a0d19d4b54f.png +.. |image17| image:: https://i.loli.net/2017/08/26/59a0d20543ba3.png +.. |image18| image:: https://i.loli.net/2017/08/26/59a0d2ed1ecf2.png +.. |image19| image:: https://i.loli.net/2017/08/26/59a0d35dd3a86.png +.. |image20| image:: https://i.loli.net/2017/08/26/59a0d3d6b85e4.png +.. |image21| image:: https://i.loli.net/2017/08/26/59a0d464bb8f1.png +.. |image22| image:: https://i.loli.net/2017/08/26/59a0f8aa02375.png +.. |image23| image:: https://i.loli.net/2017/08/26/59a0f9141a909.png +.. |image24| image:: https://i.loli.net/2017/08/26/59a0f9be5b003.png +.. |image25| image:: https://i.loli.net/2017/08/26/59a0fa9a27f35.png +.. |image26| image:: https://i.loli.net/2017/08/26/59a0fc0cbb82e.png +.. |image27| image:: https://i.loli.net/2017/08/26/59a0fc5759662.png +.. |image28| image:: https://i.loli.net/2017/08/26/59a0fd26410ed.png +.. |image29| image:: https://i.loli.net/2017/08/26/59a0fd9bc76f9.png +.. |image30| image:: https://i.loli.net/2017/08/26/59a101add19bb.png +.. |image31| image:: https://i.loli.net/2017/08/27/59a25b5ba7837.png +.. |image32| image:: https://i.loli.net/2017/08/27/59a25be55ad8e.png +.. |image33| image:: https://i.loli.net/2017/08/27/59a25ecf889b5.png +.. |image34| image:: https://i.loli.net/2017/08/27/59a261734e896.png +.. |image35| image:: https://i.loli.net/2017/08/27/59a261734ff8b.png +.. |image36| image:: https://i.loli.net/2017/08/25/599fc9aa85094.png diff --git a/source/c04/c04_10.md b/source/c04/c04_10.md index 384ef36..7336b0f 100644 --- a/source/c04/c04_10.md +++ b/source/c04/c04_10.md @@ -1,5 +1,7 @@ # 4.10 MySQL的高级进阶 +![](http://image.iswbm.com/20200602135014.png) + --- ## 一、增/删/改数据 diff --git a/source/c04/c04_10.rst b/source/c04/c04_10.rst index b11822d..803b8ac 100755 --- a/source/c04/c04_10.rst +++ b/source/c04/c04_10.rst @@ -1,6 +1,8 @@ 4.10 MySQL的高级进阶 ==================== +|image0| + -------------- 一、增/删/改数据 @@ -42,7 +44,7 @@ create table 新表 like 源表; -|image0| +|image1| 再插入表数据 @@ -141,7 +143,7 @@ Where原理: where是唯一一个直接从磁盘获取数据的时候就开始 从磁盘取出一条记录, 开始进行where判断: 判断的结果如果成立保存到内存;如果失败直接放弃. -|image1| +|image2| 2.5 group by子句 ~~~~~~~~~~~~~~~~ @@ -168,15 +170,15 @@ Where原理: where是唯一一个直接从磁盘获取数据的时候就开始 | ``group_concat(字段)`` | 可以实现分组后,对某个字段进行连接 -| |image2| +| |image3| 回溯统计(汇总统计)\ ``with rollup`` 任何一个分组后都会有一个小组, 最后都需要向上级分组进行汇报统计: 根据当前分组的字段. 这就是回溯统计: 回溯统计的时候会将分组字段置空. -如果只对一个字段分组汇总统计 |image3| 对两个字段进行分组统计汇总 -|image4| +如果只对一个字段分组汇总统计 |image4| 对两个字段进行分组统计汇总 +|image5| 2.6 Having子句 ~~~~~~~~~~~~~~ @@ -190,11 +192,11 @@ where是针对磁盘数据进行判断: 进入到内存之后,会进行分组操 Having能做where能做的几乎所有事情, 但是where却不能做having能做的很多事情. -``比较`` 分组统计的结果或者说统计函数都只有having能够使用. |image5| +``比较`` 分组统计的结果或者说统计函数都只有having能够使用. |image6| Having能够使用字段别名: where不能: where是从磁盘取数据,而名字只可能是字段名: -别名是在字段进入到内存后才会产生. |image6| +别名是在字段进入到内存后才会产生. |image7| 2.7 Order by子句 ~~~~~~~~~~~~~~~~ @@ -207,20 +209,20 @@ Order by: 排序, 根据某个字段进行升序或者降序排序, 依赖校对 Order by 字段名 [asc|desc]; -- asc是升序(默认的),desc是降序 排序可以进行多字段排序: 先根据某个字段进行排序, -然后排序好的内部,再按照某个数据进行再次排序: |image7| +然后排序好的内部,再按照某个数据进行再次排序: |image8| 2.8 Limit子句 ~~~~~~~~~~~~~ limit子句:限制返回数据的量。 -有两种使用方式 方案1: 只用来限制长度(数据量): limit 数据量; |image8| -方案2: 限制起始位置,限制数量: limit 起始位置,长度; |image9| +有两种使用方式 方案1: 只用来限制长度(数据量): limit 数据量; |image9| +方案2: 限制起始位置,限制数量: limit 起始位置,长度; |image10| 2.9 执行顺序【重要】 ~~~~~~~~~~~~~~~~~~~~ -|image10| +|image11| 三、连接查询 ------------ @@ -268,11 +270,11 @@ limit子句:限制返回数据的量。 左表 [inner] join 右表 on 左表.字段 = 右表.字段; on表示连接条件:,条件字段就是代表相同的业务含义(如my_student.c_id和my_class.id) -|image11| 字段别名以及表别名的使用: +|image12| 字段别名以及表别名的使用: 在查询数据的时候,不同表有同名字段,这个时候需要加上表名才能区分, -而表名太长, 通常可以使用别名. |image12| +而表名太长, 通常可以使用别名. |image13| -内连接还可以使用where代替on关键字(where没有on效率高) |image13| +内连接还可以使用where代替on关键字(where没有on效率高) |image14| 3.3 外连接 ~~~~~~~~~~ @@ -291,7 +293,7 @@ limit子句:限制返回数据的量。 【基本语法】 左表 left/right join 右表 on 左表.字段 = 右表.字段; -|image14| 虽然左连接和右连接有主表差异, 但是显示的结果: +|image15| 虽然左连接和右连接有主表差异, 但是显示的结果: 左表的数据在左边,右表数据在右边. 左连接和右连接可以互转. 3.4 自然连接 @@ -349,7 +351,7 @@ limit子句:限制返回数据的量。 All: 保留所有(不管重复) Distinct: 去重(整个重复): 默认的 -|image15| 联合查询只要求字段数一样, 跟数据类型无关 |image16| +|image16| 联合查询只要求字段数一样, 跟数据类型无关 |image17| 4.2 它的意义 ~~~~~~~~~~~~ @@ -363,8 +365,8 @@ limit子句:限制返回数据的量。 4.3 order by的使用 ~~~~~~~~~~~~~~~~~~ -在联合查询中: order by不能直接使用,需要对查询语句使用括号才行 |image17| -若要orderby生效: 必须搭配limit: limit使用限定的最大数即可. |image18| +在联合查询中: order by不能直接使用,需要对查询语句使用括号才行 |image18| +若要orderby生效: 必须搭配limit: limit使用限定的最大数即可. |image19| 五、子查询 ---------- @@ -417,7 +419,7 @@ limit子句:限制返回数据的量。 Select id from my_class where c_name = ‘PHP0710’; -- id一定只有一个值(一行一列) | 标量子查询实现 -| |image19| +| |image20| 5.3 列子查询 ~~~~~~~~~~~~ @@ -439,7 +441,7 @@ limit子句:限制返回数据的量。 Select id from my_class; -列子查询实现 |image20| +列子查询实现 |image21| ``IN``\ :在指定项内,同 IN(项1,项2,…)。 等价于 ``= ANY`` @@ -468,7 +470,7 @@ limit子句:限制返回数据的量。 Select max(age),max(height) from my_student; -``行子查询``: 需要构造\ ``行元素``: 行元素由多个字段构成 |image21| +``行子查询``: 需要构造\ ``行元素``: 行元素由多个字段构成 |image22| 5.5表子查询 ~~~~~~~~~~~ @@ -490,7 +492,7 @@ limit子句:限制返回数据的量。 Select * from my_student group by c_id; -- 每个班选出第一个学生 -``表子查询``: ``from子查询``: 得到的结果作为from的数据源 |image22| +``表子查询``: ``from子查询``: 得到的结果作为from的数据源 |image23| 5.6 exits子查询 ~~~~~~~~~~~~~~~ @@ -505,7 +507,7 @@ limit子句:限制返回数据的量。 **举个例子**\ 更好理解 -|image23| +|image24| 六、系统查询 ------------ @@ -541,28 +543,29 @@ limit子句:限制返回数据的量。 关注公众号,获取最新干货! -.. |image0| image:: https://ooo.0o0.ooo/2017/08/26/59a1429722061.png -.. |image1| image:: https://i.loli.net/2017/08/27/59a2307e7b6bc.png -.. |image2| image:: https://i.loli.net/2017/08/27/59a2329680727.png -.. |image3| image:: https://i.loli.net/2017/08/27/59a2346a1c446.png -.. |image4| image:: https://i.loli.net/2017/08/27/59a2346a304d9.png -.. |image5| image:: https://i.loli.net/2017/08/27/59a235b1adb8e.png -.. |image6| image:: https://i.loli.net/2017/08/27/59a235b1aee6d.png -.. |image7| image:: https://i.loli.net/2017/08/27/59a23643148a5.png -.. |image8| image:: https://i.loli.net/2017/08/27/59a23722a893b.png -.. |image9| image:: https://i.loli.net/2017/08/27/59a23722bcedf.png -.. |image10| image:: https://i.loli.net/2017/08/27/59a2416c37929.png -.. |image11| image:: https://i.loli.net/2017/08/27/59a24f9d9e1eb.png -.. |image12| image:: https://i.loli.net/2017/08/27/59a24f9db31ba.png -.. |image13| image:: https://i.loli.net/2017/08/27/59a2507e611d2.png -.. |image14| image:: https://i.loli.net/2017/08/27/59a2517b690fd.png -.. |image15| image:: https://i.loli.net/2017/08/27/59a26a86275ed.png -.. |image16| image:: https://i.loli.net/2017/08/27/59a26a8612ff5.png -.. |image17| image:: https://i.loli.net/2017/08/27/59a26b27cd2bb.png -.. |image18| image:: https://i.loli.net/2017/08/27/59a26b26bffdc.png -.. |image19| image:: https://i.loli.net/2017/08/27/59a26f38468d8.png -.. |image20| image:: https://i.loli.net/2017/08/27/59a26f3847710.png -.. |image21| image:: https://i.loli.net/2017/08/27/59a2756c66952.png -.. |image22| image:: https://i.loli.net/2017/08/27/59a27651cd78e.png -.. |image23| image:: https://i.loli.net/2017/08/27/59a2784f6562d.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://ooo.0o0.ooo/2017/08/26/59a1429722061.png +.. |image2| image:: https://i.loli.net/2017/08/27/59a2307e7b6bc.png +.. |image3| image:: https://i.loli.net/2017/08/27/59a2329680727.png +.. |image4| image:: https://i.loli.net/2017/08/27/59a2346a1c446.png +.. |image5| image:: https://i.loli.net/2017/08/27/59a2346a304d9.png +.. |image6| image:: https://i.loli.net/2017/08/27/59a235b1adb8e.png +.. |image7| image:: https://i.loli.net/2017/08/27/59a235b1aee6d.png +.. |image8| image:: https://i.loli.net/2017/08/27/59a23643148a5.png +.. |image9| image:: https://i.loli.net/2017/08/27/59a23722a893b.png +.. |image10| image:: https://i.loli.net/2017/08/27/59a23722bcedf.png +.. |image11| image:: https://i.loli.net/2017/08/27/59a2416c37929.png +.. |image12| image:: https://i.loli.net/2017/08/27/59a24f9d9e1eb.png +.. |image13| image:: https://i.loli.net/2017/08/27/59a24f9db31ba.png +.. |image14| image:: https://i.loli.net/2017/08/27/59a2507e611d2.png +.. |image15| image:: https://i.loli.net/2017/08/27/59a2517b690fd.png +.. |image16| image:: https://i.loli.net/2017/08/27/59a26a86275ed.png +.. |image17| image:: https://i.loli.net/2017/08/27/59a26a8612ff5.png +.. |image18| image:: https://i.loli.net/2017/08/27/59a26b27cd2bb.png +.. |image19| image:: https://i.loli.net/2017/08/27/59a26b26bffdc.png +.. |image20| image:: https://i.loli.net/2017/08/27/59a26f38468d8.png +.. |image21| image:: https://i.loli.net/2017/08/27/59a26f3847710.png +.. |image22| image:: https://i.loli.net/2017/08/27/59a2756c66952.png +.. |image23| image:: https://i.loli.net/2017/08/27/59a27651cd78e.png +.. |image24| image:: https://i.loli.net/2017/08/27/59a2784f6562d.png diff --git a/source/c04/c04_11.md b/source/c04/c04_11.md index dd6891d..400257a 100644 --- a/source/c04/c04_11.md +++ b/source/c04/c04_11.md @@ -1,5 +1,7 @@ # 4.11 不能不会的远程调试技巧 +![](http://image.iswbm.com/20200602135014.png) + 这一篇文章是以前的文章,有的朋友已经看过,但是没有关系,因为这次我准备介绍这几大调试工具都是如何调试,又该如何选择。 一般情况下,我们开发调试都是在个人PC上完成,遇到问题,开一下 `Pycharm` 的调试器,很快就能找到问题所在。 diff --git a/source/c04/c04_11.rst b/source/c04/c04_11.rst index 38235d9..b88c0a8 100644 --- a/source/c04/c04_11.rst +++ b/source/c04/c04_11.rst @@ -1,6 +1,8 @@ 4.11 不能不会的远程调试技巧 =========================== +|image0| + 这一篇文章是以前的文章,有的朋友已经看过,但是没有关系,因为这次我准备介绍这几大调试工具都是如何调试,又该如何选择。 一般情况下,我们开发调试都是在个人PC上完成,遇到问题,开一下 ``Pycharm`` @@ -29,14 +31,14 @@ Debug 。而远程调试需要不少前置步骤,这些设置过程,也是 首先,要在Pycharm中新建一个空的项目,后面我们拉服务器上的项目代码就会放置在这个项目目录下。我这边的名字是 NOVA,你可以自己定义。 -|image0| +|image1| 4.11.2 配置连接服务器 ~~~~~~~~~~~~~~~~~~~~~ Tools -> Deployment -> configuration -|image1| +|image2| 添加一个\ ``Server`` @@ -44,7 +46,7 @@ Tools -> Deployment -> configuration - Type:设定为SFTP -|image2| +|image3| 点击\ ``OK``\ 后,进入如下界面,你可以按我的备注,填写信息: @@ -57,37 +59,37 @@ Tools -> Deployment -> configuration 这里请注意,要确保你的电脑可以ssh连接到你的服务器,不管是密钥登陆还是密码登陆,如果开启了白名单限制要先解除。 -|image3| +|image4| 填写完成后,切换到\ ``Mappings``\ 选项卡,在箭头位置,填写\ ``\`` -|image4| +|image5| 以上服务器信息配置,全部正确填写完成后,点击\ ``OK`` 接下来,我们要连接远程服务器了。 Tools -> Deployment -> Browse Remote Host -|image5| +|image6| 4.11.3 下载项目代码 ~~~~~~~~~~~~~~~~~~~ 如果之前填写的服务器登陆信息准确无误的话,现在就可以看到远程的项目代码。 -|image6| +|image7| 选择下载远程代码要本地。 -|image7| +|image8| 下载完成提示。 -|image8| +|image9| 现在的IDE界面应该是这样子的。 -|image9| +|image10| 4.11.4 下载远程解释器 ~~~~~~~~~~~~~~~~~~~~~ @@ -98,11 +100,11 @@ Host 进入 File -> Settings 按图示,添加远程解释器。 -|image10| +|image11| 填写远程服务器信息,跟之前的一样,不再赘述。 -|image11| +|image12| 点击\ ``OK``\ 后,会自动下载远程解释器。如果你的项目比较大,这个时间可能会比较久,请耐心等待。 @@ -137,19 +139,19 @@ Host 看到那个\ ``ExecStart``\ 没有?那个就是我们程序的入口。 我们只要将其拷贝至我们的Pycharm中,并向远程同步该文件。 -|image12| +|image13| 4.11.6 调试前设置 ~~~~~~~~~~~~~~~~~ 开启代码自动同步,这样,我们对代码的修改Pycharm都能识别,并且为我们提交到远程服务器。 -|image13| +|image14| 开启 ``Gevent compatible``\ ,如果不开启,在调试过程中,很可能出现无法调试,或者无法追踪/查看变量等问题。 -|image14| +|image15| 4.11.7 开始调试代码 ~~~~~~~~~~~~~~~~~~~ @@ -158,11 +160,11 @@ Host 如果你的程序入口,需要引入参数,这是经常有的事,可以的这里配置。 -|image15| +|image16| 配置完点击保存即可。 -|image16| +|image17| 4.11.8 友情提醒 ~~~~~~~~~~~~~~~ @@ -178,21 +180,22 @@ Host 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190113104817.png -.. |image1| image:: http://image.python-online.cn/20190113105512.png -.. |image2| image:: http://image.python-online.cn/20190113105858.png -.. |image3| image:: http://image.python-online.cn/20190113105931.png -.. |image4| image:: http://image.python-online.cn/20190113110928.png -.. |image5| image:: http://image.python-online.cn/20190113111042.png -.. |image6| image:: http://image.python-online.cn/20190113111151.png -.. |image7| image:: http://image.python-online.cn/20190113111217.png -.. |image8| image:: http://image.python-online.cn/20190113111248.png -.. |image9| image:: http://image.python-online.cn/20190113111307.png -.. |image10| image:: http://image.python-online.cn/20190113111747.png -.. |image11| image:: http://image.python-online.cn/20190113111828.png -.. |image12| image:: http://image.python-online.cn/20190113112004.png -.. |image13| image:: http://image.python-online.cn/20190113112055.png -.. |image14| image:: http://image.python-online.cn/20190113113211.png -.. |image15| image:: http://image.python-online.cn/20190113112456.png -.. |image16| image:: http://image.python-online.cn/20190113112649.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190113104817.png +.. |image2| image:: http://image.python-online.cn/20190113105512.png +.. |image3| image:: http://image.python-online.cn/20190113105858.png +.. |image4| image:: http://image.python-online.cn/20190113105931.png +.. |image5| image:: http://image.python-online.cn/20190113110928.png +.. |image6| image:: http://image.python-online.cn/20190113111042.png +.. |image7| image:: http://image.python-online.cn/20190113111151.png +.. |image8| image:: http://image.python-online.cn/20190113111217.png +.. |image9| image:: http://image.python-online.cn/20190113111248.png +.. |image10| image:: http://image.python-online.cn/20190113111307.png +.. |image11| image:: http://image.python-online.cn/20190113111747.png +.. |image12| image:: http://image.python-online.cn/20190113111828.png +.. |image13| image:: http://image.python-online.cn/20190113112004.png +.. |image14| image:: http://image.python-online.cn/20190113112055.png +.. |image15| image:: http://image.python-online.cn/20190113113211.png +.. |image16| image:: http://image.python-online.cn/20190113112456.png +.. |image17| image:: http://image.python-online.cn/20190113112649.png diff --git a/source/c04/c04_12.md b/source/c04/c04_12.md index 84f5860..88e456f 100644 --- a/source/c04/c04_12.md +++ b/source/c04/c04_12.md @@ -1,5 +1,7 @@ # 4.12 服务器调试神器:pdb +![](http://image.iswbm.com/20200602135014.png) + 上一篇文章,讲的是 Pycharm 的远程调试,若你还没学会,可以点击这里进行查看。 [不能不会的远程调试技巧](http://python-online.cn/zh_CN/latest/c04/c04_11.html) diff --git a/source/c04/c04_12.rst b/source/c04/c04_12.rst index 0ca9199..ec4728f 100644 --- a/source/c04/c04_12.rst +++ b/source/c04/c04_12.rst @@ -1,6 +1,8 @@ 4.12 服务器调试神器:pdb ======================== +|image0| + 上一篇文章,讲的是 Pycharm 的远程调试,若你还没学会,可以点击这里进行查看。 @@ -54,7 +56,7 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 使用这个方式进入调试模式,会在脚本的第一行开始单步调试。 -|image0| +|image1| 对于单文件的脚本并没有什么问题,如果是一个大型的项目,项目里有很多的文件,使用这种方式只能大大降低我们的效率。 @@ -70,10 +72,10 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 然后执行时,也不需要再指定\ ``-m pdb``\ 了,直接\ ``python pdb_demo.py`` ,就会直接在这个地方暂停。 -|image1| - |image2| +|image3| + 4.12.3 调试指令 --------------- @@ -157,14 +159,14 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 其实你大可不必死记这些命令,忘记的时候,只要敲入\ ``help``\ 并回车,就可以看所有的指令了。 -|image3| +|image4| 4.12.4 开始调试 --------------- 这里就几个最常用的指定,来演示一遍。 -|image4| +|image5| 这个调试过程,我加了些注释,你应该能够很轻易地理解这种调试方式。 @@ -179,9 +181,10 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190118000111.png -.. |image1| image:: http://image.python-online.cn/20190118000234.png -.. |image2| image:: http://image.python-online.cn/20190118000557.png -.. |image3| image:: http://image.python-online.cn/20190118083809.png -.. |image4| image:: http://image.python-online.cn/20190118005507.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190118000111.png +.. |image2| image:: http://image.python-online.cn/20190118000234.png +.. |image3| image:: http://image.python-online.cn/20190118000557.png +.. |image4| image:: http://image.python-online.cn/20190118083809.png +.. |image5| image:: http://image.python-online.cn/20190118005507.png diff --git a/source/c04/c04_13.md b/source/c04/c04_13.md index fa1b818..55ce718 100644 --- a/source/c04/c04_13.md +++ b/source/c04/c04_13.md @@ -1,5 +1,7 @@ # 4.13 命令行解析工具:argparse +![](http://image.iswbm.com/20200602135014.png) + Python 做为一个脚本语言,可以很方便地写各种工具。当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现)。 如果要以命令行执行,那你需要解析一个命令行参数解析的模块来帮你做这个苦力活。 diff --git a/source/c04/c04_13.rst b/source/c04/c04_13.rst index fb0f9fc..1928a73 100644 --- a/source/c04/c04_13.rst +++ b/source/c04/c04_13.rst @@ -1,6 +1,8 @@ 4.13 命令行解析工具:argparse ============================= +|image0| + Python 做为一个脚本语言,可以很方便地写各种工具。当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现)。 @@ -435,3 +437,6 @@ arguments)。 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c04/c04_14.md b/source/c04/c04_14.md index aa76f4a..0981998 100644 --- a/source/c04/c04_14.md +++ b/source/c04/c04_14.md @@ -1,5 +1,7 @@ # 4.14 虚拟环境:Pipenv +![](http://image.iswbm.com/20200602135014.png) + 以前一直使用pip+virtualenv+virtualwrapper管理模块和环境, 但是virtualwrapper在windows上使用不太方便,而且包和环境分开管理确实经常不记得哪个是哪个了。 diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 94d2edf..143052f 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -1,6 +1,8 @@ 4.14 虚拟环境:Pipenv ===================== +|image0| + 以前一直使用pip+virtualenv+virtualwrapper管理模块和环境, 但是virtualwrapper在windows上使用不太方便,而且包和环境分开管理确实经常不记得哪个是哪个了。 @@ -26,11 +28,11 @@ 如果你的电脑是 windows 的。 -|image0| +|image1| 需要将如标示路径,加入到 环境变量 PATH 中。 -|image1| +|image2| 然后需要重启一下,CMD 终端才能够刷新环境变量。 @@ -58,13 +60,13 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 这边以安装 python2 版本的虚拟环境为例说明。 -|image2| +|image3| 如果你原项目使用的是 requirements.txt 这个管理包的方式,这时候执行 ``pipenv --tow`` 创建一个虚拟环境后,会找到 requirements.txt ,并根据这里面的依赖包生成 Pipfile文件。 -|image3| +|image4| 4.14.3 查询虚拟环境 ------------------- @@ -82,7 +84,7 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 演示如下: -|image4| +|image5| 4.14.4 操作虚拟环境 ------------------- @@ -102,7 +104,7 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 执行 ``pipenv shell`` 就可以进入这个虚拟环境,在头部会有虚拟环境的标识名称。有这个标识,说明已经进入虚拟环境。 -|image5| +|image6| .. code:: python @@ -157,12 +159,12 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 打印该虚拟环境下所有包的依赖关系图 -|image6| +|image7| 有的python第三方包旧版本会有安全漏洞,使用 pipenv check 可以检查安全漏洞。 -|image7| +|image8| .env`文件,用来存放一些环境变量。 @@ -173,12 +175,13 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn -.. |image1| image:: http://image.python-online.cn/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX -.. |image2| image:: http://image.python-online.cn/20190612211330.png -.. |image3| image:: http://image.python-online.cn/20190612213015.png -.. |image4| image:: http://image.python-online.cn/20190612213950.png -.. |image5| image:: http://image.python-online.cn/20190612211925.png -.. |image6| image:: http://image.python-online.cn/20190614000336.png -.. |image7| image:: http://image.python-online.cn/20190612215924.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn +.. |image2| image:: http://image.python-online.cn/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX +.. |image3| image:: http://image.python-online.cn/20190612211330.png +.. |image4| image:: http://image.python-online.cn/20190612213015.png +.. |image5| image:: http://image.python-online.cn/20190612213950.png +.. |image6| image:: http://image.python-online.cn/20190612211925.png +.. |image7| image:: http://image.python-online.cn/20190614000336.png +.. |image8| image:: http://image.python-online.cn/20190612215924.png diff --git a/source/c04/c04_15.md b/source/c04/c04_15.md index 76c3140..721d413 100644 --- a/source/c04/c04_15.md +++ b/source/c04/c04_15.md @@ -1,5 +1,7 @@ # 4.15 30个 PyCharm 实用技巧 +![](http://image.iswbm.com/20200602135014.png) + 刚开始做公众号的时候,更新频率正常是一周两到三篇。老读者应该有注意到,以前都是写系列教程,对于读者而言,系列教程会更加友好,学习起来会更容易深入浅出,而对于作者来说,写系列教程,更有一种使命感,而这种使命感是维持更新一大动力。 不写文章的朋友,可能不会知道,写文章也是很费脑子的事情,由于知识的诅咒的存在,往往我会认为我知道的事情,大家也都知道,分享的欲望其实并没有那么强烈,久而久之,这种恶性循环会让我产生更多的焦虑感,产出也越发下降。为了改变这种现状,我打算从今天开始,重新走以前的风格,继续以系列来输出文章。可能会有多个系列在同时进行着,提前告知一下。 diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index f7babd9..9085cf3 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -1,6 +1,8 @@ 4.15 30个 PyCharm 实用技巧 ========================== +|image0| + 刚开始做公众号的时候,更新频率正常是一周两到三篇。老读者应该有注意到,以前都是写系列教程,对于读者而言,系列教程会更加友好,学习起来会更容易深入浅出,而对于作者来说,写系列教程,更有一种使命感,而这种使命感是维持更新一大动力。 不写文章的朋友,可能不会知道,写文章也是很费脑子的事情,由于知识的诅咒的存在,往往我会认为我知道的事情,大家也都知道,分享的欲望其实并没有那么强烈,久而久之,这种恶性循环会让我产生更多的焦虑感,产出也越发下降。为了改变这种现状,我打算从今天开始,重新走以前的风格,继续以系列来输出文章。可能会有多个系列在同时进行着,提前告知一下。 @@ -32,19 +34,19 @@ Working directory: $ProjectFileDir$ Output filters: $FILE_PATH$\:$LINE$\:$COLUMN$\:.* -|image0| +|image1| 我随意写了一段不符合 pep8 规范的代码。 -|image1| +|image2| 点击右键,选择 ``External Tools`` -> ``AutoPep8`` -|image2| +|image3| 看一下效果,还是挺明显的。 -|image3| +|image4| 你可能会说,Pycharm 本身就自带这个功能了呀,快捷键 ``Command``\ +\ ``Option``\ +\ ``L`` @@ -77,7 +79,7 @@ Pycharm 的隐藏的一个功能 ``Show History``\ ,你可以看到这里有个记录板。如果你想恢复删除的文件,就在删除的记录项点击右键,选择 ``Revert`` 即可恢复。 -|image4| +|image5| 4.15.3 拥抱Vim,远离鼠标 ------------------------ @@ -91,7 +93,7 @@ Pycharm 中 使用 vim 来编辑代码。 安装方法如下,安装完后需要重启 Pycharm 生效。 -|image5| +|image6| 4.15.4 代码模板,效率编码 ------------------------- @@ -99,11 +101,11 @@ Pycharm 中 使用 vim 来编辑代码。 Pycharm 提供的这个代码模板,可以说是相当实用的一个功能了。它可以在你新建一个文件时,按照你预设的模板给你生成一段内容,比如解释器路径,编码方法,作者详细信息等 -|image6| +|image7| 按照上图模板,生成的效果如下。 -|image7| +|image8| 除了新建文件时可以初始化文件,在开发编写代码时,也同样使用 Pycharm 中自带的实用的代码模板,提高你的编码效率。 @@ -111,16 +113,16 @@ Pycharm 当你在键盘中敲入 ``Command`` + ``J`` 时,就可以调出一个面板,从下图可以看出里面有许多预设的模板。 -|image8| +|image9| 如果我们想选择最后一个 main ,可以继续键入 main,然后就可以直接生成如下这段平时都要手动敲入的代码。 -|image9| +|image10| 这里再举个例子,for 循环 可以这样写。 -|image10| +|image11| 4.15.5 使用书签,快速定位 ------------------------- @@ -149,7 +151,7 @@ command + 鼠标左键 加了 ``1`` 这个序号,下次你就可以使用 ``Control`` + ``1`` 直接跳转到这个位置。 -|image11| +|image12| 当然你也可以不加,不加的话就是匿名书签了。你可以使用 ``Shift`` + ``F11`` 展示所有的书签,再进行跳转。 @@ -171,16 +173,16 @@ Pycharm 中可以像 IPython Shell 和 Jupyter Notebook 假如我在调试如下几行简单的代码。在第 3 行处打了个断点。然后点击图示位置 ``Show Python Prompt`` 按钮。 -|image12| +|image13| 就进入了 ``Python Shell`` 的界面,这个Shell 环境和我们当前运行的程序环境是打通的,变量之间可以互相访问,这下你可以轻松地进行调试了。 -|image13| +|image14| 上面我们打了个断点,是为了方便说明这个效果。并不是说一定要打断点。如果不打断点,在脚本执行完成后,也仍然可以在这个界面查看并操作所有变量。 -|image14| +|image15| 现在我们已经可以满足我们的调试的需求,但是每次运行脚本,都要手动点击 ``Show Python Prompt`` @@ -188,11 +190,11 @@ Pycharm 中可以像 IPython Shell 和 Jupyter Notebook 你需要点击图示位置 ``Edit Configurations`` 处。 -|image15| +|image16| 然后在这里打勾选中。 -|image16| +|image17| 设置上之后,之后你每次运行后脚本后,都会默认为你存储所有变量的值,并为你打开 console 命令行调试界面。 @@ -203,12 +205,12 @@ debug 模式运行项目,并打断点。 使用方法就是,在你打了断点后,在图示位置处,点击右键使用 ``Evaluate Expression`` -|image17| +|image18| 就弹出了一个 ``Evaluate Expression`` 窗口,这里 可以运行命令表达式,直接操作变量。 -|image18| +|image19| 4.15.7 指定参数执行脚本 ----------------------- @@ -227,16 +229,16 @@ debug 模式运行项目,并打断点。 对于刚使用 Pycharm 的同学,可能并不知道 Pycharm 也是可以指定参数的。点击下图位置 -|image19| +|image20| 进入设置面板,在 ``Script parameters`` 中填入参数即可。 -|image20| +|image21| 同时在上图的底部,你可以看到,这里可以很方便的切换 解释器,比你跑到这边来要容易得多吧 -|image21| +|image22| 4.15. 8 搜索时过滤测试文件 -------------------------- @@ -248,20 +250,20 @@ debug 模式运行项目,并打断点。 时带来了不小的困扰,你可以从下图的搜索结果中感受一下,搜索一个函数,test 文件里的结果比 正常文件要多很多。 -|image22| +|image23| 这些测试文件的搜索结果,对于我们看源代码不仅没有任何帮助的,更重要的是还干扰视线。于是我就研究了一下,从文件名入手,只要在 ``File mask`` 里填写 ``!test*`` 可以将这些test文件过滤掉。搜索结果一下子清晰很多。 -|image23| +|image24| 4.15.9 关闭烦人的灯泡提示 ------------------------- 本来没有想写这个的,但是知乎上有一位朋友有这个需求,那我研究了下。 -|image24| +|image25| 先来说下这个灯泡提示是什么,有什么用? @@ -275,7 +277,7 @@ debug 模式运行项目,并打断点。 我研究了下,Pycharm (2018版本)里是有开关按钮的,将下图中的这个选项(\ ``Show intention bulb``\ )取消勾选,就可以关闭这个功能。 -|image25| +|image26| 4.15.10 关闭碍眼的波浪线 ------------------------ @@ -283,7 +285,7 @@ debug 模式运行项目,并打断点。 下面我先给出了一小段代码示例,思考一下,为什么name,my_name 不会有波浪线,而 myname 和 wangbm 会有波浪线呢? -|image26| +|image27| Pycharm 本身会实时地对变量名进行检查,如果变量名不是一个已存在的英文单词,就会出现一条波浪线,当一个变量里有多个单词时,Python @@ -296,16 +298,16 @@ myname 会被当成是一个单词对待,由于它在单词库里并没有它 这种风格的变量命名方法,像下面这样(随便找了一段 cloudinit 的代码),是让人挺不舒服的,总有一种代码有 bug 的错觉。 -|image27| +|image28| 那么如何关闭这个非语法级别的波浪线呢?很简单,它的开关就在你的右下角那个像 人头像 一样的按钮 -|image28| +|image29| 然后选择 ``Syntax`` 级别的即可。同样一段代码,效果如下,干净了很多。 -|image29| +|image30| 4.15.11 一键进行代码性能分析 ---------------------------- @@ -348,11 +350,11 @@ cProfile,在某些框架中,也内置了中间件帮助你进行性能分析 点击 Run -> Profile ‘程序’ ,即可进行性能分析。 -|image30| +|image31| 运行完毕后,会自动跳出一个性能统计界面。 -|image31| +|image32| 性能统计界面由Name、Call Count、Time(ms)、Own Time(ms) ,4列组成一个表格,见下图。 @@ -366,7 +368,7 @@ cProfile,在某些框架中,也内置了中间件帮助你进行性能分析 点击 Call Graph(调用关系图)界面直观展示了各函数直接的调用关系、运行时间和时间百分比,见下图。 -|image32| +|image33| 右上角的4个按钮表示放大、缩小、真实大小、合适大小; @@ -383,26 +385,26 @@ Graph(调用关系图)界面直观展示了各函数直接的调用关系、 按照如下提示点击 Git 仓库配置 -|image33| +|image34| 接着输入仓库地址 -|image34| +|image35| 点击 Test,测试连通性,会要求输入密码 -|image35| +|image36| 若一切顺利,则会看到如下界面 -|image36| +|image37| 测试连接成功后,点击 Clone 就可以克隆下来了。 对于以前使用 Git 命令来管理的,现在可以直接使用 PyCharm 的菜单栏来操作,这些功能已经可以满足大多数人的日常需求了,应该是够用了。 -|image37| +|image38| 4.15.13 Tab轻松转空格 --------------------- @@ -416,7 +418,7 @@ Pycharm 在图示位置打勾即可开启自动检测。 -|image38| +|image39| 上面是对一个旧的 Python 模块进行修改时,如何决定当前编辑的缩进方式。 @@ -425,7 +427,7 @@ Pycharm 如下图,若在 ``Use tab character`` 打上勾,则你新建一个 Python 后,就会使用 TAB 进行缩进,反之,则使用四个空格进行缩进。 -|image39| +|image40| 5. @@ -435,7 +437,7 @@ Pycharm PyCharm 有分两个版本,一个是社区版(免费功能有限),一个是专业版(有一些增强功能),详细差异你可以参考这个图,一般来说,社区版用作学习用途是没有问题的。 -|image40| +|image41| 如果需要使用专业版,网上也有一些注册服务器使用,非常方便,缺点是过一段时间,可能就会失效。这里有一种一劳永逸的方法,但可能仅对早期的 PyCharm 版本有效,可以实现永久激活(到 2099 / @@ -465,21 +467,21 @@ PyCharm 版本有效,可以实现永久激活(到 2099 / 将第一步下载的 jar 包放入这个目录,并打开如下两个以 ``vmoptions`` 后缀结尾的文件: -|image41| +|image42| 添加如下这一行(请根据你的实际安装目录自行调整) -|image42| +|image43| 若是 Mac OS 系统,请找到并进入你的 Pycharm 安装启动目录(以我的为例) 将第一步下载的 jar 包放入这个目录 -|image43| +|image44| 并打开如下一个以 ``vmoptions`` 后缀结尾的文件: -|image44| +|image45| **第三步**\ : @@ -495,12 +497,12 @@ PyCharm 版本有效,可以实现永久激活(到 2099 / 若是 Windows 系统,重启 PyCharm 后,查看激活信息:Help -> About -|image45| +|image46| 如果是 Mac OS 系统,重启 PyCharm 后,查看激活信息:PyCharm -> About PyCharm -|image46| +|image47| 另外,以上仅做交流和个人学习使用,请勿商用,有能力的朋友还是希望多支持正版! @@ -521,22 +523,22 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 几个简单的 Example Usage 有助于理解使用。在这一点上,Flask 可以说做得相当好。这边随便截一个 Werkzeug 的例子。 -|image47| +|image48| 假如我们在使用这个类的时候,忘记了这个用法,可以按住 Ctrl + q(Mac暂时未找到对应快捷键),在当前页面就可以快速预览 LocalStack 的接口文档。 -|image48| +|image49| 同样的,如果你对这个类或者函数的代码逻辑感兴趣,也可以使用快速预览的方式在当前页面展示源代码。快捷键是:Ctrl + shift + i (Mac:Command + shift + i)。效果如下 -|image49| +|image50| 如果 PyCharm 检测到的同名函数有多个,可以点这里进行选择查看 -|image50| +|image51| 这两个快捷键比起使用 Ctrl + 鼠标左键 跳进源代码来说,更加方便,,就像微信小程序一样,用完即焚,不会新产生一个标签页,也不需要来回跳转页面。 @@ -547,7 +549,7 @@ q(Mac暂时未找到对应快捷键),在当前页面就可以快速预览 前几天打开 PyCharm,发现在导航栏这里出现了很多波浪线,有过 PyCharm 使用经验的同学,就会知道,这是代码中出现了错误。 -|image51| +|image52| 顺着波浪线,我一层一层地展开目录树,终于找到了那个包含错误的文件。由于是手误,我也不知道我改动了哪一行,看了下这个文件,有将近8000行的代码,难道一行一行地去找? @@ -561,11 +563,11 @@ q(Mac暂时未找到对应快捷键),在当前页面就可以快速预览 我在搜索框输入 Error,就找到了快速定位到错误位置的快捷键 ``F2`` 和 ``Shift+F2`` 可以快速的定位到错误行。 -|image52| +|image53| 使用快捷键 F2 查看了下原来是这里缩进有问题。 -|image53| +|image54| 4.15.17 快速查看最近的修改 -------------------------- @@ -578,7 +580,7 @@ q(Mac暂时未找到对应快捷键),在当前页面就可以快速预览 option+shift+C 可以快速查看最近修改的内容(windows 上应该是alt+shift+c吧) -|image54| +|image55| 4.15.18 静态代码分析检查 ------------------------ @@ -601,11 +603,11 @@ Java,需要将代码编译成机器可识别的语言才可运行,在编译 你只需要像下面这样点击项目文件夹,然后右键,选择 ``Inspect Code``\ ,就可以开启静态检查。 -|image55| +|image56| 我对开源组件 nova 的静态检查发现,其有不规范的地方有数千处。 -|image56| +|image57| 4.15.19 全方位无死角精准定位 ---------------------------- @@ -624,26 +626,26 @@ OpenStack - 精准定位到文件:Windows(Ctrl+Shift+N),Mac(Command+ shift +N) -|image57| +|image58| - 精准定位到类:Windows(Ctrl+N),Mac(Command+N) -|image58| +|image59| - 精准定位到符号:类的所有成员(函数、变量等)都可以称之为符号,Windows(Ctrl+Alt+Shift+N),Mac(Option+Shift+Command+N) -|image59| +|image60| - 精准定位到文件结构:文件结构包括类、函数、变量,这说明上面定位到类和定位到符号的方法,你都可以用这个来代替。 Windows:Ctrl+F12,Mac:Command+F12,如果和我一样是Mac是带touchbar的,键盘上是没有F12的,那你应该先按住 Command + fn,这时 touchbar 上会出现 F12,再按F12即可。 -|image60| +|image61| - 精准定位到某行:Windows(Ctrl+G),Mac(Command+G),如下图定位到第510行第9个字符处。 -|image61| +|image62| 4.15.20 TODO 解救“中年痴呆” --------------------------- @@ -672,14 +674,14 @@ OpenStack 开头。然后,你要查看全局项目中的所有 TODO 事项的时候,可以使用快捷键调出 TODO 面板。如果你是 Mac, 快捷键 是Command + 6,而 Windows 是 Alt+6。 -|image62| +|image63| 另外,我还使用这个来记录下个版本要优化的代码逻辑,要添加的功能。 如果是比较紧急的 BUG,可以使用类似 TODO 的标记 — ``FIXME`` 来区分紧急程度。 -|image63| +|image64| 4.15.21 随处折叠,实现代码自由 ------------------------------ @@ -691,7 +693,7 @@ PyCharm 里代码块的折叠功能,相当的显眼,在代码编辑框的左 如果你和我一样是个键盘党,你可以使用快捷(Mac:按住Command键,再按\ ``+``\ 或者\ ``-`` ,Windows:按住Ctrl键,再按\ ``+``\ 或者\ ``-`` )进行快速反折叠/折叠。 -|image64| +|image65| 代码块的折叠和反折叠,应该是一个代码编辑器的基本功能。在这一点上, PyCharm 做为一个 IDE,在这一点上势必要做得更出色,事实证明,它做到了。 @@ -705,7 +707,7 @@ PyCharm 做为一个 IDE,在这一点上势必要做得更出色,事实证 只要你先选中你想折叠的代码,再按住 Command 紧接着按住 ``.`` 就可以了。效果如下: -|image65| +|image66| (GIF动态只播放两次,重播请刷新页面) @@ -723,7 +725,7 @@ PyCharm 做为一个 IDE,在这一点上势必要做得更出色,事实证 比如下面这段代码,我只想改myfun 里的的test_name,而对于全局下的同名变量是不应该修改的。如果你全局替换,就会有误伤。 -|image66| +|image67| 这时候,我们如何做呢? @@ -733,7 +735,7 @@ PyCharm 做为一个 IDE,在这一点上势必要做得更出色,事实证 操作方法很简单,先选中你的变量,然后使用快捷键 Shift+F6,就可以直接重命名了。 -|image67| +|image68| (GIF动态只播放两次,重播请刷新页面) @@ -752,7 +754,7 @@ Command+y 删除该行,就删除了该函数。 做录制方法如下: -|image68| +|image69| (GIF动态只播放两次,重播请刷新页面) @@ -761,13 +763,13 @@ Command+y 删除该行,就删除了该函数。 这样播放宏显得有点繁琐,个人建议你为这个宏定义一个快捷键,这样会更方便播放宏。 -|image69| +|image70| 设置快捷键时,注意不要和已有的快捷键冲突。 设置好后,查看 Macro,发现PyCharm已经将这个快捷键绑定给这个宏。 -|image70| +|image71| 之后你就可以使用这个快捷键删除一个函数(其实这只是删除一个代码块,但是这里只讨论设置方法)。 @@ -779,7 +781,7 @@ PyCharm 打开一个文件,就占用一个标签面。 你有没有发现,不知不觉地,打开的文件越来越多,多到一行标签都装不下,装不下的标签页 PyCharm 会将其隐藏起来,并以数字的形式告诉你隐藏了几个文件。 -|image71| +|image72| 点击数字5,你才可以查看隐藏了哪些文件。 @@ -789,11 +791,11 @@ PyCharm 会将其隐藏起来,并以数字的形式告诉你隐藏了几个文 如下图,将单行显示取消勾选即可。 -|image72| +|image73| 设置完后,有哪些文件就非常清晰了。 -|image73| +|image74| 4.15.25 应用搜索,阅读源码必备 ------------------------------ @@ -812,7 +814,7 @@ PyCharm 会将其隐藏起来,并以数字的形式告诉你隐藏了几个文 如下图所示,按下快捷键后可以很轻松地看见调用列表。 -|image74| +|image75| 如果你嫌这快捷键太长了,可以使用 ``鼠标中键`` 点击这个类,可以达到同样的效果。 @@ -830,19 +832,19 @@ PyCharm 会将其隐藏起来,并以数字的形式告诉你隐藏了几个文 对比示例,可以查看下面这张图,UI做的还是挺好看的。 -|image75| +|image76| 4.15.27 以列为单位的块编辑 -------------------------- 先给你出道小题,像下面这段代码,如果在不影响代码的情况下,快速删除后面代码后面的注释呢? -|image76| +|image77| 我能想到的有两种方法,如果像如上这种有规律的注释,可以使用 ``正则匹配`` + ``替换`` 来实现。 -|image77| +|image78| 对于这个场景我想到了可以用 vim来轻松的解决,vim 支持块编辑,可以以列为单位选择区域然后进行操作,这在vim中是很常用的一个取消注释的操作。 @@ -852,7 +854,7 @@ PyCharm 会将其隐藏起来,并以数字的形式告诉你隐藏了几个文 当你按住 alt(windows)或者option(mac),然后使用鼠标进行选择,你会发现这样一件神奇的事情。 -|image78| +|image79| 4.15.28 智能补全,忽略大小写 ---------------------------- @@ -861,17 +863,17 @@ alt(windows)或者option(mac),然后使用鼠标进行选择,你会 当你的对象是以大写字母开头时,而你使用小写字母编写代码时,是不能查找到该函数的,你必须得先切换成大写再输入一遍。 -|image79| +|image80| 如何避免这种尴尬的情况? 只要在配置中关闭大小写匹配即可。 -|image80| +|image81| 效果如下: -|image81| +|image82| 4.15.29 保护眼睛,从PyCharm开始 ------------------------------- @@ -884,7 +886,7 @@ alt(windows)或者option(mac),然后使用鼠标进行选择,你会 这里就教大家如何设置 PyCharm 的背景色为护眼色,方法如下: -|image82| +|image83| 设置护眼色,会降低 PyCharm 的顔值,这需要你从中取一个取舍。 @@ -930,7 +932,7 @@ Debug ,而远程调试需要不少前置步骤。 这里我提前准备了几种编程语言的 Hello World ,效果如下: -|image83| +|image84| 4.15.32 轻松实现 json 格式化 ---------------------------- @@ -944,18 +946,18 @@ Debug ,而远程调试需要不少前置步骤。 以前我经常使用一些在线的网站,比如:https://tool.oschina.net/codeformat/json -|image84| +|image85| 如果你的电脑无法连网,或者不喜欢多记一个网址,完全可以使用 PyCharnm 来解决这一诉求 没有经过美化是这样的: -|image85| +|image86| 按住 ``Ctrl+Alt+L``\ 经过美化后是这样的 -|image86| +|image87| 4.15.33 在Windows上使用 Linux 命令 ---------------------------------- @@ -967,11 +969,11 @@ Debug ,而远程调试需要不少前置步骤。 对于像我这样熟悉 Linux 的开发者来说,Windows 的 那些 CMD 命令带来的糟糕体验是无法忍受的。 -|image87| +|image88| 在弹出的 Bash 窗口,你可以敲入你想使用的 Linux 命令,是不是舒服多了。 -|image88| +|image89| 4.15.34 快速进行代码封装的技巧 ------------------------------ @@ -993,36 +995,36 @@ Debug ,而远程调试需要不少前置步骤。 假如,我现在有如下一段代码,红框标出的代码放在主函数中,有些不太合适,况且这段代码不能让人一眼就看出它是在做什么事情。如何将其进行封装,对我们理清整个主程序的逻辑会有帮助。 -|image89| +|image90| 选中你要封装的代码,然后按住 ``Ctrl``\ +\ ``Alt``\ +\ ``M`` 后,会跳出如下界面,根据自己的需要,修改函数名,选择参数和返回值 -|image90| +|image91| 一切就绪点击 ``OK``\ ,PyCharm 会自动在合适的位置为你定义一个函数名,并将你选中的代码放到里面,其中参数名和返回值也都是按照你的要求,效果如下: -|image91| +|image92| 4.15.35 使用 Git 进行版本管理 ----------------------------- 点击 ``VCS`` -> ``Git`` -> ``Clone`` -|image92| +|image93| 填写git仓库相关信息 -|image93| +|image94| 点击 ``Test``\ ,会尝试连接 git 服务器,中间会让你输入登陆的帐号和密码。 -|image94| +|image95| 点击\ ``OK`` 后,若一切正常会提示连接成功。 -|image95| +|image96| 点击 ``OK`` 后,PyCharm 需要你选择如何打开这个 Git 仓库目录,是在当前窗口中打开,还是新建一个窗口? @@ -1031,14 +1033,14 @@ Debug ,而远程调试需要不少前置步骤。 仓库,为了方便,我选择在当前窗口中打开(注意勾选 ``Add to currently opened projects``\ )。 -|image96| +|image97| 至此,Git 配置完成。 此时你可以 ``VCS`` -> ``Git`` 查看,发现之前这些灰色不可用的按钮都可以使用了。 -|image97| +|image98| 本篇重在讲解 PyCharm 的配置,关于Git 的操作,不属于本篇重点,就不再展开讲了。 @@ -1046,11 +1048,11 @@ Debug ,而远程调试需要不少前置步骤。 若你想对已配置的Git仓库进行修改,可点击 ``File`` -> ``Setting`` -> ``Version Control`` 调出如下界面。 -|image98| +|image99| 不得不说 PyCharm 的这 UI 做得可以,随便改了个东西提交一下 -|image99| +|image100| 4.15.36 快速查看 Git 的修改 --------------------------- @@ -1060,18 +1062,18 @@ Debug ,而远程调试需要不少前置步骤。 第一种当然是使用 git diff -|image100| +|image101| 第二种是使用之前写的 show history -|image101| +|image102| 第三种,也是今天要介绍的,是最简便,也是直接的方法。 在有文本变动的位置,PyCharm 会有提示,如下红色箭头标识处,点击它就可以直接查看,还可以快速回滚。 -|image102| +|image103| 附录 ---- @@ -1089,107 +1091,108 @@ Debug ,而远程调试需要不少前置步骤。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190323164120.png -.. |image1| image:: http://image.python-online.cn/20190323211635.png -.. |image2| image:: http://image.python-online.cn/20190323211301.png -.. |image3| image:: http://image.python-online.cn/20190324111603.png -.. |image4| image:: http://image.python-online.cn/20190323153643.png -.. |image5| image:: http://image.python-online.cn/20190323214545.png -.. |image6| image:: http://image.python-online.cn/20190323225704.png -.. |image7| image:: http://image.python-online.cn/20190323225631.png -.. |image8| image:: http://image.python-online.cn/20190323232017.png -.. |image9| image:: https://i.loli.net/2019/03/23/5c965275bf0d7.gif -.. |image10| image:: https://i.loli.net/2019/03/23/5c9653e1b757a.gif -.. |image11| image:: http://image.python-online.cn/20190324111429.png -.. |image12| image:: http://image.python-online.cn/Fi3N02x9OeOPatGdaReam_icn9G_ -.. |image13| image:: http://image.python-online.cn/Fj1W53Txj0iFs5eYhFYh_dHlPtIL -.. |image14| image:: http://image.python-online.cn/FlMsB7B1x6ET9mLOgydTWuTEXuOe -.. |image15| image:: http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId -.. |image16| image:: http://image.python-online.cn/FiNCYpVlI93gk1zhOdQn4c0A8FMX -.. |image17| image:: http://image.python-online.cn/FrAq1tVRM7Bz948wRqZFzU2PQnI0 -.. |image18| image:: http://image.python-online.cn/Fo2aEraqbj_2KqDt44EzJTVe8pEf -.. |image19| image:: http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId -.. |image20| image:: http://image.python-online.cn/FujczKwTUPa8l5EEmS0eoh-zL1Nk -.. |image21| image:: http://image.python-online.cn/Fq60WOdcRJopqV6MVoRcIuZclYKx -.. |image22| image:: http://image.python-online.cn/FlXynbyxh8tTrCpc4tVLqycL7JQm -.. |image23| image:: http://image.python-online.cn/FiD91PR1hUu0Ruc6cmZ7EGNM6Be_ -.. |image24| image:: http://image.python-online.cn/FhkX5Ko3LVZL_p7YfitDsTDxvHmL -.. |image25| image:: http://image.python-online.cn/FuSSVa-aMqkfCaf62sbUoX2PLaYM -.. |image26| image:: http://image.python-online.cn/FtFPI89AOKmPLNpNxf-jdkn1BDLW -.. |image27| image:: http://image.python-online.cn/FiKyU6tjQauWXfaVfKLhwi3NkXBf -.. |image28| image:: http://image.python-online.cn/FsAM-8HyzSrLWZJ_lg3ofw84_ibf -.. |image29| image:: http://image.python-online.cn/FgJCtNYkjPfBaTbRxwb3Z6icHqkf -.. |image30| image:: http://image.python-online.cn/20190507222856.png -.. |image31| image:: http://image.python-online.cn/20190507222119.png -.. |image32| image:: http://image.python-online.cn/20190507223313.png -.. |image33| image:: http://image.python-online.cn/20190507215525.png -.. |image34| image:: http://image.python-online.cn/20190507220101.png -.. |image35| image:: http://image.python-online.cn/20190419152120.png -.. |image36| image:: http://image.python-online.cn/20190419152145.png -.. |image37| image:: http://image.python-online.cn/20190507220740.png -.. |image38| image:: http://image.python-online.cn/20190423162328.png -.. |image39| image:: http://image.python-online.cn/20190423163341.png -.. |image40| image:: http://image.python-online.cn/20190506150523.png -.. |image41| image:: http://image.python-online.cn/20190506150010.png -.. |image42| image:: http://image.python-online.cn/20190506150100.png -.. |image43| image:: http://image.python-online.cn/20190507000850.png -.. |image44| image:: http://image.python-online.cn/20190507001025.png -.. |image45| image:: http://image.python-online.cn/20190507001422.png -.. |image46| image:: http://image.python-online.cn/20190507001350.png -.. |image47| image:: http://image.python-online.cn/20190507152911.png -.. |image48| image:: http://image.python-online.cn/20190507152840.png -.. |image49| image:: http://image.python-online.cn/20190507153847.png -.. |image50| image:: http://image.python-online.cn/20190507154027.png -.. |image51| image:: http://image.python-online.cn/20190613154147.png -.. |image52| image:: http://image.python-online.cn/20190613154401.png -.. |image53| image:: http://image.python-online.cn/20190613160905.png -.. |image54| image:: http://image.python-online.cn/20190614235120.png -.. |image55| image:: http://image.python-online.cn/20190616211359.png -.. |image56| image:: http://image.python-online.cn/20190616214310.png -.. |image57| image:: http://image.python-online.cn/20190616221620.png -.. |image58| image:: http://image.python-online.cn/20190616232746.png -.. |image59| image:: http://image.python-online.cn/20190616233827.png -.. |image60| image:: http://image.python-online.cn/20190616235007.png -.. |image61| image:: http://image.python-online.cn/20190616234038.png -.. |image62| image:: http://image.python-online.cn/20190616231649.png -.. |image63| image:: http://image.python-online.cn/20190616232527.png -.. |image64| image:: http://image.python-online.cn/20190629183430.png -.. |image65| image:: https://i.loli.net/2019/06/29/5d17589c1603755790.gif -.. |image66| image:: http://image.python-online.cn/20190629211910.png -.. |image67| image:: https://i.loli.net/2019/06/29/5d1764b94d11128912.gif -.. |image68| image:: https://i.loli.net/2019/06/29/5d176e9ba92e916696.gif -.. |image69| image:: http://image.python-online.cn/20190629221224.png -.. |image70| image:: http://image.python-online.cn/20190629221547.png -.. |image71| image:: http://image.python-online.cn/20190629223534.png -.. |image72| image:: http://image.python-online.cn/20190629224229.png -.. |image73| image:: http://image.python-online.cn/20190629224430.png -.. |image74| image:: http://image.python-online.cn/20190629231322.png -.. |image75| image:: http://image.python-online.cn/20190721125739.png -.. |image76| image:: http://image.python-online.cn/20190721132238.png -.. |image77| image:: http://image.python-online.cn/20190721133403.png -.. |image78| image:: https://i.loli.net/2019/07/21/5d3401410087b61815.gif -.. |image79| image:: http://image.python-online.cn/20190721141327.png -.. |image80| image:: http://image.python-online.cn/20190721141653.png -.. |image81| image:: http://image.python-online.cn/20190721141751.png -.. |image82| image:: http://image.python-online.cn/20190721143450.png -.. |image83| image:: http://image.python-online.cn/20191211210012.png -.. |image84| image:: http://image.python-online.cn/20191211211309.png -.. |image85| image:: http://image.python-online.cn/20191211211334.png -.. |image86| image:: http://image.python-online.cn/20191211211626.png -.. |image87| image:: http://image.python-online.cn/20191211212546.png -.. |image88| image:: http://image.python-online.cn/20191222143741.png -.. |image89| image:: http://image.python-online.cn/20191222141905.png -.. |image90| image:: http://image.python-online.cn/20191222141955.png -.. |image91| image:: http://image.python-online.cn/20191222142223.png -.. |image92| image:: http://image.python-online.cn/20191211100048.png -.. |image93| image:: http://image.python-online.cn/20191211100657.png -.. |image94| image:: http://image.python-online.cn/20191211101706.png -.. |image95| image:: http://image.python-online.cn/20191211101845.png -.. |image96| image:: http://image.python-online.cn/20191211102501.png -.. |image97| image:: http://image.python-online.cn/20191211102826.png -.. |image98| image:: http://image.python-online.cn/20191211133836.png -.. |image99| image:: http://image.python-online.cn/20191211143510.png -.. |image100| image:: http://image.iswbm.com/20200420085524.png -.. |image101| image:: http://image.iswbm.com/20200420090117.png -.. |image102| image:: http://image.iswbm.com/20200420090428.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190323164120.png +.. |image2| image:: http://image.python-online.cn/20190323211635.png +.. |image3| image:: http://image.python-online.cn/20190323211301.png +.. |image4| image:: http://image.python-online.cn/20190324111603.png +.. |image5| image:: http://image.python-online.cn/20190323153643.png +.. |image6| image:: http://image.python-online.cn/20190323214545.png +.. |image7| image:: http://image.python-online.cn/20190323225704.png +.. |image8| image:: http://image.python-online.cn/20190323225631.png +.. |image9| image:: http://image.python-online.cn/20190323232017.png +.. |image10| image:: https://i.loli.net/2019/03/23/5c965275bf0d7.gif +.. |image11| image:: https://i.loli.net/2019/03/23/5c9653e1b757a.gif +.. |image12| image:: http://image.python-online.cn/20190324111429.png +.. |image13| image:: http://image.python-online.cn/Fi3N02x9OeOPatGdaReam_icn9G_ +.. |image14| image:: http://image.python-online.cn/Fj1W53Txj0iFs5eYhFYh_dHlPtIL +.. |image15| image:: http://image.python-online.cn/FlMsB7B1x6ET9mLOgydTWuTEXuOe +.. |image16| image:: http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId +.. |image17| image:: http://image.python-online.cn/FiNCYpVlI93gk1zhOdQn4c0A8FMX +.. |image18| image:: http://image.python-online.cn/FrAq1tVRM7Bz948wRqZFzU2PQnI0 +.. |image19| image:: http://image.python-online.cn/Fo2aEraqbj_2KqDt44EzJTVe8pEf +.. |image20| image:: http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId +.. |image21| image:: http://image.python-online.cn/FujczKwTUPa8l5EEmS0eoh-zL1Nk +.. |image22| image:: http://image.python-online.cn/Fq60WOdcRJopqV6MVoRcIuZclYKx +.. |image23| image:: http://image.python-online.cn/FlXynbyxh8tTrCpc4tVLqycL7JQm +.. |image24| image:: http://image.python-online.cn/FiD91PR1hUu0Ruc6cmZ7EGNM6Be_ +.. |image25| image:: http://image.python-online.cn/FhkX5Ko3LVZL_p7YfitDsTDxvHmL +.. |image26| image:: http://image.python-online.cn/FuSSVa-aMqkfCaf62sbUoX2PLaYM +.. |image27| image:: http://image.python-online.cn/FtFPI89AOKmPLNpNxf-jdkn1BDLW +.. |image28| image:: http://image.python-online.cn/FiKyU6tjQauWXfaVfKLhwi3NkXBf +.. |image29| image:: http://image.python-online.cn/FsAM-8HyzSrLWZJ_lg3ofw84_ibf +.. |image30| image:: http://image.python-online.cn/FgJCtNYkjPfBaTbRxwb3Z6icHqkf +.. |image31| image:: http://image.python-online.cn/20190507222856.png +.. |image32| image:: http://image.python-online.cn/20190507222119.png +.. |image33| image:: http://image.python-online.cn/20190507223313.png +.. |image34| image:: http://image.python-online.cn/20190507215525.png +.. |image35| image:: http://image.python-online.cn/20190507220101.png +.. |image36| image:: http://image.python-online.cn/20190419152120.png +.. |image37| image:: http://image.python-online.cn/20190419152145.png +.. |image38| image:: http://image.python-online.cn/20190507220740.png +.. |image39| image:: http://image.python-online.cn/20190423162328.png +.. |image40| image:: http://image.python-online.cn/20190423163341.png +.. |image41| image:: http://image.python-online.cn/20190506150523.png +.. |image42| image:: http://image.python-online.cn/20190506150010.png +.. |image43| image:: http://image.python-online.cn/20190506150100.png +.. |image44| image:: http://image.python-online.cn/20190507000850.png +.. |image45| image:: http://image.python-online.cn/20190507001025.png +.. |image46| image:: http://image.python-online.cn/20190507001422.png +.. |image47| image:: http://image.python-online.cn/20190507001350.png +.. |image48| image:: http://image.python-online.cn/20190507152911.png +.. |image49| image:: http://image.python-online.cn/20190507152840.png +.. |image50| image:: http://image.python-online.cn/20190507153847.png +.. |image51| image:: http://image.python-online.cn/20190507154027.png +.. |image52| image:: http://image.python-online.cn/20190613154147.png +.. |image53| image:: http://image.python-online.cn/20190613154401.png +.. |image54| image:: http://image.python-online.cn/20190613160905.png +.. |image55| image:: http://image.python-online.cn/20190614235120.png +.. |image56| image:: http://image.python-online.cn/20190616211359.png +.. |image57| image:: http://image.python-online.cn/20190616214310.png +.. |image58| image:: http://image.python-online.cn/20190616221620.png +.. |image59| image:: http://image.python-online.cn/20190616232746.png +.. |image60| image:: http://image.python-online.cn/20190616233827.png +.. |image61| image:: http://image.python-online.cn/20190616235007.png +.. |image62| image:: http://image.python-online.cn/20190616234038.png +.. |image63| image:: http://image.python-online.cn/20190616231649.png +.. |image64| image:: http://image.python-online.cn/20190616232527.png +.. |image65| image:: http://image.python-online.cn/20190629183430.png +.. |image66| image:: https://i.loli.net/2019/06/29/5d17589c1603755790.gif +.. |image67| image:: http://image.python-online.cn/20190629211910.png +.. |image68| image:: https://i.loli.net/2019/06/29/5d1764b94d11128912.gif +.. |image69| image:: https://i.loli.net/2019/06/29/5d176e9ba92e916696.gif +.. |image70| image:: http://image.python-online.cn/20190629221224.png +.. |image71| image:: http://image.python-online.cn/20190629221547.png +.. |image72| image:: http://image.python-online.cn/20190629223534.png +.. |image73| image:: http://image.python-online.cn/20190629224229.png +.. |image74| image:: http://image.python-online.cn/20190629224430.png +.. |image75| image:: http://image.python-online.cn/20190629231322.png +.. |image76| image:: http://image.python-online.cn/20190721125739.png +.. |image77| image:: http://image.python-online.cn/20190721132238.png +.. |image78| image:: http://image.python-online.cn/20190721133403.png +.. |image79| image:: https://i.loli.net/2019/07/21/5d3401410087b61815.gif +.. |image80| image:: http://image.python-online.cn/20190721141327.png +.. |image81| image:: http://image.python-online.cn/20190721141653.png +.. |image82| image:: http://image.python-online.cn/20190721141751.png +.. |image83| image:: http://image.python-online.cn/20190721143450.png +.. |image84| image:: http://image.python-online.cn/20191211210012.png +.. |image85| image:: http://image.python-online.cn/20191211211309.png +.. |image86| image:: http://image.python-online.cn/20191211211334.png +.. |image87| image:: http://image.python-online.cn/20191211211626.png +.. |image88| image:: http://image.python-online.cn/20191211212546.png +.. |image89| image:: http://image.python-online.cn/20191222143741.png +.. |image90| image:: http://image.python-online.cn/20191222141905.png +.. |image91| image:: http://image.python-online.cn/20191222141955.png +.. |image92| image:: http://image.python-online.cn/20191222142223.png +.. |image93| image:: http://image.python-online.cn/20191211100048.png +.. |image94| image:: http://image.python-online.cn/20191211100657.png +.. |image95| image:: http://image.python-online.cn/20191211101706.png +.. |image96| image:: http://image.python-online.cn/20191211101845.png +.. |image97| image:: http://image.python-online.cn/20191211102501.png +.. |image98| image:: http://image.python-online.cn/20191211102826.png +.. |image99| image:: http://image.python-online.cn/20191211133836.png +.. |image100| image:: http://image.python-online.cn/20191211143510.png +.. |image101| image:: http://image.iswbm.com/20200420085524.png +.. |image102| image:: http://image.iswbm.com/20200420090117.png +.. |image103| image:: http://image.iswbm.com/20200420090428.png diff --git a/source/c04/c04_16.md b/source/c04/c04_16.md index 0dbccaa..d4dd1e0 100644 --- a/source/c04/c04_16.md +++ b/source/c04/c04_16.md @@ -1,5 +1,7 @@ # 4.16 Python 开发技巧集合 +![](http://image.iswbm.com/20200602135014.png) + ## 4.16.1 如何在被调用方法中获取调用者的方法名? Python:如何在被调用方法中获取调用者的方法名? diff --git a/source/c04/c04_16.rst b/source/c04/c04_16.rst index 51c01a4..73f748e 100644 --- a/source/c04/c04_16.rst +++ b/source/c04/c04_16.rst @@ -1,6 +1,8 @@ 4.16 Python 开发技巧集合 ======================== +|image0| + 4.16.1 如何在被调用方法中获取调用者的方法名? --------------------------------------------- @@ -76,3 +78,6 @@ Python:如何在被调用方法中获取调用者的方法名? :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c04/c04_17.md b/source/c04/c04_17.md index 9f3dd10..018397c 100644 --- a/source/c04/c04_17.md +++ b/source/c04/c04_17.md @@ -1,5 +1,7 @@ # 4.17 详解 23 种设计模式 +![](http://image.iswbm.com/20200602135014.png) + ## 4.17.1 策略模式 diff --git a/source/c04/c04_17.rst b/source/c04/c04_17.rst index 018f952..b994414 100644 --- a/source/c04/c04_17.rst +++ b/source/c04/c04_17.rst @@ -1,6 +1,8 @@ 4.17 详解 23 种设计模式 ======================= +|image0| + 4.17.1 策略模式 --------------- @@ -20,7 +22,7 @@ - Stragety:策略基类 - ConcreteStragety:具体策略 -|image0| +|image1| 以第一个超市做活动的场景来举个例子。 @@ -281,7 +283,7 @@ Order 验证结果 -|image1| +|image2| - 使用装饰器 @@ -310,7 +312,7 @@ Order 验证结果 -|image2| +|image3| - 使用元类 @@ -333,7 +335,7 @@ Order 验证结果 -|image3| +|image4| 以上的代码,一般情况下没有问题,但在并发场景中,就会出现线程安全的问题。 @@ -477,8 +479,9 @@ Order 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190414144511.png -.. |image1| image:: http://image.python-online.cn/20190512113846.png -.. |image2| image:: http://image.python-online.cn/20190512113917.png -.. |image3| image:: http://image.python-online.cn/20190512114028.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190414144511.png +.. |image2| image:: http://image.python-online.cn/20190512113846.png +.. |image3| image:: http://image.python-online.cn/20190512113917.png +.. |image4| image:: http://image.python-online.cn/20190512114028.png diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index d8b0542..87beb71 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -1,5 +1,7 @@ # 4.18 详细的 Mac 使用指南 +![](http://image.iswbm.com/20200602135014.png) + ## 4.18.1 iTerm2 介绍一下快捷键 diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 340d81e..3f722cf 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -1,6 +1,8 @@ 4.18 详细的 Mac 使用指南 ======================== +|image0| + 4.18.1 iTerm2 ------------- @@ -101,7 +103,7 @@ finder的显示 -|image0| +|image1| `防止电脑温度过高 `__ @@ -117,7 +119,7 @@ finder的显示 3. 打开「活动监视器」(Alfred就可以打开),杀掉暂没用且cpu使用率最高的程序 - |image1| + |image2| 4. MacBook Pro CPU 温度在 5、60℃ 的时候,风扇会转到两三千转每分钟,只有 CPU 温度达到 70 @@ -332,6 +334,7 @@ finder的显示 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190810161513.png -.. |image1| image:: http://image.python-online.cn/20190810162315.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190810161513.png +.. |image2| image:: http://image.python-online.cn/20190810162315.png diff --git a/source/c04/c04_19.md b/source/c04/c04_19.md index 031f998..dcad975 100644 --- a/source/c04/c04_19.md +++ b/source/c04/c04_19.md @@ -1,5 +1,7 @@ # 4.19 程序员编码必学:Vim +![](http://image.iswbm.com/20200602135014.png) + 我本人是 Vim 的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim 可以让我对文本的操作更加精准、高效。 对于未使用过 Vim 的朋友来说,可能还无法体会到这种感觉。由于使用 Vim 有一定的学习成本,只有做到非常熟练的程度才能感受到它带来的快捷。 diff --git a/source/c04/c04_19.rst b/source/c04/c04_19.rst index 09a9255..a12e161 100644 --- a/source/c04/c04_19.rst +++ b/source/c04/c04_19.rst @@ -1,6 +1,8 @@ 4.19 程序员编码必学:Vim ======================== +|image0| + 我本人是 Vim 的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim 可以让我对文本的操作更加精准、高效。 @@ -723,3 +725,6 @@ n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c04/c04_20.md b/source/c04/c04_20.md index 8edd8ca..b94eff8 100644 --- a/source/c04/c04_20.md +++ b/source/c04/c04_20.md @@ -1,5 +1,7 @@ # 4.20 学会使用谷歌搜索引擎 +![](http://image.iswbm.com/20200602135014.png) + 1、按文件类型搜索 加filetype ``` diff --git a/source/c04/c04_20.rst b/source/c04/c04_20.rst index 9e31f91..72b74f8 100644 --- a/source/c04/c04_20.rst +++ b/source/c04/c04_20.rst @@ -1,6 +1,8 @@ 4.20 学会使用谷歌搜索引擎 ========================= +|image0| + 1、按文件类型搜索 加filetype :: @@ -36,3 +38,6 @@ :: "装饰器进阶用法详解" + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c04/c04_21.md b/source/c04/c04_21.md index 1e5e425..5756cd4 100644 --- a/source/c04/c04_21.md +++ b/source/c04/c04_21.md @@ -1,5 +1,7 @@ # 4.21 最全的 pip 使用指南,50% 你可能没用过 +![](http://image.iswbm.com/20200602135014.png) + 所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python 为基础封装出各种有利于开发的第三方工具包。 这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index b0a08a7..7401c4d 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -1,6 +1,8 @@ 4.21 最全的 pip 使用指南,50% 你可能没用过 ========================================== +|image0| + 所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python @@ -366,12 +368,13 @@ pip 的文件夹,若没有则创建之。 以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 -|image0| +|image1| .. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20191105200041.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191105200041.png diff --git a/source/c04/c04_22.md b/source/c04/c04_22.md index e3ca425..a9d1f88 100644 --- a/source/c04/c04_22.md +++ b/source/c04/c04_22.md @@ -1,5 +1,7 @@ # 4.22 用好 Chrome 必看 +![](http://image.iswbm.com/20200602135014.png) + ## 开启阅读模式 开启阅读模式:chrome://flags/#enable-reader-mode diff --git a/source/c04/c04_22.rst b/source/c04/c04_22.rst index c791369..a179e21 100644 --- a/source/c04/c04_22.rst +++ b/source/c04/c04_22.rst @@ -1,14 +1,17 @@ 4.22 用好 Chrome 必看 ===================== +|image0| + 开启阅读模式 ------------ 开启阅读模式:chrome://flags/#enable-reader-mode -|image0| +|image1| 开启完成后,需要重启浏览器,你可以随便打开一篇博客,然后在地址栏右边会有一个阅读模式的按钮。 -.. |image0| image:: http://image.python-online.cn/20191201103653.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191201103653.png diff --git a/source/c04/c04_23.md b/source/c04/c04_23.md index 577c09c..7c4195e 100644 --- a/source/c04/c04_23.md +++ b/source/c04/c04_23.md @@ -1,5 +1,7 @@ # 4.23 电脑使用技巧 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 添加右键菜单 参考 Sublime Text3的添加方法 diff --git a/source/c04/c04_23.rst b/source/c04/c04_23.rst index 63db2c5..08803c5 100644 --- a/source/c04/c04_23.rst +++ b/source/c04/c04_23.rst @@ -1,6 +1,8 @@ 4.23 电脑使用技巧 ================= +|image0| + 1. 添加右键菜单 --------------- @@ -55,7 +57,8 @@ 點擊,選擇你想要添加的輸入方案,比如這裏選擇 五筆-拼音,再點中,然後選擇皮膚,最後就會觸發重新部署,配置生效。 -|image0| +|image1| -.. |image0| image:: http://image.python-online.cn/20200119143952.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20200119143952.png diff --git a/source/c04/c04_24.md b/source/c04/c04_24.md index 7ddfaf5..d93d46d 100644 --- a/source/c04/c04_24.md +++ b/source/c04/c04_24.md @@ -1,5 +1,7 @@ # 4.24 Python “用户环境”的一次完美应用 +![](http://image.iswbm.com/20200602135014.png) + 在之前写过一篇关于虚拟环境使用的文章 :[Python 虚拟环境使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&chksm=e886669bdff1ef8d82aae3a231ef0651f82d5e97cf1e64aceda00e686119900518c202dc9b1b&scene=21#wechat_redirect). 但是还没有好好的介绍一下 Python 的用户环境,原因是自己一直没遇到要使用 `用户环境` 的使用场景,所以就一直懒得写。 diff --git a/source/c04/c04_24.rst b/source/c04/c04_24.rst index c0804c8..630834c 100644 --- a/source/c04/c04_24.rst +++ b/source/c04/c04_24.rst @@ -1,6 +1,8 @@ 4.24 Python “用户环境”的一次完美应用 ==================================== +|image0| + 在之前写过一篇关于虚拟环境使用的文章 :\ `Python 虚拟环境使用指南 `__. @@ -21,11 +23,11 @@ Python 来实现,这个 Python 脚本里有一些依赖库(比如 之前介绍过的 paramiko 这个神器),在跳板机上中并没有安装。 -|image0| +|image1| 做为普通用户的你,是没有权限安装第三方包的。 -|image1| +|image2| 问题就来了,我如何才能在跳板机中使用 paramiko 这个包呢? @@ -48,7 +50,7 @@ paramiko 这个包不就好了。 3、 使用 console 模式调试的话,进入很不方便 -|image2| +|image3| 就算你不使用 console 模式,你调用脚本的方式,也会很奇怪,你得这样 @@ -182,15 +184,16 @@ Python 如何确定应该从哪个路径进行导入呢? 然后直接使用 python 进入 console 模式,现在已经可以直接使用 paramiko 这个包了。 -|image3| +|image4| .. figure:: http://image.python-online.cn/image-20200320125724880.png :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! -.. |image0| image:: http://image.iswbm.com/20200427180207.png -.. |image1| image:: http://image.iswbm.com/20200427180042.png -.. |image2| image:: http://image.iswbm.com/20200427182334.png -.. |image3| image:: http://image.iswbm.com/20200427185854.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200427180207.png +.. |image2| image:: http://image.iswbm.com/20200427180042.png +.. |image3| image:: http://image.iswbm.com/20200427182334.png +.. |image4| image:: http://image.iswbm.com/20200427185854.png diff --git a/source/c05/c05_01.md b/source/c05/c05_01.md index 5525f87..7bfe802 100644 --- a/source/c05/c05_01.md +++ b/source/c05/c05_01.md @@ -1,5 +1,7 @@ # 5.1 图解九大经典排序算法 +![](http://image.iswbm.com/20200602135014.png) + --- diff --git a/source/c05/c05_01.rst b/source/c05/c05_01.rst index 8f77543..716e913 100755 --- a/source/c05/c05_01.rst +++ b/source/c05/c05_01.rst @@ -1,6 +1,8 @@ 5.1 图解九大经典排序算法 ======================== +|image0| + -------------- 排序算法,可谓是算法中的基础,在面试中,也是面试官最喜欢考察的。经常会让你手写一个选择排序、冒泡排序,如果能在最短的时间内顺畅地写出来,一定会让面试官眼前一亮。 @@ -78,7 +80,7 @@ **排序原理** :遍历n趟(n为数组的长度),每一趟都从「待排序」的元素中排出最小(或最大)的元素。 -**原理图解** : |image2| +**原理图解** : |image3| 代码实现: @@ -109,7 +111,7 @@ **排序原理** :每次将一个待排序的元素与已排序的元素进行逐一比较,直到找到合适的位置按大小插入。通俗地说,就类似我们打牌的时候,给牌进行排序。 -**原理图解** : |image3| +**原理图解** : |image4| 代码实现: @@ -158,7 +160,7 @@ **原理图解**\ : -|image4| |希尔排序| +|image5| |希尔排序| **代码实现**\ : @@ -288,7 +290,7 @@ **原理图解**\ : |分而治之思想| 重点其实是这个“治”的过程,如何实现将两个有序数组合并起来?思路大概是这样的。 -|image8| |合并两个有序数组| +|image9| |合并两个有序数组| **代码实现**\ : 这里只实现正序,感兴趣的同学,可以试倒序。 @@ -424,15 +426,16 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD 关注公众号,获取最新干货! +.. |image0| image:: http://image.iswbm.com/20200602135014.png .. |快速排序| image:: http://image.python-online.cn/Fpj4DFN_YCtfmJwb_85QnsuIVLqk .. |\|冒泡排序\|| image:: http://image.python-online.cn/FvbrVECeq58hY8TptG4ilkL5Owcc -.. |image2| image:: http://image.python-online.cn/FmZ_24t62gF32Dg3AgtZe-U5OuLY -.. |image3| image:: http://image.python-online.cn/FmLrNuhfNcnYnLGoYJv-YbpBPV7n -.. |image4| image:: http://image.python-online.cn/Fm44FD0KE9Y4RM7MF3knlGVCJba4 +.. |image3| image:: http://image.python-online.cn/FmZ_24t62gF32Dg3AgtZe-U5OuLY +.. |image4| image:: http://image.python-online.cn/FmLrNuhfNcnYnLGoYJv-YbpBPV7n +.. |image5| image:: http://image.python-online.cn/Fm44FD0KE9Y4RM7MF3knlGVCJba4 .. |希尔排序| image:: http://image.python-online.cn/FqTP6YjNoM52fA-bA8pSPdbLgcZh .. |堆排序| image:: http://image.python-online.cn/FgRFOfPhrL0yeUuGzly5309APCnD .. |分而治之思想| image:: http://image.python-online.cn/FjQebwFfa2tDYS78_CUxy1rXmufj -.. |image8| image:: http://image.python-online.cn/FjP-_a66OUAZtTpU1ytqZ66My80C +.. |image9| image:: http://image.python-online.cn/FjP-_a66OUAZtTpU1ytqZ66My80C .. |合并两个有序数组| image:: http://image.python-online.cn/FnCZ-3Pj39T_ELROSsGRDN31yWtY .. |桶排序| image:: http://image.python-online.cn/FljGa3F3wM_YGACETeuRHCiERXKb .. |基数排序| image:: http://image.python-online.cn/FhwWVp4LVABIMPHqNo_cpjJA9kHV diff --git a/source/c05/c05_02.md b/source/c05/c05_02.md index fe6f528..0340779 100644 --- a/source/c05/c05_02.md +++ b/source/c05/c05_02.md @@ -1,5 +1,7 @@ # 5.2 递归算法:走楼梯会思考的题 +![](http://image.iswbm.com/20200602135014.png) + 今天来看一道有点意思的题目,有点意思的意思呢,不是说难,而是题目一想好像很难,但是如果找对了解决的思路,就能迎刃而解了。 问题是: diff --git a/source/c05/c05_02.rst b/source/c05/c05_02.rst index c5abf2d..05e6518 100755 --- a/source/c05/c05_02.rst +++ b/source/c05/c05_02.rst @@ -1,6 +1,8 @@ 5.2 递归算法:走楼梯会思考的题 ============================== +|image0| + 今天来看一道有点意思的题目,有点意思的意思呢,不是说难,而是题目一想好像很难,但是如果找对了解决的思路,就能迎刃而解了。 问题是: >假如这里有 n 个台阶,你可以选择每次完成一个台阶 或者 @@ -124,3 +126,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c05/c05_03.md b/source/c05/c05_03.md index eb3dd2e..0660838 100644 --- a/source/c05/c05_03.md +++ b/source/c05/c05_03.md @@ -1,5 +1,7 @@ # 5.3 哈希算法:安全方面的算法应用 +![](http://image.iswbm.com/20200602135014.png) + 昨天准备登陆某网站的时候,在尝试了几次常用密码失败后,我点击了“忘记密码”,娴熟地填入手机号码,随即就收到了一条来自陌生号码的短信,里面包含着一个六个数字串,将这个数字串填入网站提供的输入框,就进入了密码重置流程。 这里有一点细节,值得我们注意,为什么我忘记了密码,你不直接把这个密码返回给我?而是给我一个不相关的口令来重置密码? diff --git a/source/c05/c05_03.rst b/source/c05/c05_03.rst index 823304f..1b00524 100755 --- a/source/c05/c05_03.rst +++ b/source/c05/c05_03.rst @@ -1,6 +1,8 @@ 5.3 哈希算法:安全方面的算法应用 ================================ +|image0| + 昨天准备登陆某网站的时候,在尝试了几次常用密码失败后,我点击了“忘记密码”,娴熟地填入手机号码,随即就收到了一条来自陌生号码的短信,里面包含着一个六个数字串,将这个数字串填入网站提供的输入框,就进入了密码重置流程。 这里有一点细节,值得我们注意,为什么我忘记了密码,你不直接把这个密码返回给我?而是给我一个不相关的口令来重置密码? @@ -121,7 +123,7 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 彩虹表确实像它的名字一样美好,至少黑客眼里是这样。下表是7位以内密码在不同字符集下构造出的彩虹表的情况,彩虹表中哈希链的长度和个数随着字符集的增长而增长,彩虹表的大小和生成时间也随之成倍增加。7位数字组合在彩虹表面前简直就是秒破,即使最复杂的7位密码不到一个小时就能破解,如果采用普通的暴力攻击,破解时间可能需要三周。 -|image0| +|image1| 32位的16进制,我们可以计算一下,可以表示多少原值呢? @@ -164,5 +166,6 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190112181126.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190112181126.png diff --git a/source/c06/c06_01.md b/source/c06/c06_01.md index 2176810..e4f2850 100644 --- a/source/c06/c06_01.md +++ b/source/c06/c06_01.md @@ -1,5 +1,7 @@ # 6.1 一张图带你入门matplotlib +![](http://image.iswbm.com/20200602135014.png) + --- diff --git a/source/c06/c06_01.rst b/source/c06/c06_01.rst index 8929cd0..e9d6966 100755 --- a/source/c06/c06_01.rst +++ b/source/c06/c06_01.rst @@ -1,6 +1,8 @@ 6.1 一张图带你入门matplotlib ============================ +|image0| + -------------- Python在数据分析领域有一个很完备的生态系统,在数据处理方面有 @@ -63,7 +65,7 @@ pylab 已经不再推荐使用了。所以你如果是一个初学者,在网 ------------------ 前两天的官网上闲逛的时候,发现了一张图,可以说把一个图表该有的元素都很好的展示出来了。从这张图入手,一点一点地实现这里面的每个元素,我们也算是入门了matplotlib了。 -|image0| +|image1| 这里面的大部分元素,我相信会点英文的你都能很轻松的理解。 @@ -127,7 +129,7 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 以上的注释,可以说是很直白啦。一张图表该有的东西都有了,不花哨,但实用。 看看我们的代码输出的图表是啥样的。 -|image1| +|image2| -------------- @@ -136,6 +138,7 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png -.. |image1| image:: http://image.python-online.cn/20190511164650.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png +.. |image2| image:: http://image.python-online.cn/20190511164650.png diff --git a/source/c06/c06_02.md b/source/c06/c06_02.md index 6606e83..923e2a4 100644 --- a/source/c06/c06_02.md +++ b/source/c06/c06_02.md @@ -1,5 +1,7 @@ # 6.2 详解六种可视化图表 +![](http://image.iswbm.com/20200602135014.png) + --- diff --git a/source/c06/c06_02.rst b/source/c06/c06_02.rst index cc807e1..9c2556f 100755 --- a/source/c06/c06_02.rst +++ b/source/c06/c06_02.rst @@ -1,6 +1,8 @@ 6.2 详解六种可视化图表 ====================== +|image0| + -------------- 可视化图表,有相当多种,但常见的也就下面几种,其他比较复杂一点,大都也是基于如下几种进行组合,变换出来的。对于初学者来说,很容易被这官网上众多的图表类型给吓着了,由于种类太多,几种图表的绘制方法很有可能会混淆起来。 @@ -36,7 +38,7 @@ show image -|image0| +|image1| 02. 散点图 ---------- @@ -56,7 +58,7 @@ show image plt.plot(x, x, 'r--', x, x**2, 'bs', x, x**3, 'g^') plt.show() -show image |image1| +show image |image2| 03. 直方图 ---------- @@ -98,7 +100,7 @@ show image |image1| show image -|image2| +|image3| 04. 柱状图 ---------- @@ -134,7 +136,7 @@ show image show image -|image3| +|image4| 4.2 叠加柱状图 ~~~~~~~~~~~~~~ @@ -161,7 +163,7 @@ show image plt.grid(True) plt.show() -show image |image4| +show image |image5| 05. 饼图 -------- @@ -187,7 +189,7 @@ show image |image4| plt.show() -show image |image5| +show image |image6| 5.2 嵌套饼图 ~~~~~~~~~~~~ @@ -221,7 +223,7 @@ show image |image5| plt.axis('equal') plt.show() -show image |image6| +show image |image7| 5.3 极轴饼图 ~~~~~~~~~~~~ @@ -253,7 +255,7 @@ show image |image6| plt.show() -show image |image7| +show image |image8| 06. 三维图 ---------- @@ -281,7 +283,7 @@ show image |image7| ax.set_xlabel('X') plt.show() -show image |image8| +show image |image9| 6.2 绘制三维平面图 ~~~~~~~~~~~~~~~~~~ @@ -305,7 +307,7 @@ show image |image8| plt.show() -show image |image9| +show image |image10| -------------- @@ -314,14 +316,15 @@ show image |image9| 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511164738.png -.. |image1| image:: http://image.python-online.cn/20190511164753.png -.. |image2| image:: http://image.python-online.cn/20190511164802.png -.. |image3| image:: http://image.python-online.cn/20190511164814.png -.. |image4| image:: http://image.python-online.cn/20190511164825.png -.. |image5| image:: http://image.python-online.cn/20190511164835.png -.. |image6| image:: http://image.python-online.cn/20190511164843.png -.. |image7| image:: http://image.python-online.cn/20190511164852.png -.. |image8| image:: http://image.python-online.cn/20190511164900.png -.. |image9| image:: http://image.python-online.cn/20190511164915.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511164738.png +.. |image2| image:: http://image.python-online.cn/20190511164753.png +.. |image3| image:: http://image.python-online.cn/20190511164802.png +.. |image4| image:: http://image.python-online.cn/20190511164814.png +.. |image5| image:: http://image.python-online.cn/20190511164825.png +.. |image6| image:: http://image.python-online.cn/20190511164835.png +.. |image7| image:: http://image.python-online.cn/20190511164843.png +.. |image8| image:: http://image.python-online.cn/20190511164852.png +.. |image9| image:: http://image.python-online.cn/20190511164900.png +.. |image10| image:: http://image.python-online.cn/20190511164915.png diff --git a/source/c06/c06_03.md b/source/c06/c06_03.md index 144f5f4..4afd19a 100644 --- a/source/c06/c06_03.md +++ b/source/c06/c06_03.md @@ -1,5 +1,7 @@ # 6.3 如何绘制正余弦函数图象 +![](http://image.iswbm.com/20200602135014.png) + --- diff --git a/source/c06/c06_03.rst b/source/c06/c06_03.rst index 1dc0793..fbed036 100755 --- a/source/c06/c06_03.rst +++ b/source/c06/c06_03.rst @@ -1,6 +1,8 @@ 6.3 如何绘制正余弦函数图象 ========================== +|image0| + -------------- 今天打算通过绘制正弦和余弦函数,从默认的设置开始,一步一步地调整改进,让它变得好看,变成我们初高中学习过的图象那样。通过这个过程来学习如何进行对图表的一些元素的进行调整。 @@ -24,7 +26,7 @@ matplotlib有一套允许定制各种属性的默认设置。你可以几乎控 show() -show image |image0| +show image |image1| 6.3.2 设置基本元素 ------------------ @@ -65,7 +67,7 @@ show image |image0| plt.show() -show image |image1| ## 6.3.3 移动轴线 +show image |image2| ## 6.3.3 移动轴线 还记得我们在初高中学习的三角函数图象,可不是这样,它应该是有四个象限的。而这里却是一个四四方方的图表。 @@ -89,14 +91,14 @@ show image |image1| ## 6.3.3 移动轴线 ax.spines['left'].set_position(('data',0)) 关于\ ``set_position()``\ 这个函数中的data是啥意思?我查了下官网。解释如下 -|image2| 然后最后发现,上面的写法可以用一定更简洁的方式设置,是等价的。 +|image3| 然后最后发现,上面的写法可以用一定更简洁的方式设置,是等价的。 .. code:: python ax.spines['bottom'].set_position('zero') ax.spines['left'].set_position('zero') -show image |image3| ## 6.3.4 添加注释 +show image |image4| ## 6.3.4 添加注释 现在的图形部分已经成型,接下让我们现在使用annotate命令注解一些我们感兴趣的点。 @@ -134,7 +136,7 @@ show image |image3| ## 6.3.4 添加注释 第六个参数,\ ``fontsize``\ ,注释大小; 第七个参数,\ ``arrowprops``\ ,对箭头的类型的一些设置。 -show image |image4| +show image |image5| 6.3.5 完整代码 -------------- @@ -210,9 +212,10 @@ show image |image4| 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511164936.png -.. |image1| image:: http://image.python-online.cn/20190511164949.png -.. |image2| image:: http://image.python-online.cn/20190511165003.png -.. |image3| image:: http://image.python-online.cn/20190511165013.png -.. |image4| image:: http://image.python-online.cn/20190511165020.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511164936.png +.. |image2| image:: http://image.python-online.cn/20190511164949.png +.. |image3| image:: http://image.python-online.cn/20190511165003.png +.. |image4| image:: http://image.python-online.cn/20190511165013.png +.. |image5| image:: http://image.python-online.cn/20190511165020.png diff --git a/source/c06/c06_04.md b/source/c06/c06_04.md index 4b437ea..10e53d1 100644 --- a/source/c06/c06_04.md +++ b/source/c06/c06_04.md @@ -1,5 +1,7 @@ # 6.4 子图与子区 难点突破 +![](http://image.iswbm.com/20200602135014.png) + --- 在 matplotlib 中有两个非常重要,而且很容易混淆的概念,一个是 subplot,一个是axes。这两个概念将贯穿整个 matplotlib 学习历程。在往后进行深度研究之前呢,务必要先弄懂这两个概念,否则将后面的绘制代码,我相信你一定会一头雾水的。 diff --git a/source/c06/c06_04.rst b/source/c06/c06_04.rst index a21e883..d8beb3e 100755 --- a/source/c06/c06_04.rst +++ b/source/c06/c06_04.rst @@ -1,6 +1,8 @@ 6.4 子图与子区 难点突破 ======================= +|image0| + -------------- 在 matplotlib 中有两个非常重要,而且很容易混淆的概念,一个是 @@ -25,7 +27,7 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib 是将当前图像(figure)按 2 行 2 列的布局进行分割,然后取索引为 1 的子图。注意 matlibplot 是完全借鉴了 MATLAB 的思想,所以的起始索引为 -1,不像 Python 的起始索引为 0。 |image0| +1,不像 Python 的起始索引为 0。 |image1| 他有好几种写法,这里写我在官网学到的几个方法。 @@ -70,14 +72,14 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib plt.plot(t2, np.cos(2*np.pi*t2), 'r--') plt.show() -|image1| +|image2| 6.4.2 子图 ---------- 子图(axes),和子区(subplot)非常相似,一个子图可能是由一个或多个子区域构成的。它比子区更加灵活。 -它可以是这样 |image2| +它可以是这样 |image3| 要实现如上这个效果。常用的有两种方法。 @@ -105,7 +107,7 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib 这个比较规则的划分我们举个例子看看。 -|image3| +|image4| 代码如下: @@ -139,11 +141,11 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib 为什么说,子图的灵活性更高呢,因为它允许把图片放置到图像(figure)中的任何地方(如下图)。所以如果我们想要在一个大图片中嵌套一个小点的图片,我们通过子图(axes)来完成它。 -|image4| +|image5| 图中的 axes 是如何实现的,刚开始我也有点懵逼,在查阅了官方文档后,我才明白。 -|image5| +|image6| ``left`` 是指,离左边界的距离。 ``bottom`` 是指,离底边的距离。 ``width`` 是指,子图的宽度。 ``height`` 是指,子图的高度。 @@ -153,7 +155,7 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib 同样地,这个我们也来看一个例子。 这个图的亮点,在于中间,多了两个子图,就像往图中贴上了两个插画一样。 -|image6| +|image7| 那么这个如何实现呢? @@ -204,11 +206,12 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511165103.png -.. |image1| image:: http://image.python-online.cn/20190511165132.png -.. |image2| image:: http://image.python-online.cn/20190511165152.png -.. |image3| image:: http://image.python-online.cn/20190511165159.png -.. |image4| image:: http://image.python-online.cn/20190511165211.png -.. |image5| image:: http://image.python-online.cn/20190511165221.png -.. |image6| image:: http://image.python-online.cn/20190511165229.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511165103.png +.. |image2| image:: http://image.python-online.cn/20190511165132.png +.. |image3| image:: http://image.python-online.cn/20190511165152.png +.. |image4| image:: http://image.python-online.cn/20190511165159.png +.. |image5| image:: http://image.python-online.cn/20190511165211.png +.. |image6| image:: http://image.python-online.cn/20190511165221.png +.. |image7| image:: http://image.python-online.cn/20190511165229.png diff --git a/source/c06/c06_05.md b/source/c06/c06_05.md index a1b502e..e787493 100644 --- a/source/c06/c06_05.md +++ b/source/c06/c06_05.md @@ -1,5 +1,7 @@ # 6.5 绘制酷炫的gif动态图 +![](http://image.iswbm.com/20200602135014.png) + --- ## 6.5.1 准备工作 diff --git a/source/c06/c06_05.rst b/source/c06/c06_05.rst index f1883dd..0b63174 100755 --- a/source/c06/c06_05.rst +++ b/source/c06/c06_05.rst @@ -1,6 +1,8 @@ 6.5 绘制酷炫的gif动态图 ======================= +|image0| + -------------- 6.5.1 准备工作 @@ -137,7 +139,7 @@ matplotlib 给我们提供了一个函数,\ ``animation.FuncAnimation`` # 将 gif 图展示在页面上 Image(url='./ming.gif') -绘制出来的结果如下: |image0| +绘制出来的结果如下: |image1| -------------- @@ -146,5 +148,6 @@ matplotlib 给我们提供了一个函数,\ ``animation.FuncAnimation`` 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2018/12/25/5c2226078799b.gif +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2018/12/25/5c2226078799b.gif diff --git a/source/c06/c06_06.md b/source/c06/c06_06.md index 8e8a594..aaa386b 100644 --- a/source/c06/c06_06.md +++ b/source/c06/c06_06.md @@ -1,5 +1,7 @@ # 6.6 自动生成图像视频 +![](http://image.iswbm.com/20200602135014.png) + ## 6.6.1 准备工作 如果你和我一样使用的是 Jupyter 这个工具的话,在绘制之前,需要安装一个工具,就是 `ffmpeg`。 diff --git a/source/c06/c06_06.rst b/source/c06/c06_06.rst index 16a290a..9723f92 100755 --- a/source/c06/c06_06.rst +++ b/source/c06/c06_06.rst @@ -1,6 +1,8 @@ 6.6 自动生成图像视频 ==================== +|image0| + 6.6.1 准备工作 -------------- @@ -14,7 +16,7 @@ ,我需要将其安装到我的环境中。首先打开我们的命令行(注意不是一般的CMD),使用windows 的查找入口: -|image0| +|image1| 然后执行如下命令安装 @@ -142,5 +144,6 @@ Jupyter NoteBook 里观察整个变化的过程。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190511165315.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190511165315.png diff --git a/source/c07/C07_08.md b/source/c07/C07_08.md index 6e96207..d2f8d54 100644 --- a/source/c07/C07_08.md +++ b/source/c07/C07_08.md @@ -1,5 +1,7 @@ # 7.8 Keepalived 部署文档 +![](http://image.iswbm.com/20200602135014.png) + 参考文档:[keepalived搭建zabbix server双机高可用](https://segmentfault.com/a/1190000008684320) ```bash diff --git a/source/c07/c07_01.md b/source/c07/c07_01.md index e5a82cd..cda49e3 100644 --- a/source/c07/c07_01.md +++ b/source/c07/c07_01.md @@ -1,1787 +1,1789 @@ -# 7.1 Linux 命令行的艺术 - ---- - -## 一、目录/文件 - -### 1.1 目录文件日常操作 -``` -. -> 当前目录 -.. -> 上一级目录 -.file -> 隐藏文件 -.dir -> 文件夹 -``` -【ls】 查看指定目录文件 -`ls`命令是最常用的linux命令,要配合着选项使用。 - -``` -常用命令 - -ls -lh # 方便查看文件大小 -ls -AsSh -``` -【pwd】:查看当前目录 -``` -pwd等价于/bin/pwd -L,显示当前绝对路径,如果是链接,则显示链接路径 -pwd -P等价于/bin/pwd -P,显示实际路径,而非链接路径 - -如果文件夹被临时删除,pwd还是显示未删除文件夹的那个路径,这时候可以使用/bin/pwd,就会提示当前路径不存在 -``` -【cd】:切换目录 - -``` -cd - # 上一目录 -cd ~ # 家目录 -cd # 家目录 -cd .. # 上一目录 -cd !$ # 将上命令的参数做为cd 参数 -cd ~zabbix # 进入zabbix用户的主目录 -``` - -【touch】:新建文件 -``` -1. 创建新文件同时可以指定一些时间参数 -$ touch newfile - -2. 一次性创建多个文件 -$ touch {1..10}.txt -``` -可以对已有文件修改时间戳(ll显示的时间) -``` -【1】 -touch -d [[CC]YY]MMDD text -touch -t [CC[YY]MMDDhhmm[.SS] text -CC为年数中的前两位,即”世纪数”;YY为年数的后两位,即某世纪中的年数.如果不给出CC的值 -touch -d 20171004 text -touch -t 201710041330.30 text - -【2】 --r:以另一文件为基准更新时间戳 -以file1的时间戳为基准,将file2的改成一样的 -touch -r file1 file2 - -【3】其他不常用参数 --a 或--time=atime或--time=access或--time=use  只更改存取时间。 --c 或--no-create  不建立任何文档。 --f  此参数将忽略不予处理,仅负责解决BSD版本touch指令的兼容性问题。 --m 或--time=mtime或--time=modify  只更改变动时间。 -``` -【mkdir】:新建文件夹 -``` -# 创建一个或多个的同级目录 -mkdir adir bdir - -# 递归创建目录 -# 当前目录下并没有cdir这个文件夹,如果使用单纯mkdir cdir/ddir会报错。 -# 应该使用 -mkdir -p cdir/ddir - -# 指定权限 -mkdir -m 777 dir_name - -# 显示创建信息(成功或失败) -mkdir -v dir_name - -# 使用!$快速进入新建文件夹 -mkdir ming -cd !$ -``` - -【rm | rmdir】 - -``` -# 只能删除空文件夹 -rmdir dirname - -# 删除当前文件夹下所有文件并不提示 -rm -f * - -# 递归删除dirname下所有文件 -rm -r dirname - -# 上面二者结合,递归删除dirname下所有文件并不提示 -rm -rf dirname -``` - -【cp】:复制文件或目录 -``` -# 递归复制old_dir目录下所有文件和文件夹到new_dir文件夹下 -cp -r old_dir new_dir -``` - - -> 注意:默认cp拷贝文件后会使用预设权限,即其他人没有更改的权限,需要使用-p或 -> 者-a文件所有的所有特性都一起复制过来 (拉取源站数据缓存,不更改文件最后修改 -> 时间) - -【mv】:移动或重命名 -``` -# 移动/tmp/test/sample.txt文件到当前目录下 -mv /tmp/test/sample.txt ./ -``` -【cat 】查看文件 - -**作用**:文本文件查看和连接工具,用于查看文本文件的内容。 -**命令格式**:cat file 经常和more、head、tail、less以及管道命令结合使用,如:cat file | more、cat file | head等。 - -三个功能 -``` -1.一次显示整个文件:cat filename -2.从键盘创建一个文件:cat > filename 创建文件,输入内容,Ctrl+d 结束 -3.将几个文件合并为一个文件:cat file1 file2 > file -``` - -命令参数 -``` --A, --show-all 等价于 -vET --b, --number-nonblank 对非空输出行编号 --e 等价于 -vE --E, --show-ends 在每行结束处显示 $ --n, --number 对输出的所有行编号,由1开始对所有输出的行数编号 --s, --squeeze-blank 有连续两行以上的空白行,就代换为一行的空白行 --t 与 -vT 等价 --T, --show-tabs 将跳格字符显示为 ^I --u (被忽略) --v, --show-nonprinting 使用 ^ 和 M- 引用,除了 LFD 和 TAB 之外 -``` -【tac】反向查看 -``` -cat是第一行到最后一行,tac是最后一行到第一行 -``` -【head | tail】 - -``` -# 显示前10行 -head xs.txt 等价于 cat xs.txt|head 等价于 cat xs.txt|head -n 10 - - -# 显示最后10行 -tail xs.txt 等价于 tail -n 10 xs.txt - - -# 显示除末尾10行外 -head -n -10 xs.txt -``` - -【more | less】 - -更具体命令可以参考:[Linux中more和less命令用法](http://www.cnblogs.com/aijianshi/p/5750911.html) -``` -【more】 -# 回车就往下一行显示,空白键就往下一页显示 -# 按 b 键就会往回一页显示,按 q 就会退出 -more xs.txt - -# 查找比较鸡肋,并从该处前两行开始显示输出 -more +/查找内容 xs.txt - -【less】 -# 和more几乎一样的功能,但是less更灵活(可以使用任何vim的移动命令,还有标记功能很使用),比如查找命令,直接像vim一样输入/即可 -less xs.txt - -ma : 使用 a 标记文本的当前位置 -'a : 导航到标记 a 处 - -F :实现和tail -f的功能,实时输出内容,tail +F xs.txt -``` - - - -**小练习** - -选取xs.txt的10-20行 - -``` -head -n 20 xs.txt|tail - -# 更快捷的方法 -sed -n '10,20p' xs.txt -``` - - - -### 1.2 文件处理 - -【wc】 - -统计文件信息 -``` -# 统计所有信息(行数、单词数、字符数) -$ wc /etc/passwd - -# 行数 -$ wc -l /etc/passwd - -# 单词数 # 中文无法统计 -$ wc -w /etc/passwd - -# 字节数 -$ wc -c /etc/passwd - -# 字符数 -$ wc -m /etc/passwd - -# 最长行字节数 -$ wc -L /etc/passwd -``` - -【sort】 - -``` -$ cat /etc/passwd | sort -$ cat /etc/passwd | sort -r - -# 以:为分隔符,对第三列排序,所得的结果,再通过cut以:为分隔符取第三列 -$ cat /etc/passwd | sort -t ":" -k 3 |cut -d ":" -f 3 -``` - -【uniq】 - -``` -# 显示每行重复频率 -uniq -c 文件名 - -# 只显示有重复的行 -uniq -d 文件名 -``` -### 1.3 文字处理 - -【tr】 - -`tr`可以删除或者去重某文本信息中的某些文字。还可以进行替换操作 -``` -# 删除hello,里面所有l,o字符 -$ echo 'hello' | tr -d 'lo' - -# 去重hello里的l -$ echo 'hello' | tr -s 'l' - -# hello里,l换成a,e换成b -$ echo 'hello' | tr 'le' 'ab' -``` - -【col】 - -将tab转换为等数量的空格,或者反转 -``` --x tab转空格 --h 空格转tab(默认) -``` - -``` -# 查看 /etc/protocols 中的不可见字符,可以看到很多 ^I ,这其实就是 Tab 转义成可见字符的符号 -$ cat -A /etc/protocols - -# 使用 col -x 将 /etc/protocols 中的 Tab 转换为空格,然后再使用 cat 查看,你发现 ^I 不见了 -$ cat /etc/protocols | col -x | cat -A -``` - -【sed】 - -文件处理工具。 -使得不需要打开文件就可以对文件进行操作(删除,替换,选取,新增)。以行为单位进行处理。 - -常用选项 - -``` --n∶经过处理后的结果显示出来。不影响真实文件。 --e:直接在指令列模式上进行 sed 的动作编辑;(没明白) --f∶直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的sed 动作; --i∶直接修改读取的档案内容,而不是由屏幕输出。 -``` -常用命令: -``` -a∶新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~ -c∶取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行! -d∶删除,因为是删除啊,所以 d 后面通常不接任何咚咚; -i∶插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行); -p∶列印,亦即将某个选择的资料印出。通常 p 会与参数 sed -n -s∶取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦! -``` - -删除某行 -``` - sed '1d' somefile #删除第一行 - sed '$d' somefile #删除最后一行 - sed '1,2d' somefile #删除第一行到第二行 - sed '2,$d' somefile #删除第二行到最后一行 - - # 以上,只是输出,源文件并不会替换,若要对源文件进行修改,需加 -i - sed -i '1,2d' somefile -``` -删除指定行的上一行或下一行 - -```shell -删除指定文件的上一行 -sed -i -e :a -e '$!N;s/.*n(.*directory)/1/;ta' -e 'P;D' server.xml -删除指定文件的下一行 -sed -i '/pattern="%/{n;d}' server.xml -``` - -显示某行 - -``` - sed -n '1p' somefile #显示第一行 - sed -n '$p' somefile #显示最后一行 - sed -n '1,2p' somefile #显示第一行到第二行 - sed -n '2,$p' somefile #显示第二行到最后一行 -``` -使用模式进行查询 -``` - sed -n '/ruby/p' somefile #查询包括关键字ruby所在所有行 - sed -n '/\$/p' somefile #查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含义 -``` -插入行(a 表示 append 追加,也可以使用 i,表示 insert,如果你要在第一行插入,就不得不使用 i 了) -``` -sed '1a drink tea' somefile #第一行后增加字符串"drink tea" -sed '1,3a drink tea' somefile #第一行到第三行后增加字符串"drink tea" -sed '1a drink tea\nor coffee' somefile #第一行后增加多行,使用换行符\n -``` -在某一行后面加入一行(示例为第四行) - -``` -sed -i 'N;4addpdf' a.txt -sed -i 'N;4ieepdf' a.txt -``` - - - -替换行 - -``` -sed '1c Hi' somefile #第一行代替为Hi -sed '1,2c Hi' somefile #第一行到第二行代替为Hi -``` -替换行中部分数据 -``` -sed 's/ruby/bird/g somefile' #替换ruby为bird,记住这个并不会更改源文件,只是输出 -sed 's/ruby//g' somefile #删除ruby -``` - -在某行的前一行或后一行添加内容,其中 `\` 可省略,但为了可读性,所以我一般不省略 - -```shell -# 匹配行前加 -sed -i '/2222222222/a\3333333333' test.txt - -# 匹配行前后 -sed -i '/2222222222/i\3333333333' test.txt -``` - - - -【注意】:以上对源文件都不做修改,若要修改,要加上`-i` - -替换换行符 - -```shell -# 将换行符换成逗号 -$ cat a.txt -1 -2 -3 -3 -$ sed ":a;N;s/\n/,/g;ta" a.txt -1,2,3,4 -``` - - - - -### 1.4 文件重构 -**cut** - -【显示每行的某位置的内容】 -``` -# 前五个(包含第五个) -$ cut /etc/passwd -c -5 -# 前五个之后的(包含第五个) -$ cut /etc/passwd -c 5- -# 第五个 -$ cut /etc/passwd -c 5 -# 2到5之间的(包含第五个) -$ cut /etc/passwd -c 2-5 -``` -【以指定的分隔符分隔,并返回某些列】 -``` -# 返回第一列和第六列 -$ cut /etc/passwd -d ":" -f 1,6 -``` -**awk** - -awk是一个强大的文本分析工具。 -简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片(相当于Excel的分列),切开的部分再进行各种分析处理。 - -``` -# 打印空白行,文件有几个空行,就输出几个空行 -awk '/^$/{print "This is a blank line"}' awk.txt - -# 打印全部列,引用变量 -awk '{print $0}' awk.txt - -# 打印前三列,引用变量 -awk '{print $1,$2,$3}' awk.txt - -# 指定间隔符为空格,获取第四列 -awk -F" " '{print $4}' awk.txt - -# 重组表格 -awk -F ':' '{print $1"\t"$7}' awk.txt - -# 表头和结尾,会先输出name,shell -awk -F ':' 'BEGIN {print "name,shell"} {print $1","$7} END {print "blue,/bin/nosh"}' awk.txt - -# 在awk中引用变量变量,在原理是拼接. -limit=30 -df -Th| grep "/dev/vd" | sed 's/%//g' |awk '{ if($6>'"$limit"') print $6}' -``` - - - -### 1.5 其他命令 -**nl** - --b :指定行号指定的方式,主要有两种: -``` --b a :表示不论是否为空行,也同样列出行号(类似 cat -n); --b t :如果有空行,空的那一行不要列出行号(默认值); -``` --n :列出行号表示的方法,主要有三种: -``` --n ln :行号在萤幕的最左方显示; --n rn :行号在自己栏位的最右方显示,且不加 0 ; --n rz :行号在自己栏位的最右方显示,且加 0 ; -``` - - -``` --w :行号栏位的占用的位数。 -nl -b a -n rz -w 3 text -``` - -### 1.6 文件查找 -**which:查询软件** - -在PATH变量指定的路径中,搜索某个系统命令(`可执行文件`)的位置,并且返回第一个搜索结果。 - -参数选项(基本不用) -``` --n  指定文件名长度,指定的长度必须大于或等于所有文件中最长的文件名。 --p  与-n参数相同,但此处的包括了文件的路径。 --w  指定输出时栏位的宽度。 --V  显示版本信息 -``` - -**grep:搜索神器** - -搜索并筛选显示结果。 -该命令经常配合管道命令来控制输出。 -以下 是常用的选项: -![](http://image.python-online.cn/17-9-20/47469030.jpg) - -【非常好用:不打开文件,直接搜索指定目录下文件内的内容】 -``` -$ vim a # wongbingming -$ vim b # wangbingming -$ grep -rnI "bingming" . # 当然这里也可以使用正则表达式 -./a:1:wongbingming -./b:1:wangbingming - -参数解释 --r 递归遍历各个文件夹下的所有文件 --n 显示在文件中的第几行查询到 --I 忽略二进制文件 -``` - -**whreris:简单快速** - -定位可执行文件、源代码文件、帮助文件在文件系统中的位置。 -个搜索很快,因为它并没有从硬盘中依次查找,而是直接从数据库中查询。 -``` -$ whereis who -``` -参数选项 -``` --b 定位可执行文件。 --m 定位帮助文件。 --s 定位源代码文件。 --U 搜索默认路径下除可执行文件、源代码文件、帮助文件以外的其它文件。 --B 指定搜索可执行文件的路径。 --M 指定搜索帮助文件的路径。 --S 指定搜索源代码文件的路径。 -``` -**locate:快而全** - -通过`/var/lib/mlocate/mlocate.db`数据库查找,不过这个数据库也不是实时更新的,系统会使用定时任务每天自动执行`updatedb`命令更新一次,所以有时候你刚添加的文件,它可能会找不到,需要手动执行一次 `updatedb`命令(在我们的环境中必须先执行一次该命令)。 -``` -$ updatedb -$ locate /etc/sh - -# 查找/etc/目录下所有以sh开头的文件 -``` -**find:小而细** - -[鸟哥的Linux私房菜-find](http://linux.vbird.org/linux_basic/0220filemanager.php#find) -[每天一个linux命令(19):find 命令概览](http://www.cnblogs.com/peida/archive/2012/11/13/2767374.html) -[每天一个linux命令(20):find命令之exec](http://www.cnblogs.com/peida/archive/2012/11/14/2769248.html) - -### 1.7 文件传输/下载 -**scp** - -实现不同机器之间传输数据(加密) -[scp详解](http://www.cnblogs.com/peida/archive/2013/03/15/2960802.html) - -**curl** - -Curl是一个命令行方式下传输数据的开源传输工具,支持多种协议包括:FTP,HTTP,HTTPS,IMAP,POP3,TELNET等。同样支持HTTP POST方法,PUT方法,FTP上传,cookie,用户名/密码认证,下载文件端点续传等,功能十分强大。 - -常用的,用于模拟浏览器请求。 -[curl详解](http://blog.csdn.net/zzzmmmkkk/article/details/38569057) - -**wget** - -测试速率 -``` -wget -S http://115.231.74.93:80/lvs.lxdns.net/test.rar && rm -rf test.rar* -``` - - - -### 1.8 文件压缩 -**zcat** - -查看压缩的文件内容 -``` -zcat file.gz -``` -Linux上的压缩格式比Windows上多很多,在 Windows 上最常见的不外乎这三种 `*.zip`,`*.rar`,`*.7z` 后缀的压缩文件。而在 Linux 上面常见的格式除了以上三种外,还有 `*.gz`,`*.xz`,`*.bz2`,`*.tar`,`*.tar.gz`,`*.tar.xz`,`*.tar.bz2`,对于常见的压缩格式,tar已经可以解决,所以这里只介绍tar。tar并不能压缩和解压7z,zip等其他文件 - -**tar** - -压缩示例 -``` -# test是当前目录下一个文件夹 -$ tar -czvphf test.tar.gz old_folder -``` -参数解释 -``` --c 指明创建tar文件 --z 指明生成gz文件 - --v 可视输出,不加就静默压缩 - --f 指明压缩后的文件名,必须-f后必须紧跟文件名,否则无效 --p 当在其他主机还原时希望保留文件的属性 --h 备份链接指向的源文件而不是链接本身 - -``` -解包示例 -``` -$ mkdir new_folder -$ tar -xzvf test.tar.gz -C new_folder -``` -参数解释 -``` --x 解压命令 --z 指明源文件是gz文件 --f 指明压缩后的文件名,必须-f后必须紧跟文件名,否则无效 - --v 可视化输出解包过程,一般不加,静默解包 - --C 指明解压到哪个目录 -``` -其他压缩格式 -``` -*.tar.gz -z -*.tar.xz -J -*tar.bz2 -j - -``` -**gz** - -最简单的压缩格式 - -``` -# 压缩 -gzip somefile - -# 解压 -gzip -d somefile.gz -``` - -**7zip** - -``` -yun install p7zip - -# 解压 -7za x file.7z - -# 压缩 -# 先将要压缩的文件都放到~/test/ 下 -7za a file.7z ~/test/ -``` -**zip** - -``` -# 解压 -unzip some.file - -# 压缩 -zip -r some.zip file1 file2 dir1 dir2... - -# 如果你的目标zip包没有 . ,那么zip会自动加上后缀 .zip -zip stuff * - -# 打包包含隐藏文件的包 -zip stuff .* * - -# 打包 foo 文件夹的文件,但是不包含文件夹本身 -zip -j foo foo/* - -# 断点打包,在磁盘空间不足的使用可以使用 -zip -rm foo foo/tom -zip -rm foo foo/dick -zip -rm foo foo/harry - -# 分多个包,如果foo文件夹下有5G大小的文件,那么会分成两个2g的,一个1g的三个包,分别命名:split.z01,split.z02,split.zip -zip -s 2g -r split.zip foo -``` - -### 1.9 rpm包管理 - -``` -# 解压rpm包 -rpm2cpio xxx.rpm |cpio -div - -# 查看文件属于哪个rpm包 -rpm -qf /path/filename - -# 使用rpmrebuild重新生成rpm包 -# 使用rpmrebuild需要依赖rpmbuild:yum install -y rpmbuild -rpmrebuild xxx -``` - -### 1.10 ftp工具使用 - -```shell -$ ftp -ftp> help -``` - -![](http://image.python-online.cn/20190705182629.png) - - - -## 二、系统管理 - -### 2.0 环境变量 - -变量分为用户变量(env)和shell变量(set) -``` -# 添加shell变量 - -[root@host ~]# myuser=wangbm -[root@host ~]# echo $myuser -wangbm -[root@host ~]# env|grep myuser -[root@host ~]# set|grep myuser -myuser=wangbm - - -# 添加用户变量,会发现用户变量会覆盖shell变量 -[root@host ~]# export myuser=wangbingming -[root@host ~]# env|grep myuser -myuser=wangbingming -[root@host ~]# set|grep myuser -myuser=wangbingming - - -# 删除环境变量 -[root@host ~]# unset myuser -[root@host ~]# env|grep myuser -[root@host ~]# set|grep myuser -``` -一些系统的变量 -``` -# 这里生成的变量,对所有用户生效 -/etc/profile - -# 对特定user生效 -/root/.bash_profile -/home/user/.bashrc - -# 注意修改文件后,要手动source一下 -``` - -### 2.1 相关查询 - -#### 系统信息 - -``` -# 查询开机时间 -$ who -b - -# 查询系统内核 -$ uname -r - -# 查询是否安装某个rpm包 -$ rpm -qa|grep nova - -# service 文件目录 -$ /usr/lib/systemd/system # centos 7.x -$ /etc/init.d # centos 6.x - -# 查询开机自启列表 -$ systemctl list-unit-files -$ chkconfig --list [service_name] - 等级0表示:表示关机 - 等级1表示:单用户模式 - 等级2表示:无网络连接的多用户命令行模式 - 等级3表示:有网络连接的多用户命令行模式 - 等级4表示:不可用 - 等级5表示:带图形界面的多用户模式 - 等级6表示:重新启动 - -# 查看系统版本 -$ cat /etc/redhat-release - -# ------或者------ -$ yum install redhat-lsb -y -$ lsb_release -a - -# 进程树 -$ pstree -p - -# 查看系统运行多长时间 -$ uptime/w - -# 查看系统版本 -lsb_release -a - -# 查看CPU信息 -$ cat /proc/cpuinfo -$ numactk -H - - -# ubuntu 查看更新有哪些是安全更新 -$ apt-get -s --no-download dist-upgrade -V | grep "^Inst.*security.*$" | cut -d " " -f 2 - -# ubuntu 登陆时,显示的安全更新是如何来的,内容:/etc/update-motd.d/90-updates-available -/usr/lib/update-notifier/apt-check --human-readable - -``` - - - -#### 时间查询 - -输出操作系统的当前日期、时间和时区。 -``` -# -s参数用于修改当前的日期和时间 -date –s 2007-10-17 -date –s 18:05:00 - -用法:cd /CNCLog/cache/qsLogBackSrcFull/bkDir/`date +%Y-%m-%d` -``` - -### 2.2 系统分区 - -#### 分区介绍 -Linux的分区,不同于Windows,一定要区别对待,不然会搞不明白。 - -Linux的分区的过程经历以下几个步骤 -``` -1. 设备分区:对硬盘存储空间的划分 -2. 格式化:写入文件系统 -3. 挂载:将分区挂载到目录上,才能访问数据 -``` -关于硬件对应的设备文件名,可以参照下图 -![](http://image.python-online.cn/17-10-15/97911325.jpg) - -其中以硬盘为例来说明 -``` -硬盘可以分为三种 -hd : IDE硬盘接口(淘汰,接口最大传输100来M) -sd : SCSI硬盘接口(淘汰,接口最大传输200M),和SATA硬盘接口 - -现在都是SATA的硬盘接口 - -/dev/sda1 表示的是第一块(a)SATA硬盘的第一个分区(1) -/dev/sdb2 表示的是第二块(b)SATA硬盘的第二个分区(2) -``` -分区类型 -可以分为:`主分区` 、`扩展分区` 、`逻辑分区` -``` -【主分区】:最多只能有4个 (受硬盘结构限制,如果硬盘结构不变,将都被限制) - -【扩展分区】: - 1. 最多只能有一个 - 2. 主分区+扩展分区,一共只能有四个,可以少于 - 3. 扩展分区下面,不能存放数据,只能进行逻辑分区的划分 - -【逻辑分区】:挂载后就是一个目录下的空间,数量不受限制 -``` -格式化做了哪些事 -``` -【目的】 -1. 不是为了清空数据 -2. 主要是为了写入文件系统 - -【文件系统】 -Windows: -FAT16(每个分区大小最大不能超过2G),FAT32(单个分区大小最大16G,单个文件大小不能超过4G),NTFS - -Linux:ext2,ext3,ext4 - -->>>>> 越往后越先进 - -【写入文件系统做了啥】 -1. 分数据块 -把空间分成若干个等大小的数据块(block),每个大小4kb -如果我们有一个文件10kb,那么会占用3个数据块,实际大小就会是12kb - -你可以查看一个文件夹的大小,也都是4kb -ll -l - -2. 建立数据表 -一个文件被分成若干个的数据块,那么如果有用户访问的时候,就需要有一个表把这些数据块拼凑起来。 -这个数据表就记录了这个文件由哪些数据块组成。 -``` -分区说明 -``` -必须分区 -/ 根目录,最高级目录,不分配的话,所有的文件都没存储,软件没法运行 -/swap 交换分区,虚拟内存,4G以下,分2倍,4G以上,和真实内存一样即可 - -推荐分区 -/boot 启动分区,防止/ 分区写满,导致系统无法启动,不需要很大,200M足矣 - - -逻辑分区号:只能从5开始,即使3,4没有被使用 -``` - -#### 分区操作 -关于分区的操作可以参考这个: [分区操作](https://www.cnblogs.com/zishengY/p/7137671.html) - - -``` -## 查看分区表信息 -$ sudo fdisk -l -$ lsblk - -# 删除分区前,先确认有没有挂载点,有的话需要先umount卸载 -$ fdisk /dev/vdb 然后再按d,w -``` - -挂载分区 fstab - -``` -# 当fstab的根分区被注释后,所有的文件都是只读的。连fstab文件也是,无法取消注释 -mount -o remount,rw / -``` - -格化化分区 - -``` -mkfs.ext4 /dev/sdb1 -``` - - - -### 2.3 进程管理 - -**ps、kill、killall** - -参考文档:[ps 命令的十个简单用法](https://www.cnblogs.com/fakerbin/p/6513365.html) - -``` -# 查看当前所有进程 -ps -aux - -# 终止pid为1095的进程 -kill 1095 - -# 强制终止pid为1095的进程 -# 9是信号强度,强制杀死 -# 其他信号,-1 该信号让进程正常关闭,然后重新读取配置文件之后重启 -# -15 正常结束进程的信号,kill命令默认信号 -kill -9 1095 - -# 终止指定程序 -killall 程序名 - -# pkill -pkill -9 httpd 强制终止进程 -pkill -t -9 pts/1 强制杀死pts/1虚拟终端登入的进程 -``` - -**top** - -常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。 -[linux的top命令参数详解](http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316399.html) - -``` -top 实时得查看进程的状态,以及系统的一些信息(如 CPU、内存信息等),3s刷新一次 -ps 来静态查看当前的进程信息 -pstree 来查看当前活跃进程的树形结构。 -``` - -**pgrep** - -```shell -[root@wangbm web]# pgrep keepalived -21955 -21956 -21957 -[root@wangbm web]# ps -ef|grep keepalived -root 21955 1 0 21:27 ? 00:00:00 /usr/sbin/keepalived -D -root 21956 21955 0 21:27 ? 00:00:00 /usr/sbin/keepalived -D -root 21957 21955 0 21:27 ? 00:00:00 /usr/sbin/keepalived -D -root 22035 12159 0 21:33 pts/0 00:00:00 grep --color=auto keepalived -``` - -**清除僵尸进程** - -一个僵尸进程产生的过程是:父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中。 - -``` -ps -e -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9 -``` - - - -### 2.4 设备信息管理 - -**df** - -显示磁盘的相关信息 -``` -df -Th -``` - -**free** - -输出内存的使用情况,m,g分别是指定单位,默认是kb -``` -free -m -free -g -``` -total 表示总内存大小; -used和free分别表示被使用和空闲内存大小; -share指可被多个进程共同享有的内存; buffers和cached用来保留最近访问的文件和数据,当其他进程需要更多的内存时,这些内容可以被缩减; Free命令还可以输出交换空间的相关信息。 - -**ifconfig** - -显示或设置网络设备 - - - -**last** - -列出目前与过去登入系统的用户相关信息。一般可用来查看系统重启记录 - - - -**history** - -屏幕输出当前用户在命令行模式下执行的最后(1000个)命令 - - - -**passwd** - -这个用于修改密码 - -非交互式修改密码 - -``` -echo 'root12#$'| passwd --stdin root -``` - - - -**reboot/shutdown** - -重启/关机 -``` -reboot/shutdown -r now - -# reboot [-n][-w][d][-i] 重新启动计算机,使用权限是系统管理员 --n 重启前不将记录写回硬盘 --w 并不是真的重启,只是把记录写道/var/log/wtmp文件中 --d 不把记录写入/var/log/wtmp文件中 --i 重启谦先把所有与网络相关的装备停止 -``` - -**rpm** - -``` -# 安装rpm包 -rpm -ivh monitor-system-1.2-1.i386.rpm - -# 更新rpm包 -rpm -U/Fvh (F只更新已存在的文件) - -# 查询包中的文件 -rpm -ql monitor-system - -# 查询文件所属的包 -rpm -qf /usr/local/squid/etc/squid.conf(绝对路径/到目录下查找) - -# 查询所有包 -rpm –qa | grep squid - -# 卸载某个rpm包 -rpm –e monitor-system -``` -**开启80端口** - -``` -vi /etc/sysconfig/iptables - -# 在22端口下面添加一行 --A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT - -# 重启防火墙 -/bin/systemctl restart iptables.service -``` -**修改时区** - -``` -# 由EDT(美国)改成CST(中国) - -$ mv /etc/localtime /etc/localtime.bak -$ ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime -``` - -查看机器连接的哪个交换机 - -``` -$ service lldpd restart -$ lldpcli show nei sum # 查看JS_HuaiAnDXXZ4_166.3 -------------------------------------------------------------------------------- -LLDP neighbors: -------------------------------------------------------------------------------- -Interface: eth1, via: LLDP - Chassis: - ChassisID: mac 00:6b:8e:01:1a:07 - SysName: JS_HuaiAnDXXZ4_165.132_IPMI - Port: - PortID: local 39 - PortDescr: Ethernet1/0/39 - TTL: 120 -------------------------------------------------------------------------------- -Interface: eth0, via: LLDP - Chassis: - ChassisID: mac 58:69:6c:62:7b:c2 - SysName: JS_HuaiAnDXXZ4_166.3 - Port: - PortID: ifname TenGigabitEthernet 0/41 - PortDescr: TenGigabitEthernet 0/41 - TTL: 121 -------------------------------------------------------------------------------- -``` - - - -### 2.5 磁盘管理 - -**df** - -显示指定磁盘文件的可用空间。 -这里要理解挂载的概念。 - -所有的设备(磁盘/等其他介质)都需要挂载在一个目录,Linux才能访问数据。 - -具体可以参考这篇文章:[df 命令](http://www.cnblogs.com/peida/archive/2012/12/07/2806483.html) - -**常用命令** - -``` -$ df -Th -$ df -lh - - -$ df -h 以1024来换算 -$ df -H 以1000来换算 - -$ df -t ext3 指定设备类型 -$ df -T 文件系统的类型 -$ df -i 查看inode的使用情况 -``` -关于inode可以查看:[inode的理解](https://www.cnblogs.com/itech/archive/2012/05/15/2502284.html) - -**du** - -查看目录的容量 -``` -$ du -h -d 0 ~ - -# 参数解释 --h 以人能看得懂的方式显示 --d 扫描的目录层级,0表示只有一个指定目录,1表示次级目录 - -~ 是家目录,这里可以选你指定的目录 - -$ du -s -# 查看当前所处目录总容量大小,单位是k,不可跟-d - -$ du -a -# 查看当前所处目录及所有子目录的所有文件,单位是k,建议不用 -``` -查看文件的大小 -``` -$ du -h file.txt -``` - -**dd** - -`dd`命令用于转换和复制文件,不过它的复制不同于`cp`。之前提到过关于 Linux的很重要的一点,一切即文件,在 Linux上,硬件的设备驱动(如硬盘)和特殊设备文件(如`/dev/zero`和`/dev/random`)都像普通文件一样,只要在各自的驱动程序中实现了对应的功能,`dd`也可以读取自和/或写入到这些文件。这样,`dd`也可以用在备份硬件的引导扇区、获取一定数量的随机数据或者空数据等任务中。dd程序也可以在复制时处理数据,例如转换字节序、或在 `ASCII` 与 `EBCDIC`编码间互换。 - - -这里有篇文章讲得很好:[Linux-dd命令详解](http://www.cnblogs.com/dkblog/archive/2009/09/18/1980715.html) - -补充一点 -``` -# 输出到文件 -$ dd of=test bs=10 count=1 # 或者 dd if=/dev/stdin of=test bs=10 count=1 - -# 输出到标准输出 -$ dd if=/dev/stdin of=/dev/stdout bs=10 count=1 -# 注 -在打完了这个命令后,继续在终端打字,作为你的输入 -``` -前面说到dd在拷贝的同时还可以实现数据转换,那下面就举一个简单的例子:将输出的英文字符转换为大写再写入文件 -``` -dd if=/dev/stdin of=test bs=10 count=1 conv=ucase -``` - - -#### 制作虚拟镜像并挂载 -``` -# 制作虚拟镜像 -$ dd if=/dev/zero of=virtual.img bs=1M count=256 -$ mkfs.ext4 virtual.img - -# 使用一个循环设备(/dev/loop)连接虚拟镜像文件 -$ sudo losetup /dev/loop0 virtual.img - -# 挂载前查看下当前已挂载的设备 -$ sudo mount - -# 挂载 -$ sudo mkdir /mnt/point -$ sudo mount /dev/loop0 /mnt/point - -# 上面连接虚拟镜像文件和挂载设备两条命令可以合并成一条,挂载类型可以省略,会自动识别 -$ sudo mount -o loop [-t ext4] /mnt/point - -# 再次查看挂载的设备,就可以发现我们新挂载的 -$ sudo mount - - -# 卸载:一定要加-fl。不然无法卸载 -$ sudo umount -fl /mnt/point - -# 查看所有与文件相关联的 loop 设备 -$ losetup -a -# 卸载 -$ losetup -d /dev/loop0 -``` - -使用dd拷贝磁盘可以,但是如果想要快速生成大文件,速度就相当慢了,可以试试fallocate命令 - -```shell -fallocate -l 100G test_file -``` - -### 2.6 任务管理 - -#### 一次性任务(at) -##### 创建定时任务 -``` -$ sudo apt-get install at -$ at now+5 minutes -at>/bin/ls -at> # 就是Ctrl+d -job 2 at Sum Oct 15 14:58:00 2017 # 结束的时候,告知执行时间 -``` -##### 管理定时任务 -``` -$ atq # 可以查看当前还有那些定时任务,会显示任务号 - -# 查看具体任务代码 -$ at -c - -# 删除任务 -$ atrm -``` -#### 例行性任务(crontab) -参考以下文章 -1. [每天一个linux命令(50):crontab命令](http://www.cnblogs.com/peida/archive/2013/01/08/2850483.html) -2. [鸟哥的私房菜](http://linux.vbird.org/linux_basic/0430cron.php) - -做个示例 -``` -# 检查crond服务是否启动 -$ ps aux|grep crond - -# 创建任务 -$ crontab -e - -# 输入1 回车 -# 任务是:每天凌晨3点备份日志到/home/temp/目录,文件名为日期 -# 跳到文件尾部输入任务:* 3 * * * cp alternatives.lob /home/temp/$(date \%Y-\%m-\%d)。保存退出 -# 注意%需要加\来转义,不然会被当成换行使用 - -# 查看任务 -$ crontab -l - -# 删除任务(当前用户),当然也可以指定用户 -$ crontab -r [-u user] - -``` - -除了以上,例行性任务还有可能在 /etc/cron.daily/ ,/etc/cron.hourly/, /etc/cron.monthly/, /etc/cron.weekly/ 下面。 - -如果messages 不能正常回滚,可以考虑加个参数 `-f` - -``` -/usr/sbin/logrotate -f /etc/logrotate.conf -``` - -如果还不行,检查一下 `/etc/logrotate.d/syslog` - -``` -chmod 644 /etc/logrotate.d/syslog -``` - -## 三、用户/权限管理 - -### 3.1 用户管理 - -**创建用户** - -``` -【useradd和adduser区别】 -useradd:只创建用户,创建完了用 passwd lilei去设置新用户的密码。更像一种命令。 - -adduser:会创建用户,创建目录,创建密码(提示你设置),做这一系列的操作。更像是一个程序,执行确认一系列操作。 -``` - -**删除用户** - -``` -sudo deluser lilei --remove-home -``` - -**切换用户** - -``` -su [user] # 切换到其他用户,环境变量不变,还是当前路径,如果不指定user,就切换到root - -su - [user] # 切换到其他用户,环境变量也切换,路径变为user家目录,如果不指定username,就切换到root,相当于使用user进行登录 -``` - -Ubuntu切换用户 -``` -# 切换到root -$ sudo su - -# 切换到普通用户 -$ su user -``` -CentOS - -``` -$ su -$ su root -$ su - -$ su -root - -$ su user -$ su -user -``` -**用户组** - -``` -1. 每个用户都至少属于一个用户组,创建的时候如果不指定,就和当前用户的组一样(root用户组除外)。 -2. 一个用户可以属于多个用户组。 -``` - -``` -# 查看所属用户组 -$ groups [user] -``` - -**把用户加入到用户组** - -```shell -# 把 zabbix 加入到 root 用户组 -usermod -G root zabbix -``` - -另外还有其他方法,可查阅:[在 Linux 中把用户添加到组的四个方法](https://linux.cn/article-10768-1.html) - -**UID/GID/组ID** - -``` -# 显示相关信息,如要查看root用户的信息 -id root - -# 所有文件都保存在/etc/)asswd -vi /etc/passwd -``` - -管理用户:[查看用户的UID和GID](http://blog.csdn.net/jackailson/article/details/50993427) -管理密码:[Linux下/etc/shadow文件](http://blog.csdn.net/u011641885/article/details/46681697) - - - -**添加sudo用户组** - -``` -【第一种方法】 -在root下,visudo或者vi /etc/sudoers,找到root ALL=(ALL)的下一行添加一行,user ALL=(ALL),user是对应的用户名 - -【第二种方法】 -在root下,使用命令sudo usermod -G sudo user,将user加入sudo用户组 -``` - -**sudo免密** - -当有些操作只有root用户才能操作的时候,怎么办? -1.我们需要切换到root用户操作。 -2. 当前用户属于sudo组,可以使用sudo [command] -3. 使用sudo输入一次密码免密使用5分钟。还是太麻烦,可以配置当前用户免密执行sudo。 - -如何免密配置 -``` -vi /etc/sudoers.d/ - -# 添加内容 -# 如果要指定特定的命令不需要密码的话,就把ALL替换成命令路径,如下 -# NOPASSWD: /sbin/mount, (root) NOPASSWD: /bin/umount - ALL=(ALL) NOPASSWD:ALL -Defaults:shiyanlou !requiretty - -# 有时候,虽然用户设置免密了,但是还是需要输入密码,是group覆盖了,需要把group也设成免密。 -``` - -参考资料:[sudo免密](http://www.cnblogs.com/kungfupanda/p/4305049.html) - - - -### 3.2 权限管理 - -**更改文件所有者和所属组** - -``` -# [sudo] chown 用户组:用户 文件/文件夹,以下两种均可 -chown root:zabbix some.txt -chown root.zabbix some.txt - -# 只更改所属用户,[sudo] chowm 用户 文件/文件夹 -chown zabbix some.txt - -# 只更改所属用户组,[sudo] chown .用户组 文件/文件夹 -chown .root some.txt -``` - -**修改文件权限** - -文件权限有`读`、`写`、`执行`三种 -分别对应数字4,2,1,也就是2^2,2^1,2^0 - -如何修改文件权限 -``` -【第一种方法】 -chmod 777 文件 - -【第二种方法】:加减的方法 -g、o、u、a分别表示 group、others、user和all -+、-、= 分别表示增加、去掉和设置相应的权限。 - -举个例子 -比如一个文件权限是:-wr-wr-wr- -chmod go-wr 文件 - -然后文件权限就变成:-wr------- - -再设置回原来的 -chmod ugo=wr 文件 - -或者将user和group改为可执行 -chmod ug=wrx,o=wr 文件 -``` -**禁止修改、删除、移动文件** - -`chattr -i`和`chattr +i` - -``` -+ :在原有参数设定基础上,追加参数。 -- :在原有参数设定基础上,移除参数。 - -命令:chattr [ -RV ] [ -v version ] [ mode ] files - -A: 文件或目录的 atime (access time)不可被修改(modified), 可以有效预防例如手提电脑磁盘I/O错误的发生。 -S: 硬盘I/O同步选项,功能类似sync。 -a: 即append,设定该参数后,只能向文件中添加数据,而不能删除,多用于服务器日志文 件安全,只有root才能设定这个属性。 -c: 即compresse,设定文件是否经压缩后再存储。读取时需要经过自动解压操作。 -d: 即no dump,设定文件不能成为dump程序的备份目标。 -i: 设定文件不能被删除、改名、设定链接关系,同时不能写入或新增内容。i参数对于文件 系统的安全设置有很大帮助。 -j: 即journal,设定此参数使得当通过 mount参数:data=ordered 或者 data=writeback 挂 载的文件系统,文件在写入时会先被记录(在journal中)。如果filesystem被设定参数为 data=journal,则该参数自动失效。 -s: 保密性地删除文件或目录,即硬盘空间被全部收回。 -u: 与s相反,当设定为u时,数据内容其实还存在磁盘中,可以用于undeletion. -``` - -切换用户执行命令 - -``` -$ su - zabbix -s /bin/bash - -# 退出的话,使用exit -``` - -## 四、网络管理 - -### 4.1 iptables -命令格式 -``` -iptables [-t table] 命令 [chain] [rules] [-j target] - -【参数解释】 -table 表名:filter、nat、mangle、raw,后两者不常用 -命令 对链的操作命令 - -P或–policy 定义默认策略 - -L或–list 查看iptables规则列表 - -A或—append 在规则列表的最后增加1条规则 - -I或–insert 在指定的位置插入1条规则 - -D或–delete 从规则列表中删除1条规则 - -R或–replace 替换规则列表中的某条规则 - -F或–flush 删除表中所有规则 - -Z或–zero 将表中数据包计数器和流量计数器归零 -chain 链名:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTIN - -rules 规则,可以说是匹配规则。 - 分为 - 1. 【通用匹配】 - -s: 指定作为源地址匹配,必须是IP,取反,就加一个! - -d: 表示匹配目标地址 - -p: 用于匹配协议的(这里的协议通常有3种,TCP/UDP/ICMP) - -i eth0:从这块网卡流入的数据,流入一般用在INPUT和PREROUTING上 - -o eth0:从这块网卡流出的数据,流出一般在OUTPUT和POSTROUTING上 - - 2. 【扩展匹配】 - -p tcp: TCP协议的扩展。一般有三种扩展 - --dport: 指定目标端口,--dport 21或者 --dport 21-23 (此时表示21,22,23),不能表示非连续端口 - --sport: 指定源端口 - - --tcp-fiags: TCP的标志位(SYN,ACK,FIN,PSH,RST,URG) - 对于它,一般要跟两个参数: - 1.检查的标志位 - 2.必须为1的标志位 - --tcpflags syn,ack,fin,rst syn = --syn - 表示检查这4个位,这4个位中syn必须为1,其他的必须为0。所以这个意思就是用于检测三次握手的第一次包的。对于这种专门匹配第一包的SYN为1的包,还有一种简写方式,叫做--syn - - -p udp: UDP协议的扩展 - --dport - --sport - - -p icmp: icmp数据报文的扩展 - --icmp-type: - echo-request(请求回显), 一般用8 来表示 - echo-reply (响应的数据包) 一般用0来表示 -``` - -参考文章 -1. [ubuntu配置iptables](http://wiki.ubuntu.org.cn/IptablesHowTo) -2. [netfilter/iptables全攻略](http://www.linuxso.com/linuxpeixun/10332.html) -3. [iptables详解](http://blog.chinaunix.net/uid-26495963-id-3279216.html) - -### 4.2 端口相关 - -查询端口占用情况 - -``` -lsof -i:10051 -``` - -开放端口 - -``` -# 在filter表里添加规则 --A INPUT -m state --state NEW -m tcp -p tcp --dport 8500 -j ACCEPT - -# 重启iptables -$ service iptables restart - -# 监听端口 -$ nc -lp 8500 & - -# 检测是否开启完成 -$ netstat -tunl|grep 8500 -``` - -端口转发 - -``` -# 按开放端口配置好后才可转发。 -# 假设我们现在要将36.250.x.x的8500端口转发到192.168.2.55的80端口上 - -# filter表 --A INPUT -m state --state NEW -m tcp -p tcp --dport 8500 -j ACCEPT - -# nat表 --A PREROUTING -d 36.250.x.x -m tcp -p tcp --dport 8500 -j DNAT --to-destination 192.168.2.55:80 - -# 测试一下,在浏览器输入 36.250.x.x:8500 -# 就可以看到我们的内容了。 -``` - -### 4.3 路由相关 - -路由转发:snat - -``` - -# 1、开启转发 -# vim /etc/sysctl.conf,sysctl -p 该命令可以查看是否修改成功 -net.ipv4.ip_forwaed=1 - -# 或者可以执行下面这两条命令其中一条 -echo 1 > /proc/sys/net/ipv4/ip_forward -sysctl -w net.ipv4.ip_forward=1 - -# 2、在网关节点的 /etc/sysconfig/iptables 加上规则。 -*nat -:PREROUTING ACCEPT [4730:370403] -:INPUT ACCEPT [501:29436] -:OUTPUT ACCEPT [3563:214910] -:POSTROUTING ACCEPT [3564:214973] --A POSTROUTING -s 172.20.20.0/24 -o eth0 -j SNAT --to-source 58.xx.xx.xx -COMMIT - -# 重启iptables -systemctl restart iptables -``` - -### 4.4 ip 命令 - -``` -# ubuntu 临时修改网络,需要刷新一下 -ip addr flush dev ens4 -``` - - - -## 五、Shell命令 - -### 5.1 执行顺序控制 -``` -1、&& -方式:command1 && command2 -如果command1执行成功,则执行command2 - -2、|| -方式:command1 || command2 -如果command1执行失败,则执行command2 -``` -### 5.2 管道通信 - -``` -$ ps aux | grep mysqld - -# 将ps aux得到的结果传给grep命令 -``` -### 5.3 重定向 - -`>/dev/null 2>&1 &` - -分析下这个语句 -`command >/dev/null 2>&1 &` -执行command后的标准输出不在屏幕显示,而是直接丢入/dev/null 垃圾桶,如果有错误输出,则重定向到标准输出。最后&表示在后台运行。 - -### 5.4 其他常用 - -【nohup】 - -一般在一终端或一个SSH连接运行一个软件或服务,该软件或服务的生命周期受终端/SSH连接影响,关闭后就自动也停止。 -为了将程序放在后台运行,可以使用nohup命令 -``` -$ nohup 程序路径/程序名 & -``` - -【alias】 -给常用的长命令取别名,变成短的,提高效率 -``` -# 查看现有别名 -$ alias - -# 添加别名 -$ alias catgra='cat /var/lib/mysql/grastate.dat' - -# 取消别名状态 -# 比如我们的ll,系统默认给我们加了别名,ll='ls -l --color=auto',也就是加上颜色效果 -# 如果我们不要颜色效果,可以这样使用转义符,使用原生的命令 -$ \ll somedir -``` -这里转几条来自 「**良许Linux**」的总结,非常实用,你可以跟着设置一下 - -1、压缩包文件,特别是 tar 文件在 Linux 下使用非常广泛,但是 tar 命令的选项又非常多,也不好记住。所以我们可以将常用的几个选项定义为一个别名 **untar** ,这样我们需要解压 tar 文件时,直接 **untar filename** 即可。 - -``` -alias untar='tar -zxvf ' -``` - -2、我们下载一个很大的文件时,突然网络异常中断了,我们重新下载是不是很抓狂?别担心,我们的 wget 命令有个 -c 选项,支持断点下载,我们也可以将它设置为别名: - -``` -alias wget='wget -c ' -``` - -3、有时我们需要生成一个 20 个字符的随机数密码,我们可以使用 openssl 命令,但完整的命令又很长很不方便,我们可以设置别名: - -``` -alias getpass="openssl rand -base64 20" -``` - -4、下载一个文件之后,我们想要校验一下它的 checksum 值,可以将这个命令封装为一个别名 **sha** ,之后我们 **sha filename** 就可以校验文件的 checksum 值。 - -``` -alias sha='shasum -a 256 ' -``` - -5、正常情况下,ping 命令将无限次输出,但其实没多大意义。我们可以使用 -c 命令将其限制为 5 次输出,然后设置为别名 **ping** ,使用时,**ping url** 即可。 - -``` -alias ping='ping -c 5' -``` - -6、如果我们想随时随地启动一个 web 服务器,我们可以使用这个别名: - -``` -alias www='python -m SimpleHTTPServer 8000' -``` - -7、网速的测试在工作中也经常用到,但 Linux 没有自带命令可用,我们可以借助第三方工具 **speedtest-cli** 。这个工具可以直接从 Github 上下载,使用方法里面也有详细介绍。我们需要先使用 **speedtest-cli** 命令来选择离我们最近的服务器,然后设置如下别名: - -``` -alias speed='speedtest-cli --server 2406 --simple' -``` - -8、你的公网 IP 是多少?记性好的可以直接背下来,但如果你有 10 台上百台服务器呢?也可以背下来,然后参加最强大脑。其实有个命令可以直接查询,但那个命令太变态,不好记,果断设置为别名。 - -``` -alias ipe='curl ipinfo.io/ip' -``` - -9、如何知道自己的局域网 IP ?这个命令同样变态,果断设置别名。 - -``` -alias ipi='ipconfig getifaddr en0' -``` - -10、最后,清屏,我们可以使用 **ctrl + l** 快捷键,也可以将 **clear** 命令定义得更短,这样使用起来更直接,更粗暴。 - -``` -alias c='clear' -``` - -【column】 - -一个很好用的命令,经常用于管道符后,进行文本展示 -``` -$ mount -sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) -proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) -devtmpfs on /dev type devtmpfs (rw,nosuid,size=3996196k,nr_inodes=999049,mode=755) -securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime) - -# 使用后,t是按表格的形式展示,-s "@" 指定分割符 -$ mount | column -t -sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) -proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) -devtmpfs on /dev type devtmpfs (rw,nosuid,size=3996196k,nr_inodes=999049,mode=755) -securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime) -``` - - -## 六、业务相关 - - -查看虚机的CPU是否支持虚拟化 - -``` -egrep '(vmx|svm)' /proc/cpuinfo -# 如果有标红的vmx(Intel)、svm(AMD)说明就支持,virtualbox不支持(坑) - -# 或者直接查看 -vi /proc/cpuinfo -``` -查看模块是否加载 -``` -lsmod | grep kvm -``` -开启服务 -``` -# CentOS 6 -service xx start - -# CentOS 7 -systemctl start libvirtd -``` -开机自启动服务 -``` -CentOS 6 -chkconfig acpid on - -# CentOS 7 -systemctl enable libvirtd -``` -开启shh登录,修改端口等配置 -``` -vi /etc/ssh/sshd_config -------------------------------------- -Port 57891 -PasswordAuthentication yes -ClientAliveInterval 60 -ClientAliveCountMax 10 -``` -关闭防火墙 -``` -setenforce 0 -service firewalld stop -chkconfig firewalld off -``` -换yum源 -``` -yum install wget - -mv /etc/yum.repos.d/CentOS-Base.repo /root/ - -# 下载yum源,这里是CentOS6的,请下载对应版本 -wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo - -yum clean all -yum makecache -``` - -磁盘修复 - -```shell - xfs_repair /dev/vda2 -L -``` - - - -## 附:推荐阅读 - -- [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - - - ---- - -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +# 7.1 Linux 命令行的艺术 + +![](http://image.iswbm.com/20200602135014.png) + +--- + +## 一、目录/文件 + +### 1.1 目录文件日常操作 +``` +. -> 当前目录 +.. -> 上一级目录 +.file -> 隐藏文件 +.dir -> 文件夹 +``` +【ls】 查看指定目录文件 +`ls`命令是最常用的linux命令,要配合着选项使用。 + +``` +常用命令 + +ls -lh # 方便查看文件大小 +ls -AsSh +``` +【pwd】:查看当前目录 +``` +pwd等价于/bin/pwd -L,显示当前绝对路径,如果是链接,则显示链接路径 +pwd -P等价于/bin/pwd -P,显示实际路径,而非链接路径 + +如果文件夹被临时删除,pwd还是显示未删除文件夹的那个路径,这时候可以使用/bin/pwd,就会提示当前路径不存在 +``` +【cd】:切换目录 + +``` +cd - # 上一目录 +cd ~ # 家目录 +cd # 家目录 +cd .. # 上一目录 +cd !$ # 将上命令的参数做为cd 参数 +cd ~zabbix # 进入zabbix用户的主目录 +``` + +【touch】:新建文件 +``` +1. 创建新文件同时可以指定一些时间参数 +$ touch newfile + +2. 一次性创建多个文件 +$ touch {1..10}.txt +``` +可以对已有文件修改时间戳(ll显示的时间) +``` +【1】 +touch -d [[CC]YY]MMDD text +touch -t [CC[YY]MMDDhhmm[.SS] text +CC为年数中的前两位,即”世纪数”;YY为年数的后两位,即某世纪中的年数.如果不给出CC的值 +touch -d 20171004 text +touch -t 201710041330.30 text + +【2】 +-r:以另一文件为基准更新时间戳 +以file1的时间戳为基准,将file2的改成一样的 +touch -r file1 file2 + +【3】其他不常用参数 +-a 或--time=atime或--time=access或--time=use  只更改存取时间。 +-c 或--no-create  不建立任何文档。 +-f  此参数将忽略不予处理,仅负责解决BSD版本touch指令的兼容性问题。 +-m 或--time=mtime或--time=modify  只更改变动时间。 +``` +【mkdir】:新建文件夹 +``` +# 创建一个或多个的同级目录 +mkdir adir bdir + +# 递归创建目录 +# 当前目录下并没有cdir这个文件夹,如果使用单纯mkdir cdir/ddir会报错。 +# 应该使用 +mkdir -p cdir/ddir + +# 指定权限 +mkdir -m 777 dir_name + +# 显示创建信息(成功或失败) +mkdir -v dir_name + +# 使用!$快速进入新建文件夹 +mkdir ming +cd !$ +``` + +【rm | rmdir】 + +``` +# 只能删除空文件夹 +rmdir dirname + +# 删除当前文件夹下所有文件并不提示 +rm -f * + +# 递归删除dirname下所有文件 +rm -r dirname + +# 上面二者结合,递归删除dirname下所有文件并不提示 +rm -rf dirname +``` + +【cp】:复制文件或目录 +``` +# 递归复制old_dir目录下所有文件和文件夹到new_dir文件夹下 +cp -r old_dir new_dir +``` + + +> 注意:默认cp拷贝文件后会使用预设权限,即其他人没有更改的权限,需要使用-p或 +> 者-a文件所有的所有特性都一起复制过来 (拉取源站数据缓存,不更改文件最后修改 +> 时间) + +【mv】:移动或重命名 +``` +# 移动/tmp/test/sample.txt文件到当前目录下 +mv /tmp/test/sample.txt ./ +``` +【cat 】查看文件 + +**作用**:文本文件查看和连接工具,用于查看文本文件的内容。 +**命令格式**:cat file 经常和more、head、tail、less以及管道命令结合使用,如:cat file | more、cat file | head等。 + +三个功能 +``` +1.一次显示整个文件:cat filename +2.从键盘创建一个文件:cat > filename 创建文件,输入内容,Ctrl+d 结束 +3.将几个文件合并为一个文件:cat file1 file2 > file +``` + +命令参数 +``` +-A, --show-all 等价于 -vET +-b, --number-nonblank 对非空输出行编号 +-e 等价于 -vE +-E, --show-ends 在每行结束处显示 $ +-n, --number 对输出的所有行编号,由1开始对所有输出的行数编号 +-s, --squeeze-blank 有连续两行以上的空白行,就代换为一行的空白行 +-t 与 -vT 等价 +-T, --show-tabs 将跳格字符显示为 ^I +-u (被忽略) +-v, --show-nonprinting 使用 ^ 和 M- 引用,除了 LFD 和 TAB 之外 +``` +【tac】反向查看 +``` +cat是第一行到最后一行,tac是最后一行到第一行 +``` +【head | tail】 + +``` +# 显示前10行 +head xs.txt 等价于 cat xs.txt|head 等价于 cat xs.txt|head -n 10 + + +# 显示最后10行 +tail xs.txt 等价于 tail -n 10 xs.txt + + +# 显示除末尾10行外 +head -n -10 xs.txt +``` + +【more | less】 + +更具体命令可以参考:[Linux中more和less命令用法](http://www.cnblogs.com/aijianshi/p/5750911.html) +``` +【more】 +# 回车就往下一行显示,空白键就往下一页显示 +# 按 b 键就会往回一页显示,按 q 就会退出 +more xs.txt + +# 查找比较鸡肋,并从该处前两行开始显示输出 +more +/查找内容 xs.txt + +【less】 +# 和more几乎一样的功能,但是less更灵活(可以使用任何vim的移动命令,还有标记功能很使用),比如查找命令,直接像vim一样输入/即可 +less xs.txt + +ma : 使用 a 标记文本的当前位置 +'a : 导航到标记 a 处 + +F :实现和tail -f的功能,实时输出内容,tail +F xs.txt +``` + + + +**小练习** + +选取xs.txt的10-20行 + +``` +head -n 20 xs.txt|tail + +# 更快捷的方法 +sed -n '10,20p' xs.txt +``` + + + +### 1.2 文件处理 + +【wc】 + +统计文件信息 +``` +# 统计所有信息(行数、单词数、字符数) +$ wc /etc/passwd + +# 行数 +$ wc -l /etc/passwd + +# 单词数 # 中文无法统计 +$ wc -w /etc/passwd + +# 字节数 +$ wc -c /etc/passwd + +# 字符数 +$ wc -m /etc/passwd + +# 最长行字节数 +$ wc -L /etc/passwd +``` + +【sort】 + +``` +$ cat /etc/passwd | sort +$ cat /etc/passwd | sort -r + +# 以:为分隔符,对第三列排序,所得的结果,再通过cut以:为分隔符取第三列 +$ cat /etc/passwd | sort -t ":" -k 3 |cut -d ":" -f 3 +``` + +【uniq】 + +``` +# 显示每行重复频率 +uniq -c 文件名 + +# 只显示有重复的行 +uniq -d 文件名 +``` +### 1.3 文字处理 + +【tr】 + +`tr`可以删除或者去重某文本信息中的某些文字。还可以进行替换操作 +``` +# 删除hello,里面所有l,o字符 +$ echo 'hello' | tr -d 'lo' + +# 去重hello里的l +$ echo 'hello' | tr -s 'l' + +# hello里,l换成a,e换成b +$ echo 'hello' | tr 'le' 'ab' +``` + +【col】 + +将tab转换为等数量的空格,或者反转 +``` +-x tab转空格 +-h 空格转tab(默认) +``` + +``` +# 查看 /etc/protocols 中的不可见字符,可以看到很多 ^I ,这其实就是 Tab 转义成可见字符的符号 +$ cat -A /etc/protocols + +# 使用 col -x 将 /etc/protocols 中的 Tab 转换为空格,然后再使用 cat 查看,你发现 ^I 不见了 +$ cat /etc/protocols | col -x | cat -A +``` + +【sed】 + +文件处理工具。 +使得不需要打开文件就可以对文件进行操作(删除,替换,选取,新增)。以行为单位进行处理。 + +常用选项 + +``` +-n∶经过处理后的结果显示出来。不影响真实文件。 +-e:直接在指令列模式上进行 sed 的动作编辑;(没明白) +-f∶直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的sed 动作; +-i∶直接修改读取的档案内容,而不是由屏幕输出。 +``` +常用命令: +``` +a∶新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~ +c∶取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行! +d∶删除,因为是删除啊,所以 d 后面通常不接任何咚咚; +i∶插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行); +p∶列印,亦即将某个选择的资料印出。通常 p 会与参数 sed -n +s∶取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦! +``` + +删除某行 +``` + sed '1d' somefile #删除第一行 + sed '$d' somefile #删除最后一行 + sed '1,2d' somefile #删除第一行到第二行 + sed '2,$d' somefile #删除第二行到最后一行 + + # 以上,只是输出,源文件并不会替换,若要对源文件进行修改,需加 -i + sed -i '1,2d' somefile +``` +删除指定行的上一行或下一行 + +```shell +删除指定文件的上一行 +sed -i -e :a -e '$!N;s/.*n(.*directory)/1/;ta' -e 'P;D' server.xml +删除指定文件的下一行 +sed -i '/pattern="%/{n;d}' server.xml +``` + +显示某行 + +``` + sed -n '1p' somefile #显示第一行 + sed -n '$p' somefile #显示最后一行 + sed -n '1,2p' somefile #显示第一行到第二行 + sed -n '2,$p' somefile #显示第二行到最后一行 +``` +使用模式进行查询 +``` + sed -n '/ruby/p' somefile #查询包括关键字ruby所在所有行 + sed -n '/\$/p' somefile #查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含义 +``` +插入行(a 表示 append 追加,也可以使用 i,表示 insert,如果你要在第一行插入,就不得不使用 i 了) +``` +sed '1a drink tea' somefile #第一行后增加字符串"drink tea" +sed '1,3a drink tea' somefile #第一行到第三行后增加字符串"drink tea" +sed '1a drink tea\nor coffee' somefile #第一行后增加多行,使用换行符\n +``` +在某一行后面加入一行(示例为第四行) + +``` +sed -i 'N;4addpdf' a.txt +sed -i 'N;4ieepdf' a.txt +``` + + + +替换行 + +``` +sed '1c Hi' somefile #第一行代替为Hi +sed '1,2c Hi' somefile #第一行到第二行代替为Hi +``` +替换行中部分数据 +``` +sed 's/ruby/bird/g somefile' #替换ruby为bird,记住这个并不会更改源文件,只是输出 +sed 's/ruby//g' somefile #删除ruby +``` + +在某行的前一行或后一行添加内容,其中 `\` 可省略,但为了可读性,所以我一般不省略 + +```shell +# 匹配行前加 +sed -i '/2222222222/a\3333333333' test.txt + +# 匹配行前后 +sed -i '/2222222222/i\3333333333' test.txt +``` + + + +【注意】:以上对源文件都不做修改,若要修改,要加上`-i` + +替换换行符 + +```shell +# 将换行符换成逗号 +$ cat a.txt +1 +2 +3 +3 +$ sed ":a;N;s/\n/,/g;ta" a.txt +1,2,3,4 +``` + + + + +### 1.4 文件重构 +**cut** + +【显示每行的某位置的内容】 +``` +# 前五个(包含第五个) +$ cut /etc/passwd -c -5 +# 前五个之后的(包含第五个) +$ cut /etc/passwd -c 5- +# 第五个 +$ cut /etc/passwd -c 5 +# 2到5之间的(包含第五个) +$ cut /etc/passwd -c 2-5 +``` +【以指定的分隔符分隔,并返回某些列】 +``` +# 返回第一列和第六列 +$ cut /etc/passwd -d ":" -f 1,6 +``` +**awk** + +awk是一个强大的文本分析工具。 +简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片(相当于Excel的分列),切开的部分再进行各种分析处理。 + +``` +# 打印空白行,文件有几个空行,就输出几个空行 +awk '/^$/{print "This is a blank line"}' awk.txt + +# 打印全部列,引用变量 +awk '{print $0}' awk.txt + +# 打印前三列,引用变量 +awk '{print $1,$2,$3}' awk.txt + +# 指定间隔符为空格,获取第四列 +awk -F" " '{print $4}' awk.txt + +# 重组表格 +awk -F ':' '{print $1"\t"$7}' awk.txt + +# 表头和结尾,会先输出name,shell +awk -F ':' 'BEGIN {print "name,shell"} {print $1","$7} END {print "blue,/bin/nosh"}' awk.txt + +# 在awk中引用变量变量,在原理是拼接. +limit=30 +df -Th| grep "/dev/vd" | sed 's/%//g' |awk '{ if($6>'"$limit"') print $6}' +``` + + + +### 1.5 其他命令 +**nl** + +-b :指定行号指定的方式,主要有两种: +``` +-b a :表示不论是否为空行,也同样列出行号(类似 cat -n); +-b t :如果有空行,空的那一行不要列出行号(默认值); +``` +-n :列出行号表示的方法,主要有三种: +``` +-n ln :行号在萤幕的最左方显示; +-n rn :行号在自己栏位的最右方显示,且不加 0 ; +-n rz :行号在自己栏位的最右方显示,且加 0 ; +``` + + +``` +-w :行号栏位的占用的位数。 +nl -b a -n rz -w 3 text +``` + +### 1.6 文件查找 +**which:查询软件** + +在PATH变量指定的路径中,搜索某个系统命令(`可执行文件`)的位置,并且返回第一个搜索结果。 + +参数选项(基本不用) +``` +-n  指定文件名长度,指定的长度必须大于或等于所有文件中最长的文件名。 +-p  与-n参数相同,但此处的包括了文件的路径。 +-w  指定输出时栏位的宽度。 +-V  显示版本信息 +``` + +**grep:搜索神器** + +搜索并筛选显示结果。 +该命令经常配合管道命令来控制输出。 +以下 是常用的选项: +![](http://image.python-online.cn/17-9-20/47469030.jpg) + +【非常好用:不打开文件,直接搜索指定目录下文件内的内容】 +``` +$ vim a # wongbingming +$ vim b # wangbingming +$ grep -rnI "bingming" . # 当然这里也可以使用正则表达式 +./a:1:wongbingming +./b:1:wangbingming + +参数解释 +-r 递归遍历各个文件夹下的所有文件 +-n 显示在文件中的第几行查询到 +-I 忽略二进制文件 +``` + +**whreris:简单快速** + +定位可执行文件、源代码文件、帮助文件在文件系统中的位置。 +个搜索很快,因为它并没有从硬盘中依次查找,而是直接从数据库中查询。 +``` +$ whereis who +``` +参数选项 +``` +-b 定位可执行文件。 +-m 定位帮助文件。 +-s 定位源代码文件。 +-U 搜索默认路径下除可执行文件、源代码文件、帮助文件以外的其它文件。 +-B 指定搜索可执行文件的路径。 +-M 指定搜索帮助文件的路径。 +-S 指定搜索源代码文件的路径。 +``` +**locate:快而全** + +通过`/var/lib/mlocate/mlocate.db`数据库查找,不过这个数据库也不是实时更新的,系统会使用定时任务每天自动执行`updatedb`命令更新一次,所以有时候你刚添加的文件,它可能会找不到,需要手动执行一次 `updatedb`命令(在我们的环境中必须先执行一次该命令)。 +``` +$ updatedb +$ locate /etc/sh + +# 查找/etc/目录下所有以sh开头的文件 +``` +**find:小而细** + +[鸟哥的Linux私房菜-find](http://linux.vbird.org/linux_basic/0220filemanager.php#find) +[每天一个linux命令(19):find 命令概览](http://www.cnblogs.com/peida/archive/2012/11/13/2767374.html) +[每天一个linux命令(20):find命令之exec](http://www.cnblogs.com/peida/archive/2012/11/14/2769248.html) + +### 1.7 文件传输/下载 +**scp** + +实现不同机器之间传输数据(加密) +[scp详解](http://www.cnblogs.com/peida/archive/2013/03/15/2960802.html) + +**curl** + +Curl是一个命令行方式下传输数据的开源传输工具,支持多种协议包括:FTP,HTTP,HTTPS,IMAP,POP3,TELNET等。同样支持HTTP POST方法,PUT方法,FTP上传,cookie,用户名/密码认证,下载文件端点续传等,功能十分强大。 + +常用的,用于模拟浏览器请求。 +[curl详解](http://blog.csdn.net/zzzmmmkkk/article/details/38569057) + +**wget** + +测试速率 +``` +wget -S http://115.231.74.93:80/lvs.lxdns.net/test.rar && rm -rf test.rar* +``` + + + +### 1.8 文件压缩 +**zcat** + +查看压缩的文件内容 +``` +zcat file.gz +``` +Linux上的压缩格式比Windows上多很多,在 Windows 上最常见的不外乎这三种 `*.zip`,`*.rar`,`*.7z` 后缀的压缩文件。而在 Linux 上面常见的格式除了以上三种外,还有 `*.gz`,`*.xz`,`*.bz2`,`*.tar`,`*.tar.gz`,`*.tar.xz`,`*.tar.bz2`,对于常见的压缩格式,tar已经可以解决,所以这里只介绍tar。tar并不能压缩和解压7z,zip等其他文件 + +**tar** + +压缩示例 +``` +# test是当前目录下一个文件夹 +$ tar -czvphf test.tar.gz old_folder +``` +参数解释 +``` +-c 指明创建tar文件 +-z 指明生成gz文件 + +-v 可视输出,不加就静默压缩 + +-f 指明压缩后的文件名,必须-f后必须紧跟文件名,否则无效 +-p 当在其他主机还原时希望保留文件的属性 +-h 备份链接指向的源文件而不是链接本身 + +``` +解包示例 +``` +$ mkdir new_folder +$ tar -xzvf test.tar.gz -C new_folder +``` +参数解释 +``` +-x 解压命令 +-z 指明源文件是gz文件 +-f 指明压缩后的文件名,必须-f后必须紧跟文件名,否则无效 + +-v 可视化输出解包过程,一般不加,静默解包 + +-C 指明解压到哪个目录 +``` +其他压缩格式 +``` +*.tar.gz -z +*.tar.xz -J +*tar.bz2 -j + +``` +**gz** + +最简单的压缩格式 + +``` +# 压缩 +gzip somefile + +# 解压 +gzip -d somefile.gz +``` + +**7zip** + +``` +yun install p7zip + +# 解压 +7za x file.7z + +# 压缩 +# 先将要压缩的文件都放到~/test/ 下 +7za a file.7z ~/test/ +``` +**zip** + +``` +# 解压 +unzip some.file + +# 压缩 +zip -r some.zip file1 file2 dir1 dir2... + +# 如果你的目标zip包没有 . ,那么zip会自动加上后缀 .zip +zip stuff * + +# 打包包含隐藏文件的包 +zip stuff .* * + +# 打包 foo 文件夹的文件,但是不包含文件夹本身 +zip -j foo foo/* + +# 断点打包,在磁盘空间不足的使用可以使用 +zip -rm foo foo/tom +zip -rm foo foo/dick +zip -rm foo foo/harry + +# 分多个包,如果foo文件夹下有5G大小的文件,那么会分成两个2g的,一个1g的三个包,分别命名:split.z01,split.z02,split.zip +zip -s 2g -r split.zip foo +``` + +### 1.9 rpm包管理 + +``` +# 解压rpm包 +rpm2cpio xxx.rpm |cpio -div + +# 查看文件属于哪个rpm包 +rpm -qf /path/filename + +# 使用rpmrebuild重新生成rpm包 +# 使用rpmrebuild需要依赖rpmbuild:yum install -y rpmbuild +rpmrebuild xxx +``` + +### 1.10 ftp工具使用 + +```shell +$ ftp +ftp> help +``` + +![](http://image.python-online.cn/20190705182629.png) + + + +## 二、系统管理 + +### 2.0 环境变量 + +变量分为用户变量(env)和shell变量(set) +``` +# 添加shell变量 + +[root@host ~]# myuser=wangbm +[root@host ~]# echo $myuser +wangbm +[root@host ~]# env|grep myuser +[root@host ~]# set|grep myuser +myuser=wangbm + + +# 添加用户变量,会发现用户变量会覆盖shell变量 +[root@host ~]# export myuser=wangbingming +[root@host ~]# env|grep myuser +myuser=wangbingming +[root@host ~]# set|grep myuser +myuser=wangbingming + + +# 删除环境变量 +[root@host ~]# unset myuser +[root@host ~]# env|grep myuser +[root@host ~]# set|grep myuser +``` +一些系统的变量 +``` +# 这里生成的变量,对所有用户生效 +/etc/profile + +# 对特定user生效 +/root/.bash_profile +/home/user/.bashrc + +# 注意修改文件后,要手动source一下 +``` + +### 2.1 相关查询 + +#### 系统信息 + +``` +# 查询开机时间 +$ who -b + +# 查询系统内核 +$ uname -r + +# 查询是否安装某个rpm包 +$ rpm -qa|grep nova + +# service 文件目录 +$ /usr/lib/systemd/system # centos 7.x +$ /etc/init.d # centos 6.x + +# 查询开机自启列表 +$ systemctl list-unit-files +$ chkconfig --list [service_name] + 等级0表示:表示关机 + 等级1表示:单用户模式 + 等级2表示:无网络连接的多用户命令行模式 + 等级3表示:有网络连接的多用户命令行模式 + 等级4表示:不可用 + 等级5表示:带图形界面的多用户模式 + 等级6表示:重新启动 + +# 查看系统版本 +$ cat /etc/redhat-release + +# ------或者------ +$ yum install redhat-lsb -y +$ lsb_release -a + +# 进程树 +$ pstree -p + +# 查看系统运行多长时间 +$ uptime/w + +# 查看系统版本 +lsb_release -a + +# 查看CPU信息 +$ cat /proc/cpuinfo +$ numactk -H + + +# ubuntu 查看更新有哪些是安全更新 +$ apt-get -s --no-download dist-upgrade -V | grep "^Inst.*security.*$" | cut -d " " -f 2 + +# ubuntu 登陆时,显示的安全更新是如何来的,内容:/etc/update-motd.d/90-updates-available +/usr/lib/update-notifier/apt-check --human-readable + +``` + + + +#### 时间查询 + +输出操作系统的当前日期、时间和时区。 +``` +# -s参数用于修改当前的日期和时间 +date –s 2007-10-17 +date –s 18:05:00 + +用法:cd /CNCLog/cache/qsLogBackSrcFull/bkDir/`date +%Y-%m-%d` +``` + +### 2.2 系统分区 + +#### 分区介绍 +Linux的分区,不同于Windows,一定要区别对待,不然会搞不明白。 + +Linux的分区的过程经历以下几个步骤 +``` +1. 设备分区:对硬盘存储空间的划分 +2. 格式化:写入文件系统 +3. 挂载:将分区挂载到目录上,才能访问数据 +``` +关于硬件对应的设备文件名,可以参照下图 +![](http://image.python-online.cn/17-10-15/97911325.jpg) + +其中以硬盘为例来说明 +``` +硬盘可以分为三种 +hd : IDE硬盘接口(淘汰,接口最大传输100来M) +sd : SCSI硬盘接口(淘汰,接口最大传输200M),和SATA硬盘接口 + +现在都是SATA的硬盘接口 + +/dev/sda1 表示的是第一块(a)SATA硬盘的第一个分区(1) +/dev/sdb2 表示的是第二块(b)SATA硬盘的第二个分区(2) +``` +分区类型 +可以分为:`主分区` 、`扩展分区` 、`逻辑分区` +``` +【主分区】:最多只能有4个 (受硬盘结构限制,如果硬盘结构不变,将都被限制) + +【扩展分区】: + 1. 最多只能有一个 + 2. 主分区+扩展分区,一共只能有四个,可以少于 + 3. 扩展分区下面,不能存放数据,只能进行逻辑分区的划分 + +【逻辑分区】:挂载后就是一个目录下的空间,数量不受限制 +``` +格式化做了哪些事 +``` +【目的】 +1. 不是为了清空数据 +2. 主要是为了写入文件系统 + +【文件系统】 +Windows: +FAT16(每个分区大小最大不能超过2G),FAT32(单个分区大小最大16G,单个文件大小不能超过4G),NTFS + +Linux:ext2,ext3,ext4 + +->>>>> 越往后越先进 + +【写入文件系统做了啥】 +1. 分数据块 +把空间分成若干个等大小的数据块(block),每个大小4kb +如果我们有一个文件10kb,那么会占用3个数据块,实际大小就会是12kb + +你可以查看一个文件夹的大小,也都是4kb +ll -l + +2. 建立数据表 +一个文件被分成若干个的数据块,那么如果有用户访问的时候,就需要有一个表把这些数据块拼凑起来。 +这个数据表就记录了这个文件由哪些数据块组成。 +``` +分区说明 +``` +必须分区 +/ 根目录,最高级目录,不分配的话,所有的文件都没存储,软件没法运行 +/swap 交换分区,虚拟内存,4G以下,分2倍,4G以上,和真实内存一样即可 + +推荐分区 +/boot 启动分区,防止/ 分区写满,导致系统无法启动,不需要很大,200M足矣 + + +逻辑分区号:只能从5开始,即使3,4没有被使用 +``` + +#### 分区操作 +关于分区的操作可以参考这个: [分区操作](https://www.cnblogs.com/zishengY/p/7137671.html) + + +``` +## 查看分区表信息 +$ sudo fdisk -l +$ lsblk + +# 删除分区前,先确认有没有挂载点,有的话需要先umount卸载 +$ fdisk /dev/vdb 然后再按d,w +``` + +挂载分区 fstab + +``` +# 当fstab的根分区被注释后,所有的文件都是只读的。连fstab文件也是,无法取消注释 +mount -o remount,rw / +``` + +格化化分区 + +``` +mkfs.ext4 /dev/sdb1 +``` + + + +### 2.3 进程管理 + +**ps、kill、killall** + +参考文档:[ps 命令的十个简单用法](https://www.cnblogs.com/fakerbin/p/6513365.html) + +``` +# 查看当前所有进程 +ps -aux + +# 终止pid为1095的进程 +kill 1095 + +# 强制终止pid为1095的进程 +# 9是信号强度,强制杀死 +# 其他信号,-1 该信号让进程正常关闭,然后重新读取配置文件之后重启 +# -15 正常结束进程的信号,kill命令默认信号 +kill -9 1095 + +# 终止指定程序 +killall 程序名 + +# pkill +pkill -9 httpd 强制终止进程 +pkill -t -9 pts/1 强制杀死pts/1虚拟终端登入的进程 +``` + +**top** + +常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。 +[linux的top命令参数详解](http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316399.html) + +``` +top 实时得查看进程的状态,以及系统的一些信息(如 CPU、内存信息等),3s刷新一次 +ps 来静态查看当前的进程信息 +pstree 来查看当前活跃进程的树形结构。 +``` + +**pgrep** + +```shell +[root@wangbm web]# pgrep keepalived +21955 +21956 +21957 +[root@wangbm web]# ps -ef|grep keepalived +root 21955 1 0 21:27 ? 00:00:00 /usr/sbin/keepalived -D +root 21956 21955 0 21:27 ? 00:00:00 /usr/sbin/keepalived -D +root 21957 21955 0 21:27 ? 00:00:00 /usr/sbin/keepalived -D +root 22035 12159 0 21:33 pts/0 00:00:00 grep --color=auto keepalived +``` + +**清除僵尸进程** + +一个僵尸进程产生的过程是:父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中。 + +``` +ps -e -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9 +``` + + + +### 2.4 设备信息管理 + +**df** + +显示磁盘的相关信息 +``` +df -Th +``` + +**free** + +输出内存的使用情况,m,g分别是指定单位,默认是kb +``` +free -m +free -g +``` +total 表示总内存大小; +used和free分别表示被使用和空闲内存大小; +share指可被多个进程共同享有的内存; buffers和cached用来保留最近访问的文件和数据,当其他进程需要更多的内存时,这些内容可以被缩减; Free命令还可以输出交换空间的相关信息。 + +**ifconfig** + +显示或设置网络设备 + + + +**last** + +列出目前与过去登入系统的用户相关信息。一般可用来查看系统重启记录 + + + +**history** + +屏幕输出当前用户在命令行模式下执行的最后(1000个)命令 + + + +**passwd** + +这个用于修改密码 + +非交互式修改密码 + +``` +echo 'root12#$'| passwd --stdin root +``` + + + +**reboot/shutdown** + +重启/关机 +``` +reboot/shutdown -r now + +# reboot [-n][-w][d][-i] 重新启动计算机,使用权限是系统管理员 +-n 重启前不将记录写回硬盘 +-w 并不是真的重启,只是把记录写道/var/log/wtmp文件中 +-d 不把记录写入/var/log/wtmp文件中 +-i 重启谦先把所有与网络相关的装备停止 +``` + +**rpm** + +``` +# 安装rpm包 +rpm -ivh monitor-system-1.2-1.i386.rpm + +# 更新rpm包 +rpm -U/Fvh (F只更新已存在的文件) + +# 查询包中的文件 +rpm -ql monitor-system + +# 查询文件所属的包 +rpm -qf /usr/local/squid/etc/squid.conf(绝对路径/到目录下查找) + +# 查询所有包 +rpm –qa | grep squid + +# 卸载某个rpm包 +rpm –e monitor-system +``` +**开启80端口** + +``` +vi /etc/sysconfig/iptables + +# 在22端口下面添加一行 +-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT + +# 重启防火墙 +/bin/systemctl restart iptables.service +``` +**修改时区** + +``` +# 由EDT(美国)改成CST(中国) + +$ mv /etc/localtime /etc/localtime.bak +$ ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +``` + +查看机器连接的哪个交换机 + +``` +$ service lldpd restart +$ lldpcli show nei sum # 查看JS_HuaiAnDXXZ4_166.3 +------------------------------------------------------------------------------- +LLDP neighbors: +------------------------------------------------------------------------------- +Interface: eth1, via: LLDP + Chassis: + ChassisID: mac 00:6b:8e:01:1a:07 + SysName: JS_HuaiAnDXXZ4_165.132_IPMI + Port: + PortID: local 39 + PortDescr: Ethernet1/0/39 + TTL: 120 +------------------------------------------------------------------------------- +Interface: eth0, via: LLDP + Chassis: + ChassisID: mac 58:69:6c:62:7b:c2 + SysName: JS_HuaiAnDXXZ4_166.3 + Port: + PortID: ifname TenGigabitEthernet 0/41 + PortDescr: TenGigabitEthernet 0/41 + TTL: 121 +------------------------------------------------------------------------------- +``` + + + +### 2.5 磁盘管理 + +**df** + +显示指定磁盘文件的可用空间。 +这里要理解挂载的概念。 + +所有的设备(磁盘/等其他介质)都需要挂载在一个目录,Linux才能访问数据。 + +具体可以参考这篇文章:[df 命令](http://www.cnblogs.com/peida/archive/2012/12/07/2806483.html) + +**常用命令** + +``` +$ df -Th +$ df -lh + + +$ df -h 以1024来换算 +$ df -H 以1000来换算 + +$ df -t ext3 指定设备类型 +$ df -T 文件系统的类型 +$ df -i 查看inode的使用情况 +``` +关于inode可以查看:[inode的理解](https://www.cnblogs.com/itech/archive/2012/05/15/2502284.html) + +**du** + +查看目录的容量 +``` +$ du -h -d 0 ~ + +# 参数解释 +-h 以人能看得懂的方式显示 +-d 扫描的目录层级,0表示只有一个指定目录,1表示次级目录 + +~ 是家目录,这里可以选你指定的目录 + +$ du -s +# 查看当前所处目录总容量大小,单位是k,不可跟-d + +$ du -a +# 查看当前所处目录及所有子目录的所有文件,单位是k,建议不用 +``` +查看文件的大小 +``` +$ du -h file.txt +``` + +**dd** + +`dd`命令用于转换和复制文件,不过它的复制不同于`cp`。之前提到过关于 Linux的很重要的一点,一切即文件,在 Linux上,硬件的设备驱动(如硬盘)和特殊设备文件(如`/dev/zero`和`/dev/random`)都像普通文件一样,只要在各自的驱动程序中实现了对应的功能,`dd`也可以读取自和/或写入到这些文件。这样,`dd`也可以用在备份硬件的引导扇区、获取一定数量的随机数据或者空数据等任务中。dd程序也可以在复制时处理数据,例如转换字节序、或在 `ASCII` 与 `EBCDIC`编码间互换。 + + +这里有篇文章讲得很好:[Linux-dd命令详解](http://www.cnblogs.com/dkblog/archive/2009/09/18/1980715.html) + +补充一点 +``` +# 输出到文件 +$ dd of=test bs=10 count=1 # 或者 dd if=/dev/stdin of=test bs=10 count=1 + +# 输出到标准输出 +$ dd if=/dev/stdin of=/dev/stdout bs=10 count=1 +# 注 +在打完了这个命令后,继续在终端打字,作为你的输入 +``` +前面说到dd在拷贝的同时还可以实现数据转换,那下面就举一个简单的例子:将输出的英文字符转换为大写再写入文件 +``` +dd if=/dev/stdin of=test bs=10 count=1 conv=ucase +``` + + +#### 制作虚拟镜像并挂载 +``` +# 制作虚拟镜像 +$ dd if=/dev/zero of=virtual.img bs=1M count=256 +$ mkfs.ext4 virtual.img + +# 使用一个循环设备(/dev/loop)连接虚拟镜像文件 +$ sudo losetup /dev/loop0 virtual.img + +# 挂载前查看下当前已挂载的设备 +$ sudo mount + +# 挂载 +$ sudo mkdir /mnt/point +$ sudo mount /dev/loop0 /mnt/point + +# 上面连接虚拟镜像文件和挂载设备两条命令可以合并成一条,挂载类型可以省略,会自动识别 +$ sudo mount -o loop [-t ext4] /mnt/point + +# 再次查看挂载的设备,就可以发现我们新挂载的 +$ sudo mount + + +# 卸载:一定要加-fl。不然无法卸载 +$ sudo umount -fl /mnt/point + +# 查看所有与文件相关联的 loop 设备 +$ losetup -a +# 卸载 +$ losetup -d /dev/loop0 +``` + +使用dd拷贝磁盘可以,但是如果想要快速生成大文件,速度就相当慢了,可以试试fallocate命令 + +```shell +fallocate -l 100G test_file +``` + +### 2.6 任务管理 + +#### 一次性任务(at) +##### 创建定时任务 +``` +$ sudo apt-get install at +$ at now+5 minutes +at>/bin/ls +at> # 就是Ctrl+d +job 2 at Sum Oct 15 14:58:00 2017 # 结束的时候,告知执行时间 +``` +##### 管理定时任务 +``` +$ atq # 可以查看当前还有那些定时任务,会显示任务号 + +# 查看具体任务代码 +$ at -c + +# 删除任务 +$ atrm +``` +#### 例行性任务(crontab) +参考以下文章 +1. [每天一个linux命令(50):crontab命令](http://www.cnblogs.com/peida/archive/2013/01/08/2850483.html) +2. [鸟哥的私房菜](http://linux.vbird.org/linux_basic/0430cron.php) + +做个示例 +``` +# 检查crond服务是否启动 +$ ps aux|grep crond + +# 创建任务 +$ crontab -e + +# 输入1 回车 +# 任务是:每天凌晨3点备份日志到/home/temp/目录,文件名为日期 +# 跳到文件尾部输入任务:* 3 * * * cp alternatives.lob /home/temp/$(date \%Y-\%m-\%d)。保存退出 +# 注意%需要加\来转义,不然会被当成换行使用 + +# 查看任务 +$ crontab -l + +# 删除任务(当前用户),当然也可以指定用户 +$ crontab -r [-u user] + +``` + +除了以上,例行性任务还有可能在 /etc/cron.daily/ ,/etc/cron.hourly/, /etc/cron.monthly/, /etc/cron.weekly/ 下面。 + +如果messages 不能正常回滚,可以考虑加个参数 `-f` + +``` +/usr/sbin/logrotate -f /etc/logrotate.conf +``` + +如果还不行,检查一下 `/etc/logrotate.d/syslog` + +``` +chmod 644 /etc/logrotate.d/syslog +``` + +## 三、用户/权限管理 + +### 3.1 用户管理 + +**创建用户** + +``` +【useradd和adduser区别】 +useradd:只创建用户,创建完了用 passwd lilei去设置新用户的密码。更像一种命令。 + +adduser:会创建用户,创建目录,创建密码(提示你设置),做这一系列的操作。更像是一个程序,执行确认一系列操作。 +``` + +**删除用户** + +``` +sudo deluser lilei --remove-home +``` + +**切换用户** + +``` +su [user] # 切换到其他用户,环境变量不变,还是当前路径,如果不指定user,就切换到root + +su - [user] # 切换到其他用户,环境变量也切换,路径变为user家目录,如果不指定username,就切换到root,相当于使用user进行登录 +``` + +Ubuntu切换用户 +``` +# 切换到root +$ sudo su + +# 切换到普通用户 +$ su user +``` +CentOS + +``` +$ su +$ su root +$ su - +$ su -root + +$ su user +$ su -user +``` +**用户组** + +``` +1. 每个用户都至少属于一个用户组,创建的时候如果不指定,就和当前用户的组一样(root用户组除外)。 +2. 一个用户可以属于多个用户组。 +``` + +``` +# 查看所属用户组 +$ groups [user] +``` + +**把用户加入到用户组** + +```shell +# 把 zabbix 加入到 root 用户组 +usermod -G root zabbix +``` + +另外还有其他方法,可查阅:[在 Linux 中把用户添加到组的四个方法](https://linux.cn/article-10768-1.html) + +**UID/GID/组ID** + +``` +# 显示相关信息,如要查看root用户的信息 +id root + +# 所有文件都保存在/etc/)asswd +vi /etc/passwd +``` + +管理用户:[查看用户的UID和GID](http://blog.csdn.net/jackailson/article/details/50993427) +管理密码:[Linux下/etc/shadow文件](http://blog.csdn.net/u011641885/article/details/46681697) + + + +**添加sudo用户组** + +``` +【第一种方法】 +在root下,visudo或者vi /etc/sudoers,找到root ALL=(ALL)的下一行添加一行,user ALL=(ALL),user是对应的用户名 + +【第二种方法】 +在root下,使用命令sudo usermod -G sudo user,将user加入sudo用户组 +``` + +**sudo免密** + +当有些操作只有root用户才能操作的时候,怎么办? +1.我们需要切换到root用户操作。 +2. 当前用户属于sudo组,可以使用sudo [command] +3. 使用sudo输入一次密码免密使用5分钟。还是太麻烦,可以配置当前用户免密执行sudo。 + +如何免密配置 +``` +vi /etc/sudoers.d/ + +# 添加内容 +# 如果要指定特定的命令不需要密码的话,就把ALL替换成命令路径,如下 +# NOPASSWD: /sbin/mount, (root) NOPASSWD: /bin/umount + ALL=(ALL) NOPASSWD:ALL +Defaults:shiyanlou !requiretty + +# 有时候,虽然用户设置免密了,但是还是需要输入密码,是group覆盖了,需要把group也设成免密。 +``` + +参考资料:[sudo免密](http://www.cnblogs.com/kungfupanda/p/4305049.html) + + + +### 3.2 权限管理 + +**更改文件所有者和所属组** + +``` +# [sudo] chown 用户组:用户 文件/文件夹,以下两种均可 +chown root:zabbix some.txt +chown root.zabbix some.txt + +# 只更改所属用户,[sudo] chowm 用户 文件/文件夹 +chown zabbix some.txt + +# 只更改所属用户组,[sudo] chown .用户组 文件/文件夹 +chown .root some.txt +``` + +**修改文件权限** + +文件权限有`读`、`写`、`执行`三种 +分别对应数字4,2,1,也就是2^2,2^1,2^0 + +如何修改文件权限 +``` +【第一种方法】 +chmod 777 文件 + +【第二种方法】:加减的方法 +g、o、u、a分别表示 group、others、user和all ++、-、= 分别表示增加、去掉和设置相应的权限。 + +举个例子 +比如一个文件权限是:-wr-wr-wr- +chmod go-wr 文件 + +然后文件权限就变成:-wr------- + +再设置回原来的 +chmod ugo=wr 文件 + +或者将user和group改为可执行 +chmod ug=wrx,o=wr 文件 +``` +**禁止修改、删除、移动文件** + +`chattr -i`和`chattr +i` + +``` ++ :在原有参数设定基础上,追加参数。 +- :在原有参数设定基础上,移除参数。 + +命令:chattr [ -RV ] [ -v version ] [ mode ] files + +A: 文件或目录的 atime (access time)不可被修改(modified), 可以有效预防例如手提电脑磁盘I/O错误的发生。 +S: 硬盘I/O同步选项,功能类似sync。 +a: 即append,设定该参数后,只能向文件中添加数据,而不能删除,多用于服务器日志文 件安全,只有root才能设定这个属性。 +c: 即compresse,设定文件是否经压缩后再存储。读取时需要经过自动解压操作。 +d: 即no dump,设定文件不能成为dump程序的备份目标。 +i: 设定文件不能被删除、改名、设定链接关系,同时不能写入或新增内容。i参数对于文件 系统的安全设置有很大帮助。 +j: 即journal,设定此参数使得当通过 mount参数:data=ordered 或者 data=writeback 挂 载的文件系统,文件在写入时会先被记录(在journal中)。如果filesystem被设定参数为 data=journal,则该参数自动失效。 +s: 保密性地删除文件或目录,即硬盘空间被全部收回。 +u: 与s相反,当设定为u时,数据内容其实还存在磁盘中,可以用于undeletion. +``` + +切换用户执行命令 + +``` +$ su - zabbix -s /bin/bash + +# 退出的话,使用exit +``` + +## 四、网络管理 + +### 4.1 iptables +命令格式 +``` +iptables [-t table] 命令 [chain] [rules] [-j target] + +【参数解释】 +table 表名:filter、nat、mangle、raw,后两者不常用 +命令 对链的操作命令 + -P或–policy 定义默认策略 + -L或–list 查看iptables规则列表 + -A或—append 在规则列表的最后增加1条规则 + -I或–insert 在指定的位置插入1条规则 + -D或–delete 从规则列表中删除1条规则 + -R或–replace 替换规则列表中的某条规则 + -F或–flush 删除表中所有规则 + -Z或–zero 将表中数据包计数器和流量计数器归零 +chain 链名:PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTIN + +rules 规则,可以说是匹配规则。 + 分为 + 1. 【通用匹配】 + -s: 指定作为源地址匹配,必须是IP,取反,就加一个! + -d: 表示匹配目标地址 + -p: 用于匹配协议的(这里的协议通常有3种,TCP/UDP/ICMP) + -i eth0:从这块网卡流入的数据,流入一般用在INPUT和PREROUTING上 + -o eth0:从这块网卡流出的数据,流出一般在OUTPUT和POSTROUTING上 + + 2. 【扩展匹配】 + -p tcp: TCP协议的扩展。一般有三种扩展 + --dport: 指定目标端口,--dport 21或者 --dport 21-23 (此时表示21,22,23),不能表示非连续端口 + --sport: 指定源端口 + + --tcp-fiags: TCP的标志位(SYN,ACK,FIN,PSH,RST,URG) + 对于它,一般要跟两个参数: + 1.检查的标志位 + 2.必须为1的标志位 + --tcpflags syn,ack,fin,rst syn = --syn + 表示检查这4个位,这4个位中syn必须为1,其他的必须为0。所以这个意思就是用于检测三次握手的第一次包的。对于这种专门匹配第一包的SYN为1的包,还有一种简写方式,叫做--syn + + -p udp: UDP协议的扩展 + --dport + --sport + + -p icmp: icmp数据报文的扩展 + --icmp-type: + echo-request(请求回显), 一般用8 来表示 + echo-reply (响应的数据包) 一般用0来表示 +``` + +参考文章 +1. [ubuntu配置iptables](http://wiki.ubuntu.org.cn/IptablesHowTo) +2. [netfilter/iptables全攻略](http://www.linuxso.com/linuxpeixun/10332.html) +3. [iptables详解](http://blog.chinaunix.net/uid-26495963-id-3279216.html) + +### 4.2 端口相关 + +查询端口占用情况 + +``` +lsof -i:10051 +``` + +开放端口 + +``` +# 在filter表里添加规则 +-A INPUT -m state --state NEW -m tcp -p tcp --dport 8500 -j ACCEPT + +# 重启iptables +$ service iptables restart + +# 监听端口 +$ nc -lp 8500 & + +# 检测是否开启完成 +$ netstat -tunl|grep 8500 +``` + +端口转发 + +``` +# 按开放端口配置好后才可转发。 +# 假设我们现在要将36.250.x.x的8500端口转发到192.168.2.55的80端口上 + +# filter表 +-A INPUT -m state --state NEW -m tcp -p tcp --dport 8500 -j ACCEPT + +# nat表 +-A PREROUTING -d 36.250.x.x -m tcp -p tcp --dport 8500 -j DNAT --to-destination 192.168.2.55:80 + +# 测试一下,在浏览器输入 36.250.x.x:8500 +# 就可以看到我们的内容了。 +``` + +### 4.3 路由相关 + +路由转发:snat + +``` + +# 1、开启转发 +# vim /etc/sysctl.conf,sysctl -p 该命令可以查看是否修改成功 +net.ipv4.ip_forwaed=1 + +# 或者可以执行下面这两条命令其中一条 +echo 1 > /proc/sys/net/ipv4/ip_forward +sysctl -w net.ipv4.ip_forward=1 + +# 2、在网关节点的 /etc/sysconfig/iptables 加上规则。 +*nat +:PREROUTING ACCEPT [4730:370403] +:INPUT ACCEPT [501:29436] +:OUTPUT ACCEPT [3563:214910] +:POSTROUTING ACCEPT [3564:214973] +-A POSTROUTING -s 172.20.20.0/24 -o eth0 -j SNAT --to-source 58.xx.xx.xx +COMMIT + +# 重启iptables +systemctl restart iptables +``` + +### 4.4 ip 命令 + +``` +# ubuntu 临时修改网络,需要刷新一下 +ip addr flush dev ens4 +``` + + + +## 五、Shell命令 + +### 5.1 执行顺序控制 +``` +1、&& +方式:command1 && command2 +如果command1执行成功,则执行command2 + +2、|| +方式:command1 || command2 +如果command1执行失败,则执行command2 +``` +### 5.2 管道通信 + +``` +$ ps aux | grep mysqld + +# 将ps aux得到的结果传给grep命令 +``` +### 5.3 重定向 + +`>/dev/null 2>&1 &` + +分析下这个语句 +`command >/dev/null 2>&1 &` +执行command后的标准输出不在屏幕显示,而是直接丢入/dev/null 垃圾桶,如果有错误输出,则重定向到标准输出。最后&表示在后台运行。 + +### 5.4 其他常用 + +【nohup】 + +一般在一终端或一个SSH连接运行一个软件或服务,该软件或服务的生命周期受终端/SSH连接影响,关闭后就自动也停止。 +为了将程序放在后台运行,可以使用nohup命令 +``` +$ nohup 程序路径/程序名 & +``` + +【alias】 +给常用的长命令取别名,变成短的,提高效率 +``` +# 查看现有别名 +$ alias + +# 添加别名 +$ alias catgra='cat /var/lib/mysql/grastate.dat' + +# 取消别名状态 +# 比如我们的ll,系统默认给我们加了别名,ll='ls -l --color=auto',也就是加上颜色效果 +# 如果我们不要颜色效果,可以这样使用转义符,使用原生的命令 +$ \ll somedir +``` +这里转几条来自 「**良许Linux**」的总结,非常实用,你可以跟着设置一下 + +1、压缩包文件,特别是 tar 文件在 Linux 下使用非常广泛,但是 tar 命令的选项又非常多,也不好记住。所以我们可以将常用的几个选项定义为一个别名 **untar** ,这样我们需要解压 tar 文件时,直接 **untar filename** 即可。 + +``` +alias untar='tar -zxvf ' +``` + +2、我们下载一个很大的文件时,突然网络异常中断了,我们重新下载是不是很抓狂?别担心,我们的 wget 命令有个 -c 选项,支持断点下载,我们也可以将它设置为别名: + +``` +alias wget='wget -c ' +``` + +3、有时我们需要生成一个 20 个字符的随机数密码,我们可以使用 openssl 命令,但完整的命令又很长很不方便,我们可以设置别名: + +``` +alias getpass="openssl rand -base64 20" +``` + +4、下载一个文件之后,我们想要校验一下它的 checksum 值,可以将这个命令封装为一个别名 **sha** ,之后我们 **sha filename** 就可以校验文件的 checksum 值。 + +``` +alias sha='shasum -a 256 ' +``` + +5、正常情况下,ping 命令将无限次输出,但其实没多大意义。我们可以使用 -c 命令将其限制为 5 次输出,然后设置为别名 **ping** ,使用时,**ping url** 即可。 + +``` +alias ping='ping -c 5' +``` + +6、如果我们想随时随地启动一个 web 服务器,我们可以使用这个别名: + +``` +alias www='python -m SimpleHTTPServer 8000' +``` + +7、网速的测试在工作中也经常用到,但 Linux 没有自带命令可用,我们可以借助第三方工具 **speedtest-cli** 。这个工具可以直接从 Github 上下载,使用方法里面也有详细介绍。我们需要先使用 **speedtest-cli** 命令来选择离我们最近的服务器,然后设置如下别名: + +``` +alias speed='speedtest-cli --server 2406 --simple' +``` + +8、你的公网 IP 是多少?记性好的可以直接背下来,但如果你有 10 台上百台服务器呢?也可以背下来,然后参加最强大脑。其实有个命令可以直接查询,但那个命令太变态,不好记,果断设置为别名。 + +``` +alias ipe='curl ipinfo.io/ip' +``` + +9、如何知道自己的局域网 IP ?这个命令同样变态,果断设置别名。 + +``` +alias ipi='ipconfig getifaddr en0' +``` + +10、最后,清屏,我们可以使用 **ctrl + l** 快捷键,也可以将 **clear** 命令定义得更短,这样使用起来更直接,更粗暴。 + +``` +alias c='clear' +``` + +【column】 + +一个很好用的命令,经常用于管道符后,进行文本展示 +``` +$ mount +sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) +proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) +devtmpfs on /dev type devtmpfs (rw,nosuid,size=3996196k,nr_inodes=999049,mode=755) +securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime) + +# 使用后,t是按表格的形式展示,-s "@" 指定分割符 +$ mount | column -t +sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) +proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) +devtmpfs on /dev type devtmpfs (rw,nosuid,size=3996196k,nr_inodes=999049,mode=755) +securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime) +``` + + +## 六、业务相关 + + +查看虚机的CPU是否支持虚拟化 + +``` +egrep '(vmx|svm)' /proc/cpuinfo +# 如果有标红的vmx(Intel)、svm(AMD)说明就支持,virtualbox不支持(坑) + +# 或者直接查看 +vi /proc/cpuinfo +``` +查看模块是否加载 +``` +lsmod | grep kvm +``` +开启服务 +``` +# CentOS 6 +service xx start + +# CentOS 7 +systemctl start libvirtd +``` +开机自启动服务 +``` +CentOS 6 +chkconfig acpid on + +# CentOS 7 +systemctl enable libvirtd +``` +开启shh登录,修改端口等配置 +``` +vi /etc/ssh/sshd_config +------------------------------------- +Port 57891 +PasswordAuthentication yes +ClientAliveInterval 60 +ClientAliveCountMax 10 +``` +关闭防火墙 +``` +setenforce 0 +service firewalld stop +chkconfig firewalld off +``` +换yum源 +``` +yum install wget + +mv /etc/yum.repos.d/CentOS-Base.repo /root/ + +# 下载yum源,这里是CentOS6的,请下载对应版本 +wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo + +yum clean all +yum makecache +``` + +磁盘修复 + +```shell + xfs_repair /dev/vda2 -L +``` + + + +## 附:推荐阅读 + +- [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) + + + +--- + +![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index 0e49392..58e5f0e 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -1,6 +1,8 @@ 7.1 Linux 命令行的艺术 ====================== +|image0| + -------------- 一、目录/文件 @@ -519,7 +521,7 @@ awk是一个强大的文本分析工具。 **grep:搜索神器** 搜索并筛选显示结果。 该命令经常配合管道命令来控制输出。 以下 -是常用的选项: |image0| +是常用的选项: |image1| 【非常好用:不打开文件,直接搜索指定目录下文件内的内容】 @@ -740,7 +742,7 @@ Linux上的压缩格式比Windows上多很多,在 Windows 上最常见的不 $ ftp ftp> help -|image1| +|image2| 二、系统管理 ------------ @@ -876,7 +878,7 @@ Linux的分区的过程经历以下几个步骤 2. 格式化:写入文件系统 3. 挂载:将分区挂载到目录上,才能访问数据 -关于硬件对应的设备文件名,可以参照下图 |image2| +关于硬件对应的设备文件名,可以参照下图 |image3| 其中以硬盘为例来说明 @@ -1940,7 +1942,8 @@ url** 即可。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/17-9-20/47469030.jpg -.. |image1| image:: http://image.python-online.cn/20190705182629.png -.. |image2| image:: http://image.python-online.cn/17-10-15/97911325.jpg +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/17-9-20/47469030.jpg +.. |image2| image:: http://image.python-online.cn/20190705182629.png +.. |image3| image:: http://image.python-online.cn/17-10-15/97911325.jpg diff --git a/source/c07/c07_02.md b/source/c07/c07_02.md index 40e7226..aab879d 100644 --- a/source/c07/c07_02.md +++ b/source/c07/c07_02.md @@ -1,5 +1,7 @@ # 7.2 Zabbix 监控部署文档 +![](http://image.iswbm.com/20200602135014.png) + --- ## 7.2.1 Zabbix架构图 diff --git a/source/c07/c07_02.rst b/source/c07/c07_02.rst index a79bfc4..a37dddd 100755 --- a/source/c07/c07_02.rst +++ b/source/c07/c07_02.rst @@ -1,6 +1,8 @@ 7.2 Zabbix 监控部署文档 ======================= +|image0| + -------------- 7.2.1 Zabbix架构图 @@ -10,12 +12,12 @@ zabbix 的使用架构主要有两种,如果你的监控环境比较单一,可以使用简单的\ ``Server`` -> ``Agent``\ ,由zabbix-agent 直接上报给 zabbix-server。 -|image0| +|image1| 如果监控的环境比较复杂,就比如我们的线上的生产环境是分布式集群,最好使用的 ``Server``-> ``Proxy`` -> ``Agent`` -|image1| +|image2| Zabbix Proxy 可以代替 Zabbix Server 检索客户端的数据,然后把数据汇报给 Zabbix Server, 并且在一定程度上分担了 Zabbix Server 的压力。Zabbix @@ -234,7 +236,7 @@ Zabbix Proxy 是一个数据收集器,它不计算触发器、不处理事件 在使用 Proxy 的时候,需要在 Server 端的web 界面上,先注册一下。 -|image2| +|image3| 7.2.2.3 部署 Agent 端 ~~~~~~~~~~~~~~~~~~~~~ @@ -282,26 +284,26 @@ Agent 就会发送心跳到 Proxy ,再转到Zabbix Server上,Server 按照如下图点击,Event source注意选择\ ``Auto registration`` -|image3| - |image4| +|image5| + 7.2.3.2 配置监控项 ~~~~~~~~~~~~~~~~~~ 一般情况下,我们的监控项,不会独立存在,而是依托于模板而存在。所以我们在创建监控项前,要首先创建一个模板。 -|image5| +|image6| 创建完模板后,点进模板的 items 按钮,正常情况下,这里会有很多监控项,但由于我们还没有创建,所以现在一个也没有。现在你可以自己点右上角(\ ``Create Item``\ )自己创建一个,创建界面是这样的 -|image6| +|image7| 这里说一下,zabbix 自带有许多的模板,其实对于一些党规的监控项(说白了,就是zabbix给我们造好了轮子),这些模板已经足够了。这边只截了一小部分。 -|image7| +|image8| 如果以上这些不能满足你的需要,也没有关系,你也可以自己写脚本获取监控数据。 @@ -318,7 +320,7 @@ Agent 就会发送心跳到 Proxy ,再转到Zabbix Server上,Server 那我在 web 界面上配置 监控项,就可以这样写 -|image8| +|image9| openstack-nova-api 是一个服务名,它将作为一个参数,传递给\ ``isActive.sh`` 这个脚本。 @@ -337,11 +339,11 @@ Proxy,然后由Proxy上报Server。 你可以通过点击下图的操作,查看最近上报的数据(我这里选择value直接查看值,你可以选择Graph,按图表的形式查看) -|image9| +|image10| 数据是这样的。 -|image10| +|image11| 7.2.3.4 配置动作 ~~~~~~~~~~~~~~~~ @@ -349,11 +351,11 @@ Proxy,然后由Proxy上报Server。 假如,我们要监控当 CPU 使用率超过90% 就发个通知邮件,那我们就要新增一个发邮件的动作。 -|image11| +|image12| 然后点击 ``Operations``\ , 添加触发的动作类型,比如发邮件 -|image12| +|image13| 其中的HTML样式,也是我网上找来的,我觉得还挺不错,这里也贴出来 @@ -391,7 +393,7 @@ Proxy,然后由Proxy上报Server。 按钮,它是说当我们的问题解决后,要让zabbix做些什么,比如我想让 当CPU的使用率降下来后,发一个邮件通知一下。 -|image13| +|image14| 邮件的 HTML 样式 @@ -427,7 +429,7 @@ Proxy,然后由Proxy上报Server。 上面邮件中,有设置了一些颜色的自定义宏,我是在这里设置的。 -|image14| +|image15| 上面的动作,都是写的发邮件,也可以远程执行脚本,比如,我们监控服务,当服务被人为关闭了,我们可以让Zabbix Agent执行重启服务的命令。 @@ -459,7 +461,7 @@ Agent执行重启服务的命令。 那在ACTION 里如何配置呢,看下图。你可以选择在 agent或者proxy,或者server执行都可以,非常灵活。 -|image15| +|image16| 7.2.3.5 配置发件人 ~~~~~~~~~~~~~~~~~~ @@ -468,7 +470,7 @@ Zabbix 要能发送邮件,需要配置邮箱发送方。这里以163邮箱为 vim /etc/mail.rc -|image16| +|image17| 配置完成后,可用以下命令测试一下配置是否有效,若 yyy@163.com 能收到一条来自xxx@163.com 的邮件,则说明配置成功。 @@ -499,7 +501,7 @@ outlook 的形式展现。 先添加媒介:Administration - Media Types - Create media type -|image17| +|image18| 上面用到 sendmail.sh 是一个脚本,需要我们来自己写,内容就是如何将我们的告警内容发送出去。 @@ -520,11 +522,11 @@ Script parameters。 然后再创建用户,并添加邮箱:Administration - Users - Admin - Media -|image18| +|image19| 在创建用户的时候,要指定用户组,要注意这个用户组的权限一定要有相应主机组的权限,否则无法发送告警邮件。 -|image19| +|image20| 配置好收件人后 ,发件人呢? @@ -547,7 +549,7 @@ Script parameters。 zabbix 自带 mysql 的监控模板,监控项不多,只有 14 项。 -|image20| +|image21| 这个模板在使用前,需要进行两个配置。 @@ -577,7 +579,7 @@ zabbix 自带 mysql 的监控模板,监控项不多,只有 14 项。 能够及时将配置文件路径修改过来就行。如下图所示,你只要修改下方 ``HOME`` 变量值。 -|image21| +|image22| 然后记得去 web界面在 该host主机上link到 ``Template DB MySQL`` 这个模板上。 @@ -698,26 +700,27 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190404193811.png -.. |image1| image:: http://image.python-online.cn/20190404194416.png -.. |image2| image:: http://image.python-online.cn/20190404201313.png -.. |image3| image:: http://image.python-online.cn/20190404205221.png -.. |image4| image:: http://image.python-online.cn/20190404205617.png -.. |image5| image:: http://image.python-online.cn/20190404202122.png -.. |image6| image:: http://image.python-online.cn/20190404202353.png -.. |image7| image:: http://image.python-online.cn/20190404210213.png -.. |image8| image:: http://image.python-online.cn/20190404213125.png -.. |image9| image:: http://image.python-online.cn/20190404202855.png -.. |image10| image:: http://image.python-online.cn/20190404202937.png -.. |image11| image:: http://image.python-online.cn/20190404203425.png -.. |image12| image:: http://image.python-online.cn/20190404203805.png -.. |image13| image:: http://image.python-online.cn/20190404204212.png -.. |image14| image:: http://image.python-online.cn/20190404205837.png -.. |image15| image:: http://image.python-online.cn/20190404212423.png -.. |image16| image:: http://image.python-online.cn/20190411205822.png -.. |image17| image:: http://image.python-online.cn/20190417202834.png -.. |image18| image:: http://image.python-online.cn/20190404204534.png -.. |image19| image:: http://image.python-online.cn/20190605173956.png -.. |image20| image:: http://image.python-online.cn/20190409103417.png -.. |image21| image:: http://image.python-online.cn/20190409104026.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190404193811.png +.. |image2| image:: http://image.python-online.cn/20190404194416.png +.. |image3| image:: http://image.python-online.cn/20190404201313.png +.. |image4| image:: http://image.python-online.cn/20190404205221.png +.. |image5| image:: http://image.python-online.cn/20190404205617.png +.. |image6| image:: http://image.python-online.cn/20190404202122.png +.. |image7| image:: http://image.python-online.cn/20190404202353.png +.. |image8| image:: http://image.python-online.cn/20190404210213.png +.. |image9| image:: http://image.python-online.cn/20190404213125.png +.. |image10| image:: http://image.python-online.cn/20190404202855.png +.. |image11| image:: http://image.python-online.cn/20190404202937.png +.. |image12| image:: http://image.python-online.cn/20190404203425.png +.. |image13| image:: http://image.python-online.cn/20190404203805.png +.. |image14| image:: http://image.python-online.cn/20190404204212.png +.. |image15| image:: http://image.python-online.cn/20190404205837.png +.. |image16| image:: http://image.python-online.cn/20190404212423.png +.. |image17| image:: http://image.python-online.cn/20190411205822.png +.. |image18| image:: http://image.python-online.cn/20190417202834.png +.. |image19| image:: http://image.python-online.cn/20190404204534.png +.. |image20| image:: http://image.python-online.cn/20190605173956.png +.. |image21| image:: http://image.python-online.cn/20190409103417.png +.. |image22| image:: http://image.python-online.cn/20190409104026.png diff --git a/source/c07/c07_03.md b/source/c07/c07_03.md index 26fb469..00ca5ca 100644 --- a/source/c07/c07_03.md +++ b/source/c07/c07_03.md @@ -1,5 +1,7 @@ # 7.3 Docker:Hello world +![](http://image.iswbm.com/20200602135014.png) + --- ## 1. 安装Docker diff --git a/source/c07/c07_03.rst b/source/c07/c07_03.rst index 785a324..334931e 100755 --- a/source/c07/c07_03.rst +++ b/source/c07/c07_03.rst @@ -1,6 +1,8 @@ 7.3 Docker:Hello world ======================= +|image0| + -------------- 1. 安装Docker @@ -283,7 +285,7 @@ namespace 有下面六种 6. User namespace 让容器能够管理自己的用户,host 不能看到容器中创建的用户。 -正在运行的容器 |image0| 文件夹内容 |image1| +正在运行的容器 |image1| 文件夹内容 |image2| -------------- @@ -292,6 +294,7 @@ namespace 有下面六种 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/17-12-23/44035514.jpg -.. |image1| image:: http://image.python-online.cn/17-12-23/20133481.jpg +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/17-12-23/44035514.jpg +.. |image2| image:: http://image.python-online.cn/17-12-23/20133481.jpg diff --git a/source/c07/c07_04.md b/source/c07/c07_04.md index 676a753..bc91b12 100644 --- a/source/c07/c07_04.md +++ b/source/c07/c07_04.md @@ -1,5 +1,7 @@ # 7.4 Docker:关于镜像 +![](http://image.iswbm.com/20200602135014.png) + --- ## 一、镜像的基本内容 diff --git a/source/c07/c07_04.rst b/source/c07/c07_04.rst index f186d28..194891e 100755 --- a/source/c07/c07_04.rst +++ b/source/c07/c07_04.rst @@ -1,6 +1,8 @@ 7.4 Docker:关于镜像 ==================== +|image0| + -------------- 一、镜像的基本内容 @@ -39,7 +41,7 @@ hub下载的image可以在这里搜索\ `dockerfile `__ --generic-ssh-port 57891 \ bm-docker-02 -|image1| +|image2| centos的配置文件路径如下,ubuntu的有所不同 @@ -301,7 +303,7 @@ centos的配置文件路径如下,ubuntu的有所不同 /etc/systemd/system/multi-user.target.wants/docker.service 通过查看进程,左边是用docker-machine安装有docker,和手动安装有所不同 -红框标出的,表示,允许远程连接。 |image2| +红框标出的,表示,允许远程连接。 |image3| 2.3 管理Docker Machine ~~~~~~~~~~~~~~~~~~~~~~ @@ -331,7 +333,8 @@ centos的配置文件路径如下,ubuntu的有所不同 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png -.. |image1| image:: https://i.loli.net/2018/01/03/5a4ce6eeaff7d.png -.. |image2| image:: https://i.loli.net/2018/01/03/5a4ce964d3e73.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png +.. |image2| image:: https://i.loli.net/2018/01/03/5a4ce6eeaff7d.png +.. |image3| image:: https://i.loli.net/2018/01/03/5a4ce964d3e73.png diff --git a/source/c07/c07_07.md b/source/c07/c07_07.md index 948fab4..acd0345 100644 --- a/source/c07/c07_07.md +++ b/source/c07/c07_07.md @@ -1,5 +1,7 @@ # 7.7 SaltStack 入门指南 +![](http://image.iswbm.com/20200602135014.png) + --- ## 一、安装SaltStack diff --git a/source/c07/c07_07.rst b/source/c07/c07_07.rst index 7af2e7c..a567c9a 100755 --- a/source/c07/c07_07.rst +++ b/source/c07/c07_07.rst @@ -1,6 +1,8 @@ 7.7 SaltStack 入门指南 ====================== +|image0| + -------------- 一、安装SaltStack @@ -420,3 +422,6 @@ salt命令格式 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_08.rst b/source/c07/c07_08.rst index a0a1c35..66e9f37 100644 --- a/source/c07/c07_08.rst +++ b/source/c07/c07_08.rst @@ -1,6 +1,8 @@ 7.8 Keepalived 部署文档 ======================= +|image0| + 参考文档:\ `keepalived搭建zabbix server双机高可用 `__ @@ -177,3 +179,6 @@ chk_zabbix.sh :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_09.md b/source/c07/c07_09.md index 9b46279..6c540ca 100644 --- a/source/c07/c07_09.md +++ b/source/c07/c07_09.md @@ -1,5 +1,7 @@ # 7.9 Ansible 入门指南使用手册 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 有用的小技巧 ### 1. 只输出错误的信息 diff --git a/source/c07/c07_09.rst b/source/c07/c07_09.rst index d263d02..54849d2 100644 --- a/source/c07/c07_09.rst +++ b/source/c07/c07_09.rst @@ -1,6 +1,8 @@ 7.9 Ansible 入门指南使用手册 ============================ +|image0| + 1. 有用的小技巧 --------------- @@ -239,3 +241,6 @@ 1. `Ansible 配置全解 `__ + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_10.md b/source/c07/c07_10.md index 5c9e3f9..089d3c7 100644 --- a/source/c07/c07_10.md +++ b/source/c07/c07_10.md @@ -1,5 +1,7 @@ # 7.10 Ansible API 最全使用文档(中文) +![](http://image.iswbm.com/20200602135014.png) + 大家都知道 Ansible 是一个轻量级的部署工具,这里的轻量体现在哪里呢? master 与 节点间使用ssh通信,也不需要像salt那样需要在客户端装minion,然后在master和minion各起一个服务。 diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index 494ce9f..a09923c 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -1,6 +1,8 @@ 7.10 Ansible API 最全使用文档(中文) ===================================== +|image0| + 大家都知道 Ansible 是一个轻量级的部署工具,这里的轻量体现在哪里呢? master 与 @@ -14,7 +16,7 @@ master 与 无意中使用 pip search 进行搜索,还真的有 ansible-api -|image0| +|image1| 这是 2018 年10月份才开始的项目,目前来说成熟度还不够。 @@ -128,7 +130,7 @@ ansible-api 会调用 ansible 库的命令,这个过程不能指定 ansible.cf 然后由于原生的 ansible-api 的bug,需要修改代码,在如下函数位置(\ ``/usr/local/python3/lib/python3.7/site-packages/ansible_api/callback.py``\ )添加一个参数 -|image1| +|image2| 通过执行命令,即可开启 ansible server @@ -173,14 +175,14 @@ ansible-api 会调用 ansible 库的命令,这个过程不能指定 ansible.cf 发送了请求后,返回的结果如下 -|image2| +|image3| rc 为0,表示所有节点都没有出现 fatal 致命错误(有设置 ignore_errors 的错误也会返回0). rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级失败。 -|image3| +|image4| -------------- @@ -189,8 +191,9 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190716111523.png -.. |image1| image:: http://image.python-online.cn/20190716112113.png -.. |image2| image:: http://image.python-online.cn/20190716112824.png -.. |image3| image:: http://image.python-online.cn/20190716112838.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190716111523.png +.. |image2| image:: http://image.python-online.cn/20190716112113.png +.. |image3| image:: http://image.python-online.cn/20190716112824.png +.. |image4| image:: http://image.python-online.cn/20190716112838.png diff --git a/source/c07/c07_11.md b/source/c07/c07_11.md index 9d5f6ca..49239f4 100644 --- a/source/c07/c07_11.md +++ b/source/c07/c07_11.md @@ -1,5 +1,7 @@ # 7.11 K8S:基础入门 +![](http://image.iswbm.com/20200602135014.png) + 一些基础命令 ``` diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst index 7951bb0..29370c7 100644 --- a/source/c07/c07_11.rst +++ b/source/c07/c07_11.rst @@ -1,6 +1,8 @@ 7.11 K8S:基础入门 ================== +|image0| + 一些基础命令 :: @@ -61,7 +63,7 @@ K8s 角色详解 -|image0| +|image1| 其中 Controller 还分为几种: @@ -80,5 +82,6 @@ K8s 角色详解 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190907162015.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190907162015.png diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md index 708666a..71eca1b 100644 --- a/source/c07/c07_12.md +++ b/source/c07/c07_12.md @@ -1,5 +1,7 @@ # 7.12 Linux 包管理工具:yum 和 rpm +![](http://image.iswbm.com/20200602135014.png) + ## yum 只下载安装包 diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index d37d5dd..42547e8 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -1,6 +1,8 @@ 7.12 Linux 包管理工具:yum 和 rpm ================================= +|image0| + yum --- @@ -102,7 +104,7 @@ yum-utils 使用 经常在安装一个包的时候,会报如下的错误,找不到某 so 文件 -|image0| +|image1| 如果是缺一个包,那我们安装它就行了,缺 so 文件,那咋弄? @@ -130,7 +132,7 @@ yum-utils 使用 # 只能查已安装的包的安装日期。截图中,之所以存在两个包,是因为该机器上存在两个版本的包 $ rpm -qa --last | grep python-nova-tests -|image1| +|image2| 查看安装某个包是如何安装的,从哪安装 @@ -138,7 +140,7 @@ yum-utils 使用 $ yumdb info python-nova-tests -|image2| +|image3| 查看rpm包的版本 @@ -211,7 +213,8 @@ rpm 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20191219152328.png -.. |image1| image:: http://image.python-online.cn/20191225173340.png -.. |image2| image:: http://image.python-online.cn/20191225175350.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191219152328.png +.. |image2| image:: http://image.python-online.cn/20191225173340.png +.. |image3| image:: http://image.python-online.cn/20191225175350.png diff --git a/source/c07/c07_13.md b/source/c07/c07_13.md index bc9dba3..8732f66 100644 --- a/source/c07/c07_13.md +++ b/source/c07/c07_13.md @@ -1,5 +1,7 @@ # 7.13 基于 ansible-api 二次开发 +![](http://image.iswbm.com/20200602135014.png) + 长久以来,IT 运维在企业内部一直是个耗人耗力的事情。随着虚拟化的大量应用、私有云、容器的不断普及,数据中心内部的压力愈发增加。传统的自动化工具,往往是面向于数据中心特定的一类对象,例如操作系统、虚拟化、网络设备的自动化运维工具往往是不同的。那么,有没有一种数据中心级别的统一的自动化运维工具呢? 答案就是[ Ansible](https://www.ansible.com/)。和传统的自动化工具 (如 Puppet)相比,Ansible 尤其明显的优势: diff --git a/source/c07/c07_13.rst b/source/c07/c07_13.rst index 7bf2666..7e7816e 100644 --- a/source/c07/c07_13.rst +++ b/source/c07/c07_13.rst @@ -1,6 +1,8 @@ 7.13 基于 ansible-api 二次开发 ============================== +|image0| + 长久以来,IT 运维在企业内部一直是个耗人耗力的事情。随着虚拟化的大量应用、私有云、容器的不断普及,数据中心内部的压力愈发增加。传统的自动化工具,往往是面向于数据中心特定的一类对象,例如操作系统、虚拟化、网络设备的自动化运维工具往往是不同的。那么,有没有一种数据中心级别的统一的自动化运维工具呢? @@ -119,3 +121,6 @@ ansible 的 api 开发出来的吗? :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_14.md b/source/c07/c07_14.md index b0031db..c588e70 100644 --- a/source/c07/c07_14.md +++ b/source/c07/c07_14.md @@ -1,5 +1,7 @@ # 7.14 Linux 如何写判断语句 +![](http://image.iswbm.com/20200602135014.png) + ## 7.14. 如何判断文件 `-e filename`: 如果 filename存在,则为真 diff --git a/source/c07/c07_14.rst b/source/c07/c07_14.rst index 7bc09f9..68cdd04 100644 --- a/source/c07/c07_14.rst +++ b/source/c07/c07_14.rst @@ -1,6 +1,8 @@ 7.14 Linux 如何写判断语句 ========================= +|image0| + 7.14. 如何判断文件 ------------------ @@ -28,3 +30,6 @@ filename可读,则为真 ``-w filename``: 如果 filename可写,则为真 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_15.md b/source/c07/c07_15.md index 0dbed61..4085907 100644 --- a/source/c07/c07_15.md +++ b/source/c07/c07_15.md @@ -1,5 +1,7 @@ # 7.15 Mariadb 与 Galera 集群总结 +![](http://image.iswbm.com/20200602135014.png) + MariaDB是MySQL的一个分支,由MySQL的创始人Michael Widenius主导开发,采用GPL授权许可。 开发这个分支的原因之一是Oracle公司收购了MySQL后,有将MySQL闭源的潜在风险,因此社区采用分支的方式来避开这个风险。 diff --git a/source/c07/c07_15.rst b/source/c07/c07_15.rst index bbb1e93..8278c3c 100644 --- a/source/c07/c07_15.rst +++ b/source/c07/c07_15.rst @@ -1,6 +1,8 @@ 7.15 Mariadb 与 Galera 集群总结 =============================== +|image0| + MariaDB是MySQL的一个分支,由MySQL的创始人Michael Widenius主导开发,采用GPL授权许可。 @@ -20,7 +22,7 @@ Galera 是一个MySQL(也支持MariaDB,Percona) 5. 用户连接集群后,使用跟 MySQL 基本一致。 6. 不会写binlog -关于Galera是如何做到多主的,可以借助这张图来看看\ |image0| +关于Galera是如何做到多主的,可以借助这张图来看看\ |image1| 来源:https://blog.csdn.net/weixin_42867972/article/details/84198696 @@ -222,5 +224,6 @@ wsrep_flow_control_sent 和 wsrep_local_recv_queue_avg 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20191213162259.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191213162259.png diff --git a/source/c07/c07_16.md b/source/c07/c07_16.md index 0aff82a..8459b7b 100644 --- a/source/c07/c07_16.md +++ b/source/c07/c07_16.md @@ -1,5 +1,7 @@ # 7.16 Linux 运维之路 +![](http://image.iswbm.com/20200602135014.png) + ## 7.16.1 如何查看并计算 CPU 使用率 有很多的工具可以查看CPU使用率,使用Linux 自带的 top 是最常用的方式。![](http://image.python-online.cn/20191220202103.png) diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index 9edd7f0..80d215b 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -1,11 +1,13 @@ 7.16 Linux 运维之路 =================== +|image0| + 7.16.1 如何查看并计算 CPU 使用率 -------------------------------- 有很多的工具可以查看CPU使用率,使用Linux 自带的 top -是最常用的方式。\ |image0| +是最常用的方式。\ |image1| 图中标注解释 @@ -21,11 +23,11 @@ - si,software interrupt: 处理软件中断的CPU时间 - st:这个虚拟机被hypervisor偷去的CPU时间(译注:如果当前处于一个hypervisor下的vm,实际上hypervisor也是要消耗一部分CPU处理时间的)。 -若你觉得这样不够直观,可以按 ``t`` ,切换显示\ |image1| +若你觉得这样不够直观,可以按 ``t`` ,切换显示\ |image2| -若这是多核机器,输入 ``1`` ,可以显示单核CPU使用情况。\ |image2| +若这是多核机器,输入 ``1`` ,可以显示单核CPU使用情况。\ |image3| -若你觉得这样不够直观,可以按 ``t``\ 切换成直方图显示\ |image3| +若你觉得这样不够直观,可以按 ``t``\ 切换成直方图显示\ |image4| 标为 ``2`` 的 栏目,表示各个进程的 cpu 使用率,比如第一个进程 348% 就是占满了3.48个核。 @@ -86,8 +88,9 @@ 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20191220202103.png -.. |image1| image:: http://image.python-online.cn/20191220203403.png -.. |image2| image:: http://image.python-online.cn/20191220202408.png -.. |image3| image:: http://image.python-online.cn/20191220203205.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191220202103.png +.. |image2| image:: http://image.python-online.cn/20191220203403.png +.. |image3| image:: http://image.python-online.cn/20191220202408.png +.. |image4| image:: http://image.python-online.cn/20191220203205.png diff --git a/source/c07/c07_17.md b/source/c07/c07_17.md index 529ade8..fd6944f 100644 --- a/source/c07/c07_17.md +++ b/source/c07/c07_17.md @@ -1,5 +1,7 @@ # 1.17 ansible 自定义 Jinja2 过滤器 +![](http://image.iswbm.com/20200602135014.png) + 在 ansible 中 Jinja2 的过滤器是作为插件存在于 ansible 的项目中。 它的代码位于 /usr/lib/python2.7/site-packages/ansible/plugins/filter diff --git a/source/c07/c07_17.rst b/source/c07/c07_17.rst index ab910d7..19ad5b3 100644 --- a/source/c07/c07_17.rst +++ b/source/c07/c07_17.rst @@ -1,6 +1,8 @@ 1.17 ansible 自定义 Jinja2 过滤器 ================================= +|image0| + 在 ansible 中 Jinja2 的过滤器是作为插件存在于 ansible 的项目中。 它的代码位于 /usr/lib/python2.7/site-packages/ansible/plugins/filter @@ -15,3 +17,6 @@ https://www.jianshu.com/p/ae74f5f39828 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_18.md b/source/c07/c07_18.md index c13615f..b3f3a22 100644 --- a/source/c07/c07_18.md +++ b/source/c07/c07_18.md @@ -1,5 +1,7 @@ # 7.18 Shell中去除字符串前后空格的方法 +![](http://image.iswbm.com/20200602135014.png) + 原文:https://www.jb51.net/article/157327.htm 经常碰到的场景,需要去除字符串中的前后的空格。在Shell中不像其他语言有strip()来处理,不过也是可以使用诸如awk等命令来处理。 diff --git a/source/c07/c07_18.rst b/source/c07/c07_18.rst index d90a701..93fa977 100644 --- a/source/c07/c07_18.rst +++ b/source/c07/c07_18.rst @@ -1,6 +1,8 @@ 7.18 Shell中去除字符串前后空格的方法 ==================================== +|image0| + 原文:https://www.jb51.net/article/157327.htm 经常碰到的场景,需要去除字符串中的前后的空格。在Shell中不像其他语言有strip()来处理,不过也是可以使用诸如awk等命令来处理。 @@ -64,3 +66,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_19.md b/source/c07/c07_19.md index 4d9ad8f..45cca7b 100644 --- a/source/c07/c07_19.md +++ b/source/c07/c07_19.md @@ -1,5 +1,7 @@ # 7.19 Ansible 使用教程 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 环境准备 安装 Ansible ,需要 epel 源,所以要先配置一下 diff --git a/source/c07/c07_19.rst b/source/c07/c07_19.rst index d7bcb5d..c17e689 100644 --- a/source/c07/c07_19.rst +++ b/source/c07/c07_19.rst @@ -1,6 +1,8 @@ 7.19 Ansible 使用教程 ===================== +|image0| + 1. 环境准备 ----------- @@ -50,3 +52,6 @@ :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c07/c07_20.md b/source/c07/c07_20.md index 7dc445f..91f0cb9 100644 --- a/source/c07/c07_20.md +++ b/source/c07/c07_20.md @@ -1,5 +1,7 @@ # 7.20 使用 Shell 删除文件的多种方法 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 当前目录下的文件 最经典的方法,删除当前目录下的所有类型的文件 diff --git a/source/c07/c07_20.rst b/source/c07/c07_20.rst index fcb4ca0..7154b00 100644 --- a/source/c07/c07_20.rst +++ b/source/c07/c07_20.rst @@ -1,6 +1,8 @@ 7.20 使用 Shell 删除文件的多种方法 ================================== +|image0| + 1. 当前目录下的文件 ------------------- @@ -46,3 +48,6 @@ 1. 把上面的 ``.`` 全部改为指定的目录 2. 在上面的命令之前,都加上 ``cd ${dest_dir};`` ,并且命令后,加上 ``cd -`` + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c08/c08_01.md b/source/c08/c08_01.md index 694ef43..2f0f2d5 100644 --- a/source/c08/c08_01.md +++ b/source/c08/c08_01.md @@ -1,5 +1,7 @@ # 8.1 OpenStack 运维命令 +![](http://image.iswbm.com/20200602135014.png) + --- ## 一、OpenStack diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index 90bb12e..e17b6fc 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -1,6 +1,8 @@ 8.1 OpenStack 运维命令 ====================== +|image0| + -------------- 一、OpenStack @@ -448,3 +450,6 @@ aggregate管理 :alt: 关注公众号,获取最新干货! 关注公众号,获取最新干货! + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c08/c08_02.md b/source/c08/c08_02.md index 2b88eda..601c9fd 100644 --- a/source/c08/c08_02.md +++ b/source/c08/c08_02.md @@ -1,5 +1,7 @@ # 8.2 OpenStack 部署SR-IOV +![](http://image.iswbm.com/20200602135014.png) + --- **摘要**: SR-IOV 技术是一种基于硬件的虚拟化解决方案,可提高性能和可伸缩性。SR-IOV 标准允许在虚拟机之间高效共享 PCIe(Peripheral Component Interconnect Express,快速外设组件互连)设备,并且它是在硬件中实现的,可以获得能够与本机性能媲美的 I/O 性能。 diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index 09c3722..7b746da 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -1,6 +1,8 @@ 8.2 OpenStack 部署SR-IOV ======================== +|image0| + -------------- **摘要**: SR-IOV @@ -21,13 +23,13 @@ SR-IOV 8.2.1.1 bios开启vt-d, sriov ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -|image0| |image1| +|image1| |image2| 8.2.1.2 开启iommu ~~~~~~~~~~~~~~~~~ vim /etc/sysconfig/grub(或者/etc/default/grub):intel_iommu=on -|image2| 改完后,重新生成配置 +|image3| 改完后,重新生成配置 :: @@ -118,7 +120,7 @@ vim /etc/nova/nova.conf service openstack-nova-compute restart -查看数据库,compute_nodes,确认VF是否有被管理起来 |image3| +查看数据库,compute_nodes,确认VF是否有被管理起来 |image4| 8.2.3 控制节点 -------------- @@ -128,7 +130,7 @@ vim /etc/nova/nova.conf 8.2.3.1 neutron-server ~~~~~~~~~~~~~~~~~~~~~~ -vim /etc/neutron/plugins/ml2/ml2_conf.ini |image4| +vim /etc/neutron/plugins/ml2/ml2_conf.ini |image5| 添加sriov配置文件(如果没有此文件,就添加) vim /etc/neutron/plugins/ml2/ml2_conf_sriov.ini @@ -145,7 +147,7 @@ vim /etc/neutron/plugins/ml2/ml2_conf.ini |image4| --config-file /etc/neutron/plugins/ml2/ml2_conf_sriov.ini -|image5| +|image6| 重启服务,使配置生效 @@ -264,7 +266,7 @@ interface-attach不适用于sriov port。 1、使用neutron port-show ,查看并记录原 port 的 ``binding:profile`` 信息,如果有多个port,把每个port的信息都记录下来。 -|image6| +|image7| 2、nova interface-detach卸载原来的port。 @@ -290,7 +292,7 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr select * from pci_devices where instance_uuid='5a1c1828-4190-43fa-8e05-ae51b0196656'; -|image7| +|image8| 附录:参考文档 -------------- @@ -308,12 +310,13 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2018/01/19/5a61bfa0ca66f.png -.. |image1| image:: https://i.loli.net/2018/01/19/5a61bfd243111.png -.. |image2| image:: https://i.loli.net/2018/01/19/5a61c022d68d3.png -.. |image3| image:: https://i.loli.net/2018/01/19/5a61c1cf51b58.png -.. |image4| image:: https://i.loli.net/2018/01/19/5a61c1faac447.png -.. |image5| image:: https://i.loli.net/2018/01/19/5a61c246451e7.png -.. |image6| image:: http://image.python-online.cn/20190529202132.png -.. |image7| image:: http://image.python-online.cn/20190529202440.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2018/01/19/5a61bfa0ca66f.png +.. |image2| image:: https://i.loli.net/2018/01/19/5a61bfd243111.png +.. |image3| image:: https://i.loli.net/2018/01/19/5a61c022d68d3.png +.. |image4| image:: https://i.loli.net/2018/01/19/5a61c1cf51b58.png +.. |image5| image:: https://i.loli.net/2018/01/19/5a61c1faac447.png +.. |image6| image:: https://i.loli.net/2018/01/19/5a61c246451e7.png +.. |image7| image:: http://image.python-online.cn/20190529202132.png +.. |image8| image:: http://image.python-online.cn/20190529202440.png diff --git a/source/c08/c08_03.md b/source/c08/c08_03.md index 55fac94..1ed1c5a 100644 --- a/source/c08/c08_03.md +++ b/source/c08/c08_03.md @@ -1,5 +1,7 @@ # 8.3 制作 OpenStack 镜像 +![](http://image.iswbm.com/20200602135014.png) + --- 这里仅以Ubuntu系统为例,其他系统也是类似,部分不同的地方会给出标注。 diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index 229c25c..d9f0440 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -1,6 +1,8 @@ 8.3 制作 OpenStack 镜像 ======================= +|image0| + -------------- 这里仅以Ubuntu系统为例,其他系统也是类似,部分不同的地方会给出标注。 @@ -125,7 +127,7 @@ 下载速度十分缓慢,可能需要半个多小时,我已经下载好,上传到百度云盘,需要自取。 -|image0| +|image1| 安装好后,可以尝试连接虚拟机。 如果连接失败,可以自己的排查下原因 @@ -134,7 +136,7 @@ 1. 宿主机的iptables,firewall 2. 端口是否开放,telnet 一下 -|image1| +|image2| 然后根据提示安装系统(注意要先新建一个用户,设置该用户密码,后续要登陆虚拟机使用)。安装完成后,退出spice。 @@ -496,7 +498,7 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` 通过 guestfish 工具可以实现不用创建虚拟机就可以修改镜像里的文件内容。 -|image2| +|image3| 8.3.3 KVM 镜像快照 ------------------ @@ -508,10 +510,10 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` $ virsh snapshot-create-as ws_controller01 2.2.7 --disk-only --atomic $ virsh blockcommit ws_controller01 hda --active --verbose --pivot -|image3| - |image4| +|image5| + 附录:参考文档 -------------- @@ -533,9 +535,10 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` 关注公众号,获取最新干货! -.. |image0| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png -.. |image1| image:: https://i.loli.net/2018/01/27/5a6c34b14c6ec.png -.. |image2| image:: http://image.python-online.cn/20190827200522.png -.. |image3| image:: http://image.python-online.cn/20191211174659.png -.. |image4| image:: http://image.python-online.cn/20191211174956.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png +.. |image2| image:: https://i.loli.net/2018/01/27/5a6c34b14c6ec.png +.. |image3| image:: http://image.python-online.cn/20190827200522.png +.. |image4| image:: http://image.python-online.cn/20191211174659.png +.. |image5| image:: http://image.python-online.cn/20191211174956.png diff --git a/source/c08/c08_04.md b/source/c08/c08_04.md index 0c25a83..792c96f 100644 --- a/source/c08/c08_04.md +++ b/source/c08/c08_04.md @@ -1,5 +1,7 @@ # 8.4 云计算与虚拟化入门通识 +![](http://image.iswbm.com/20200602135014.png) + 经常有朋友问我,你是做什么的呢? 我回答说,云计算。 diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index fa24ae1..a68ded8 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -1,6 +1,8 @@ 8.4 云计算与虚拟化入门通识 ========================== +|image0| + 经常有朋友问我,你是做什么的呢? 我回答说,云计算。 @@ -11,7 +13,7 @@ 为了让你多了解一些云计算的内容,我想着写这么一篇文章,介绍一下我从事的领域,同时也对云计算和虚拟化这块入门级知识做一个梳理,如果刚好你也想进入这个领域,这份入门通识指南,应该挺适合你的。 -|image0| +|image1| 8.4.1 云计算是什么? -------------------- @@ -25,7 +27,7 @@ computing**\ ),是一种基于\ `互联网 `` 下的文件获取的,这个目录每个虚拟机一个。 -|image17| +|image18| 如果你的模块里已经配置了频率,则以此为准。若没有配置,则默认为 ``PER_INSTANCE``\ 。 具体函数:\ ``_run_modules`` -|image18| +|image19| 那么cloudinit 是如何控制模块的执行频率呢? @@ -458,15 +460,15 @@ ephemeral0 拿到对应的 /dev/vdb,并将其写入 /etc/fstab 文件,说明已经执行过(返回 False ),如果不存在 sem 文件(返回 True) 说明未执行过。 -|image19| +|image20| sem 文件是怎样的? 这是 ``PER_ONCE`` 的 sem 文件: -|image20| +|image21| -这是 ``PER_INSTANCE`` 的 sem 文件\ |image21| +这是 ``PER_INSTANCE`` 的 sem 文件\ |image22| 8.6.10 如何解析网卡配置 ----------------------- @@ -481,7 +483,7 @@ ubuntu 的网卡配置不是正常我们常见的 json 或者 yaml from cloudinit.net import eni config=eni.parse_deb_config('/etc/network/interfaces.d/50-cloud-init.cfg') -|image22| +|image23| 8.6.11 低版本的日志输出 ----------------------- @@ -491,7 +493,7 @@ ubuntu 的网卡配置不是正常我们常见的 json 或者 yaml 经过排查,你可以在 ``cloudinit/log.py`` 中按下面的改法修改解决 -|image23| +|image24| 8.6.12 旧版本网络配置的三个巨坑 ------------------------------- @@ -507,12 +509,12 @@ ubuntu 的网卡配置不是正常我们常见的 json 或者 yaml 在 cloud-init 0.7.5中,网络信息的读取与配置都是在且仅能在 local 阶段进行的,代码如下,只在 dsmode 为 local 时才会执行 on_first_boot。 -|image24| +|image25| 而如果是虚拟机重启的话,是在stages.py 这边判断是否为新虚拟机的,这也和旧版本有所区别。 -|image25| +|image26| 为了让你能更加清晰的了解这个网络配置过程,我阅读了这块的源代码。 @@ -529,12 +531,12 @@ ubuntu 的网卡配置不是正常我们常见的 json 或者 yaml 如果是按照旧虚拟机创建新的快照镜像,然后使用这个镜像创建新的虚拟机,有可能会在同一块网卡上出现新旧两个ip,这是因为虚拟机在启动过程中,会先读取原网络配置配置ip,然后才会运行 cloud-init 进行新ip的配置,而新ip的配置是使用 ``ifup`` -这个命令\ |image26| +这个命令\ |image27| -使用这种方式并不会将第一次配置的旧ip给清除掉。\ |image27| +使用这种方式并不会将第一次配置的旧ip给清除掉。\ |image28| 这个问题,目前我只在CentOS6 中遇到过。可以通过修改代码让其先 ``ifdown`` -再 ``ifup`` 就可以解决这个问题。\ |image28| +再 ``ifup`` 就可以解决这个问题。\ |image29| **坑二** @@ -544,7 +546,7 @@ NetworkManager 具体的创建逻辑是在这 -|image29| +|image30| **坑三** @@ -565,12 +567,12 @@ NetworkManager 一个是会自动DHCP获取到一个以ip命名的hostname,并将原来的覆盖掉。 -|image30| +|image31| 为了避免出现这些情况,请务必保证这些包都安装完整(左为 CentOS 7.2,右为 CentOS 6.5)。 -|image31| +|image32| 8.6.13 网络是如何启动的?(新版本) ----------------------------------- @@ -585,17 +587,17 @@ CentOS 6.5)。 在 local 阶段的时候,\ ``existing`` 是 ``check`` ,这是合理的。 -|image32| - |image33| +|image34| + 在local阶段,on_first_boot 的函数 network 是 False ,所以这里也不会写网卡配置文件,自然也不会配置。 -|image34| - |image35| +|image36| + 再往后面看,就可以发现,原来写配置文件的地方是在 ``cmd/main.py`` 里。 .. code:: python @@ -620,11 +622,11 @@ True,意思是会启用网卡,配置ip。 2. 校正网卡名,以 ConfigDrive 的配置为准 3. 将ip信息写入配置文件,并启动网卡 -|image36| +|image37| 那它是如何对网卡进行重命令的呢?看了代码,其实是用ip命令实现的。 -|image37| +|image38| 提取出来其实就三条命令,要注意的是,这三条命令执行是有顺序的 @@ -639,11 +641,11 @@ True,意思是会启用网卡,配置ip。 目录下获取的。每个网卡一个目录,每个目录下都有相应的文件记录相应的信息,比如 ``/sys/class/net/ens3/address`` 记录的是网卡的 mac 地址。 -|image38| +|image39| 接下来就要开始配置网络了,先写网络配置文件,再根据参数选择是否启用网络。 -|image39| +|image40| 如果是重启虚拟机或者 init 阶段进入这里呢,会不会又重复配置网络了呢? @@ -652,7 +654,7 @@ True,意思是会启用网卡,配置ip。 cloudinit 会根据缓存中的虚拟机的uuid来与ConfigDrive 的对比,如果不一样,则认为这台虚拟机是新创建的虚拟机,只有新的虚拟机才会走入这里去配置网络。 -|image40| +|image41| 那问题又来了,虽然上面有个 bring_up 的参数,实际上,通过代码可以发现,在 local 阶段,bring_up 为 False 不会去启用网卡,而在 init 阶段呢,虽然 @@ -710,45 +712,46 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190430204707.png -.. |image1| image:: http://image.python-online.cn/20190430204933.png -.. |image2| image:: http://image.python-online.cn/20190430205449.png -.. |image3| image:: http://image.python-online.cn/20190430211900.png -.. |image4| image:: http://image.python-online.cn/20190430213012.png -.. |image5| image:: http://image.python-online.cn/20190430213337.png -.. |image6| image:: http://image.python-online.cn/20190430213429.png -.. |image7| image:: http://image.python-online.cn/20190430213729.png -.. |image8| image:: http://image.python-online.cn/20190430225605.png -.. |image9| image:: http://image.python-online.cn/20190430225726.png -.. |image10| image:: http://image.python-online.cn/20190430230214.png -.. |image11| image:: http://image.python-online.cn/FpqcyL4hWwpaAGzsdreQwXvH4Rx8 -.. |image12| image:: http://image.python-online.cn/20190430230839.png -.. |image13| image:: http://image.python-online.cn/20190430231108.png -.. |image14| image:: http://image.python-online.cn/20190623091911.png -.. |image15| image:: http://image.python-online.cn/20190708175813.png -.. |image16| image:: http://image.python-online.cn/20190910160035.png -.. |image17| image:: http://image.python-online.cn/20190910150305.png -.. |image18| image:: http://image.python-online.cn/20190910142222.png -.. |image19| image:: http://image.python-online.cn/20190910153637.png -.. |image20| image:: http://image.python-online.cn/20190910171359.png -.. |image21| image:: http://image.python-online.cn/20190910171538.png -.. |image22| image:: http://image.python-online.cn/20190906091102.png -.. |image23| image:: http://image.python-online.cn/20190909172153.png -.. |image24| image:: http://image.python-online.cn/20190429104357.png -.. |image25| image:: http://image.python-online.cn/20190829141059.png -.. |image26| image:: http://image.python-online.cn/Fp1TeHSiIMIQoZygbW9VSfAagB_d -.. |image27| image:: http://image.python-online.cn/Fh-5SQ8qYjhJEKovI6LmIpabSy2c -.. |image28| image:: http://image.python-online.cn/20190430231812.png -.. |image29| image:: http://image.python-online.cn/20190430232309.png -.. |image30| image:: http://image.python-online.cn/20190429205735.png -.. |image31| image:: http://image.python-online.cn/20190430232911.png -.. |image32| image:: http://image.python-online.cn/20190911175423.png -.. |image33| image:: http://image.python-online.cn/20190911174648.png -.. |image34| image:: http://image.python-online.cn/20190911173615.png -.. |image35| image:: http://image.python-online.cn/20190911195024.png -.. |image36| image:: http://image.python-online.cn/20190911202425.png -.. |image37| image:: http://image.python-online.cn/20190911202551.png -.. |image38| image:: http://image.python-online.cn/20190911203953.png -.. |image39| image:: http://image.python-online.cn/20190911204805.png -.. |image40| image:: http://image.python-online.cn/20190911205518.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190430204707.png +.. |image2| image:: http://image.python-online.cn/20190430204933.png +.. |image3| image:: http://image.python-online.cn/20190430205449.png +.. |image4| image:: http://image.python-online.cn/20190430211900.png +.. |image5| image:: http://image.python-online.cn/20190430213012.png +.. |image6| image:: http://image.python-online.cn/20190430213337.png +.. |image7| image:: http://image.python-online.cn/20190430213429.png +.. |image8| image:: http://image.python-online.cn/20190430213729.png +.. |image9| image:: http://image.python-online.cn/20190430225605.png +.. |image10| image:: http://image.python-online.cn/20190430225726.png +.. |image11| image:: http://image.python-online.cn/20190430230214.png +.. |image12| image:: http://image.python-online.cn/FpqcyL4hWwpaAGzsdreQwXvH4Rx8 +.. |image13| image:: http://image.python-online.cn/20190430230839.png +.. |image14| image:: http://image.python-online.cn/20190430231108.png +.. |image15| image:: http://image.python-online.cn/20190623091911.png +.. |image16| image:: http://image.python-online.cn/20190708175813.png +.. |image17| image:: http://image.python-online.cn/20190910160035.png +.. |image18| image:: http://image.python-online.cn/20190910150305.png +.. |image19| image:: http://image.python-online.cn/20190910142222.png +.. |image20| image:: http://image.python-online.cn/20190910153637.png +.. |image21| image:: http://image.python-online.cn/20190910171359.png +.. |image22| image:: http://image.python-online.cn/20190910171538.png +.. |image23| image:: http://image.python-online.cn/20190906091102.png +.. |image24| image:: http://image.python-online.cn/20190909172153.png +.. |image25| image:: http://image.python-online.cn/20190429104357.png +.. |image26| image:: http://image.python-online.cn/20190829141059.png +.. |image27| image:: http://image.python-online.cn/Fp1TeHSiIMIQoZygbW9VSfAagB_d +.. |image28| image:: http://image.python-online.cn/Fh-5SQ8qYjhJEKovI6LmIpabSy2c +.. |image29| image:: http://image.python-online.cn/20190430231812.png +.. |image30| image:: http://image.python-online.cn/20190430232309.png +.. |image31| image:: http://image.python-online.cn/20190429205735.png +.. |image32| image:: http://image.python-online.cn/20190430232911.png +.. |image33| image:: http://image.python-online.cn/20190911175423.png +.. |image34| image:: http://image.python-online.cn/20190911174648.png +.. |image35| image:: http://image.python-online.cn/20190911173615.png +.. |image36| image:: http://image.python-online.cn/20190911195024.png +.. |image37| image:: http://image.python-online.cn/20190911202425.png +.. |image38| image:: http://image.python-online.cn/20190911202551.png +.. |image39| image:: http://image.python-online.cn/20190911203953.png +.. |image40| image:: http://image.python-online.cn/20190911204805.png +.. |image41| image:: http://image.python-online.cn/20190911205518.png diff --git a/source/c08/c08_07.md b/source/c08/c08_07.md index b81e476..b59582f 100644 --- a/source/c08/c08_07.md +++ b/source/c08/c08_07.md @@ -1,5 +1,7 @@ # 8.7 OpenStack 实现GPU直通 +![](http://image.iswbm.com/20200602135014.png) + ## 8.7.1 环境准备 检查是否有 GPU 设备:`lspci | grep NVIDIA` diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index 0b65413..4c07921 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -1,6 +1,8 @@ 8.7 OpenStack 实现GPU直通 ========================= +|image0| + 8.7.1 环境准备 -------------- @@ -8,11 +10,11 @@ (若没有lspci命令,则安装 yum install pciutils -y) -|image0| +|image1| 此时查看,驱动是 nvidia -|image1| +|image2| :: @@ -39,15 +41,15 @@ 未创建虚拟机 -|image2| +|image3| 创建gpu虚拟机成功,driver 由 nvidia 变为 vfio-pci -|image3| +|image4| 删除虚拟机成功 -|image4| +|image5| 8.7.2.2 隐藏虚拟机标识 ~~~~~~~~~~~~~~~~~~~~~~ @@ -59,7 +61,7 @@ id。 (若没有cpuid,则安装 yum install -y cpuid) -|image5| +|image6| 如何隐藏 hypervisor id,只需要在xml的feature加上这段。 @@ -71,7 +73,7 @@ id。 然后destroy再start一下虚拟机,在虚拟机内部再次查看。 -|image6| +|image7| 在Pike 版本,OpenStack 已经可以根据镜像里是否有\ ``img_hide_hypervisor_id=true`` @@ -92,16 +94,16 @@ Pike 版本自己修改OpenStack 的源码,在xml里加上这段。 具体 Pike版的代码如何实现,主要函数是这段 -|image7| +|image8| 记得在镜像meta里添加 img_hide_hypervisor_id 的字段 -|image8| +|image9| 8.7.2.3. 是否直通成功 ~~~~~~~~~~~~~~~~~~~~~ -在虚拟机内部执行 nvidia-smi\ |image9| +在虚拟机内部执行 nvidia-smi\ |image10| 8.7.2.4 称重器 ~~~~~~~~~~~~~~ @@ -116,7 +118,7 @@ GPU 的宿主机上。 设备数来决定)的宿主机的权重为最低,为了保证这点,这个称重器的权重系数应最大,暂定为200(可以通过配置项 ws_gpu_weight_multiplier 设置)。 -|image10| +|image11| 8.7.3 使用说明 -------------- @@ -153,15 +155,16 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190419144135.png -.. |image1| image:: http://image.python-online.cn/20190419144044.png -.. |image2| image:: http://image.python-online.cn/20190422201117.png -.. |image3| image:: http://image.python-online.cn/20190422201041.png -.. |image4| image:: http://image.python-online.cn/20190422201117.png -.. |image5| image:: http://image.python-online.cn/20190422205222.png -.. |image6| image:: http://image.python-online.cn/20190422204755.png -.. |image7| image:: http://image.python-online.cn/20190528105408.png -.. |image8| image:: http://image.python-online.cn/20190528105021.png -.. |image9| image:: http://image.python-online.cn/20190528114526.png -.. |image10| image:: http://image.python-online.cn/20190606185531.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190419144135.png +.. |image2| image:: http://image.python-online.cn/20190419144044.png +.. |image3| image:: http://image.python-online.cn/20190422201117.png +.. |image4| image:: http://image.python-online.cn/20190422201041.png +.. |image5| image:: http://image.python-online.cn/20190422201117.png +.. |image6| image:: http://image.python-online.cn/20190422205222.png +.. |image7| image:: http://image.python-online.cn/20190422204755.png +.. |image8| image:: http://image.python-online.cn/20190528105408.png +.. |image9| image:: http://image.python-online.cn/20190528105021.png +.. |image10| image:: http://image.python-online.cn/20190528114526.png +.. |image11| image:: http://image.python-online.cn/20190606185531.png diff --git a/source/c08/c08_08.md b/source/c08/c08_08.md index 3acfe11..489747c 100644 --- a/source/c08/c08_08.md +++ b/source/c08/c08_08.md @@ -1,5 +1,7 @@ # 8.8 OpenStack 如何使用DHCP? +![](http://image.iswbm.com/20200602135014.png) + ## 8.8.1 为何要使用dhcp? 在创建虚拟机时,虚拟机的ip获取一般有两种方式: diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index 049617e..6286354 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -1,6 +1,8 @@ 8.8 OpenStack 如何使用DHCP? ============================ +|image0| + 8.8.1 为何要使用dhcp? ---------------------- @@ -22,7 +24,7 @@ 如下图所示 -|image0| +|image1| 红色部分是虚拟机内部需要增加的网卡设备。 @@ -87,7 +89,7 @@ server网卡设备与ovs网桥。一个network一个tap设备。 确保控制节点都安装了 ``neutron-openvswitch-agent`` 和 ``neutron-dhcp-agent`` 等几个包 -|image1| +|image2| 确保如下几个配置无误 @@ -122,7 +124,7 @@ server网卡设备与ovs网桥。一个network一个tap设备。 通过 ovs-vsctl show 检查环境是否正常 -|image2| +|image3| 8.8.3.3 镜像支持 ~~~~~~~~~~~~~~~~ @@ -139,7 +141,7 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 1. 启用DHCP后,dhcp server 会占用一个allocation_pools中的ip。 2. 子网启用了dhcp,ConfigDrive不会携带静态的ip地址(类型为ipv4,左图),而是携带了将类型变更为ipv4_dhcp(右图),虚拟机启动时发送广播消息DHCP - discover从dhcp server获得真正的ip地址。 |image3| + discover从dhcp server获得真正的ip地址。 |image4| 3. 若子网启用了dhcp,并且创建了虚拟机,此时 disable dhcp,只是表象上 dhcp 被关了,而实际 dhcp port 被是占用着,因为 dhcp server 还要给之前创建的虚拟机提供服务。如果再用这个子网创建的虚拟机会使用dhcp @@ -192,17 +194,17 @@ NetworkManager, ubuntu 是network-manager)才负责获取ip。 比如 local 阶段作为第一个执行的。 -|image4| +|image5| 而后的几个服务(如init阶段),都需要保证 后于 local 已经启动过才能运行。 -|image5| +|image6| 在 **CentOS 6.x** 中,由于系统过于古老并没有 systemd ,它的服务启动顺序是由 ``/etc/rc.d/rc3.d`` 目录进行管理的,按照编号进行从小到大执行。 -|image6| +|image7| 这个启动顺序相当重要,在 CentOS6.x 上会因此出现问题 @@ -254,11 +256,12 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190514202013.png -.. |image1| image:: http://image.python-online.cn/20190514202442.png -.. |image2| image:: http://image.python-online.cn/20190514202736.png -.. |image3| image:: http://image.python-online.cn/20190514203612.png -.. |image4| image:: http://image.python-online.cn/20190430204707.png -.. |image5| image:: http://image.python-online.cn/20190430204933.png -.. |image6| image:: http://image.python-online.cn/20190430205449.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190514202013.png +.. |image2| image:: http://image.python-online.cn/20190514202442.png +.. |image3| image:: http://image.python-online.cn/20190514202736.png +.. |image4| image:: http://image.python-online.cn/20190514203612.png +.. |image5| image:: http://image.python-online.cn/20190430204707.png +.. |image6| image:: http://image.python-online.cn/20190430204933.png +.. |image7| image:: http://image.python-online.cn/20190430205449.png diff --git a/source/c08/c08_09.md b/source/c08/c08_09.md index 7c9cc13..86b5adb 100644 --- a/source/c08/c08_09.md +++ b/source/c08/c08_09.md @@ -1,5 +1,7 @@ # 8.9 从0到1:全面理解RPC远程调用 +![](http://image.iswbm.com/20200602135014.png) + 什么是RPC呢? 百度百科给出的解释是这样的:“RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议”。这个概念听起来还是比较抽象,没关系,继续往后看,后面概念性的东西,我会讲得足够清楚,让你完全掌握 RPC 的基础内容。在后面的篇章中还会结合其在 OpenStack 中实际应用,一步一步揭开 rpc 的神秘面纱。 diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index 2c1d900..3409f00 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -1,6 +1,8 @@ 8.9 从0到1:全面理解RPC远程调用 =============================== +|image0| + 什么是RPC呢? 百度百科给出的解释是这样的:“RPC(Remote Procedure Call @@ -239,7 +241,7 @@ SimpleXMLRPCServer 标准库来进行编写的。 server = jsonrpclib.Server("http://localhost:8080") -|image0| +|image1| 再来看第二种python-jsonrpc,写起来貌似有些复杂。 @@ -275,7 +277,7 @@ SimpleXMLRPCServer 标准库来进行编写的。 url="http://localhost:8080/jsonrpc" ) -|image1| +|image2| 还记得上面我提到过的 zabbix API,因为我有接触过,所以也拎出来讲讲。zabbix API 也是基于 json-rpc @@ -284,7 +286,7 @@ API,因为我有接触过,所以也拎出来讲讲。zabbix API 也是基于 因为内容较多,这里只带大家打个,zabbix 是如何调用的:直接指明要调用 zabbix server 的哪个方法,要传给这个方法的参数有哪些。 -|image2| +|image3| **03、基于 zerorpc** @@ -339,11 +341,11 @@ MessagePack的,速度相对快,响应时间短,并发高。zerorpc 和 pyj c = zerorpc.Client() c.connect("tcp://127.0.0.1:4242") -|image3| +|image4| 客户端除了可以使用zerorpc框架实现代码调用之外,它还支持使用“命令行”的方式调用。 -|image4| +|image5| 客户端可以使用命令行,那服务端是不是也可以呢? @@ -362,7 +364,7 @@ MessagePack的,速度相对快,响应时间短,并发高。zerorpc 和 pyj zerorpc --client --connect tcp://127.0.0.1:1234 strftime %Y/%m/%d -|image5| +|image6| 8.9.3 往rpc中引入消息中间件 --------------------------- @@ -443,7 +445,7 @@ Qpid,RabbitMQ等。 为了让你明白这几者的关系,我画了一张模型图。 -|image6| +|image7| 关于AMQP,有几下几点值得注意: @@ -518,7 +520,7 @@ Qpid,RabbitMQ等。 其他模型我不太清楚,在 OpenStack 中的应用模型是这样的 -|image7| +|image8| 至于为什么要如此设计,前面我已经给出了自己的观点。 @@ -569,7 +571,7 @@ rpc.call 和 rpc.cast transport_url=rabbit://user:passwd@host:5672 - |image8| + |image9| .. code:: python @@ -598,30 +600,30 @@ rpc.call 和 rpc.cast rpc server 要获取消息,需要定义target,就像一个门牌号一样。 - |image9| + |image10| rpc client 要发送消息,也需要有target,说明消息要发到哪去。 - |image10| + |image11| - endpoints:是可供别人远程调用的对象 RPC服务器暴露出endpoint,每个 endpoint 包涵一系列的可被远程客户端通过 transport 调用的方法。直观理解,可以参考nova-conductor创建rpc server的代码,这边的endpoints就是 - ``nova/manager.py:ConductorManager()``\ |image11| + ``nova/manager.py:ConductorManager()``\ |image12| -- dispatcher:分发器,这是 rpc server 才有的概念 |image12|\ 只有通过它 +- dispatcher:分发器,这是 rpc server 才有的概念 |image13|\ 只有通过它 server 端才知道接收到的rpc请求,要交给谁处理,怎么处理? 在client端,是这样指定要调用哪个方法的。 - |image13| + |image14| 而在server端,是如何知道要执行这个方法的呢?这就是dispatcher 要干的事,它从 endpoint 里找到这个方法,然后执行,最后返回。 - |image14| + |image15| - Serializer:在 python 对象和message(notification) 之间数据做序列化或是反序列化的基类。 @@ -737,28 +739,28 @@ rpc server 和rpc client 的四个重要方法 首先在这里先定义合法的 ``notification_event_types``\ ,相当于添加白名单。 -|image15| +|image16| 然后在调用处,使用 ``rpc.get_notifier`` 来发送消息给ceilometer。 -|image16| +|image17| 继续查看 ``rpc.get_notifier`` 做了什么事?如何实现直接info 就能发送消息的。 -|image17| +|image18| 当你使用的event_types 不在白名单内,或者是异常信息。就会给打印warn日志 -|image18| +|image19| 在rabbit里查看队列,notification 是 topic -|image19| +|image20| 而 debug ,info 等是event priority -|image20| +|image21| 参考文章: @@ -787,25 +789,26 @@ rpc server 和rpc client 的四个重要方法 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190623185008.png -.. |image1| image:: http://image.python-online.cn/20190623165341.png -.. |image2| image:: http://image.python-online.cn/20190623171138.png -.. |image3| image:: http://image.python-online.cn/20190623155955.png -.. |image4| image:: http://image.python-online.cn/20190623162725.png -.. |image5| image:: http://image.python-online.cn/20190623191042.png -.. |image6| image:: http://image.python-online.cn/20190630160025.png -.. |image7| image:: http://image.python-online.cn/20190623201427.png -.. |image8| image:: http://image.python-online.cn/20190526182125.png -.. |image9| image:: http://image.python-online.cn/20190526184854.png -.. |image10| image:: http://image.python-online.cn/20190526185217.png -.. |image11| image:: http://image.python-online.cn/20190526221219.png -.. |image12| image:: http://image.python-online.cn/20190526220809.png -.. |image13| image:: http://image.python-online.cn/20190527220820.png -.. |image14| image:: http://image.python-online.cn/20190527220012.png -.. |image15| image:: http://image.python-online.cn/20190526172514.png -.. |image16| image:: http://image.python-online.cn/20190526172725.png -.. |image17| image:: http://image.python-online.cn/20190526173314.png -.. |image18| image:: http://image.python-online.cn/20190526175100.png -.. |image19| image:: http://image.python-online.cn/20190526180708.png -.. |image20| image:: http://image.python-online.cn/20190526181433.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190623185008.png +.. |image2| image:: http://image.python-online.cn/20190623165341.png +.. |image3| image:: http://image.python-online.cn/20190623171138.png +.. |image4| image:: http://image.python-online.cn/20190623155955.png +.. |image5| image:: http://image.python-online.cn/20190623162725.png +.. |image6| image:: http://image.python-online.cn/20190623191042.png +.. |image7| image:: http://image.python-online.cn/20190630160025.png +.. |image8| image:: http://image.python-online.cn/20190623201427.png +.. |image9| image:: http://image.python-online.cn/20190526182125.png +.. |image10| image:: http://image.python-online.cn/20190526184854.png +.. |image11| image:: http://image.python-online.cn/20190526185217.png +.. |image12| image:: http://image.python-online.cn/20190526221219.png +.. |image13| image:: http://image.python-online.cn/20190526220809.png +.. |image14| image:: http://image.python-online.cn/20190527220820.png +.. |image15| image:: http://image.python-online.cn/20190527220012.png +.. |image16| image:: http://image.python-online.cn/20190526172514.png +.. |image17| image:: http://image.python-online.cn/20190526172725.png +.. |image18| image:: http://image.python-online.cn/20190526173314.png +.. |image19| image:: http://image.python-online.cn/20190526175100.png +.. |image20| image:: http://image.python-online.cn/20190526180708.png +.. |image21| image:: http://image.python-online.cn/20190526181433.png diff --git a/source/c08/c08_10.md b/source/c08/c08_10.md index f2e3acc..b5fdd36 100644 --- a/source/c08/c08_10.md +++ b/source/c08/c08_10.md @@ -1,5 +1,7 @@ # 8.16 修改 KVM 镜像文件的三种方法 +![](http://image.iswbm.com/20200602135014.png) + 如下工具的更多说明,请查看官方文档:http://libguestfs.org/guestfs-recipes.1.html ## 8.16.1 使用 guestfish diff --git a/source/c08/c08_10.rst b/source/c08/c08_10.rst index 87f65c6..46e08f3 100644 --- a/source/c08/c08_10.rst +++ b/source/c08/c08_10.rst @@ -1,12 +1,14 @@ 8.16 修改 KVM 镜像文件的三种方法 ================================ +|image0| + 如下工具的更多说明,请查看官方文档:http://libguestfs.org/guestfs-recipes.1.html 8.16.1 使用 guestfish --------------------- -|image0| +|image1| guestfish 里的命令有限,大概会 600 多个,不能 cd,操作文件时,需要使用绝对路径。 @@ -14,16 +16,17 @@ cd,操作文件时,需要使用绝对路径。 8.16.2 使用 guestmount ---------------------- -|image1| +|image2| 使用完后,记得 umount /home/wangbm/mount 8.16.3 使用 virt-\* 命令 ------------------------ -|image2| +|image3| -.. |image0| image:: http://image.python-online.cn/20191111112221.png -.. |image1| image:: http://image.python-online.cn/20191111112421.png -.. |image2| image:: http://image.python-online.cn/20191111112548.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20191111112221.png +.. |image2| image:: http://image.python-online.cn/20191111112421.png +.. |image3| image:: http://image.python-online.cn/20191111112548.png diff --git a/source/c08/c08_11.md b/source/c08/c08_11.md index cecf80d..d359250 100644 --- a/source/c08/c08_11.md +++ b/source/c08/c08_11.md @@ -1,5 +1,7 @@ # 8.11 OpenStack 问题排查 +![](http://image.iswbm.com/20200602135014.png) + ## 8.11.1 虚拟机启动不了 问题描述 diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index 44680f0..8560483 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -1,6 +1,8 @@ 8.11 OpenStack 问题排查 ======================= +|image0| + 8.11.1 虚拟机启动不了 --------------------- @@ -14,7 +16,7 @@ ``/var/log/messages`` 报错如下 -|image0| +|image1| 解决方法 @@ -87,5 +89,6 @@ ConfigDrive 是一个 iso9660 格式的文件,只读。 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190530175817.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190530175817.png diff --git a/source/c08/c08_12.md b/source/c08/c08_12.md index aac31f2..17c67f1 100644 --- a/source/c08/c08_12.md +++ b/source/c08/c08_12.md @@ -1,5 +1,7 @@ # 8.12 OpenStack之主机调度 +![](http://image.iswbm.com/20200602135014.png) + 一般情况下,一个 OpenStack 中,会部署有许多个计算节点。当我们创建一个虚拟机时,OpenStack 如何决定要将我们的虚拟机创建在哪里呢?这就是 openstack-nova-scheduler 要做的事。 顾名思义,它是对集群内的所有计算节点的资源情况进行比较。主要分为两个过程: diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index 5057fe5..ea1c18e 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -1,6 +1,8 @@ 8.12 OpenStack之主机调度 ======================== +|image0| + 一般情况下,一个 OpenStack 中,会部署有许多个计算节点。当我们创建一个虚拟机时,OpenStack 如何决定要将我们的虚拟机创建在哪里呢?这就是 openstack-nova-scheduler @@ -25,12 +27,12 @@ nova-conductor,这时候 nova-conductor 才会去调用 nova-compute nova-compute 发创建请求前,会先让 nova-scheduler 选出一台资源充足的计算节点。 -|image0| +|image1| nova-scheduler 的调度主要由两部分组成(nova/scheduler/filter_scheduler.py:FilterScheduler._schedule()) -|image1| +|image2| - 过滤器:filter,将不满足条件(硬性条件,比如内存,cpu,磁盘,pci设备等)的计算节点,直接过滤掉。意义:从过滤器出来的那些计算节点,理论上都可以创建虚拟机。 - 称重器:weigher,对满足硬性条件的众多主机按照一定的规则进行权重配比。意义:经过称重器计算,选出你更希望在哪台节点上创建虚拟机。 @@ -44,25 +46,25 @@ nova-scheduler compute 的 id,可以在 ``_update_from_compute_node`` 函数中添加。它会从compute_nodes 表中取得你想要的信息。 - |image2| + |image3| - spec_obj:你所要请求创建的虚拟机信息(模板,镜像等)。它是从 objects.RequestSpec.from_primitives 中取得的 - |image3| + |image4| 过滤器,它的代码如下: -|image4| +|image5| 称重器,它的规则主要看这段代码。 -|image5| +|image6| 我在代码中,加了几段日志。从左到右,三个不同颜色的内容分别为,原始权值,配重系数(越高说明越占比越大,越影响最终结果),经过 nomalize 后的权值(只有 0 和 1)。 -|image6| +|image7| 那最终的权值如何计算呢? @@ -82,7 +84,7 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 当指定宿主机进行虚拟机的创建后,以上所有的过滤器都会无效(不会走代码)。 -|image7| +|image8| -------------- @@ -91,12 +93,13 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190424212211.png -.. |image1| image:: http://image.python-online.cn/20190424213430.png -.. |image2| image:: http://image.python-online.cn/20190424214653.png -.. |image3| image:: http://image.python-online.cn/20190424214540.png -.. |image4| image:: http://image.python-online.cn/20190424221602.png -.. |image5| image:: http://image.python-online.cn/20190424215735.png -.. |image6| image:: http://image.python-online.cn/20190424220008.png -.. |image7| image:: http://image.python-online.cn/20191011103832.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190424212211.png +.. |image2| image:: http://image.python-online.cn/20190424213430.png +.. |image3| image:: http://image.python-online.cn/20190424214653.png +.. |image4| image:: http://image.python-online.cn/20190424214540.png +.. |image5| image:: http://image.python-online.cn/20190424221602.png +.. |image6| image:: http://image.python-online.cn/20190424215735.png +.. |image7| image:: http://image.python-online.cn/20190424220008.png +.. |image8| image:: http://image.python-online.cn/20191011103832.png diff --git a/source/c08/c08_13.md b/source/c08/c08_13.md index 7b715cc..8e26536 100644 --- a/source/c08/c08_13.md +++ b/source/c08/c08_13.md @@ -1,5 +1,7 @@ # 8.13 网络知识必知必会 +![](http://image.iswbm.com/20200602135014.png) + ## 8.13.1 iptables 推荐文章:[朱双印的 iptables 系列文章](http://www.zsythink.net/archives/tag/iptables/) diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index 1633296..a85b399 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -1,6 +1,8 @@ 8.13 网络知识必知必会 ===================== +|image0| + 8.13.1 iptables --------------- @@ -46,7 +48,7 @@ start启动iptables“服务”,但是其实准确的来说,iptables并没 **动作** -|image0| +|image1| 命令相关 ~~~~~~~~ @@ -77,7 +79,7 @@ start启动iptables“服务”,但是其实准确的来说,iptables并没 多出来的几个字段如何理解呢?我摘取《\ `朱双印的个人日志 `__\ 》的解释到这边。 -|image1| +|image2| iptables 默认为我们配置了域名反解(根据ip解析成域名),这个过程效率很低,我们可以指定参数 @@ -188,7 +190,7 @@ iptables # 那如何更改默认动作呢? iptables -t filter -P FORWARD DROP -|image2| +|image3| 保存规则 @@ -228,7 +230,7 @@ table,主要是 ip与mac地址的映射。如果在 /etc/hosts ``arp -a`` 实现相同功能,只是 ``-a`` 是标准输出格式,没有像使用 ``-e`` 一样类似表格一样的输出效果。 -|image3| +|image4| 此时,我们ping一下 172.20.22.3 这个ip,显然是可以的,因为这里的其对应的mac地址是真实准确的。 @@ -317,8 +319,9 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190706114314.png -.. |image1| image:: http://image.python-online.cn/20190706093904.png -.. |image2| image:: http://image.python-online.cn/20190706160632.png -.. |image3| image:: http://image.python-online.cn/20190804162402.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190706114314.png +.. |image2| image:: http://image.python-online.cn/20190706093904.png +.. |image3| image:: http://image.python-online.cn/20190706160632.png +.. |image4| image:: http://image.python-online.cn/20190804162402.png diff --git a/source/c08/c08_14.md b/source/c08/c08_14.md index dd83ae8..4256cae 100644 --- a/source/c08/c08_14.md +++ b/source/c08/c08_14.md @@ -1,5 +1,7 @@ # 8.14 支持 IPv6以及多运营商 +![](http://image.iswbm.com/20200602135014.png) + ## 8.14.1 手工如何配置 IPv6 使用命令设置临时的IPv6 diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index bfd3507..ccaa3ab 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -1,6 +1,8 @@ 8.14 支持 IPv6以及多运营商 ========================== +|image0| + 8.14.1 手工如何配置 IPv6 ------------------------ @@ -109,7 +111,7 @@ 信息写入ConfigDrive,也只有这样cloudinit才能自动为我们配置上 IPv6 的 ip。 -|image0| +|image1| 而对于 Neutron 来说,要使用 IPv6,当然要先创建一个 IPv6 的子网,这里要注意,IPv6 @@ -192,11 +194,11 @@ Port 去创建虚拟机,二是,修改 nova-api 的接口,使之支持。 在一张网卡上配置多个版本的IP(一个IPv4和一个IPv6),只在一个配置文件中配置就可以支持,因此cloudinit的处理的时候,会将同一个tap设备的归为一个同一张网卡。 -|image1| +|image2| 也就是说,cloudinit本身很轻松地就可以支持单网卡多版本IP的配置。 -|image2| +|image3| 而对于多线的IP,由于 Nova 写入ConfigDrive时,尽管一个Port上有多个IPv4或者多个IPv6,同个版本的都会只取第一个写入,自然从cloudinit那侧就无法进一步操作。 @@ -229,7 +231,7 @@ Port 去创建虚拟机,二是,修改 nova-api 的接口,使之支持。 }], - Neutron 接口可以不改,因为创建Port的接口参数中的 fixed_ips - 是一个数组类型,所以我们可以把这些版本信息,运营商信息放进这个数组的每一个元素中。\ |image3| + 是一个数组类型,所以我们可以把这些版本信息,运营商信息放进这个数组的每一个元素中。\ |image4| 所以我们只要修改Neutron 创建 Port 的代码逻辑。 @@ -243,7 +245,7 @@ Port 去创建虚拟机,二是,修改 nova-api 的接口,使之支持。 最后另外再说一点无关紧要的,cloudinit 解析后,每张网卡一个ip与一张网卡两个ip的区别如下。 -|image4| +|image5| 重装 ``openvswith.ko`` 过程 @@ -256,40 +258,40 @@ Port 去创建虚拟机,二是,修改 nova-api 的接口,使之支持。 CentOS 6.x 在使用 ipv6 方面有问题 -|image5| +|image6| 导致在网卡配置文件里的配置的ip 都为none -|image6| +|image7| 上面的 callbak 是 这个函数 -|image7| +|image8| centos6.x 的网络信息不是从 latest 里的 network_data.json 里读取的,而是从 content/0000 里读取 -|image8| +|image9| 这是因为 centos 6.x 配置网络是在 local 阶段做的,而local阶段做的话,就会从 content/0000 -|image9| +|image10| 而 centos7.x 其实也会走 on_first_boot 去配置网络,但是cloudinit 在centos7.x 里 sources.DSMODE_PASS -|image10| +|image11| 导致在 local 阶段,这里配置网络被pass掉,自然也就不会从 content/0000 读取了。 -|image11| +|image12| centos 6.x 配置网络是在 on_first_boot 函数里,这是 local 阶段就会执行的。 -|image12| +|image13| 8.14.5 ubuntu禁用ipv6 --------------------- @@ -297,7 +299,7 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local 如果你给你的ubuntu配置上了ipv6的ip,那当你使用 apt-get install 软件包的时候,会使用ipv6去源安装,这将会使你的安装过程卡住: -|image13| +|image14| 如何解决这个问题呢? @@ -328,18 +330,19 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20190716175250.png -.. |image1| image:: http://image.python-online.cn/20190716180655.png -.. |image2| image:: http://image.python-online.cn/20190716180952.png -.. |image3| image:: http://image.python-online.cn/20190804110647.png -.. |image4| image:: http://image.python-online.cn/20190716180726.png -.. |image5| image:: http://image.python-online.cn/20190829103805.png -.. |image6| image:: http://image.python-online.cn/20190829104544.png -.. |image7| image:: http://image.python-online.cn/20190829104806.png -.. |image8| image:: http://image.python-online.cn/20190829110541.png -.. |image9| image:: http://image.python-online.cn/20190829105558.png -.. |image10| image:: http://image.python-online.cn/20190829112446.png -.. |image11| image:: http://image.python-online.cn/20190829111917.png -.. |image12| image:: http://image.python-online.cn/20190829161243.png -.. |image13| image:: http://image.python-online.cn/20190926171038.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190716175250.png +.. |image2| image:: http://image.python-online.cn/20190716180655.png +.. |image3| image:: http://image.python-online.cn/20190716180952.png +.. |image4| image:: http://image.python-online.cn/20190804110647.png +.. |image5| image:: http://image.python-online.cn/20190716180726.png +.. |image6| image:: http://image.python-online.cn/20190829103805.png +.. |image7| image:: http://image.python-online.cn/20190829104544.png +.. |image8| image:: http://image.python-online.cn/20190829104806.png +.. |image9| image:: http://image.python-online.cn/20190829110541.png +.. |image10| image:: http://image.python-online.cn/20190829105558.png +.. |image11| image:: http://image.python-online.cn/20190829112446.png +.. |image12| image:: http://image.python-online.cn/20190829111917.png +.. |image13| image:: http://image.python-online.cn/20190829161243.png +.. |image14| image:: http://image.python-online.cn/20190926171038.png diff --git a/source/c08/c08_15.md b/source/c08/c08_15.md index 1e13ac6..c1b1bfb 100644 --- a/source/c08/c08_15.md +++ b/source/c08/c08_15.md @@ -1,5 +1,7 @@ # 8.15 Neutron 源码解读 +![](http://image.iswbm.com/20200602135014.png) + neutron api 的入口是在这里![](http://image.python-online.cn/20190804111844.png) 在这里会校验并打印请求的信息![](http://image.python-online.cn/20190804111715.png) diff --git a/source/c08/c08_15.rst b/source/c08/c08_15.rst index a063773..68fb625 100644 --- a/source/c08/c08_15.rst +++ b/source/c08/c08_15.rst @@ -1,29 +1,31 @@ 8.15 Neutron 源码解读 ===================== -neutron api 的入口是在这里\ |image0| +|image0| -在这里会校验并打印请求的信息\ |image1| +neutron api 的入口是在这里\ |image1| -而对网络、子网、port操作的逻辑处理代码的入口都是从这里开始的\ |image2| +在这里会校验并打印请求的信息\ |image2| + +而对网络、子网、port操作的逻辑处理代码的入口都是从这里开始的\ |image3| 8.15.1 创建Port --------------- -从 neutron api 请求过来后,就会经过这里\ |image3| +从 neutron api 请求过来后,就会经过这里\ |image4| -把 port 信息打印一下\ |image4| +把 port 信息打印一下\ |image5| 点进上面的\ ``self._create_port_db()``\ ,可以看到这里先是创建了一个空壳的port -|image5| +|image6| -再分配ip\ |image6| +再分配ip\ |image7| 上图有一个函数 ``_allocate_ips_for_port``\ ,相当重要,一般人只要从这里关注即可 -|image7| +|image8| 它会先根据port创建请求里的内容,去数据库中一一比对,找出符合条件的子网,当然在找的过程中,会校验请求参数的准确性,比如它是指定ip和subnet创建的port,那么会检查这个ip是否在subnet内。 @@ -33,37 +35,38 @@ neutron api 的入口是在这里\ |image0| 里。如果指定了ip,这个list里的元素就会有 ip_address,否则就只有 subnet_id。 -|image8| - |image9| +|image10| + -------------- 当不指定子网、也不指定ip时(也就是不传fixed_ip),而且 cluster 里 即 有ipv4 的子网也有ipv6的子网,这时候 neutron 会去创建一个既有ipv4也有ipv6的port,只要有一个version的子网里没有可用ip,都会失败。 -|image10| +|image11| 这里是遍历一个version的所有的子网列表,只要能找到一个子网有可用ip,就返回,如果遍历完都没有找到ip,那就要抛出异常了。 -|image11| +|image12| .. code:: python class IpAddressGenerationFailureAllSubnets(IpAddressGenerationFailure): message = _("No more IP addresses available.") -.. |image0| image:: http://image.python-online.cn/20190804111844.png -.. |image1| image:: http://image.python-online.cn/20190804111715.png -.. |image2| image:: http://image.python-online.cn/20190803181706.png -.. |image3| image:: http://image.python-online.cn/20190803182042.png -.. |image4| image:: http://image.python-online.cn/20190803182223.png -.. |image5| image:: http://image.python-online.cn/20190804091016.png -.. |image6| image:: http://image.python-online.cn/20190804091226.png -.. |image7| image:: http://image.python-online.cn/20190804094131.png -.. |image8| image:: http://image.python-online.cn/20190804092214.png -.. |image9| image:: http://image.python-online.cn/20190804091911.png -.. |image10| image:: http://image.python-online.cn/20190809213209.png -.. |image11| image:: http://image.python-online.cn/20190809213223.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20190804111844.png +.. |image2| image:: http://image.python-online.cn/20190804111715.png +.. |image3| image:: http://image.python-online.cn/20190803181706.png +.. |image4| image:: http://image.python-online.cn/20190803182042.png +.. |image5| image:: http://image.python-online.cn/20190803182223.png +.. |image6| image:: http://image.python-online.cn/20190804091016.png +.. |image7| image:: http://image.python-online.cn/20190804091226.png +.. |image8| image:: http://image.python-online.cn/20190804094131.png +.. |image9| image:: http://image.python-online.cn/20190804092214.png +.. |image10| image:: http://image.python-online.cn/20190804091911.png +.. |image11| image:: http://image.python-online.cn/20190809213209.png +.. |image12| image:: http://image.python-online.cn/20190809213223.png diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index 23d9ccd..d1cfba5 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -1,5 +1,7 @@ # 9.1 情人节来了,教你使用 Python 来表白 +![](http://image.iswbm.com/20200602135014.png) + **作者**:@明哥 **公众号**:Python编程时光 diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 216c302..bcdce1d 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -1,6 +1,8 @@ 9.1 情人节来了,教你使用 Python 来表白 ====================================== +|image0| + **作者**\ :@明哥 **公众号**\ :Python编程时光 -------------- @@ -47,11 +49,11 @@ 这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 -|image0| +|image1| 使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? -|image1| +|image2| 然后将这张图片发给你的女神,具体话术你自己想咯。 @@ -75,7 +77,7 @@ 用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 -|image2| +|image3| 我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 @@ -155,7 +157,8 @@ 关注公众号,获取最新干货! -.. |image0| image:: http://image.python-online.cn/20200214104413.png -.. |image1| image:: http://image.python-online.cn/save.jpeg -.. |image2| image:: http://image.python-online.cn/20200214104646.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.python-online.cn/20200214104413.png +.. |image2| image:: http://image.python-online.cn/save.jpeg +.. |image3| image:: http://image.python-online.cn/20200214104646.png diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md index 7ef71ad..c0ac7b5 100644 --- a/source/c09/c09_02.md +++ b/source/c09/c09_02.md @@ -1,5 +1,7 @@ # 9.2 没有这 50 个APP,我的 Mac 将只是一块铁 +![](http://image.iswbm.com/20200602135014.png) + ## 1. 效率神器 ### 1. Alfred 3 diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index a31b463..aaf1d37 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -1,6 +1,8 @@ 9.2 没有这 50 个APP,我的 Mac 将只是一块铁 ========================================== +|image0| + 1. 效率神器 ----------- @@ -26,7 +28,7 @@ Mac 中必不可少的APP,没有它 Mac 基本也就『残废』了。 NewFileMenu 可以解决你的烦恼 -|image0| +|image1| 3. Magnet/Moon ~~~~~~~~~~~~~~ @@ -157,5 +159,6 @@ iMazing,最佳 iOS 备份及管理软件,完美替代 iTunes。 Macs Fan Control:控制风扇转速,加快散热 -.. |image0| image:: http://image.iswbm.com/image-20200524183640630.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/image-20200524183640630.png diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md index 64a21df..4c92424 100644 --- a/source/c09/c09_03.md +++ b/source/c09/c09_03.md @@ -1,5 +1,7 @@ # 9.3 明哥的在线工具集 +![](http://image.iswbm.com/20200602135014.png) + diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index 0e13613..d461838 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -1,6 +1,8 @@ 9.3 明哥的在线工具集 ==================== +|image0| + 文档 ---- @@ -40,3 +42,6 @@ ai-art(自拍照生成为名画艺术品):https://ai-art.tokyo/en ------ 码力全开:https://www.maliquankai.com/ + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c10/c10_01.md b/source/c10/c10_01.md index 3ec17c4..94d9bf8 100644 --- a/source/c10/c10_01.md +++ b/source/c10/c10_01.md @@ -1,5 +1,7 @@ # 10.1 网络知识扫盲:一篇文章搞懂 DNS +![](http://image.iswbm.com/20200602135014.png) + ## 1. DNS 是什么? DNS (Domain Name System 的缩写)的作用非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。 diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst index 9e52d2d..bbf6ded 100644 --- a/source/c10/c10_01.rst +++ b/source/c10/c10_01.rst @@ -1,6 +1,8 @@ 10.1 网络知识扫盲:一篇文章搞懂 DNS =================================== +|image0| + 1. DNS 是什么? --------------- @@ -60,7 +62,7 @@ DNS (Domain Name System 2. 从“顶级域名服务器”查到“次级域名服务器”的NS记录和A记录(IP地址) 3. 从“次级域名服务器”查出“主机名”的IP地址 -|image0| +|image1| 4. DNS的缓存时间 ---------------- @@ -72,7 +74,7 @@ DNS (Domain Name System Live),意思就是这个缓存可以存活多长时间,过了这个时间,本地 DNS 就会删除这条记录,删除了缓存后,你再访问,就要重新走一遍上面的流程,获取最新的地址。 -|image1| +|image2| 5. DNS 的记录类型 ----------------- @@ -103,7 +105,7 @@ Live),意思就是这个缓存可以存活多长时间,过了这个时间 后面我将使用 wireshark 抓取 DNS 的数据包,但是在开始之前 ,得先了解一下 DNS 的报文结构 -|image2| +|image3| - 事务 ID:DNS 报文的 ID 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。通过它可以区分 @@ -125,17 +127,17 @@ DNS 的报文结构 1. DNS 是应用层协议,传输层协议使用的是 UDP 2. DNS 默认端口是 53 -|image3| +|image4| 请求和应答的报文的截图我放在了下面,接下来我将逐个分析。 **请求** -|image4| +|image5| **应答** -|image5| +|image6| Transaction ID ~~~~~~~~~~~~~~ @@ -251,7 +253,7 @@ dig是一个在类Unix命令行模式下查询DNS包括NS记录,A记录,MX 通过 dig (参数:\ ``+trace``\ )命令,我们可以看到上面描述的 DNS 解析的详细过程 -|image6| +|image7| 从返回的结果,我们可以看得出几点信息 @@ -264,15 +266,15 @@ dig是一个在类Unix命令行模式下查询DNS包括NS记录,A记录,MX 如果你只想看到结果,可以使用 ``+short`` 参数,可以直接返回 www.163.com 对应着哪几个ip -|image7| +|image8| 你也可以加个 ``@`` 参数 ,指定从某个 DNS 服务器进行查询 -|image8| +|image9| 如果你只想查看指定的记录类型 -|image9| +|image10| host 命令 ~~~~~~~~~ @@ -280,14 +282,14 @@ host 命令 ``host`` 命令 可以看作\ ``dig``\ 命令的简化版本,返回当前请求域名的各种记录。 -|image10| +|image11| whois命令 ~~~~~~~~~ ``whois``\ 命令用来查看域名的注册情况。 -|image11| +|image12| nslookup命令 ~~~~~~~~~~~~ @@ -298,11 +300,11 @@ nslookup也是常用的一个查询 DNS 解析结果的工具 $ nslookup [查询的域名] [指定DNS服务器] -|image12| +|image13| 你也可以指定公网的域名服务器进行查询,比如常见的 114.114.114.114 -|image13| +|image14| 10. 手动清理本地缓存 -------------------- @@ -335,18 +337,19 @@ Linux 关注公众号,获取最新干货! -.. |image0| image:: http://image.iswbm.com/464291-20170703113844956-354755333.jpg -.. |image1| image:: http://image.iswbm.com/image-20200531141521689.png -.. |image2| image:: http://image.iswbm.com/image-20200531152824672.png -.. |image3| image:: http://image.iswbm.com/20200531175736.png -.. |image4| image:: http://image.iswbm.com/20200531175811.png -.. |image5| image:: http://image.iswbm.com/image-20200531153110621.png -.. |image6| image:: http://image.iswbm.com/image-20200531162810531.png -.. |image7| image:: http://image.iswbm.com/image-20200531164525384.png -.. |image8| image:: http://image.iswbm.com/image-20200531170427834.png -.. |image9| image:: http://image.iswbm.com/image-20200531170543250.png -.. |image10| image:: http://image.iswbm.com/image-20200531171610902.png -.. |image11| image:: http://image.iswbm.com/image-20200531171905345.png -.. |image12| image:: http://image.iswbm.com/image-20200531145109182.png -.. |image13| image:: http://image.iswbm.com/image-20200531145449577.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/464291-20170703113844956-354755333.jpg +.. |image2| image:: http://image.iswbm.com/image-20200531141521689.png +.. |image3| image:: http://image.iswbm.com/image-20200531152824672.png +.. |image4| image:: http://image.iswbm.com/20200531175736.png +.. |image5| image:: http://image.iswbm.com/20200531175811.png +.. |image6| image:: http://image.iswbm.com/image-20200531153110621.png +.. |image7| image:: http://image.iswbm.com/image-20200531162810531.png +.. |image8| image:: http://image.iswbm.com/image-20200531164525384.png +.. |image9| image:: http://image.iswbm.com/image-20200531170427834.png +.. |image10| image:: http://image.iswbm.com/image-20200531170543250.png +.. |image11| image:: http://image.iswbm.com/image-20200531171610902.png +.. |image12| image:: http://image.iswbm.com/image-20200531171905345.png +.. |image13| image:: http://image.iswbm.com/image-20200531145109182.png +.. |image14| image:: http://image.iswbm.com/image-20200531145449577.png diff --git a/source/c10/c10_02.md b/source/c10/c10_02.md index 13805cb..7371a33 100644 --- a/source/c10/c10_02.md +++ b/source/c10/c10_02.md @@ -1,5 +1,7 @@ # 10.2 网络知识扫盲:如何理解 OSI七层模型 +![](http://image.iswbm.com/20200602135014.png) + OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 ![img](https://pic4.zhimg.com/80/v2-854e3df8ea850c977c30cb1deb1f64db_1440w.jpg) diff --git a/source/c10/c10_02.rst b/source/c10/c10_02.rst index 841ba73..703aa3b 100644 --- a/source/c10/c10_02.rst +++ b/source/c10/c10_02.rst @@ -1,6 +1,8 @@ 10.2 网络知识扫盲:如何理解 OSI七层模型 ======================================= +|image0| + OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 @@ -22,7 +24,7 @@ Interconnect),即开放式系统互联。一般都叫OSI参考模型,是IS 就如下面这张图所示 -|image0| +|image1| 应用层 ------ @@ -148,5 +150,6 @@ UDP还是TCP协议,这个是用于对端在接收到这个数据包后知道 - https://juejin.im/post/59eb06b1f265da430f313c7f -.. |image0| image:: http://image.iswbm.com/20200526233356.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200526233356.png diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md index 0dd6260..5207498 100644 --- a/source/c10/c10_03.md +++ b/source/c10/c10_03.md @@ -1,5 +1,7 @@ # 10.3 网络知识扫盲:详解TCP的三次握手与四次挥手 +![](http://image.iswbm.com/20200602135014.png) + ## 1. TCP 协议是什么? diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst index 5ff4511..a2e6f63 100644 --- a/source/c10/c10_03.rst +++ b/source/c10/c10_03.rst @@ -1,6 +1,8 @@ 10.3 网络知识扫盲:详解TCP的三次握手与四次挥手 ============================================== +|image0| + 1. TCP 协议是什么? ------------------- @@ -171,7 +173,7 @@ TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15) 运行后,可以使用 lsof 命令查看 13200 端口是否处于监听中 -|image0| +|image1| **2、客户端** @@ -198,19 +200,19 @@ TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15) 一切准备就绪后,打开我们的 wireshark ,并设置捕获过滤器 port=13200 -|image1| +|image2| 然后开启抓包,最后执行上面的 客户端代码\ ``tcp_client.py``\ ,就可以在 wireshark 上看到如下内容。 -|image2| +|image3| 三次握手 ~~~~~~~~ 三次握手的过程可以参考下面这张图来帮助理解 -|image3| +|image4| 使用 wireshark 抓到的三次握手的包如下所示 @@ -247,7 +249,7 @@ ACK 包表示同意。\ **这就是第三次握手。** 首先是第一个包 -|image4| +|image5| 然后是第二个包 @@ -263,14 +265,14 @@ ACK 包表示同意。\ **这就是第三次握手。** 因为客户端没有向服务端发送数据,所以 ack 将始终为1,直到客户端要向服务端发送数据。 -|image5| +|image6| 四次挥手 ~~~~~~~~ 四次挥手的过程可以参考下面这张图来帮助理解 -|image6| +|image7| 使用 wireshark 抓到的四次挥手的包如下所示 @@ -364,7 +366,7 @@ seq +1,这和第二次挥手时是一样的。 后,很有可能过了很久还没有到达目标机器,此时,客户端会重新发送一个 SYN 包 b重新请求连接。 -|image7| +|image8| b 包比 a 包先到达了目标机器(即使a包是先发的),当目标机器收到了 b 包,就会回复给源机器一个回包,当后面 a @@ -407,7 +409,7 @@ b 包比 a 包先到达了目标机器(即使a包是先发的),当目标 b,如果只进行了两次握手,目标机器就建立了连接,那么当 b 包到达后,目标机器又会创建一个连接,而这个连接是无用的、多余的。 -|image8| +|image9| 这里仅仅假设只超时重发一次就成功了,如果超时重发了 10 次,甚至更多呢?本来TCP 传输只需要一个连接就行了,现在服务端却创建了 n @@ -431,7 +433,7 @@ b,如果只进行了两次握手,目标机器就建立了连接,那么当 和三次握手相对比,其实就是把原来第二次握手的内容拆分成两次发送。 -|image9| +|image10| 所以为什么不握手四次? @@ -474,7 +476,7 @@ Maximum Transmission Unit,最大传输单元。 其他的你可以参考 下面这张图(摘自维基百科) -|image10| +|image11| 如果上层协议(如 TCP)交给IP协议的内容实在是太多,使得 IP 报文的大小超过了 MTU ,以以太网为例,如果 IP 报文大小超过了1500 Bytes @@ -493,7 +495,7 @@ MSS 和 MTU 的关系是: MSS = MTU - IP首部大小 - TCP首部大小 -|image11| +|image12| **那为什么要规定一个最大报文长度MSS呢?** @@ -516,7 +518,7 @@ IP层是没有超时重传机制的,如果IP层对一个数据包进行了分 这里有必要说一下,面向 TCP 进行网络编程的常规步骤 -|image12| +|image13| 如果是服务端: @@ -627,7 +629,7 @@ TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 试想一下,服务端本来就没有程序监听在这个接口,因此在服务端是无法完成连接的建立过程的。我们参考三次握手的流程可以知道当客户端的SYNC包到达服务端时,TCP协议没有找到监听的套接字,就会向客户端发送一个错误的报文,告诉客户端产生了错误。而该错误报文就是一个包含RST的报文。这种异常情况也很容易模拟,我们只需要写一个小程序,连接服务器上没有监听的端口即可。如下是通过wireshark捕获的数据包,可以看到红色部分的RST报文。 -|image13| +|image14| 试图与一个某端口建立连接但该主机已经宕机(主机宕机) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -636,7 +638,7 @@ TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 如下图所示,可以看到客户端每隔一段时间就会向服务端发送一个SYNC数据包。这里面具体的时间是跟TCP协议相关的,具体时间不同的操作系统实现可能稍有不同。 -|image14| +|image15| 建立连接时,服务器应用被阻塞(或者僵死) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -672,19 +674,20 @@ TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 `近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题 `__ -.. |image0| image:: http://image.iswbm.com/image-20200601221524846.png -.. |image1| image:: http://image.iswbm.com/image-20200601222110435.png -.. |image2| image:: http://image.iswbm.com/image-20200602234904143.png -.. |image3| image:: http://image.iswbm.com/20200605130951.png -.. |image4| image:: http://image.iswbm.com/image-20200602235431620.png -.. |image5| image:: http://image.iswbm.com/image-20200602235723214.png -.. |image6| image:: http://image.iswbm.com/20200605192855.png -.. |image7| image:: http://image.iswbm.com/20200605200027.png -.. |image8| image:: http://image.iswbm.com/20200605201138.png -.. |image9| image:: http://image.iswbm.com/20200605202450.png -.. |image10| image:: http://image.iswbm.com/image-20200604204657243.png -.. |image11| image:: http://image.iswbm.com/tcp_pdus.png -.. |image12| image:: http://image.iswbm.com/20200605204727.png -.. |image13| image:: http://image.iswbm.com/image-20200604223625787.png -.. |image14| image:: http://image.iswbm.com/image-20200604224127512.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/image-20200601221524846.png +.. |image2| image:: http://image.iswbm.com/image-20200601222110435.png +.. |image3| image:: http://image.iswbm.com/image-20200602234904143.png +.. |image4| image:: http://image.iswbm.com/20200605130951.png +.. |image5| image:: http://image.iswbm.com/image-20200602235431620.png +.. |image6| image:: http://image.iswbm.com/image-20200602235723214.png +.. |image7| image:: http://image.iswbm.com/20200605192855.png +.. |image8| image:: http://image.iswbm.com/20200605200027.png +.. |image9| image:: http://image.iswbm.com/20200605201138.png +.. |image10| image:: http://image.iswbm.com/20200605202450.png +.. |image11| image:: http://image.iswbm.com/image-20200604204657243.png +.. |image12| image:: http://image.iswbm.com/tcp_pdus.png +.. |image13| image:: http://image.iswbm.com/20200605204727.png +.. |image14| image:: http://image.iswbm.com/image-20200604223625787.png +.. |image15| image:: http://image.iswbm.com/image-20200604224127512.png diff --git a/source/c10/c10_05.md b/source/c10/c10_05.md index 86ee149..67386e8 100644 --- a/source/c10/c10_05.md +++ b/source/c10/c10_05.md @@ -1,5 +1,7 @@ # 10.5 Wireshark 抓包教程 +![](http://image.iswbm.com/20200602135014.png) + ## 过滤器 diff --git a/source/c10/c10_05.rst b/source/c10/c10_05.rst index d805256..a42d0f4 100644 --- a/source/c10/c10_05.rst +++ b/source/c10/c10_05.rst @@ -1,6 +1,8 @@ 10.5 Wireshark 抓包教程 ======================= +|image0| + 过滤器 ------ @@ -31,3 +33,6 @@ http.request.method == “GET”,只显示 HTTP GET 方法的。 AND/OR 也可以写成 ``&&`` / ``||`` ``Wireshark`` 只能查看封包,而不能修改封包的内容,或者发送封包。 + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c10/c10_06.md b/source/c10/c10_06.md index 38cc080..23b1ffc 100644 --- a/source/c10/c10_06.md +++ b/source/c10/c10_06.md @@ -1,5 +1,7 @@ # 10.6 通过比较,学习 TCP 与 UDP +![](http://image.iswbm.com/20200602135014.png) + ## 1. TCP 与 UDP 的区别 **1. 连接** diff --git a/source/c10/c10_06.rst b/source/c10/c10_06.rst index c895104..45de435 100644 --- a/source/c10/c10_06.rst +++ b/source/c10/c10_06.rst @@ -1,6 +1,8 @@ 10.6 通过比较,学习 TCP 与 UDP ============================== +|image0| + 1. TCP 与 UDP 的区别 -------------------- @@ -35,3 +37,6 @@ TCP 首部长度较长,且是可变长的,会有一定的开销。 UDP 首部固定只有 8 个字节,开销较小。 `面向报文(UDP)和面向字节流(TCP)的区别 `__ + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + From 40f52e3d5442976161aba8112043b91316ebe332 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 6 Jun 2020 18:35:43 +0800 Subject: [PATCH 078/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + source/c01/c01_02.md | 160 +++++++++++++ source/c01/c01_02.rst | 208 +++++++++++++++- source/c03/c03_07.md | 508 +++++++++++++++++++++++++++++++++++++++ source/c03/c03_07.rst | 535 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1411 insertions(+), 2 deletions(-) create mode 100644 source/c01/c01_02.md create mode 100644 source/c03/c03_07.md create mode 100644 source/c03/c03_07.rst diff --git a/README.md b/README.md index 4a307ec..33bf271 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ 关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python.iswbm.com/en/latest/c04/c04_03.html) ## 第一章:基础知识 - 1.1 [13条Python2.x和3.x的区别?](http://python.iswbm.com/en/latest/c01/c01_01.html) +- 1.2 [Python 自省机制详解](http://python.iswbm.com/en/latest/c01/c01_02.html) - 1.4 [什么是猴子补丁?](http://python.iswbm.com/en/latest/c01/c01_04.html) - 1.5 [深入闭包与变量作用域](http://python.iswbm.com/en/latest/c01/c01_05.html) - 1.6 [深入理解元组存在的意义](http://python.iswbm.com/en/latest/c01/c01_06.html) @@ -68,6 +69,7 @@ - 3.4 [Django+Uwsgi部署网站](http://python.iswbm.com/en/latest/c03/c03_04.html) - 3.5 [源码解读:Flask上下文与代理模式](http://python.iswbm.com/en/latest/c03/c03_05.html) - 3.6 [Web开发者必看:理解WSGI](http://python.iswbm.com/en/latest/c03/c03_06.html) +- 3.7 [超实用10 条 Python使用技巧](http://python.iswbm.com/en/latest/c03/c03_07.html) ## 第四章:开发工具 - 4.1 [虚拟环境:virtualenv](http://python.iswbm.com/en/latest/c04/c04_01.html) diff --git a/source/c01/c01_02.md b/source/c01/c01_02.md new file mode 100644 index 0000000..a53cbc9 --- /dev/null +++ b/source/c01/c01_02.md @@ -0,0 +1,160 @@ +# 1.2 Python 自省机制详解 + +自省,在我们日常生活中,通常是自我反省的意思。 + +但在计算机编程中,自省并不是这个意思,它的英文单词是 introspection,表示的是自我检查的行为或能力。 + +它的内容包括 + +1. 告诉别人,我是谁 +2. 告诉别人,我能做什么 + +(有点面试的感觉了) + +Python 是一门动态语言,有了自省,就能让程序在运行时能够获知对象的类型以及该对象下有哪些方法等。 + +## 1. 学习 Python 模块的入口 + +### help() + +在 console 模式下,输入 `help()` ,可以看到输出了一段帮助文档,教你如何使用这个 help,当你看到提示符变成了 `help>` 时,这时候就进入了 help 模式。 + +![image-20200606121047415](/Users/MING/Library/Application Support/typora-user-images/image-20200606121047415.png) + +此时你可以键入你想要了解的模块、语法等,help 告诉你如何使用。 + +比如我输入 keywords ,就可以看到 Python 里所有的关键字。再输入 modules 就可以查看 Python 中所有的内置模块。 + +![image-20200606121544062](/Users/MING/Library/Application Support/typora-user-images/image-20200606121544062.png) + +输入 modules + `指定包名`,就可以查看这个包下有哪些模块 + +![image-20200606121942898](/Users/MING/Library/Application Support/typora-user-images/image-20200606121942898.png) + +如果你想学习某个包要如何使用,可以直接在 help 模式下输入 `包名`,就像下面这样,我就可以获得一份 json 的帮助文档。 + +![image-20200606122408522](/Users/MING/Library/Application Support/typora-user-images/image-20200606122408522.png) + +如果你想学习某个关键字的用法,可以在 help 模式下直接键入 `关键字` 查询用法,比如我直接键入 `for` 。 + +![image-20200606133933401](/Users/MING/Library/Application Support/typora-user-images/image-20200606133933401.png) + +查完后,使用 quit 就可以退出 help 模式了。 + +![image-20200606123145109](/Users/MING/Library/Application Support/typora-user-images/image-20200606123145109.png) + +如果你觉得进入 help 模式太麻烦,可以在 console 模式下直接查询 + +```python +>>> help("json") +``` + + + +### dir() + +dir() 函数可能是 Python 自省机制中最著名的部分了。它返回传递给它的任何对象的属性名称经过排序的列表。如果不指定对象,则 dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 keyword 模块,并观察它揭示了什么: + +![image-20200606134519352](/Users/MING/Library/Application Support/typora-user-images/image-20200606134519352.png) + +### \__doc__ + +使用 `__doc__` 这个魔法方法,可以查询该模块的文档,它输出的内容和 help() 一样。 + +![image-20200606134858285](/Users/MING/Library/Application Support/typora-user-images/image-20200606134858285.png) + +## 2. 应用到实际开发中 + +### type() + +type() 函数有助于我们确定对象是字符串还是整数,或是其它类型的对象。它通过返回类型对象来做到这一点,可以将这个类型对象与 types 模块中定义的类型相比较: + +```python +>>> type(42) + +>>> type([]) + +``` + +### hasattr() + +使用 dir() 函数会返回一个对象的属性列表。 + +但是,有时我们只想测试一个或多个属性是否存在。如果对象具有我们正在考虑的属性,那么通常希望只检索该属性。这个任务可以由 hasattr() 来完成. + +```python +>>> import json +>>> hasattr(json, "dumps") +True +>>> +``` + +### getattr() + +使用 hasattr 获知了对象拥有某个属性后,可以搭配 getattr() 函数来获取其属性值。 + +```python +>>> getattr(json, "__path__") +['/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json'] +>>> +``` + +使用 getattr 获取函数后,可以很方便地使用这个函数,比如下面这样,可以不再使写 json.dumps 这么字。 + +```python +>>> dumps = getattr(json, "dumps") +>>> dumps({"name": "MING"}) +'{"name": "MING"}' +>>> + +# 当然你还有更简单的方法 +>>> mydumps = json.dumps +>>> mydumps({"name": "MING"}) +'{"name": "MING"}' +``` + +### id() + +**id()** 函数返回对象的唯一标识符,标识符是一个整数。 + +```python +>>> a = "hello" +>>> b = "world" +>>> +>>> id(a) +4470767944 +>>> id(b) +4499487408 +>>> +``` + + + +### isinstance() + +使用 isinstance() 函数可以确定一个对象是否是某个特定类型或定制类的实例。 + +```python +>>> isinstance("python", str) +True +>>> isinstance(10, int) +True +>>> isinstance(False, bool) +True +``` + + + +### callable() + +使用 callable 可以确定一个对象是否是可调用的,比如函数,类这些对象都是可以调用的对象。 + +```python +>>> callable("hello") +False +>>> +>>> callable(str) +True +>>> +``` + diff --git a/source/c01/c01_02.rst b/source/c01/c01_02.rst index da2afa2..b4a9800 100755 --- a/source/c01/c01_02.rst +++ b/source/c01/c01_02.rst @@ -1,3 +1,207 @@ -1.2 Python的自省机制 -================================ +1.2 Python 自省机制详解 +======================= +自省,在我们日常生活中,通常是自我反省的意思。 + +但在计算机编程中,自省并不是这个意思,它的英文单词是 +introspection,表示的是自我检查的行为或能力。 + +它的内容包括 + +1. 告诉别人,我是谁 +2. 告诉别人,我能做什么 + +(有点面试的感觉了) + +Python +是一门动态语言,有了自省,就能让程序在运行时能够获知对象的类型以及该对象下有哪些方法等。 + +1. 学习 Python 模块的入口 +------------------------- + +help() +~~~~~~ + +在 console 模式下,输入 ``help()`` +,可以看到输出了一段帮助文档,教你如何使用这个 +help,当你看到提示符变成了 ``help>`` 时,这时候就进入了 help 模式。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606121047415.png + :alt: image-20200606121047415 + + image-20200606121047415 + +此时你可以键入你想要了解的模块、语法等,help 告诉你如何使用。 + +比如我输入 keywords ,就可以看到 Python 里所有的关键字。再输入 modules +就可以查看 Python 中所有的内置模块。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606121544062.png + :alt: image-20200606121544062 + + image-20200606121544062 + +输入 modules + ``指定包名``\ ,就可以查看这个包下有哪些模块 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606121942898.png + :alt: image-20200606121942898 + + image-20200606121942898 + +如果你想学习某个包要如何使用,可以直接在 help 模式下输入 +``包名``\ ,就像下面这样,我就可以获得一份 json 的帮助文档。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606122408522.png + :alt: image-20200606122408522 + + image-20200606122408522 + +如果你想学习某个关键字的用法,可以在 help 模式下直接键入 ``关键字`` +查询用法,比如我直接键入 ``for`` 。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606133933401.png + :alt: image-20200606133933401 + + image-20200606133933401 + +查完后,使用 quit 就可以退出 help 模式了。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606123145109.png + :alt: image-20200606123145109 + + image-20200606123145109 + +如果你觉得进入 help 模式太麻烦,可以在 console 模式下直接查询 + +.. code:: python + + >>> help("json") + +dir() +~~~~~ + +dir() 函数可能是 Python +自省机制中最著名的部分了。它返回传递给它的任何对象的属性名称经过排序的列表。如果不指定对象,则 +dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 keyword +模块,并观察它揭示了什么: + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606134519352.png + :alt: image-20200606134519352 + + image-20200606134519352 + +\__doc_\_ +~~~~~~~~~ + +使用 ``__doc__`` 这个魔法方法,可以查询该模块的文档,它输出的内容和 +help() 一样。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606134858285.png + :alt: image-20200606134858285 + + image-20200606134858285 + +2. 应用到实际开发中 +------------------- + +type() +~~~~~~ + +type() +函数有助于我们确定对象是字符串还是整数,或是其它类型的对象。它通过返回类型对象来做到这一点,可以将这个类型对象与 +types 模块中定义的类型相比较: + +.. code:: python + + >>> type(42) + + >>> type([]) + + +hasattr() +~~~~~~~~~ + +使用 dir() 函数会返回一个对象的属性列表。 + +但是,有时我们只想测试一个或多个属性是否存在。如果对象具有我们正在考虑的属性,那么通常希望只检索该属性。这个任务可以由 +hasattr() 来完成. + +.. code:: python + + >>> import json + >>> hasattr(json, "dumps") + True + >>> + +getattr() +~~~~~~~~~ + +使用 hasattr 获知了对象拥有某个属性后,可以搭配 getattr() +函数来获取其属性值。 + +.. code:: python + + >>> getattr(json, "__path__") + ['/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json'] + >>> + +使用 getattr +获取函数后,可以很方便地使用这个函数,比如下面这样,可以不再使写 +json.dumps 这么字。 + +.. code:: python + + >>> dumps = getattr(json, "dumps") + >>> dumps({"name": "MING"}) + '{"name": "MING"}' + >>> + + # 当然你还有更简单的方法 + >>> mydumps = json.dumps + >>> mydumps({"name": "MING"}) + '{"name": "MING"}' + +id() +~~~~ + +**id()** 函数返回对象的唯一标识符,标识符是一个整数。 + +.. code:: python + + >>> a = "hello" + >>> b = "world" + >>> + >>> id(a) + 4470767944 + >>> id(b) + 4499487408 + >>> + +isinstance() +~~~~~~~~~~~~ + +使用 isinstance() 函数可以确定一个对象是否是某个特定类型或定制类的实例。 + +.. code:: python + + >>> isinstance("python", str) + True + >>> isinstance(10, int) + True + >>> isinstance(False, bool) + True + +callable() +~~~~~~~~~~ + +使用 callable +可以确定一个对象是否是可调用的,比如函数,类这些对象都是可以调用的对象。 + +.. code:: python + + >>> callable("hello") + False + >>> + >>> callable(str) + True + >>> diff --git a/source/c03/c03_07.md b/source/c03/c03_07.md new file mode 100644 index 0000000..a6ffa20 --- /dev/null +++ b/source/c03/c03_07.md @@ -0,0 +1,508 @@ +# 3.7 超实用10 条 Python使用技巧 + +![](http://image.iswbm.com/20200602135014.png) + +## 1. 如何在运行状态查看源代码? + +查看函数的源代码,我们通常会使用 IDE 来完成。 + +比如在 PyCharm 中,你可以 Ctrl + 鼠标点击 进入函数的源代码。 + +那如果没有 IDE 呢? + +当我们想使用一个函数时,如何知道这个函数需要接收哪些参数呢? + +当我们在使用函数时出现问题的时候,如何通过阅读源代码来排查问题所在呢? + + + +这时候,我们可以使用 inspect 来代替 IDE 帮助你完成这些事 + +```python +# demo.py +import inspect + + +def add(x, y): + return x + y + +print("===================") +print(inspect.getsource(add)) +``` + +运行结果如下 + +```shell +$ python demo.py +=================== +def add(x, y): + return x + y +``` + + + + + +## 2. 如何关闭异常自动关联上下文? + +当你在处理异常时,由于处理不当或者其他问题,再次抛出另一个异常时,往外抛出的异常也会携带原始的异常信息。 + +就像这样子。 + +```python +try: + print(1 / 0) +except Exception as exc: + raise RuntimeError("Something bad happened") +``` + +从输出可以看到两个异常信息 + +```python +Traceback (most recent call last): + File "demo.py", line 2, in + print(1 / 0) +ZeroDivisionError: division by zero + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "demo.py", line 4, in + raise RuntimeError("Something bad happened") +RuntimeError: Something bad happened +``` + +如果在异常处理程序或 finally 块中引发异常,默认情况下,异常机制会隐式工作会将先前的异常附加为新异常的 `__context__`属性。这就是 Python 默认开启的自动关联异常上下文。 + +如果你想自己控制这个上下文,可以加个 from 关键字(`from` 语法会有个限制,就是第二个表达式必须是另一个异常类或实例。),来表明你的新异常是直接由哪个异常引起的。 + +```python +try: + print(1 / 0) +except Exception as exc: + raise RuntimeError("Something bad happened") from exc +``` + +输出如下 + +```python +Traceback (most recent call last): + File "demo.py", line 2, in + print(1 / 0) +ZeroDivisionError: division by zero + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "demo.py", line 4, in + raise RuntimeError("Something bad happened") from exc +RuntimeError: Something bad happened +``` + + +当然,你也可以通过`with_traceback()`方法为异常设置上下文`__context__`属性,这也能在`traceback`更好的显示异常信息。 + +```python +try: + print(1 / 0) +except Exception as exc: + raise RuntimeError("bad thing").with_traceback(exc) +``` + + +最后,如果我想彻底关闭这个自动关联异常上下文的机制?有什么办法呢? + +可以使用 `raise...from None`,从下面的例子上看,已经没有了原始异常 + +```python +$ cat demo.py +try: + print(1 / 0) +except Exception as exc: + raise RuntimeError("Something bad happened") from None +$ +$ python demo.py +Traceback (most recent call last): + File "demo.py", line 4, in + raise RuntimeError("Something bad happened") from None +RuntimeError: Something bad happened +(PythonCodingTime) +``` + + +## 03. 最快查看包搜索路径的方式 + +当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 查看。 + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path) +['', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages'] +>>> +``` + +那有没有更快的方式呢? + +我这有一种连 console 模式都不用进入的方法呢? + +你可能会想到这种,但这本质上与上面并无区别 + +```python +[wangbm@localhost ~]$ python -c "print('\n'.join(__import__('sys').path))" + +/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg +/usr/lib/python2.7/site-packages/redis-3.0.1-py2.7.egg +/usr/lib64/python27.zip +/usr/lib64/python2.7 +/usr/lib64/python2.7/plat-linux2 +/usr/lib64/python2.7/lib-tk +/usr/lib64/python2.7/lib-old +/usr/lib64/python2.7/lib-dynload +/home/wangbm/.local/lib/python2.7/site-packages +/usr/lib64/python2.7/site-packages +/usr/lib64/python2.7/site-packages/gtk-2.0 +/usr/lib/python2.7/site-packages +``` + +这里我要介绍的是比上面两种都方便的多的方法,一行命令即可解决 + +```shell +[wangbm@localhost ~]$ python3 -m site +sys.path = [ + '/home/wangbm', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages', +] +USER_BASE: '/home/wangbm/.local' (exists) +USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) +ENABLE_USER_SITE: True +``` + +从输出你可以发现,这个列的路径会比 sys.path 更全,它包含了用户环境的目录。 + + +## 4. 将嵌套 for 循环写成单行 + +我们经常会如下这种嵌套的 for 循环代码 + +```python +list1 = range(1,3) +list2 = range(4,6) +list3 = range(7,9) +for item1 in list1: + for item2 in list2: + for item3 in list3: + print(item1+item2+item3) +``` + +这里仅仅是三个 for 循环,在实际编码中,有可能会有更层。 + +这样的代码,可读性非常的差,很多人不想这么写,可又没有更好的写法。 + +这里介绍一种我常用的写法,使用 itertools 这个库来实现更优雅易读的代码。 + +```python +from itertools import product +list1 = range(1,3) +list2 = range(4,6) +list3 = range(7,9) +for item1,item2,item3 in product(list1, list2, list3): + print(item1+item2+item3) +``` + +输出如下 + +```shell +$ python demo.py +12 +13 +13 +14 +13 +14 +14 +15 +``` + +## 5. 如何使用 print 输出日志 + +初学者喜欢使用 print 来调试代码,并记录程序运行过程。 + +但是 print 只会将内容输出到终端上,不能持久化到日志文件中,并不利于问题的排查。 + +如果你热衷于使用 print 来调试代码(虽然这并不是最佳做法),记录程序运行过程,那么下面介绍的这个 print 用法,可能会对你有用。 + +Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大,指定一些参数可以将 print 的内容输出到日志文件中 + +代码如下: + + +```python +>>> with open('test.log', mode='w') as f: +... print('hello, python', file=f, flush=True) +>>> exit() + +$ cat test.log +hello, python +``` + +## 6. 如何快速计算函数运行时间 + +计算一个函数的运行时间,你可能会这样子做 + +```python +import time + +start = time.time() + +# run the function + +end = time.time() +print(end-start) +``` + +你看看你为了计算函数运行时间,写了几行代码了。 + +有没有一种方法可以更方便的计算这个运行时间呢? + +有。 + +有一个内置模块叫 timeit + +使用它,只用一行代码即可 +```python +import time +import timeit + +def run_sleep(second): + print(second) + time.sleep(second) + +# 只用这一行 +print(timeit.timeit(lambda :run_sleep(2), number=5)) +``` +运行结果如下 +```python +2 +2 +2 +2 +2 +10.020059824 +``` + + + +## 7. 利用自带的缓存机制提高效率 + +缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 + +数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 + +为了实现这个需求,Python 3.2 + 中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。 + +这个机制实现于 functool 模块中的 lru_cache 装饰器。 + +```python +@functools.lru_cache(maxsize=None, typed=False) +``` + +参数解读: + +- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 2 的幂时,性能最佳 +- typed:若为 True,则不同参数类型的调用将分别缓存。 + +举个例子 + +```python +from functools import lru_cache + +@lru_cache(None) +def add(x, y): + print("calculating: %s + %s" % (x, y)) + return x + y + +print(add(1, 2)) +print(add(1, 2)) +print(add(2, 3)) +``` + +输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果 + +```shell +calculating: 1 + 2 +3 +3 +calculating: 2 + 3 +5 +``` + +下面这个是经典的斐波那契数列,当你指定的 n 较大时,会存在大量的重复计算 + +```python +def fib(n): + if n < 2: + return n + return fib(n - 2) + fib(n - 1) +``` + +第六点介绍的 timeit,现在可以用它来测试一下到底可以提高多少的效率。 + +不使用 lru_cache 的情况下,运行时间 31 秒 +```python +import timeit + +def fib(n): + if n < 2: + return n + return fib(n - 2) + fib(n - 1) + + + +print(timeit.timeit(lambda :fib(40), number=1)) +# output: 31.2725698948 +``` +由于使用了 lru_cache 后,运行速度实在太快了,所以我将 n 值由 30 调到 500,可即使是这样,运行时间也才 0.0004 秒。提高速度非常显著。 +```python +import timeit +from functools import lru_cache + +@lru_cache(None) +def fib(n): + if n < 2: + return n + return fib(n - 2) + fib(n - 1) + +print(timeit.timeit(lambda :fib(500), number=1)) +# output: 0.0004921059880871326 +``` + +## 8. 在程序退出前执行代码的技巧 + +使用 atexit 这个内置模块,可以很方便的注册退出函数。 + +不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 + +示例如下 + +![](http://image.iswbm.com/20200510112133.png) + +如果`clean()`函数有参数,那么你可以不用装饰器,而是直接调用`atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')`。 + +可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit 来得优雅,来得方便,并且它很容易扩展。 + +但是使用 atexit 仍然有一些局限性,比如: + +- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 +- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 +- 如果你手动调用了`os._exit()`,你注册的函数无法正常执行。 + + + + +## 9. 实现类似 defer 的延迟调用 + +在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 + +```go +import "fmt" + +func myfunc() { + fmt.Println("B") +} + +func main() { + defer myfunc() + fmt.Println("A") +} +``` + +输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 的调用写在函数的第一行,这就是延迟调用。 + +``` +A +B +``` + +那么在 Python 中否有这种机制呢? + +当然也有,只不过并没有 Golang 这种简便。 + +在 Python 可以使用 **上下文管理器** 达到这种效果 + +```python +import contextlib + +def callback(): + print('B') + +with contextlib.ExitStack() as stack: + stack.callback(callback) + print('A') +``` + +输出如下 + +``` +A +B +``` + +## 10. 如何流式读取数G超大文件 + +使用 with...open... 可以从一个文件中读取数据,这是所有 Python 开发者都非常熟悉的操作。 + +但是如果你使用不当,也会带来很大的麻烦。 + +比如当你使用了 read 函数,其实 Python 会将文件的内容一次性的全部载入内存中,如果文件有 10 个G甚至更多,那么你的电脑就要消耗的内存非常巨大。 + +```python +# 一次性读取 +with open("big_file.txt", "r") as fp: + content = fp.read() +``` + +对于这个问题,你也许会想到使用 readline 去做一个生成器来逐行返回。 + +```python +def read_from_file(filename): + with open(filename, "r") as fp: + yield fp.readline() +``` + +可如果这个文件内容就一行呢,一行就 10个G,其实你还是会一次性读取全部内容。 + +最优雅的解决方法是,在使用 read 方法时,指定每次只读取固定大小的内容,比如下面的代码中,每次只读取 8kb 返回。 + +```python +def read_from_file(filename, block_size = 1024 * 8): + with open(filename, "r") as fp: + while True: + chunk = fp.read(block_size) + if not chunk: + break + + yield chunk +``` + +上面的代码,功能上已经没有问题了,但是代码看起来代码还是有些臃肿。 + + 借助偏函数 和 iter 函数可以优化一下代码 + +```python +from functools import partial + +def read_from_file(filename, block_size = 1024 * 8): + with open(filename, "r") as fp: + for chunk in iter(partial(fp.read, block_size), ""): + yield chunk +``` \ No newline at end of file diff --git a/source/c03/c03_07.rst b/source/c03/c03_07.rst new file mode 100644 index 0000000..eed3b00 --- /dev/null +++ b/source/c03/c03_07.rst @@ -0,0 +1,535 @@ +3.7 超实用10 条 Python使用技巧 +============================== + +|image0| + +1. 如何在运行状态查看源代码? +----------------------------- + +查看函数的源代码,我们通常会使用 IDE 来完成。 + +比如在 PyCharm 中,你可以 Ctrl + 鼠标点击 进入函数的源代码。 + +那如果没有 IDE 呢? + +当我们想使用一个函数时,如何知道这个函数需要接收哪些参数呢? + +当我们在使用函数时出现问题的时候,如何通过阅读源代码来排查问题所在呢? + +这时候,我们可以使用 inspect 来代替 IDE 帮助你完成这些事 + +.. code:: python + + # demo.py + import inspect + + + def add(x, y): + return x + y + + print("===================") + print(inspect.getsource(add)) + +运行结果如下 + +.. code:: shell + + $ python demo.py + =================== + def add(x, y): + return x + y + +2. 如何关闭异常自动关联上下文? +------------------------------- + +当你在处理异常时,由于处理不当或者其他问题,再次抛出另一个异常时,往外抛出的异常也会携带原始的异常信息。 + +就像这样子。 + +.. code:: python + + try: + print(1 / 0) + except Exception as exc: + raise RuntimeError("Something bad happened") + +从输出可以看到两个异常信息 + +.. code:: python + + Traceback (most recent call last): + File "demo.py", line 2, in + print(1 / 0) + ZeroDivisionError: division by zero + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "demo.py", line 4, in + raise RuntimeError("Something bad happened") + RuntimeError: Something bad happened + +如果在异常处理程序或 finally +块中引发异常,默认情况下,异常机制会隐式工作会将先前的异常附加为新异常的 +``__context__``\ 属性。这就是 Python 默认开启的自动关联异常上下文。 + +如果你想自己控制这个上下文,可以加个 from 关键字(\ ``from`` +语法会有个限制,就是第二个表达式必须是另一个异常类或实例。),来表明你的新异常是直接由哪个异常引起的。 + +.. code:: python + + try: + print(1 / 0) + except Exception as exc: + raise RuntimeError("Something bad happened") from exc + +输出如下 + +.. code:: python + + Traceback (most recent call last): + File "demo.py", line 2, in + print(1 / 0) + ZeroDivisionError: division by zero + + The above exception was the direct cause of the following exception: + + Traceback (most recent call last): + File "demo.py", line 4, in + raise RuntimeError("Something bad happened") from exc + RuntimeError: Something bad happened + +当然,你也可以通过\ ``with_traceback()``\ 方法为异常设置上下文\ ``__context__``\ 属性,这也能在\ ``traceback``\ 更好的显示异常信息。 + +.. code:: python + + try: + print(1 / 0) + except Exception as exc: + raise RuntimeError("bad thing").with_traceback(exc) + +最后,如果我想彻底关闭这个自动关联异常上下文的机制?有什么办法呢? + +可以使用 ``raise...from None``\ ,从下面的例子上看,已经没有了原始异常 + +.. code:: python + + $ cat demo.py + try: + print(1 / 0) + except Exception as exc: + raise RuntimeError("Something bad happened") from None + $ + $ python demo.py + Traceback (most recent call last): + File "demo.py", line 4, in + raise RuntimeError("Something bad happened") from None + RuntimeError: Something bad happened + (PythonCodingTime) + +03. 最快查看包搜索路径的方式 +---------------------------- + +当你使用 import 导入一个包或模块时,Python +会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path +查看。 + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path) + ['', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages'] + >>> + +那有没有更快的方式呢? + +我这有一种连 console 模式都不用进入的方法呢? + +你可能会想到这种,但这本质上与上面并无区别 + +.. code:: python + + [wangbm@localhost ~]$ python -c "print('\n'.join(__import__('sys').path))" + + /usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg + /usr/lib/python2.7/site-packages/redis-3.0.1-py2.7.egg + /usr/lib64/python27.zip + /usr/lib64/python2.7 + /usr/lib64/python2.7/plat-linux2 + /usr/lib64/python2.7/lib-tk + /usr/lib64/python2.7/lib-old + /usr/lib64/python2.7/lib-dynload + /home/wangbm/.local/lib/python2.7/site-packages + /usr/lib64/python2.7/site-packages + /usr/lib64/python2.7/site-packages/gtk-2.0 + /usr/lib/python2.7/site-packages + +这里我要介绍的是比上面两种都方便的多的方法,一行命令即可解决 + +.. code:: shell + + [wangbm@localhost ~]$ python3 -m site + sys.path = [ + '/home/wangbm', + '/usr/local/Python3.7/lib/python37.zip', + '/usr/local/Python3.7/lib/python3.7', + '/usr/local/Python3.7/lib/python3.7/lib-dynload', + '/home/wangbm/.local/lib/python3.7/site-packages', + '/usr/local/Python3.7/lib/python3.7/site-packages', + ] + USER_BASE: '/home/wangbm/.local' (exists) + USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) + ENABLE_USER_SITE: True + +从输出你可以发现,这个列的路径会比 sys.path +更全,它包含了用户环境的目录。 + +4. 将嵌套 for 循环写成单行 +-------------------------- + +我们经常会如下这种嵌套的 for 循环代码 + +.. code:: python + + list1 = range(1,3) + list2 = range(4,6) + list3 = range(7,9) + for item1 in list1: + for item2 in list2: + for item3 in list3: + print(item1+item2+item3) + +这里仅仅是三个 for 循环,在实际编码中,有可能会有更层。 + +这样的代码,可读性非常的差,很多人不想这么写,可又没有更好的写法。 + +这里介绍一种我常用的写法,使用 itertools 这个库来实现更优雅易读的代码。 + +.. code:: python + + from itertools import product + list1 = range(1,3) + list2 = range(4,6) + list3 = range(7,9) + for item1,item2,item3 in product(list1, list2, list3): + print(item1+item2+item3) + +输出如下 + +.. code:: shell + + $ python demo.py + 12 + 13 + 13 + 14 + 13 + 14 + 14 + 15 + +5. 如何使用 print 输出日志 +-------------------------- + +初学者喜欢使用 print 来调试代码,并记录程序运行过程。 + +但是 print +只会将内容输出到终端上,不能持久化到日志文件中,并不利于问题的排查。 + +如果你热衷于使用 print +来调试代码(虽然这并不是最佳做法),记录程序运行过程,那么下面介绍的这个 +print 用法,可能会对你有用。 + +Python 3 中的 print +作为一个函数,由于可以接收更多的参数,所以功能变为更加强大,指定一些参数可以将 +print 的内容输出到日志文件中 + +代码如下: + +.. code:: python + + >>> with open('test.log', mode='w') as f: + ... print('hello, python', file=f, flush=True) + >>> exit() + + $ cat test.log + hello, python + +6. 如何快速计算函数运行时间 +--------------------------- + +计算一个函数的运行时间,你可能会这样子做 + +.. code:: python + + import time + + start = time.time() + + # run the function + + end = time.time() + print(end-start) + +你看看你为了计算函数运行时间,写了几行代码了。 + +有没有一种方法可以更方便的计算这个运行时间呢? + +有。 + +有一个内置模块叫 timeit + +使用它,只用一行代码即可 + +.. code:: python + + import time + import timeit + + def run_sleep(second): + print(second) + time.sleep(second) + + # 只用这一行 + print(timeit.timeit(lambda :run_sleep(2), number=5)) + +运行结果如下 + +.. code:: python + + 2 + 2 + 2 + 2 + 2 + 10.020059824 + +7. 利用自带的缓存机制提高效率 +----------------------------- + +缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 + +数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 + +为了实现这个需求,Python 3.2 + +中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。 + +这个机制实现于 functool 模块中的 lru_cache 装饰器。 + +.. code:: python + + @functools.lru_cache(maxsize=None, typed=False) + +参数解读: + +- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 + 2 的幂时,性能最佳 +- typed:若为 True,则不同参数类型的调用将分别缓存。 + +举个例子 + +.. code:: python + + from functools import lru_cache + + @lru_cache(None) + def add(x, y): + print("calculating: %s + %s" % (x, y)) + return x + y + + print(add(1, 2)) + print(add(1, 2)) + print(add(2, 3)) + +输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果 + +.. code:: shell + + calculating: 1 + 2 + 3 + 3 + calculating: 2 + 3 + 5 + +下面这个是经典的斐波那契数列,当你指定的 n 较大时,会存在大量的重复计算 + +.. code:: python + + def fib(n): + if n < 2: + return n + return fib(n - 2) + fib(n - 1) + +第六点介绍的 timeit,现在可以用它来测试一下到底可以提高多少的效率。 + +不使用 lru_cache 的情况下,运行时间 31 秒 + +.. code:: python + + import timeit + + def fib(n): + if n < 2: + return n + return fib(n - 2) + fib(n - 1) + + + + print(timeit.timeit(lambda :fib(40), number=1)) + # output: 31.2725698948 + +由于使用了 lru_cache 后,运行速度实在太快了,所以我将 n 值由 30 调到 +500,可即使是这样,运行时间也才 0.0004 秒。提高速度非常显著。 + +.. code:: python + + import timeit + from functools import lru_cache + + @lru_cache(None) + def fib(n): + if n < 2: + return n + return fib(n - 2) + fib(n - 1) + + print(timeit.timeit(lambda :fib(500), number=1)) + # output: 0.0004921059880871326 + +8. 在程序退出前执行代码的技巧 +----------------------------- + +使用 atexit 这个内置模块,可以很方便的注册退出函数。 + +不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 + +示例如下 + +|image1| + +如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。 + +可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit +来得优雅,来得方便,并且它很容易扩展。 + +但是使用 atexit 仍然有一些局限性,比如: + +- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 +- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 +- 如果你手动调用了\ ``os._exit()``\ ,你注册的函数无法正常执行。 + +9. 实现类似 defer 的延迟调用 +---------------------------- + +在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 + +.. code:: go + + import "fmt" + + func myfunc() { + fmt.Println("B") + } + + func main() { + defer myfunc() + fmt.Println("A") + } + +输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc +的调用写在函数的第一行,这就是延迟调用。 + +:: + + A + B + +那么在 Python 中否有这种机制呢? + +当然也有,只不过并没有 Golang 这种简便。 + +在 Python 可以使用 **上下文管理器** 达到这种效果 + +.. code:: python + + import contextlib + + def callback(): + print('B') + + with contextlib.ExitStack() as stack: + stack.callback(callback) + print('A') + +输出如下 + +:: + + A + B + +10. 如何流式读取数G超大文件 +--------------------------- + +使用 with…open… 可以从一个文件中读取数据,这是所有 Python +开发者都非常熟悉的操作。 + +但是如果你使用不当,也会带来很大的麻烦。 + +比如当你使用了 read 函数,其实 Python +会将文件的内容一次性的全部载入内存中,如果文件有 10 +个G甚至更多,那么你的电脑就要消耗的内存非常巨大。 + +.. code:: python + + # 一次性读取 + with open("big_file.txt", "r") as fp: + content = fp.read() + +对于这个问题,你也许会想到使用 readline 去做一个生成器来逐行返回。 + +.. code:: python + + def read_from_file(filename): + with open(filename, "r") as fp: + yield fp.readline() + +可如果这个文件内容就一行呢,一行就 +10个G,其实你还是会一次性读取全部内容。 + +最优雅的解决方法是,在使用 read +方法时,指定每次只读取固定大小的内容,比如下面的代码中,每次只读取 8kb +返回。 + +.. code:: python + + def read_from_file(filename, block_size = 1024 * 8): + with open(filename, "r") as fp: + while True: + chunk = fp.read(block_size) + if not chunk: + break + + yield chunk + +上面的代码,功能上已经没有问题了,但是代码看起来代码还是有些臃肿。 + +借助偏函数 和 iter 函数可以优化一下代码 + +.. code:: python + + from functools import partial + + def read_from_file(filename, block_size = 1024 * 8): + with open(filename, "r") as fp: + for chunk in iter(partial(fp.read, block_size), ""): + yield chunk + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200510112133.png + From be6fc962f2de366fee66a4896e2771297fe01759 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 7 Jun 2020 13:49:33 +0800 Subject: [PATCH 079/147] Update README.md --- README.md | 200 +++++++++--------------------------------------------- 1 file changed, 33 insertions(+), 167 deletions(-) diff --git a/README.md b/README.md index 33bf271..3725d04 100644 --- a/README.md +++ b/README.md @@ -1,169 +1,35 @@ +![](http://image.iswbm.com/20200607133552.png) + +

    + Build Status + + + + +

    + +## [项目主页](http://python.iswbm.com/) + +在线阅读:[Python 编程时光](http://python.iswbm.com/) + +![](http://image.iswbm.com/20200607130051.png) + +## 文章结构 + +![](http://image.iswbm.com/20200607131339.png) + + + +## 我的微信 + +对文章有什么疑问,对项目有什么建议,可以加这个微信与我交流。 + +![](http://image.iswbm.com/20200607134035.png) + +## 欢迎关注 + +本项目的文章首发于我的个人微信公众号 ,扫描下方二维码,可以关注。 + +![](http://image.iswbm.com/20200607133315.png) -这是我的个人博客( [Python编程时光](http://python.iswbm.com/) ),主要写关于Python的一些思考总结。 - -关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python.iswbm.com/en/latest/c04/c04_03.html) -## 第一章:基础知识 -- 1.1 [13条Python2.x和3.x的区别?](http://python.iswbm.com/en/latest/c01/c01_01.html) -- 1.2 [Python 自省机制详解](http://python.iswbm.com/en/latest/c01/c01_02.html) -- 1.4 [什么是猴子补丁?](http://python.iswbm.com/en/latest/c01/c01_04.html) -- 1.5 [深入闭包与变量作用域](http://python.iswbm.com/en/latest/c01/c01_05.html) -- 1.6 [深入理解元组存在的意义](http://python.iswbm.com/en/latest/c01/c01_06.html) -- 1.7 [15个Pythonic的代码示例](http://python.iswbm.com/en/latest/c01/c01_07.html) -- 1.8 [新式类和经典类的区别?](http://python.iswbm.com/en/latest/c01/c01_08.html) -- 1.9 [多继承与Mixin设计模式](http://python.iswbm.com/en/latest/c01/c01_09.html) -- 1.10 [Python 黑魔法指南 50 例](http://python.iswbm.com/en/latest/c01/c01_10.html) -- 1.11 [正则表达式必知必会](http://python.iswbm.com/en/latest/c01/c01_11.html) -- 1.12 [搞懂字符编码的前世今生](http://python.iswbm.com/en/latest/c01/c01_12.html) -- 1.13 [Python几个高阶函数](http://python.iswbm.com/en/latest/c01/c01_13.html) -- 1.14 [with 与 上下文管理器](http://python.iswbm.com/en/latest/c01/c01_14.html) -- 1.15 [提升Python性能的7个习惯](http://python.iswbm.com/en/latest/c01/c01_15.html) -- 1.16 [泛型函数怎么写?](http://python.iswbm.com/en/latest/c01/c01_16.html) -- 1.17 [深入理解「描述符」](http://python.iswbm.com/en/latest/c01/c01_17.html) -- 1.18 [MySQL 使用总结](http://python.iswbm.com/en/latest/c01/c01_18.html) -- 1.20 [静态方法其实暗藏玄机](http://python.iswbm.com/en/latest/c01/c01_20.html) -- 1.21 [开发小技巧](http://python.iswbm.com/en/latest/c01/c01_21.html) -- 1.22 [如何修改 CentOS 6.x 上默认Python](http://python.iswbm.com/en/latest/c01/c01_22.html) -- 1.23 [Pythonista 学习 Js](http://python.iswbm.com/en/latest/c01/c01_23.html) -- 1.24 [深入探讨 Python 的 import 机制:实现远程导入模块](http://python.iswbm.com/en/latest/c01/c01_24.html) -- 1.25 [50% 的人不知道的Python 包与模块的知识盲区](http://python.iswbm.com/en/latest/c01/c01_25.html) -- 1.26 [C语言基础的学习](http://python.iswbm.com/en/latest/c01/c01_26.html) -- 1.27 [全面学习 Python 包:包的构建与分发](http://python.iswbm.com/en/latest/c01/c01_27.html) -- 1.27 [如何阅读 CPython源码?](http://python.iswbm.com/en/latest/c01/c01_29.html) -- 1.30 [盘点程序员学习编程的那些网站](http://python.iswbm.com/en/latest/c01/c01_30.html) -- 1.31 [学习 Pillow 笔记](http://python.iswbm.com/en/latest/c01/c01_31.html) -- 1.32 [在 CentOS 7.2 上安装 Python3.7](http://python.iswbm.com/en/latest/c01/c01_32.html) -- 1.33 [如何调试已经运行中的程序](http://python.iswbm.com/en/latest/c01/c01_33.html) -- 1.34 [每日一库:sh,最优雅的命令调用方式](http://python.iswbm.com/en/latest/c01/c01_34.html) -- 1.35 [使用 Python 远程登陆服务器的利器](http://python.iswbm.com/en/latest/c01/c01_35.html) -- 1.36 [每日一库:pretty_errors 解决bug 洁癖](http://python.iswbm.com/en/latest/c01/c01_36.html) -- 1.37 [Python 炫技操作:条件语句的七种写法](http://python.iswbm.com/en/latest/c01/c01_37.html) -- 1.38 [/usr/bin/env python 有什么用?](http://python.iswbm.com/en/latest/c01/c01_38.html) -- 1.39 [Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_39.html) -- 1.40 [Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_40.html) -- 1.41 [Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_41.html) -- 1.42 [Python 炫技操作:海象运算符的三种用法](http://python.iswbm.com/en/latest/c01/c01_42.html) -- 1.43 [求你了,别再使用 pprint 打印字典了](http://python.iswbm.com/en/latest/c01/c01_43.html) -- 1.44 [详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_44.html) -- 1.45 [Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_45.html) - -## 第二章:并发编程 -- 2.1 [从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) -- 2.2 [创建多线程的几种方法](http://python.iswbm.com/en/latest/c02/c02_02.html) -- 2.3 [谈谈线程中的“锁机制”](http://python.iswbm.com/en/latest/c02/c02_03.html) -- 2.4 [线程消息通信机制](http://python.iswbm.com/en/latest/c02/c02_04.html) -- 2.5 [线程中的信息隔离](http://python.iswbm.com/en/latest/c02/c02_05.html) -- 2.6 [线程池与进程池的创建](http://python.iswbm.com/en/latest/c02/c02_06.html) -- 2.7 [从生成器使用入门协程](http://python.iswbm.com/en/latest/c02/c02_07.html) -- 2.8 [深入理解yield from语法](http://python.iswbm.com/en/latest/c02/c02_08.html) -- 2.9 [初识异步IO框架:asyncio 上篇](http://python.iswbm.com/en/latest/c02/c02_09.html) -- 2.10 [深入异步IO框架:asyncio 中篇](http://python.iswbm.com/en/latest/c02/c02_10.html) -- 2.11 [实战异步IO框架:asyncio 下篇](http://python.iswbm.com/en/latest/c02/c02_11.html) -- 2.12 [生成器与协程,你分清了吗?](http://python.iswbm.com/en/latest/c02/c02_12.html) -- 2.13 [I/O多路复用:select/poll/epoll](http://python.iswbm.com/en/latest/c02/c02_13.html) -- 2.14 [浅谈线程安全那些事儿](http://python.iswbm.com/en/latest/c02/c02_14.html) - -## 第三章:高级编程 -- 3.1 [装饰器进阶用法详解](http://python.iswbm.com/en/latest/c03/c03_01.html) -- 3.2 [深入理解Python元类](http://python.iswbm.com/en/latest/c03/c03_02.html) -- 3.3 [Socket编程实现在线聊天](http://python.iswbm.com/en/latest/c03/c03_03.html) -- 3.4 [Django+Uwsgi部署网站](http://python.iswbm.com/en/latest/c03/c03_04.html) -- 3.5 [源码解读:Flask上下文与代理模式](http://python.iswbm.com/en/latest/c03/c03_05.html) -- 3.6 [Web开发者必看:理解WSGI](http://python.iswbm.com/en/latest/c03/c03_06.html) -- 3.7 [超实用10 条 Python使用技巧](http://python.iswbm.com/en/latest/c03/c03_07.html) - -## 第四章:开发工具 -- 4.1 [虚拟环境:virtualenv](http://python.iswbm.com/en/latest/c04/c04_01.html) -- 4.2 [Xshell的高效使用手册](http://python.iswbm.com/en/latest/c04/c04_02.html) -- 4.3 [30分钟教你搭建一个博客](http://python.iswbm.com/en/latest/c04/c04_03.html) -- 4.4 [Jupyter NoteBook 使用指南](http://python.iswbm.com/en/latest/c04/c04_04.html) -- 4.5 [Win10+Ubuntu 双系统安装教程](http://python.iswbm.com/en/latest/c04/c04_05.html) -- 4.6 [我的 Git 使用指南](http://python.iswbm.com/en/latest/c04/c04_06.html) -- 4.7 [Hexo 搭建博客教程](http://python.iswbm.com/en/latest/c04/c04_07.html) -- 4.8 [珍惜生命,远离鼠标](http://python.iswbm.com/en/latest/c04/c04_08.html) -- 4.9 [MySQL的基本使用](http://python.iswbm.com/en/latest/c04/c04_09.html) -- 4.10 [MySQL的高级进阶](http://python.iswbm.com/en/latest/c04/c04_10.html) -- 4.11 [不能不会的远程调试技巧](http://python.iswbm.com/en/latest/c04/c04_11.html) -- 4.12 [服务器调试神器:pdb](http://python.iswbm.com/en/latest/c04/c04_12.html) -- 4.13 [命令行解析工具:argparse](http://python.iswbm.com/en/latest/c04/c04_13.html) -- 4.14 [虚拟环境:Pipenv](http://python.iswbm.com/en/latest/c04/c04_14.html) -- 4.15 [30个 PyCharm 实用技巧](http://python.iswbm.com/en/latest/c04/c04_15.html) -- 4.16 [Python 开发技巧集合](http://python.iswbm.com/en/latest/c04/c04_16.html) -- 4.17 [详解 23 种设计模式](http://python.iswbm.com/en/latest/c04/c04_17.html) -- 4.18 [详细的 Mac 使用指南](http://python.iswbm.com/en/latest/c04/c04_18.html) -- 4.19 [程序员编码必学:Vim](http://python.iswbm.com/en/latest/c04/c04_19.html) -- 4.20 [学会使用谷歌搜索引擎](http://python.iswbm.com/en/latest/c04/c04_20.html) -- 4.21 [最全的 pip 使用指南,50% 你可能没用过](http://python.iswbm.com/en/latest/c04/c04_21.html) -- 4.22 [用好 Chrome 必看](http://python.iswbm.com/en/latest/c04/c04_22.html) -- 4.23 [电脑使用技巧](http://python.iswbm.com/en/latest/c04/c04_23.html) -- 4.24 [Python “用户环境”的一次完美应用](http://python.iswbm.com/en/latest/c04/c04_24.html) - -## 第五章:算法教程 -- 5.1 [图解九大经典排序算法](http://python.iswbm.com/en/latest/c05/c05_01.html) -- 5.2 [递归算法:走楼梯会思考的题](http://python.iswbm.com/en/latest/c05/c05_02.html) -- 5.3 [哈希算法:安全方面的算法应用](http://python.iswbm.com/en/latest/c05/c05_03.html) - -## 第六章:数据分析 -- 6.1 [一张图带你入门matplotlib](http://python.iswbm.com/en/latest/c06/c06_01.html) -- 6.2 [详解六种可视化图表](http://python.iswbm.com/en/latest/c06/c06_02.html) -- 6.3 [如何绘制正余弦函数图象](http://python.iswbm.com/en/latest/c06/c06_03.html) -- 6.4 [子图与子区 难点突破](http://python.iswbm.com/en/latest/c06/c06_04.html) -- 6.5 [绘制酷炫的gif动态图](http://python.iswbm.com/en/latest/c06/c06_05.html) -- 6.6 [自动生成图像视频](http://python.iswbm.com/en/latest/c06/c06_06.html) - -## 第七章:运维人生 -- 7.1 [Linux 命令行的艺术](http://python.iswbm.com/en/latest/c07/c07_01.html) -- 7.2 [Zabbix 监控部署文档](http://python.iswbm.com/en/latest/c07/c07_02.html) -- 7.3 [Docker:Hello world](http://python.iswbm.com/en/latest/c07/c07_03.html) -- 7.4 [Docker:关于镜像](http://python.iswbm.com/en/latest/c07/c07_04.html) -- 7.5 [Docker:网络通信](http://python.iswbm.com/en/latest/c07/c07_05.html) -- 7.6 [Docker:存储与多主机](http://python.iswbm.com/en/latest/c07/c07_06.html) -- 7.7 [SaltStack 入门指南](http://python.iswbm.com/en/latest/c07/c07_07.html) -- 7.8 [Keepalived 部署文档](http://python.iswbm.com/en/latest/c07/c07_08.html) -- 7.9 [Ansible 入门指南使用手册](http://python.iswbm.com/en/latest/c07/c07_09.html) -- 7.10 [Ansible API 最全使用文档(中文)](http://python.iswbm.com/en/latest/c07/c07_10.html) -- 7.11 [K8S:基础入门](http://python.iswbm.com/en/latest/c07/c07_11.html) -- 7.12 [Linux 包管理工具:yum 和 rpm](http://python.iswbm.com/en/latest/c07/c07_12.html) -- 7.13 [基于 ansible-api 二次开发](http://python.iswbm.com/en/latest/c07/c07_13.html) -- 7.14 [Linux 如何写判断语句](http://python.iswbm.com/en/latest/c07/c07_14.html) -- 7.15 [Mariadb 与 Galera 集群总结](http://python.iswbm.com/en/latest/c07/c07_15.html) -- 7.16 [Linux 运维之路](http://python.iswbm.com/en/latest/c07/c07_16.html) -- 1.17 [ansible 自定义 Jinja2 过滤器](http://python.iswbm.com/en/latest/c07/c07_17.html) -- 7.18 [Shell中去除字符串前后空格的方法](http://python.iswbm.com/en/latest/c07/c07_18.html) -- 7.19 [Ansible 使用教程](http://python.iswbm.com/en/latest/c07/c07_19.html) -- 7.20 [使用 Shell 删除文件的多种方法](http://python.iswbm.com/en/latest/c07/c07_20.html) -- 7.21 [如何快速创建超大文件?](http://python.iswbm.com/en/latest/c07/c07_21.html) - -## 第八章:OpenStack -- 8.1 [OpenStack 运维命令](http://python.iswbm.com/en/latest/c08/c08_01.html) -- 8.2 [OpenStack 部署SR-IOV](http://python.iswbm.com/en/latest/c08/c08_02.html) -- 8.3 [制作 OpenStack 镜像](http://python.iswbm.com/en/latest/c08/c08_03.html) -- 8.4 [云计算与虚拟化入门通识](http://python.iswbm.com/en/latest/c08/c08_04.html) -- 8.5 [OpenStack 源码剖析与改造](http://python.iswbm.com/en/latest/c08/c08_05.html) -- 8.6 [源码解读:Cloud-Init](http://python.iswbm.com/en/latest/c08/c08_06.html) -- 8.7 [OpenStack 实现GPU直通](http://python.iswbm.com/en/latest/c08/c08_07.html) -- 8.8 [OpenStack 如何使用DHCP?](http://python.iswbm.com/en/latest/c08/c08_08.html) -- 8.9 [从0到1:全面理解RPC远程调用](http://python.iswbm.com/en/latest/c08/c08_09.html) -- 8.16 [修改 KVM 镜像文件的三种方法](http://python.iswbm.com/en/latest/c08/c08_10.html) -- 8.11 [OpenStack 问题排查](http://python.iswbm.com/en/latest/c08/c08_11.html) -- 8.12 [OpenStack之主机调度](http://python.iswbm.com/en/latest/c08/c08_12.html) -- 8.13 [网络知识必知必会](http://python.iswbm.com/en/latest/c08/c08_13.html) -- 8.14 [支持 IPv6以及多运营商](http://python.iswbm.com/en/latest/c08/c08_14.html) -- 8.15 [Neutron 源码解读](http://python.iswbm.com/en/latest/c08/c08_15.html) - -## 第九章:有趣的工具 -- 9.1 [情人节来了,教你使用 Python 来表白](http://python.iswbm.com/en/latest/c09/c09_01.html) -- 9.2 [没有这 50 个APP,我的 Mac 将只是一块铁](http://python.iswbm.com/en/latest/c09/c09_02.html) -- 9.3 [明哥的在线工具集](http://python.iswbm.com/en/latest/c09/c09_03.html) - -## 第十章:网络基础 -- 10.1 [网络知识扫盲:一篇文章搞懂 DNS](http://python.iswbm.com/en/latest/c10/c10_01.html) -- 10.2 [网络知识扫盲:如何理解 OSI七层模型](http://python.iswbm.com/en/latest/c10/c10_02.html) -- 10.3 [网络知识扫盲:详解TCP的三次握手与四次挥手](http://python.iswbm.com/en/latest/c10/c10_03.html) -- 10.5 [Wireshark 抓包教程](http://python.iswbm.com/en/latest/c10/c10_05.html) -- 10.6 [通过比较,学习 TCP 与 UDP](http://python.iswbm.com/en/latest/c10/c10_06.html) -- 10.7 [网络知识扫盲:一篇文章理解 HTTP](http://python.iswbm.com/en/latest/c10/c10_07.html) - - ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) From ebea7e243ecab94767f2e1b49c5443e81aeb82da Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 7 Jun 2020 14:40:43 +0800 Subject: [PATCH 080/147] Update README.md --- README.md | 15 +++++---------- md2rst.py | 23 ++++++++++++++++++++--- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3725d04..fa7d9f1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](http://image.iswbm.com/20200607133552.png) +![](http://image.iswbm.com/image-20200607140915244.png)

    Build Status @@ -8,6 +8,7 @@

    + ## [项目主页](http://python.iswbm.com/) 在线阅读:[Python 编程时光](http://python.iswbm.com/) @@ -20,16 +21,10 @@ -## 我的微信 - -对文章有什么疑问,对项目有什么建议,可以加这个微信与我交流。 - -![](http://image.iswbm.com/20200607134035.png) - -## 欢迎关注 +## 欢迎交流 -本项目的文章首发于我的个人微信公众号 ,扫描下方二维码,可以关注。 +对文章有什么疑问,对项目有什么建议,可以添加微信与我交流,同时欢迎关注我的个人微信公众号。 -![](http://image.iswbm.com/20200607133315.png) +![](http://image.iswbm.com/20200607140327.png) diff --git a/md2rst.py b/md2rst.py index 4b94018..940cd4e 100644 --- a/md2rst.py +++ b/md2rst.py @@ -26,9 +26,26 @@ base_link = "http://python.iswbm.com/en/latest/" readme_header = ''' -这是我的个人博客( [Python编程时光](http://python.iswbm.com/) ),主要写关于Python的一些思考总结。 +![](http://image.iswbm.com/20200607120940.png) + +

    + Build Status + + + + +

    + +## [项目主页](http://python.iswbm.com/) + +在线阅读:[Python 编程时光](http://python.iswbm.com/) + +![](http://image.iswbm.com/20200607130051.png) + +## 文章结构 + +![](http://image.iswbm.com/20200607131339.png) -关于搭建教程,感兴趣的可以查看这边:[Sphinx 搭建博客的图文教程](http://python.iswbm.com/en/latest/c04/c04_03.html) ''' readme_tooter = ''' --- @@ -128,5 +145,5 @@ def main(index_info): if __name__ == '__main__': index_info = init_index_info() main(index_info) - render_index_page(index_info) + # render_index_page(index_info) print("OK") From 1ff08ffdafd104492aa662d3ec65b4f10f6c9462 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 7 Jun 2020 17:56:08 +0800 Subject: [PATCH 081/147] Update image --- md2rst.py | 2 +- source/aboutme.rst | 2 +- source/c01/c01_01.md | 2 +- source/c01/c01_01.rst | 6 +++--- source/c01/c01_04.md | 2 +- source/c01/c01_04.rst | 4 ++-- source/c01/c01_05.md | 2 +- source/c01/c01_05.rst | 6 +++--- source/c01/c01_06.md | 2 +- source/c01/c01_06.rst | 6 +++--- source/c01/c01_07.md | 2 +- source/c01/c01_07.rst | 6 +++--- source/c01/c01_08.md | 2 +- source/c01/c01_08.rst | 6 +++--- source/c01/c01_09.md | 2 +- source/c01/c01_09.rst | 6 +++--- source/c01/c01_10.md | 2 +- source/c01/c01_10.rst | 6 +++--- source/c01/c01_11.md | 2 +- source/c01/c01_11.rst | 6 +++--- source/c01/c01_12.md | 2 +- source/c01/c01_12.rst | 6 +++--- source/c01/c01_13.md | 2 +- source/c01/c01_13.rst | 6 +++--- source/c01/c01_14.md | 2 +- source/c01/c01_14.rst | 6 +++--- source/c01/c01_15.md | 2 +- source/c01/c01_15.rst | 6 +++--- source/c01/c01_16.md | 2 +- source/c01/c01_16.rst | 6 +++--- source/c01/c01_17.md | 2 +- source/c01/c01_17.rst | 6 +++--- source/c01/c01_20.md | 2 +- source/c01/c01_20.rst | 6 +++--- source/c01/c01_21.md | 2 +- source/c01/c01_21.rst | 6 +++--- source/c01/c01_22.md | 2 +- source/c01/c01_22.rst | 6 +++--- source/c01/c01_23.md | 2 +- source/c01/c01_23.rst | 6 +++--- source/c01/c01_24.md | 2 +- source/c01/c01_24.rst | 6 +++--- source/c01/c01_25.md | 2 +- source/c01/c01_25.rst | 6 +++--- source/c01/c01_26.md | 2 +- source/c01/c01_26.rst | 6 +++--- source/c01/c01_27.md | 2 +- source/c01/c01_27.rst | 6 +++--- source/c01/c01_29.md | 2 +- source/c01/c01_29.rst | 6 +++--- source/c01/c01_31.md | 2 +- source/c01/c01_31.rst | 6 +++--- source/c01/c01_32.md | 2 +- source/c01/c01_32.rst | 6 +++--- source/c01/c01_33.md | 2 +- source/c01/c01_33.rst | 6 +++--- source/c01/c01_34.md | 2 +- source/c01/c01_34.rst | 6 +++--- source/c01/c01_35.md | 2 +- source/c01/c01_35.rst | 6 +++--- source/c01/c01_36.md | 2 +- source/c01/c01_36.rst | 6 +++--- source/c01/c01_37.md | 2 +- source/c01/c01_37.rst | 6 +++--- source/c01/c01_38.md | 2 +- source/c01/c01_38.rst | 6 +++--- source/c01/c01_39.md | 2 +- source/c01/c01_39.rst | 6 +++--- source/c01/c01_40.md | 2 +- source/c01/c01_40.rst | 6 +++--- source/c01/c01_41.md | 2 +- source/c01/c01_41.rst | 6 +++--- source/c01/c01_42.md | 2 +- source/c01/c01_42.rst | 6 +++--- source/c01/c01_43.md | 2 +- source/c01/c01_43.rst | 6 +++--- source/c01/c01_44.md | 2 +- source/c01/c01_44.rst | 6 +++--- source/c01/c01_45.md | 2 +- source/c01/c01_45.rst | 6 +++--- source/c02/c02_01.md | 2 +- source/c02/c02_01.rst | 6 +++--- source/c02/c02_02.md | 2 +- source/c02/c02_02.rst | 6 +++--- source/c02/c02_03.md | 2 +- source/c02/c02_03.rst | 6 +++--- source/c02/c02_04.md | 2 +- source/c02/c02_04.rst | 6 +++--- source/c02/c02_05.md | 2 +- source/c02/c02_05.rst | 6 +++--- source/c02/c02_06.md | 2 +- source/c02/c02_06.rst | 6 +++--- source/c02/c02_07.md | 2 +- source/c02/c02_07.rst | 6 +++--- source/c02/c02_08.md | 2 +- source/c02/c02_08.rst | 6 +++--- source/c02/c02_09.md | 2 +- source/c02/c02_09.rst | 6 +++--- source/c02/c02_10.md | 2 +- source/c02/c02_10.rst | 6 +++--- source/c02/c02_11.md | 2 +- source/c02/c02_11.rst | 6 +++--- source/c02/c02_12.md | 2 +- source/c02/c02_12.rst | 6 +++--- source/c02/c02_14.md | 2 +- source/c02/c02_14.rst | 6 +++--- source/c03/c03_01.md | 2 +- source/c03/c03_01.rst | 6 +++--- source/c03/c03_02.md | 2 +- source/c03/c03_02.rst | 6 +++--- source/c03/c03_03.md | 2 +- source/c03/c03_03.rst | 6 +++--- source/c03/c03_04.md | 2 +- source/c03/c03_04.rst | 6 +++--- source/c03/c03_05.md | 2 +- source/c03/c03_05.rst | 6 +++--- source/c03/c03_06.md | 2 +- source/c03/c03_06.rst | 6 +++--- source/c04/c04_01.md | 2 +- source/c04/c04_01.rst | 6 +++--- source/c04/c04_02.md | 2 +- source/c04/c04_02.rst | 6 +++--- source/c04/c04_03.md | 2 +- source/c04/c04_03.rst | 6 +++--- source/c04/c04_04.md | 2 +- source/c04/c04_04.rst | 6 +++--- source/c04/c04_05.md | 2 +- source/c04/c04_05.rst | 6 +++--- source/c04/c04_06.md | 2 +- source/c04/c04_06.rst | 6 +++--- source/c04/c04_07.md | 2 +- source/c04/c04_07.rst | 6 +++--- source/c04/c04_08.md | 2 +- source/c04/c04_08.rst | 6 +++--- source/c04/c04_09.md | 2 +- source/c04/c04_09.rst | 6 +++--- source/c04/c04_10.md | 2 +- source/c04/c04_10.rst | 6 +++--- source/c04/c04_11.md | 2 +- source/c04/c04_11.rst | 6 +++--- source/c04/c04_12.md | 2 +- source/c04/c04_12.rst | 6 +++--- source/c04/c04_13.md | 2 +- source/c04/c04_13.rst | 6 +++--- source/c04/c04_14.md | 2 +- source/c04/c04_14.rst | 6 +++--- source/c04/c04_15.md | 2 +- source/c04/c04_15.rst | 6 +++--- source/c04/c04_16.md | 2 +- source/c04/c04_16.rst | 6 +++--- source/c04/c04_17.md | 2 +- source/c04/c04_17.rst | 6 +++--- source/c04/c04_18.md | 2 +- source/c04/c04_18.rst | 6 +++--- source/c04/c04_19.md | 2 +- source/c04/c04_19.rst | 6 +++--- source/c04/c04_21.md | 2 +- source/c04/c04_21.rst | 6 +++--- source/c04/c04_24.md | 2 +- source/c04/c04_24.rst | 6 +++--- source/c05/c05_01.md | 2 +- source/c05/c05_01.rst | 6 +++--- source/c05/c05_02.md | 2 +- source/c05/c05_02.rst | 6 +++--- source/c05/c05_03.md | 2 +- source/c05/c05_03.rst | 6 +++--- source/c06/c06_01.md | 2 +- source/c06/c06_01.rst | 6 +++--- source/c06/c06_02.md | 2 +- source/c06/c06_02.rst | 6 +++--- source/c06/c06_03.md | 2 +- source/c06/c06_03.rst | 6 +++--- source/c06/c06_04.md | 2 +- source/c06/c06_04.rst | 6 +++--- source/c06/c06_05.md | 2 +- source/c06/c06_05.rst | 6 +++--- source/c06/c06_06.md | 2 +- source/c06/c06_06.rst | 6 +++--- source/c07/C07_08.md | 2 +- source/c07/c07_01.md | 2 +- source/c07/c07_01.rst | 6 +++--- source/c07/c07_02.md | 2 +- source/c07/c07_02.rst | 6 +++--- source/c07/c07_03.md | 2 +- source/c07/c07_03.rst | 6 +++--- source/c07/c07_04.md | 2 +- source/c07/c07_04.rst | 6 +++--- source/c07/c07_05.md | 2 +- source/c07/c07_05.rst | 6 +++--- source/c07/c07_06.md | 2 +- source/c07/c07_06.rst | 6 +++--- source/c07/c07_07.md | 2 +- source/c07/c07_07.rst | 6 +++--- source/c07/c07_08.rst | 6 +++--- source/c07/c07_10.md | 2 +- source/c07/c07_10.rst | 6 +++--- source/c07/c07_11.md | 2 +- source/c07/c07_11.rst | 6 +++--- source/c07/c07_12.md | 2 +- source/c07/c07_12.rst | 6 +++--- source/c07/c07_13.md | 2 +- source/c07/c07_13.rst | 6 +++--- source/c07/c07_14.md | 2 +- source/c07/c07_14.rst | 6 +++--- source/c07/c07_15.md | 2 +- source/c07/c07_15.rst | 6 +++--- source/c07/c07_16.md | 2 +- source/c07/c07_16.rst | 6 +++--- source/c07/c07_17.md | 2 +- source/c07/c07_17.rst | 6 +++--- source/c07/c07_18.md | 2 +- source/c07/c07_18.rst | 6 +++--- source/c07/c07_19.md | 2 +- source/c07/c07_19.rst | 6 +++--- source/c08/c08_01.md | 2 +- source/c08/c08_01.rst | 6 +++--- source/c08/c08_02.md | 2 +- source/c08/c08_02.rst | 6 +++--- source/c08/c08_03.md | 2 +- source/c08/c08_03.rst | 6 +++--- source/c08/c08_04.md | 2 +- source/c08/c08_04.rst | 6 +++--- source/c08/c08_05.md | 2 +- source/c08/c08_05.rst | 6 +++--- source/c08/c08_06.md | 2 +- source/c08/c08_06.rst | 6 +++--- source/c08/c08_07.md | 2 +- source/c08/c08_07.rst | 6 +++--- source/c08/c08_08.md | 2 +- source/c08/c08_08.rst | 6 +++--- source/c08/c08_09.md | 2 +- source/c08/c08_09.rst | 6 +++--- source/c08/c08_11.md | 2 +- source/c08/c08_11.rst | 6 +++--- source/c08/c08_12.md | 2 +- source/c08/c08_12.rst | 6 +++--- source/c08/c08_13.md | 2 +- source/c08/c08_13.rst | 6 +++--- source/c08/c08_14.md | 2 +- source/c08/c08_14.rst | 6 +++--- source/c09/c09_01.md | 2 +- source/c09/c09_01.rst | 6 +++--- source/c10/c10_01.md | 2 +- source/c10/c10_01.rst | 6 +++--- source/chapters/p01.rst | 2 +- source/chapters/p02.rst | 2 +- source/chapters/p03.rst | 2 +- source/chapters/p04.rst | 2 +- source/chapters/p05.rst | 2 +- source/chapters/p06.rst | 2 +- source/chapters/p07.rst | 2 +- source/chapters/p08.rst | 2 +- source/chapters/p09.rst | 2 +- source/chapters/p10.rst | 2 +- source/index.rst | 2 +- source/preface.rst | 2 +- source/py_mp_index.md | 2 +- source/roadmap.rst | 2 +- source/thanks.rst | 2 +- 259 files changed, 500 insertions(+), 500 deletions(-) diff --git a/md2rst.py b/md2rst.py index 940cd4e..3dca78a 100644 --- a/md2rst.py +++ b/md2rst.py @@ -49,7 +49,7 @@ ''' readme_tooter = ''' --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) ''' diff --git a/source/aboutme.rst b/source/aboutme.rst index 8e3f65b..33479e6 100755 --- a/source/aboutme.rst +++ b/source/aboutme.rst @@ -10,5 +10,5 @@ -------------------------------------------- -.. image:: http://image.python-online.cn/image-20200320125724880.png +.. image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_01.md b/source/c01/c01_01.md index 1739c71..ce91a40 100644 --- a/source/c01/c01_01.md +++ b/source/c01/c01_01.md @@ -273,4 +273,4 @@ class Person(metaclass=MetaPerson): -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 307c91c..7d1375e 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -316,10 +316,10 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511165542.png diff --git a/source/c01/c01_04.md b/source/c01/c01_04.md index 713a621..8dbacd2 100644 --- a/source/c01/c01_04.md +++ b/source/c01/c01_04.md @@ -103,4 +103,4 @@ setattr(sys.modules[module], key, --- -![关注公众号,获取最新干货!](http://image.python-online.cn/20191117142849.png) +![](http://image.python-online.cn/20191117142849.png) diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index 9813668..9baf45c 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -118,9 +118,9 @@ -------------- .. figure:: http://image.python-online.cn/20191117142849.png - :alt: 关注公众号,获取最新干货! + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190404215330.png diff --git a/source/c01/c01_05.md b/source/c01/c01_05.md index 79e75fe..9afedee 100644 --- a/source/c01/c01_05.md +++ b/source/c01/c01_05.md @@ -168,4 +168,4 @@ foobar() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index 124fedf..225bd47 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -179,10 +179,10 @@ locals() -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_06.md b/source/c01/c01_06.md index e6048ca..ca2f673 100644 --- a/source/c01/c01_06.md +++ b/source/c01/c01_06.md @@ -101,4 +101,4 @@ print(Xiamen_dict) -------------- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index 2e6fc0e..54d2f72 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -106,10 +106,10 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_07.md b/source/c01/c01_07.md index 4c5647b..c4e1e39 100644 --- a/source/c01/c01_07.md +++ b/source/c01/c01_07.md @@ -293,4 +293,4 @@ b = 2 if a > 2 else 1 - https://foofish.net/idiomatic_part2.html ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index 61a0ef8..55c5c7d 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -350,10 +350,10 @@ Pythonic -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_08.md b/source/c01/c01_08.md index 30a9956..e651615 100644 --- a/source/c01/c01_08.md +++ b/source/c01/c01_08.md @@ -252,4 +252,4 @@ A.__mro__ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 2b0036a..2a0067d 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -271,10 +271,10 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg diff --git a/source/c01/c01_09.md b/source/c01/c01_09.md index 2bd4784..44f3738 100644 --- a/source/c01/c01_09.md +++ b/source/c01/c01_09.md @@ -68,5 +68,5 @@ class Airplane(Vehicle, PlaneMixin): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index 2ff4861..b15f1d5 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -75,10 +75,10 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index 97ce7f3..fbe8fc3 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -2190,4 +2190,4 @@ with close_stdout(): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 1365f15..c63cb31 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -2321,10 +2321,10 @@ python 解释器都是 ``/usr/bin/python`` 。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511165650.png diff --git a/source/c01/c01_11.md b/source/c01/c01_11.md index a934919..f2c9867 100644 --- a/source/c01/c01_11.md +++ b/source/c01/c01_11.md @@ -324,4 +324,4 @@ match.span() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index 1b79c43..4434cff 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -346,10 +346,10 @@ start(),end(),end() -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_12.md b/source/c01/c01_12.md index e8a368c..58e6dd7 100644 --- a/source/c01/c01_12.md +++ b/source/c01/c01_12.md @@ -136,4 +136,4 @@ GB 18030 与 GB 2312-1980 和 GBK 兼容,共收录汉字70244个。 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index 85837f5..a43a912 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -156,10 +156,10 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2017/08/02/598168fe2b016.png diff --git a/source/c01/c01_13.md b/source/c01/c01_13.md index c56cf93..ec0ee13 100644 --- a/source/c01/c01_13.md +++ b/source/c01/c01_13.md @@ -126,4 +126,4 @@ from functools import reduce --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index bfdd509..1616a71 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -159,10 +159,10 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg diff --git a/source/c01/c01_14.md b/source/c01/c01_14.md index 4b0fa4f..efae821 100644 --- a/source/c01/c01_14.md +++ b/source/c01/c01_14.md @@ -197,4 +197,4 @@ with open_func('/Users/MING/mytest.txt') as file_in: ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index 518b8bc..fc93466 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -211,10 +211,10 @@ open)的上下文管理器。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190310172800.png diff --git a/source/c01/c01_15.md b/source/c01/c01_15.md index 2fa8af3..03ca8f1 100644 --- a/source/c01/c01_15.md +++ b/source/c01/c01_15.md @@ -70,4 +70,4 @@ a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index 6071011..8857cff 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -80,10 +80,10 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_16.md b/source/c01/c01_16.md index 687de84..11847b4 100644 --- a/source/c01/c01_16.md +++ b/source/c01/c01_16.md @@ -161,4 +161,4 @@ hello, world ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst index 54e51e1..a7a7787 100644 --- a/source/c01/c01_16.rst +++ b/source/c01/c01_16.rst @@ -168,10 +168,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md index 1b9ca39..fb3f247 100644 --- a/source/c01/c01_17.md +++ b/source/c01/c01_17.md @@ -527,4 +527,4 @@ class Student: --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index d394bd4..b8d3db1 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -579,10 +579,10 @@ super 的实现原理,就交由你来自己完成。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190425221322.png diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md index e36aa12..bc98f95 100644 --- a/source/c01/c01_20.md +++ b/source/c01/c01_20.md @@ -98,5 +98,5 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index 117c9ec..37f8fe9 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -107,10 +107,10 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod 写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190630111243.png diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md index 1ebc32e..b4a4f30 100644 --- a/source/c01/c01_21.md +++ b/source/c01/c01_21.md @@ -85,4 +85,4 @@ with close_stdout(): -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index fea0d27..1f1e420 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -91,10 +91,10 @@ >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) 24 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_22.md b/source/c01/c01_22.md index 6f66b59..6590721 100644 --- a/source/c01/c01_22.md +++ b/source/c01/c01_22.md @@ -190,4 +190,4 @@ $ cloud-init init - https://www.cnblogs.com/stonehe/p/7944366.html -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index 09a4c53..ad03cf8 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -199,10 +199,10 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip - https://www.cnblogs.com/stonehe/p/7944366.html -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190831160317.png diff --git a/source/c01/c01_23.md b/source/c01/c01_23.md index b1f01b4..1c7b47f 100644 --- a/source/c01/c01_23.md +++ b/source/c01/c01_23.md @@ -125,4 +125,4 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index 49bd71c..bc62d96 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -120,10 +120,10 @@ Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md index 9a48f7c..c82e2cf 100644 --- a/source/c01/c01_24.md +++ b/source/c01/c01_24.md @@ -722,4 +722,4 @@ ok -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index b24758a..60e9f8e 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -795,10 +795,10 @@ sys.path)查找器 - https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc - https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191027192949.png diff --git a/source/c01/c01_25.md b/source/c01/c01_25.md index 7f7a1bf..ea6cf52 100644 --- a/source/c01/c01_25.md +++ b/source/c01/c01_25.md @@ -108,4 +108,4 @@ AttributeError: 'module' object has no attribute '__file__' -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index f9d887c..cd9b7c0 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -107,10 +107,10 @@ 因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md index 8096211..71a3729 100644 --- a/source/c01/c01_26.md +++ b/source/c01/c01_26.md @@ -261,4 +261,4 @@ int main(int argc, char const *argv[]) -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index 878caea..a518be9 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -284,10 +284,10 @@ getchar() & putchar() return 0; } -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index a0d4f7c..3b09a1d 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -642,4 +642,4 @@ $ python setup.py upload -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index f5604c8..9701dd2 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -685,10 +685,10 @@ Index)上,它是 Python - http://blog.konghy.cn/2018/04/29/setup-dot-py/ - https://note.qidong.name/2018/01/python-setup-requires/ -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191218202833.png diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md index 264d2c7..16db089 100644 --- a/source/c01/c01_29.md +++ b/source/c01/c01_29.md @@ -10,4 +10,4 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index 71180f4..fcc075b 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -7,10 +7,10 @@ 基于 Python 3.6 的源码分析:https://he11olx.com/ -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_31.md b/source/c01/c01_31.md index ca530b8..870abb5 100644 --- a/source/c01/c01_31.md +++ b/source/c01/c01_31.md @@ -22,4 +22,4 @@ RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G) -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index a84d9b5..0233f91 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -19,10 +19,10 @@ ARGB RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md index f3471bb..9d8e2f4 100644 --- a/source/c01/c01_32.md +++ b/source/c01/c01_32.md @@ -36,4 +36,4 @@ python3 -m pip install --user requests aiohttp cryptography pymysql prettytable -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst index d3e5c46..7006f8d 100644 --- a/source/c01/c01_32.rst +++ b/source/c01/c01_32.rst @@ -33,10 +33,10 @@ requests.get(“https://www.baidu.com”) python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_33.md b/source/c01/c01_33.md index 96fbc6b..d5acadd 100644 --- a/source/c01/c01_33.md +++ b/source/c01/c01_33.md @@ -48,7 +48,7 @@ sudo yum install gdb python-debuginfo -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_33.rst b/source/c01/c01_33.rst index 9fec603..39fd212 100644 --- a/source/c01/c01_33.rst +++ b/source/c01/c01_33.rst @@ -46,10 +46,10 @@ sudo yum install gdb python-debuginfo -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_34.md b/source/c01/c01_34.md index 5a47f3e..e5ad8a0 100644 --- a/source/c01/c01_34.md +++ b/source/c01/c01_34.md @@ -89,4 +89,4 @@ innodb: foreign key constraint system tables created -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index 761febe..c9c425b 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -90,10 +90,10 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 innodb: foreign key constraint system tables created -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200227201644.png diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md index 68322ec..3c72d22 100644 --- a/source/c01/c01_35.md +++ b/source/c01/c01_35.md @@ -381,4 +381,4 @@ ssh.close() -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index 6b5048a..f7d3e8a 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -395,10 +395,10 @@ Windows,这里就有一件好事,一件坏事了,。 - https://www.liujiangblog.com/blog/15/ - http://docs.paramiko.org/en/stable/ -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200228085749.png diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md index e3d3536..e6f136b 100644 --- a/source/c01/c01_36.md +++ b/source/c01/c01_36.md @@ -250,4 +250,4 @@ if __name__ == "__main__": 以上,就是我对 `pretty_errors` 的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,`pretty_errors` 会是一个不错的解决方案,明哥把它推荐给你。 -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst index c9cd9a7..ed0ff15 100644 --- a/source/c01/c01_36.rst +++ b/source/c01/c01_36.rst @@ -268,10 +268,10 @@ PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,\ ``pretty_errors`` 会是一个不错的解决方案,明哥把它推荐给你。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/image-20200307210853246.png diff --git a/source/c01/c01_37.md b/source/c01/c01_37.md index 8454bb8..b206844 100644 --- a/source/c01/c01_37.md +++ b/source/c01/c01_37.md @@ -158,4 +158,4 @@ else: 看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_37.rst b/source/c01/c01_37.rst index 0e09b0d..cd4cd95 100644 --- a/source/c01/c01_37.rst +++ b/source/c01/c01_37.rst @@ -175,10 +175,10 @@ Python 功力。 看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_38.md b/source/c01/c01_38.md index 6bf3ed5..4303123 100644 --- a/source/c01/c01_38.md +++ b/source/c01/c01_38.md @@ -58,4 +58,4 @@ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_38.rst b/source/c01/c01_38.rst index 1d59e40..2896d71 100644 --- a/source/c01/c01_38.rst +++ b/source/c01/c01_38.rst @@ -64,10 +64,10 @@ 个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 python 解释器都是 ``/usr/bin/python`` 。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200331184021.png diff --git a/source/c01/c01_39.md b/source/c01/c01_39.md index 2fc848d..3f37049 100644 --- a/source/c01/c01_39.md +++ b/source/c01/c01_39.md @@ -217,4 +217,4 @@ Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的 -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_39.rst b/source/c01/c01_39.rst index 840c335..b2e997c 100644 --- a/source/c01/c01_39.rst +++ b/source/c01/c01_39.rst @@ -224,10 +224,10 @@ Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_40.md b/source/c01/c01_40.md index e605731..2d70011 100644 --- a/source/c01/c01_40.md +++ b/source/c01/c01_40.md @@ -140,4 +140,4 @@ print(is_in("hello, python", "lol")) # False -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_40.rst b/source/c01/c01_40.rst index dabb092..6903170 100644 --- a/source/c01/c01_40.rst +++ b/source/c01/c01_40.rst @@ -147,10 +147,10 @@ python 代码快。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_41.md b/source/c01/c01_41.md index 7ccfad5..185dfc3 100644 --- a/source/c01/c01_41.md +++ b/source/c01/c01_41.md @@ -211,6 +211,6 @@ sorted(itertools.chain(*iterables)) -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_41.rst b/source/c01/c01_41.rst index 16df3b4..c4e3160 100644 --- a/source/c01/c01_41.rst +++ b/source/c01/c01_41.rst @@ -226,10 +226,10 @@ yield from 意义及使用方法。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_42.md b/source/c01/c01_42.md index 956ef33..5c92a8e 100644 --- a/source/c01/c01_42.md +++ b/source/c01/c01_42.md @@ -172,4 +172,4 @@ fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_42.rst b/source/c01/c01_42.rst index 47bc5b1..af93dd7 100644 --- a/source/c01/c01_42.rst +++ b/source/c01/c01_42.rst @@ -175,10 +175,10 @@ Golang,那这里要注意,Golang 中的 ``:=`` -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/image-20200418122739417.png diff --git a/source/c01/c01_43.md b/source/c01/c01_43.md index 08da1b7..f5c9bc6 100644 --- a/source/c01/c01_43.md +++ b/source/c01/c01_43.md @@ -350,4 +350,4 @@ json.dumps 的关键参数有两个: -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_43.rst b/source/c01/c01_43.rst index affda76..e62f4cd 100644 --- a/source/c01/c01_43.rst +++ b/source/c01/c01_43.rst @@ -381,10 +381,10 @@ json.dumps 的关键参数有两个: 以上。希望此文能对你有帮助。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200507171451.png diff --git a/source/c01/c01_44.md b/source/c01/c01_44.md index 9d8f50a..5f818e4 100644 --- a/source/c01/c01_44.md +++ b/source/c01/c01_44.md @@ -271,4 +271,4 @@ sys.setdefaultencoding('utf-8') -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_44.rst b/source/c01/c01_44.rst index 3b92e50..69bf823 100644 --- a/source/c01/c01_44.rst +++ b/source/c01/c01_44.rst @@ -286,10 +286,10 @@ sys.setdefaultencoding 这个方法。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200423185819.png diff --git a/source/c01/c01_45.md b/source/c01/c01_45.md index b9e9e2f..caa4153 100644 --- a/source/c01/c01_45.md +++ b/source/c01/c01_45.md @@ -287,4 +287,4 @@ ok -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_45.rst b/source/c01/c01_45.rst index 4629688..95e46a6 100644 --- a/source/c01/c01_45.rst +++ b/source/c01/c01_45.rst @@ -302,10 +302,10 @@ python 文件,如果后面导入成功会打印 ``ok``\ 。 这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习\ ``__import__``\ 以及 importlib 是非常有必要的。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_01.md b/source/c02/c02_01.md index fd3a1dd..521c3cd 100644 --- a/source/c02/c02_01.md +++ b/source/c02/c02_01.md @@ -237,4 +237,4 @@ multi_process(io_simulation, type="模拟IO密集型") - 多进程虽然总是最快的,但是不一定是最优的选择,因为它需要CPU资源支持下才能体现优势 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index 57b79d8..a49981c 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -250,10 +250,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png diff --git a/source/c02/c02_02.md b/source/c02/c02_02.md index 08a1dcb..e451a67 100644 --- a/source/c02/c02_02.md +++ b/source/c02/c02_02.md @@ -133,4 +133,4 @@ t.name = "My-Thread" 至此,Python线程基础知识,我们大概都介绍完了。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index ba358a6..80db84d 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -147,10 +147,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_03.md b/source/c02/c02_03.md index 8541826..095a76c 100644 --- a/source/c02/c02_03.md +++ b/source/c02/c02_03.md @@ -326,4 +326,4 @@ t2.start() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index f936804..d6d5148 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -354,10 +354,10 @@ CPython,所以也就默许了Python具有GIL锁这个事。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index cd18c0e..4049c74 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -281,4 +281,4 @@ teacher.call('小亮') ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 533e1e8..340de57 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -309,10 +309,10 @@ Queue.task_done(),说明队列这个任务已经结束了。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_05.md b/source/c02/c02_05.md index cf71504..f87c9db 100644 --- a/source/c02/c02_05.md +++ b/source/c02/c02_05.md @@ -251,4 +251,4 @@ item5 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 1c45439..7cbc047 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -261,10 +261,10 @@ Out),就是先进入队列的消息,将优先被消费。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index ceaf744..19ba6bf 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -149,4 +149,4 @@ running thread-123145485651968:1 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index c03c6db..cb85e5b 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -156,10 +156,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_07.md b/source/c02/c02_07.md index b0b3aec..a743540 100644 --- a/source/c02/c02_07.md +++ b/source/c02/c02_07.md @@ -366,4 +366,4 @@ if __name__ == '__main__': 下一章,我将讲一个Python3.5新引入的语法:`yield from`。篇幅也比较多,所以就单独拿出来讲。 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index e6ea410..3088304 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -392,10 +392,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190527123516.png diff --git a/source/c02/c02_08.md b/source/c02/c02_08.md index 9456748..ea82375 100644 --- a/source/c02/c02_08.md +++ b/source/c02/c02_08.md @@ -335,4 +335,4 @@ RESULT = _r --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index a801241..325b8e7 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -361,10 +361,10 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_09.md b/source/c02/c02_09.md index 1d3d363..540d0c8 100644 --- a/source/c02/c02_09.md +++ b/source/c02/c02_09.md @@ -222,4 +222,4 @@ emmm,和上面的结果是一样的。nice --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 202c42a..b332bf3 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -246,10 +246,10 @@ emmm,和上面的结果是一样的。nice -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |普通函数中 不能使用 await| image:: https://i.loli.net/2018/05/26/5b09794f45340.png diff --git a/source/c02/c02_10.md b/source/c02/c02_10.md index b22ab09..82a253e 100644 --- a/source/c02/c02_10.md +++ b/source/c02/c02_10.md @@ -460,4 +460,4 @@ loop.close() ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index dc4381b..019401e 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -509,10 +509,10 @@ asyncio.gather -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_11.md b/source/c02/c02_11.md index f8e5879..602e681 100644 --- a/source/c02/c02_11.md +++ b/source/c02/c02_11.md @@ -213,4 +213,4 @@ Thu May 31 23:42:48 2018 ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 12f71aa..84b908c 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -218,10 +218,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/06/03/5b13ba8525bcf.png diff --git a/source/c02/c02_12.md b/source/c02/c02_12.md index 2ca157d..45c34a7 100644 --- a/source/c02/c02_12.md +++ b/source/c02/c02_12.md @@ -110,4 +110,4 @@ MING.send('香肠') --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index fe84d8b..f2b017d 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -109,10 +109,10 @@ yield -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_14.md b/source/c02/c02_14.md index f8af4af..2c2c526 100644 --- a/source/c02/c02_14.md +++ b/source/c02/c02_14.md @@ -180,4 +180,4 @@ https://juejin.im/post/5b129a1be51d45068a6c91d4#comment -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c02/c02_14.rst b/source/c02/c02_14.rst index 79ab29d..204dd46 100644 --- a/source/c02/c02_14.rst +++ b/source/c02/c02_14.rst @@ -193,10 +193,10 @@ https://zhuanlan.zhihu.com/p/34150765 https://juejin.im/post/5b129a1be51d45068a6c91d4#comment -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200506080445.png diff --git a/source/c03/c03_01.md b/source/c03/c03_01.md index 716091f..b97b33f 100644 --- a/source/c03/c03_01.md +++ b/source/c03/c03_01.md @@ -706,4 +706,4 @@ def timeout_limit(timeout_time): 我的文章更新频率是远低于其他 Python 技术号,但我仍然坚持自己,坚持原创,每周虽然只有一篇,但我能保证我的每一篇文章都是诚意之作。希望那些对你有帮助的文章能够多多帮忙转发分享。这也是我更新的一大动力。非常感谢。 -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 9559d36..66f588c 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -775,10 +775,10 @@ property 我的文章更新频率是远低于其他 Python 技术号,但我仍然坚持自己,坚持原创,每周虽然只有一篇,但我能保证我的每一篇文章都是诚意之作。希望那些对你有帮助的文章能够多多帮忙转发分享。这也是我更新的一大动力。非常感谢。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190811100737.png diff --git a/source/c03/c03_02.md b/source/c03/c03_02.md index bed0f4c..8ee46db 100644 --- a/source/c03/c03_02.md +++ b/source/c03/c03_02.md @@ -346,4 +346,4 @@ in User ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index f03f4d4..9a02005 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -376,10 +376,10 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c03/c03_03.md b/source/c03/c03_03.md index 33c1814..bc5dee3 100644 --- a/source/c03/c03_03.md +++ b/source/c03/c03_03.md @@ -259,4 +259,4 @@ if __name__ == '__main__': ---- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index abd5879..bd40607 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -294,10 +294,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png diff --git a/source/c03/c03_04.md b/source/c03/c03_04.md index d425cda..6437148 100644 --- a/source/c03/c03_04.md +++ b/source/c03/c03_04.md @@ -453,4 +453,4 @@ vim /etc/nginx/nginx.conf --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index 1bcc0f4..d2c890a 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -512,10 +512,10 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2017/08/20/599982f513b7e.png diff --git a/source/c03/c03_05.md b/source/c03/c03_05.md index 9bb787e..da3aacc 100644 --- a/source/c03/c03_05.md +++ b/source/c03/c03_05.md @@ -459,4 +459,4 @@ with app.app_context(): --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index a58ba7a..9ad5043 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -500,10 +500,10 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX diff --git a/source/c03/c03_06.md b/source/c03/c03_06.md index 664a654..728a25b 100644 --- a/source/c03/c03_06.md +++ b/source/c03/c03_06.md @@ -772,4 +772,4 @@ meth : /etc/netns/ns1/resolv.conf --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index f0cbf21..eb45035 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -319,10 +319,10 @@ Socket发些“奇怪”的数据。 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/18-1-28/92519416.jpg diff --git a/source/c07/c07_06.md b/source/c07/c07_06.md index 6058f0b..76a9555 100644 --- a/source/c07/c07_06.md +++ b/source/c07/c07_06.md @@ -282,4 +282,4 @@ docker-machine scp bm-docker-01:/tmp/a bm-docker-02:/tmp/b --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c07/c07_06.rst b/source/c07/c07_06.rst index 56a5126..d5eb751 100755 --- a/source/c07/c07_06.rst +++ b/source/c07/c07_06.rst @@ -328,10 +328,10 @@ centos的配置文件路径如下,ubuntu的有所不同 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png diff --git a/source/c07/c07_07.md b/source/c07/c07_07.md index acd0345..1a46442 100644 --- a/source/c07/c07_07.md +++ b/source/c07/c07_07.md @@ -355,4 +355,4 @@ salt minion01 saltutil.sync_grains # 只同步grains --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c07/c07_07.rst b/source/c07/c07_07.rst index a567c9a..a12e525 100755 --- a/source/c07/c07_07.rst +++ b/source/c07/c07_07.rst @@ -418,10 +418,10 @@ salt命令格式 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c07/c07_08.rst b/source/c07/c07_08.rst index 66e9f37..62dc6f3 100644 --- a/source/c07/c07_08.rst +++ b/source/c07/c07_08.rst @@ -175,10 +175,10 @@ chk_zabbix.sh -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c07/c07_10.md b/source/c07/c07_10.md index 089d3c7..ff003a0 100644 --- a/source/c07/c07_10.md +++ b/source/c07/c07_10.md @@ -173,5 +173,5 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index a09923c..1a33466 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -186,10 +186,10 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190716111523.png diff --git a/source/c07/c07_11.md b/source/c07/c07_11.md index 49239f4..d971a32 100644 --- a/source/c07/c07_11.md +++ b/source/c07/c07_11.md @@ -80,4 +80,4 @@ K8s 角色详解 -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst index 29370c7..2e7d4b9 100644 --- a/source/c07/c07_11.rst +++ b/source/c07/c07_11.rst @@ -77,10 +77,10 @@ K8s 角色详解 里可以运行一个容器(最常用),也可以运行多个容器,若运行多个容器,那这几个容器的工作必定有着紧密的联系,而且需要直接 **共享资源**\ 。 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190907162015.png diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md index 71eca1b..e6b31cc 100644 --- a/source/c07/c07_12.md +++ b/source/c07/c07_12.md @@ -221,4 +221,4 @@ rpm –rebuilddb -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index 42547e8..8674ac0 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -208,10 +208,10 @@ rpm # 重建rpm数据文件 rpm –rebuilddb -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191219152328.png diff --git a/source/c07/c07_13.md b/source/c07/c07_13.md index 8732f66..dcc0e57 100644 --- a/source/c07/c07_13.md +++ b/source/c07/c07_13.md @@ -109,4 +109,4 @@ class ResultCallback(CallbackModule): -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_13.rst b/source/c07/c07_13.rst index 7e7816e..46b9137 100644 --- a/source/c07/c07_13.rst +++ b/source/c07/c07_13.rst @@ -117,10 +117,10 @@ ansible 的 api 开发出来的吗? - v2_runner_on_skipped:部署任务跳过时,会调用 - v2_playbook_on_stats:所有的部署任务完成时,调用 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c07/c07_14.md b/source/c07/c07_14.md index c588e70..36d764f 100644 --- a/source/c07/c07_14.md +++ b/source/c07/c07_14.md @@ -40,4 +40,4 @@ filename1 -ot filename2 如果 filename1比 filename2旧,则为真。 -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_14.rst b/source/c07/c07_14.rst index 68cdd04..7cdeb95 100644 --- a/source/c07/c07_14.rst +++ b/source/c07/c07_14.rst @@ -26,10 +26,10 @@ filename可读,则为真 ``-w filename``: 如果 filename可写,则为真 -eq :等于 -ne :不等于 -gt :大于 -ge :大于等于 -lt :小于 -le :小于等于 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c07/c07_15.md b/source/c07/c07_15.md index 4085907..0efaa7a 100644 --- a/source/c07/c07_15.md +++ b/source/c07/c07_15.md @@ -216,4 +216,4 @@ show status like '%wsrep%'; - http://www.360doc.com/content/13/0817/15/834950_307820923.shtml - http://blog.itpub.net/30126024/viewspace-2221483/ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_15.rst b/source/c07/c07_15.rst index 8278c3c..4394958 100644 --- a/source/c07/c07_15.rst +++ b/source/c07/c07_15.rst @@ -219,10 +219,10 @@ wsrep_flow_control_sent 和 wsrep_local_recv_queue_avg - http://www.360doc.com/content/13/0817/15/834950_307820923.shtml - http://blog.itpub.net/30126024/viewspace-2221483/ -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191213162259.png diff --git a/source/c07/c07_16.md b/source/c07/c07_16.md index 8459b7b..b63f093 100644 --- a/source/c07/c07_16.md +++ b/source/c07/c07_16.md @@ -91,4 +91,4 @@ result=`random_range 1 60` -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index 80d215b..99b4472 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -83,10 +83,10 @@ result=`random_range 1 60` -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191220202103.png diff --git a/source/c07/c07_17.md b/source/c07/c07_17.md index fd6944f..3d78407 100644 --- a/source/c07/c07_17.md +++ b/source/c07/c07_17.md @@ -20,4 +20,4 @@ https://www.jianshu.com/p/ae74f5f39828 -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_17.rst b/source/c07/c07_17.rst index 19ad5b3..381f095 100644 --- a/source/c07/c07_17.rst +++ b/source/c07/c07_17.rst @@ -13,10 +13,10 @@ https://blog.oddbit.com/post/2019-04-25-writing-ansible-filter-plugins/ https://www.jianshu.com/p/ae74f5f39828 -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c07/c07_18.md b/source/c07/c07_18.md index b3f3a22..0163a7f 100644 --- a/source/c07/c07_18.md +++ b/source/c07/c07_18.md @@ -65,4 +65,4 @@ A BC -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_18.rst b/source/c07/c07_18.rst index 93fa977..7a6ef11 100644 --- a/source/c07/c07_18.rst +++ b/source/c07/c07_18.rst @@ -62,10 +62,10 @@ [root@linux ~]# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1' A BC -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c07/c07_19.md b/source/c07/c07_19.md index 45cca7b..1fed505 100644 --- a/source/c07/c07_19.md +++ b/source/c07/c07_19.md @@ -47,4 +47,4 @@ $ ansible 172.20.20.1 -m ping -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_19.rst b/source/c07/c07_19.rst index c17e689..78c8600 100644 --- a/source/c07/c07_19.rst +++ b/source/c07/c07_19.rst @@ -48,10 +48,10 @@ $ ansible 172.20.20.1 -m ping -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c08/c08_01.md b/source/c08/c08_01.md index 2f0f2d5..ff8812c 100644 --- a/source/c08/c08_01.md +++ b/source/c08/c08_01.md @@ -432,4 +432,4 @@ pkill -9 pacemaker;service pacemaker restart --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index e17b6fc..0014c6f 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -446,10 +446,10 @@ aggregate管理 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c08/c08_02.md b/source/c08/c08_02.md index 601c9fd..30a0018 100644 --- a/source/c08/c08_02.md +++ b/source/c08/c08_02.md @@ -266,4 +266,4 @@ select * from pci_devices where instance_uuid='5a1c1828-4190-43fa-8e05-ae51b0196 ------ -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index 7b746da..da2a961 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -305,10 +305,10 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/01/19/5a61bfa0ca66f.png diff --git a/source/c08/c08_03.md b/source/c08/c08_03.md index 1ed1c5a..23e8cb0 100644 --- a/source/c08/c08_03.md +++ b/source/c08/c08_03.md @@ -486,4 +486,4 @@ $ virsh blockcommit ws_controller01 hda --active --verbose --pivot --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index d9f0440..1f0876a 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -530,10 +530,10 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png diff --git a/source/c08/c08_04.md b/source/c08/c08_04.md index 792c96f..12ade3c 100644 --- a/source/c08/c08_04.md +++ b/source/c08/c08_04.md @@ -435,4 +435,4 @@ nova boot \ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index a68ded8..8c58904 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -485,10 +485,10 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190714161353.png diff --git a/source/c08/c08_05.md b/source/c08/c08_05.md index 2fb5c9b..1058733 100644 --- a/source/c08/c08_05.md +++ b/source/c08/c08_05.md @@ -672,4 +672,4 @@ python -c 'import crypt,getpass;pw=getpass.getpass();print(crypt.crypt(pw,crypt. --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index ef3b20e..0027c76 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -732,10 +732,10 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190526144846.png diff --git a/source/c08/c08_06.md b/source/c08/c08_06.md index 7817eb2..39463be 100644 --- a/source/c08/c08_06.md +++ b/source/c08/c08_06.md @@ -626,4 +626,4 @@ ip addr flush dev ens3 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index 95c2669..7cf352a 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -707,10 +707,10 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190430204707.png diff --git a/source/c08/c08_07.md b/source/c08/c08_07.md index b59582f..adf8987 100644 --- a/source/c08/c08_07.md +++ b/source/c08/c08_07.md @@ -125,4 +125,4 @@ openstack image set --property img_hide_hypervisor_id=true --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index 4c07921..b39f7d0 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -150,10 +150,10 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190419144135.png diff --git a/source/c08/c08_08.md b/source/c08/c08_08.md index 489747c..14161ec 100644 --- a/source/c08/c08_08.md +++ b/source/c08/c08_08.md @@ -187,4 +187,4 @@ systemctl restart neutron-openvswitch-agent --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index 6286354..a1b598f 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -251,10 +251,10 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190514202013.png diff --git a/source/c08/c08_09.md b/source/c08/c08_09.md index 86b5adb..39d5279 100644 --- a/source/c08/c08_09.md +++ b/source/c08/c08_09.md @@ -678,4 +678,4 @@ server.wait() --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index 3409f00..b0e445a 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -784,10 +784,10 @@ rpc server 和rpc client 的四个重要方法 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190623185008.png diff --git a/source/c08/c08_11.md b/source/c08/c08_11.md index d359250..05883df 100644 --- a/source/c08/c08_11.md +++ b/source/c08/c08_11.md @@ -82,4 +82,4 @@ $ nova reboot --hard -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index 8560483..db28b76 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -84,10 +84,10 @@ ConfigDrive 是一个 iso9660 格式的文件,只读。 # 硬重启,重新生成 xml,如果有必要的话 $ nova reboot --hard -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190530175817.png diff --git a/source/c08/c08_12.md b/source/c08/c08_12.md index 17c67f1..1ad11bb 100644 --- a/source/c08/c08_12.md +++ b/source/c08/c08_12.md @@ -70,4 +70,4 @@ LOG.debug("Selected host: %(host)s", {'host': chosen_host}) --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index ea1c18e..aba42bc 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -88,10 +88,10 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190424212211.png diff --git a/source/c08/c08_13.md b/source/c08/c08_13.md index 8e26536..a472ab7 100644 --- a/source/c08/c08_13.md +++ b/source/c08/c08_13.md @@ -314,4 +314,4 @@ ovs-vsctl set port vnet0 tag=4 --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index a85b399..21e3981 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -314,10 +314,10 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190706114314.png diff --git a/source/c08/c08_14.md b/source/c08/c08_14.md index 4256cae..c12b178 100644 --- a/source/c08/c08_14.md +++ b/source/c08/c08_14.md @@ -331,4 +331,4 @@ sudo sysctl -p --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index ccaa3ab..3652947 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -325,10 +325,10 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190716175250.png diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index d1cfba5..a4321dd 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -143,4 +143,4 @@ img_new.convert('RGB').save("F://save.jpeg") -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index bcdce1d..1cd703d 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -152,10 +152,10 @@ img_new.convert('RGB').save("F://save.jpeg") -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200214104413.png diff --git a/source/c10/c10_01.md b/source/c10/c10_01.md index 94d9bf8..8b9bb69 100644 --- a/source/c10/c10_01.md +++ b/source/c10/c10_01.md @@ -274,4 +274,4 @@ $ sudo dnsmasq restart -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst index bbf6ded..92b1c49 100644 --- a/source/c10/c10_01.rst +++ b/source/c10/c10_01.rst @@ -332,10 +332,10 @@ Linux # 服务器或者路由器使用DNSMASQ $ sudo dnsmasq restart -.. figure:: http://image.python-online.cn/image-20200320125724880.png - :alt: 关注公众号,获取最新干货! +.. figure:: http://image.iswbm.com/20200607174235.png + :alt: + - 关注公众号,获取最新干货! .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/464291-20170703113844956-354755333.jpg diff --git a/source/chapters/p01.rst b/source/chapters/p01.rst index 93de56a..202c6bf 100755 --- a/source/chapters/p01.rst +++ b/source/chapters/p01.rst @@ -26,4 +26,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p02.rst b/source/chapters/p02.rst index f0eeafa..6e94a2e 100755 --- a/source/chapters/p02.rst +++ b/source/chapters/p02.rst @@ -18,4 +18,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p03.rst b/source/chapters/p03.rst index 054b6b7..bde8b30 100755 --- a/source/chapters/p03.rst +++ b/source/chapters/p03.rst @@ -18,4 +18,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p04.rst b/source/chapters/p04.rst index e366137..6141a9d 100755 --- a/source/chapters/p04.rst +++ b/source/chapters/p04.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p05.rst b/source/chapters/p05.rst index 1a47f88..dbea992 100755 --- a/source/chapters/p05.rst +++ b/source/chapters/p05.rst @@ -28,4 +28,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p06.rst b/source/chapters/p06.rst index 5d015f8..c398e34 100755 --- a/source/chapters/p06.rst +++ b/source/chapters/p06.rst @@ -17,4 +17,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p07.rst b/source/chapters/p07.rst index 2ae6b0b..4e7675b 100755 --- a/source/chapters/p07.rst +++ b/source/chapters/p07.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p08.rst b/source/chapters/p08.rst index 9189ce8..0f25823 100755 --- a/source/chapters/p08.rst +++ b/source/chapters/p08.rst @@ -16,4 +16,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p09.rst b/source/chapters/p09.rst index 4571ac5..5161c9b 100644 --- a/source/chapters/p09.rst +++ b/source/chapters/p09.rst @@ -18,4 +18,4 @@ Python 是一门可以快速开发的语言,使用它可以用最少的时间 -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/chapters/p10.rst b/source/chapters/p10.rst index 9f0594a..b41a354 100755 --- a/source/chapters/p10.rst +++ b/source/chapters/p10.rst @@ -20,4 +20,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/index.rst b/source/index.rst index 0071da4..15e3000 100755 --- a/source/index.rst +++ b/source/index.rst @@ -22,4 +22,4 @@ Contents: -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/preface.rst b/source/preface.rst index 8ffbf73..4651dd5 100755 --- a/source/preface.rst +++ b/source/preface.rst @@ -31,6 +31,6 @@ ------------------------------ -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png .. _博客构建教程: http://python-online.cn/zh_CN/latest/c04/c04_03.html diff --git a/source/py_mp_index.md b/source/py_mp_index.md index 62880d9..9c4fc32 100644 --- a/source/py_mp_index.md +++ b/source/py_mp_index.md @@ -458,4 +458,4 @@ --- -![关注公众号,获取最新干货!](http://image.python-online.cn/image-20200320125724880.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/roadmap.rst b/source/roadmap.rst index a2f790b..fe0749d 100755 --- a/source/roadmap.rst +++ b/source/roadmap.rst @@ -12,4 +12,4 @@ Roadmap -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png diff --git a/source/thanks.rst b/source/thanks.rst index d9b6b7a..41808de 100644 --- a/source/thanks.rst +++ b/source/thanks.rst @@ -25,4 +25,4 @@ -------------- -.. figure:: http://image.python-online.cn/image-20200320125724880.png +.. figure:: http://image.iswbm.com/20200607174235.png From 933fdf2d2b671a4e554662d43d029e684f0e4673 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Fri, 12 Jun 2020 08:53:09 +0800 Subject: [PATCH 082/147] update footer image --- source/.DS_Store | Bin 12292 -> 12292 bytes source/c01/c01_01.rst | 6 ++---- source/c01/c01_04.rst | 6 ++---- source/c01/c01_05.rst | 6 ++---- source/c01/c01_06.rst | 6 ++---- source/c01/c01_07.rst | 6 ++---- source/c01/c01_08.rst | 6 ++---- source/c01/c01_09.rst | 6 ++---- source/c01/c01_10.rst | 6 ++---- source/c01/c01_11.rst | 6 ++---- source/c01/c01_12.rst | 6 ++---- source/c01/c01_13.rst | 6 ++---- source/c01/c01_14.rst | 6 ++---- source/c01/c01_15.rst | 6 ++---- source/c01/c01_16.rst | 6 ++---- source/c01/c01_17.rst | 6 ++---- source/c01/c01_20.rst | 6 ++---- source/c01/c01_21.rst | 6 ++---- source/c01/c01_22.rst | 6 ++---- source/c01/c01_23.rst | 6 ++---- source/c01/c01_24.rst | 6 ++---- source/c01/c01_25.rst | 6 ++---- source/c01/c01_26.md | 21 +-------------------- source/c01/c01_26.rst | 29 ++--------------------------- source/c01/c01_27.rst | 6 ++---- source/c01/c01_29.rst | 6 ++---- source/c01/c01_31.rst | 6 ++---- source/c01/c01_32.rst | 6 ++---- source/c01/c01_33.rst | 6 ++---- source/c01/c01_34.rst | 6 ++---- source/c01/c01_35.rst | 6 ++---- source/c01/c01_36.rst | 6 ++---- source/c01/c01_37.rst | 6 ++---- source/c01/c01_38.rst | 6 ++---- source/c01/c01_39.rst | 6 ++---- source/c01/c01_40.rst | 6 ++---- source/c01/c01_41.rst | 6 ++---- source/c01/c01_42.rst | 6 ++---- source/c01/c01_43.rst | 6 ++---- source/c01/c01_44.rst | 6 ++---- source/c01/c01_45.rst | 6 ++---- source/c02/c02_01.rst | 6 ++---- source/c02/c02_02.rst | 6 ++---- source/c02/c02_03.rst | 6 ++---- source/c02/c02_04.rst | 6 ++---- source/c02/c02_05.rst | 6 ++---- source/c02/c02_06.rst | 6 ++---- source/c02/c02_07.rst | 6 ++---- source/c02/c02_08.rst | 6 ++---- source/c02/c02_09.rst | 6 ++---- source/c02/c02_10.rst | 6 ++---- source/c02/c02_11.rst | 6 ++---- source/c02/c02_12.rst | 6 ++---- source/c02/c02_14.rst | 6 ++---- source/c03/c03_01.rst | 6 ++---- source/c03/c03_02.rst | 6 ++---- source/c03/c03_03.rst | 6 ++---- source/c03/c03_04.rst | 6 ++---- source/c03/c03_05.rst | 6 ++---- source/c03/c03_06.rst | 6 ++---- source/c04/c04_01.rst | 6 ++---- source/c04/c04_02.rst | 6 ++---- source/c04/c04_03.rst | 6 ++---- source/c04/c04_04.rst | 6 ++---- source/c04/c04_05.rst | 6 ++---- source/c04/c04_06.rst | 6 ++---- source/c04/c04_07.rst | 6 ++---- source/c04/c04_08.rst | 6 ++---- source/c04/c04_09.rst | 6 ++---- source/c04/c04_10.rst | 6 ++---- source/c04/c04_11.rst | 6 ++---- source/c04/c04_12.rst | 6 ++---- source/c04/c04_13.rst | 6 ++---- source/c04/c04_14.rst | 6 ++---- source/c04/c04_15.rst | 6 ++---- source/c04/c04_16.rst | 6 ++---- source/c04/c04_17.rst | 6 ++---- source/c04/c04_18.rst | 6 ++---- source/c04/c04_19.rst | 6 ++---- source/c04/c04_21.rst | 6 ++---- source/c04/c04_24.rst | 6 ++---- source/c05/c05_01.rst | 6 ++---- source/c05/c05_02.rst | 6 ++---- source/c05/c05_03.rst | 6 ++---- source/c06/c06_01.rst | 6 ++---- source/c06/c06_02.rst | 6 ++---- source/c06/c06_03.rst | 6 ++---- source/c06/c06_04.rst | 6 ++---- source/c06/c06_05.rst | 6 ++---- source/c06/c06_06.rst | 6 ++---- source/c07/c07_01.rst | 6 ++---- source/c07/c07_02.rst | 6 ++---- source/c07/c07_03.rst | 6 ++---- source/c07/c07_04.rst | 6 ++---- source/c07/c07_05.rst | 6 ++---- source/c07/c07_06.rst | 6 ++---- source/c07/c07_07.rst | 6 ++---- source/c07/c07_08.rst | 6 ++---- source/c07/c07_10.rst | 6 ++---- source/c07/c07_11.rst | 6 ++---- source/c07/c07_12.rst | 6 ++---- source/c07/c07_13.rst | 6 ++---- source/c07/c07_14.rst | 6 ++---- source/c07/c07_15.rst | 6 ++---- source/c07/c07_16.rst | 6 ++---- source/c07/c07_17.rst | 6 ++---- source/c07/c07_18.rst | 6 ++---- source/c07/c07_19.rst | 6 ++---- source/c07/c07_21.md | 2 +- source/c07/c07_21.rst | 2 +- source/c08/c08_01.rst | 6 ++---- source/c08/c08_02.rst | 6 ++---- source/c08/c08_03.rst | 6 ++---- source/c08/c08_04.rst | 6 ++---- source/c08/c08_05.rst | 6 ++---- source/c08/c08_06.rst | 6 ++---- source/c08/c08_07.rst | 6 ++---- source/c08/c08_08.rst | 6 ++---- source/c08/c08_09.rst | 6 ++---- source/c08/c08_11.rst | 6 ++---- source/c08/c08_12.rst | 6 ++---- source/c08/c08_13.rst | 6 ++---- source/c08/c08_14.rst | 6 ++---- source/c09/c09_01.rst | 6 ++---- source/c10/c10_01.rst | 6 ++---- source/c10/c10_02.md | 10 +++++++++- source/c10/c10_02.rst | 5 +++++ source/c10/c10_03.md | 3 +++ source/c10/c10_03.rst | 5 +++++ 129 files changed, 267 insertions(+), 530 deletions(-) diff --git a/source/.DS_Store b/source/.DS_Store index 8b16f25cacfdffc0fff13bc7781052a0a241216d..f66a82f92fda39d3b637c1750247e57a245e6021 100644 GIT binary patch delta 14 VcmZokXi3x^Lo)xDF7>H1tb6f delta 14 VcmZokXi3h^Lo)xDF7>B1tS0e diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 7d1375e..76e531c 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -316,12 +316,10 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511165542.png .. |image2| image:: http://image.python-online.cn/20190511165551.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index 9baf45c..13384fd 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -117,11 +117,9 @@ -------------- -.. figure:: http://image.python-online.cn/20191117142849.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190404215330.png +.. |image2| image:: http://image.python-online.cn/20191117142849.png diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index 225bd47..a1712cc 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -179,10 +179,8 @@ locals() -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index 54d2f72..9a21991 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -106,10 +106,8 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index 55c5c7d..24a196f 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -350,10 +350,8 @@ Pythonic -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 2a0067d..14ff6f1 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -271,10 +271,7 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg @@ -283,4 +280,5 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 .. |image4| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg .. |image5| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg .. |image6| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg +.. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index b15f1d5..7924b47 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -75,10 +75,8 @@ C3 算法,如果你还不清楚,可以点击我的另一篇文章 ,了解 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index c63cb31..1c8a7a6 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -2321,10 +2321,7 @@ python 解释器都是 ``/usr/bin/python`` 。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image13| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511165650.png @@ -2339,4 +2336,5 @@ python 解释器都是 ``/usr/bin/python`` 。 .. |image10| image:: http://image.python-online.cn/20200331184755.png .. |image11| image:: http://image.python-online.cn/20200331185741.png .. |image12| image:: http://image.python-online.cn/20200331190224.png +.. |image13| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index 4434cff..0334634 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -346,10 +346,8 @@ start(),end(),end() -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index a43a912..540dd6e 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -156,12 +156,10 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2017/08/02/598168fe2b016.png .. |image2| image:: https://i.loli.net/2017/08/02/59816d652aeb9.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index 1616a71..e5f826c 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -159,11 +159,9 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index fc93466..62ccd87 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -211,11 +211,9 @@ open)的上下文管理器。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190310172800.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index 8857cff..1ed39f1 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -80,10 +80,8 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_16.rst b/source/c01/c01_16.rst index a7a7787..50c0dcd 100644 --- a/source/c01/c01_16.rst +++ b/source/c01/c01_16.rst @@ -168,10 +168,8 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index b8d3db1..2594d3b 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -579,14 +579,12 @@ super 的实现原理,就交由你来自己完成。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190425221322.png .. |image2| image:: http://image.python-online.cn/20190425221322.png .. |image3| image:: http://image.python-online.cn/20190425221233.png .. |image4| image:: http://image.python-online.cn/20190519001930.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index 37f8fe9..de5e995 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -107,14 +107,12 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod 写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190630111243.png .. |image2| image:: http://image.python-online.cn/20190630104956.png .. |image3| image:: http://image.python-online.cn/20190630105735.png .. |image4| image:: http://image.python-online.cn/20190630105600.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index 1f1e420..a3b4ae5 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -91,10 +91,8 @@ >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) 24 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index ad03cf8..ccd94f4 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -199,11 +199,9 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip - https://www.cnblogs.com/stonehe/p/7944366.html -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190831160317.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index bc62d96..6b03b45 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -120,10 +120,8 @@ Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index 60e9f8e..e5c2fdd 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -795,11 +795,9 @@ sys.path)查找器 - https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc - https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191027192949.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index cd9b7c0..f7fda8f 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -107,10 +107,8 @@ 因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md index 71a3729..c4dafae 100644 --- a/source/c01/c01_26.md +++ b/source/c01/c01_26.md @@ -109,26 +109,7 @@ $ ./a.out -## 字符串学习 - -### 格式式字符串 - -```shell -%d   有符号10进制整数(%ld 长整型,%hd短整型 ) -%hu   无符号短整形(%u无符号整形,%lu无符号长整形) -%i   有符号10进制整数 (%i 和%d 没有区别,%i 是老式写法,都是整型格式) - -%o   无符号8进制整数 -%u   无符号10进制整数 -%x   无符号的16进制数字,并以小写abcdef表示 -%X   无符号的16进制数字,并以大写ABCDEF表示 - -%f   输入输出为浮点型 (%lf双精度浮点型) -%E/e 用科学表示格式的浮点数 - -%c 输入输出为单个字符 -%s 输入输出为字符串 -``` +## ### 字符串声明定义 diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index a518be9..b576f57 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -129,29 +129,6 @@ Xcode,您就能使用 GNU 编译器。 "version": 4 } -字符串学习 ----------- - -格式式字符串 -~~~~~~~~~~~~ - -.. code:: shell - - %d   有符号10进制整数(%ld 长整型,%hd短整型 ) - %hu   无符号短整形(%u无符号整形,%lu无符号长整形) - %i   有符号10进制整数 (%i 和%d 没有区别,%i 是老式写法,都是整型格式) - - %o   无符号8进制整数 - %u   无符号10进制整数 - %x   无符号的16进制数字,并以小写abcdef表示 - %X   无符号的16进制数字,并以大写ABCDEF表示 - - %f   输入输出为浮点型 (%lf双精度浮点型) - %E/e 用科学表示格式的浮点数 - - %c 输入输出为单个字符 - %s 输入输出为字符串 - 字符串声明定义 ~~~~~~~~~~~~~~ @@ -284,10 +261,8 @@ getchar() & putchar() return 0; } -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 9701dd2..9ffabbd 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -685,14 +685,12 @@ Index)上,它是 Python - http://blog.konghy.cn/2018/04/29/setup-dot-py/ - https://note.qidong.name/2018/01/python-setup-requires/ -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191218202833.png .. |image2| image:: http://image.python-online.cn/20191218203005.png .. |image3| image:: http://image.python-online.cn/20191218203255.png .. |image4| image:: http://image.python-online.cn/20191218203517.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index fcc075b..f801300 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -7,10 +7,8 @@ 基于 Python 3.6 的源码分析:https://he11olx.com/ -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst index 0233f91..536d407 100644 --- a/source/c01/c01_31.rst +++ b/source/c01/c01_31.rst @@ -19,10 +19,8 @@ ARGB RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst index 7006f8d..4317fad 100644 --- a/source/c01/c01_32.rst +++ b/source/c01/c01_32.rst @@ -33,10 +33,8 @@ requests.get(“https://www.baidu.com”) python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_33.rst b/source/c01/c01_33.rst index 39fd212..b0eded2 100644 --- a/source/c01/c01_33.rst +++ b/source/c01/c01_33.rst @@ -46,10 +46,8 @@ sudo yum install gdb python-debuginfo -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index c9c425b..c020d68 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -90,11 +90,9 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 innodb: foreign key constraint system tables created -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200227201644.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index f7d3e8a..c2ae53d 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -395,13 +395,11 @@ Windows,这里就有一件好事,一件坏事了,。 - https://www.liujiangblog.com/blog/15/ - http://docs.paramiko.org/en/stable/ -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200228085749.png .. |image2| image:: http://image.python-online.cn/20200228093627.png .. |image3| image:: http://image.python-online.cn/20200228111654.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst index ed0ff15..bbed8dd 100644 --- a/source/c01/c01_36.rst +++ b/source/c01/c01_36.rst @@ -268,10 +268,7 @@ PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,\ ``pretty_errors`` 会是一个不错的解决方案,明哥把它推荐给你。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image12| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/image-20200307210853246.png @@ -285,4 +282,5 @@ PEP8 .. |image9| image:: http://image.python-online.cn/image-20200307214600749.png .. |image10| image:: http://image.python-online.cn/image-20200308121949011.png .. |image11| image:: http://image.python-online.cn/image-20200308125431779.png +.. |image12| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_37.rst b/source/c01/c01_37.rst index cd4cd95..15c3b09 100644 --- a/source/c01/c01_37.rst +++ b/source/c01/c01_37.rst @@ -175,10 +175,8 @@ Python 功力。 看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_38.rst b/source/c01/c01_38.rst index 2896d71..94438e8 100644 --- a/source/c01/c01_38.rst +++ b/source/c01/c01_38.rst @@ -64,10 +64,7 @@ 个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 python 解释器都是 ``/usr/bin/python`` 。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200331184021.png @@ -75,4 +72,5 @@ python 解释器都是 ``/usr/bin/python`` 。 .. |image3| image:: http://image.python-online.cn/20200331184755.png .. |image4| image:: http://image.python-online.cn/20200331185741.png .. |image5| image:: http://image.python-online.cn/20200331190224.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_39.rst b/source/c01/c01_39.rst index b2e997c..89a0d3d 100644 --- a/source/c01/c01_39.rst +++ b/source/c01/c01_39.rst @@ -224,10 +224,8 @@ Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_40.rst b/source/c01/c01_40.rst index 6903170..7bd620a 100644 --- a/source/c01/c01_40.rst +++ b/source/c01/c01_40.rst @@ -147,10 +147,8 @@ python 代码快。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_41.rst b/source/c01/c01_41.rst index c4e3160..82b79a2 100644 --- a/source/c01/c01_41.rst +++ b/source/c01/c01_41.rst @@ -226,10 +226,8 @@ yield from 意义及使用方法。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_42.rst b/source/c01/c01_42.rst index af93dd7..9df2a34 100644 --- a/source/c01/c01_42.rst +++ b/source/c01/c01_42.rst @@ -175,11 +175,9 @@ Golang,那这里要注意,Golang 中的 ``:=`` -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/image-20200418122739417.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_43.rst b/source/c01/c01_43.rst index e62f4cd..cb4ba3b 100644 --- a/source/c01/c01_43.rst +++ b/source/c01/c01_43.rst @@ -381,13 +381,11 @@ json.dumps 的关键参数有两个: 以上。希望此文能对你有帮助。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200507171451.png .. |image2| image:: http://image.iswbm.com/20200507174459.png .. |image3| image:: http://image.iswbm.com/20200507174802.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_44.rst b/source/c01/c01_44.rst index 69bf823..70b3276 100644 --- a/source/c01/c01_44.rst +++ b/source/c01/c01_44.rst @@ -286,12 +286,10 @@ sys.setdefaultencoding 这个方法。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200423185819.png .. |image2| image:: http://image.iswbm.com/20200423190331.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_45.rst b/source/c01/c01_45.rst index 95e46a6..052224e 100644 --- a/source/c01/c01_45.rst +++ b/source/c01/c01_45.rst @@ -302,10 +302,8 @@ python 文件,如果后面导入成功会打印 ``ok``\ 。 这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习\ ``__import__``\ 以及 importlib 是非常有必要的。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index a49981c..eeb14f5 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -250,10 +250,7 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png @@ -261,4 +258,5 @@ .. |image3| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg .. |image4| image:: http://image.python-online.cn/20190112205155.png .. |image5| image:: http://image.python-online.cn/20190112204930.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index 80db84d..14ee3ea 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -147,10 +147,8 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index d6d5148..03cde42 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -354,10 +354,8 @@ CPython,所以也就默许了Python具有GIL锁这个事。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 340de57..85e6579 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -309,10 +309,8 @@ Queue.task_done(),说明队列这个任务已经结束了。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 7cbc047..4df48c5 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -261,10 +261,8 @@ Out),就是先进入队列的消息,将优先被消费。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index cb85e5b..35abcb7 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -156,10 +156,8 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index 3088304..ec51a27 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -392,12 +392,10 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190527123516.png .. |image2| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index 325b8e7..21019c2 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -361,10 +361,8 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index b332bf3..42ec222 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -246,13 +246,11 @@ emmm,和上面的结果是一样的。nice -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |普通函数中 不能使用 await| image:: https://i.loli.net/2018/05/26/5b09794f45340.png .. |async 中 不能使用yield| image:: https://i.loli.net/2018/05/26/5b0978b646230.png .. |验证通过| image:: https://i.loli.net/2018/05/26/5b09814dc4714.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index 019401e..ea98273 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -509,10 +509,8 @@ asyncio.gather -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index 84b908c..f747cd4 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -218,14 +218,12 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/06/03/5b13ba8525bcf.png .. |image2| image:: https://i.loli.net/2018/06/03/5b13ba9f66baa.png .. |image3| image:: https://i.loli.net/2018/06/03/5b13bab682a32.png .. |image4| image:: https://i.loli.net/2018/06/03/5b13bad79f5ce.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_12.rst b/source/c02/c02_12.rst index f2b017d..1efdb19 100644 --- a/source/c02/c02_12.rst +++ b/source/c02/c02_12.rst @@ -109,10 +109,8 @@ yield -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_14.rst b/source/c02/c02_14.rst index 204dd46..3be3f8f 100644 --- a/source/c02/c02_14.rst +++ b/source/c02/c02_14.rst @@ -193,12 +193,10 @@ https://zhuanlan.zhihu.com/p/34150765 https://juejin.im/post/5b129a1be51d45068a6c91d4#comment -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200506080445.png .. |image2| image:: http://image.iswbm.com/20200506081541.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 66f588c..305fde9 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -775,12 +775,10 @@ property 我的文章更新频率是远低于其他 Python 技术号,但我仍然坚持自己,坚持原创,每周虽然只有一篇,但我能保证我的每一篇文章都是诚意之作。希望那些对你有帮助的文章能够多多帮忙转发分享。这也是我更新的一大动力。非常感谢。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190811100737.png .. |image2| image:: http://image.python-online.cn/20190512113917.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index 9a02005..a134058 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -376,10 +376,8 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index bd40607..8c032a5 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -294,12 +294,10 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/04/30/5ae6c303c870c.png .. |image2| image:: https://i.loli.net/2018/04/30/5ae6c31b2d1c8.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_04.rst b/source/c03/c03_04.rst index d2c890a..c36d5ed 100755 --- a/source/c03/c03_04.rst +++ b/source/c03/c03_04.rst @@ -512,10 +512,7 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2017/08/20/599982f513b7e.png @@ -524,4 +521,5 @@ .. |image4| image:: https://i.loli.net/2017/08/20/59998bdf36c79.png .. |image5| image:: https://i.loli.net/2017/08/25/59a0236def497.png .. |image6| image:: https://i.loli.net/2017/08/25/59a0244d239f0.png +.. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index 9ad5043..a9de523 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -500,13 +500,11 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX .. |image2| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup .. |image3| image:: http://image.python-online.cn/FimULzWaeZWS2KJx_EQLAK_yRZ4A +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_06.rst b/source/c03/c03_06.rst index f4b2992..fa804d4 100644 --- a/source/c03/c03_06.rst +++ b/source/c03/c03_06.rst @@ -884,10 +884,7 @@ response 进行初步封装。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image24| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190607131728.png @@ -913,4 +910,5 @@ response 进行初步封装。 .. |image21| image:: http://image.python-online.cn/20190602220511.png .. |image22| image:: http://image.python-online.cn/20190602220700.png .. |image23| image:: http://image.python-online.cn/20190605203016.png +.. |image24| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_01.rst b/source/c04/c04_01.rst index 474251b..c10202e 100755 --- a/source/c04/c04_01.rst +++ b/source/c04/c04_01.rst @@ -267,10 +267,7 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200209161935.png @@ -278,4 +275,5 @@ https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html .. |image3| image:: https://i.loli.net/2018/06/11/5b1e7f140be6a.png .. |image4| image:: https://i.loli.net/2018/06/11/5b1e805c996c8.png .. |image5| image:: https://i.loli.net/2018/06/11/5b1e812db603f.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index ddfb728..1e9fadc 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -168,10 +168,7 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511162815.png @@ -179,4 +176,5 @@ .. |image3| image:: http://image.python-online.cn/20190511162607.png .. |image4| image:: http://image.python-online.cn/20190511162716.png .. |image5| image:: http://image.python-online.cn/20190511162730.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 48626df..6744df9 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -480,10 +480,7 @@ Python 3.x ,所以这里的代码也要对应修改。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image19| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511160523.png @@ -504,4 +501,5 @@ Python 3.x ,所以这里的代码也要对应修改。 .. |image16| image:: http://image.python-online.cn/20191016205653.png .. |image17| image:: http://image.python-online.cn/20191015225652.png .. |image18| image:: http://image.python-online.cn/20191016211012.png +.. |image19| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_04.rst b/source/c04/c04_04.rst index 8ef375e..bbe77dd 100755 --- a/source/c04/c04_04.rst +++ b/source/c04/c04_04.rst @@ -239,10 +239,7 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image10| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511163102.png @@ -254,4 +251,5 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 .. |image7| image:: http://image.python-online.cn/20190511163253.png .. |image8| image:: http://image.python-online.cn/20190511163304.png .. |image9| image:: http://image.python-online.cn/20190511163311.png +.. |image10| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_05.rst b/source/c04/c04_05.rst index de95469..e4093c4 100755 --- a/source/c04/c04_05.rst +++ b/source/c04/c04_05.rst @@ -159,10 +159,7 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image18| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511163441.png @@ -182,4 +179,5 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 .. |image15| image:: http://image.python-online.cn/20190511163750.png .. |image16| image:: http://image.python-online.cn/20190511163757.png .. |image17| image:: http://image.python-online.cn/20190511163805.png +.. |image18| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 4c4a1a2..f1873b9 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -595,10 +595,7 @@ Desktop真心觉得不好用)。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image8| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191217150942.png @@ -608,4 +605,5 @@ Desktop真心觉得不好用)。 .. |image5| image:: http://image.python-online.cn/20190511163855.png .. |image6| image:: https://i.loli.net/2018/04/15/5ad2c2a9813b9.png .. |image7| image:: http://image.python-online.cn/20190430235625.png +.. |image8| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_07.rst b/source/c04/c04_07.rst index a051ca6..a38ede7 100755 --- a/source/c04/c04_07.rst +++ b/source/c04/c04_07.rst @@ -891,10 +891,7 @@ bash窗口,不然会提示找不到npm命令) -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image12| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/image-20200321163152876.png @@ -908,4 +905,5 @@ bash窗口,不然会提示找不到npm命令) .. |image9| image:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200321210014963.png .. |image10| image:: http://image.python-online.cn/image-20200321193444320.png .. |image11| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png +.. |image12| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_08.rst b/source/c04/c04_08.rst index c88be2c..0e3c231 100755 --- a/source/c04/c04_08.rst +++ b/source/c04/c04_08.rst @@ -430,10 +430,8 @@ URL相关 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_09.rst b/source/c04/c04_09.rst index 85c7b92..ff86349 100755 --- a/source/c04/c04_09.rst +++ b/source/c04/c04_09.rst @@ -712,10 +712,7 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image37| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2017/08/25/599feabef31a6.png @@ -754,4 +751,5 @@ SO,如果是提交的是英文的话,不会有冲突。因为都是一个字 .. |image34| image:: https://i.loli.net/2017/08/27/59a261734e896.png .. |image35| image:: https://i.loli.net/2017/08/27/59a261734ff8b.png .. |image36| image:: https://i.loli.net/2017/08/25/599fc9aa85094.png +.. |image37| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_10.rst b/source/c04/c04_10.rst index 563ff61..439f67e 100755 --- a/source/c04/c04_10.rst +++ b/source/c04/c04_10.rst @@ -538,10 +538,7 @@ limit子句:限制返回数据的量。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image25| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://ooo.0o0.ooo/2017/08/26/59a1429722061.png @@ -568,4 +565,5 @@ limit子句:限制返回数据的量。 .. |image22| image:: https://i.loli.net/2017/08/27/59a2756c66952.png .. |image23| image:: https://i.loli.net/2017/08/27/59a27651cd78e.png .. |image24| image:: https://i.loli.net/2017/08/27/59a2784f6562d.png +.. |image25| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_11.rst b/source/c04/c04_11.rst index 7a46453..61adafe 100644 --- a/source/c04/c04_11.rst +++ b/source/c04/c04_11.rst @@ -175,10 +175,7 @@ Host -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image18| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190113104817.png @@ -198,4 +195,5 @@ Host .. |image15| image:: http://image.python-online.cn/20190113113211.png .. |image16| image:: http://image.python-online.cn/20190113112456.png .. |image17| image:: http://image.python-online.cn/20190113112649.png +.. |image18| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_12.rst b/source/c04/c04_12.rst index 2689dcd..d9e56e9 100644 --- a/source/c04/c04_12.rst +++ b/source/c04/c04_12.rst @@ -176,10 +176,7 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190118000111.png @@ -187,4 +184,5 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 .. |image3| image:: http://image.python-online.cn/20190118000557.png .. |image4| image:: http://image.python-online.cn/20190118083809.png .. |image5| image:: http://image.python-online.cn/20190118005507.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_13.rst b/source/c04/c04_13.rst index fe22832..2059a5a 100644 --- a/source/c04/c04_13.rst +++ b/source/c04/c04_13.rst @@ -433,10 +433,8 @@ arguments)。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 58e70cf..7fa8d79 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -170,10 +170,7 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image9| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn @@ -184,4 +181,5 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 .. |image6| image:: http://image.python-online.cn/20190612211925.png .. |image7| image:: http://image.python-online.cn/20190614000336.png .. |image8| image:: http://image.python-online.cn/20190612215924.png +.. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index 71d2ad9..940729c 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -1086,10 +1086,7 @@ Debug ,而远程调试需要不少前置步骤。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image104| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190323164120.png @@ -1195,4 +1192,5 @@ Debug ,而远程调试需要不少前置步骤。 .. |image101| image:: http://image.iswbm.com/20200420085524.png .. |image102| image:: http://image.iswbm.com/20200420090117.png .. |image103| image:: http://image.iswbm.com/20200420090428.png +.. |image104| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_16.rst b/source/c04/c04_16.rst index c65135b..b26a9e1 100644 --- a/source/c04/c04_16.rst +++ b/source/c04/c04_16.rst @@ -74,10 +74,8 @@ Python:如何在被调用方法中获取调用者的方法名? -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_17.rst b/source/c04/c04_17.rst index cacda33..9a457f4 100644 --- a/source/c04/c04_17.rst +++ b/source/c04/c04_17.rst @@ -474,14 +474,12 @@ Order -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190414144511.png .. |image2| image:: http://image.python-online.cn/20190512113846.png .. |image3| image:: http://image.python-online.cn/20190512113917.png .. |image4| image:: http://image.python-online.cn/20190512114028.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 0574d55..219419b 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -329,12 +329,10 @@ finder的显示 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190810161513.png .. |image2| image:: http://image.python-online.cn/20190810162315.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_19.rst b/source/c04/c04_19.rst index b27ab4e..19f0945 100644 --- a/source/c04/c04_19.rst +++ b/source/c04/c04_19.rst @@ -721,10 +721,8 @@ n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index 5ba1523..36aeff6 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -370,11 +370,9 @@ pip 的文件夹,若没有则创建之。 |image1| -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191105200041.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_24.rst b/source/c04/c04_24.rst index deecd49..9b8949a 100644 --- a/source/c04/c04_24.rst +++ b/source/c04/c04_24.rst @@ -186,14 +186,12 @@ Python 如何确定应该从哪个路径进行导入呢? |image4| -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200427180207.png .. |image2| image:: http://image.iswbm.com/20200427180042.png .. |image3| image:: http://image.iswbm.com/20200427182334.png .. |image4| image:: http://image.iswbm.com/20200427185854.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c05/c05_01.rst b/source/c05/c05_01.rst index 876849c..2f1233f 100755 --- a/source/c05/c05_01.rst +++ b/source/c05/c05_01.rst @@ -421,10 +421,7 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image13| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |快速排序| image:: http://image.python-online.cn/Fpj4DFN_YCtfmJwb_85QnsuIVLqk @@ -439,4 +436,5 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD .. |合并两个有序数组| image:: http://image.python-online.cn/FnCZ-3Pj39T_ELROSsGRDN31yWtY .. |桶排序| image:: http://image.python-online.cn/FljGa3F3wM_YGACETeuRHCiERXKb .. |基数排序| image:: http://image.python-online.cn/FhwWVp4LVABIMPHqNo_cpjJA9kHV +.. |image13| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c05/c05_02.rst b/source/c05/c05_02.rst index c40c3df..ae496fc 100755 --- a/source/c05/c05_02.rst +++ b/source/c05/c05_02.rst @@ -122,10 +122,8 @@ -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c05/c05_03.rst b/source/c05/c05_03.rst index f1435fb..2de0e8d 100755 --- a/source/c05/c05_03.rst +++ b/source/c05/c05_03.rst @@ -161,11 +161,9 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190112181126.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_01.rst b/source/c06/c06_01.rst index 63b9c24..9fd8245 100755 --- a/source/c06/c06_01.rst +++ b/source/c06/c06_01.rst @@ -133,12 +133,10 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png .. |image2| image:: http://image.python-online.cn/20190511164650.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_02.rst b/source/c06/c06_02.rst index b78c20a..d7e5895 100755 --- a/source/c06/c06_02.rst +++ b/source/c06/c06_02.rst @@ -311,10 +311,7 @@ show image |image10| -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image11| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511164738.png @@ -327,4 +324,5 @@ show image |image10| .. |image8| image:: http://image.python-online.cn/20190511164852.png .. |image9| image:: http://image.python-online.cn/20190511164900.png .. |image10| image:: http://image.python-online.cn/20190511164915.png +.. |image11| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_03.rst b/source/c06/c06_03.rst index 8dc34ff..c5bd3db 100755 --- a/source/c06/c06_03.rst +++ b/source/c06/c06_03.rst @@ -207,10 +207,7 @@ show image |image5| -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511164936.png @@ -218,4 +215,5 @@ show image |image5| .. |image3| image:: http://image.python-online.cn/20190511165003.png .. |image4| image:: http://image.python-online.cn/20190511165013.png .. |image5| image:: http://image.python-online.cn/20190511165020.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_04.rst b/source/c06/c06_04.rst index 0f7ae5f..7e2b655 100755 --- a/source/c06/c06_04.rst +++ b/source/c06/c06_04.rst @@ -201,10 +201,7 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image8| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511165103.png @@ -214,4 +211,5 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib .. |image5| image:: http://image.python-online.cn/20190511165211.png .. |image6| image:: http://image.python-online.cn/20190511165221.png .. |image7| image:: http://image.python-online.cn/20190511165229.png +.. |image8| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_05.rst b/source/c06/c06_05.rst index 817c63f..719cb35 100755 --- a/source/c06/c06_05.rst +++ b/source/c06/c06_05.rst @@ -143,11 +143,9 @@ matplotlib 给我们提供了一个函数,\ ``animation.FuncAnimation`` -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/12/25/5c2226078799b.gif +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_06.rst b/source/c06/c06_06.rst index 527f7d2..6a76a83 100755 --- a/source/c06/c06_06.rst +++ b/source/c06/c06_06.rst @@ -139,11 +139,9 @@ Jupyter NoteBook 里观察整个变化的过程。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511165315.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index 13603ee..dd8dcd4 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -1937,13 +1937,11 @@ url** 即可。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/17-9-20/47469030.jpg .. |image2| image:: http://image.python-online.cn/20190705182629.png .. |image3| image:: http://image.python-online.cn/17-10-15/97911325.jpg +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_02.rst b/source/c07/c07_02.rst index ef9e9a6..07c75fb 100755 --- a/source/c07/c07_02.rst +++ b/source/c07/c07_02.rst @@ -695,10 +695,7 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image23| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190404193811.png @@ -723,4 +720,5 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 .. |image20| image:: http://image.python-online.cn/20190605173956.png .. |image21| image:: http://image.python-online.cn/20190409103417.png .. |image22| image:: http://image.python-online.cn/20190409104026.png +.. |image23| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_03.rst b/source/c07/c07_03.rst index c4653ea..bda175a 100755 --- a/source/c07/c07_03.rst +++ b/source/c07/c07_03.rst @@ -289,12 +289,10 @@ namespace 有下面六种 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/17-12-23/44035514.jpg .. |image2| image:: http://image.python-online.cn/17-12-23/20133481.jpg +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_04.rst b/source/c07/c07_04.rst index 2f95d47..4d5c5e7 100755 --- a/source/c07/c07_04.rst +++ b/source/c07/c07_04.rst @@ -360,10 +360,7 @@ CMD:两个功能 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image10| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/17-12-23/49304868.jpg @@ -375,4 +372,5 @@ CMD:两个功能 .. |image7| image:: http://image.python-online.cn/17-12-24/42825662.jpg .. |image8| image:: http://image.python-online.cn/17-12-24/80077038.jpg .. |image9| image:: http://image.python-online.cn/17-12-24/98318652.jpg +.. |image10| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index eb45035..4ca28c1 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -319,14 +319,12 @@ Socket发些“奇怪”的数据。 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/18-1-28/92519416.jpg .. |image2| image:: http://image.python-online.cn/18-1-28/37395940.jpg .. |image3| image:: https://i.loli.net/2018/01/28/5a6de8702428c.png .. |image4| image:: https://i.loli.net/2018/01/28/5a6de73776390.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_06.rst b/source/c07/c07_06.rst index d5eb751..5358702 100755 --- a/source/c07/c07_06.rst +++ b/source/c07/c07_06.rst @@ -328,13 +328,11 @@ centos的配置文件路径如下,ubuntu的有所不同 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2017/12/30/5a473ba8f374f.png .. |image2| image:: https://i.loli.net/2018/01/03/5a4ce6eeaff7d.png .. |image3| image:: https://i.loli.net/2018/01/03/5a4ce964d3e73.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_07.rst b/source/c07/c07_07.rst index a12e525..88a4d23 100755 --- a/source/c07/c07_07.rst +++ b/source/c07/c07_07.rst @@ -418,10 +418,8 @@ salt命令格式 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_08.rst b/source/c07/c07_08.rst index 62dc6f3..de57c84 100644 --- a/source/c07/c07_08.rst +++ b/source/c07/c07_08.rst @@ -175,10 +175,8 @@ chk_zabbix.sh -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index 1a33466..1620ef6 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -186,14 +186,12 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190716111523.png .. |image2| image:: http://image.python-online.cn/20190716112113.png .. |image3| image:: http://image.python-online.cn/20190716112824.png .. |image4| image:: http://image.python-online.cn/20190716112838.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst index 2e7d4b9..68c4150 100644 --- a/source/c07/c07_11.rst +++ b/source/c07/c07_11.rst @@ -77,11 +77,9 @@ K8s 角色详解 里可以运行一个容器(最常用),也可以运行多个容器,若运行多个容器,那这几个容器的工作必定有着紧密的联系,而且需要直接 **共享资源**\ 。 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190907162015.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index 8674ac0..d7dbe58 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -208,13 +208,11 @@ rpm # 重建rpm数据文件 rpm –rebuilddb -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191219152328.png .. |image2| image:: http://image.python-online.cn/20191225173340.png .. |image3| image:: http://image.python-online.cn/20191225175350.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_13.rst b/source/c07/c07_13.rst index 46b9137..a7b39d5 100644 --- a/source/c07/c07_13.rst +++ b/source/c07/c07_13.rst @@ -117,10 +117,8 @@ ansible 的 api 开发出来的吗? - v2_runner_on_skipped:部署任务跳过时,会调用 - v2_playbook_on_stats:所有的部署任务完成时,调用 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_14.rst b/source/c07/c07_14.rst index 7cdeb95..2a0f1a7 100644 --- a/source/c07/c07_14.rst +++ b/source/c07/c07_14.rst @@ -26,10 +26,8 @@ filename可读,则为真 ``-w filename``: 如果 filename可写,则为真 -eq :等于 -ne :不等于 -gt :大于 -ge :大于等于 -lt :小于 -le :小于等于 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_15.rst b/source/c07/c07_15.rst index 4394958..8037826 100644 --- a/source/c07/c07_15.rst +++ b/source/c07/c07_15.rst @@ -219,11 +219,9 @@ wsrep_flow_control_sent 和 wsrep_local_recv_queue_avg - http://www.360doc.com/content/13/0817/15/834950_307820923.shtml - http://blog.itpub.net/30126024/viewspace-2221483/ -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191213162259.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index 99b4472..b11e5ac 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -83,14 +83,12 @@ result=`random_range 1 60` -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20191220202103.png .. |image2| image:: http://image.python-online.cn/20191220203403.png .. |image3| image:: http://image.python-online.cn/20191220202408.png .. |image4| image:: http://image.python-online.cn/20191220203205.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_17.rst b/source/c07/c07_17.rst index 381f095..9261a6d 100644 --- a/source/c07/c07_17.rst +++ b/source/c07/c07_17.rst @@ -13,10 +13,8 @@ https://blog.oddbit.com/post/2019-04-25-writing-ansible-filter-plugins/ https://www.jianshu.com/p/ae74f5f39828 -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_18.rst b/source/c07/c07_18.rst index 7a6ef11..09fc58c 100644 --- a/source/c07/c07_18.rst +++ b/source/c07/c07_18.rst @@ -62,10 +62,8 @@ [root@linux ~]# echo ' A BC ' | awk '{sub(/^ */, "");sub(/ *$/, "")}1' A BC -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_19.rst b/source/c07/c07_19.rst index 78c8600..e18353d 100644 --- a/source/c07/c07_19.rst +++ b/source/c07/c07_19.rst @@ -48,10 +48,8 @@ $ ansible 172.20.20.1 -m ping -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_21.md b/source/c07/c07_21.md index 2ded6d3..e6573c7 100644 --- a/source/c07/c07_21.md +++ b/source/c07/c07_21.md @@ -119,4 +119,4 @@ $ du -sh big_file -![](http://image.iswbm.com/20200602133847.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_21.rst b/source/c07/c07_21.rst index b8189e8..a2bbd9f 100644 --- a/source/c07/c07_21.rst +++ b/source/c07/c07_21.rst @@ -124,5 +124,5 @@ fallocate,速度够快,也是也会真实地占用磁盘空间,符合真 |image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200602133847.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index 0014c6f..071db14 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -446,10 +446,8 @@ aggregate管理 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index da2a961..14b0539 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -305,10 +305,7 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image9| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/01/19/5a61bfa0ca66f.png @@ -319,4 +316,5 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr .. |image6| image:: https://i.loli.net/2018/01/19/5a61c246451e7.png .. |image7| image:: http://image.python-online.cn/20190529202132.png .. |image8| image:: http://image.python-online.cn/20190529202440.png +.. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index 1f0876a..4899e3f 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -530,10 +530,7 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png @@ -541,4 +538,5 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` .. |image3| image:: http://image.python-online.cn/20190827200522.png .. |image4| image:: http://image.python-online.cn/20191211174659.png .. |image5| image:: http://image.python-online.cn/20191211174956.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index 8c58904..e57be47 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -485,10 +485,7 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190714161353.png @@ -497,4 +494,5 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 .. |image4| image:: http://image.python-online.cn/20190716005951.png .. |image5| image:: http://image.python-online.cn/20190714141644.png .. |image6| image:: https://i.loli.net/2019/02/25/5c73e6160764a.png +.. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index 0027c76..570f2b2 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -732,10 +732,7 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image61| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190526144846.png @@ -798,4 +795,5 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 .. |image58| image:: http://image.python-online.cn/20190830092203.png .. |image59| image:: http://image.python-online.cn/20190830093613.png .. |image60| image:: http://image.python-online.cn/20190912135302.png +.. |image61| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index 7cf352a..7b04f5b 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -707,10 +707,7 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image42| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190430204707.png @@ -754,4 +751,5 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 .. |image39| image:: http://image.python-online.cn/20190911203953.png .. |image40| image:: http://image.python-online.cn/20190911204805.png .. |image41| image:: http://image.python-online.cn/20190911205518.png +.. |image42| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index b39f7d0..15c40f9 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -150,10 +150,7 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image12| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190419144135.png @@ -167,4 +164,5 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 .. |image9| image:: http://image.python-online.cn/20190528105021.png .. |image10| image:: http://image.python-online.cn/20190528114526.png .. |image11| image:: http://image.python-online.cn/20190606185531.png +.. |image12| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index a1b598f..f23daba 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -251,10 +251,7 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image8| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190514202013.png @@ -264,4 +261,5 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: .. |image5| image:: http://image.python-online.cn/20190430204707.png .. |image6| image:: http://image.python-online.cn/20190430204933.png .. |image7| image:: http://image.python-online.cn/20190430205449.png +.. |image8| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index b0e445a..f0c6701 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -784,10 +784,7 @@ rpc server 和rpc client 的四个重要方法 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image22| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190623185008.png @@ -811,4 +808,5 @@ rpc server 和rpc client 的四个重要方法 .. |image19| image:: http://image.python-online.cn/20190526175100.png .. |image20| image:: http://image.python-online.cn/20190526180708.png .. |image21| image:: http://image.python-online.cn/20190526181433.png +.. |image22| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index db28b76..432b22e 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -84,11 +84,9 @@ ConfigDrive 是一个 iso9660 格式的文件,只读。 # 硬重启,重新生成 xml,如果有必要的话 $ nova reboot --hard -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190530175817.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index aba42bc..be4958c 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -88,10 +88,7 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image9| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190424212211.png @@ -102,4 +99,5 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 .. |image6| image:: http://image.python-online.cn/20190424215735.png .. |image7| image:: http://image.python-online.cn/20190424220008.png .. |image8| image:: http://image.python-online.cn/20191011103832.png +.. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index 21e3981..c61670f 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -314,14 +314,12 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190706114314.png .. |image2| image:: http://image.python-online.cn/20190706093904.png .. |image3| image:: http://image.python-online.cn/20190706160632.png .. |image4| image:: http://image.python-online.cn/20190804162402.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index 3652947..aec531c 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -325,10 +325,7 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local -------------- -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image15| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190716175250.png @@ -345,4 +342,5 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local .. |image12| image:: http://image.python-online.cn/20190829111917.png .. |image13| image:: http://image.python-online.cn/20190829161243.png .. |image14| image:: http://image.python-online.cn/20190926171038.png +.. |image15| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 1cd703d..79a9a17 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -152,13 +152,11 @@ img_new.convert('RGB').save("F://save.jpeg") -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200214104413.png .. |image2| image:: http://image.python-online.cn/save.jpeg .. |image3| image:: http://image.python-online.cn/20200214104646.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst index 92b1c49..bdabd65 100644 --- a/source/c10/c10_01.rst +++ b/source/c10/c10_01.rst @@ -332,10 +332,7 @@ Linux # 服务器或者路由器使用DNSMASQ $ sudo dnsmasq restart -.. figure:: http://image.iswbm.com/20200607174235.png - :alt: - - +|image15| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/464291-20170703113844956-354755333.jpg @@ -352,4 +349,5 @@ Linux .. |image12| image:: http://image.iswbm.com/image-20200531171905345.png .. |image13| image:: http://image.iswbm.com/image-20200531145109182.png .. |image14| image:: http://image.iswbm.com/image-20200531145449577.png +.. |image15| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_02.md b/source/c10/c10_02.md index 7371a33..316caf8 100644 --- a/source/c10/c10_02.md +++ b/source/c10/c10_02.md @@ -138,4 +138,12 @@ IP地址和我们的住址有点相似,我们的住址可以从省到市再到 ## 参考文章 -- https://juejin.im/post/59eb06b1f265da430f313c7f \ No newline at end of file +- https://juejin.im/post/59eb06b1f265da430f313c7f + + + +--- + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_02.rst b/source/c10/c10_02.rst index 703aa3b..db03718 100644 --- a/source/c10/c10_02.rst +++ b/source/c10/c10_02.rst @@ -150,6 +150,11 @@ UDP还是TCP协议,这个是用于对端在接收到这个数据包后知道 - https://juejin.im/post/59eb06b1f265da430f313c7f +-------------- + +|image2| + .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200526233356.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md index 5207498..f715317 100644 --- a/source/c10/c10_03.md +++ b/source/c10/c10_03.md @@ -569,3 +569,6 @@ ConnectionRefusedError: [Errno 61] Connection refused +--- + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst index a2e6f63..b6fda4a 100644 --- a/source/c10/c10_03.rst +++ b/source/c10/c10_03.rst @@ -674,6 +674,10 @@ TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 `近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题 `__ +-------------- + +|image16| + .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/image-20200601221524846.png .. |image2| image:: http://image.iswbm.com/image-20200601222110435.png @@ -690,4 +694,5 @@ TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 .. |image13| image:: http://image.iswbm.com/20200605204727.png .. |image14| image:: http://image.iswbm.com/image-20200604223625787.png .. |image15| image:: http://image.iswbm.com/image-20200604224127512.png +.. |image16| image:: http://image.iswbm.com/20200607174235.png From 9b1f4044acc7588f824a13549a994c3dbf571527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Wed, 17 Jun 2020 18:58:47 +0800 Subject: [PATCH 083/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_27.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index 3b09a1d..5974033 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -442,6 +442,16 @@ setup( +**指定release** + +setup.py 里只能指定 version,而不能指定 release,如果你需要变更版本号,可以使用 `--release` 参数进行指定 + +```shell +python setup.py bdist_rpm --release=20200617 +``` + + + setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数: ![](http://image.python-online.cn/20191218203255.png) From 6babe0798140adadebddb019775692a4ace3c4f0 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Fri, 19 Jun 2020 08:39:47 +0800 Subject: [PATCH 084/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_27.rst | 9 +++++++++ source/c03/c03_07.md | 14 +++++++++++++- source/c03/c03_07.rst | 25 +++++++++++++++++++++---- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 9ffabbd..7676aa6 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -476,6 +476,15 @@ Python 详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options +**指定release** + +setup.py 里只能指定 version,而不能指定 +release,如果你需要变更版本号,可以使用 ``--release`` 参数进行指定 + +.. code:: shell + + python setup.py bdist_rpm --release=20200617 + setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数: diff --git a/source/c03/c03_07.md b/source/c03/c03_07.md index a6ffa20..392be63 100644 --- a/source/c03/c03_07.md +++ b/source/c03/c03_07.md @@ -2,6 +2,18 @@ ![](http://image.iswbm.com/20200602135014.png) +今天给大家分享 10 个我平时整理非常实用的 Python 开发小技巧,内容目录如下: + +![](http://image.iswbm.com/20200617084208.png) + +值得一提的是,这 10 个技巧全部收录在我自己写的 《Python黑魔法指南》里。 + +![](http://image.iswbm.com/20200617085313.png) + +你可以在按照如下方法,后台发送『黑魔法』就可以获取精美排版的 PDF 电子书。 + +![](http://image.iswbm.com/20200617085001.png) + ## 1. 如何在运行状态查看源代码? 查看函数的源代码,我们通常会使用 IDE 来完成。 @@ -130,7 +142,7 @@ RuntimeError: Something bad happened ``` -## 03. 最快查看包搜索路径的方式 +## 3. 最快查看包搜索路径的方式 当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 查看。 diff --git a/source/c03/c03_07.rst b/source/c03/c03_07.rst index eed3b00..ffad685 100644 --- a/source/c03/c03_07.rst +++ b/source/c03/c03_07.rst @@ -3,6 +3,20 @@ |image0| +今天给大家分享 10 个我平时整理非常实用的 Python +开发小技巧,内容目录如下: + +|image1| + +值得一提的是,这 10 个技巧全部收录在我自己写的 《Python黑魔法指南》里。 + +|image2| + +你可以在按照如下方法,后台发送『黑魔法』就可以获取精美排版的 PDF +电子书。 + +|image3| + 1. 如何在运行状态查看源代码? ----------------------------- @@ -127,8 +141,8 @@ RuntimeError: Something bad happened (PythonCodingTime) -03. 最快查看包搜索路径的方式 ----------------------------- +3. 最快查看包搜索路径的方式 +--------------------------- 当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path @@ -410,7 +424,7 @@ print 的内容输出到日志文件中 示例如下 -|image1| +|image4| 如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。 @@ -531,5 +545,8 @@ print 的内容输出到日志文件中 yield chunk .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200510112133.png +.. |image1| image:: http://image.iswbm.com/20200617084208.png +.. |image2| image:: http://image.iswbm.com/20200617085313.png +.. |image3| image:: http://image.iswbm.com/20200617085001.png +.. |image4| image:: http://image.iswbm.com/20200510112133.png From 4e3a3cebbd3258837a94585a698928855cbf99bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Mon, 22 Jun 2020 18:07:32 +0800 Subject: [PATCH 085/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_06.md | 57 ++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/source/c04/c04_06.md b/source/c04/c04_06.md index c845cf0..90e6dc5 100644 --- a/source/c04/c04_06.md +++ b/source/c04/c04_06.md @@ -50,37 +50,56 @@ $ git rm readme.txt # 删除工作区和暂存区文件,commit后版本库也 ### 1.4 远程仓库 -添加远程仓库 +origin 并不是指得是远程的仓库,而是指得是远程仓库在本地的一个指针。 + +为什么是这个名字呢?仅仅是因为 git 用它做为默认名被广泛使用,一个习惯而已,你也可以叫其他名字。 + +对一个在本地创建的仓库,如果添加远程仓库? ```shell $ git remote add [shortname] [url] # shortname是你命名的远程仓库名字,url格式如下:git@github.com:github_account/repository_name.git -$ git remote add learn_git git@github.com:wongbingming/my_repository.git #注意记得先在GitHub上先新建仓库,my_repository,不然后面推送会出错 +#注意记得先在GitHub上先新建仓库,my_repository,不然后面 push 会出错 +$ git remote add learn_git git@github.com:wongbingming/my_repository.git ``` + + 推送/克隆 ```shell $ git push -u learn_git master #learn_git是之前命名的远程仓库名,master是你要推送到的远程仓库的分支名字 +$ git push -u learn-git master:master $ git clone [github url] # 举例 -$ git clone git@github.com:BingmingWong/test.git +$ git clone git@github.com:iswbm/test.git +``` -# 更改远程仓库地址 -git remote set-url origin git@:you_repo.git +更改远程仓库地址 + +```shell +$ git remote set-url origin git@:you_repo.git ``` +upstream 和 downstream + +Git中的upstream和downstream的概念是相对的。 + +如果A库中的分支x被push到B库中的分支y,则y就是x的upstream,而x就是y的downstream。 + + + ### 1.5 GitHub合并至本地 -``` +```shell $ git merge origin/master ``` ### 1.6 创建SSHkeys -``` +```shell $ ssh-keygen -t rsa -C "youremail@example.com" ``` @@ -93,7 +112,7 @@ $ ssh-keygen -t rsa -C "youremail@example.com" 建完成后,使用如下命令查找位置 -``` +```shell $ find / -name id_rsa.pub ``` @@ -103,7 +122,7 @@ $ find / -name id_rsa.pub ### 2.1 文件状态 -``` +```shell $ git status #如果修改了文件,状态会提示你有文件被修改(但不能告诉你哪里被修改),提示你要commit $ cat readme.txt #查看实体文件的内容 $ git ls-files #查看哪些文件在版本控制下 @@ -111,7 +130,7 @@ $ git ls-files #查看哪些文件在版本控制下 查看当前目录下有哪些远程仓库 -``` +```shell $ git remote # 远程仓库名字 $ git remote -v # verbose的缩写,显示详细信息,包括项目url 需要注意的是,当前Git Shell在不同目录下,git remote显示的远程仓库就不同 @@ -119,13 +138,13 @@ $ git remote -v # verbose的缩写,显示详细信息,包括项目url 查看提交的历史 -``` +```shell $ git log --pretty=oneline --abbrev-commit ``` 查看不同的地方(修改) -``` +```shell $ git diff # 工作区(work dict)和暂存区(stage)的比较 $ git diff --cached # 是暂存区(stage)和分支(master)的比较 意思就是说,我们修改文件并保存实体文件,可以使用git diff查看不同之处,再确定是否add到暂存区 @@ -158,11 +177,13 @@ git log -p ### 2.2 Git日志 -``` +```shell $ git log # 可以查看who在when修改了文件(会写出版本说明),但是这个看着眼花缭乱 $ git log --pretty=online # 这样,每行只显示一次修改,修改信息只有:commitid + 版本说明 $ git reflog # 显示所有修改的日志 + +$ git log --graph --all --decorate # 以可视化图的形式展示 ``` @@ -175,7 +196,7 @@ $ git reflog # 显示所有修改的日志 查看两个 commit 之间的修改 -``` +```shell $ git diff commit_id1 commit_id2 $ git diff commit_id^! @@ -185,14 +206,14 @@ $ git diff commit_id1 commit_id2 --stat 查看某个 commit 的改动 -``` -git show commit_id -git show --stat commit_id +```shell +$ git show commit_id +$ git show --stat commit_id ``` 查看两个分支之间的差异 -``` +```shell # 查看两个分支有哪些文件发生改变了 git diff stable/2.2.9 stable/2.3.0 --stat From f5860f7e93405889d316aa831ea318840339b762 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 28 Jun 2020 08:38:36 +0800 Subject: [PATCH 086/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_04.md | 294 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 source/c10/c10_04.md diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md new file mode 100644 index 0000000..30327c3 --- /dev/null +++ b/source/c10/c10_04.md @@ -0,0 +1,294 @@ +# 10.4 tcpdump 抓包教程 + +## 1. tcpdump 命令的组成 + +初学者在没有掌握 tcpdump 时,会对这个命令的参数产生恐惧。 + +就比如下面这个命令,我们要通过 `host` 参数指定 host ip 进行过滤 + +```shell +$ tcpdump host 192.168.10.100 +``` + +`主程序` + `参数名`+ `参数值` 这样的组合才是我们正常认知里面命令行该有的样子。 + +可 tcpdump 却不走寻常路,我们居然还可以在 host 前再加一个限定词,来缩小过滤的范围? + +```shell +$ tcpdump src host 192.168.10.100 +``` + +从字面上理解,确实很容易理解,但是这不符合编写命令行程序的正常逻辑,导致我们会有所疑虑: + +1. 除了 src ,dst,可还有其它可以用的限定词? + +2. src,host 应该如何理解它们,叫参数名?不合适,因为 src 明显不合适。 + + +如果你在网上看到有关 tcpdump 的博客、教程,无一不是给你一个参数组合,告诉你这是实现了怎样的一个过滤器?这样的教学方式,很容易让你依赖别人的文章来使用 tcpdump,而不能将 tcpdump 这样神器消化,达到灵活应用,灵活搭配过滤器的效果。 + +上面加了 src 本身就颠覆了我们的认知,你可知道在 src 之前还可以加更多的条件,比如 tcp, udp, icmp 等词,在你之前的基础上再过滤一层。 + +这种参数的不确定性,让大多数人对 tcpdump 的学习始终无法得其精髓。 + +因此,在学习 tcpdump 之前,我觉得有必要要先让你知道:**tcpdump 的参数是如何组成的?这非常重要。** + + + +1. type 修饰符:host, net, port, portrange +2. direction 修饰符:src, dst +3. proto 修饰符:ether, fddi, tr, wlan, ip, ip6, arp, rarp, decnet, tcp以及 upd + +## 1. 基于网卡过滤 + +```shell +$ tcpdump -i eth0 +``` + +## 2. 基于IP地址过滤 + +使用 `host` 就可以指定 host ip 进行过滤 + +```shell +$ tcpdump host 192.168.10.100 +``` + +数据包的 ip 可以再细分为源ip和目标ip两种 + +```shell +# 根据源ip进行过滤 +$ tcpdump src 192.168.10.100 + +# 根据目标ip进行过滤 +$ tcpdump dst 192.168.10.200 +``` + +若你的ip范围是一个网段,可以直接这样指定 + +```shell +$ tcpdump net 192.168.10.0/24 +``` + +网段同样可以再细分为源网段和目标网段 + +```shell +# 根据源网段进行过滤 +$ tcpdump src net 192.168 + +# 根据目标网段进行过滤 +$ tcpdump dst net 192.168 +``` + + + +## 3. 基于端口进行过滤 + +使用 `port` 就可以指定特定端口进行过滤 + +```shell +$ tcpdump port 8088 +``` + +端口同样可以再细分为源端口,目标端口 + +```shell +# 根据源端口进行过滤 +$ tcpdump src port 8088 + +# 根据目标端口进行过滤 +$ tcpdump dst port 443 +``` + +若你想过滤的是一个端口范围,一个一个指定就较为麻烦,此时你可以这样指定一个端口段。 + +```shell +$ tcpdump portrange 12400-12450 +``` + + + +## 4. 基于协议进行过滤 + +常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等 + +若你只想查看 icmp 的包,可以直接这样写 + +```shell +$ tcpdump icmp +``` + +ip 可以细分为 ipv4 和 ipv6,那么如何过滤呢? + +```shell +$ cpdump ip6 +``` + + + +## 5. 基于包大小进行过滤 + +若你想查看指定大小的数据包,也是可以的 + +```shell +$ tcpdump less 32 +$ tcpdump greater 64 +$ tcpdump <= 128 +``` + + + +## 6. 过滤结果输出到文件 + +使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 wireshark 。 + +而要使用wireshark ,我们得将 tcpdump 抓到的包数据生成到文件中,最后再使用 wireshark 打开它即可。 + +使用 `-w` 参数后接一个以 `.pcap` 后缀命令的文件名,就可以将 tcpdump 抓到的数据保存到文件中。 + +```shell +$ tcpdump icmp -w icmp.pcap +``` + + + +## 7. 从文件中读取包数据进行分析 + +使用 `-w` 是写入数据到文件,而使用 `-r` 是从文件中读取数据。 + +读取后,我们照样可以使用上述的过滤器语法进行过滤分析。 + +```shell +$ tcpdump icmp -r all.pcap +``` + + + +## 8. and or not 实现多个过滤器组合 + +有编程基础的同学,对于这三者应该很熟悉了 + +- and:所有的条件都需要满足,也可以表示为 `&&` +- or:只要有一个条件满足就可以,也可以表示为 `||` +- not:取反,也可以使用 `!` + +举个例子,我想需要抓一个来自`10.5.2.3`,发往任意主机的3389端口的包 + +```shell +$ tcpdump src 10.5.2.3 and dst port 3389 +``` + +当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell 中是特殊符号,因为你需要使用引号将其包含。例子如下: + +```shell +$ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' +``` + +而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用下面两个符号 + +- =:判断二者相等 +- !=:判断二者不相等 + +当你使用这两个符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如 + +- if:表示网卡接口名、 +- proc:表示进程名 +- pid:表示进程 id +- svc:表示 service class +- dir:表示方向,in 和 out +- eproc:表示 effective process name +- epid:表示 effective process ID + +比如我现在要过滤来自进程名为 `nc` 发出的流经 en0 网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写 + +```shell +-Q "( if=en0 and proc =nc ) || (if != en0 and dir=in)" +``` + + + + + +## 9. 根据 tcpflags 进行过滤 + +``` +proto [ expr : size ] +``` + +“proto”可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6),“expr”表示与指定的协议头开头相关的字节偏移量。有我们熟知的直接偏移量如tcpflags,也有取值常量如tcp-syn,tcp-ack或者tcp-fin。“size”是可选的,表示从字节偏移量开始检查的字节数量。 + +对于下面的例子,我先解释一下 + +- tcpflags 是一个常量 相当于 13,它代表着与指定的协议头开头相关的字节偏移量。 +- tcp-syn 和 tcp-ack ,以及 tcp-fin 都是常量,分别代表 1,2,16,由于数字不好记忆,所以一般使用常量表示。 + +![TCP 报文首部](http://image.iswbm.com/20200606095627.png) + +只捕获TCP SYN包: + +```shell +$ tcpdump -i "tcp[tcpflags] & (tcp-syn) != 0" +``` + +只捕获TCP ACK包: + +```shell +$ tcpdump -i "tcp[tcpflags] & (tcp-ack) != 0" +``` + +只捕获TCP FIN包: + +```shell +$ tcpdump -i "tcp[tcpflags] & (tcp-fin) != 0" +``` + +之捕获TCP SYN或ACK包: + +```shell +$ tcpdump -i "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0" + +# 或者 + +$ tcpdump -i 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack' + +# 或者 + +$ tcpdump -i 'tcp[13] == 2 or tcp[13] == 16' +``` + + + +## 10. 对输出内容进行控制的参数 + +- `-A`:以ASCII码方式显示每一个数据包(不显示链路层头部信息). 在抓取包含网页数据的数据包时, 可方便查看数据 + +- `-D` : 显示所有可用网络接口的列表 +- `-l` : 基于行的输出,便于你保存查看,或者交给其它工具分析 +- `-q` : 使用较少的信息(more quiet),显示较少的协议信息 +- `-c` : 捕获 count 个包 tcpdump 就退出 +- `-s` : 定义包获取的字节大小.使用`-s0`获取完整的包 +- `-S` : 使用绝对序列号,而不是相对序列号 +- `-e` : 每行的打印输出中将包括数据包的数据链路层头部信息 +- `-E` : 揭秘IPSEC数据 +- `-C`:file-size,tcpdump 在把原始数据包直接保存到文件中之前, 检查此文件大小是否超过file-size. 如果超过了, 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w 选项指定的文件名一致, 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加. file-size的单位是百万字节(nt: 这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576) +- `-F`:使用file 文件作为过滤条件表达式的输入, 此时命令行上的输入将被忽略. +- `-L` :列出指定网络接口所支持的数据链路层的类型后退出 +- `-n`:不解析域名,直接显示 ip +- `-nn`:不解析域名和端口 +- `-t `:在每行的输出中不输出时间 +- `-tt`:在每行的输出中会输出时间戳 +- `-ttt`:输出每两行打印的时间间隔(以毫秒为单位) +- `-tttt`:在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观) +- `-v`:产生详细的输出. 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。 +- `-vv`:产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。(摘自网络,目前我还未使用过) +- `-vvv`:产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) +- `-x`:以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部) + +- `-xx`:以16进制的形式打印每个包的头部数据(包括数据链路层的头部) +- `-X`:以16进制和 ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。 +- `-XX`:以16进制和 ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。 +- `-Z`:后接用户名,在抓包时会受到权限的限制。如果以root用户启动tcpdump,tcpdump将会有超级用户权限。 +- `-d`:打印出易读的包匹配码 +- `-dd`:以C语言的形式打印出包匹配码. +- `-ddd`:以十进制数的形式打印出包匹配码 +- `-i`:指定要过滤的网卡接口,如果要查看所有网卡,可以 `-i any` +- -Q: 选择是入方向还是出方向的数据包,可选项有:in, out, inout,也可以使用 --direction=[direction] 这种写法 \ No newline at end of file From 14f8664c9dc2a1398c8bb56447ea0cab45321509 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 28 Jun 2020 08:47:54 +0800 Subject: [PATCH 087/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c09/c09_02.md | 2 ++ source/c10/c10_06.md | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md index c0ac7b5..ec909f5 100644 --- a/source/c09/c09_02.md +++ b/source/c09/c09_02.md @@ -81,6 +81,8 @@ Coffee Buzz:阻止电脑休眠,三种模式:永不休眠、屏幕熄灭但 ShortCat:在系统栏也可以搜索聚焦 +TouchBarServer:在外接屏幕上调出 touchbar + ## 4. 图片影音 Snipaste:截图工具 diff --git a/source/c10/c10_06.md b/source/c10/c10_06.md index 23b1ffc..5859d1f 100644 --- a/source/c10/c10_06.md +++ b/source/c10/c10_06.md @@ -36,4 +36,6 @@ UDP 首部固定只有 8 个字节,开销较小。 -[面向报文(UDP)和面向字节流(TCP)的区别](https://blog.csdn.net/ce123_zhouwei/article/details/8976006) \ No newline at end of file +[面向报文(UDP)和面向字节流(TCP)的区别](https://blog.csdn.net/ce123_zhouwei/article/details/8976006) + +[TCP缓存区与窗口的关系](https://blog.csdn.net/yxccc_914/article/details/52515558) \ No newline at end of file From 3008adbc86cfc1028e90d7a9095feffd9bd63ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sun, 28 Jun 2020 22:16:40 +0800 Subject: [PATCH 088/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_04.md | 422 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 340 insertions(+), 82 deletions(-) diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index 30327c3..a6d1596 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -1,6 +1,11 @@ -# 10.4 tcpdump 抓包教程 +# 10.4 全网最全的 tcpdump 抓包指南 -## 1. tcpdump 命令的组成 +在讲解之前,有两点需要声明: + +1. 第二节到第三节里的 tcpdump 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第四节的逻辑逻辑运算符进行组合搭配。 +2. 不同 Linux 发行版下、不同版本的 tcpdump 可能有小许差异, 本文是基于 CentOS 7.2 的 4.5.1 版本的tcpdump 进行学习的,若在你的环境中无法使用,请参考 `man tcpdump` 进行针对性学习。 + +## 1. tcpdump 核心参数图解 初学者在没有掌握 tcpdump 时,会对这个命令的参数产生恐惧。 @@ -29,23 +34,38 @@ $ tcpdump src host 192.168.10.100 上面加了 src 本身就颠覆了我们的认知,你可知道在 src 之前还可以加更多的条件,比如 tcp, udp, icmp 等词,在你之前的基础上再过滤一层。 +```shell +$ tcpdump tcp src host 192.168.10.100 +``` + + + 这种参数的不确定性,让大多数人对 tcpdump 的学习始终无法得其精髓。 因此,在学习 tcpdump 之前,我觉得有必要要先让你知道:**tcpdump 的参数是如何组成的?这非常重要。** +为此,我画了一张图,方便你直观的理解 tcpdump 的各种参数: +![](http://image.iswbm.com/20200628111325.png) -1. type 修饰符:host, net, port, portrange -2. direction 修饰符:src, dst -3. proto 修饰符:ether, fddi, tr, wlan, ip, ip6, arp, rarp, decnet, tcp以及 upd +1. option 可选参数:将在后边一一解释,对应本文[第三节:可选参数解析]() +2. proto 限定词:根据协议进行过滤,可识别的关键词有: upd, udp, icmp, ip, ip6, arp, rarp,ether,wlan, fddi, tr, decnet +3. type 限定词:可识别的关键词有:host, net, port, portrange,这些词后边需要再接参数。 +4. direction 限定词:根据数据流向进行过滤,可识别的关键字有:src, dst,同时你可以使用逻辑运算符进行组合,比如 src or dst -## 1. 基于网卡过滤 +proto、type、direction 这三个限定词的内容比较简单,也最常用,因此我将其放在最前面,也就是 [第二节:常规过滤规则]() 一起介绍。 -```shell -$ tcpdump -i eth0 -``` +而 option 可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 [第三节:可选参数解析]() + +当你看完前面四节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% 的使用需求。 + +你一定会问了,还有 20% 呢? + +其实 tcpdump 还有一些过滤关键词,它不符合以上四种限定规则,可能需要你单独记忆。关于这部分我会在 [第五节:特殊过滤规则]() 里进行介绍。 + +## 2. 常规过滤规则 -## 2. 基于IP地址过滤 +### 1.1 基于IP地址过滤:host 使用 `host` 就可以指定 host ip 进行过滤 @@ -57,12 +77,14 @@ $ tcpdump host 192.168.10.100 ```shell # 根据源ip进行过滤 -$ tcpdump src 192.168.10.100 +$ tcpdump -i eth2 src 192.168.10.100 # 根据目标ip进行过滤 -$ tcpdump dst 192.168.10.200 +$ tcpdump -i eth2 dst 192.168.10.200 ``` +### 1.2 基于网段进行过滤:net + 若你的ip范围是一个网段,可以直接这样指定 ```shell @@ -81,7 +103,7 @@ $ tcpdump dst net 192.168 -## 3. 基于端口进行过滤 +### 1.3 基于端口进行过滤:port 使用 `port` 就可以指定特定端口进行过滤 @@ -96,18 +118,20 @@ $ tcpdump port 8088 $ tcpdump src port 8088 # 根据目标端口进行过滤 -$ tcpdump dst port 443 +$ tcpdump dst port 8088 ``` 若你想过滤的是一个端口范围,一个一个指定就较为麻烦,此时你可以这样指定一个端口段。 ```shell -$ tcpdump portrange 12400-12450 +$ tcpdump portrange 8000-8080 +$ tcpdump src portrange 8000-8080 +$ tcpdump dst portrange 8000-8080 ``` -## 4. 基于协议进行过滤 +### 1.4 基于协议进行过滤:proto 常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等 @@ -117,27 +141,100 @@ $ tcpdump portrange 12400-12450 $ tcpdump icmp ``` -ip 可以细分为 ipv4 和 ipv6,那么如何过滤呢? +protocol 可选值:ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or netbeui + + + +### 1.5 基本IP协议的版本进行过滤 + +当你想查看 tcp 的包,你也许会这样子写 ```shell -$ cpdump ip6 +$ tcpdump tcp ``` +这样子写也没问题,就是不够精准,为什么这么说呢? +ip 根据版本的不同,可以再细分为 IPv4 和 IPv6 两种,如果你只指定了 tcp,这两种其实都会包含在内。 -## 5. 基于包大小进行过滤 -若你想查看指定大小的数据包,也是可以的 + +那有什么办法,能够将 IPv4 和 IPv6 区分开来呢? + +很简单,如果是 IPv4 的 tcp 包 ,就这样写(友情提示:数字 6 表示的是 tcp 在ip报文中的编号。) ```shell -$ tcpdump less 32 -$ tcpdump greater 64 -$ tcpdump <= 128 +$ tcpdump 'ip proto tcp' + +# or + +$ tcpdump ip proto 6 + +# or + +$ tcpdump 'ip protochain tcp' + +# or +$ tcpdump ip protochain 6 +``` + +而如果是 IPv6 的 tcp 包 ,就这样写 + +```shell +$ tcpdump 'ip6 proto tcp' + +# or + +$ tcpdump ip6 proto 6 + +# or + +$ tcpdump 'ip6 protochain tcp' + +# or +$ tcpdump ip6 protochain 6 +``` + + + +关于上面这几个命令示例,有两点需要注意: + +1. 跟在 proto 和 protochain 后面的如果是 tcp, udp, icmp ,那么过滤器需要用引号包含,这是因为 tcp,udp, icmp 是 tcpdump 的关键字。 +2. 跟在ip 和 ip6 关键字后面的 proto 和 protochain 是两个新面孔,看起来用法类似,它们是否等价,又有什么区别呢? + +关于第二点,网络上没有找到很具体的答案,我只能通过 `man tcpdump` 的提示, 给出自己的个人猜测,但不保证正确。 + +proto 后面跟的 `` 的关键词是固定的,只能是 ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or netbeui 这里面的其中一个。 + +而 protochain 后面跟的 protocol 要求就没有那么严格,它可以是任意词,只要 tcpdump 的 IP 报文头部里的 protocol 字段为 `` 就能匹配上。 + + + +理论上来讲,下面两种写法效果是一样的 + +```shell +$ tcpdump 'ip && tcp' +$ tcpdump 'ip proto tcp' +``` + +同样的,这两种写法也是一样的 + +```shell +$ tcpdump 'ip6 && tcp' +$ tcpdump 'ip6 proto tcp' ``` -## 6. 过滤结果输出到文件 +## 3. 可选参数解析 + +### 3.1 设置不解析域名提升速度 + +- `-n`:不把ip转化成域名,直接显示 ip,避免执行 DNS lookups 的过程,速度会快很多 +- `-nn`:不把协议和端口号转化成名字,速度也会快很多。 +- `-N`:不打印出host 的域名部分.。比如,,如果设置了此选现,tcpdump 将会打印'nic' 而不是 'nic.ddn.mil'. + +### 3.2 过滤结果输出到文件 使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 wireshark 。 @@ -149,9 +246,7 @@ $ tcpdump <= 128 $ tcpdump icmp -w icmp.pcap ``` - - -## 7. 从文件中读取包数据进行分析 +### 3.3 从文件中读取包数据 使用 `-w` 是写入数据到文件,而使用 `-r` 是从文件中读取数据。 @@ -163,9 +258,60 @@ $ tcpdump icmp -r all.pcap -## 8. and or not 实现多个过滤器组合 +### 3.4 控制详细内容的输出 + +- `-v`:产生详细的输出. 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。 +- `-vv`:产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。(摘自网络,目前我还未使用过) +- `-vvv`:产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) + +### 3.5 控制时间的显示 + +- `-t `:在每行的输出中不输出时间 +- `-tt`:在每行的输出中会输出时间戳 +- `-ttt`:输出每两行打印的时间间隔(以毫秒为单位) +- `-tttt`:在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观) + +### 3.6 显示数据包的头部 + +- `-x`:以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部) +- `-xx`:以16进制的形式打印每个包的头部数据(包括数据链路层的头部) +- `-X`:以16进制和 ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。 +- `-XX`:以16进制和 ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。 + +### 3.7 过滤指定网卡的数据包 + +- `-i`:指定要过滤的网卡接口,如果要查看所有网卡,可以 `-i any` + +### 3.8 过滤特定流向的数据包 + +- `-Q`: 选择是入方向还是出方向的数据包,可选项有:in, out, inout,也可以使用 --direction=[direction] 这种写法 + +### 3.9 其他常用的一些参数 + +- `-A`:以ASCII码方式显示每一个数据包(不显示链路层头部信息). 在抓取包含网页数据的数据包时, 可方便查看数据 + +- `-l` : 基于行的输出,便于你保存查看,或者交给其它工具分析 +- `-q` : 简洁地打印输出。即打印很少的协议相关信息, 从而输出行都比较简短. +- `-c` : 捕获 count 个包 tcpdump 就退出 +- `-s` : 定义包获取的字节大小.使用`-s0`获取完整的包 +- `-S` : 使用绝对序列号,而不是相对序列号 +- `-C`:file-size,tcpdump 在把原始数据包直接保存到文件中之前, 检查此文件大小是否超过file-size. 如果超过了, 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w 选项指定的文件名一致, 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加. file-size的单位是百万字节(nt: 这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576) +- `-F`:使用file 文件作为过滤条件表达式的输入, 此时命令行上的输入将被忽略. + +### 3.10 对输出内容进行控制的参数 + +- `-D` : 显示所有可用网络接口的列表 +- `-e` : 每行的打印输出中将包括数据包的数据链路层头部信息 +- `-E` : 揭秘IPSEC数据 +- `-L` :列出指定网络接口所支持的数据链路层的类型后退出 +- `-Z`:后接用户名,在抓包时会受到权限的限制。如果以root用户启动tcpdump,tcpdump将会有超级用户权限。 +- `-d`:打印出易读的包匹配码 +- `-dd`:以C语言的形式打印出包匹配码. +- `-ddd`:以十进制数的形式打印出包匹配码 + +## 4. 过滤规则组合 -有编程基础的同学,对于这三者应该很熟悉了 +有编程基础的同学,对于下面三个逻辑运算符应该不陌生了吧 - and:所有的条件都需要满足,也可以表示为 `&&` - or:只要有一个条件满足就可以,也可以表示为 `||` @@ -185,8 +331,9 @@ $ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' 而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用下面两个符号 -- =:判断二者相等 -- !=:判断二者不相等 +- `=`:判断二者相等 +- `==`:判断二者相等 +- `!=`:判断二者不相等 当你使用这两个符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如 @@ -201,94 +348,205 @@ $ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' 比如我现在要过滤来自进程名为 `nc` 发出的流经 en0 网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写 ```shell --Q "( if=en0 and proc =nc ) || (if != en0 and dir=in)" +$ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" ``` +## + +## 5. 特殊过滤规则 +### 5.1 根据 tcpflags 进行过滤 +通过上一篇文章,我们知道了 tcp 的首部有一个标志位。 +![TCP 报文首部](http://image.iswbm.com/20200606095627.png) -## 9. 根据 tcpflags 进行过滤 +tcpdump 支持我们根据数据包的标志位进行过滤 ``` -proto [ expr : size ] +proto [ expr:size ] ``` -“proto”可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6),“expr”表示与指定的协议头开头相关的字节偏移量。有我们熟知的直接偏移量如tcpflags,也有取值常量如tcp-syn,tcp-ack或者tcp-fin。“size”是可选的,表示从字节偏移量开始检查的字节数量。 +“proto”可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6),“expr”表示与指定的协议头开头相关的字节偏移量。有我们熟知的直接偏移量如tcpflags,也有取值常量如tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg。“size”是可选的,表示从字节偏移量开始检查的字节数量。 对于下面的例子,我先解释一下 -- tcpflags 是一个常量 相当于 13,它代表着与指定的协议头开头相关的字节偏移量。 -- tcp-syn 和 tcp-ack ,以及 tcp-fin 都是常量,分别代表 1,2,16,由于数字不好记忆,所以一般使用常量表示。 +1、tcpflags 可以理解为是一个常量,相当于 13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位 + +![](E:\MING-Git\PythonCodingTime\source\c10\image-20200628175102266.png) + +2、tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg 这些同样可以理解为常量,分别代表 1,2,4,8,16,32,64。这些数字是如何计算出来的呢? + +以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2 + +![](E:\MING-Git\PythonCodingTime\source\c10\image-20200628175633284.png) + +由于数字不好记忆,所以一般使用“常量”表示。 + -![TCP 报文首部](http://image.iswbm.com/20200606095627.png) -只捕获TCP SYN包: +下面以最常见的 syn包为例,演示一下如何用抓取 syn 包,其他的类型的包你可以自行修改。 + +主要有三种写法: +1、第一种写法:使用数字表示偏移量 ```shell -$ tcpdump -i "tcp[tcpflags] & (tcp-syn) != 0" +$ tcpdump -i eth0 "tcp[13] & 2 != 0" ``` -只捕获TCP ACK包: +2、第二种写法:使用常量表示偏移量 ```shell -$ tcpdump -i "tcp[tcpflags] & (tcp-ack) != 0" +$ tcpdump -i eth0 "tcp[tcpflags] & tcp-syn != 0" ``` -只捕获TCP FIN包: +3、第三种写法:使用混合写法 ```shell -$ tcpdump -i "tcp[tcpflags] & (tcp-fin) != 0" +$ tcpdump -i eth0 "tcp[tcpflags] & 2 != 0" + +# or + +$ tcpdump -i eth0 "tcp[13] & tcp-syn != 0" ``` -之捕获TCP SYN或ACK包: + + +如果我想同时捕获多种类型的包呢,比如 syn + ack 包 + +1、第一种写法 ```shell -$ tcpdump -i "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0" +$ tcpdump -i eth0 'tcp[13] == 2 or tcp[13] == 16' +``` -# 或者 +2、第二种写法 -$ tcpdump -i 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack' +```shell +$ tcpdump -i eth0 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack' +``` -# 或者 +3、第三种写法 -$ tcpdump -i 'tcp[13] == 2 or tcp[13] == 16' +```shell +$ tcpdump -i eth0 "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0" ``` +4、第四种写法:注意这里是 单个等号,而不是像上面一样两个等号,18(syn+ack) = 2(syn) + 16(ack) +```shell +$ tcpdump -i eth0 'tcp[13] = 18' -## 10. 对输出内容进行控制的参数 +# or -- `-A`:以ASCII码方式显示每一个数据包(不显示链路层头部信息). 在抓取包含网页数据的数据包时, 可方便查看数据 +$ tcpdump -i eth0 'tcp[tcpflags] = 18' +``` -- `-D` : 显示所有可用网络接口的列表 -- `-l` : 基于行的输出,便于你保存查看,或者交给其它工具分析 -- `-q` : 使用较少的信息(more quiet),显示较少的协议信息 -- `-c` : 捕获 count 个包 tcpdump 就退出 -- `-s` : 定义包获取的字节大小.使用`-s0`获取完整的包 -- `-S` : 使用绝对序列号,而不是相对序列号 -- `-e` : 每行的打印输出中将包括数据包的数据链路层头部信息 -- `-E` : 揭秘IPSEC数据 -- `-C`:file-size,tcpdump 在把原始数据包直接保存到文件中之前, 检查此文件大小是否超过file-size. 如果超过了, 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w 选项指定的文件名一致, 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加. file-size的单位是百万字节(nt: 这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576) -- `-F`:使用file 文件作为过滤条件表达式的输入, 此时命令行上的输入将被忽略. -- `-L` :列出指定网络接口所支持的数据链路层的类型后退出 -- `-n`:不解析域名,直接显示 ip -- `-nn`:不解析域名和端口 -- `-t `:在每行的输出中不输出时间 -- `-tt`:在每行的输出中会输出时间戳 -- `-ttt`:输出每两行打印的时间间隔(以毫秒为单位) -- `-tttt`:在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观) -- `-v`:产生详细的输出. 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。 -- `-vv`:产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。(摘自网络,目前我还未使用过) -- `-vvv`:产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) -- `-x`:以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部) -- `-xx`:以16进制的形式打印每个包的头部数据(包括数据链路层的头部) -- `-X`:以16进制和 ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。 -- `-XX`:以16进制和 ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。 -- `-Z`:后接用户名,在抓包时会受到权限的限制。如果以root用户启动tcpdump,tcpdump将会有超级用户权限。 -- `-d`:打印出易读的包匹配码 -- `-dd`:以C语言的形式打印出包匹配码. -- `-ddd`:以十进制数的形式打印出包匹配码 -- `-i`:指定要过滤的网卡接口,如果要查看所有网卡,可以 `-i any` -- -Q: 选择是入方向还是出方向的数据包,可选项有:in, out, inout,也可以使用 --direction=[direction] 这种写法 \ No newline at end of file + +tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp 协议,可以使用的常量有 + +```shell +icmp-echoreply, icmp-unreach, icmp-sourcequench, +icmp-redirect, icmp-echo, icmp-routeradvert, +icmp-routersolicit, icmp-timx-ceed, icmp-paramprob, +icmp-tstamp, icmp-tstampreply,icmp-ireq, +icmp-ireqreply, icmp-maskreq, icmp-maskreply +``` + + + +### 5.2 基于包大小进行过滤 + +若你想查看指定大小的数据包,也是可以的 + +```shell +$ tcpdump less 32 +$ tcpdump greater 64 +$ tcpdump <= 128 +``` + + + +### 5.3 根据 mac 地址进行过滤 + +例子如下,其中 ehost 是记录在 /etc/ethers 里的 name + +```shell +$ tcpdump ether host [ehost] +$ tcpdump ether dst [ehost] +$ tcpdump ether src [ehost] +``` + +### 5.4 过滤通过指定网关的数据包 + +```shell +$ tcpdump gateway [host] +``` + +### 5.5 过滤广播/多播数据包 + +```shell +$ tcpdump ether broadcast +$ tcpdump ether multicast + +$ tcpdump ip broadcast +$ tcpdump ip multicast + +$ tcpdump ip6 multicast +``` + + + +## 6. 理解 tcpdump 的输出 + +### 1. 输出内容结构 + +tcpdump 输出的内容虽然多,却很规律。 + +这里以我随便抓取的一个 tcp 包为例来看一下 + +```shell +21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48 +``` + +从上面的输出来看,可以总结出: + +1. 第一列:时分秒毫秒 21:26:49.013621 +2. 第二列:网络协议 IP +3. 第三列:发送方的ip地址+端口号,其中172.20.20.1是 ip,而15605 是端口号 +4. 第四列:箭头 >, 表示数据流向 +5. 第五列:接收方的ip地址+端口号,其中 172.20.20.2 是 ip,而5920 是端口号 +6. 第六列:冒号 +7. 第七列:数据包内容,包括Flags 标识符,seq 号,ack 号,win 窗口,数据长度 length,其中 [P.] 表示 PUSH 标志位为 1,更多标识符见下面 + + + +### 2. Flags 标识符 + +使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种: + +- `[S]` : SYN(开始连接) +- `[P]` : PSH(推送数据) +- `[F]` : FIN (结束连接) +- `[R]` : RST(重置连接) +- `[.]` : 没有 Flag,由于除了 SYN 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK + + + +## 7. 实战应用例子 + +https://juejin.im/post/5e64571bf265da57104393a1#heading-14 + +https://danielmiessler.com/study/tcpdump/ + +https://colobu.com/2019/07/16/a-tcpdump-tutorial-with-examples/ + + + +参考文章 + +1. https://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+7.2-RELEASE&format=html +2. https://wizardforcel.gitbooks.io/network-basic/17.html +3. https://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html +4. [一份快速实用的 tcpdump 命令参考手册](http://team.jiunile.com/blog/2019/06/tcpdump.html) \ No newline at end of file From eb9a7d26d8229ac06b1183075319a66af95c5e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sun, 28 Jun 2020 22:22:11 +0800 Subject: [PATCH 089/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_04.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index a6d1596..120b9c8 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -373,13 +373,13 @@ proto [ expr:size ] 1、tcpflags 可以理解为是一个常量,相当于 13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位 -![](E:\MING-Git\PythonCodingTime\source\c10\image-20200628175102266.png) +![](http://image.iswbm.com/20200628222034.png) 2、tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg 这些同样可以理解为常量,分别代表 1,2,4,8,16,32,64。这些数字是如何计算出来的呢? 以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2 -![](E:\MING-Git\PythonCodingTime\source\c10\image-20200628175633284.png) +![](http://image.iswbm.com/20200628222010.png) 由于数字不好记忆,所以一般使用“常量”表示。 From 52f821e4a7bc054f55606954bb1d1c71542d9f8a Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 29 Jun 2020 13:05:52 +0800 Subject: [PATCH 090/147] =?UTF-8?q?Add=EF=BC=9A=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_04.md | 173 +++++++--- source/c10/c10_04.rst | 748 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 880 insertions(+), 41 deletions(-) create mode 100644 source/c10/c10_04.rst diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index 120b9c8..afe6022 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -63,9 +63,43 @@ proto、type、direction 这三个限定词的内容比较简单,也最常用 其实 tcpdump 还有一些过滤关键词,它不符合以上四种限定规则,可能需要你单独记忆。关于这部分我会在 [第五节:特殊过滤规则]() 里进行介绍。 -## 2. 常规过滤规则 +## 2. 理解 tcpdump 的输出 -### 1.1 基于IP地址过滤:host +### 2.1 输出内容结构 + +tcpdump 输出的内容虽然多,却很规律。 + +这里以我随便抓取的一个 tcp 包为例来看一下 + +```shell +21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48 +``` + +从上面的输出来看,可以总结出: + +1. 第一列:时分秒毫秒 21:26:49.013621 +2. 第二列:网络协议 IP +3. 第三列:发送方的ip地址+端口号,其中172.20.20.1是 ip,而15605 是端口号 +4. 第四列:箭头 >, 表示数据流向 +5. 第五列:接收方的ip地址+端口号,其中 172.20.20.2 是 ip,而5920 是端口号 +6. 第六列:冒号 +7. 第七列:数据包内容,包括Flags 标识符,seq 号,ack 号,win 窗口,数据长度 length,其中 [P.] 表示 PUSH 标志位为 1,更多标识符见下面 + + + +### 2.2 Flags 标识符 + +使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种: + +- `[S]` : SYN(开始连接) +- `[P]` : PSH(推送数据) +- `[F]` : FIN (结束连接) +- `[R]` : RST(重置连接) +- `[.]` : 没有 Flag,由于除了 SYN 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK + +## 3. 常规过滤规则 + +### 3.1 基于IP地址过滤:host 使用 `host` 就可以指定 host ip 进行过滤 @@ -83,7 +117,7 @@ $ tcpdump -i eth2 src 192.168.10.100 $ tcpdump -i eth2 dst 192.168.10.200 ``` -### 1.2 基于网段进行过滤:net +### 3.2 基于网段进行过滤:net 若你的ip范围是一个网段,可以直接这样指定 @@ -103,7 +137,7 @@ $ tcpdump dst net 192.168 -### 1.3 基于端口进行过滤:port +### 3.3 基于端口进行过滤:port 使用 `port` 就可以指定特定端口进行过滤 @@ -131,7 +165,7 @@ $ tcpdump dst portrange 8000-8080 -### 1.4 基于协议进行过滤:proto +### 3.4 基于协议进行过滤:proto 常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等 @@ -145,7 +179,7 @@ protocol 可选值:ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, mopdl, -### 1.5 基本IP协议的版本进行过滤 +### 3.5 基本IP协议的版本进行过滤 当你想查看 tcp 的包,你也许会这样子写 @@ -226,15 +260,15 @@ $ tcpdump 'ip6 proto tcp' -## 3. 可选参数解析 +## 4. 可选参数解析 -### 3.1 设置不解析域名提升速度 +### 4.1 设置不解析域名提升速度 - `-n`:不把ip转化成域名,直接显示 ip,避免执行 DNS lookups 的过程,速度会快很多 - `-nn`:不把协议和端口号转化成名字,速度也会快很多。 - `-N`:不打印出host 的域名部分.。比如,,如果设置了此选现,tcpdump 将会打印'nic' 而不是 'nic.ddn.mil'. -### 3.2 过滤结果输出到文件 +### 4.2 过滤结果输出到文件 使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 wireshark 。 @@ -246,7 +280,7 @@ $ tcpdump 'ip6 proto tcp' $ tcpdump icmp -w icmp.pcap ``` -### 3.3 从文件中读取包数据 +### 4.3 从文件中读取包数据 使用 `-w` 是写入数据到文件,而使用 `-r` 是从文件中读取数据。 @@ -258,47 +292,47 @@ $ tcpdump icmp -r all.pcap -### 3.4 控制详细内容的输出 +### 4.4 控制详细内容的输出 - `-v`:产生详细的输出. 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。 - `-vv`:产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。(摘自网络,目前我还未使用过) - `-vvv`:产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) -### 3.5 控制时间的显示 +### 4.5 控制时间的显示 - `-t `:在每行的输出中不输出时间 - `-tt`:在每行的输出中会输出时间戳 - `-ttt`:输出每两行打印的时间间隔(以毫秒为单位) - `-tttt`:在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观) -### 3.6 显示数据包的头部 +### 4.6 显示数据包的头部 - `-x`:以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部) - `-xx`:以16进制的形式打印每个包的头部数据(包括数据链路层的头部) - `-X`:以16进制和 ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。 - `-XX`:以16进制和 ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。 -### 3.7 过滤指定网卡的数据包 +### 4.7 过滤指定网卡的数据包 - `-i`:指定要过滤的网卡接口,如果要查看所有网卡,可以 `-i any` -### 3.8 过滤特定流向的数据包 +### 4.8 过滤特定流向的数据包 - `-Q`: 选择是入方向还是出方向的数据包,可选项有:in, out, inout,也可以使用 --direction=[direction] 这种写法 -### 3.9 其他常用的一些参数 +### 4.9 其他常用的一些参数 - `-A`:以ASCII码方式显示每一个数据包(不显示链路层头部信息). 在抓取包含网页数据的数据包时, 可方便查看数据 - `-l` : 基于行的输出,便于你保存查看,或者交给其它工具分析 - `-q` : 简洁地打印输出。即打印很少的协议相关信息, 从而输出行都比较简短. - `-c` : 捕获 count 个包 tcpdump 就退出 -- `-s` : 定义包获取的字节大小.使用`-s0`获取完整的包 +- `-s` : tcpdump 默认只会截取前 `96` 字节的内容,要想截取所有的报文内容,可以使用 `-s number`, `number` 就是你要截取的报文字节数,如果是 0 的话,表示截取报文全部内容。 - `-S` : 使用绝对序列号,而不是相对序列号 - `-C`:file-size,tcpdump 在把原始数据包直接保存到文件中之前, 检查此文件大小是否超过file-size. 如果超过了, 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w 选项指定的文件名一致, 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加. file-size的单位是百万字节(nt: 这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576) - `-F`:使用file 文件作为过滤条件表达式的输入, 此时命令行上的输入将被忽略. -### 3.10 对输出内容进行控制的参数 +### 4.10 对输出内容进行控制的参数 - `-D` : 显示所有可用网络接口的列表 - `-e` : 每行的打印输出中将包括数据包的数据链路层头部信息 @@ -309,7 +343,7 @@ $ tcpdump icmp -r all.pcap - `-dd`:以C语言的形式打印出包匹配码. - `-ddd`:以十进制数的形式打印出包匹配码 -## 4. 过滤规则组合 +## 5. 过滤规则组合 有编程基础的同学,对于下面三个逻辑运算符应该不陌生了吧 @@ -353,7 +387,7 @@ $ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" ## -## 5. 特殊过滤规则 +## 6. 特殊过滤规则 ### 5.1 根据 tcpflags 进行过滤 @@ -498,43 +532,100 @@ $ tcpdump ip6 multicast -## 6. 理解 tcpdump 的输出 +## 7. 如何抓取到更精准的包? -### 1. 输出内容结构 +先给你抛出一个问题:如果我只想抓取 HTTP 的 POST 请求该如何写呢? -tcpdump 输出的内容虽然多,却很规律。 +如果只学习了上面的内容,恐怕你还是无法写法满足这个抓取需求的过滤器。 -这里以我随便抓取的一个 tcp 包为例来看一下 +在学习之前,我先给出答案,然后再剖析一下,这个过滤器是如何生效的,居然能让我们对包内的内容进行判断。 ```shell -21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48 +$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' ``` -从上面的输出来看,可以总结出: +命令里的可选参数,在前面的内容里已经详细讲过了。这里不再细讲。 -1. 第一列:时分秒毫秒 21:26:49.013621 -2. 第二列:网络协议 IP -3. 第三列:发送方的ip地址+端口号,其中172.20.20.1是 ip,而15605 是端口号 -4. 第四列:箭头 >, 表示数据流向 -5. 第五列:接收方的ip地址+端口号,其中 172.20.20.2 是 ip,而5920 是端口号 -6. 第六列:冒号 -7. 第七列:数据包内容,包括Flags 标识符,seq 号,ack 号,win 窗口,数据长度 length,其中 [P.] 表示 PUSH 标志位为 1,更多标识符见下面 +本节的重点是引号里的内容,看起来很复杂的样子。 +将它逐一分解,我们只要先理解了下面几种用法,就能明白 +- `tcp[n]`:表示 tcp 报文里 第 n 个字节 -### 2. Flags 标识符 +- `tcp[n:c]`:表示 tcp 报文里从第n个字节开始取 c 个字节,tcp[12:1] 表示从报文的第12个字节(因为有第0个字节,所以这里的12其实表示的是13)开始算起取一个字节,也就是 8 个bit。查看 [tcp 的报文首部结构](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure),可以得知这 8 个bit 其实就是下图中的红框圈起来的位置,而在这里我们只要前面 4个bit,也就是实际数据在整个报文首部中的偏移量。 -使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种: + ![](http://image.iswbm.com/20200629085659.png) + +- `&`:是[位运算](https://en.wikipedia.org/wiki/Bitwise_operation)里的 and 操作符,比如 `0011 & 0010 = 0010` +- `>>`:是位运算里的右移操作,比如 `0111 >> 2 = 0011` +- `0xf0`:是 10 进制的 240 的 16 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 4bit 全部是 1,后面4个bit全部是0. + +分解完后,再慢慢合并起来看 + +1、`tcp[12:1] && 0xf0` 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 `10110000`,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。 + +2、`tcp[12:1] & 0xf0) >> 2` :如果你不理解 tcp 报文首部里的数据偏移,请先点击这个前往我的上一篇文章,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 + +`tcp[12:1] & 0xf0) >> 2` 这个表达式实际是 `(tcp[12:1] & 0xf0) >> 4 ) << 2` 的简写形式。所以要搞懂 `tcp[12:1] & 0xf0) >> 2` 只要理解了`(tcp[12:1] & 0xf0) >> 4 ) << 2` 就行了 。 + +从上一步我们算出了 `tcp[12:1] & 0xf0` 的值其实是一个字节,也就是 8 个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 4个bit,也就是说 上面得到的值 10110000,前面 4 位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 右移4位即可,也就是 `tcp[12:1] & 0xf0) >> 4`,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 Data Offset 的单位是 4个字节,因为要将 1011 乘以 4才可以,除以4在位运算中相当于左移2位,也就是 `<<2`,与前面的 `>>4` 结合起来一起算的话,最终的运算可以简化为 `>>2` + +至此,我们终于得出了实际数据开始的位置是 `tcp[12:1] & 0xf0) >> 2` (单位是字节)。 + +找到了数据的起点后,可别忘了我们的目的是从数据中打到 HTTP 请求的方法,是 GET 呢 还是 POST ,或者是其他的? + +有了上面的经验,我们自然懂得使用 `tcp[((tcp[12:1] & 0xf0) >> 2):4]` 从数据开始的位置再取出四个字节,然后将结果与 `GET ` (注意 GET最后还有个空格)的 16进制写法(也就是 `0x47455420`)进行比对。 + +```shell +0x47 --> 71 --> G +0x45 --> 69 --> E +0x54 --> 84 --> T +0x20 --> 32 --> 空格 +``` + +![](http://image.iswbm.com/20200629130407.png) + +如果相等,则该表达式为True,tcpdump 认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。 + + + +## 8. 实战应用例子 + +### 8.1 提取 HTTP 的 User-Agent + +从 HTTP 请求头中提取 HTTP 用户代理: + +```bash +$ tcpdump -nn -A -s1500 -l | grep "User-Agent:"复制代码 +``` + +通过 `egrep` 可以同时提取用户代理和主机名(或其他头文件): + +```bash +$ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:'复制代码 +``` + +### 8.2 抓取 HTTP GET 和 POST 请求 + +抓取 HTTP GET 流量: + +```bash +$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' +``` + +也可以抓取 HTTP POST 请求流量: + +```bash +$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354' +``` + +注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST 请求会被分割为多个 TCP 数据包。 + +上述两个表达式中的十六进制将会与 GET 和 POST 请求的 `ASCII` 字符串匹配。例如,`tcp[((tcp[12:1] & 0xf0) >> 2):4]` 首先会[确定我们感兴趣的字节的位置](https://security.stackexchange.com/questions/121011/wireshark-tcp-filter-tcptcp121-0xf0-24)(在 TCP header 之后),然后选择我们希望匹配的 4 个字节。 -- `[S]` : SYN(开始连接) -- `[P]` : PSH(推送数据) -- `[F]` : FIN (结束连接) -- `[R]` : RST(重置连接) -- `[.]` : 没有 Flag,由于除了 SYN 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK -## 7. 实战应用例子 https://juejin.im/post/5e64571bf265da57104393a1#heading-14 diff --git a/source/c10/c10_04.rst b/source/c10/c10_04.rst new file mode 100644 index 0000000..e22ab08 --- /dev/null +++ b/source/c10/c10_04.rst @@ -0,0 +1,748 @@ +10.4 全网最全的 tcpdump 抓包指南 +================================ + +在讲解之前,有两点需要声明: + +1. 第二节到第三节里的 tcpdump + 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第四节的逻辑逻辑运算符进行组合搭配。 +2. 不同 Linux 发行版下、不同版本的 tcpdump 可能有小许差异, 本文是基于 + CentOS 7.2 的 4.5.1 版本的tcpdump + 进行学习的,若在你的环境中无法使用,请参考 ``man tcpdump`` + 进行针对性学习。 + +1. tcpdump 核心参数图解 +----------------------- + +初学者在没有掌握 tcpdump 时,会对这个命令的参数产生恐惧。 + +就比如下面这个命令,我们要通过 ``host`` 参数指定 host ip 进行过滤 + +.. code:: shell + + $ tcpdump host 192.168.10.100 + +``主程序`` + ``参数名``\ + ``参数值`` +这样的组合才是我们正常认知里面命令行该有的样子。 + +可 tcpdump 却不走寻常路,我们居然还可以在 host +前再加一个限定词,来缩小过滤的范围? + +.. code:: shell + + $ tcpdump src host 192.168.10.100 + +从字面上理解,确实很容易理解,但是这不符合编写命令行程序的正常逻辑,导致我们会有所疑虑: + +1. 除了 src ,dst,可还有其它可以用的限定词? + +2. src,host 应该如何理解它们,叫参数名?不合适,因为 src 明显不合适。 + +如果你在网上看到有关 tcpdump +的博客、教程,无一不是给你一个参数组合,告诉你这是实现了怎样的一个过滤器?这样的教学方式,很容易让你依赖别人的文章来使用 +tcpdump,而不能将 tcpdump +这样神器消化,达到灵活应用,灵活搭配过滤器的效果。 + +上面加了 src 本身就颠覆了我们的认知,你可知道在 src +之前还可以加更多的条件,比如 tcp, udp, icmp +等词,在你之前的基础上再过滤一层。 + +.. code:: shell + + $ tcpdump tcp src host 192.168.10.100 + +这种参数的不确定性,让大多数人对 tcpdump 的学习始终无法得其精髓。 + +因此,在学习 tcpdump 之前,我觉得有必要要先让你知道:\ **tcpdump +的参数是如何组成的?这非常重要。** + +为此,我画了一张图,方便你直观的理解 tcpdump 的各种参数: + +|image0| + +1. option + 可选参数:将在后边一一解释,对应本文\ `第三节:可选参数解析 <>`__ +2. proto 限定词:根据协议进行过滤,可识别的关键词有: upd, udp, icmp, + ip, ip6, arp, rarp,ether,wlan, fddi, tr, decnet +3. type 限定词:可识别的关键词有:host, net, port, + portrange,这些词后边需要再接参数。 +4. direction 限定词:根据数据流向进行过滤,可识别的关键字有:src, + dst,同时你可以使用逻辑运算符进行组合,比如 src or dst + +proto、type、direction +这三个限定词的内容比较简单,也最常用,因此我将其放在最前面,也就是 +`第二节:常规过滤规则 <>`__ 一起介绍。 + +而 option +可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 +`第三节:可选参数解析 <>`__ + +当你看完前面四节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% +的使用需求。 + +你一定会问了,还有 20% 呢? + +其实 tcpdump +还有一些过滤关键词,它不符合以上四种限定规则,可能需要你单独记忆。关于这部分我会在 +`第五节:特殊过滤规则 <>`__ 里进行介绍。 + +2. 理解 tcpdump 的输出 +---------------------- + +2.1 输出内容结构 +~~~~~~~~~~~~~~~~ + +tcpdump 输出的内容虽然多,却很规律。 + +这里以我随便抓取的一个 tcp 包为例来看一下 + +.. code:: shell + + 21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48 + +从上面的输出来看,可以总结出: + +1. 第一列:时分秒毫秒 21:26:49.013621 +2. 第二列:网络协议 IP +3. 第三列:发送方的ip地址+端口号,其中172.20.20.1是 ip,而15605 是端口号 +4. 第四列:箭头 >, 表示数据流向 +5. 第五列:接收方的ip地址+端口号,其中 172.20.20.2 是 ip,而5920 + 是端口号 +6. 第六列:冒号 +7. 第七列:数据包内容,包括Flags 标识符,seq 号,ack 号,win + 窗口,数据长度 length,其中 [P.] 表示 PUSH 标志位为 + 1,更多标识符见下面 + +2.2 Flags 标识符 +~~~~~~~~~~~~~~~~ + +使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种: + +- ``[S]`` : SYN(开始连接) +- ``[P]`` : PSH(推送数据) +- ``[F]`` : FIN (结束连接) +- ``[R]`` : RST(重置连接) +- ``[.]`` : 没有 Flag,由于除了 SYN + 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK + +3. 常规过滤规则 +--------------- + +3.1 基于IP地址过滤:host +~~~~~~~~~~~~~~~~~~~~~~~~ + +使用 ``host`` 就可以指定 host ip 进行过滤 + +.. code:: shell + + $ tcpdump host 192.168.10.100 + +数据包的 ip 可以再细分为源ip和目标ip两种 + +.. code:: shell + + # 根据源ip进行过滤 + $ tcpdump -i eth2 src 192.168.10.100 + + # 根据目标ip进行过滤 + $ tcpdump -i eth2 dst 192.168.10.200 + +3.2 基于网段进行过滤:net +~~~~~~~~~~~~~~~~~~~~~~~~~ + +若你的ip范围是一个网段,可以直接这样指定 + +.. code:: shell + + $ tcpdump net 192.168.10.0/24 + +网段同样可以再细分为源网段和目标网段 + +.. code:: shell + + # 根据源网段进行过滤 + $ tcpdump src net 192.168 + + # 根据目标网段进行过滤 + $ tcpdump dst net 192.168 + +3.3 基于端口进行过滤:port +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +使用 ``port`` 就可以指定特定端口进行过滤 + +.. code:: shell + + $ tcpdump port 8088 + +端口同样可以再细分为源端口,目标端口 + +.. code:: shell + + # 根据源端口进行过滤 + $ tcpdump src port 8088 + + # 根据目标端口进行过滤 + $ tcpdump dst port 8088 + +若你想过滤的是一个端口范围,一个一个指定就较为麻烦,此时你可以这样指定一个端口段。 + +.. code:: shell + + $ tcpdump portrange 8000-8080 + $ tcpdump src portrange 8000-8080 + $ tcpdump dst portrange 8000-8080 + +3.4 基于协议进行过滤:proto +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等 + +若你只想查看 icmp 的包,可以直接这样写 + +.. code:: shell + + $ tcpdump icmp + +protocol 可选值:ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, +mopdl, moprc, iso, stp, ipx, or netbeui + +3.5 基本IP协议的版本进行过滤 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +当你想查看 tcp 的包,你也许会这样子写 + +.. code:: shell + + $ tcpdump tcp + +这样子写也没问题,就是不够精准,为什么这么说呢? + +ip 根据版本的不同,可以再细分为 IPv4 和 IPv6 两种,如果你只指定了 +tcp,这两种其实都会包含在内。 + +那有什么办法,能够将 IPv4 和 IPv6 区分开来呢? + +很简单,如果是 IPv4 的 tcp 包 ,就这样写(友情提示:数字 6 表示的是 tcp +在ip报文中的编号。) + +.. code:: shell + + $ tcpdump 'ip proto tcp' + + # or + + $ tcpdump ip proto 6 + + # or + + $ tcpdump 'ip protochain tcp' + + # or + $ tcpdump ip protochain 6 + +而如果是 IPv6 的 tcp 包 ,就这样写 + +.. code:: shell + + $ tcpdump 'ip6 proto tcp' + + # or + + $ tcpdump ip6 proto 6 + + # or + + $ tcpdump 'ip6 protochain tcp' + + # or + $ tcpdump ip6 protochain 6 + +关于上面这几个命令示例,有两点需要注意: + +1. 跟在 proto 和 protochain 后面的如果是 tcp, udp, icmp + ,那么过滤器需要用引号包含,这是因为 tcp,udp, icmp 是 tcpdump + 的关键字。 +2. 跟在ip 和 ip6 关键字后面的 proto 和 protochain + 是两个新面孔,看起来用法类似,它们是否等价,又有什么区别呢? + +关于第二点,网络上没有找到很具体的答案,我只能通过 ``man tcpdump`` +的提示, 给出自己的个人猜测,但不保证正确。 + +proto 后面跟的 ```` 的关键词是固定的,只能是 ip, ip6, arp, +rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or +netbeui 这里面的其中一个。 + +而 protochain 后面跟的 protocol 要求就没有那么严格,它可以是任意词,只要 +tcpdump 的 IP 报文头部里的 protocol 字段为 ```` 就能匹配上。 + +理论上来讲,下面两种写法效果是一样的 + +.. code:: shell + + $ tcpdump 'ip && tcp' + $ tcpdump 'ip proto tcp' + +同样的,这两种写法也是一样的 + +.. code:: shell + + $ tcpdump 'ip6 && tcp' + $ tcpdump 'ip6 proto tcp' + +4. 可选参数解析 +--------------- + +4.1 设置不解析域名提升速度 +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``-n``\ :不把ip转化成域名,直接显示 ip,避免执行 DNS lookups + 的过程,速度会快很多 +- ``-nn``\ :不把协议和端口号转化成名字,速度也会快很多。 +- ``-N``\ :不打印出host 的域名部分.。比如,,如果设置了此选现,tcpdump + 将会打印’nic’ 而不是 ‘nic.ddn.mil’. + +4.2 过滤结果输出到文件 +~~~~~~~~~~~~~~~~~~~~~~ + +使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 +wireshark 。 + +而要使用wireshark ,我们得将 tcpdump +抓到的包数据生成到文件中,最后再使用 wireshark 打开它即可。 + +使用 ``-w`` 参数后接一个以 ``.pcap`` 后缀命令的文件名,就可以将 tcpdump +抓到的数据保存到文件中。 + +.. code:: shell + + $ tcpdump icmp -w icmp.pcap + +4.3 从文件中读取包数据 +~~~~~~~~~~~~~~~~~~~~~~ + +使用 ``-w`` 是写入数据到文件,而使用 ``-r`` 是从文件中读取数据。 + +读取后,我们照样可以使用上述的过滤器语法进行过滤分析。 + +.. code:: shell + + $ tcpdump icmp -r all.pcap + +4.4 控制详细内容的输出 +~~~~~~~~~~~~~~~~~~~~~~ + +- ``-v``\ :产生详细的输出. + 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。 +- ``-vv``\ :产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, + SMB数据包也会被完全解码。(摘自网络,目前我还未使用过) +- ``-vvv``\ :产生比-vv更详细的输出。比如 telent 时所使用的SB, SE + 选项将会被打印, + 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) + +4.5 控制时间的显示 +~~~~~~~~~~~~~~~~~~ + +- ``-t``\ :在每行的输出中不输出时间 +- ``-tt``\ :在每行的输出中会输出时间戳 +- ``-ttt``\ :输出每两行打印的时间间隔(以毫秒为单位) +- ``-tttt``\ :在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观) + +4.6 显示数据包的头部 +~~~~~~~~~~~~~~~~~~~~ + +- ``-x``\ :以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部) +- ``-xx``\ :以16进制的形式打印每个包的头部数据(包括数据链路层的头部) +- ``-X``\ :以16进制和 + ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。 +- ``-XX``\ :以16进制和 + ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。 + +4.7 过滤指定网卡的数据包 +~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``-i``\ :指定要过滤的网卡接口,如果要查看所有网卡,可以 ``-i any`` + +4.8 过滤特定流向的数据包 +~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``-Q``\ : 选择是入方向还是出方向的数据包,可选项有:in, out, + inout,也可以使用 –direction=[direction] 这种写法 + +4.9 其他常用的一些参数 +~~~~~~~~~~~~~~~~~~~~~~ + +- ``-A``\ :以ASCII码方式显示每一个数据包(不显示链路层头部信息). + 在抓取包含网页数据的数据包时, 可方便查看数据 + +- ``-l`` : 基于行的输出,便于你保存查看,或者交给其它工具分析 +- ``-q`` : 简洁地打印输出。即打印很少的协议相关信息, + 从而输出行都比较简短. +- ``-c`` : 捕获 count 个包 tcpdump 就退出 +- ``-s`` : tcpdump 默认只会截取前 ``96`` + 字节的内容,要想截取所有的报文内容,可以使用 ``-s number``\ , + ``number`` 就是你要截取的报文字节数,如果是 0 + 的话,表示截取报文全部内容。 +- ``-S`` : 使用绝对序列号,而不是相对序列号 +- ``-C``\ :file-size,tcpdump 在把原始数据包直接保存到文件中之前, + 检查此文件大小是否超过file-size. 如果超过了, + 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w + 选项指定的文件名一致, + 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加. + file-size的单位是百万字节(nt: + 这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, + 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576) +- ``-F``\ :使用file 文件作为过滤条件表达式的输入, + 此时命令行上的输入将被忽略. + +4.10 对输出内容进行控制的参数 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``-D`` : 显示所有可用网络接口的列表 +- ``-e`` : 每行的打印输出中将包括数据包的数据链路层头部信息 +- ``-E`` : 揭秘IPSEC数据 +- ``-L`` :列出指定网络接口所支持的数据链路层的类型后退出 +- ``-Z``\ :后接用户名,在抓包时会受到权限的限制。如果以root用户启动tcpdump,tcpdump将会有超级用户权限。 +- ``-d``\ :打印出易读的包匹配码 +- ``-dd``\ :以C语言的形式打印出包匹配码. +- ``-ddd``\ :以十进制数的形式打印出包匹配码 + +5. 过滤规则组合 +--------------- + +有编程基础的同学,对于下面三个逻辑运算符应该不陌生了吧 + +- and:所有的条件都需要满足,也可以表示为 ``&&`` +- or:只要有一个条件满足就可以,也可以表示为 ``||`` +- not:取反,也可以使用 ``!`` + +举个例子,我想需要抓一个来自\ ``10.5.2.3``\ ,发往任意主机的3389端口的包 + +.. code:: shell + + $ tcpdump src 10.5.2.3 and dst port 3389 + +当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell +中是特殊符号,因为你需要使用引号将其包含。例子如下: + +.. code:: shell + + $ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' + +而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用下面两个符号 + +- ``=``\ :判断二者相等 +- ``==``\ :判断二者相等 +- ``!=``\ :判断二者不相等 + +当你使用这两个符号时,tcpdump +还提供了一些关键字的接口来方便我们进行判断,比如 + +- if:表示网卡接口名、 +- proc:表示进程名 +- pid:表示进程 id +- svc:表示 service class +- dir:表示方向,in 和 out +- eproc:表示 effective process name +- epid:表示 effective process ID + +比如我现在要过滤来自进程名为 ``nc`` 发出的流经 en0 +网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写 + +.. code:: shell + + $ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" + +6. 特殊过滤规则 +--------------- + +5.1 根据 tcpflags 进行过滤 +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +通过上一篇文章,我们知道了 tcp 的首部有一个标志位。 + +.. figure:: http://image.iswbm.com/20200606095627.png + :alt: TCP 报文首部 + + TCP 报文首部 + +tcpdump 支持我们根据数据包的标志位进行过滤 + +:: + + proto [ expr:size ] + +“proto”可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6),“expr”表示与指定的协议头开头相关的字节偏移量。有我们熟知的直接偏移量如tcpflags,也有取值常量如tcp-fin, +tcp-syn, tcp-rst, tcp-push, tcp-ack, +tcp-urg。“size”是可选的,表示从字节偏移量开始检查的字节数量。 + +对于下面的例子,我先解释一下 + +1、tcpflags 可以理解为是一个常量,相当于 +13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位 + +|image1| + +2、tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg +这些同样可以理解为常量,分别代表 +1,2,4,8,16,32,64。这些数字是如何计算出来的呢? + +以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2 + +|image2| + +由于数字不好记忆,所以一般使用“常量”表示。 + +下面以最常见的 syn包为例,演示一下如何用抓取 syn +包,其他的类型的包你可以自行修改。 + +主要有三种写法: 1、第一种写法:使用数字表示偏移量 + +.. code:: shell + + $ tcpdump -i eth0 "tcp[13] & 2 != 0" + +2、第二种写法:使用常量表示偏移量 + +.. code:: shell + + $ tcpdump -i eth0 "tcp[tcpflags] & tcp-syn != 0" + +3、第三种写法:使用混合写法 + +.. code:: shell + + $ tcpdump -i eth0 "tcp[tcpflags] & 2 != 0" + + # or + + $ tcpdump -i eth0 "tcp[13] & tcp-syn != 0" + +如果我想同时捕获多种类型的包呢,比如 syn + ack 包 + +1、第一种写法 + +.. code:: shell + + $ tcpdump -i eth0 'tcp[13] == 2 or tcp[13] == 16' + +2、第二种写法 + +.. code:: shell + + $ tcpdump -i eth0 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack' + +3、第三种写法 + +.. code:: shell + + $ tcpdump -i eth0 "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0" + +4、第四种写法:注意这里是 +单个等号,而不是像上面一样两个等号,18(syn+ack) = 2(syn) + 16(ack) + +.. code:: shell + + $ tcpdump -i eth0 'tcp[13] = 18' + + # or + + $ tcpdump -i eth0 'tcp[tcpflags] = 18' + +tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp +协议,可以使用的常量有 + +.. code:: shell + + icmp-echoreply, icmp-unreach, icmp-sourcequench, + icmp-redirect, icmp-echo, icmp-routeradvert, + icmp-routersolicit, icmp-timx-ceed, icmp-paramprob, + icmp-tstamp, icmp-tstampreply,icmp-ireq, + icmp-ireqreply, icmp-maskreq, icmp-maskreply + +5.2 基于包大小进行过滤 +~~~~~~~~~~~~~~~~~~~~~~ + +若你想查看指定大小的数据包,也是可以的 + +.. code:: shell + + $ tcpdump less 32 + $ tcpdump greater 64 + $ tcpdump <= 128 + +5.3 根据 mac 地址进行过滤 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +例子如下,其中 ehost 是记录在 /etc/ethers 里的 name + +.. code:: shell + + $ tcpdump ether host [ehost] + $ tcpdump ether dst [ehost] + $ tcpdump ether src [ehost] + +5.4 过滤通过指定网关的数据包 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: shell + + $ tcpdump gateway [host] + +5.5 过滤广播/多播数据包 +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: shell + + $ tcpdump ether broadcast + $ tcpdump ether multicast + + $ tcpdump ip broadcast + $ tcpdump ip multicast + + $ tcpdump ip6 multicast + +7. 如何抓取到更精准的包? +------------------------- + +先给你抛出一个问题:如果我只想抓取 HTTP 的 POST 请求该如何写呢? + +如果只学习了上面的内容,恐怕你还是无法写法满足这个抓取需求的过滤器。 + +在学习之前,我先给出答案,然后再剖析一下,这个过滤器是如何生效的,居然能让我们对包内的内容进行判断。 + +.. code:: shell + + $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' + +命令里的可选参数,在前面的内容里已经详细讲过了。这里不再细讲。 + +本节的重点是引号里的内容,看起来很复杂的样子。 + +将它逐一分解,我们只要先理解了下面几种用法,就能明白 + +- ``tcp[n]``\ :表示 tcp 报文里 第 n 个字节 + +- ``tcp[n:c]``\ :表示 tcp 报文里从第n个字节开始取 c 个字节,tcp[12:1] + 表示从报文的第12个字节(因为有第0个字节,所以这里的12其实表示的是13)开始算起取一个字节,也就是 + 8 个bit。查看 `tcp + 的报文首部结构 `__\ ,可以得知这 + 8 个bit 其实就是下图中的红框圈起来的位置,而在这里我们只要前面 + 4个bit,也就是实际数据在整个报文首部中的偏移量。 + + |image3| + +- ``&``\ :是\ `位运算 `__\ 里的 + and 操作符,比如 ``0011 & 0010 = 0010`` +- ``>>``\ :是位运算里的右移操作,比如 ``0111 >> 2 = 0011`` +- ``0xf0``\ :是 10 进制的 240 的 16 + 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 + 4bit 全部是 1,后面4个bit全部是0. + +分解完后,再慢慢合并起来看 + +1、\ ``tcp[12:1] && 0xf0`` +其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 +个字节是这样组成的 ``10110000``\ ,那么这个表达式就可以变成 10110110 && +11110000 = 10110000,得到了 10110000 后,再进入下一步。 + +2、\ ``tcp[12:1] & 0xf0) >> 2`` :如果你不理解 tcp +报文首部里的数据偏移,请先点击这个前往我的上一篇文章,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 + +``tcp[12:1] & 0xf0) >> 2`` 这个表达式实际是 +``(tcp[12:1] & 0xf0) >> 4 ) << 2`` 的简写形式。所以要搞懂 +``tcp[12:1] & 0xf0) >> 2`` +只要理解了\ ``(tcp[12:1] & 0xf0) >> 4 ) << 2`` 就行了 。 + +从上一步我们算出了 ``tcp[12:1] & 0xf0`` 的值其实是一个字节,也就是 8 +个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 +4个bit,也就是说 上面得到的值 10110000,前面 4 +位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 +右移4位即可,也就是 +``tcp[12:1] & 0xf0) >> 4``\ ,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 +Data Offset 的单位是 4个字节,因为要将 1011 乘以 +4才可以,除以4在位运算中相当于左移2位,也就是 ``<<2``\ ,与前面的 +``>>4`` 结合起来一起算的话,最终的运算可以简化为 ``>>2`` + +至此,我们终于得出了实际数据开始的位置是 ``tcp[12:1] & 0xf0) >> 2`` +(单位是字节)。 + +找到了数据的起点后,可别忘了我们的目的是从数据中打到 HTTP 请求的方法,是 +GET 呢 还是 POST ,或者是其他的? + +有了上面的经验,我们自然懂得使用 ``tcp[((tcp[12:1] & 0xf0) >> 2):4]`` +从数据开始的位置再取出四个字节,然后将结果与 ``GET`` (注意 +GET最后还有个空格)的 16进制写法(也就是 ``0x47455420``\ )进行比对。 + +.. code:: shell + + 0x47 --> 71 --> G + 0x45 --> 69 --> E + 0x54 --> 84 --> T + 0x20 --> 32 --> 空格 + +|image4| + +如果相等,则该表达式为True,tcpdump +认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。 + +8. 实战应用例子 +--------------- + +8.1 提取 HTTP 的 User-Agent +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +从 HTTP 请求头中提取 HTTP 用户代理: + +.. code:: bash + + $ tcpdump -nn -A -s1500 -l | grep "User-Agent:"复制代码 + +通过 ``egrep`` 可以同时提取用户代理和主机名(或其他头文件): + +.. code:: bash + + $ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:'复制代码 + +8.2 抓取 HTTP GET 和 POST 请求 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +抓取 HTTP GET 流量: + +.. code:: bash + + $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' + +也可以抓取 HTTP POST 请求流量: + +.. code:: bash + + $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354' + +注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST +请求会被分割为多个 TCP 数据包。 + +上述两个表达式中的十六进制将会与 GET 和 POST 请求的 ``ASCII`` +字符串匹配。例如,\ ``tcp[((tcp[12:1] & 0xf0) >> 2):4]`` +首先会\ `确定我们感兴趣的字节的位置 `__\ (在 +TCP header 之后),然后选择我们希望匹配的 4 个字节。 + +https://juejin.im/post/5e64571bf265da57104393a1#heading-14 + +https://danielmiessler.com/study/tcpdump/ + +https://colobu.com/2019/07/16/a-tcpdump-tutorial-with-examples/ + +参考文章 + +1. https://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+7.2-RELEASE&format=html +2. https://wizardforcel.gitbooks.io/network-basic/17.html +3. https://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html +4. `一份快速实用的 tcpdump + 命令参考手册 `__ + +.. |image0| image:: http://image.iswbm.com/20200628111325.png +.. |image1| image:: http://image.iswbm.com/20200628222034.png +.. |image2| image:: http://image.iswbm.com/20200628222010.png +.. |image3| image:: http://image.iswbm.com/20200629085659.png +.. |image4| image:: http://image.iswbm.com/20200629130407.png + From 625ace68db87711a0bed0cc48aaaf6e281e35799 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 29 Jun 2020 13:06:08 +0800 Subject: [PATCH 091/147] =?UTF-8?q?Update=EF=BC=9A=E7=94=9F=E6=88=90=20rst?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c04/c04_06.rst | 54 ++++++++++++++++++++++++++++--------------- source/c09/c09_02.rst | 2 ++ source/c10/c10_06.rst | 2 ++ 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index f1873b9..5f68c13 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -58,39 +58,55 @@ add/commit 前撤销对文件的修改 1.4 远程仓库 ~~~~~~~~~~~~ -添加远程仓库 +origin 并不是指得是远程的仓库,而是指得是远程仓库在本地的一个指针。 + +为什么是这个名字呢?仅仅是因为 git +用它做为默认名被广泛使用,一个习惯而已,你也可以叫其他名字。 + +对一个在本地创建的仓库,如果添加远程仓库? .. code:: shell $ git remote add [shortname] [url] # shortname是你命名的远程仓库名字,url格式如下:git@github.com:github_account/repository_name.git - $ git remote add learn_git git@github.com:wongbingming/my_repository.git #注意记得先在GitHub上先新建仓库,my_repository,不然后面推送会出错 + #注意记得先在GitHub上先新建仓库,my_repository,不然后面 push 会出错 + $ git remote add learn_git git@github.com:wongbingming/my_repository.git 推送/克隆 .. code:: shell $ git push -u learn_git master #learn_git是之前命名的远程仓库名,master是你要推送到的远程仓库的分支名字 + $ git push -u learn-git master:master $ git clone [github url] # 举例 - $ git clone git@github.com:BingmingWong/test.git + $ git clone git@github.com:iswbm/test.git + +更改远程仓库地址 + +.. code:: shell + + $ git remote set-url origin git@:you_repo.git + +upstream 和 downstream + +Git中的upstream和downstream的概念是相对的。 - # 更改远程仓库地址 - git remote set-url origin git@:you_repo.git +如果A库中的分支x被push到B库中的分支y,则y就是x的upstream,而x就是y的downstream。 1.5 GitHub合并至本地 ~~~~~~~~~~~~~~~~~~~~ -:: +.. code:: shell $ git merge origin/master 1.6 创建SSHkeys ~~~~~~~~~~~~~~~ -:: +.. code:: shell $ ssh-keygen -t rsa -C "youremail@example.com" @@ -103,7 +119,7 @@ add/commit 前撤销对文件的修改 建完成后,使用如下命令查找位置 -:: +.. code:: shell $ find / -name id_rsa.pub @@ -113,7 +129,7 @@ add/commit 前撤销对文件的修改 2.1 文件状态 ~~~~~~~~~~~~ -:: +.. code:: shell $ git status #如果修改了文件,状态会提示你有文件被修改(但不能告诉你哪里被修改),提示你要commit $ cat readme.txt #查看实体文件的内容 @@ -121,7 +137,7 @@ add/commit 前撤销对文件的修改 查看当前目录下有哪些远程仓库 -:: +.. code:: shell $ git remote # 远程仓库名字 $ git remote -v # verbose的缩写,显示详细信息,包括项目url @@ -129,13 +145,13 @@ add/commit 前撤销对文件的修改 查看提交的历史 -:: +.. code:: shell $ git log --pretty=oneline --abbrev-commit 查看不同的地方(修改) -:: +.. code:: shell $ git diff # 工作区(work dict)和暂存区(stage)的比较 $ git diff --cached # 是暂存区(stage)和分支(master)的比较 @@ -167,13 +183,15 @@ add/commit 前撤销对文件的修改 2.2 Git日志 ~~~~~~~~~~~ -:: +.. code:: shell $ git log # 可以查看who在when修改了文件(会写出版本说明),但是这个看着眼花缭乱 $ git log --pretty=online # 这样,每行只显示一次修改,修改信息只有:commitid + 版本说明 $ git reflog # 显示所有修改的日志 + $ git log --graph --all --decorate # 以可视化图的形式展示 + 2.3 查看修改 ~~~~~~~~~~~~ @@ -183,7 +201,7 @@ add/commit 前撤销对文件的修改 查看两个 commit 之间的修改 -:: +.. code:: shell $ git diff commit_id1 commit_id2 $ git diff commit_id^! @@ -193,14 +211,14 @@ add/commit 前撤销对文件的修改 查看某个 commit 的改动 -:: +.. code:: shell - git show commit_id - git show --stat commit_id + $ git show commit_id + $ git show --stat commit_id 查看两个分支之间的差异 -:: +.. code:: shell # 查看两个分支有哪些文件发生改变了 git diff stable/2.2.9 stable/2.3.0 --stat diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index aaf1d37..a451c79 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -86,6 +86,8 @@ Buzz:阻止电脑休眠,三种模式:永不休眠、屏幕熄灭但电脑 ShortCat:在系统栏也可以搜索聚焦 +TouchBarServer:在外接屏幕上调出 touchbar + 4. 图片影音 ----------- diff --git a/source/c10/c10_06.rst b/source/c10/c10_06.rst index 45de435..3c1507f 100644 --- a/source/c10/c10_06.rst +++ b/source/c10/c10_06.rst @@ -38,5 +38,7 @@ UDP 首部固定只有 8 个字节,开销较小。 `面向报文(UDP)和面向字节流(TCP)的区别 `__ +`TCP缓存区与窗口的关系 `__ + .. |image0| image:: http://image.iswbm.com/20200602135014.png From 20dce116751ac4ac71ddbc96979af4c8f1548713 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 29 Jun 2020 23:42:14 +0800 Subject: [PATCH 092/147] =?UTF-8?q?Update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_12.md | 401 ++++++++++++++++++++++++++++++++++++++----- source/c10/c10_07.md | 14 ++ 2 files changed, 372 insertions(+), 43 deletions(-) create mode 100644 source/c10/c10_07.md diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md index e6b31cc..bfe8d5e 100644 --- a/source/c07/c07_12.md +++ b/source/c07/c07_12.md @@ -4,6 +4,30 @@ ## yum +### 安装 + +加个 `-y` 可以在安装包时代替用户自动响应 yes + +```shell +$ yum install -y xxx +``` + +加个 `--installroot=/usr/local/` 指定将软件安装在 /usr/local 目录下,而不安装在默认路径下 + +```shell +$ yum install --installroot=/usr/local/ -y xxx +``` + +重新安装 == 卸载再安装 + +```shell +$ yum reinstall -y xxx +``` + + + +### 下载 + 只下载安装包 ```shell @@ -17,72 +41,299 @@ yumdownloader --resolve --destdir /home/wangbm/ perl-Sys-Virt.x86_64 yumdownloader --resolve --destdir /home/test/ systemd-devel-219-62.el7_6.2.x86_64 ``` -查看一个包的安装时间 +### 查看 + +查看具体包的信息 +```shell +$ yum info zabbix-agent ``` -[root@ws_compute01 ~]# rpm -qi|grep zlib-1.2.7-17.el7.i686 -rpm: no arguments given for query -[root@ws_compute01 ~]# rpm -qi zlib-1.2.7-17.el7.i686 -Name : zlib -Version : 1.2.7 -Release : 17.el7 -Architecture: i686 -Install Date: Tue 13 Aug 2019 10:22:41 AM CST -Group : System Environment/Libraries -Size : 184798 -License : zlib and Boost -Signature : RSA/SHA256, Mon 21 Nov 2016 05:05:03 AM CST, Key ID 24c6a8a7f4a80eb5 -Source RPM : zlib-1.2.7-17.el7.src.rpm -Build Date : Sun 06 Nov 2016 02:07:22 AM CST -Build Host : worker1.bsys.centos.org -Relocations : (not relocatable) -Packager : CentOS BuildSystem -Vendor : CentOS -URL : http://www.zlib.net/ -Summary : The compression and decompression library -Description : -Zlib is a general-purpose, patent-free, lossless data compression -library which is used by many different programs. + +查看配置并启用的仓库中有哪些包,包括已安装和未安装的 + +```shell +$ yum list ``` -查看一个包在系统已配的源里都有哪些版本 +查看有哪些包可以升级的 +```shell +$ yum list updates ``` -[root@ws_compute01 ~]# yum provides -Loaded plugins: fastestmirror, versionlock -Loading mirror speeds from cached hostfile - * epel: hkg.mirror.rackspace.com -1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines -Repo : ansible-master +查询一个文件在什么包里 -1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines -Repo : libvirt-3.9.0 +```shell +$ yum provides xxx +``` +查询一个命令是哪个包提供的 -1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines -Repo : wsbase +```shell +$ yum whatprovides xxx +``` +列出所有的容器 -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : ansible-master +```shell +$ yum repolist all +``` +查询某个仓库下的所有包 -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : libvirt-4.5.0 +```shell +$ yum repo-pkgs list +``` +查看一个包的所有依赖项 -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : wsupdates +```shell +$ yum deplist httpd +``` -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : @libvirt-4.5.0 +### 搜索 + +在配置并启用的仓库中搜索包 + +```shell +$ yum search zabbix-agent +``` + + + +### 升级 + +```shell +$ yum update xxx +``` + + + +### 删除 + +```shell +$ yum remove -y xxx +``` + + + +### 清理 + +清理已下载的软件文件 + +```shell +$ yum clean packages +``` + +清理已下载的软件文件头 + +```shell +$ yum clean headers +``` + +清理下载过的容器相关数据 +```shell +$ yum clean all +``` + + + +### 分组 + +查看分组 + +```shell +$ yum grouplist +``` + +查看分组信息 + +```shell +$ yum groupinfo +``` + +安装一整组软件 + +```shell +$ yum groupinstall +``` + +删除某个组 + +```shell +$ yum groupremove +``` + +### 历史 + +```shell +$ yum history +``` + +### 语言 + +列出已安装的语言 + +```shell +$ yum langlist +``` + +为语言安装适当的语言包 + +```shell +$ yum langinstall +``` + +删除语言的已安装语言包 + +```shell +$ yum langremove +``` + + + +### 其他 + +执行事务 + +```shell +$ yum load-transaction /tmp/yum_save_tx.xxx.n0EVjx.yumtx +``` + +生成元数据缓存 + +```shell +$ yum makecache +``` + +进入交互式模式 + +```shell +$ yum shell +``` + + + +### 选项 + +```shell +-h, - help显示此帮助消息并退出 +-t, - 容忍错误 +-C,--cacheonly完全从系统缓存运行,不更新缓存 + +-q, - 安静的操作 +-v, - verbose详细操作 +-y, - assumeyes对所有问题都回答是 + +--assumeno对所有问题都回答否 +--version显示Yum版本并退出 +--installroot = [path]设置安装root + +--obsoletes在更新期间启用过时处理 +--noplugins禁用Yum插件 +--nogpgcheck禁用gpg签名检查 + + +-skip-broken 跳过没有解决问题的包 +--color = COLOR控制是否使用颜色 + +--downloadonly不更新,只需下载 +--downloaddir = DLDIR指定存储包的备用目录 +--setopt = SETOPTS设置任意配置和repo选项 +--bugfix在更新中包含bugfix相关包 +--security在更新中包含安全相关的包 +--advisory = ADVS, - advisories = ADVS 更新包括修复给定建议所需的包 +--bzs = BZS 更新包括修复给定BZ所需的包 +--cves = CVES 更新包括修复给定CVE所需的包 +--sec-severity = SEVS, - secseverity = SEVS +``` + +配置文件位置 + +```shell +-c [config file], - config = [config file] +``` + +最大命令等待时间 + +```shell +-R [minute], - randomwait = [minute] +``` + +调试输出级别 + +```shell +-d [debug level], - debug level = [debug level] +``` + +在repos中,在list / search命令中显示重复项 + +```shell +--showduplicates +``` + +错误输出级别 + +```shell +-e [error level], - errorlevel = [error level] +``` + + +调试rpm的输出级别 + +```shell +--rpmverbosity = [debug level name] +``` + +启用/禁用仓库(允许使用通配符) + +```shell +--enablerepo = [repo] +--disablerepo = [repo] +``` + +按名称或glob排除包 + +```shell +-x [package], - exclude = [package] +``` + +禁用从main,repo或所有内容的排除 + +```shell +--disableexcludes = [plugin] +``` + +禁用包含repo或所有内容的includepkgs + +```shell +--disableincludes = [plugin] +``` + +按名称禁用插件 + +```shell +--disableplugin = [plugin] +``` + +按名称启用插件 + +```shell +--enableplugin = [plugin] +``` + +在yum config和repo文件中设置$ releasever的值 + +```shell +--releasever = RELEASEVER ``` + + yum-utils 使用 ```shell @@ -217,6 +468,70 @@ rm -f /var/lib/rpm/__db.00* rpm –rebuilddb ``` +查看一个包的安装时间 + +``` +[root@ws_compute01 ~]# rpm -qi|grep zlib-1.2.7-17.el7.i686 +rpm: no arguments given for query +[root@ws_compute01 ~]# rpm -qi zlib-1.2.7-17.el7.i686 +Name : zlib +Version : 1.2.7 +Release : 17.el7 +Architecture: i686 +Install Date: Tue 13 Aug 2019 10:22:41 AM CST +Group : System Environment/Libraries +Size : 184798 +License : zlib and Boost +Signature : RSA/SHA256, Mon 21 Nov 2016 05:05:03 AM CST, Key ID 24c6a8a7f4a80eb5 +Source RPM : zlib-1.2.7-17.el7.src.rpm +Build Date : Sun 06 Nov 2016 02:07:22 AM CST +Build Host : worker1.bsys.centos.org +Relocations : (not relocatable) +Packager : CentOS BuildSystem +Vendor : CentOS +URL : http://www.zlib.net/ +Summary : The compression and decompression library +Description : +Zlib is a general-purpose, patent-free, lossless data compression +library which is used by many different programs. +``` + +查看一个包在系统已配的源里都有哪些版本 + +``` +[root@ws_compute01 ~]# yum provides +Loaded plugins: fastestmirror, versionlock +Loading mirror speeds from cached hostfile + * epel: hkg.mirror.rackspace.com +1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines +Repo : ansible-master + + +1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines +Repo : libvirt-3.9.0 + + +1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines +Repo : wsbase + + +1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines +Repo : ansible-master + + +1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines +Repo : libvirt-4.5.0 + + +1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines +Repo : wsupdates + + +1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines +Repo : @libvirt-4.5.0 + +``` + diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md new file mode 100644 index 0000000..4bcce3b --- /dev/null +++ b/source/c10/c10_07.md @@ -0,0 +1,14 @@ +# 10.7 网络知识扫盲:一篇文章理解 HTTP + +![](http://image.iswbm.com/20200602135014.png) + + + +[Wireshark: 分析 TCP 四次挥手](http://www.veryitman.com/2018/12/16/Wireshark-分析-TCP-四次挥手/) + +https://zhuanlan.zhihu.com/p/53374516 + + + +[vlan学习总结](https://www.jianshu.com/p/54d5189ddb88) + From cd52ad07b78df01079cdfce0378c3cdef1900ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 30 Jun 2020 09:29:30 +0800 Subject: [PATCH 093/147] =?UTF-8?q?Update=EF=BC=9A=E5=85=A8=E7=BD=91?= =?UTF-8?q?=E6=9C=80=E5=85=A8=E7=9A=84=20tcpdump=20=E6=8A=93=E5=8C=85?= =?UTF-8?q?=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_04.md | 224 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 180 insertions(+), 44 deletions(-) diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index afe6022..06b33ab 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -1,13 +1,27 @@ -# 10.4 全网最全的 tcpdump 抓包指南 +# 10.4 全网最全(中文) tcpdump 抓包指南 + +今天要给大家介绍的一个 Unix 下的一个 **网络数据采集分析工具**,也就是我们常说的抓包工具。 + +与它功能类似的工具有 wireshark ,不同的是,wireshark 有图形化界面,而 tcpdump 则只有命令行。 + +由于我本人更习惯使用命令行的方式进行抓包,因此今天先跳过 wireshark,直接给大家介绍这个 tcpdump 神器。 + +这篇文章,我肝了好几天,借助于Linux 的 man 帮助命令,我把 tcpdump 的用法全部研究了个遍,才形成了本文,不夸张的说,应该可以算是中文里把 tcpdump 讲得最清楚明白,并且还最全的文章了(至少我从百度、谷歌的情况来看),所以本文值得你收藏分享,就怕你错过了,就再也找不到像这样把 tcpdump 讲得直白而且特全的文章了。 在讲解之前,有两点需要声明: -1. 第二节到第三节里的 tcpdump 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第四节的逻辑逻辑运算符进行组合搭配。 +1. 第三节到第六节里的 tcpdump 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第五节的逻辑逻辑运算符进行组合搭配。 2. 不同 Linux 发行版下、不同版本的 tcpdump 可能有小许差异, 本文是基于 CentOS 7.2 的 4.5.1 版本的tcpdump 进行学习的,若在你的环境中无法使用,请参考 `man tcpdump` 进行针对性学习。 ## 1. tcpdump 核心参数图解 -初学者在没有掌握 tcpdump 时,会对这个命令的参数产生恐惧。 +大家都知道,网络上的流量、数据包,非常的多,因此要想抓到我们所需要的数据包,就需要我们定义一个精准的过滤器,把这些目标数据包,从巨大的数据包网络中抓取出来。 + +所以学习抓包工具,其实就是学习如何定义过滤器的过程。 + +而在 tcpdump 的世界里,过滤器的实现,都是通过一个又一个的参数组合起来,一个参数不够精准,那就再加一个,直到我们能过滤掉无用的数据包,只留下我们感兴趣的数据包。 + +tcpdump 的参数非常的多,初学者在没有掌握 tcpdump 时,会对这个命令的众多参数产生很多的疑惑。 就比如下面这个命令,我们要通过 `host` 参数指定 host ip 进行过滤 @@ -48,20 +62,20 @@ $ tcpdump tcp src host 192.168.10.100 ![](http://image.iswbm.com/20200628111325.png) -1. option 可选参数:将在后边一一解释,对应本文[第三节:可选参数解析]() -2. proto 限定词:根据协议进行过滤,可识别的关键词有: upd, udp, icmp, ip, ip6, arp, rarp,ether,wlan, fddi, tr, decnet -3. type 限定词:可识别的关键词有:host, net, port, portrange,这些词后边需要再接参数。 -4. direction 限定词:根据数据流向进行过滤,可识别的关键字有:src, dst,同时你可以使用逻辑运算符进行组合,比如 src or dst +1. option 可选参数:将在后边一一解释。 +2. proto 类过滤器:根据协议进行过滤,可识别的关键词有: tcp, udp, icmp, ip, ip6, arp, rarp,ether,wlan, fddi, tr, decnet +3. type 类过滤器:可识别的关键词有:host, net, port, portrange,这些词后边需要再接参数。 +4. direction 类过滤器:根据数据流向进行过滤,可识别的关键字有:src, dst,同时你可以使用逻辑运算符进行组合,比如 src or dst -proto、type、direction 这三个限定词的内容比较简单,也最常用,因此我将其放在最前面,也就是 [第二节:常规过滤规则]() 一起介绍。 +proto、type、direction 这三类过滤器的内容比较简单,也最常用,因此我将其放在最前面,也就是 **第三节:常规过滤规则**一起介绍。 -而 option 可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 [第三节:可选参数解析]() +而 option 可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 **第四节:可选参数解析** -当你看完前面四节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% 的使用需求。 +当你看完前面六节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% 的使用需求。 你一定会问了,还有 20% 呢? -其实 tcpdump 还有一些过滤关键词,它不符合以上四种限定规则,可能需要你单独记忆。关于这部分我会在 [第五节:特殊过滤规则]() 里进行介绍。 +其实 tcpdump 还有一些过滤关键词,它不符合以上四种过滤规则,可能需要你单独记忆。关于这部分我会在 **第六节:特殊过滤规则** 里进行介绍。 ## 2. 理解 tcpdump 的输出 @@ -95,9 +109,7 @@ tcpdump 输出的内容虽然多,却很规律。 - `[P]` : PSH(推送数据) - `[F]` : FIN (结束连接) - `[R]` : RST(重置连接) -- `[.]` : 没有 Flag,由于除了 SYN 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK - -## 3. 常规过滤规则 +- `[.]` : 没有 Flag (意思是除上面四种类型外的其他情况,有可能是 ACK 也有可能是 URG) ### 3.1 基于IP地址过滤:host @@ -155,7 +167,19 @@ $ tcpdump src port 8088 $ tcpdump dst port 8088 ``` -若你想过滤的是一个端口范围,一个一个指定就较为麻烦,此时你可以这样指定一个端口段。 +如果你想要同时指定两个端口你可以这样写 + +```shell +$ tcpdump port 80 or port 8088 +``` + +但也可以简写成这样 + +```shell +$ tcpdump port 80 or 8088 +``` + +如果你的想抓取的不再是一两个端口,而是一个范围,一个一个指定就非常麻烦了,此时你可以这样指定一个端口段。 ```shell $ tcpdump portrange 8000-8080 @@ -165,6 +189,16 @@ $ tcpdump dst portrange 8000-8080 +对于一些常见协议的默认端口,我们还可以直接使用协议名,而不用具体的端口号 + +比如 http == 80,https == 443 等 + +```shell +$ tcpdump tcp port http +``` + + + ### 3.4 基于协议进行过滤:proto 常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等 @@ -209,6 +243,7 @@ $ tcpdump ip proto 6 $ tcpdump 'ip protochain tcp' # or + $ tcpdump ip protochain 6 ``` @@ -226,6 +261,7 @@ $ tcpdump ip6 proto 6 $ tcpdump 'ip6 protochain tcp' # or + $ tcpdump ip6 protochain 6 ``` @@ -385,7 +421,7 @@ $ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' $ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" ``` -## + ## 6. 特殊过滤规则 @@ -401,34 +437,46 @@ tcpdump 支持我们根据数据包的标志位进行过滤 proto [ expr:size ] ``` -“proto”可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6),“expr”表示与指定的协议头开头相关的字节偏移量。有我们熟知的直接偏移量如tcpflags,也有取值常量如tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg。“size”是可选的,表示从字节偏移量开始检查的字节数量。 +- `proto`:可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6) + +- `expr`:可以是数值,也可以是一个表达式,表示与指定的协议头开始处的字节偏移量。 +- `size`:是可选的,表示从字节偏移量开始取的字节数量。 -对于下面的例子,我先解释一下 +接下来,我将举几个例子,让人明白它的写法,不过在那之前,有几个点需要你明白,这在后面的例子中会用到: -1、tcpflags 可以理解为是一个常量,相当于 13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位 +**1、**tcpflags 可以理解为是一个别名常量,相当于 13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位,所以 tcp[tcpflags] 等价于 tcp[13] ,对应下图中的报文位置。 ![](http://image.iswbm.com/20200628222034.png) -2、tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg 这些同样可以理解为常量,分别代表 1,2,4,8,16,32,64。这些数字是如何计算出来的呢? +**2、**tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg 这些同样可以理解为别名常量,分别代表 1,2,4,8,16,32,64。这些数字是如何计算出来的呢? 以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2 ![](http://image.iswbm.com/20200628222010.png) -由于数字不好记忆,所以一般使用“常量”表示。 +由于数字不好记忆,所以一般使用这样的“别名常量”表示。 + +因此当下面这个表达式成立时,就代表这个包是一个 syn 包。 + +```shell +tcp[tcpflags] == tcp-syn +``` + + +要抓取特定数据包,方法有很多种。 +下面以最常见的 syn包为例,演示一下如何用 tcpdump 抓取到 syn 包,而其他的类型的包也是同样的道理。 -下面以最常见的 syn包为例,演示一下如何用抓取 syn 包,其他的类型的包你可以自行修改。 +据我总结,主要有三种写法: -主要有三种写法: 1、第一种写法:使用数字表示偏移量 ```shell $ tcpdump -i eth0 "tcp[13] & 2 != 0" ``` -2、第二种写法:使用常量表示偏移量 +2、第二种写法:使用别名常量表示偏移量 ```shell $ tcpdump -i eth0 "tcp[tcpflags] & tcp-syn != 0" @@ -478,7 +526,7 @@ $ tcpdump -i eth0 'tcp[tcpflags] = 18' -tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp 协议,可以使用的常量有 +tcp 中有 类似 tcp-syn 的别名常量,其他协议也是有的,比如 icmp 协议,可以使用的别名常量有 ```shell icmp-echoreply, icmp-unreach, icmp-sourcequench, @@ -512,12 +560,16 @@ $ tcpdump ether dst [ehost] $ tcpdump ether src [ehost] ``` + + ### 5.4 过滤通过指定网关的数据包 ```shell $ tcpdump gateway [host] ``` + + ### 5.5 过滤广播/多播数据包 ```shell @@ -557,18 +609,18 @@ $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' ![](http://image.iswbm.com/20200629085659.png) - `&`:是[位运算](https://en.wikipedia.org/wiki/Bitwise_operation)里的 and 操作符,比如 `0011 & 0010 = 0010` -- `>>`:是位运算里的右移操作,比如 `0111 >> 2 = 0011` -- `0xf0`:是 10 进制的 240 的 16 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 4bit 全部是 1,后面4个bit全部是0. +- `>>`:是位运算里的右移操作,比如 `0111 >> 2 = 0001` +- `0xf0`:是 10 进制的 240 的 16 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 4bit 全部是 1,后面4个bit全部是0,往后看你就知道这个特点有什么用了。 分解完后,再慢慢合并起来看 -1、`tcp[12:1] && 0xf0` 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 `10110000`,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。 +1、`tcp[12:1] & 0xf0` 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 `10110000`,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。 2、`tcp[12:1] & 0xf0) >> 2` :如果你不理解 tcp 报文首部里的数据偏移,请先点击这个前往我的上一篇文章,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 `tcp[12:1] & 0xf0) >> 2` 这个表达式实际是 `(tcp[12:1] & 0xf0) >> 4 ) << 2` 的简写形式。所以要搞懂 `tcp[12:1] & 0xf0) >> 2` 只要理解了`(tcp[12:1] & 0xf0) >> 4 ) << 2` 就行了 。 -从上一步我们算出了 `tcp[12:1] & 0xf0` 的值其实是一个字节,也就是 8 个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 4个bit,也就是说 上面得到的值 10110000,前面 4 位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 右移4位即可,也就是 `tcp[12:1] & 0xf0) >> 4`,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 Data Offset 的单位是 4个字节,因为要将 1011 乘以 4才可以,除以4在位运算中相当于左移2位,也就是 `<<2`,与前面的 `>>4` 结合起来一起算的话,最终的运算可以简化为 `>>2` +从上一步我们算出了 `tcp[12:1] & 0xf0` 的值其实是一个字节,也就是 8 个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 4个bit,也就是说 上面得到的值 10110000,前面 4 位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 右移4位即可,也就是 `tcp[12:1] & 0xf0) >> 4`,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 Data Offset 的单位是 4个字节,因为要将 1011 乘以 4才可以,除以4在位运算中相当于左移2位,也就是 `<<2`,与前面的 `>>4` 结合起来一起算的话,最终的运算可以简化为 `>>2`。 至此,我们终于得出了实际数据开始的位置是 `tcp[12:1] & 0xf0) >> 2` (单位是字节)。 @@ -593,51 +645,135 @@ $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' ### 8.1 提取 HTTP 的 User-Agent -从 HTTP 请求头中提取 HTTP 用户代理: +从 HTTP 请求头中提取 HTTP 的 User-Agent: ```bash -$ tcpdump -nn -A -s1500 -l | grep "User-Agent:"复制代码 +$ tcpdump -nn -A -s1500 -l | grep "User-Agent:" ``` -通过 `egrep` 可以同时提取用户代理和主机名(或其他头文件): +通过 `egrep` 可以同时提取User-Agent 和主机名(或其他头文件): ```bash -$ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:'复制代码 +$ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:' ``` ### 8.2 抓取 HTTP GET 和 POST 请求 -抓取 HTTP GET 流量: +抓取 HTTP GET 请求包: ```bash $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' + +# or + +$ tcpdump -vvAls0 | grep 'GET' ``` -也可以抓取 HTTP POST 请求流量: +可以抓取 HTTP POST 请求包: ```bash $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354' + +# or + +$ tcpdump -vvAls0 | grep 'POST' ``` 注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST 请求会被分割为多个 TCP 数据包。 -上述两个表达式中的十六进制将会与 GET 和 POST 请求的 `ASCII` 字符串匹配。例如,`tcp[((tcp[12:1] & 0xf0) >> 2):4]` 首先会[确定我们感兴趣的字节的位置](https://security.stackexchange.com/questions/121011/wireshark-tcp-filter-tcptcp121-0xf0-24)(在 TCP header 之后),然后选择我们希望匹配的 4 个字节。 +### 8.3 找出发包数最多的 IP + +找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 IP,可以使用下面的命令: + +```bash +$ tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20 +``` + +- **cut -f 1,2,3,4 -d '.'** : 以 `.` 为分隔符,打印出每行的前四列。即 IP 地址。 +- **sort | uniq -c** : 排序并计数 +- **sort -nr** : 按照数值大小逆向排序 + +### 8.4 抓取 DNS 请求和响应 + +DNS 的默认端口是 53,因此可以通过端口进行过滤 + +```shell +$ tcpdump -i any -s0 port 53 +``` + + + +### 8.5 切割 pcap 文件 + +当抓取大量数据并写入文件时,可以自动切割为多个大小相同的文件。例如,下面的命令表示每 3600 秒创建一个新文件 `capture-(hour).pcap`,每个文件大小不超过 `200*1000000` 字节: + +```bash +$ tcpdump -w /tmp/capture-%H.pcap -G 3600 -C 200 +``` + +这些文件的命名为 `capture-{1-24}.pcap`,24 小时之后,之前的文件就会被覆盖。 + +### 8.6 提取 HTTP POST 请求中的密码 + +从 HTTP POST 请求中提取密码和主机名: + +```shell +$ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:" +``` + +### 8.7 提取 HTTP 请求的 URL + +提取 HTTP 请求的主机名和路径: + +```shell +$ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:" +``` + +### 8.8 抓取 HTTP 有效数据包 + +抓取 80 端口的 HTTP 有效数据包,排除 TCP 连接建立过程的数据包(SYN / FIN / ACK): + +```shell +$ tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' +``` + +### 8.9 结合 Wireshark 进行分析 + +通常 `Wireshark`(或 tshark)比 tcpdump 更容易分析应用层协议。一般的做法是在远程服务器上先使用 `tcpdump` 抓取数据并写入文件,然后再将文件拷贝到本地工作站上用 `Wireshark` 分析。 + +还有一种更高效的方法,可以通过 ssh 连接将抓取到的数据实时发送给 Wireshark 进行分析。以 MacOS 系统为例,可以通过 `brew cask install wireshark` 来安装,然后通过下面的命令来分析: + +```shell +$ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - not port 22' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - +``` + +例如,如果想分析 DNS 协议,可以使用下面的命令: + +```shell +$ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - port 53' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - +``` + +抓取到的数据: +![](https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/20200210170101.png) +`-c` 选项用来限制抓取数据的大小。如果不限制大小,就只能通过 `ctrl-c` 来停止抓取,这样一来不仅关闭了 tcpdump,也关闭了 wireshark。 -https://juejin.im/post/5e64571bf265da57104393a1#heading-14 +到这里,我已经将我所知道的 tcpdump 的用法全部说了一遍,如果你有认真地看完本文,相信会有不小的收获,掌握一个上手的抓包工具,对于以后我们学习网络、分析网络协议、以及定位网络问题,会很有帮助,而 tcpdump 是我推荐的一个抓包工具。 -https://danielmiessler.com/study/tcpdump/ +## 9. 参考文章 -https://colobu.com/2019/07/16/a-tcpdump-tutorial-with-examples/ +1. [FreeBSD Manual Pages About tcpdump](https://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+7.2-RELEASE&format=html) +3. [Linux tcpdump命令详解](https://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html) +4. [一份快速实用的 tcpdump 命令参考手册](http://team.jiunile.com/blog/2019/06/tcpdump.html) +4. [超详细的网络抓包神器 tcpdump 使用指南](https://fuckcloudnative.io/posts/tcpdump-examples/) +5. [[译]tcpdump 示例教程](https://colobu.com/2019/07/16/a-tcpdump-tutorial-with-examples/) +6. [[英]tcpdump 示例教程](https://danielmiessler.com/study/tcpdump/) -参考文章 +--- -1. https://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+7.2-RELEASE&format=html -2. https://wizardforcel.gitbooks.io/network-basic/17.html -3. https://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html -4. [一份快速实用的 tcpdump 命令参考手册](http://team.jiunile.com/blog/2019/06/tcpdump.html) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file From ca8e45b8c5287f24b30bdb7944690f8fd71648f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Tue, 30 Jun 2020 10:01:57 +0800 Subject: [PATCH 094/147] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=8F=8A=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_04.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index 06b33ab..139c526 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -1,5 +1,7 @@ # 10.4 全网最全(中文) tcpdump 抓包指南 +![](http://image.iswbm.com/20200602135014.png) + 今天要给大家介绍的一个 Unix 下的一个 **网络数据采集分析工具**,也就是我们常说的抓包工具。 与它功能类似的工具有 wireshark ,不同的是,wireshark 有图形化界面,而 tcpdump 则只有命令行。 @@ -8,6 +10,8 @@ 这篇文章,我肝了好几天,借助于Linux 的 man 帮助命令,我把 tcpdump 的用法全部研究了个遍,才形成了本文,不夸张的说,应该可以算是中文里把 tcpdump 讲得最清楚明白,并且还最全的文章了(至少我从百度、谷歌的情况来看),所以本文值得你收藏分享,就怕你错过了,就再也找不到像这样把 tcpdump 讲得直白而且特全的文章了。 +![](http://image.iswbm.com/20200630095709.png) + 在讲解之前,有两点需要声明: 1. 第三节到第六节里的 tcpdump 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第五节的逻辑逻辑运算符进行组合搭配。 @@ -111,6 +115,8 @@ tcpdump 输出的内容虽然多,却很规律。 - `[R]` : RST(重置连接) - `[.]` : 没有 Flag (意思是除上面四种类型外的其他情况,有可能是 ACK 也有可能是 URG) +## 3. 常规过滤规则 + ### 3.1 基于IP地址过滤:host 使用 `host` 就可以指定 host ip 进行过滤 @@ -427,7 +433,7 @@ $ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" ### 5.1 根据 tcpflags 进行过滤 -通过上一篇文章,我们知道了 tcp 的首部有一个标志位。 +通过[上一篇文章](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&token=1970357830&lang=zh_CN#rd),我们知道了 tcp 的首部有一个标志位。 ![TCP 报文首部](http://image.iswbm.com/20200606095627.png) @@ -616,7 +622,7 @@ $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' 1、`tcp[12:1] & 0xf0` 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 `10110000`,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。 -2、`tcp[12:1] & 0xf0) >> 2` :如果你不理解 tcp 报文首部里的数据偏移,请先点击这个前往我的上一篇文章,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 +2、`tcp[12:1] & 0xf0) >> 2` :如果你不理解 tcp 报文首部里的数据偏移,请先点击这个前往我的[上一篇文章](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&token=1970357830&lang=zh_CN#rd),搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 `tcp[12:1] & 0xf0) >> 2` 这个表达式实际是 `(tcp[12:1] & 0xf0) >> 4 ) << 2` 的简写形式。所以要搞懂 `tcp[12:1] & 0xf0) >> 2` 只要理解了`(tcp[12:1] & 0xf0) >> 4 ) << 2` 就行了 。 @@ -641,7 +647,7 @@ $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' -## 8. 实战应用例子 +## 8. 抓包实战应用例子 ### 8.1 提取 HTTP 的 User-Agent From cd1f1169f116a0d00dcac8e2477ccb30b0d5c91b Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 30 Jun 2020 12:12:22 +0800 Subject: [PATCH 095/147] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E7=AC=94=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_12.rst | 400 +++++++++++++++++++++++++++++++++++++----- source/c10/c10_04.md | 10 +- source/c10/c10_04.rst | 324 ++++++++++++++++++++++++++-------- 3 files changed, 608 insertions(+), 126 deletions(-) diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index d7dbe58..3a62e3e 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -6,6 +6,31 @@ yum --- +安装 +~~~~ + +加个 ``-y`` 可以在安装包时代替用户自动响应 yes + +.. code:: shell + + $ yum install -y xxx + +加个 ``--installroot=/usr/local/`` 指定将软件安装在 /usr/local +目录下,而不安装在默认路径下 + +.. code:: shell + + $ yum install --installroot=/usr/local/ -y xxx + +重新安装 == 卸载再安装 + +.. code:: shell + + $ yum reinstall -y xxx + +下载 +~~~~ + 只下载安装包 .. code:: shell @@ -19,68 +44,290 @@ yum yumdownloader --resolve --destdir /home/wangbm/ perl-Sys-Virt.x86_64 yumdownloader --resolve --destdir /home/test/ systemd-devel-219-62.el7_6.2.x86_64 -查看一个包的安装时间 +查看 +~~~~ -:: +查看具体包的信息 - [root@ws_compute01 ~]# rpm -qi|grep zlib-1.2.7-17.el7.i686 - rpm: no arguments given for query - [root@ws_compute01 ~]# rpm -qi zlib-1.2.7-17.el7.i686 - Name : zlib - Version : 1.2.7 - Release : 17.el7 - Architecture: i686 - Install Date: Tue 13 Aug 2019 10:22:41 AM CST - Group : System Environment/Libraries - Size : 184798 - License : zlib and Boost - Signature : RSA/SHA256, Mon 21 Nov 2016 05:05:03 AM CST, Key ID 24c6a8a7f4a80eb5 - Source RPM : zlib-1.2.7-17.el7.src.rpm - Build Date : Sun 06 Nov 2016 02:07:22 AM CST - Build Host : worker1.bsys.centos.org - Relocations : (not relocatable) - Packager : CentOS BuildSystem - Vendor : CentOS - URL : http://www.zlib.net/ - Summary : The compression and decompression library - Description : - Zlib is a general-purpose, patent-free, lossless data compression - library which is used by many different programs. +.. code:: shell -查看一个包在系统已配的源里都有哪些版本 + $ yum info zabbix-agent -:: +查看配置并启用的仓库中有哪些包,包括已安装和未安装的 - [root@ws_compute01 ~]# yum provides - Loaded plugins: fastestmirror, versionlock - Loading mirror speeds from cached hostfile - * epel: hkg.mirror.rackspace.com - 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines - Repo : ansible-master +.. code:: shell + $ yum list - 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines - Repo : libvirt-3.9.0 +查看有哪些包可以升级的 +.. code:: shell - 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines - Repo : wsbase + $ yum list updates +查询一个文件在什么包里 - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : ansible-master +.. code:: shell + $ yum provides xxx - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : libvirt-4.5.0 +查询一个命令是哪个包提供的 +.. code:: shell - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : wsupdates + $ yum whatprovides xxx +列出所有的容器 - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : @libvirt-4.5.0 +.. code:: shell + + $ yum repolist all + +查询某个仓库下的所有包 + +.. code:: shell + + $ yum repo-pkgs list + +查看一个包的所有依赖项 + +.. code:: shell + + $ yum deplist httpd + +搜索 +~~~~ + +在配置并启用的仓库中搜索包 + +.. code:: shell + + $ yum search zabbix-agent + +升级 +~~~~ + +.. code:: shell + + $ yum update xxx + +删除 +~~~~ + +.. code:: shell + + $ yum remove -y xxx + +清理 +~~~~ + +清理已下载的软件文件 + +.. code:: shell + + $ yum clean packages + +清理已下载的软件文件头 + +.. code:: shell + + $ yum clean headers + +清理下载过的容器相关数据 + +.. code:: shell + + $ yum clean all + +分组 +~~~~ + +查看分组 + +.. code:: shell + + $ yum grouplist + +查看分组信息 + +.. code:: shell + + $ yum groupinfo + +安装一整组软件 + +.. code:: shell + + $ yum groupinstall + +删除某个组 + +.. code:: shell + + $ yum groupremove + +历史 +~~~~ + +.. code:: shell + + $ yum history + +语言 +~~~~ + +列出已安装的语言 + +.. code:: shell + + $ yum langlist + +为语言安装适当的语言包 + +.. code:: shell + + $ yum langinstall + +删除语言的已安装语言包 + +.. code:: shell + + $ yum langremove + +其他 +~~~~ + +执行事务 + +.. code:: shell + + $ yum load-transaction /tmp/yum_save_tx.xxx.n0EVjx.yumtx + +生成元数据缓存 + +.. code:: shell + + $ yum makecache + +进入交互式模式 + +.. code:: shell + + $ yum shell + +选项 +~~~~ + +.. code:: shell + + -h, - help显示此帮助消息并退出 + -t, - 容忍错误 + -C,--cacheonly完全从系统缓存运行,不更新缓存 + + -q, - 安静的操作 + -v, - verbose详细操作 + -y, - assumeyes对所有问题都回答是 + + --assumeno对所有问题都回答否 + --version显示Yum版本并退出 + --installroot = [path]设置安装root + + --obsoletes在更新期间启用过时处理 + --noplugins禁用Yum插件 + --nogpgcheck禁用gpg签名检查 + + + -skip-broken 跳过没有解决问题的包 + --color = COLOR控制是否使用颜色 + + --downloadonly不更新,只需下载 + --downloaddir = DLDIR指定存储包的备用目录 + --setopt = SETOPTS设置任意配置和repo选项 + --bugfix在更新中包含bugfix相关包 + --security在更新中包含安全相关的包 + --advisory = ADVS, - advisories = ADVS 更新包括修复给定建议所需的包 + --bzs = BZS 更新包括修复给定BZ所需的包 + --cves = CVES 更新包括修复给定CVE所需的包 + --sec-severity = SEVS, - secseverity = SEVS + +配置文件位置 + +.. code:: shell + + -c [config file], - config = [config file] + +最大命令等待时间 + +.. code:: shell + + -R [minute], - randomwait = [minute] + +调试输出级别 + +.. code:: shell + + -d [debug level], - debug level = [debug level] + +在repos中,在list / search命令中显示重复项 + +.. code:: shell + + --showduplicates + +错误输出级别 + +.. code:: shell + + -e [error level], - errorlevel = [error level] + +调试rpm的输出级别 + +.. code:: shell + + --rpmverbosity = [debug level name] + +启用/禁用仓库(允许使用通配符) + +.. code:: shell + + --enablerepo = [repo] + --disablerepo = [repo] + +按名称或glob排除包 + +.. code:: shell + + -x [package], - exclude = [package] + +禁用从main,repo或所有内容的排除 + +.. code:: shell + + --disableexcludes = [plugin] + +禁用包含repo或所有内容的includepkgs + +.. code:: shell + + --disableincludes = [plugin] + +按名称禁用插件 + +.. code:: shell + + --disableplugin = [plugin] + +按名称启用插件 + +.. code:: shell + + --enableplugin = [plugin] + +在yum config和repo文件中设置$ releasever的值 + +.. code:: shell + + --releasever = RELEASEVER yum-utils 使用 @@ -208,6 +455,69 @@ rpm # 重建rpm数据文件 rpm –rebuilddb +查看一个包的安装时间 + +:: + + [root@ws_compute01 ~]# rpm -qi|grep zlib-1.2.7-17.el7.i686 + rpm: no arguments given for query + [root@ws_compute01 ~]# rpm -qi zlib-1.2.7-17.el7.i686 + Name : zlib + Version : 1.2.7 + Release : 17.el7 + Architecture: i686 + Install Date: Tue 13 Aug 2019 10:22:41 AM CST + Group : System Environment/Libraries + Size : 184798 + License : zlib and Boost + Signature : RSA/SHA256, Mon 21 Nov 2016 05:05:03 AM CST, Key ID 24c6a8a7f4a80eb5 + Source RPM : zlib-1.2.7-17.el7.src.rpm + Build Date : Sun 06 Nov 2016 02:07:22 AM CST + Build Host : worker1.bsys.centos.org + Relocations : (not relocatable) + Packager : CentOS BuildSystem + Vendor : CentOS + URL : http://www.zlib.net/ + Summary : The compression and decompression library + Description : + Zlib is a general-purpose, patent-free, lossless data compression + library which is used by many different programs. + +查看一个包在系统已配的源里都有哪些版本 + +:: + + [root@ws_compute01 ~]# yum provides + Loaded plugins: fastestmirror, versionlock + Loading mirror speeds from cached hostfile + * epel: hkg.mirror.rackspace.com + 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines + Repo : ansible-master + + + 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines + Repo : libvirt-3.9.0 + + + 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines + Repo : wsbase + + + 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines + Repo : ansible-master + + + 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines + Repo : libvirt-4.5.0 + + + 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines + Repo : wsupdates + + + 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines + Repo : @libvirt-4.5.0 + |image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index 139c526..4b665fb 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -431,7 +431,7 @@ $ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" ## 6. 特殊过滤规则 -### 5.1 根据 tcpflags 进行过滤 +### 6.1 根据 tcpflags 进行过滤 通过[上一篇文章](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&token=1970357830&lang=zh_CN#rd),我们知道了 tcp 的首部有一个标志位。 @@ -544,7 +544,7 @@ icmp-ireqreply, icmp-maskreq, icmp-maskreply -### 5.2 基于包大小进行过滤 +### 6.2 基于包大小进行过滤 若你想查看指定大小的数据包,也是可以的 @@ -556,7 +556,7 @@ $ tcpdump <= 128 -### 5.3 根据 mac 地址进行过滤 +### 6.3 根据 mac 地址进行过滤 例子如下,其中 ehost 是记录在 /etc/ethers 里的 name @@ -568,7 +568,7 @@ $ tcpdump ether src [ehost] -### 5.4 过滤通过指定网关的数据包 +### 6.4 过滤通过指定网关的数据包 ```shell $ tcpdump gateway [host] @@ -576,7 +576,7 @@ $ tcpdump gateway [host] -### 5.5 过滤广播/多播数据包 +### 6.5 过滤广播/多播数据包 ```shell $ tcpdump ether broadcast diff --git a/source/c10/c10_04.rst b/source/c10/c10_04.rst index e22ab08..e32a599 100644 --- a/source/c10/c10_04.rst +++ b/source/c10/c10_04.rst @@ -1,10 +1,29 @@ -10.4 全网最全的 tcpdump 抓包指南 -================================ +10.4 全网最全(中文) tcpdump 抓包指南 +==================================== + +|image0| + +今天要给大家介绍的一个 Unix 下的一个 +**网络数据采集分析工具**\ ,也就是我们常说的抓包工具。 + +与它功能类似的工具有 wireshark ,不同的是,wireshark 有图形化界面,而 +tcpdump 则只有命令行。 + +由于我本人更习惯使用命令行的方式进行抓包,因此今天先跳过 +wireshark,直接给大家介绍这个 tcpdump 神器。 + +这篇文章,我肝了好几天,借助于Linux 的 man 帮助命令,我把 tcpdump +的用法全部研究了个遍,才形成了本文,不夸张的说,应该可以算是中文里把 +tcpdump +讲得最清楚明白,并且还最全的文章了(至少我从百度、谷歌的情况来看),所以本文值得你收藏分享,就怕你错过了,就再也找不到像这样把 +tcpdump 讲得直白而且特全的文章了。 + +|image1| 在讲解之前,有两点需要声明: -1. 第二节到第三节里的 tcpdump - 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第四节的逻辑逻辑运算符进行组合搭配。 +1. 第三节到第六节里的 tcpdump + 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第五节的逻辑逻辑运算符进行组合搭配。 2. 不同 Linux 发行版下、不同版本的 tcpdump 可能有小许差异, 本文是基于 CentOS 7.2 的 4.5.1 版本的tcpdump 进行学习的,若在你的环境中无法使用,请参考 ``man tcpdump`` @@ -13,7 +32,15 @@ 1. tcpdump 核心参数图解 ----------------------- -初学者在没有掌握 tcpdump 时,会对这个命令的参数产生恐惧。 +大家都知道,网络上的流量、数据包,非常的多,因此要想抓到我们所需要的数据包,就需要我们定义一个精准的过滤器,把这些目标数据包,从巨大的数据包网络中抓取出来。 + +所以学习抓包工具,其实就是学习如何定义过滤器的过程。 + +而在 tcpdump +的世界里,过滤器的实现,都是通过一个又一个的参数组合起来,一个参数不够精准,那就再加一个,直到我们能过滤掉无用的数据包,只留下我们感兴趣的数据包。 + +tcpdump 的参数非常的多,初学者在没有掌握 tcpdump +时,会对这个命令的众多参数产生很多的疑惑。 就比如下面这个命令,我们要通过 ``host`` 参数指定 host ip 进行过滤 @@ -57,33 +84,32 @@ tcpdump,而不能将 tcpdump 为此,我画了一张图,方便你直观的理解 tcpdump 的各种参数: -|image0| +|image2| -1. option - 可选参数:将在后边一一解释,对应本文\ `第三节:可选参数解析 <>`__ -2. proto 限定词:根据协议进行过滤,可识别的关键词有: upd, udp, icmp, +1. option 可选参数:将在后边一一解释。 +2. proto 类过滤器:根据协议进行过滤,可识别的关键词有: tcp, udp, icmp, ip, ip6, arp, rarp,ether,wlan, fddi, tr, decnet -3. type 限定词:可识别的关键词有:host, net, port, +3. type 类过滤器:可识别的关键词有:host, net, port, portrange,这些词后边需要再接参数。 -4. direction 限定词:根据数据流向进行过滤,可识别的关键字有:src, +4. direction 类过滤器:根据数据流向进行过滤,可识别的关键字有:src, dst,同时你可以使用逻辑运算符进行组合,比如 src or dst proto、type、direction -这三个限定词的内容比较简单,也最常用,因此我将其放在最前面,也就是 -`第二节:常规过滤规则 <>`__ 一起介绍。 +这三类过滤器的内容比较简单,也最常用,因此我将其放在最前面,也就是 +**第三节:常规过滤规则**\ 一起介绍。 而 option 可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 -`第三节:可选参数解析 <>`__ +**第四节:可选参数解析** -当你看完前面四节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% +当你看完前面六节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% 的使用需求。 你一定会问了,还有 20% 呢? 其实 tcpdump -还有一些过滤关键词,它不符合以上四种限定规则,可能需要你单独记忆。关于这部分我会在 -`第五节:特殊过滤规则 <>`__ 里进行介绍。 +还有一些过滤关键词,它不符合以上四种过滤规则,可能需要你单独记忆。关于这部分我会在 +**第六节:特殊过滤规则** 里进行介绍。 2. 理解 tcpdump 的输出 ---------------------- @@ -121,8 +147,8 @@ tcpdump 输出的内容虽然多,却很规律。 - ``[P]`` : PSH(推送数据) - ``[F]`` : FIN (结束连接) - ``[R]`` : RST(重置连接) -- ``[.]`` : 没有 Flag,由于除了 SYN - 包外所有的数据包都有ACK,所以一般这个标志也可表示 ACK +- ``[.]`` : 没有 Flag (意思是除上面四种类型外的其他情况,有可能是 ACK + 也有可能是 URG) 3. 常规过滤规则 --------------- @@ -184,7 +210,19 @@ tcpdump 输出的内容虽然多,却很规律。 # 根据目标端口进行过滤 $ tcpdump dst port 8088 -若你想过滤的是一个端口范围,一个一个指定就较为麻烦,此时你可以这样指定一个端口段。 +如果你想要同时指定两个端口你可以这样写 + +.. code:: shell + + $ tcpdump port 80 or port 8088 + +但也可以简写成这样 + +.. code:: shell + + $ tcpdump port 80 or 8088 + +如果你的想抓取的不再是一两个端口,而是一个范围,一个一个指定就非常麻烦了,此时你可以这样指定一个端口段。 .. code:: shell @@ -192,6 +230,14 @@ tcpdump 输出的内容虽然多,却很规律。 $ tcpdump src portrange 8000-8080 $ tcpdump dst portrange 8000-8080 +对于一些常见协议的默认端口,我们还可以直接使用协议名,而不用具体的端口号 + +比如 http == 80,https == 443 等 + +.. code:: shell + + $ tcpdump tcp port http + 3.4 基于协议进行过滤:proto ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -238,6 +284,7 @@ tcp,这两种其实都会包含在内。 $ tcpdump 'ip protochain tcp' # or + $ tcpdump ip protochain 6 而如果是 IPv6 的 tcp 包 ,就这样写 @@ -255,6 +302,7 @@ tcp,这两种其实都会包含在内。 $ tcpdump 'ip6 protochain tcp' # or + $ tcpdump ip6 protochain 6 关于上面这几个命令示例,有两点需要注意: @@ -455,10 +503,11 @@ wireshark 。 6. 特殊过滤规则 --------------- -5.1 根据 tcpflags 进行过滤 +6.1 根据 tcpflags 进行过滤 ~~~~~~~~~~~~~~~~~~~~~~~~~~ -通过上一篇文章,我们知道了 tcp 的首部有一个标志位。 +通过\ `上一篇文章 `__\ ,我们知道了 +tcp 的首部有一个标志位。 .. figure:: http://image.iswbm.com/20200606095627.png :alt: TCP 报文首部 @@ -471,37 +520,49 @@ tcpdump 支持我们根据数据包的标志位进行过滤 proto [ expr:size ] -“proto”可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6),“expr”表示与指定的协议头开头相关的字节偏移量。有我们熟知的直接偏移量如tcpflags,也有取值常量如tcp-fin, -tcp-syn, tcp-rst, tcp-push, tcp-ack, -tcp-urg。“size”是可选的,表示从字节偏移量开始检查的字节数量。 +- ``proto``\ :可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6) -对于下面的例子,我先解释一下 +- ``expr``\ :可以是数值,也可以是一个表达式,表示与指定的协议头开始处的字节偏移量。 +- ``size``\ :是可选的,表示从字节偏移量开始取的字节数量。 -1、tcpflags 可以理解为是一个常量,相当于 -13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位 +接下来,我将举几个例子,让人明白它的写法,不过在那之前,有几个点需要你明白,这在后面的例子中会用到: -|image1| +**1、**\ tcpflags 可以理解为是一个别名常量,相当于 +13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位,所以 +tcp[tcpflags] 等价于 tcp[13] ,对应下图中的报文位置。 + +|image3| -2、tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg -这些同样可以理解为常量,分别代表 +**2、**\ tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg +这些同样可以理解为别名常量,分别代表 1,2,4,8,16,32,64。这些数字是如何计算出来的呢? 以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2 -|image2| +|image4| + +由于数字不好记忆,所以一般使用这样的“别名常量”表示。 + +因此当下面这个表达式成立时,就代表这个包是一个 syn 包。 + +.. code:: shell -由于数字不好记忆,所以一般使用“常量”表示。 + tcp[tcpflags] == tcp-syn -下面以最常见的 syn包为例,演示一下如何用抓取 syn -包,其他的类型的包你可以自行修改。 +要抓取特定数据包,方法有很多种。 -主要有三种写法: 1、第一种写法:使用数字表示偏移量 +下面以最常见的 syn包为例,演示一下如何用 tcpdump 抓取到 syn +包,而其他的类型的包也是同样的道理。 + +据我总结,主要有三种写法: + +1、第一种写法:使用数字表示偏移量 .. code:: shell $ tcpdump -i eth0 "tcp[13] & 2 != 0" -2、第二种写法:使用常量表示偏移量 +2、第二种写法:使用别名常量表示偏移量 .. code:: shell @@ -548,8 +609,8 @@ tcp-urg。“size”是可选的,表示从字节偏移量开始检查的字节 $ tcpdump -i eth0 'tcp[tcpflags] = 18' -tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp -协议,可以使用的常量有 +tcp 中有 类似 tcp-syn 的别名常量,其他协议也是有的,比如 icmp +协议,可以使用的别名常量有 .. code:: shell @@ -559,7 +620,7 @@ tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp icmp-tstamp, icmp-tstampreply,icmp-ireq, icmp-ireqreply, icmp-maskreq, icmp-maskreply -5.2 基于包大小进行过滤 +6.2 基于包大小进行过滤 ~~~~~~~~~~~~~~~~~~~~~~ 若你想查看指定大小的数据包,也是可以的 @@ -570,7 +631,7 @@ tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp $ tcpdump greater 64 $ tcpdump <= 128 -5.3 根据 mac 地址进行过滤 +6.3 根据 mac 地址进行过滤 ~~~~~~~~~~~~~~~~~~~~~~~~~ 例子如下,其中 ehost 是记录在 /etc/ethers 里的 name @@ -581,14 +642,14 @@ tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp $ tcpdump ether dst [ehost] $ tcpdump ether src [ehost] -5.4 过滤通过指定网关的数据包 +6.4 过滤通过指定网关的数据包 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: shell $ tcpdump gateway [host] -5.5 过滤广播/多播数据包 +6.5 过滤广播/多播数据包 ~~~~~~~~~~~~~~~~~~~~~~~ .. code:: shell @@ -629,24 +690,24 @@ tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp 8 个bit 其实就是下图中的红框圈起来的位置,而在这里我们只要前面 4个bit,也就是实际数据在整个报文首部中的偏移量。 - |image3| + |image5| - ``&``\ :是\ `位运算 `__\ 里的 and 操作符,比如 ``0011 & 0010 = 0010`` -- ``>>``\ :是位运算里的右移操作,比如 ``0111 >> 2 = 0011`` +- ``>>``\ :是位运算里的右移操作,比如 ``0111 >> 2 = 0001`` - ``0xf0``\ :是 10 进制的 240 的 16 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 - 4bit 全部是 1,后面4个bit全部是0. + 4bit 全部是 1,后面4个bit全部是0,往后看你就知道这个特点有什么用了。 分解完后,再慢慢合并起来看 -1、\ ``tcp[12:1] && 0xf0`` +1、\ ``tcp[12:1] & 0xf0`` 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 ``10110000``\ ,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。 2、\ ``tcp[12:1] & 0xf0) >> 2`` :如果你不理解 tcp -报文首部里的数据偏移,请先点击这个前往我的上一篇文章,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 +报文首部里的数据偏移,请先点击这个前往我的\ `上一篇文章 `__\ ,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 ``tcp[12:1] & 0xf0) >> 2`` 这个表达式实际是 ``(tcp[12:1] & 0xf0) >> 4 ) << 2`` 的简写形式。所以要搞懂 @@ -661,7 +722,7 @@ tcp 中有 类似 tcp-syn 的常量,其他协议也是有的,比如 icmp ``tcp[12:1] & 0xf0) >> 4``\ ,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 Data Offset 的单位是 4个字节,因为要将 1011 乘以 4才可以,除以4在位运算中相当于左移2位,也就是 ``<<2``\ ,与前面的 -``>>4`` 结合起来一起算的话,最终的运算可以简化为 ``>>2`` +``>>4`` 结合起来一起算的话,最终的运算可以简化为 ``>>2``\ 。 至此,我们终于得出了实际数据开始的位置是 ``tcp[12:1] & 0xf0) >> 2`` (单位是字节)。 @@ -680,69 +741,180 @@ GET最后还有个空格)的 16进制写法(也就是 ``0x47455420``\ )进 0x54 --> 84 --> T 0x20 --> 32 --> 空格 -|image4| +|image6| 如果相等,则该表达式为True,tcpdump 认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。 -8. 实战应用例子 ---------------- +8. 抓包实战应用例子 +------------------- 8.1 提取 HTTP 的 User-Agent ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -从 HTTP 请求头中提取 HTTP 用户代理: +从 HTTP 请求头中提取 HTTP 的 User-Agent: .. code:: bash - $ tcpdump -nn -A -s1500 -l | grep "User-Agent:"复制代码 + $ tcpdump -nn -A -s1500 -l | grep "User-Agent:" -通过 ``egrep`` 可以同时提取用户代理和主机名(或其他头文件): +通过 ``egrep`` 可以同时提取User-Agent 和主机名(或其他头文件): .. code:: bash - $ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:'复制代码 + $ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:' 8.2 抓取 HTTP GET 和 POST 请求 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -抓取 HTTP GET 流量: +抓取 HTTP GET 请求包: .. code:: bash $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' -也可以抓取 HTTP POST 请求流量: + # or + + $ tcpdump -vvAls0 | grep 'GET' + +可以抓取 HTTP POST 请求包: .. code:: bash $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354' + # or + + $ tcpdump -vvAls0 | grep 'POST' + 注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST 请求会被分割为多个 TCP 数据包。 -上述两个表达式中的十六进制将会与 GET 和 POST 请求的 ``ASCII`` -字符串匹配。例如,\ ``tcp[((tcp[12:1] & 0xf0) >> 2):4]`` -首先会\ `确定我们感兴趣的字节的位置 `__\ (在 -TCP header 之后),然后选择我们希望匹配的 4 个字节。 +8.3 找出发包数最多的 IP +~~~~~~~~~~~~~~~~~~~~~~~ -https://juejin.im/post/5e64571bf265da57104393a1#heading-14 +找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 +IP,可以使用下面的命令: -https://danielmiessler.com/study/tcpdump/ +.. code:: bash -https://colobu.com/2019/07/16/a-tcpdump-tutorial-with-examples/ + $ tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20 -参考文章 +- **cut -f 1,2,3,4 -d ‘.’** : 以 ``.`` 为分隔符,打印出每行的前四列。即 + IP 地址。 +- **sort \| uniq -c** : 排序并计数 +- **sort -nr** : 按照数值大小逆向排序 -1. https://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+7.2-RELEASE&format=html -2. https://wizardforcel.gitbooks.io/network-basic/17.html -3. https://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html -4. `一份快速实用的 tcpdump - 命令参考手册 `__ +8.4 抓取 DNS 请求和响应 +~~~~~~~~~~~~~~~~~~~~~~~ + +DNS 的默认端口是 53,因此可以通过端口进行过滤 + +.. code:: shell + + $ tcpdump -i any -s0 port 53 + +8.5 切割 pcap 文件 +~~~~~~~~~~~~~~~~~~ + +当抓取大量数据并写入文件时,可以自动切割为多个大小相同的文件。例如,下面的命令表示每 +3600 秒创建一个新文件 ``capture-(hour).pcap``\ ,每个文件大小不超过 +``200*1000000`` 字节: + +.. code:: bash + + $ tcpdump -w /tmp/capture-%H.pcap -G 3600 -C 200 + +这些文件的命名为 ``capture-{1-24}.pcap``\ ,24 +小时之后,之前的文件就会被覆盖。 + +8.6 提取 HTTP POST 请求中的密码 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +从 HTTP POST 请求中提取密码和主机名: + +.. code:: shell + + $ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:" + +8.7 提取 HTTP 请求的 URL +~~~~~~~~~~~~~~~~~~~~~~~~ + +提取 HTTP 请求的主机名和路径: + +.. code:: shell + + $ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:" -.. |image0| image:: http://image.iswbm.com/20200628111325.png -.. |image1| image:: http://image.iswbm.com/20200628222034.png -.. |image2| image:: http://image.iswbm.com/20200628222010.png -.. |image3| image:: http://image.iswbm.com/20200629085659.png -.. |image4| image:: http://image.iswbm.com/20200629130407.png +8.8 抓取 HTTP 有效数据包 +~~~~~~~~~~~~~~~~~~~~~~~~ + +抓取 80 端口的 HTTP 有效数据包,排除 TCP 连接建立过程的数据包(SYN / FIN +/ ACK): + +.. code:: shell + + $ tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' + +8.9 结合 Wireshark 进行分析 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +通常 ``Wireshark``\ (或 tshark)比 tcpdump +更容易分析应用层协议。一般的做法是在远程服务器上先使用 ``tcpdump`` +抓取数据并写入文件,然后再将文件拷贝到本地工作站上用 ``Wireshark`` +分析。 + +还有一种更高效的方法,可以通过 ssh 连接将抓取到的数据实时发送给 +Wireshark 进行分析。以 MacOS 系统为例,可以通过 +``brew cask install wireshark`` 来安装,然后通过下面的命令来分析: + +.. code:: shell + + $ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - not port 22' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - + +例如,如果想分析 DNS 协议,可以使用下面的命令: + +.. code:: shell + + $ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - port 53' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - + +抓取到的数据: + +|image7| + +``-c`` 选项用来限制抓取数据的大小。如果不限制大小,就只能通过 ``ctrl-c`` +来停止抓取,这样一来不仅关闭了 tcpdump,也关闭了 wireshark。 + +到这里,我已经将我所知道的 tcpdump +的用法全部说了一遍,如果你有认真地看完本文,相信会有不小的收获,掌握一个上手的抓包工具,对于以后我们学习网络、分析网络协议、以及定位网络问题,会很有帮助,而 +tcpdump 是我推荐的一个抓包工具。 + +9. 参考文章 +----------- + +1. `FreeBSD Manual Pages About + tcpdump `__ +2. `Linux + tcpdump命令详解 `__ +3. `一份快速实用的 tcpdump + 命令参考手册 `__ +4. `超详细的网络抓包神器 tcpdump + 使用指南 `__ +5. `[译]tcpdump + 示例教程 `__ +6. `[英]tcpdump 示例教程 `__ + +-------------- + +|image8| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200630095709.png +.. |image2| image:: http://image.iswbm.com/20200628111325.png +.. |image3| image:: http://image.iswbm.com/20200628222034.png +.. |image4| image:: http://image.iswbm.com/20200628222010.png +.. |image5| image:: http://image.iswbm.com/20200629085659.png +.. |image6| image:: http://image.iswbm.com/20200629130407.png +.. |image7| image:: https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/20200210170101.png +.. |image8| image:: http://image.iswbm.com/20200607174235.png From b6d45f96b0746dcbb36453aea3da89b1b4cf41c5 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 30 Jun 2020 22:16:15 +0800 Subject: [PATCH 096/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_12.md | 169 +++++------------------------------------- source/c07/c07_12.rst | 167 +++++------------------------------------ 2 files changed, 37 insertions(+), 299 deletions(-) diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md index bfe8d5e..099d935 100644 --- a/source/c07/c07_12.md +++ b/source/c07/c07_12.md @@ -1,10 +1,10 @@ -# 7.12 Linux 包管理工具:yum 和 rpm +# 7.12 yum 命令使用指南及问题排查方法 ![](http://image.iswbm.com/20200602135014.png) -## yum -### 安装 + +## 安装 加个 `-y` 可以在安装包时代替用户自动响应 yes @@ -26,7 +26,7 @@ $ yum reinstall -y xxx -### 下载 +## 下载 只下载安装包 @@ -41,7 +41,7 @@ yumdownloader --resolve --destdir /home/wangbm/ perl-Sys-Virt.x86_64 yumdownloader --resolve --destdir /home/test/ systemd-devel-219-62.el7_6.2.x86_64 ``` -### 查看 +## 查看 查看具体包的信息 @@ -61,13 +61,13 @@ $ yum list $ yum list updates ``` -查询一个文件在什么包里 +查询一个命令是由哪个包提供的 ```shell $ yum provides xxx ``` -查询一个命令是哪个包提供的 +查询一个模块/共享库/文件是哪个包提供的 ```shell $ yum whatprovides xxx @@ -93,7 +93,7 @@ $ yum deplist httpd -### 搜索 +## 搜索 在配置并启用的仓库中搜索包 @@ -103,7 +103,7 @@ $ yum search zabbix-agent -### 升级 +## 升级 ```shell $ yum update xxx @@ -111,7 +111,7 @@ $ yum update xxx -### 删除 +## 删除 ```shell $ yum remove -y xxx @@ -119,7 +119,7 @@ $ yum remove -y xxx -### 清理 +## 清理 清理已下载的软件文件 @@ -140,7 +140,7 @@ $ yum clean all -### 分组 +## 分组 查看分组 @@ -166,13 +166,13 @@ $ yum groupinstall $ yum groupremove ``` -### 历史 +## 历史 ```shell $ yum history ``` -### 语言 +## 语言 列出已安装的语言 @@ -194,7 +194,7 @@ $ yum langremove -### 其他 +## 其他 执行事务 @@ -216,7 +216,7 @@ $ yum shell -### 选项 +## 选项 ```shell -h, - help显示此帮助消息并退出 @@ -354,6 +354,8 @@ $ rpm -e glibc-common-2.17-196.el7_4.2.x86_64 +## 问题排查记录 + 查找一个 so 文件是属于哪个 rpm 包 经常在安装一个包的时候,会报如下的错误,找不到某 so 文件 @@ -401,139 +403,4 @@ $ yumdb info python-nova-tests -查看rpm包的版本 - -```bash -# 查看软件包的详细信息 -rpm -qpi xxx.rpm - -# 查看软件包所包含的目录和文件 -rpm -qpl xxx.rpm - -# 查看软件包的文档所在的位置 -rpm -qpd xxx.rpm - -# 查看软件包的配置文件 -rpm -qpc rpm - -# 查看软件包的依赖关系 -rpm -qpR xxx.rpm -``` - - - -## rpm - -```shell -rpm -ivh xxx.rpm-------安装软件 - -   -e-----------------卸载指定软件,不能是安装包名称 - -   -q-----------------查询指定软件是否安装,跟软件名称 - -   -qi----------------查询已经安装的软件的信息 - -  -ql-----------------查询已经安装的软件中包含什么样的内容 - -  -qf /etc/fstab ----查询这个文件是由哪个安装包产生的 - -  -qc-----------------查询已经安装的软件中包含的配置文件 - -  -qd-----------------查询已经安装的软件中包含的doc文件 - -  -q --scripts ------查询软件的脚本内容 - -  -Uvh----------------升级软件 - -  -Fvh----------------刷新 - -  -p------------------对于未安装的软件包查询信息,需要额外加此选项 - -  -qip----------------查询一个尚款安装的安装包的信息 - -  -qpc----------------查询一个尚未安装的安装包的配置文件 - -  -qpd----------------查询一个尚未安装的安装包的doc文件 - -  -qpl----------------查询一个尚未安装的安装包包含的信息 -``` - -重建 rpmdb - -```shell -# 删除rpm数据文件 -rm -f /var/lib/rpm/__db.00* - -# 重建rpm数据文件 -rpm –rebuilddb -``` - -查看一个包的安装时间 - -``` -[root@ws_compute01 ~]# rpm -qi|grep zlib-1.2.7-17.el7.i686 -rpm: no arguments given for query -[root@ws_compute01 ~]# rpm -qi zlib-1.2.7-17.el7.i686 -Name : zlib -Version : 1.2.7 -Release : 17.el7 -Architecture: i686 -Install Date: Tue 13 Aug 2019 10:22:41 AM CST -Group : System Environment/Libraries -Size : 184798 -License : zlib and Boost -Signature : RSA/SHA256, Mon 21 Nov 2016 05:05:03 AM CST, Key ID 24c6a8a7f4a80eb5 -Source RPM : zlib-1.2.7-17.el7.src.rpm -Build Date : Sun 06 Nov 2016 02:07:22 AM CST -Build Host : worker1.bsys.centos.org -Relocations : (not relocatable) -Packager : CentOS BuildSystem -Vendor : CentOS -URL : http://www.zlib.net/ -Summary : The compression and decompression library -Description : -Zlib is a general-purpose, patent-free, lossless data compression -library which is used by many different programs. -``` - -查看一个包在系统已配的源里都有哪些版本 - -``` -[root@ws_compute01 ~]# yum provides -Loaded plugins: fastestmirror, versionlock -Loading mirror speeds from cached hostfile - * epel: hkg.mirror.rackspace.com -1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines -Repo : ansible-master - - -1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines -Repo : libvirt-3.9.0 - - -1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines -Repo : wsbase - - -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : ansible-master - - -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : libvirt-4.5.0 - - -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : wsupdates - - -1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines -Repo : @libvirt-4.5.0 - -``` - - - - - ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index 3a62e3e..0a34867 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -1,13 +1,10 @@ -7.12 Linux 包管理工具:yum 和 rpm -================================= +7.12 yum 命令使用指南及问题排查方法 +=================================== |image0| -yum ---- - 安装 -~~~~ +---- 加个 ``-y`` 可以在安装包时代替用户自动响应 yes @@ -29,7 +26,7 @@ yum $ yum reinstall -y xxx 下载 -~~~~ +---- 只下载安装包 @@ -45,7 +42,7 @@ yum yumdownloader --resolve --destdir /home/test/ systemd-devel-219-62.el7_6.2.x86_64 查看 -~~~~ +---- 查看具体包的信息 @@ -65,13 +62,13 @@ yum $ yum list updates -查询一个文件在什么包里 +查询一个命令是由哪个包提供的 .. code:: shell $ yum provides xxx -查询一个命令是哪个包提供的 +查询一个模块/共享库/文件是哪个包提供的 .. code:: shell @@ -96,7 +93,7 @@ yum $ yum deplist httpd 搜索 -~~~~ +---- 在配置并启用的仓库中搜索包 @@ -105,21 +102,21 @@ yum $ yum search zabbix-agent 升级 -~~~~ +---- .. code:: shell $ yum update xxx 删除 -~~~~ +---- .. code:: shell $ yum remove -y xxx 清理 -~~~~ +---- 清理已下载的软件文件 @@ -140,7 +137,7 @@ yum $ yum clean all 分组 -~~~~ +---- 查看分组 @@ -167,14 +164,14 @@ yum $ yum groupremove 历史 -~~~~ +---- .. code:: shell $ yum history 语言 -~~~~ +---- 列出已安装的语言 @@ -195,7 +192,7 @@ yum $ yum langremove 其他 -~~~~ +---- 执行事务 @@ -216,7 +213,7 @@ yum $ yum shell 选项 -~~~~ +---- .. code:: shell @@ -347,6 +344,9 @@ yum-utils 使用 # 将上面列出的包卸载 $ rpm -e glibc-common-2.17-196.el7_4.2.x86_64 +问题排查记录 +------------ + 查找一个 so 文件是属于哪个 rpm 包 经常在安装一个包的时候,会报如下的错误,找不到某 so 文件 @@ -389,135 +389,6 @@ yum-utils 使用 |image3| -查看rpm包的版本 - -.. code:: bash - - # 查看软件包的详细信息 - rpm -qpi xxx.rpm - - # 查看软件包所包含的目录和文件 - rpm -qpl xxx.rpm - - # 查看软件包的文档所在的位置 - rpm -qpd xxx.rpm - - # 查看软件包的配置文件 - rpm -qpc rpm - - # 查看软件包的依赖关系 - rpm -qpR xxx.rpm - -rpm ---- - -.. code:: shell - - rpm -ivh xxx.rpm-------安装软件 - -    -e-----------------卸载指定软件,不能是安装包名称 - -    -q-----------------查询指定软件是否安装,跟软件名称 - -    -qi----------------查询已经安装的软件的信息 - -   -ql-----------------查询已经安装的软件中包含什么样的内容 - -   -qf /etc/fstab ----查询这个文件是由哪个安装包产生的 - -   -qc-----------------查询已经安装的软件中包含的配置文件 - -   -qd-----------------查询已经安装的软件中包含的doc文件 - -   -q --scripts ------查询软件的脚本内容 - -   -Uvh----------------升级软件 - -   -Fvh----------------刷新 - -   -p------------------对于未安装的软件包查询信息,需要额外加此选项 - -   -qip----------------查询一个尚款安装的安装包的信息 - -   -qpc----------------查询一个尚未安装的安装包的配置文件 - -   -qpd----------------查询一个尚未安装的安装包的doc文件 - -   -qpl----------------查询一个尚未安装的安装包包含的信息 - -重建 rpmdb - -.. code:: shell - - # 删除rpm数据文件 - rm -f /var/lib/rpm/__db.00* - - # 重建rpm数据文件 - rpm –rebuilddb - -查看一个包的安装时间 - -:: - - [root@ws_compute01 ~]# rpm -qi|grep zlib-1.2.7-17.el7.i686 - rpm: no arguments given for query - [root@ws_compute01 ~]# rpm -qi zlib-1.2.7-17.el7.i686 - Name : zlib - Version : 1.2.7 - Release : 17.el7 - Architecture: i686 - Install Date: Tue 13 Aug 2019 10:22:41 AM CST - Group : System Environment/Libraries - Size : 184798 - License : zlib and Boost - Signature : RSA/SHA256, Mon 21 Nov 2016 05:05:03 AM CST, Key ID 24c6a8a7f4a80eb5 - Source RPM : zlib-1.2.7-17.el7.src.rpm - Build Date : Sun 06 Nov 2016 02:07:22 AM CST - Build Host : worker1.bsys.centos.org - Relocations : (not relocatable) - Packager : CentOS BuildSystem - Vendor : CentOS - URL : http://www.zlib.net/ - Summary : The compression and decompression library - Description : - Zlib is a general-purpose, patent-free, lossless data compression - library which is used by many different programs. - -查看一个包在系统已配的源里都有哪些版本 - -:: - - [root@ws_compute01 ~]# yum provides - Loaded plugins: fastestmirror, versionlock - Loading mirror speeds from cached hostfile - * epel: hkg.mirror.rackspace.com - 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines - Repo : ansible-master - - - 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines - Repo : libvirt-3.9.0 - - - 1:libguestfs-tools-1.28.1-1.55.el7.centos.noarch : System administration tools for virtual machines - Repo : wsbase - - - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : ansible-master - - - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : libvirt-4.5.0 - - - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : wsupdates - - - 1:libguestfs-tools-1.38.2-12.el7.noarch : System administration tools for virtual machines - Repo : @libvirt-4.5.0 - |image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png From 83e9b8df94eb03e51d602020b4654d2a68ce24b1 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 30 Jun 2020 22:53:22 +0800 Subject: [PATCH 097/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c07/c07_22.md | 184 +++++++++++++++++++++++++++++++++++++++++ source/c07/c07_22.rst | 185 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 source/c07/c07_22.md create mode 100644 source/c07/c07_22.rst diff --git a/source/c07/c07_22.md b/source/c07/c07_22.md new file mode 100644 index 0000000..f65567e --- /dev/null +++ b/source/c07/c07_22.md @@ -0,0 +1,184 @@ +# 7.22 rpm 命令详细用法 + +## 安装 + +- `-i`:表示安装 +- `-v`:查看更详细的安装信息 +- `-h`:以安装信息栏显示安装进度 + +```shell +$ rpm -ivh xxx.rpm +``` + +也可以指定 rpm 包的在线网址直接安装 + +```shell +$ rpm -ivh http://website.com/path/xxx.rpm +``` + +## 更新 + +直接更新安装 rpm 包,不管你之前有没有安装过 + +```shell +$ rpm -Uvh xxx.rpm +``` + +只更新已安装过的包 + +```shell +$ rpm -Fvh xxx.rpm +``` + +## 查询 + +查询软件是否安装 + +```shell +$ rpm -q xxx +``` + +查询已安装的软件 + +```shell +$ rpm -qa +``` + +查询已经安装的软件的信息 + +```shell +$ rpm -qi xxx +``` + +查询已经安装的软件中包含什么样的内容 + +```shell +$ rpm -ql +``` + +查询这个文件是由哪个安装包产生的 + +```shell +$ rpm -qf /etc/nova/nova.conf +``` + +查询已经安装的软件中包含的doc文件 + +```shell +$ rpm -qc xxx +``` + +查询已经安装的软件中包含的doc文件 + +```shell +$ rpm -qd +``` + +查询软件的脚本内容 + +```shell +$ rpm -q --scripts xxx +``` + +查询一个软件有关的依赖软件所包含的文件 + +```shell +$ rpm -qR xxx +``` + + + +以上都是查询已安装软件参数 + +如果要查询未安装的软件,只需要加 `-p` 即可 + + + +查询一个尚款安装的安装包的信息 + +```shell +$ rpm -qip xxx +``` + +查询一个尚未安装的安装包的配置文件 + +```shell +$ rpm -qpc xxx +``` + +查询一个尚未安装的安装包的doc文件 + +```shell +$ rpm -qpd xxx +``` + +查询一个尚未安装的安装包包含的信息 + +```shell +$ rpm -qpl xxx +``` + +## 卸载 + +卸载指定软件 + +```shell +$ rpm -e xxx +``` + + + +## 验证 + +查看一个软件里所包含的文件是否被修改过,只有被修改过才会被列出来 + +```shell +$ rpm -V xxx +``` + +加 `-a` 就表示查看所有的软件,后面不用再跟软件名 + +```shell +$ rpm -Va +``` + +加 `-p` 表示查看未安装软件,因此后面跟 rpm 包文件名 + +```shell +$ rpm -Vp xxx.rpm +``` + +查看某个文件是否被修改过 + +```shell +$ rpm -Vf /etc/path/file +``` + + + +## rpmdb + + +重建 rpmdb + +```shell +# 删除rpm数据文件 +rm -f /var/lib/rpm/__db.00* + +# 重建rpm数据文件 +rpm –rebuilddb +``` + + + +## 可选参数 + +- `--nodeps`:当软件由于依赖问题而导致无法安装和卸载时,可以加上这个参数强制进行安装或卸载 +- `--replacefiles`:当某些文件(是由该软件提供的)已经存在于机器上时,再次安装时,会提示文件已存在,此时加上这个参数 就可以直接覆盖。 +- `--replacepkgs`:当你使用 rpm -ivh *.rpm 一批软件时,如果有的包已经安装过了,此时加上这个参数就会直接重新安装,而不会因失败而退出 +- `--force`:--replacefiles 和 --replacepkgs 的综合体。 +- `--test`:测试一下能不能安装,有没有依赖问题,而不会真正去安装它。 +- `--justdb`:当 RPM 数据库损坏或者某些原因产生错误时,可使用这个参数 更新软件在数据库中的相关信息 +- `--nosignature`:跳过数字证书的检查,直接安装 +- `--prefix 新路径`:将软件安装在指定的路径 +- `--noscripts`:安装时,忽略某些命令的执行 \ No newline at end of file diff --git a/source/c07/c07_22.rst b/source/c07/c07_22.rst new file mode 100644 index 0000000..ccfc5bb --- /dev/null +++ b/source/c07/c07_22.rst @@ -0,0 +1,185 @@ +7.22 rpm 命令详细用法 +===================== + +安装 +---- + +- ``-i``\ :表示安装 +- ``-v``\ :查看更详细的安装信息 +- ``-h``\ :以安装信息栏显示安装进度 + +.. code:: shell + + $ rpm -ivh xxx.rpm + +也可以指定 rpm 包的在线网址直接安装 + +.. code:: shell + + $ rpm -ivh http://website.com/path/xxx.rpm + +更新 +---- + +直接更新安装 rpm 包,不管你之前有没有安装过 + +.. code:: shell + + $ rpm -Uvh xxx.rpm + +只更新已安装过的包 + +.. code:: shell + + $ rpm -Fvh xxx.rpm + +查询 +---- + +查询软件是否安装 + +.. code:: shell + + $ rpm -q xxx + +查询已安装的软件 + +.. code:: shell + + $ rpm -qa + +查询已经安装的软件的信息 + +.. code:: shell + + $ rpm -qi xxx + +查询已经安装的软件中包含什么样的内容 + +.. code:: shell + + $ rpm -ql + +查询这个文件是由哪个安装包产生的 + +.. code:: shell + + $ rpm -qf /etc/nova/nova.conf + +查询已经安装的软件中包含的doc文件 + +.. code:: shell + + $ rpm -qc xxx + +查询已经安装的软件中包含的doc文件 + +.. code:: shell + + $ rpm -qd + +查询软件的脚本内容 + +.. code:: shell + + $ rpm -q --scripts xxx + +查询一个软件有关的依赖软件所包含的文件 + +.. code:: shell + + $ rpm -qR xxx + +以上都是查询已安装软件参数 + +如果要查询未安装的软件,只需要加 ``-p`` 即可 + +查询一个尚款安装的安装包的信息 + +.. code:: shell + + $ rpm -qip xxx + +查询一个尚未安装的安装包的配置文件 + +.. code:: shell + + $ rpm -qpc xxx + +查询一个尚未安装的安装包的doc文件 + +.. code:: shell + + $ rpm -qpd xxx + +查询一个尚未安装的安装包包含的信息 + +.. code:: shell + + $ rpm -qpl xxx + +卸载 +---- + +卸载指定软件 + +.. code:: shell + + $ rpm -e xxx + +验证 +---- + +查看一个软件里所包含的文件是否被修改过,只有被修改过才会被列出来 + +.. code:: shell + + $ rpm -V xxx + +加 ``-a`` 就表示查看所有的软件,后面不用再跟软件名 + +.. code:: shell + + $ rpm -Va + +加 ``-p`` 表示查看未安装软件,因此后面跟 rpm 包文件名 + +.. code:: shell + + $ rpm -Vp xxx.rpm + +查看某个文件是否被修改过 + +.. code:: shell + + $ rpm -Vf /etc/path/file + +rpmdb +----- + +重建 rpmdb + +.. code:: shell + + # 删除rpm数据文件 + rm -f /var/lib/rpm/__db.00* + + # 重建rpm数据文件 + rpm –rebuilddb + +可选参数 +-------- + +- ``--nodeps``\ :当软件由于依赖问题而导致无法安装和卸载时,可以加上这个参数强制进行安装或卸载 +- ``--replacefiles``\ :当某些文件(是由该软件提供的)已经存在于机器上时,再次安装时,会提示文件已存在,此时加上这个参数 + 就可以直接覆盖。 +- ``--replacepkgs``\ :当你使用 rpm -ivh \*.rpm + 一批软件时,如果有的包已经安装过了,此时加上这个参数就会直接重新安装,而不会因失败而退出 +- ``--force``\ :–replacefiles 和 –replacepkgs 的综合体。 +- ``--test``\ :测试一下能不能安装,有没有依赖问题,而不会真正去安装它。 +- ``--justdb``\ :当 RPM + 数据库损坏或者某些原因产生错误时,可使用这个参数 + 更新软件在数据库中的相关信息 +- ``--nosignature``\ :跳过数字证书的检查,直接安装 +- ``--prefix 新路径``\ :将软件安装在指定的路径 +- ``--noscripts``\ :安装时,忽略某些命令的执行 From f95adade8ff69b8eec2588d87fd61e05ccdc98e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Wed, 1 Jul 2020 18:16:19 +0800 Subject: [PATCH 098/147] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E7=AB=A0?= =?UTF-8?q?=EF=BC=9AQoS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c08/c09_16.md | 204 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 source/c08/c09_16.md diff --git a/source/c08/c09_16.md b/source/c08/c09_16.md new file mode 100644 index 0000000..b5e9e33 --- /dev/null +++ b/source/c08/c09_16.md @@ -0,0 +1,204 @@ +# 9.16 详解 Neutron 的 QoS + +QoS 的全称是 Quality of Service,也就是服务质量。 + +Neutron 支持的 [QoS](https://docs.openstack.org/neutron/latest/admin/config-qos.html) 类型有 + +- bandwidth_limit(带宽限速):实现带宽速速 +- DSCP(差分服务代码点):给网络流量包添加一个 DSCP 标记,以此实现流量的优先级 +- minimum_bandwidth:暂时没用过 + + + +## 1. 开户 QoS 支持 + +在控制节点上执行两条命令修改配置 + +```shell +$ openstack-config --set /etc/neutron/neutron.conf DEFAULT service_plugins neutron.services.qos.qos_plugin.QoSPlugin + +$ openstack-config --set /etc/neutron/plugins/ml2/ml2_conf.ini ml2 extension_drivers port_security,qos +``` + +然后重启 neutron-server + +```shell +$ crm resource cleanup openstack-neutron-server-clone +``` + +在计算节点上执行命令修改配置 + +```shell +$ openstack-config --set /etc/neutron/plugins/ml2/openvswitch_agent.ini agent extensions qos +``` + +然后重启对应的 neutron-agent 服务 + +```shell +$ systemctl restart neutron-openvswitch-agent +``` + + + + + +## DSCP 差分服务代码点 + +DSCP 大小为 6 个 bit,也就是最大可表示 64 个分类。 + +但在 Neutron 里可用的标记只有这些 + +``` +0, 8, 10, 12, 14, 16, 18, 20, +22, 24, 26, 28, 30, 32, 34, 36, +38, 40, 46, 48, 56 +``` + +可以看到,它们都是0-56 间的偶数,但是排除 2-6, 42, 44 和 50-54。 + +![](http://image.iswbm.com/20200701155207.png) + +### 原理 + +在 IP 协议分组里有一个 ToS(服务类型) 的字段,就是用来表示 ToS 的。 + +ToS 字段,总共 8 个 bit + +![](http://img.wandouip.com/crawler/article/2019411/546f47120fa14a2a1cfc44c9e8a48e71) + +**前面 3 个 bit** + +为优选权子字段,现在已经废弃,这个字段默认值是000,从wireshark抓包结果看,表示的是: + +![003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明](https://s4.51cto.com/images/blog/201804/25/ede8e1de3c98c2fdfeb044cb0cf74034.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) + +但是在某些协议中仍然是有用的,比如 OSPFv2 协议 + +![](http://image.iswbm.com/20200701170223.png) + +关于 Precedence (优先级),有如下几种 + +```shell +111 - Network Control +110 - Internetwork Control +101 - CRITIC/ECP +100 - Flash Override +011 - Flash +010 - Immediate +001 - Priority +000 – Routine +``` + +**中间 4 个bit** + +这四个 bit 组合在一起,表示了该数据报对应的服务类别,这个应用层的服务类别是不同的。这里所说的服务类别,是指: + +``` +1000 -- minimize delay 最小延迟 +0100 -- maximize throughput 最大吞吐量 +0010 -- maximize reliability 最高可靠性 +0001 -- minimize monetary cost 最小费用 +0000 -- normal service 一般服务 +``` + +IP首部中的ToS字段,只能表示一种服务类别,也就是:这4bit字段中,最多只能有一个bit字段为1。 + +看下不同应用下该4bit字段对应的值: +![003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明](https://s4.51cto.com/images/blog/201804/25/3c42c64b7240ef12b991f69644a145ac.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) +翻译过来就是: +![003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明](https://s4.51cto.com/images/blog/201804/25/4f03b09e8081d8fc7073f29870bc1c95.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) +**最小延迟**,对应于对延迟敏感的应用,如telnet和人login等。 +**最大吞吐量**,对应于对吞吐量要求比较高的应用,如FTP文件应用,对文件传输吞吐量有比较高的要求。 +**最高可靠性**,对网络传输可靠性要求高的应用,如使用SNMP的应用、路由协议等等。 +**最小费用**,如NNTP这种用户网络新闻等。 + +**最后 1 个bit** + +这个1bit末尾,没有被使用,必须强制设置为0 + + + +最后,很重要的一点,只有当**网络设备(如交换机等)能够支持**(能够识别IP首部中的ToS字段)识别ToS字段时,这给字段设置才有意义。 + +### 创建 policy + +```shell +$ neutron qos-policy-create qos-dscp --shared +Created a new policy: ++-----------------+--------------------------------------+ +| Field | Value | ++-----------------+--------------------------------------+ +| created_at | 2020-07-01T06:43:23Z | +| description | | +| id | ee7e7a83-c67d-4f27-b77c-3345553e5abe | +| name | qos-dscp | +| project_id | 2ac17c7c792d45eaa764c30bac37fad9 | +| revision_number | 1 | +| rules | | +| shared | True | +| tenant_id | 2ac17c7c792d45eaa764c30bac37fad9 | +| updated_at | 2020-07-01T06:43:23Z | ++-----------------+--------------------------------------+ +``` + +### 创建 rule + +创建时需要指定 QOS_POLICY,创建完后,就会自动添加到 QOS_POLICY + +```shell +$ neutron qos-dscp-marking-rule-create --dscp-mark 14 ee7e7a83-c67d-4f27-b77c-3345553e5abe +Created a new dscp_marking_rule: ++-----------+--------------------------------------+ +| Field | Value | ++-----------+--------------------------------------+ +| dscp_mark | 14 | +| id | 1d045cf3-eb31-440b-9a74-a9d5fea6a7e0 | ++-----------+--------------------------------------+ + +``` + +### 绑定policy到 port + +```shell +$ neutron port-update --qos-policy qos-dscp +``` + +### 关闭 QoS + +```shell +$ neutron port-update --no-qos-policy +``` + +### 查看规则 + +过滤出 tos 后就能看到 ToS(Type of Service) 的值 + +```shell +$ ovs-ofctl dump-flows br-int | grep tos +``` + +然后再对照这个表,找到对应的 DSCP 的 decimal ,如果 tos 是 64,那么 DSCP mark 就是 16,其实除以 4 就可以了,也不用对照表。其中这个 16 需要跟交换上支持的一样。 + +![](http://image.iswbm.com/20200701155207.png) + +### 其他 + +DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时候将其绑定到 port 上就可以。 + +## 2. bandwidth_limit 带宽限速 + + + +## 3. 参考文章 + +- https://blog.51cto.com/mangguostudy/2107799 + +- https://www.jianshu.com/p/4b5cc3845f2c + +- https://blog.csdn.net/u011641885/article/details/45640313 + + + +--- + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file From bac2082555a8cea1e697db28622bba2448480493 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 4 Jul 2020 16:00:57 +0800 Subject: [PATCH 099/147] Add disqus --- requirements.txt | 1 + source/conf.py | 13 +++++++++++++ source/thanks.rst | 1 + 3 files changed, 15 insertions(+) diff --git a/requirements.txt b/requirements.txt index 5854fcd..53e9ee4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,3 +31,4 @@ sphinxcontrib-serializinghtml==1.1.3 tornado==6.0.3 urllib3==1.25.3 watchdog==0.9.0 +sphinxcontrib-disqus==1.1.0 diff --git a/source/conf.py b/source/conf.py index 1b0c693..7b8538e 100755 --- a/source/conf.py +++ b/source/conf.py @@ -130,3 +130,16 @@ 'js/readmore.js', 'js/baidutongji.js', ] + +# General configuration. +author = '王炳明' +copyright = '2020, Go编程时光' +exclude_patterns = ['_build'] +extensions = ['sphinxcontrib.disqus'] # Add to this list. +master_doc = 'index' +project = 'GolangCodingTime' +release = '1.0' +version = '1.0' + +# Options for extensions. +disqus_shortname = 'iswbm' # Add this line to conf.py. diff --git a/source/thanks.rst b/source/thanks.rst index 41808de..0334929 100644 --- a/source/thanks.rst +++ b/source/thanks.rst @@ -22,6 +22,7 @@ 2019.7.4 @魏朝阳:提出在《并发编程》中三处笔误,现已更正! +.. disqus :: -------------- From a873deb5fbf87fc4919509514a7dd9843c6240e5 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 4 Jul 2020 16:30:31 +0800 Subject: [PATCH 100/147] fixed: disqus bug --- requirements.txt | 6 ++++++ source/conf.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/requirements.txt b/requirements.txt index 53e9ee4..f0b035b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,3 +32,9 @@ tornado==6.0.3 urllib3==1.25.3 watchdog==0.9.0 sphinxcontrib-disqus==1.1.0 +sphinxcontrib-applehelp==1.0.1 +sphinxcontrib-devhelp==1.0.1 +sphinxcontrib-htmlhelp==1.0.2 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.2 +sphinxcontrib-serializinghtml==1.1.3 diff --git a/source/conf.py b/source/conf.py index 7b8538e..77d55e5 100755 --- a/source/conf.py +++ b/source/conf.py @@ -132,6 +132,13 @@ ] # General configuration. +with open("/home/docs/checkouts/readthedocs.org/user_builds/pythoncodingtime/envs/latest/lib/python3.7/site-packages/sphinxcontrib/disqus.py", "r") as file: + content = file.read() + content=content.replace("sphinx.application", "sphinx.errors") + +with open("/home/docs/checkouts/readthedocs.org/user_builds/pythoncodingtime/envs/latest/lib/python3.7/site-packages/sphinxcontrib/disqus.py", "r") as file: + file.write(content) + author = '王炳明' copyright = '2020, Go编程时光' exclude_patterns = ['_build'] From 45eddd313dbaa8640c9720c293652c2b047e8eb6 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 4 Jul 2020 16:34:20 +0800 Subject: [PATCH 101/147] fixed bug --- source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/conf.py b/source/conf.py index 77d55e5..68169c4 100755 --- a/source/conf.py +++ b/source/conf.py @@ -136,7 +136,7 @@ content = file.read() content=content.replace("sphinx.application", "sphinx.errors") -with open("/home/docs/checkouts/readthedocs.org/user_builds/pythoncodingtime/envs/latest/lib/python3.7/site-packages/sphinxcontrib/disqus.py", "r") as file: +with open("/home/docs/checkouts/readthedocs.org/user_builds/pythoncodingtime/envs/latest/lib/python3.7/site-packages/sphinxcontrib/disqus.py", "w") as file: file.write(content) author = '王炳明' From adca58d5c4d02d825344eabb6b555740bd0f070a Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 4 Jul 2020 16:38:43 +0800 Subject: [PATCH 102/147] updatae --- source/thanks.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/thanks.rst b/source/thanks.rst index 0334929..b3f0b61 100644 --- a/source/thanks.rst +++ b/source/thanks.rst @@ -23,6 +23,7 @@ @魏朝阳:提出在《并发编程》中三处笔误,现已更正! .. disqus :: + :disqus_identifier: thanks -------------- From 5731edc89fd9b5a3ec29930e55669d3086464897 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 4 Jul 2020 20:56:02 +0800 Subject: [PATCH 103/147] Update --- source/c01/c01_30.md | 32 +++++- source/c01/c01_30.rst | 34 +++++++ source/c04/c04_03.md | 27 ++++- source/c04/c04_03.rst | 37 ++++++- source/c04/c04_18.md | 26 +++++ source/c04/c04_18.rst | 49 ++++++++-- source/c08/c09_16.rst | 222 ++++++++++++++++++++++++++++++++++++++++++ source/c09/c09_03.md | 10 ++ source/c09/c09_03.rst | 7 ++ source/c09/c09_04.md | 4 + source/c09/c09_04.rst | 5 + source/c10/c10_03.md | 2 +- source/c10/c10_03.rst | 2 +- 13 files changed, 439 insertions(+), 18 deletions(-) create mode 100644 source/c08/c09_16.rst create mode 100644 source/c09/c09_04.md create mode 100644 source/c09/c09_04.rst diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md index 27e122a..1839db7 100644 --- a/source/c01/c01_30.md +++ b/source/c01/c01_30.md @@ -34,4 +34,34 @@ 在服务端网页编程里,重点介绍了 Django -![](http://image.iswbm.com/20200525080715.png) \ No newline at end of file +![](http://image.iswbm.com/20200525080715.png) + + + +## Python3 源码剖析 + +网站链接:https://flaggo.github.io/python3-source-code-analysis/ + +![](http://image.iswbm.com/image-20200701123010074.png) + + + +## Linux 手册 + +网站链接:https://man.linuxde.net/ + +![](http://image.iswbm.com/image-20200704204307530.png) + +## 每天一个linux命令 + +网站链接:www.cnblogs.com/peida/archive/2012/12/05/2803591.html + +## 实验楼:Linux 基础入门 + +网站链接:https://www.shiyanlou.com/courses/1 + +![](http://image.iswbm.com/20200704204506.png) + +网站链接:https://www.shiyanlou.com/courses/68 + +![](http://image.iswbm.com/20200704204558.png) \ No newline at end of file diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst index 84ea716..1793161 100644 --- a/source/c01/c01_30.rst +++ b/source/c01/c01_30.rst @@ -37,10 +37,44 @@ Django Web 框架 |image5| +Python3 源码剖析 +---------------- + +网站链接:https://flaggo.github.io/python3-source-code-analysis/ + +|image6| + +Linux 手册 +---------- + +网站链接:https://man.linuxde.net/ + +|image7| + +每天一个linux命令 +----------------- + +网站链接:www.cnblogs.com/peida/archive/2012/12/05/2803591.html + +实验楼:Linux 基础入门 +---------------------- + +网站链接:https://www.shiyanlou.com/courses/1 + +|image8| + +网站链接:https://www.shiyanlou.com/courses/68 + +|image9| + .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20200104144109.png .. |image2| image:: http://image.python-online.cn/20200112210558.png .. |image3| image:: http://image.iswbm.com/20200508201333.png .. |image4| image:: http://image.iswbm.com/20200525080531.png .. |image5| image:: http://image.iswbm.com/20200525080715.png +.. |image6| image:: http://image.iswbm.com/image-20200701123010074.png +.. |image7| image:: http://image.iswbm.com/image-20200704204307530.png +.. |image8| image:: http://image.iswbm.com/20200704204506.png +.. |image9| image:: http://image.iswbm.com/20200704204558.png diff --git a/source/c04/c04_03.md b/source/c04/c04_03.md index 73f8ba3..bb9a984 100644 --- a/source/c04/c04_03.md +++ b/source/c04/c04_03.md @@ -2,7 +2,7 @@ ![](http://image.iswbm.com/20200602135014.png) ---- +《网络是怎样连接的》 豆瓣评分:9.1 10个优秀的程序员里,有9个人都有写博客的习惯。这是非常好的习惯,值得每个程序员,投入时间和精力去坚持做下去。 @@ -26,7 +26,8 @@ ## 4.3.1 成品展示 -以我的博客(`python-online.cn`)为例,先给大家展示一下。 + +以我的博客(`python.iswbm.com`)为例,先给大家展示一下。 这是首页。显示了你所有的文章索引。 ![](http://image.python-online.cn/20190511160523.png) @@ -48,9 +49,11 @@ ## 4.3.2 安装Sphinx + 安装之前,请确认下Python版本。我这里使用的是Python 2.7.14,其他版本请自行尝试噢,Python3.6好像有些坑,你需要踩一下。 安装Python工具包 + ``` $ pip install sphinx sphinx-autobuild sphinx_rtd_theme -i https://pypi.douban.com/simple/ ``` @@ -215,6 +218,7 @@ The HTML pages are in build\html. ## 4.3.5 托管项目 + 看到网页的那一刻是不是相当激动。 不过别激动,这只是本地的,我们需要将其发布在线上。 @@ -234,6 +238,7 @@ build/ ## 4.3.6 发布上线 + 托管完成后,我们要发布它,让别人也可以使用公网访问。 你需要先去 Read the Docs 注册下帐号。 @@ -272,7 +277,7 @@ pandoc -V mainfont="SimSun" -f markdown -t rst hello.md -o hello.rst ## 4.3.7 自定义插件 -之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的搭建教程,完成了自己的个人站点。 +之前有不少同学看过我的个人博客(http://python.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 使用这个方法搭建的站点,一直有一个痛点,就是无法自定义页面,自由度非常的低(和 WordPress 真的是没法比,因为这两种产品定位本身就不一样。) @@ -451,7 +456,21 @@ html_js_files = [ 数据真的非常全面,你可以知道,访客都是从哪里访问(直接访问,Google等),每篇文章的点击量(你就知道哪篇是爆款?),每天有多少老访问客,多少新访客等等,更多维度的数据你可以自己去体验一下。 +##第三个插件:评论系统 + +先到这个[网站](http://disqus.com/admin/create)去注册一个 disqus 帐号,我使用了 gmail 帐号进行注册 + +![](http://image.iswbm.com/image-20200704154427375.png) + +然后根据指引填写好资料 + +![image-20200704154846176](/Users/MING/Library/Application Support/typora-user-images/image-20200704154846176.png) + +选择基础版 + +![image-20200704155335679](/Users/MING/Library/Application Support/typora-user-images/image-20200704155335679.png) +![image-20200704155410411](/Users/MING/Library/Application Support/typora-user-images/image-20200704155410411.png) ## 附录:参考文档 @@ -463,4 +482,4 @@ html_js_files = [ --- -![](http://image.iswbm.com/20200607174235.png) +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 6744df9..890f43a 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -3,7 +3,7 @@ |image0| --------------- +《网络是怎样连接的》 豆瓣评分:9.1 10个优秀的程序员里,有9个人都有写博客的习惯。这是非常好的习惯,值得每个程序员,投入时间和精力去坚持做下去。 @@ -28,7 +28,7 @@ 4.3.1 成品展示 -------------- -以我的博客(\ ``python-online.cn``)为例,先给大家展示一下。 +以我的博客(\ ``python.iswbm.com``)为例,先给大家展示一下。 这是首页。显示了你所有的文章索引。 |image1| @@ -276,7 +276,7 @@ source:raw-latex:`\index`.rst,千万要注意中间的空行不可忽略。 4.3.7 自定义插件 ---------------- -之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的搭建教程,完成了自己的个人站点。 +之前有不少同学看过我的个人博客(http://python.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 使用这个方法搭建的站点,一直有一个痛点,就是无法自定义页面,自由度非常的低(和 WordPress 真的是没法比,因为这两种产品定位本身就不一样。) @@ -471,6 +471,32 @@ Python 3.x ,所以这里的代码也要对应修改。 数据真的非常全面,你可以知道,访客都是从哪里访问(直接访问,Google等),每篇文章的点击量(你就知道哪篇是爆款?),每天有多少老访问客,多少新访客等等,更多维度的数据你可以自己去体验一下。 +##第三个插件:评论系统 + +先到这个\ `网站 `__\ 去注册一个 disqus +帐号,我使用了 gmail 帐号进行注册 + +|image19| + +然后根据指引填写好资料 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200704154846176.png + :alt: image-20200704154846176 + + image-20200704154846176 + +选择基础版 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200704155335679.png + :alt: image-20200704155335679 + + image-20200704155335679 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200704155410411.png + :alt: image-20200704155410411 + + image-20200704155410411 + 附录:参考文档 -------------- @@ -480,7 +506,7 @@ Python 3.x ,所以这里的代码也要对应修改。 -------------- -|image19| +|image20| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190511160523.png @@ -501,5 +527,6 @@ Python 3.x ,所以这里的代码也要对应修改。 .. |image16| image:: http://image.python-online.cn/20191016205653.png .. |image17| image:: http://image.python-online.cn/20191015225652.png .. |image18| image:: http://image.python-online.cn/20191016211012.png -.. |image19| image:: http://image.iswbm.com/20200607174235.png +.. |image19| image:: http://image.iswbm.com/image-20200704154427375.png +.. |image20| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 8bc86a9..50ee7ef 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -87,7 +87,26 @@ command + space 如果不仍想让访达窗口保持在最前面,就按住 command 键 ``` +设置我自定义的系统偏好设置 +![](http://image.iswbm.com/image-20200704192441091.png) + +设置打开访达快捷键 + +1. 打开 『自动操作』 +2. 新建文稿 +3. 选择『服务』 或者 『自动操作』(因为不同版本的 macOS名字不同) +4. 进行如下设置 + +![](http://image.iswbm.com/image-20200704194215498.png) + +5. 取消勾选:因为我要设置的快捷键与它冲突 + +![、](http://image.iswbm.com/image-20200704195011274.png) + +6. 设置快捷键 + +![](http://image.iswbm.com/image-20200704195122336.png) ## 4.18.4 系统设置 @@ -250,7 +269,14 @@ open . open ~/Code ``` +搜索时,优先搜索当前文件夹:访达的偏好设置 + +![](http://image.iswbm.com/image-20200704192031119.png) + +此时如果你想要搜索电脑全局,那么有两种方法 +1. Command + option + 空格 +2. command + 空格 ## 4.18.7 使用小鹤双拼 diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 219419b..55620d7 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -92,6 +92,30 @@ 直接拖动图片 如果不仍想让访达窗口保持在最前面,就按住 command 键 +设置我自定义的系统偏好设置 + +|image1| + +设置打开访达快捷键 + +1. 打开 『自动操作』 +2. 新建文稿 +3. 选择『服务』 或者 『自动操作』(因为不同版本的 macOS名字不同) +4. 进行如下设置 + +|image2| + +5. 取消勾选:因为我要设置的快捷键与它冲突 + +.. figure:: http://image.iswbm.com/image-20200704195011274.png + :alt: 、 + + 、 + +6. 设置快捷键 + +|image3| + 4.18.4 系统设置 --------------- @@ -103,7 +127,7 @@ finder的显示 -|image1| +|image4| `防止电脑温度过高 `__ @@ -119,7 +143,7 @@ finder的显示 3. 打开「活动监视器」(Alfred就可以打开),杀掉暂没用且cpu使用率最高的程序 - |image2| + |image5| 4. MacBook Pro CPU 温度在 5、60℃ 的时候,风扇会转到两三千转每分钟,只有 CPU 温度达到 70 @@ -261,6 +285,15 @@ finder的显示 # 在指定目录打开 open ~/Code +搜索时,优先搜索当前文件夹:访达的偏好设置 + +|image6| + +此时如果你想要搜索电脑全局,那么有两种方法 + +1. Command + option + 空格 +2. command + 空格 + 4.18.7 使用小鹤双拼 ------------------- @@ -329,10 +362,14 @@ finder的显示 -------------- -|image3| +|image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190810161513.png -.. |image2| image:: http://image.python-online.cn/20190810162315.png -.. |image3| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/image-20200704192441091.png +.. |image2| image:: http://image.iswbm.com/image-20200704194215498.png +.. |image3| image:: http://image.iswbm.com/image-20200704195122336.png +.. |image4| image:: http://image.python-online.cn/20190810161513.png +.. |image5| image:: http://image.python-online.cn/20190810162315.png +.. |image6| image:: http://image.iswbm.com/image-20200704192031119.png +.. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c09_16.rst b/source/c08/c09_16.rst new file mode 100644 index 0000000..696e8d5 --- /dev/null +++ b/source/c08/c09_16.rst @@ -0,0 +1,222 @@ +9.16 详解 Neutron 的 QoS +======================== + +QoS 的全称是 Quality of Service,也就是服务质量。 + +Neutron 支持的 +`QoS `__ +类型有 + +- bandwidth_limit(带宽限速):实现带宽速速 +- DSCP(差分服务代码点):给网络流量包添加一个 DSCP + 标记,以此实现流量的优先级 +- minimum_bandwidth:暂时没用过 + +1. 开户 QoS 支持 +---------------- + +在控制节点上执行两条命令修改配置 + +.. code:: shell + + $ openstack-config --set /etc/neutron/neutron.conf DEFAULT service_plugins neutron.services.qos.qos_plugin.QoSPlugin + + $ openstack-config --set /etc/neutron/plugins/ml2/ml2_conf.ini ml2 extension_drivers port_security,qos + +然后重启 neutron-server + +.. code:: shell + + $ crm resource cleanup openstack-neutron-server-clone + +在计算节点上执行命令修改配置 + +.. code:: shell + + $ openstack-config --set /etc/neutron/plugins/ml2/openvswitch_agent.ini agent extensions qos + +然后重启对应的 neutron-agent 服务 + +.. code:: shell + + $ systemctl restart neutron-openvswitch-agent + +DSCP 差分服务代码点 +------------------- + +DSCP 大小为 6 个 bit,也就是最大可表示 64 个分类。 + +但在 Neutron 里可用的标记只有这些 + +:: + + 0, 8, 10, 12, 14, 16, 18, 20, + 22, 24, 26, 28, 30, 32, 34, 36, + 38, 40, 46, 48, 56 + +可以看到,它们都是0-56 间的偶数,但是排除 2-6, 42, 44 和 50-54。 + +|image0| + +原理 +~~~~ + +在 IP 协议分组里有一个 ToS(服务类型) 的字段,就是用来表示 ToS 的。 + +ToS 字段,总共 8 个 bit + +|image1| + +**前面 3 个 bit** + +为优选权子字段,现在已经废弃,这个字段默认值是000,从wireshark抓包结果看,表示的是: + +.. figure:: https://s4.51cto.com/images/blog/201804/25/ede8e1de3c98c2fdfeb044cb0cf74034.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= + :alt: 003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明 + + 003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明 + +但是在某些协议中仍然是有用的,比如 OSPFv2 协议 + +|image2| + +关于 Precedence (优先级),有如下几种 + +.. code:: shell + + 111 - Network Control + 110 - Internetwork Control + 101 - CRITIC/ECP + 100 - Flash Override + 011 - Flash + 010 - Immediate + 001 - Priority + 000 – Routine + +**中间 4 个bit** + +这四个 bit +组合在一起,表示了该数据报对应的服务类别,这个应用层的服务类别是不同的。这里所说的服务类别,是指: + +:: + + 1000 -- minimize delay 最小延迟 + 0100 -- maximize throughput 最大吞吐量 + 0010 -- maximize reliability 最高可靠性 + 0001 -- minimize monetary cost 最小费用 + 0000 -- normal service 一般服务 + +IP首部中的ToS字段,只能表示一种服务类别,也就是:这4bit字段中,最多只能有一个bit字段为1。 + +看下不同应用下该4bit字段对应的值: +|003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| +翻译过来就是: +|003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| +**最小延迟**\ ,对应于对延迟敏感的应用,如telnet和人login等。 +**最大吞吐量**\ ,对应于对吞吐量要求比较高的应用,如FTP文件应用,对文件传输吞吐量有比较高的要求。 +**最高可靠性**\ ,对网络传输可靠性要求高的应用,如使用SNMP的应用、路由协议等等。 +**最小费用**\ ,如NNTP这种用户网络新闻等。 + +**最后 1 个bit** + +这个1bit末尾,没有被使用,必须强制设置为0 + +最后,很重要的一点,只有当\ **网络设备(如交换机等)能够支持**\ (能够识别IP首部中的ToS字段)识别ToS字段时,这给字段设置才有意义。 + +创建 policy +~~~~~~~~~~~ + +.. code:: shell + + $ neutron qos-policy-create qos-dscp --shared + Created a new policy: + +-----------------+--------------------------------------+ + | Field | Value | + +-----------------+--------------------------------------+ + | created_at | 2020-07-01T06:43:23Z | + | description | | + | id | ee7e7a83-c67d-4f27-b77c-3345553e5abe | + | name | qos-dscp | + | project_id | 2ac17c7c792d45eaa764c30bac37fad9 | + | revision_number | 1 | + | rules | | + | shared | True | + | tenant_id | 2ac17c7c792d45eaa764c30bac37fad9 | + | updated_at | 2020-07-01T06:43:23Z | + +-----------------+--------------------------------------+ + +创建 rule +~~~~~~~~~ + +创建时需要指定 QOS_POLICY,创建完后,就会自动添加到 QOS_POLICY + +.. code:: shell + + $ neutron qos-dscp-marking-rule-create --dscp-mark 14 ee7e7a83-c67d-4f27-b77c-3345553e5abe + Created a new dscp_marking_rule: + +-----------+--------------------------------------+ + | Field | Value | + +-----------+--------------------------------------+ + | dscp_mark | 14 | + | id | 1d045cf3-eb31-440b-9a74-a9d5fea6a7e0 | + +-----------+--------------------------------------+ + +绑定policy到 port +~~~~~~~~~~~~~~~~~ + +.. code:: shell + + $ neutron port-update --qos-policy qos-dscp + +关闭 QoS +~~~~~~~~ + +.. code:: shell + + $ neutron port-update --no-qos-policy + +查看规则 +~~~~~~~~ + +过滤出 tos 后就能看到 ToS(Type of Service) 的值 + +.. code:: shell + + $ ovs-ofctl dump-flows br-int | grep tos + +然后再对照这个表,找到对应的 DSCP 的 decimal ,如果 tos 是 64,那么 DSCP +mark 就是 16,其实除以 4 就可以了,也不用对照表。其中这个 16 +需要跟交换上支持的一样。 + +|image5| + +其他 +~~~~ + +DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时候将其绑定到 port +上就可以。 + +2. bandwidth_limit 带宽限速 +--------------------------- + +3. 参考文章 +----------- + +- https://blog.51cto.com/mangguostudy/2107799 + +- https://www.jianshu.com/p/4b5cc3845f2c + +- https://blog.csdn.net/u011641885/article/details/45640313 + +-------------- + +|image6| + +.. |image0| image:: http://image.iswbm.com/20200701155207.png +.. |image1| image:: http://img.wandouip.com/crawler/article/2019411/546f47120fa14a2a1cfc44c9e8a48e71 +.. |image2| image:: http://image.iswbm.com/20200701170223.png +.. |003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| image:: https://s4.51cto.com/images/blog/201804/25/3c42c64b7240ef12b991f69644a145ac.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= +.. |003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| image:: https://s4.51cto.com/images/blog/201804/25/4f03b09e8081d8fc7073f29870bc1c95.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= +.. |image5| image:: http://image.iswbm.com/20200701155207.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md index 4c92424..857c3cf 100644 --- a/source/c09/c09_03.md +++ b/source/c09/c09_03.md @@ -48,3 +48,13 @@ ai-art(自拍照生成为名画艺术品):https://ai-art.tokyo/en 码力全开:https://www.maliquankai.com/ + + +## 流程图 + + + +http://asciiflow.com/ + +https://www.draw.io/ + diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index d461838..13f2eef 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -43,5 +43,12 @@ ai-art(自拍照生成为名画艺术品):https://ai-art.tokyo/en 码力全开:https://www.maliquankai.com/ +流程图 +------ + +http://asciiflow.com/ + +https://www.draw.io/ + .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c09/c09_04.md b/source/c09/c09_04.md new file mode 100644 index 0000000..540a646 --- /dev/null +++ b/source/c09/c09_04.md @@ -0,0 +1,4 @@ +# 9.4 书籍推荐 + +《[网络是怎样连接的](https://book.douban.com/subject/26941639/)》 豆瓣评分:9.1 + diff --git a/source/c09/c09_04.rst b/source/c09/c09_04.rst new file mode 100644 index 0000000..8f7ac82 --- /dev/null +++ b/source/c09/c09_04.rst @@ -0,0 +1,5 @@ +9.4 书籍推荐 +============ + +《\ `网络是怎样连接的 `__\ 》 +豆瓣评分:9.1 diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md index f715317..f8286a3 100644 --- a/source/c10/c10_03.md +++ b/source/c10/c10_03.md @@ -50,7 +50,7 @@ TCP 自身有三次握手和超时重传等机制,所以无论网络如何变 与面向字节流相对的是,UDP 的面向报文。 -面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 +面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会使IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。 diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst index b6fda4a..e63f91b 100644 --- a/source/c10/c10_03.rst +++ b/source/c10/c10_03.rst @@ -57,7 +57,7 @@ TCP 与面向字节流相对的是,UDP 的面向报文。 -面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会是IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 +面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会使IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。 From 53b7eb693fed1166f7dad1889c0068249ba621c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Sun, 5 Jul 2020 17:47:40 +0800 Subject: [PATCH 104/147] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E7=AB=A0?= =?UTF-8?q?=EF=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_07.md | 292 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 288 insertions(+), 4 deletions(-) diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md index 4bcce3b..0770f37 100644 --- a/source/c10/c10_07.md +++ b/source/c10/c10_07.md @@ -1,14 +1,298 @@ -# 10.7 网络知识扫盲:一篇文章理解 HTTP +# 10.7 网络知识扫盲:CSRF 跨域认证与JWT ![](http://image.iswbm.com/20200602135014.png) -[Wireshark: 分析 TCP 四次挥手](http://www.veryitman.com/2018/12/16/Wireshark-分析-TCP-四次挥手/) +## 1. 什么是跨域请求 -https://zhuanlan.zhihu.com/p/53374516 +要明白什么叫跨域请求,首先得知道什么叫域。 +域,是指由 `协议` + `域名` + `端口号` 组成的一个虚拟概念。 +![](http://image.iswbm.com/20200705171112.png) -[vlan学习总结](https://www.jianshu.com/p/54d5189ddb88) +如果两个域的协议、域名、端口号都一样,就称他们为同域,但是只要有其中一个不一样,就不是同域。 +那么 `跨域请求` 又是什么意思呢? + +简单来说,就是在一个域内请求了另一个域的资源,由于域不一致,会有安全隐患。 + + + +## 2. 跨域请求的安全隐患 + +有一个词,叫 CSRF (Cross-site request forgery)攻击,中文名是 `跨站请求伪造`。 + +简单来说呢,就是攻击者盗用了你的身份,以你的名义发送恶意请求,它能做的坏事有很多,比如以你的名义发邮件,发消息,购物,盗取帐号等。 + +CSRF 的实际工作原理是怎样的? + +比如现在有两个网站,A 网站是真实受信息的网站,而 B网站是危险网站。 + +当你登陆 A 网站后,浏览器会存储 A 网站服务器给你生成的 sessionid 存入 cookie,有了这个 cookie ,就拥有了你的帐号权限,以后请求资料,就不用再次登陆啦。 + +对于真实用户来说,是便利,可对于攻击者来说,却是可乘之机。 + +![](http://image.iswbm.com/20200705172457.png) + +他们可以使用各种社工学引导你点击他们的链接/网站,然后利用你的浏览器上存储的 cookie ,然后在自己的 网站B 发起对 网站A 的请求,获取一些隐私信息,做一些侵害用户权益的事情。这便是一个完整的 CSRF 攻击。 + + + +## 3. 跨域请求的安全防御 + +完成一次完整的 CSRF 攻击,只有两个步骤: + +1. 登录受信任网站A,并在本地生成Cookie +2. 在不登出A的情况下,访问危险网站B + +只要其中一个条件不满足,CSRF 攻击就不会发生。 + +由于 cookie 机制出现得比较早,且应用比较广泛,所以早期要防御 CSRF 攻击,通常都是从 第二个步骤入手。 + +很多浏览器用户对于网络安全是无意识的,因此我们不能指望通过规范用户行为来避免CSRF攻击。 + +那如何从技术手段规避一定的 CSRF 攻击的风险呢? + +1. 利用浏览器的同源策略:最基础的安全策略 +2. 对请求的来源进行验证:Referer Check +3. 使用验证码强制使用户进行交互确认,保证请求是用户发起 +4. CSRF Token +5. JSON Web Token + +以上是我知道的历史上用来抵御 CSRF 攻击的方法 + +- 有的虽然实现简单,但是不够安全 + +- 有的虽然安全,但是用户体验不好 + +- 有的虽然安全,用户体验好,但是有缺点 + + +只有一种是最优解,具体是哪一种,请继续往下看 + +### 3.1 同源策略 + +浏览器上有一个同源策略(SOP,全称 Same origin policy),它会在一定程度上禁止这种跨域请求的发生。 + +但同源策略是最基本的安全策略,对于低级的 CSRF 攻击 ,它是很有效果的。 + +可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。 + +同源策略在提升了 Web前端的安全性的同时,也牺牲了Web拓展上的灵活性。 + +设想若把html、js、css、flash,image等文件全部布置在一台服务器上,小网站这样凑活还行,大中网站如果这样做服务器根本受不了的,因此同源策略,就像是双刃剑。不过这些都是有解的。 + +### 3.2 Referer Check + +在 HTTP 协议中,有一个字段叫做 Referer,它记录了HTTP 请求的来源地址。 + +当发生 CSRF 攻击时,这个来源地址,会变成危险网站 B,因此只要在服务端校验这个 Referer 是不是和自己同一个域就可以判断这个请求是跨站请求。 + +但这种方法,也是有局限性的,在一些非主流的浏览器,或者使用了那些非常古老的浏览器版本,这个 Referer 字段,是有可能会被篡改的。 + +退一步讲,假设你使用了最安全的最新版本的浏览器,这个值无法被篡改,依旧还是有安全隐患。 + +因为有些用户出于某些隐私考虑,会在浏览器设置关闭这个 Referer 字段,也有的网站会使用一些技术手段使用请求不携带 Referer 字段。 + +因此我们发现了只要需要客户端配合的解决方案,就有无穷无尽的场景,场景越多,可能性越多,可以被突破的漏洞就越多。 + +到头后,我还是只能将希望寄托于服务端。 + +### 3.3 加验证码 + +验证码,强制用户必须与应用进行交互,才能完成最终请求。 + +在通常情况下,验证码能很好遏制CSRF攻击。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。 + +### 3.4 CSRF Token + + CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下,直接利用用户自己的 cookie 来通过安全验证。 + +所以要抵御 CSRF,关键在于要在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中(不然黑客又能拿到了)。 + +业界普遍的防御方案是使用 CSRF Token,具体流程如下: + +1. 当用户请求一个页面时,服务端会生成一个随机的 Token,并把这个 Token 存放在 Session 里(也就是内存中),然后放入HTML表单中传给浏览器; + +2. 当用户提交表单请求时,会带上这个 Token 发送给服务端 ; +3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 token 进行对比,如果是一样的,就是合法的用户请求,如果不一样,那就是非法的请求,应当拒绝。 + +在这里面有几点值得注意: + +1. 由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。 +2. 虽然也可以通过cookie 的方式将 Token 传给浏览器,但是有了以前的经验,你会发现这种方法 Token 还是会被危险网站给【利用】(注意这里是利用,而不是获取),然后发送非法的跨域请求。 + +### 3.5 新增 Header + +CSRF 跨域攻击能够成功的最根本原因,是我们使用了 cookie,因此如果要杜绝 CSRF 的产生,就要一定要抛弃 cookie。 + +上面的 CSRF Token 就是没有使用 cookie ,才得以避免了 CSRF 的攻击问题。 + + CSRF Token 虽然能解决问题,但是解决得并不完美。 + +CSRF Token 如果不想使用 cookie,就必须要将 Token 存储在服务端的内存中,这样就会面临几个问题 + +1. 服务端每生成一个 Token,都会将以 session 形式都是保存在内存中,而随着用户请求的增多,服务端的开销会明显增大。 +2. 如果网站有多个子域,分别对应不同的服务器,比如 taobao.com 后台是服务器 a,zhibo.baotao.com 后台是 服务器b, 不同子域要想使用同一个 Token,就要求所有的服务器要能共享这个 Token。一般要有一个中心节点(且应是一个集群)来存储这个Token,这样看下来,架构就变得更加复杂了。 + +想要解决这些问题,也不困难,现如今已经有很好的方案了,那就是 JWT(全称:JSON Web Token) + +使用了 JWT 后,有了哪些变化呢 + +1. 服务器只负责生成Token和校验Token,而不再存储Token +2. 将服务器的压力分摊给了所有的客户端。 + +JWT 和 CSRF Token 一样,没有使用 Cookie,那么 Token 是如何发送给服务端 的呢,是通过新增的 Header 字段:Authorization + +JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细说说关于 JWT 的内容。 + +## 4. JWT 的工作原理 + +为了让你直观感受 JWT 的工作原理,我画了下面这张图 + +1. 用户以 Web表单 的形式,将自己的用户名和密码发送到后端的接口。(HTTPS + POST); +2. 后端核对用户名和密码成功后,会计算生成JWT Payload 字符串(具体计算方法,后续会讲)返回给前端; +3. 前端收到 JWT 后,会将其保存在 localStorage 或 sessionStorage 上。 +4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization 字段。(解决XSS和XSRF问题) +5. 后端收到新请求后,如存在验证JWT的有效性(具体如何较验,后面会讲)。 +6. 验证通过后后端使用 JWT 中包含的用户信息进行其他逻辑操作,返回相应结果。 + + + +## 5. JWT 如何生成? + +JWT 其实就是一个字符串,比如下面这样 + +```shell +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ +``` + +仔细观察,会发现它里面有三个 `.` ,以 `.` 为分界,可以将 JWT 分为三部分。 + +1. **第一部分**:头部(Header) +2. **第二部分**:载荷(Payload) +3. **第三部分**:签名(Signature) + +### 5.1 头部(Header) + +JWT 的头部承载两部分信息: + +- 声明类型:这里是 JWT +- 声明加密的算法:通常直接使用 HMAC SHA256 + +完整的头部就像下面这样的JSON: + +```bash +{ + 'typ': 'JWT', + 'alg': 'HS256' +} +``` + +然后将头部进行 Base64URL 算法加密(该加密是可以对称解密的),构成了第一部分 + +```undefined +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 +``` + +### 5.2 载荷(Payload) + +载荷,同样也是个 JSON 对象,它是存放有效信息的地方,但不建议存放密码等敏感信息。 + +JWT 规定了7个官方字段,供选用: + +- iss (issuer):签发人 +- exp (expiration time):过期时间 +- sub (subject):主题 +- aud (audience):受众 +- nbf (Not Before):生效时间 +- iat (Issued At):签发时间 +- jti (JWT ID):编号 + +除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。 + +注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。 + +```json +{ + "sub": "1234567890", + "name": "John Doe", + "admin": true +} +``` + +然后将其进行 Base64URL 算法加密,得到 JWT 的第二部分。 + +```undefined +eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 +``` + +### 5.3 签名(Signature) + +Signature 部分是对前两部分的签名,防止数据篡改。 + +首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。 + +``` +HMACSHA256( + base64UrlEncode(header) + "." + + base64UrlEncode(payload), + secret) +``` + +算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。 + +## 6. Base64URL 算法 + +前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。 + +JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。 + +## 7. JWT 如何发送? + +只要客户端收到了 JWT ,后续在这个域里的请求就会自动在 请求头里加入`Authorization`,并加上`Bearer`标注: + +```shell +'Authorization': 'Bearer ' + ${token} +``` + + + +## 8. JWT 如何校验? + +后端收到请求后,从 Header 中取出 `Authorization` 里的 JWT ,使用之前的签名算法对 header 和 payload 再次计算生成新的签名,并与 JWT 里的签名进行对比,如果一样,说明校验通过,是个合法的 Token。 + +```shell +HMACSHA256( + base64UrlEncode(header) + "." + + base64UrlEncode(payload), + secret) +``` + +## 9. 总结写在最后 + +本文先从跨域请求概念的提出,并分析了历史发展中都有哪些防御CSRF的策略,它们是如何有效的杜绝 CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细介绍了 JWT 是如何工作的,工作原理是什么? + +学完了本文,你应该知道:**cookie 是CSRF 攻击的帮凶,要防御 CSRF ,最好的办法是不使用 cookie,方案有两种:CSRF Token 和 JWT** + +而对于 JWT 的知识 ,你应该要知道: + +1. JWT 就是一个由服务端按照一定的规则生成的字符串 +2. JWT 是基于 JSON的,因此其可以进行跨语言支持的 +3. JWT 若没有经过 TLS/SSL 加密, payload 里不要放任何敏感信息 +4. JWT 的使用不需要在服务端保存会话信息, 减少了服务器的压力,有利于简化服务端的架构。 +5. JWT 若想不被伪造,只要保护好 secret 私钥就行了。 +6. JWT 与 HTTPS 协议搭配使用会更安全可靠。 + + + +## 10. 参考文章 + +- [咱妈说别乱点链接之浅谈CSRF攻击](https://cloud.tencent.com/developer/article/1004943) +- [JSON Web Token 入门教程](http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html) + + + +![](http://image.iswbm.com/20200607174235.png) From 1419c12e41d2400eb29817f0d6ccf4b61fdb1a60 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 6 Jul 2020 00:54:06 +0800 Subject: [PATCH 105/147] =?UTF-8?q?Add=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_07.md | 136 +++++++++--- source/c10/c10_07.rst | 497 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 603 insertions(+), 30 deletions(-) create mode 100644 source/c10/c10_07.rst diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md index 0770f37..7964ea6 100644 --- a/source/c10/c10_07.md +++ b/source/c10/c10_07.md @@ -4,6 +4,8 @@ +![](http://image.iswbm.com/20200705214412.png) + ## 1. 什么是跨域请求 要明白什么叫跨域请求,首先得知道什么叫域。 @@ -42,7 +44,7 @@ CSRF 的实际工作原理是怎样的? ## 3. 跨域请求的安全防御 -完成一次完整的 CSRF 攻击,只有两个步骤: +完成一次完整的 CSRF 攻击,只要两个步骤: 1. 登录受信任网站A,并在本地生成Cookie 2. 在不登出A的情况下,访问危险网站B @@ -58,7 +60,7 @@ CSRF 的实际工作原理是怎样的? 1. 利用浏览器的同源策略:最基础的安全策略 2. 对请求的来源进行验证:Referer Check 3. 使用验证码强制使用户进行交互确认,保证请求是用户发起 -4. CSRF Token +4. CSRF Token,注意不要使用 cookie 来存储token 5. JSON Web Token 以上是我知道的历史上用来抵御 CSRF 攻击的方法 @@ -69,8 +71,9 @@ CSRF 的实际工作原理是怎样的? - 有的虽然安全,用户体验好,但是有缺点 +只有一种是最优解,具体是哪一种? -只有一种是最优解,具体是哪一种,请继续往下看 +不防继续往下看 ### 3.1 同源策略 @@ -90,21 +93,19 @@ CSRF 的实际工作原理是怎样的? 当发生 CSRF 攻击时,这个来源地址,会变成危险网站 B,因此只要在服务端校验这个 Referer 是不是和自己同一个域就可以判断这个请求是跨站请求。 +![](http://image.iswbm.com/20200705193118.png) + 但这种方法,也是有局限性的,在一些非主流的浏览器,或者使用了那些非常古老的浏览器版本,这个 Referer 字段,是有可能会被篡改的。 退一步讲,假设你使用了最安全的最新版本的浏览器,这个值无法被篡改,依旧还是有安全隐患。 因为有些用户出于某些隐私考虑,会在浏览器设置关闭这个 Referer 字段,也有的网站会使用一些技术手段使用请求不携带 Referer 字段。 -因此我们发现了只要需要客户端配合的解决方案,就有无穷无尽的场景,场景越多,可能性越多,可以被突破的漏洞就越多。 - -到头后,我还是只能将希望寄托于服务端。 - ### 3.3 加验证码 验证码,强制用户必须与应用进行交互,才能完成最终请求。 -在通常情况下,验证码能很好遏制CSRF攻击。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。 +其实加验证码,是能很好遏制 CSRF 攻击,但是网站总不能给所有的操作都加上验证码吧,那样的话,用户估计都跑光光了,因此为了保证用户体验,验证码只能作为一种辅助手段,不能作为主要解决方案。 ### 3.4 CSRF Token @@ -112,12 +113,14 @@ CSRF 的实际工作原理是怎样的? 所以要抵御 CSRF,关键在于要在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中(不然黑客又能拿到了)。 -业界普遍的防御方案是使用 CSRF Token,具体流程如下: +业界普遍的防御方案是使用 CSRF Token,具体流程如下 + +![](http://image.iswbm.com/20200705211401.png) -1. 当用户请求一个页面时,服务端会生成一个随机的 Token,并把这个 Token 存放在 Session 里(也就是内存中),然后放入HTML表单中传给浏览器; +1. 当用户请求一个更新用户名的页面时,服务端会生成一个随机的 Token,并把这个 Token 存放在 Session 里(也就是内存中),然后放入HTML表单中传给浏览器; 2. 当用户提交表单请求时,会带上这个 Token 发送给服务端 ; -3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 token 进行对比,如果是一样的,就是合法的用户请求,如果不一样,那就是非法的请求,应当拒绝。 +3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 token 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 在这里面有几点值得注意: @@ -152,12 +155,14 @@ JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细 为了让你直观感受 JWT 的工作原理,我画了下面这张图 -1. 用户以 Web表单 的形式,将自己的用户名和密码发送到后端的接口。(HTTPS + POST); -2. 后端核对用户名和密码成功后,会计算生成JWT Payload 字符串(具体计算方法,后续会讲)返回给前端; -3. 前端收到 JWT 后,会将其保存在 localStorage 或 sessionStorage 上。 -4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization 字段。(解决XSS和XSRF问题) -5. 后端收到新请求后,如存在验证JWT的有效性(具体如何较验,后面会讲)。 -6. 验证通过后后端使用 JWT 中包含的用户信息进行其他逻辑操作,返回相应结果。 +![](http://image.iswbm.com/20200705220524.png) + +1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 +2. 后端核对用户名和密码成功后,会计算生成JWT Payload 字符串(具体计算方法,后续会讲),然后返回 response 给浏览器(包含Set-Cookie: HttpOnly); +3. 浏览器收到 JWT 后,将其保存在 cookie 里(为什么保存在 cookie 里,后续会讲)。 +4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization 字段。 +5. 后端收到新请求后,会使用密钥验证 JWT 签名。 +6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 @@ -171,10 +176,14 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4 仔细观察,会发现它里面有三个 `.` ,以 `.` 为分界,可以将 JWT 分为三部分。 +![](http://image.iswbm.com/20200705212820.png) + 1. **第一部分**:头部(Header) 2. **第二部分**:载荷(Payload) 3. **第三部分**:签名(Signature) +![](http://image.iswbm.com/20200705215033.png) + ### 5.1 头部(Header) JWT 的头部承载两部分信息: @@ -186,14 +195,14 @@ JWT 的头部承载两部分信息: ```bash { - 'typ': 'JWT', - 'alg': 'HS256' + "typ": "JWT", + "alg": "HS256" } ``` 然后将头部进行 Base64URL 算法加密(该加密是可以对称解密的),构成了第一部分 -```undefined +```shell eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 ``` @@ -225,7 +234,7 @@ JWT 规定了7个官方字段,供选用: 然后将其进行 Base64URL 算法加密,得到 JWT 的第二部分。 -```undefined +```shell eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 ``` @@ -242,25 +251,89 @@ HMACSHA256( secret) ``` -算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。 +算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(`.`)分隔,就可以返回给用户。 + +## 6. 手动生成 JWT -## 6. Base64URL 算法 +如果你想手动生成一个 JWT 用于测试,可以使用 `https://jwt.io/ `这个网站 。 + +我使用前面的 header 和 payload,然后使用 secret 密钥:`Python` + +最后生成的 JWT 结果如下 + +```shell +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.3wGDum3_A8tAt1bdal5CpYbIUlpHfPQxs96Ijx883kI +``` + +![](http://image.iswbm.com/20200706005103.png) + +## 7. Base64URL 算法 前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。 JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。 -## 7. JWT 如何发送? +## 8. JWT 如何保存? + +关于浏览器应该将 JWT 保存在哪?这个问题,其实也困扰了我很久。 + +如果使用搜索引擎去查,我相信你也一定会被他们绕晕。 + +比如在这篇帖子([When and how to use it](https://blog.logrocket.com/jwt-authentication-best-practices/) )里,作者的观点是,不应该保存在 localstorage 和 session storage,因为这样,第三方的脚本就能直接获取到。 -只要客户端收到了 JWT ,后续在这个域里的请求就会自动在 请求头里加入`Authorization`,并加上`Bearer`标注: +作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 + +![image-20200705233446534](/Users/MING/Library/Application Support/typora-user-images/image-20200705233446534.png) + +再比如这一篇帖子([JWT(JSON Web Token) : Implementation with Node](https://medium.com/@am_pra_veen/jwt-json-web-token-implementation-with-node-d0661d4c7cbb))提到了要把 JWT 保存到 local-storage。 + +![image-20200705233925900](/Users/MING/Library/Application Support/typora-user-images/image-20200705233925900.png) + +因此,我决定不再看网络上关于 『应将 JWT 保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 。 + +JWT 的保存位置,可以分为如下四种 + +1. 保存在 localStorage +2. 保存在 sessionStorage +3. 保存在 cookie +4. 保存在 cookie 并设置 HttpOnly + +第一种和第二种其实可以归为一类,这一类有个特点,就是该域内的 js 脚本都可以读取,这种情况下 JWT 通过 js 脚本放入 Header 里的 Authorization 字段,会存在 XSS 攻击风险。 + +第三种,与第四种相比,区别在于 cookie 有没有标记 HttpOnly,没有标记 HttpOnly 的 cookie ,客户端可以将 JWT 通过 js 脚本放入 Header 里的 Authorization 字段。这么看好像同时存在CSRF 攻击风险和 XSS 攻击风险,实则不然,我们虽然将 JWT 存储在 cookie 里,但是我们的服务端并没有利用 cookie 里的 JWT 直接去鉴权,而是通过 header 里的 Authorization 去鉴权,因此这种方法只有 XSS 攻击风险,而没有 CSRF 攻击风险。 + +而第四种,加了 HttpOnly 标记,意味着这个 cookie 无法通过js脚本进行读取和修改,杜绝了 XSS 攻击的发生。与此同时,网站自身的 js 脚本也无法利用 cookie 设置 header 的Authorization 字段,因此只能通过 cookie 里的 JWT 去鉴权,所以不可避免还是存在 CSRF 攻击风险。 + +如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 + +![image-20200706001903273](/Users/MING/Library/Application Support/typora-user-images/image-20200706001903273.png) + +是的,事实也确实如此。 + +所以我的观点是,开发人员应当根据实际情况来选择 JWT 的存储位置。 + +- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF 攻击,同时这种情况下也不会有 XSS 攻击的风险 +- 而如果访问量/业务量对服务器造成很大压力,或觉得服务器共享 token 对架构要求太高了,那就抛弃CSRF Token 的方式,而改用 JWT。选择了 JWT ,就面临着要将 JWT 存储在哪的问题。下面再继续视情况而定 +- 若选择了 JWT ,那么要知道 XSS 攻击风险 和 CSRF 攻击风险,不可避免要面临其中一个(上面那张图已经得出结论),这时候选择哪个就比较哲学了。按照当前的大环境来看,现代成熟的 Web框架 已经可以轻松地防止CSRF攻击(比如前面讲过的 Referer Check 、加验证码),而当下的 HTML5好像是更容易会受到 XSS 攻击,并且在攻击后会有较大的影响,因此在这样的情况下可以尽量避开 XSS 攻击,而选择第四种:cookie + HttpOnly。 +- 亦或者,做为开发者的你,对自己防护 XSS 攻击有足够的信心,在客户端和服务端,对提交的数据都进行了 xss 攻击的检查以及转义,这时候你就可以选择前面三种的任意一种。 + + + +## 9. JWT 如何发送? + +通过上面第七节的描述,其实我也讲到了 JWT 根据不同场景可以选择两种发送方式 + +- 第一种:将 JWT 放在 Header 里的 `Authorization` 字段,并使用 `Bearer`标注 ```shell 'Authorization': 'Bearer ' + ${token} ``` +- 第二种:把 JWT 放入 cookie ,发送给服务端 +两种的利弊上面都已经剖析过了,这里不再赘述。 -## 8. JWT 如何校验? +## 10. JWT 如何校验? 后端收到请求后,从 Header 中取出 `Authorization` 里的 JWT ,使用之前的签名算法对 header 和 payload 再次计算生成新的签名,并与 JWT 里的签名进行对比,如果一样,说明校验通过,是个合法的 Token。 @@ -271,7 +344,9 @@ HMACSHA256( secret) ``` -## 9. 总结写在最后 +在 JWT 里含有用户的相关信息,如果 Token 合法,那么Server 就可以取出里面的 payload 得知这是属于哪个用户的请求。 + +## 11. 总结写在最后 本文先从跨域请求概念的提出,并分析了历史发展中都有哪些防御CSRF的策略,它们是如何有效的杜绝 CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细介绍了 JWT 是如何工作的,工作原理是什么? @@ -288,11 +363,12 @@ HMACSHA256( -## 10. 参考文章 +## 12. 参考文章 - [咱妈说别乱点链接之浅谈CSRF攻击](https://cloud.tencent.com/developer/article/1004943) - [JSON Web Token 入门教程](http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html) +- [Where to Store your JWTs – Cookies vs HTML5 Web Storage](https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage) +- [JWT 超详细分析](https://www.cnblogs.com/DeadBoy/p/11481146.html) - -![](http://image.iswbm.com/20200607174235.png) +## ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_07.rst b/source/c10/c10_07.rst new file mode 100644 index 0000000..36c9f54 --- /dev/null +++ b/source/c10/c10_07.rst @@ -0,0 +1,497 @@ +10.7 网络知识扫盲:CSRF 跨域认证与JWT +===================================== + +|image0| + +|image1| + +1. 什么是跨域请求 +----------------- + +要明白什么叫跨域请求,首先得知道什么叫域。 + +域,是指由 ``协议`` + ``域名`` + ``端口号`` 组成的一个虚拟概念。 + +|image2| + +如果两个域的协议、域名、端口号都一样,就称他们为同域,但是只要有其中一个不一样,就不是同域。 + +那么 ``跨域请求`` 又是什么意思呢? + +简单来说,就是在一个域内请求了另一个域的资源,由于域不一致,会有安全隐患。 + +2. 跨域请求的安全隐患 +--------------------- + +有一个词,叫 CSRF (Cross-site request forgery)攻击,中文名是 +``跨站请求伪造``\ 。 + +简单来说呢,就是攻击者盗用了你的身份,以你的名义发送恶意请求,它能做的坏事有很多,比如以你的名义发邮件,发消息,购物,盗取帐号等。 + +CSRF 的实际工作原理是怎样的? + +比如现在有两个网站,A 网站是真实受信息的网站,而 B网站是危险网站。 + +当你登陆 A 网站后,浏览器会存储 A 网站服务器给你生成的 sessionid 存入 +cookie,有了这个 cookie +,就拥有了你的帐号权限,以后请求资料,就不用再次登陆啦。 + +对于真实用户来说,是便利,可对于攻击者来说,却是可乘之机。 + +|image3| + +他们可以使用各种社工学引导你点击他们的链接/网站,然后利用你的浏览器上存储的 +cookie ,然后在自己的 网站B 发起对 网站A +的请求,获取一些隐私信息,做一些侵害用户权益的事情。这便是一个完整的 +CSRF 攻击。 + +3. 跨域请求的安全防御 +--------------------- + +完成一次完整的 CSRF 攻击,只要两个步骤: + +1. 登录受信任网站A,并在本地生成Cookie +2. 在不登出A的情况下,访问危险网站B + +只要其中一个条件不满足,CSRF 攻击就不会发生。 + +由于 cookie 机制出现得比较早,且应用比较广泛,所以早期要防御 CSRF +攻击,通常都是从 第二个步骤入手。 + +很多浏览器用户对于网络安全是无意识的,因此我们不能指望通过规范用户行为来避免CSRF攻击。 + +那如何从技术手段规避一定的 CSRF 攻击的风险呢? + +1. 利用浏览器的同源策略:最基础的安全策略 +2. 对请求的来源进行验证:Referer Check +3. 使用验证码强制使用户进行交互确认,保证请求是用户发起 +4. CSRF Token,注意不要使用 cookie 来存储token +5. JSON Web Token + +以上是我知道的历史上用来抵御 CSRF 攻击的方法 + +- 有的虽然实现简单,但是不够安全 + +- 有的虽然安全,但是用户体验不好 + +- 有的虽然安全,用户体验好,但是有缺点 + +只有一种是最优解,具体是哪一种? + +不防继续往下看 + +3.1 同源策略 +~~~~~~~~~~~~ + +浏览器上有一个同源策略(SOP,全称 Same origin +policy),它会在一定程度上禁止这种跨域请求的发生。 + +但同源策略是最基本的安全策略,对于低级的 CSRF 攻击 ,它是很有效果的。 + +可以说 Web +是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。 + +同源策略在提升了 Web前端的安全性的同时,也牺牲了Web拓展上的灵活性。 + +设想若把html、js、css、flash,image等文件全部布置在一台服务器上,小网站这样凑活还行,大中网站如果这样做服务器根本受不了的,因此同源策略,就像是双刃剑。不过这些都是有解的。 + +3.2 Referer Check +~~~~~~~~~~~~~~~~~ + +在 HTTP 协议中,有一个字段叫做 Referer,它记录了HTTP 请求的来源地址。 + +当发生 CSRF 攻击时,这个来源地址,会变成危险网站 +B,因此只要在服务端校验这个 Referer +是不是和自己同一个域就可以判断这个请求是跨站请求。 + +|image4| + +但这种方法,也是有局限性的,在一些非主流的浏览器,或者使用了那些非常古老的浏览器版本,这个 +Referer 字段,是有可能会被篡改的。 + +退一步讲,假设你使用了最安全的最新版本的浏览器,这个值无法被篡改,依旧还是有安全隐患。 + +因为有些用户出于某些隐私考虑,会在浏览器设置关闭这个 Referer +字段,也有的网站会使用一些技术手段使用请求不携带 Referer 字段。 + +3.3 加验证码 +~~~~~~~~~~~~ + +验证码,强制用户必须与应用进行交互,才能完成最终请求。 + +其实加验证码,是能很好遏制 CSRF +攻击,但是网站总不能给所有的操作都加上验证码吧,那样的话,用户估计都跑光光了,因此为了保证用户体验,验证码只能作为一种辅助手段,不能作为主要解决方案。 + +3.4 CSRF Token +~~~~~~~~~~~~~~ + +CSRF +攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 +cookie 中,因此黑客可以在不知道这些验证信息的情况下,直接利用用户自己的 +cookie 来通过安全验证。 + +所以要抵御 +CSRF,关键在于要在请求中放入黑客所不能伪造的信息,并且该信息不存在于 +cookie 之中(不然黑客又能拿到了)。 + +业界普遍的防御方案是使用 CSRF Token,具体流程如下 + +|image5| + +1. 当用户请求一个更新用户名的页面时,服务端会生成一个随机的 + Token,并把这个 Token 存放在 Session + 里(也就是内存中),然后放入HTML表单中传给浏览器; + +2. 当用户提交表单请求时,会带上这个 Token 发送给服务端 ; +3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 + token + 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 + +在这里面有几点值得注意: + +1. 由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。 +2. 虽然也可以通过cookie 的方式将 Token + 传给浏览器,但是有了以前的经验,你会发现这种方法 Token + 还是会被危险网站给【利用】(注意这里是利用,而不是获取),然后发送非法的跨域请求。 + +3.5 新增 Header +~~~~~~~~~~~~~~~ + +CSRF 跨域攻击能够成功的最根本原因,是我们使用了 cookie,因此如果要杜绝 +CSRF 的产生,就要一定要抛弃 cookie。 + +上面的 CSRF Token 就是没有使用 cookie ,才得以避免了 CSRF 的攻击问题。 + +CSRF Token 虽然能解决问题,但是解决得并不完美。 + +CSRF Token 如果不想使用 cookie,就必须要将 Token +存储在服务端的内存中,这样就会面临几个问题 + +1. 服务端每生成一个 Token,都会将以 session + 形式都是保存在内存中,而随着用户请求的增多,服务端的开销会明显增大。 +2. 如果网站有多个子域,分别对应不同的服务器,比如 taobao.com + 后台是服务器 a,zhibo.baotao.com 后台是 服务器b, + 不同子域要想使用同一个 Token,就要求所有的服务器要能共享这个 + Token。一般要有一个中心节点(且应是一个集群)来存储这个Token,这样看下来,架构就变得更加复杂了。 + +想要解决这些问题,也不困难,现如今已经有很好的方案了,那就是 +JWT(全称:JSON Web Token) + +使用了 JWT 后,有了哪些变化呢 + +1. 服务器只负责生成Token和校验Token,而不再存储Token +2. 将服务器的压力分摊给了所有的客户端。 + +JWT 和 CSRF Token 一样,没有使用 Cookie,那么 Token 是如何发送给服务端 +的呢,是通过新增的 Header 字段:Authorization + +JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细说说关于 JWT +的内容。 + +4. JWT 的工作原理 +----------------- + +为了让你直观感受 JWT 的工作原理,我画了下面这张图 + +|image6| + +1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 +2. 后端核对用户名和密码成功后,会计算生成JWT Payload + 字符串(具体计算方法,后续会讲),然后返回 response + 给浏览器(包含Set-Cookie: HttpOnly); +3. 浏览器收到 JWT 后,将其保存在 cookie 里(为什么保存在 cookie + 里,后续会讲)。 +4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization + 字段。 +5. 后端收到新请求后,会使用密钥验证 JWT 签名。 +6. 验证通过后后端使用 JWT + 中包含的用户信息进行其他相关操作,返回相应结果。 + +5. JWT 如何生成? +----------------- + +JWT 其实就是一个字符串,比如下面这样 + +.. code:: shell + + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ + +仔细观察,会发现它里面有三个 ``.`` ,以 ``.`` 为分界,可以将 JWT +分为三部分。 + +|image7| + +1. **第一部分**\ :头部(Header) +2. **第二部分**\ :载荷(Payload) +3. **第三部分**\ :签名(Signature) + +|image8| + +5.1 头部(Header) +~~~~~~~~~~~~~~~~~~ + +JWT 的头部承载两部分信息: + +- 声明类型:这里是 JWT +- 声明加密的算法:通常直接使用 HMAC SHA256 + +完整的头部就像下面这样的JSON: + +.. code:: bash + + { + "typ": "JWT", + "alg": "HS256" + } + +然后将头部进行 Base64URL +算法加密(该加密是可以对称解密的),构成了第一部分 + +.. code:: shell + + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 + +5.2 载荷(Payload) +~~~~~~~~~~~~~~~~~~~ + +载荷,同样也是个 JSON +对象,它是存放有效信息的地方,但不建议存放密码等敏感信息。 + +JWT 规定了7个官方字段,供选用: + +- iss (issuer):签发人 +- exp (expiration time):过期时间 +- sub (subject):主题 +- aud (audience):受众 +- nbf (Not Before):生效时间 +- iat (Issued At):签发时间 +- jti (JWT ID):编号 + +除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。 + +注意,JWT +默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。 + +.. code:: json + + { + "sub": "1234567890", + "name": "John Doe", + "admin": true + } + +然后将其进行 Base64URL 算法加密,得到 JWT 的第二部分。 + +.. code:: shell + + eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 + +5.3 签名(Signature) +~~~~~~~~~~~~~~~~~~~~~ + +Signature 部分是对前两部分的签名,防止数据篡改。 + +首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 +Header 里面指定的签名算法(默认是 HMAC +SHA256),按照下面的公式产生签名。 + +:: + + HMACSHA256( + base64UrlEncode(header) + "." + + base64UrlEncode(payload), + secret) + +算出签名以后,把 Header、Payload、Signature +三个部分拼成一个字符串,每个部分之间用“点”(\ ``.``\ )分隔,就可以返回给用户。 + +6. 手动生成 JWT +--------------- + +如果你想手动生成一个 JWT 用于测试,可以使用 +``https://jwt.io/``\ 这个网站 。 + +我使用前面的 header 和 payload,然后使用 secret 密钥:\ ``Python`` + +最后生成的 JWT 结果如下 + +.. code:: shell + + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.3wGDum3_A8tAt1bdal5CpYbIUlpHfPQxs96Ijx883kI + +|image9| + +7. Base64URL 算法 +----------------- + +前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 +算法基本类似,但有一些小的不同。 + +JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 +api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL +里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成\_ 。这就是 +Base64URL 算法。 + +8. JWT 如何保存? +----------------- + +关于浏览器应该将 JWT 保存在哪?这个问题,其实也困扰了我很久。 + +如果使用搜索引擎去查,我相信你也一定会被他们绕晕。 + +比如在这篇帖子(\ `When and how to use +it `__ +)里,作者的观点是,不应该保存在 localstorage 和 session +storage,因为这样,第三方的脚本就能直接获取到。 + +作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200705233446534.png + :alt: image-20200705233446534 + + image-20200705233446534 + +再比如这一篇帖子(\ `JWT(JSON Web Token) : Implementation with +Node `__\ )提到了要把 +JWT 保存到 local-storage。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200705233925900.png + :alt: image-20200705233925900 + + image-20200705233925900 + +因此,我决定不再看网络上关于 『应将 JWT +保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 +。 + +JWT 的保存位置,可以分为如下四种 + +1. 保存在 localStorage +2. 保存在 sessionStorage +3. 保存在 cookie +4. 保存在 cookie 并设置 HttpOnly + +第一种和第二种其实可以归为一类,这一类有个特点,就是该域内的 js +脚本都可以读取,这种情况下 JWT 通过 js 脚本放入 Header 里的 +Authorization 字段,会存在 XSS 攻击风险。 + +第三种,与第四种相比,区别在于 cookie 有没有标记 HttpOnly,没有标记 +HttpOnly 的 cookie ,客户端可以将 JWT 通过 js 脚本放入 Header 里的 +Authorization 字段。这么看好像同时存在CSRF 攻击风险和 XSS +攻击风险,实则不然,我们虽然将 JWT 存储在 cookie +里,但是我们的服务端并没有利用 cookie 里的 JWT 直接去鉴权,而是通过 +header 里的 Authorization 去鉴权,因此这种方法只有 XSS 攻击风险,而没有 +CSRF 攻击风险。 + +而第四种,加了 HttpOnly 标记,意味着这个 cookie +无法通过js脚本进行读取和修改,杜绝了 XSS +攻击的发生。与此同时,网站自身的 js 脚本也无法利用 cookie 设置 header +的Authorization 字段,因此只能通过 cookie 里的 JWT +去鉴权,所以不可避免还是存在 CSRF 攻击风险。 + +如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 + +.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200706001903273.png + :alt: image-20200706001903273 + + image-20200706001903273 + +是的,事实也确实如此。 + +所以我的观点是,开发人员应当根据实际情况来选择 JWT 的存储位置。 + +- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF + 攻击,同时这种情况下也不会有 XSS 攻击的风险 +- 而如果访问量/业务量对服务器造成很大压力,或觉得服务器共享 token + 对架构要求太高了,那就抛弃CSRF Token 的方式,而改用 JWT。选择了 JWT + ,就面临着要将 JWT 存储在哪的问题。下面再继续视情况而定 +- 若选择了 JWT ,那么要知道 XSS 攻击风险 和 CSRF + 攻击风险,不可避免要面临其中一个(上面那张图已经得出结论),这时候选择哪个就比较哲学了。按照当前的大环境来看,现代成熟的 + Web框架 已经可以轻松地防止CSRF攻击(比如前面讲过的 Referer Check + 、加验证码),而当下的 HTML5好像是更容易会受到 XSS + 攻击,并且在攻击后会有较大的影响,因此在这样的情况下可以尽量避开 XSS + 攻击,而选择第四种:cookie + HttpOnly。 +- 亦或者,做为开发者的你,对自己防护 XSS + 攻击有足够的信心,在客户端和服务端,对提交的数据都进行了 xss + 攻击的检查以及转义,这时候你就可以选择前面三种的任意一种。 + +9. JWT 如何发送? +----------------- + +通过上面第七节的描述,其实我也讲到了 JWT +根据不同场景可以选择两种发送方式 + +- 第一种:将 JWT 放在 Header 里的 ``Authorization`` 字段,并使用 + ``Bearer``\ 标注 + +.. code:: shell + + 'Authorization': 'Bearer ' + ${token} + +- 第二种:把 JWT 放入 cookie ,发送给服务端 + +两种的利弊上面都已经剖析过了,这里不再赘述。 + +10. JWT 如何校验? +------------------ + +后端收到请求后,从 Header 中取出 ``Authorization`` 里的 JWT +,使用之前的签名算法对 header 和 payload 再次计算生成新的签名,并与 JWT +里的签名进行对比,如果一样,说明校验通过,是个合法的 Token。 + +.. code:: shell + + HMACSHA256( + base64UrlEncode(header) + "." + + base64UrlEncode(payload), + secret) + +在 JWT 里含有用户的相关信息,如果 Token 合法,那么Server +就可以取出里面的 payload 得知这是属于哪个用户的请求。 + +11. 总结写在最后 +---------------- + +本文先从跨域请求概念的提出,并分析了历史发展中都有哪些防御CSRF的策略,它们是如何有效的杜绝 +CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细介绍了 JWT +是如何工作的,工作原理是什么? + +学完了本文,你应该知道:\ **cookie 是CSRF 攻击的帮凶,要防御 CSRF +,最好的办法是不使用 cookie,方案有两种:CSRF Token 和 JWT** + +而对于 JWT 的知识 ,你应该要知道: + +1. JWT 就是一个由服务端按照一定的规则生成的字符串 +2. JWT 是基于 JSON的,因此其可以进行跨语言支持的 +3. JWT 若没有经过 TLS/SSL 加密, payload 里不要放任何敏感信息 +4. JWT 的使用不需要在服务端保存会话信息, + 减少了服务器的压力,有利于简化服务端的架构。 +5. JWT 若想不被伪造,只要保护好 secret 私钥就行了。 +6. JWT 与 HTTPS 协议搭配使用会更安全可靠。 + +12. 参考文章 +------------ + +- `咱妈说别乱点链接之浅谈CSRF攻击 `__ +- `JSON Web Token + 入门教程 `__ +- `Where to Store your JWTs – Cookies vs HTML5 Web + Storage `__ + +- `JWT 超详细分析 `__ + +|image10| +--------- + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200705214412.png +.. |image2| image:: http://image.iswbm.com/20200705171112.png +.. |image3| image:: http://image.iswbm.com/20200705172457.png +.. |image4| image:: http://image.iswbm.com/20200705193118.png +.. |image5| image:: http://image.iswbm.com/20200705211401.png +.. |image6| image:: http://image.iswbm.com/20200705220524.png +.. |image7| image:: http://image.iswbm.com/20200705212820.png +.. |image8| image:: http://image.iswbm.com/20200705215033.png +.. |image9| image:: http://image.iswbm.com/20200706005103.png +.. |image10| image:: http://image.iswbm.com/20200607174235.png + From 2b1ec15143c030b59df180d5fd48f5197eda459e Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 6 Jul 2020 01:02:55 +0800 Subject: [PATCH 106/147] Update --- source/c10/c10_07.md | 16 ++++++++-------- source/c10/c10_07.rst | 41 ++++++++++++++++++++--------------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md index 7964ea6..1356d59 100644 --- a/source/c10/c10_07.md +++ b/source/c10/c10_07.md @@ -4,7 +4,7 @@ -![](http://image.iswbm.com/20200705214412.png) +![](http://image.iswbm.com/20200706005937.png) ## 1. 什么是跨域请求 @@ -158,13 +158,13 @@ JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细 ![](http://image.iswbm.com/20200705220524.png) 1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 -2. 后端核对用户名和密码成功后,会计算生成JWT Payload 字符串(具体计算方法,后续会讲),然后返回 response 给浏览器(包含Set-Cookie: HttpOnly); -3. 浏览器收到 JWT 后,将其保存在 cookie 里(为什么保存在 cookie 里,后续会讲)。 +2. 后端核对用户名和密码成功后,会计算生成JWT Payload 字符串(具体计算方法,后续会讲),然后返回 response 给浏览器。 +3. 浏览器收到 JWT 后,将其保存在 cookie 里或者 localStorage 或者 sessionStorage 里(具体如何选,后面会说)。 4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization 字段。 5. 后端收到新请求后,会使用密钥验证 JWT 签名。 6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 - +![JSON Web Token (JWT) — The right way of implementing, with Node.js](https://miro.medium.com/max/2960/1*tW-8Y2edq04b4__zF0Jm9Q.png) ## 5. JWT 如何生成? @@ -253,7 +253,7 @@ HMACSHA256( 算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(`.`)分隔,就可以返回给用户。 -## 6. 手动生成 JWT +## 6. 如何手动生成 JWT? 如果你想手动生成一个 JWT 用于测试,可以使用 `https://jwt.io/ `这个网站 。 @@ -283,11 +283,11 @@ JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api 作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 -![image-20200705233446534](/Users/MING/Library/Application Support/typora-user-images/image-20200705233446534.png) +![](http://image.iswbm.com/image-20200705233446534.png) 再比如这一篇帖子([JWT(JSON Web Token) : Implementation with Node](https://medium.com/@am_pra_veen/jwt-json-web-token-implementation-with-node-d0661d4c7cbb))提到了要把 JWT 保存到 local-storage。 -![image-20200705233925900](/Users/MING/Library/Application Support/typora-user-images/image-20200705233925900.png) +![](http://image.iswbm.com/image-20200705233925900.png) 因此,我决定不再看网络上关于 『应将 JWT 保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 。 @@ -306,7 +306,7 @@ JWT 的保存位置,可以分为如下四种 如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 -![image-20200706001903273](/Users/MING/Library/Application Support/typora-user-images/image-20200706001903273.png) +![](http://image.iswbm.com/image-20200706001903273.png) 是的,事实也确实如此。 diff --git a/source/c10/c10_07.rst b/source/c10/c10_07.rst index 36c9f54..6bed356 100644 --- a/source/c10/c10_07.rst +++ b/source/c10/c10_07.rst @@ -197,16 +197,21 @@ JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细 1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 2. 后端核对用户名和密码成功后,会计算生成JWT Payload - 字符串(具体计算方法,后续会讲),然后返回 response - 给浏览器(包含Set-Cookie: HttpOnly); -3. 浏览器收到 JWT 后,将其保存在 cookie 里(为什么保存在 cookie - 里,后续会讲)。 + 字符串(具体计算方法,后续会讲),然后返回 response 给浏览器。 +3. 浏览器收到 JWT 后,将其保存在 cookie 里或者 localStorage 或者 + sessionStorage 里(具体如何选,后面会说)。 4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization 字段。 5. 后端收到新请求后,会使用密钥验证 JWT 签名。 6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 +.. figure:: https://miro.medium.com/max/2960/1*tW-8Y2edq04b4__zF0Jm9Q.png + :alt: JSON Web Token (JWT) — The right way of implementing, with + Node.js + + JSON Web Token (JWT) — The right way of implementing, with Node.js + 5. JWT 如何生成? ----------------- @@ -305,8 +310,8 @@ SHA256),按照下面的公式产生签名。 算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用“点”(\ ``.``\ )分隔,就可以返回给用户。 -6. 手动生成 JWT ---------------- +6. 如何手动生成 JWT? +--------------------- 如果你想手动生成一个 JWT 用于测试,可以使用 ``https://jwt.io/``\ 这个网站 。 @@ -346,19 +351,13 @@ storage,因为这样,第三方的脚本就能直接获取到。 作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200705233446534.png - :alt: image-20200705233446534 - - image-20200705233446534 +|image10| 再比如这一篇帖子(\ `JWT(JSON Web Token) : Implementation with Node `__\ )提到了要把 JWT 保存到 local-storage。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200705233925900.png - :alt: image-20200705233925900 - - image-20200705233925900 +|image11| 因此,我决定不再看网络上关于 『应将 JWT 保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 @@ -391,10 +390,7 @@ CSRF 攻击风险。 如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200706001903273.png - :alt: image-20200706001903273 - - image-20200706001903273 +|image12| 是的,事实也确实如此。 @@ -480,11 +476,11 @@ CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细 - `JWT 超详细分析 `__ -|image10| +|image13| --------- .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200705214412.png +.. |image1| image:: http://image.iswbm.com/20200706005937.png .. |image2| image:: http://image.iswbm.com/20200705171112.png .. |image3| image:: http://image.iswbm.com/20200705172457.png .. |image4| image:: http://image.iswbm.com/20200705193118.png @@ -493,5 +489,8 @@ CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细 .. |image7| image:: http://image.iswbm.com/20200705212820.png .. |image8| image:: http://image.iswbm.com/20200705215033.png .. |image9| image:: http://image.iswbm.com/20200706005103.png -.. |image10| image:: http://image.iswbm.com/20200607174235.png +.. |image10| image:: http://image.iswbm.com/image-20200705233446534.png +.. |image11| image:: http://image.iswbm.com/image-20200705233925900.png +.. |image12| image:: http://image.iswbm.com/image-20200706001903273.png +.. |image13| image:: http://image.iswbm.com/20200607174235.png From 756f7b4f4ff78e4f82fe769dc8111d3a70923127 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 8 Jul 2020 22:56:33 +0800 Subject: [PATCH 107/147] update --- source/c10/c10_07.md | 117 +++++++++++----------- source/c10/c10_07.rst | 219 +++++++++++++++++++++++------------------- 2 files changed, 185 insertions(+), 151 deletions(-) diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md index 1356d59..21e7835 100644 --- a/source/c10/c10_07.md +++ b/source/c10/c10_07.md @@ -1,10 +1,10 @@ -# 10.7 网络知识扫盲:CSRF 跨域认证与JWT +# 10.7 网络知识扫盲:CSRF 跨域攻击与JWT跨域认证 ![](http://image.iswbm.com/20200602135014.png) -![](http://image.iswbm.com/20200706005937.png) +![](http://image.iswbm.com/20200708223651.png) ## 1. 什么是跨域请求 @@ -36,7 +36,7 @@ CSRF 的实际工作原理是怎样的? 对于真实用户来说,是便利,可对于攻击者来说,却是可乘之机。 -![](http://image.iswbm.com/20200705172457.png) +![Cookie + Session 方法](http://image.iswbm.com/20200707220426.png) 他们可以使用各种社工学引导你点击他们的链接/网站,然后利用你的浏览器上存储的 cookie ,然后在自己的 网站B 发起对 网站A 的请求,获取一些隐私信息,做一些侵害用户权益的事情。这便是一个完整的 CSRF 攻击。 @@ -46,12 +46,8 @@ CSRF 的实际工作原理是怎样的? 完成一次完整的 CSRF 攻击,只要两个步骤: -1. 登录受信任网站A,并在本地生成Cookie -2. 在不登出A的情况下,访问危险网站B - -只要其中一个条件不满足,CSRF 攻击就不会发生。 - -由于 cookie 机制出现得比较早,且应用比较广泛,所以早期要防御 CSRF 攻击,通常都是从 第二个步骤入手。 +1. 登录受信任网站A,并本地已经存储了 Cookie +2. 在不登出A的情况下,访问危险网站B,网站 B 诱导你发 A 发请求。 很多浏览器用户对于网络安全是无意识的,因此我们不能指望通过规范用户行为来避免CSRF攻击。 @@ -93,7 +89,7 @@ CSRF 的实际工作原理是怎样的? 当发生 CSRF 攻击时,这个来源地址,会变成危险网站 B,因此只要在服务端校验这个 Referer 是不是和自己同一个域就可以判断这个请求是跨站请求。 -![](http://image.iswbm.com/20200705193118.png) +![Referer Check 图解](http://image.iswbm.com/20200705193118.png) 但这种方法,也是有局限性的,在一些非主流的浏览器,或者使用了那些非常古老的浏览器版本,这个 Referer 字段,是有可能会被篡改的。 @@ -101,6 +97,8 @@ CSRF 的实际工作原理是怎样的? 因为有些用户出于某些隐私考虑,会在浏览器设置关闭这个 Referer 字段,也有的网站会使用一些技术手段使用请求不携带 Referer 字段。 +因此,当你要使用 Referer Check 来做为 防御 CSRF 攻击的主要手段时,请确保你的用户群体使用的一定是最安全的最新版本的浏览器,并且默认用户不会手动关闭 Referer 。 + ### 3.3 加验证码 验证码,强制用户必须与应用进行交互,才能完成最终请求。 @@ -113,49 +111,51 @@ CSRF 的实际工作原理是怎样的? 所以要抵御 CSRF,关键在于要在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中(不然黑客又能拿到了)。 -业界普遍的防御方案是使用 CSRF Token,具体流程如下 +业界普遍的防御方案是使用 CSRF Token -![](http://image.iswbm.com/20200705211401.png) +使用 CSRF Token 根据token验证方式的不同,也可以分为两种: -1. 当用户请求一个更新用户名的页面时,服务端会生成一个随机的 Token,并把这个 Token 存放在 Session 里(也就是内存中),然后放入HTML表单中传给浏览器; +**第一种**:如图所示 -2. 当用户提交表单请求时,会带上这个 Token 发送给服务端 ; -3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 token 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 +![](http://image.iswbm.com/image-20200707221742925.png) -在这里面有几点值得注意: +1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 Token,然后放入HTML表单中传给浏览器,并且存入 session 中。 -1. 由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。 -2. 虽然也可以通过cookie 的方式将 Token 传给浏览器,但是有了以前的经验,你会发现这种方法 Token 还是会被危险网站给【利用】(注意这里是利用,而不是获取),然后发送非法的跨域请求。 +2. 当用户提交表单请求时,表单数据会带上这个 Token 发送给服务端 ; +3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 token 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 -### 3.5 新增 Header +**第二种**: -CSRF 跨域攻击能够成功的最根本原因,是我们使用了 cookie,因此如果要杜绝 CSRF 的产生,就要一定要抛弃 cookie。 +![](http://image.iswbm.com/image-20200707222024941.png) -上面的 CSRF Token 就是没有使用 cookie ,才得以避免了 CSRF 的攻击问题。 +1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 Token,然后放入HTML表单中,并且会把这个 Token 放在 cookie 里发给浏览器。 +2. 当用户提交表单请求时,表单数据会带上这个 Token 发送给服务端,并且带上携带 token 的 cookie ; +3. 服务端收到表单请求后,会从表单数据里取出 Token,与 cookie 里的 token 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 - CSRF Token 虽然能解决问题,但是解决得并不完美。 +### 3.5 新增 Header + +使用上面的 CSRF Token 已经可以避免 CSRF 攻击,但是它却有可能又引入了另一个问题。 -CSRF Token 如果不想使用 cookie,就必须要将 Token 存储在服务端的内存中,这样就会面临几个问题 +若 CSRF Token 没有使用 cookie,就必须要将 Token 存储在服务端的 Session 中,这样就会面临几个问题 -1. 服务端每生成一个 Token,都会将以 session 形式都是保存在内存中,而随着用户请求的增多,服务端的开销会明显增大。 +1. 服务端每生成一个 Token,都会存放入 session 中,而随着用户请求的增多,服务端的开销会明显增大。 2. 如果网站有多个子域,分别对应不同的服务器,比如 taobao.com 后台是服务器 a,zhibo.baotao.com 后台是 服务器b, 不同子域要想使用同一个 Token,就要求所有的服务器要能共享这个 Token。一般要有一个中心节点(且应是一个集群)来存储这个Token,这样看下来,架构就变得更加复杂了。 -想要解决这些问题,也不困难,现如今已经有很好的方案了,那就是 JWT(全称:JSON Web Token) +想要解决这些问题,可以使用我们接下来要讲的 JWT(全称:JSON Web Token) 使用了 JWT 后,有了哪些变化呢 -1. 服务器只负责生成Token和校验Token,而不再存储Token +1. 服务器只负责生成 Token和校验Token,而不再存储Token 2. 将服务器的压力分摊给了所有的客户端。 +3. 服务端的 鉴权不使用 cookie ,而是由新增的 Header 字段:Authorization 里的 JWT 。 -JWT 和 CSRF Token 一样,没有使用 Cookie,那么 Token 是如何发送给服务端 的呢,是通过新增的 Header 字段:Authorization - -JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细说说关于 JWT 的内容。 +JWT 是本篇文章重要知识点之一,下面我会详细说说关于 JWT 的内容。 -## 4. JWT 的工作原理 +## 4. JWT 的工作原理及目的 为了让你直观感受 JWT 的工作原理,我画了下面这张图 -![](http://image.iswbm.com/20200705220524.png) +![JWT 工作图解](http://image.iswbm.com/20200705220524.png) 1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 2. 后端核对用户名和密码成功后,会计算生成JWT Payload 字符串(具体计算方法,后续会讲),然后返回 response 给浏览器。 @@ -164,7 +164,17 @@ JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细 5. 后端收到新请求后,会使用密钥验证 JWT 签名。 6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 -![JSON Web Token (JWT) — The right way of implementing, with Node.js](https://miro.medium.com/max/2960/1*tW-8Y2edq04b4__zF0Jm9Q.png) +![JSON Web Token (JWT) — The right way of implementing, with Node.js](http://image.iswbm.com/1*tW-8Y2edq04b4__zF0Jm9Q.png) + +JWT 的诞生并不是解决 CSRF 跨域攻击,而是解决跨域认证的难题。 + +举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,这应该如何实现呢? + +一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。 + +另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。 + +JWT 就是这种方案的一个优秀代表。 ## 5. JWT 如何生成? @@ -312,10 +322,13 @@ JWT 的保存位置,可以分为如下四种 所以我的观点是,开发人员应当根据实际情况来选择 JWT 的存储位置。 -- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF 攻击,同时这种情况下也不会有 XSS 攻击的风险 -- 而如果访问量/业务量对服务器造成很大压力,或觉得服务器共享 token 对架构要求太高了,那就抛弃CSRF Token 的方式,而改用 JWT。选择了 JWT ,就面临着要将 JWT 存储在哪的问题。下面再继续视情况而定 -- 若选择了 JWT ,那么要知道 XSS 攻击风险 和 CSRF 攻击风险,不可避免要面临其中一个(上面那张图已经得出结论),这时候选择哪个就比较哲学了。按照当前的大环境来看,现代成熟的 Web框架 已经可以轻松地防止CSRF攻击(比如前面讲过的 Referer Check 、加验证码),而当下的 HTML5好像是更容易会受到 XSS 攻击,并且在攻击后会有较大的影响,因此在这样的情况下可以尽量避开 XSS 攻击,而选择第四种:cookie + HttpOnly。 -- 亦或者,做为开发者的你,对自己防护 XSS 攻击有足够的信心,在客户端和服务端,对提交的数据都进行了 xss 攻击的检查以及转义,这时候你就可以选择前面三种的任意一种。 +- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF 攻击 +- 而如果访问量/业务量对服务器造成很大压力,或觉得服务器共享 token 对架构要求太高了,那就抛弃CSRF Token 的方式,而改用 JWT。选择了 JWT ,就面临着要将 JWT 存储在哪的问题。 +- 若选择了 JWT ,那么请不要使用 cookie HttpCookie 来存储它,因为使用它还是会有 CSRF 攻击风险。 +- 那另外三种如何选择呢?这三种无论使用哪种,都不可避免有 XSS 攻击风险。我的思路是,XSS 攻击通过其他的手段来规避,这里使用JWT 只有 防御 CSRF 攻击与服务器性能的优化,这两个目标。 +- 那我剩下的三种,我建议是使用 cookie 存储,但不使用 cookie 来鉴权。服务器鉴权还是通过请求里的 Authorization 字段(通过js写入 Header 的)。 + +当然,如果你觉得你通过 `Referer Check` 、`加验证码` 等其他手段,已经可以保证不受 CSRF 攻击的威胁,此时你使用 JWT ,就可以选择使用 JWT + cookie HttpOnly,扼杀 XSS 攻击的可能。 @@ -329,9 +342,7 @@ JWT 的保存位置,可以分为如下四种 'Authorization': 'Bearer ' + ${token} ``` -- 第二种:把 JWT 放入 cookie ,发送给服务端 - -两种的利弊上面都已经剖析过了,这里不再赘述。 +- 第二种:把 JWT 放入 cookie ,发送给服务端,虽然发送。但是不使用它来鉴权。 ## 10. JWT 如何校验? @@ -344,24 +355,22 @@ HMACSHA256( secret) ``` -在 JWT 里含有用户的相关信息,如果 Token 合法,那么Server 就可以取出里面的 payload 得知这是属于哪个用户的请求。 +验证是个合法的 Token 后,还要检查这个 Token 是否过期,在 JWT 里的 payload 中,有 Token 的过期时间,可以通过它来检查 Token 是否可以用? -## 11. 总结写在最后 - -本文先从跨域请求概念的提出,并分析了历史发展中都有哪些防御CSRF的策略,它们是如何有效的杜绝 CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细介绍了 JWT 是如何工作的,工作原理是什么? - -学完了本文,你应该知道:**cookie 是CSRF 攻击的帮凶,要防御 CSRF ,最好的办法是不使用 cookie,方案有两种:CSRF Token 和 JWT** +payload 里同时还有用户的相关信息,有了这些信息后,后端就可以知道这是哪个用户的请求了,到这里一切都验证通过,就可以执行相关的业务逻辑了。 -而对于 JWT 的知识 ,你应该要知道: - -1. JWT 就是一个由服务端按照一定的规则生成的字符串 -2. JWT 是基于 JSON的,因此其可以进行跨语言支持的 -3. JWT 若没有经过 TLS/SSL 加密, payload 里不要放任何敏感信息 -4. JWT 的使用不需要在服务端保存会话信息, 减少了服务器的压力,有利于简化服务端的架构。 -5. JWT 若想不被伪造,只要保护好 secret 私钥就行了。 -6. JWT 与 HTTPS 协议搭配使用会更安全可靠。 +## 11. 总结写在最后 +最后,我总结一下,本文的要点: +1. CSRF 攻击的产生,需要cookie 的『助攻』,否则无法完成。 +2. CRSF 是利用 cookie,而不是盗取 cookie,这点一定要明白。 +3. 但也并不是使用了 cookie 就会有 CSRF 风险,而应该说是用 cookie 去做鉴权才会有 CSRF 风险,参考 CSRF Token (把 token 存储在 cookie 的情况)和 JWT (把 token 存储在 cookie 的情况)。 +4. CSRF Token 和 JWT 虽然都可以做到防御 CSRF 攻击,但其实无论是哪个都无法同时做到防御 CSRF 和 XSS 攻击,在阻止了 CSRF 攻击后, 需要再通过其他手段来减少 XSS 攻击的可能性。 +5. JWT 就是一个由服务端按照一定的规则生成的字符串, +6. JWT 的目的是为了做一个无状态的 session,避免去频繁查询 session,减少了对服务器产生的压力,简化后端架构模型。它的主要用途是解决跨域认证的问题,而解决 CSRF 跨域攻击只是它的附带功能。 +7. payload 是经过 base64URL 算法转换而成的字符串,是可逆的,因此尽量不要存放敏感数据,如若非要存放敏感数据,最好与 HTTPS 协议搭配使用,避免数据泄露。 +8. JWT 的保存位置与方式,没有绝对的方案,具体如何选择要视情况而定。 ## 12. 参考文章 @@ -371,4 +380,4 @@ HMACSHA256( - [JWT 超详细分析](https://www.cnblogs.com/DeadBoy/p/11481146.html) -## ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_07.rst b/source/c10/c10_07.rst index 6bed356..15971a4 100644 --- a/source/c10/c10_07.rst +++ b/source/c10/c10_07.rst @@ -1,5 +1,5 @@ -10.7 网络知识扫盲:CSRF 跨域认证与JWT -===================================== +10.7 网络知识扫盲:CSRF 跨域攻击与JWT跨域认证 +============================================= |image0| @@ -38,7 +38,10 @@ cookie,有了这个 cookie 对于真实用户来说,是便利,可对于攻击者来说,却是可乘之机。 -|image3| +.. figure:: http://image.iswbm.com/20200707220426.png + :alt: Cookie + Session 方法 + + Cookie + Session 方法 他们可以使用各种社工学引导你点击他们的链接/网站,然后利用你的浏览器上存储的 cookie ,然后在自己的 网站B 发起对 网站A @@ -50,13 +53,8 @@ CSRF 攻击。 完成一次完整的 CSRF 攻击,只要两个步骤: -1. 登录受信任网站A,并在本地生成Cookie -2. 在不登出A的情况下,访问危险网站B - -只要其中一个条件不满足,CSRF 攻击就不会发生。 - -由于 cookie 机制出现得比较早,且应用比较广泛,所以早期要防御 CSRF -攻击,通常都是从 第二个步骤入手。 +1. 登录受信任网站A,并本地已经存储了 Cookie +2. 在不登出A的情况下,访问危险网站B,网站 B 诱导你发 A 发请求。 很多浏览器用户对于网络安全是无意识的,因此我们不能指望通过规范用户行为来避免CSRF攻击。 @@ -104,7 +102,10 @@ policy),它会在一定程度上禁止这种跨域请求的发生。 B,因此只要在服务端校验这个 Referer 是不是和自己同一个域就可以判断这个请求是跨站请求。 -|image4| +.. figure:: http://image.iswbm.com/20200705193118.png + :alt: Referer Check 图解 + + Referer Check 图解 但这种方法,也是有局限性的,在一些非主流的浏览器,或者使用了那些非常古老的浏览器版本,这个 Referer 字段,是有可能会被篡改的。 @@ -114,6 +115,10 @@ Referer 字段,是有可能会被篡改的。 因为有些用户出于某些隐私考虑,会在浏览器设置关闭这个 Referer 字段,也有的网站会使用一些技术手段使用请求不携带 Referer 字段。 +因此,当你要使用 Referer Check 来做为 防御 CSRF +攻击的主要手段时,请确保你的用户群体使用的一定是最安全的最新版本的浏览器,并且默认用户不会手动关闭 +Referer 。 + 3.3 加验证码 ~~~~~~~~~~~~ @@ -134,66 +139,70 @@ cookie 来通过安全验证。 CSRF,关键在于要在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中(不然黑客又能拿到了)。 -业界普遍的防御方案是使用 CSRF Token,具体流程如下 +业界普遍的防御方案是使用 CSRF Token -|image5| +使用 CSRF Token 根据token验证方式的不同,也可以分为两种: + +**第一种**\ :如图所示 + +|image3| -1. 当用户请求一个更新用户名的页面时,服务端会生成一个随机的 - Token,并把这个 Token 存放在 Session - 里(也就是内存中),然后放入HTML表单中传给浏览器; +1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 + Token,然后放入HTML表单中传给浏览器,并且存入 session 中。 -2. 当用户提交表单请求时,会带上这个 Token 发送给服务端 ; +2. 当用户提交表单请求时,表单数据会带上这个 Token 发送给服务端 ; 3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 token 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 -在这里面有几点值得注意: +**第二种**\ : -1. 由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。 -2. 虽然也可以通过cookie 的方式将 Token - 传给浏览器,但是有了以前的经验,你会发现这种方法 Token - 还是会被危险网站给【利用】(注意这里是利用,而不是获取),然后发送非法的跨域请求。 +|image4| + +1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 + Token,然后放入HTML表单中,并且会把这个 Token 放在 cookie + 里发给浏览器。 +2. 当用户提交表单请求时,表单数据会带上这个 Token + 发送给服务端,并且带上携带 token 的 cookie ; +3. 服务端收到表单请求后,会从表单数据里取出 Token,与 cookie 里的 token + 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 3.5 新增 Header ~~~~~~~~~~~~~~~ -CSRF 跨域攻击能够成功的最根本原因,是我们使用了 cookie,因此如果要杜绝 -CSRF 的产生,就要一定要抛弃 cookie。 - -上面的 CSRF Token 就是没有使用 cookie ,才得以避免了 CSRF 的攻击问题。 - -CSRF Token 虽然能解决问题,但是解决得并不完美。 +使用上面的 CSRF Token 已经可以避免 CSRF +攻击,但是它却有可能又引入了另一个问题。 -CSRF Token 如果不想使用 cookie,就必须要将 Token -存储在服务端的内存中,这样就会面临几个问题 +若 CSRF Token 没有使用 cookie,就必须要将 Token 存储在服务端的 Session +中,这样就会面临几个问题 -1. 服务端每生成一个 Token,都会将以 session - 形式都是保存在内存中,而随着用户请求的增多,服务端的开销会明显增大。 +1. 服务端每生成一个 Token,都会存放入 session + 中,而随着用户请求的增多,服务端的开销会明显增大。 2. 如果网站有多个子域,分别对应不同的服务器,比如 taobao.com 后台是服务器 a,zhibo.baotao.com 后台是 服务器b, 不同子域要想使用同一个 Token,就要求所有的服务器要能共享这个 Token。一般要有一个中心节点(且应是一个集群)来存储这个Token,这样看下来,架构就变得更加复杂了。 -想要解决这些问题,也不困难,现如今已经有很好的方案了,那就是 -JWT(全称:JSON Web Token) +想要解决这些问题,可以使用我们接下来要讲的 JWT(全称:JSON Web Token) 使用了 JWT 后,有了哪些变化呢 -1. 服务器只负责生成Token和校验Token,而不再存储Token +1. 服务器只负责生成 Token和校验Token,而不再存储Token 2. 将服务器的压力分摊给了所有的客户端。 +3. 服务端的 鉴权不使用 cookie ,而是由新增的 Header 字段:Authorization + 里的 JWT 。 -JWT 和 CSRF Token 一样,没有使用 Cookie,那么 Token 是如何发送给服务端 -的呢,是通过新增的 Header 字段:Authorization +JWT 是本篇文章重要知识点之一,下面我会详细说说关于 JWT 的内容。 -JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细说说关于 JWT -的内容。 - -4. JWT 的工作原理 ------------------ +4. JWT 的工作原理及目的 +----------------------- 为了让你直观感受 JWT 的工作原理,我画了下面这张图 -|image6| +.. figure:: http://image.iswbm.com/20200705220524.png + :alt: JWT 工作图解 + + JWT 工作图解 1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 2. 后端核对用户名和密码成功后,会计算生成JWT Payload @@ -206,12 +215,25 @@ JWT 是本篇文章最重要的知识点,内容也不少,下面我会详细 6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 -.. figure:: https://miro.medium.com/max/2960/1*tW-8Y2edq04b4__zF0Jm9Q.png +.. figure:: http://image.iswbm.com/1*tW-8Y2edq04b4__zF0Jm9Q.png :alt: JSON Web Token (JWT) — The right way of implementing, with Node.js JSON Web Token (JWT) — The right way of implementing, with Node.js +JWT 的诞生并不是解决 CSRF 跨域攻击,而是解决跨域认证的难题。 + +举例来说,A 网站和 B +网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,这应该如何实现呢? + +一种解决方案是 session +数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。 + +另一种方案是服务器索性不保存 session +数据了,所有数据都保存在客户端,每次请求都发回服务器。 + +JWT 就是这种方案的一个优秀代表。 + 5. JWT 如何生成? ----------------- @@ -224,13 +246,13 @@ JWT 其实就是一个字符串,比如下面这样 仔细观察,会发现它里面有三个 ``.`` ,以 ``.`` 为分界,可以将 JWT 分为三部分。 -|image7| +|image5| 1. **第一部分**\ :头部(Header) 2. **第二部分**\ :载荷(Payload) 3. **第三部分**\ :签名(Signature) -|image8| +|image6| 5.1 头部(Header) ~~~~~~~~~~~~~~~~~~ @@ -324,7 +346,7 @@ SHA256),按照下面的公式产生签名。 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.3wGDum3_A8tAt1bdal5CpYbIUlpHfPQxs96Ijx883kI -|image9| +|image7| 7. Base64URL 算法 ----------------- @@ -351,13 +373,13 @@ storage,因为这样,第三方的脚本就能直接获取到。 作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 -|image10| +|image8| 再比如这一篇帖子(\ `JWT(JSON Web Token) : Implementation with Node `__\ )提到了要把 JWT 保存到 local-storage。 -|image11| +|image9| 因此,我决定不再看网络上关于 『应将 JWT 保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 @@ -390,26 +412,28 @@ CSRF 攻击风险。 如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 -|image12| +|image10| 是的,事实也确实如此。 所以我的观点是,开发人员应当根据实际情况来选择 JWT 的存储位置。 -- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF - 攻击,同时这种情况下也不会有 XSS 攻击的风险 +- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF 攻击 - 而如果访问量/业务量对服务器造成很大压力,或觉得服务器共享 token 对架构要求太高了,那就抛弃CSRF Token 的方式,而改用 JWT。选择了 JWT - ,就面临着要将 JWT 存储在哪的问题。下面再继续视情况而定 -- 若选择了 JWT ,那么要知道 XSS 攻击风险 和 CSRF - 攻击风险,不可避免要面临其中一个(上面那张图已经得出结论),这时候选择哪个就比较哲学了。按照当前的大环境来看,现代成熟的 - Web框架 已经可以轻松地防止CSRF攻击(比如前面讲过的 Referer Check - 、加验证码),而当下的 HTML5好像是更容易会受到 XSS - 攻击,并且在攻击后会有较大的影响,因此在这样的情况下可以尽量避开 XSS - 攻击,而选择第四种:cookie + HttpOnly。 -- 亦或者,做为开发者的你,对自己防护 XSS - 攻击有足够的信心,在客户端和服务端,对提交的数据都进行了 xss - 攻击的检查以及转义,这时候你就可以选择前面三种的任意一种。 + ,就面临着要将 JWT 存储在哪的问题。 +- 若选择了 JWT ,那么请不要使用 cookie HttpCookie + 来存储它,因为使用它还是会有 CSRF 攻击风险。 +- 那另外三种如何选择呢?这三种无论使用哪种,都不可避免有 XSS + 攻击风险。我的思路是,XSS 攻击通过其他的手段来规避,这里使用JWT 只有 + 防御 CSRF 攻击与服务器性能的优化,这两个目标。 +- 那我剩下的三种,我建议是使用 cookie 存储,但不使用 cookie + 来鉴权。服务器鉴权还是通过请求里的 Authorization 字段(通过js写入 + Header 的)。 + +当然,如果你觉得你通过 ``Referer Check`` 、\ ``加验证码`` +等其他手段,已经可以保证不受 CSRF 攻击的威胁,此时你使用 JWT +,就可以选择使用 JWT + cookie HttpOnly,扼杀 XSS 攻击的可能。 9. JWT 如何发送? ----------------- @@ -424,9 +448,8 @@ CSRF 攻击风险。 'Authorization': 'Bearer ' + ${token} -- 第二种:把 JWT 放入 cookie ,发送给服务端 - -两种的利弊上面都已经剖析过了,这里不再赘述。 +- 第二种:把 JWT 放入 cookie + ,发送给服务端,虽然发送。但是不使用它来鉴权。 10. JWT 如何校验? ------------------ @@ -442,28 +465,33 @@ CSRF 攻击风险。 base64UrlEncode(payload), secret) -在 JWT 里含有用户的相关信息,如果 Token 合法,那么Server -就可以取出里面的 payload 得知这是属于哪个用户的请求。 +验证是个合法的 Token 后,还要检查这个 Token 是否过期,在 JWT 里的 +payload 中,有 Token 的过期时间,可以通过它来检查 Token 是否可以用? + +payload +里同时还有用户的相关信息,有了这些信息后,后端就可以知道这是哪个用户的请求了,到这里一切都验证通过,就可以执行相关的业务逻辑了。 11. 总结写在最后 ---------------- -本文先从跨域请求概念的提出,并分析了历史发展中都有哪些防御CSRF的策略,它们是如何有效的杜绝 -CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细介绍了 JWT -是如何工作的,工作原理是什么? - -学完了本文,你应该知道:\ **cookie 是CSRF 攻击的帮凶,要防御 CSRF -,最好的办法是不使用 cookie,方案有两种:CSRF Token 和 JWT** - -而对于 JWT 的知识 ,你应该要知道: - -1. JWT 就是一个由服务端按照一定的规则生成的字符串 -2. JWT 是基于 JSON的,因此其可以进行跨语言支持的 -3. JWT 若没有经过 TLS/SSL 加密, payload 里不要放任何敏感信息 -4. JWT 的使用不需要在服务端保存会话信息, - 减少了服务器的压力,有利于简化服务端的架构。 -5. JWT 若想不被伪造,只要保护好 secret 私钥就行了。 -6. JWT 与 HTTPS 协议搭配使用会更安全可靠。 +最后,我总结一下,本文的要点: + +1. CSRF 攻击的产生,需要cookie 的『助攻』,否则无法完成。 +2. CRSF 是利用 cookie,而不是盗取 cookie,这点一定要明白。 +3. 但也并不是使用了 cookie 就会有 CSRF 风险,而应该说是用 cookie + 去做鉴权才会有 CSRF 风险,参考 CSRF Token (把 token 存储在 cookie + 的情况)和 JWT (把 token 存储在 cookie 的情况)。 +4. CSRF Token 和 JWT 虽然都可以做到防御 CSRF + 攻击,但其实无论是哪个都无法同时做到防御 CSRF 和 XSS 攻击,在阻止了 + CSRF 攻击后, 需要再通过其他手段来减少 XSS 攻击的可能性。 +5. JWT 就是一个由服务端按照一定的规则生成的字符串, +6. JWT 的目的是为了做一个无状态的 session,避免去频繁查询 + session,减少了对服务器产生的压力,简化后端架构模型。它的主要用途是解决跨域认证的问题,而解决 + CSRF 跨域攻击只是它的附带功能。 +7. payload 是经过 base64URL + 算法转换而成的字符串,是可逆的,因此尽量不要存放敏感数据,如若非要存放敏感数据,最好与 + HTTPS 协议搭配使用,避免数据泄露。 +8. JWT 的保存位置与方式,没有绝对的方案,具体如何选择要视情况而定。 12. 参考文章 ------------ @@ -476,21 +504,18 @@ CSRF 的产生的,最后引出解决 CSRF 的完美方案 :JWT ,并详细 - `JWT 超详细分析 `__ -|image13| ---------- +|image11| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200706005937.png +.. |image1| image:: http://image.iswbm.com/20200708223651.png .. |image2| image:: http://image.iswbm.com/20200705171112.png -.. |image3| image:: http://image.iswbm.com/20200705172457.png -.. |image4| image:: http://image.iswbm.com/20200705193118.png -.. |image5| image:: http://image.iswbm.com/20200705211401.png -.. |image6| image:: http://image.iswbm.com/20200705220524.png -.. |image7| image:: http://image.iswbm.com/20200705212820.png -.. |image8| image:: http://image.iswbm.com/20200705215033.png -.. |image9| image:: http://image.iswbm.com/20200706005103.png -.. |image10| image:: http://image.iswbm.com/image-20200705233446534.png -.. |image11| image:: http://image.iswbm.com/image-20200705233925900.png -.. |image12| image:: http://image.iswbm.com/image-20200706001903273.png -.. |image13| image:: http://image.iswbm.com/20200607174235.png +.. |image3| image:: http://image.iswbm.com/image-20200707221742925.png +.. |image4| image:: http://image.iswbm.com/image-20200707222024941.png +.. |image5| image:: http://image.iswbm.com/20200705212820.png +.. |image6| image:: http://image.iswbm.com/20200705215033.png +.. |image7| image:: http://image.iswbm.com/20200706005103.png +.. |image8| image:: http://image.iswbm.com/image-20200705233446534.png +.. |image9| image:: http://image.iswbm.com/image-20200705233925900.png +.. |image10| image:: http://image.iswbm.com/image-20200706001903273.png +.. |image11| image:: http://image.iswbm.com/20200607174235.png From 51cdb9fe2a7955053d0d07eb6e53ec8babc4ba6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 10 Jul 2020 18:05:07 +0800 Subject: [PATCH 108/147] update --- source/c08/c08_01.md | 2 +- source/c08/c08_05.md | 8 ++++++++ source/c08/c09_16.md | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/source/c08/c08_01.md b/source/c08/c08_01.md index ff8812c..33f3ea1 100644 --- a/source/c08/c08_01.md +++ b/source/c08/c08_01.md @@ -134,7 +134,7 @@ $neutron subnet-create --name public\ public 172.20.20.0/24 # 创建子网,更多选项可以查看 neutron subnet-create -h -neutron subnet-create --name 192.168.2.0/24 --allocation-pool start=192.168.2.200,end=192.168.2.230 --gateway 192.168.2.253 --dns-nameserver 114.114.114.114 --disable-dhcp private 192.168.2.0/24 +neutron subnet-create --name private --allocation-pool start=192.168.2.200,end=192.168.2.230 --gateway 192.168.2.253 --dns-nameserver 114.114.114.114 --disable-dhcp public 192.168.2.0/24 # 查看网络 $ neutron net-show diff --git a/source/c08/c08_05.md b/source/c08/c08_05.md index 1058733..050988a 100644 --- a/source/c08/c08_05.md +++ b/source/c08/c08_05.md @@ -222,6 +222,14 @@ compute的资源上报,是在 `nova/compute/resource_tracker.py:_init_compute_ +上面是创建虚拟机时,创建 network_info 对象的过程。 + +如果虚拟机已存在,那么如果获取呢,可以用这个函数 + +```python +network_info = compute_utils.get_nw_info_for_instance(instance) +``` + ## 8.5.8 手动引入上下文环境 diff --git a/source/c08/c09_16.md b/source/c08/c09_16.md index b5e9e33..d8f3409 100644 --- a/source/c08/c09_16.md +++ b/source/c08/c09_16.md @@ -187,7 +187,39 @@ DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时 ## 2. bandwidth_limit 带宽限速 +华云 QoS :https://support.huawei.com/enterprise/zh/doc/EDOC1100055155/101c0e7b +http://blog.chinaunix.net/uid-20530497-id-2490382.html + +https://www.zhihu.com/question/21053403 + + + +qos 查询命令 + +```shell +$ neutron qos-policy-list +$ neutron qos-bandwidth-limit-rule-list 16fbb0c2-b7ac-4053-a85f-75fb72c3ab55 +``` + + + +在 dpdk 宿主机上查询限速 + +```shell +# 查询 ingress(宿主机角度) +$ virsh dumpxml 4f6a0708-aeb8-4208-bea8-2c51e6a94948 + +# 查询 egress(虚拟机角度) +$ ovs-vsctl list interface vhu198063e9-97 + +# 查询 ingress(虚拟机角度) +$ ovs-appctl -t ovs-vswitchd qos/show vhu198063e9-97 +``` + + + +![](http://image.iswbm.com/20200709171517.png) ## 3. 参考文章 From ee986d51b592648075161714efae4095dca0cd59 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 11 Jul 2020 23:39:04 +0800 Subject: [PATCH 109/147] update --- source/c01/c01_47.md | 185 +++++++++++++++++++++++++++++++++++++++++ source/c01/c01_47.rst | 187 ++++++++++++++++++++++++++++++++++++++++++ source/c07/c07_22.md | 6 +- source/c07/c07_22.rst | 5 ++ source/c08/c08_01.rst | 2 +- source/c08/c08_05.rst | 8 ++ source/c08/c09_16.rst | 34 +++++++- source/c09/c09_02.md | 20 ++++- source/c09/c09_02.rst | 24 +++++- source/c10/c10_07.md | 111 +++++++++++++++++++++++-- source/c10/c10_07.rst | 157 +++++++++++++++++++++++++++++------ 11 files changed, 696 insertions(+), 43 deletions(-) create mode 100644 source/c01/c01_47.md create mode 100644 source/c01/c01_47.rst diff --git a/source/c01/c01_47.md b/source/c01/c01_47.md new file mode 100644 index 0000000..3b8caf0 --- /dev/null +++ b/source/c01/c01_47.md @@ -0,0 +1,185 @@ +# 1.47 少有人知的 Python "重试机制" + +为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 + +这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 + +这里要给大家介绍的是一个第三方库 - `Tenacity` ,它实现了几乎我们可以使用到的所有重试场景,比如: + +1. 在什么情况下才进行重试? +2. 重试几次呢? +3. 重试多久后结束? +4. 每次重试的间隔多长呢? +5. 重试失败后的回调? + +在使用它之前 ,先要安装它 + +```shell +$ pip install tenacity +``` + + + +### **最基本的重试** + +无条件重试,重试之间无间隔 + +```python +from tenacity import retry + +@retry +def test_retry(): + print("等待重试,重试无间隔执行...") + raise Exception + +test_retry() +``` + +无条件重试,但是在重试之前要等待 2 秒 + +```python +from tenacity import retry, wait_fixed + +@retry(wait=wait_fixed(2)) +def test_retry(): + print("等待重试...") + raise Exception + +test_retry() +``` + + + +### 设置停止基本条件 + +只重试7 次 + +```python +from tenacity import retry, stop_after_attempt + +@retry(stop=stop_after_attempt(7)) +def test_retry(): + print("等待重试...") + raise Exception + +test_retry() +``` + +重试 10 秒后不再重试 + +```python +from tenacity import retry, stop_after_delay + +@retry(stop=stop_after_delay(10)) +def test_retry(): + print("等待重试...") + raise Exception + +test_retry() +``` + +或者上面两个条件满足一个就结束重试 + +```python +from tenacity import retry, stop_after_delay, stop_after_attempt + +@retry(stop=(stop_after_delay(10) | stop_after_attempt(7))) +def test_retry(): + print("等待重试...") + raise Exception + +test_retry() +``` + +### 设置何时进行重试 + +在出现特定错误/异常(比如请求超时)的情况下,再进行重试 + +```python +from requests import exceptions +from tenacity import retry, retry_if_exception_type + +@retry(retry=retry_if_exception_type(exceptions.Timeout)) +def test_retry(): + print("等待重试...") + raise exceptions.Timeout + +test_retry() +``` + +在满足自定义条件时,再进行重试。 + +如下示例,当 `test_retry` 函数返回值为 False 时,再进行重试 + +```python +from tenacity import retry, stop_after_attempt, retry_if_result + +def is_false(value): + return value is False + +@retry(stop=stop_after_attempt(3), + retry=retry_if_result(is_false)) +def test_retry(): + return False + +test_retry() +``` + + + +### 重试后错误重新抛出 + +当出现异常后,tenacity 会进行重试,若重试后还是失败,默认情况下,往上抛出的异常会变成 RetryError,而不是最根本的原因。 + +因此可以加一个参数(`reraise=True`),使得当重试失败后,往外抛出的异常还是原来的那个。 + +```python +from tenacity import retry, stop_after_attempt + +@retry(stop=stop_after_attempt(7), reraise=True) +def test_retry(): + print("等待重试...") + raise Exception + +test_retry() +``` + + + +### 设置回调函数 + +当最后一次重试失败后,可以执行一个回调函数 + +```python +from tenacity import * + +def return_last_value(retry_state): + print("执行回调函数") + return retry_state.outcome.result() # 表示返回原函数的返回值 + +def is_false(value): + return value is False + +@retry(stop=stop_after_attempt(3), + retry_error_callback=return_last_value, + retry=retry_if_result(is_false)) +def test_retry(): + print("等待重试中...") + return False + +print(test_retry()) +``` + +输出如下 + +```shell +等待重试中... +等待重试中... +等待重试中... +执行回调函数 +False +``` + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_47.rst b/source/c01/c01_47.rst new file mode 100644 index 0000000..234bde4 --- /dev/null +++ b/source/c01/c01_47.rst @@ -0,0 +1,187 @@ +1.47 少有人知的 Python “重试机制” +================================= + +为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 + +这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 + +这里要给大家介绍的是一个第三方库 - ``Tenacity`` +,它实现了几乎我们可以使用到的所有重试场景,比如: + +1. 在什么情况下才进行重试? +2. 重试几次呢? +3. 重试多久后结束? +4. 每次重试的间隔多长呢? +5. 重试失败后的回调? + +在使用它之前 ,先要安装它 + +.. code:: shell + + $ pip install tenacity + +**最基本的重试** +~~~~~~~~~~~~~~~~ + +无条件重试,重试之间无间隔 + +.. code:: python + + from tenacity import retry + + @retry + def test_retry(): + print("等待重试,重试无间隔执行...") + raise Exception + + test_retry() + +无条件重试,但是在重试之前要等待 2 秒 + +.. code:: python + + from tenacity import retry, wait_fixed + + @retry(wait=wait_fixed(2)) + def test_retry(): + print("等待重试...") + raise Exception + + test_retry() + +设置停止基本条件 +~~~~~~~~~~~~~~~~ + +只重试7 次 + +.. code:: python + + from tenacity import retry, stop_after_attempt + + @retry(stop=stop_after_attempt(7)) + def test_retry(): + print("等待重试...") + raise Exception + + test_retry() + +重试 10 秒后不再重试 + +.. code:: python + + from tenacity import retry, stop_after_delay + + @retry(stop=stop_after_delay(10)) + def test_retry(): + print("等待重试...") + raise Exception + + test_retry() + +或者上面两个条件满足一个就结束重试 + +.. code:: python + + from tenacity import retry, stop_after_delay, stop_after_attempt + + @retry(stop=(stop_after_delay(10) | stop_after_attempt(7))) + def test_retry(): + print("等待重试...") + raise Exception + + test_retry() + +设置何时进行重试 +~~~~~~~~~~~~~~~~ + +在出现特定错误/异常(比如请求超时)的情况下,再进行重试 + +.. code:: python + + from requests import exceptions + from tenacity import retry, retry_if_exception_type + + @retry(retry=retry_if_exception_type(exceptions.Timeout)) + def test_retry(): + print("等待重试...") + raise exceptions.Timeout + + test_retry() + +在满足自定义条件时,再进行重试。 + +如下示例,当 ``test_retry`` 函数返回值为 False 时,再进行重试 + +.. code:: python + + from tenacity import retry, stop_after_attempt, retry_if_result + + def is_false(value): + return value is False + + @retry(stop=stop_after_attempt(3), + retry=retry_if_result(is_false)) + def test_retry(): + return False + + test_retry() + +重试后错误重新抛出 +~~~~~~~~~~~~~~~~~~ + +当出现异常后,tenacity +会进行重试,若重试后还是失败,默认情况下,往上抛出的异常会变成 +RetryError,而不是最根本的原因。 + +因此可以加一个参数(\ ``reraise=True``\ ),使得当重试失败后,往外抛出的异常还是原来的那个。 + +.. code:: python + + from tenacity import retry, stop_after_attempt + + @retry(stop=stop_after_attempt(7), reraise=True) + def test_retry(): + print("等待重试...") + raise Exception + + test_retry() + +设置回调函数 +~~~~~~~~~~~~ + +当最后一次重试失败后,可以执行一个回调函数 + +.. code:: python + + from tenacity import * + + def return_last_value(retry_state): + print("执行回调函数") + return retry_state.outcome.result() # 表示返回原函数的返回值 + + def is_false(value): + return value is False + + @retry(stop=stop_after_attempt(3), + retry_error_callback=return_last_value, + retry=retry_if_result(is_false)) + def test_retry(): + print("等待重试中...") + return False + + print(test_retry()) + +输出如下 + +.. code:: shell + + 等待重试中... + 等待重试中... + 等待重试中... + 执行回调函数 + False + +|image0| + +.. |image0| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c07/c07_22.md b/source/c07/c07_22.md index f65567e..a4ad156 100644 --- a/source/c07/c07_22.md +++ b/source/c07/c07_22.md @@ -181,4 +181,8 @@ rpm –rebuilddb - `--justdb`:当 RPM 数据库损坏或者某些原因产生错误时,可使用这个参数 更新软件在数据库中的相关信息 - `--nosignature`:跳过数字证书的检查,直接安装 - `--prefix 新路径`:将软件安装在指定的路径 -- `--noscripts`:安装时,忽略某些命令的执行 \ No newline at end of file +- `--noscripts`:安装时,忽略某些命令的执行 + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c07/c07_22.rst b/source/c07/c07_22.rst index ccfc5bb..bcaffe2 100644 --- a/source/c07/c07_22.rst +++ b/source/c07/c07_22.rst @@ -183,3 +183,8 @@ rpmdb - ``--nosignature``\ :跳过数字证书的检查,直接安装 - ``--prefix 新路径``\ :将软件安装在指定的路径 - ``--noscripts``\ :安装时,忽略某些命令的执行 + +|image0| + +.. |image0| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index 071db14..170c046 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -143,7 +143,7 @@ aggregate管理 public 172.20.20.0/24 # 创建子网,更多选项可以查看 neutron subnet-create -h - neutron subnet-create --name 192.168.2.0/24 --allocation-pool start=192.168.2.200,end=192.168.2.230 --gateway 192.168.2.253 --dns-nameserver 114.114.114.114 --disable-dhcp private 192.168.2.0/24 + neutron subnet-create --name private --allocation-pool start=192.168.2.200,end=192.168.2.230 --gateway 192.168.2.253 --dns-nameserver 114.114.114.114 --disable-dhcp public 192.168.2.0/24 # 查看网络 $ neutron net-show diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index 570f2b2..0ca7934 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -252,6 +252,14 @@ network_info 对象。 |image21| +上面是创建虚拟机时,创建 network_info 对象的过程。 + +如果虚拟机已存在,那么如果获取呢,可以用这个函数 + +.. code:: python + + network_info = compute_utils.get_nw_info_for_instance(instance) + 8.5.8 手动引入上下文环境 ------------------------ diff --git a/source/c08/c09_16.rst b/source/c08/c09_16.rst index 696e8d5..337a7ac 100644 --- a/source/c08/c09_16.rst +++ b/source/c08/c09_16.rst @@ -199,6 +199,35 @@ DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时 2. bandwidth_limit 带宽限速 --------------------------- +华云 QoS +:https://support.huawei.com/enterprise/zh/doc/EDOC1100055155/101c0e7b + +http://blog.chinaunix.net/uid-20530497-id-2490382.html + +https://www.zhihu.com/question/21053403 + +qos 查询命令 + +.. code:: shell + + $ neutron qos-policy-list + $ neutron qos-bandwidth-limit-rule-list 16fbb0c2-b7ac-4053-a85f-75fb72c3ab55 + +在 dpdk 宿主机上查询限速 + +.. code:: shell + + # 查询 ingress(宿主机角度) + $ virsh dumpxml 4f6a0708-aeb8-4208-bea8-2c51e6a94948 + + # 查询 egress(虚拟机角度) + $ ovs-vsctl list interface vhu198063e9-97 + + # 查询 ingress(虚拟机角度) + $ ovs-appctl -t ovs-vswitchd qos/show vhu198063e9-97 + +|image6| + 3. 参考文章 ----------- @@ -210,7 +239,7 @@ DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时 -------------- -|image6| +|image7| .. |image0| image:: http://image.iswbm.com/20200701155207.png .. |image1| image:: http://img.wandouip.com/crawler/article/2019411/546f47120fa14a2a1cfc44c9e8a48e71 @@ -218,5 +247,6 @@ DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时 .. |003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| image:: https://s4.51cto.com/images/blog/201804/25/3c42c64b7240ef12b991f69644a145ac.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= .. |003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| image:: https://s4.51cto.com/images/blog/201804/25/4f03b09e8081d8fc7073f29870bc1c95.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= .. |image5| image:: http://image.iswbm.com/20200701155207.png -.. |image6| image:: http://image.iswbm.com/20200607174235.png +.. |image6| image:: http://image.iswbm.com/20200709171517.png +.. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md index ec909f5..3f658ee 100644 --- a/source/c09/c09_02.md +++ b/source/c09/c09_02.md @@ -29,10 +29,16 @@ NewFileMenu 可以解决你的烦恼 -### 3. Magnet/Moon +### 3. Magnet/Moon/Rectangle 分屏工作几乎是办公必备技能。 +Magnet:收费 + +Moon:收费 + +Rectangle:[官网](https://rectangleapp.com)免费下载,支持拖动分屏 + ### 4. 快贴 @@ -41,6 +47,12 @@ https://clipber.com/ 用于多设备间剪切板同步共享 +### CopyLess + +剪切板工具,免费可支持 100 个剪切项目,但是双击粘贴需要在偏好设置里额外安装一个插件(具体打开设置后有步骤指引)。 + +有批量复制粘贴功能。 + ### GoodSync 和 windows 平台同步文件 @@ -83,9 +95,13 @@ ShortCat:在系统栏也可以搜索聚焦 TouchBarServer:在外接屏幕上调出 touchbar +Hazeover:干扰调节神器,把活动窗口除久的其他范围调暗 + ## 4. 图片影音 -Snipaste:截图工具 +Snipaste:截图工具,支持钉图 + +iShot:截图工具,支持长截图/滚动截图,钉图 Capture Gif:Gif 录制(不推荐) diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index a451c79..c1ad1be 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -30,11 +30,17 @@ NewFileMenu 可以解决你的烦恼 |image1| -3. Magnet/Moon -~~~~~~~~~~~~~~ +3. Magnet/Moon/Rectangle +~~~~~~~~~~~~~~~~~~~~~~~~ 分屏工作几乎是办公必备技能。 +Magnet:收费 + +Moon:收费 + +Rectangle:\ `官网 `__\ 免费下载,支持拖动分屏 + 4. 快贴 ~~~~~~~ @@ -42,6 +48,14 @@ https://clipber.com/ 用于多设备间剪切板同步共享 +CopyLess +~~~~~~~~ + +剪切板工具,免费可支持 100 +个剪切项目,但是双击粘贴需要在偏好设置里额外安装一个插件(具体打开设置后有步骤指引)。 + +有批量复制粘贴功能。 + GoodSync ~~~~~~~~ @@ -88,10 +102,14 @@ ShortCat:在系统栏也可以搜索聚焦 TouchBarServer:在外接屏幕上调出 touchbar +Hazeover:干扰调节神器,把活动窗口除久的其他范围调暗 + 4. 图片影音 ----------- -Snipaste:截图工具 +Snipaste:截图工具,支持钉图 + +iShot:截图工具,支持长截图/滚动截图,钉图 Capture Gif:Gif 录制(不推荐) diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md index 21e7835..5290a55 100644 --- a/source/c10/c10_07.md +++ b/source/c10/c10_07.md @@ -4,7 +4,7 @@ -![](http://image.iswbm.com/20200708223651.png) +![](http://image.iswbm.com/20200711143644.png) ## 1. 什么是跨域请求 @@ -164,7 +164,7 @@ JWT 是本篇文章重要知识点之一,下面我会详细说说关于 JWT 5. 后端收到新请求后,会使用密钥验证 JWT 签名。 6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 -![JSON Web Token (JWT) — The right way of implementing, with Node.js](http://image.iswbm.com/1*tW-8Y2edq04b4__zF0Jm9Q.png) +![](http://image.iswbm.com/20200711144042.png) JWT 的诞生并不是解决 CSRF 跨域攻击,而是解决跨域认证的难题。 @@ -210,7 +210,7 @@ JWT 的头部承载两部分信息: } ``` -然后将头部进行 Base64URL 算法加密(该加密是可以对称解密的),构成了第一部分 +然后将头部进行 Base64URL 算法编码转换,构成了第一部分 ```shell eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 @@ -242,7 +242,7 @@ JWT 规定了7个官方字段,供选用: } ``` -然后将其进行 Base64URL 算法加密,得到 JWT 的第二部分。 +然后将其进行 Base64URL 算法转换,得到 JWT 的第二部分。 ```shell eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 @@ -265,7 +265,9 @@ HMACSHA256( ## 6. 如何手动生成 JWT? -如果你想手动生成一个 JWT 用于测试,可以使用 `https://jwt.io/ `这个网站 。 +如果你想手动生成一个 JWT 用于测试,有两种方法 + +**第一种:使用 https://jwt.io/ 这个网站 。** 我使用前面的 header 和 payload,然后使用 secret 密钥:`Python` @@ -277,6 +279,47 @@ eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4 ![](http://image.iswbm.com/20200706005103.png) +**第二种:使用 Python 代码生成** + +首先安装一下 pyjwt 这个库 + +```shell +$ pip install pyjwt +``` + +然后就可以在代码中使用它 + +```python +import jwt +import datetime +import uuid + +salt = 'minggezuishuai' + +# 构造header , 这里不写默认的也是 +headers = { + 'typ': 'JWT', + 'alg': 'HS256' +} + +# 构造payload +payload = { + 'user_id': str(uuid.uuid4()), # 自定义用户ID + 'username': "wangbm", # 自定义用户名 + 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间,取现在时间,五分钟后token失效 +} +token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8') + +# token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8 +``` + +如果你只是测试使用,完全不用写那么多代码,用命令行即可 + +```shell +$ pyjwt --key="minggezuishuai" encode user_id=888f20d9-07ed-41bd-b329-17c6f058a14e username=wangbm exp=+120 +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 +``` + ## 7. Base64URL 算法 前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。 @@ -359,7 +402,61 @@ HMACSHA256( payload 里同时还有用户的相关信息,有了这些信息后,后端就可以知道这是哪个用户的请求了,到这里一切都验证通过,就可以执行相关的业务逻辑了。 -## 11. 总结写在最后 +前面我使用了 `pyjwt` 这个来生成 JWT ,事实上,这个库也可以用来验证 token。 + +使用 jwt 的 decode 会先验签再解码取得 payload 的信息。 + +```python +>>> import jwt +>>> token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8" +>>> jwt.decode(token, 'minggezuishuai', algorithms=['HS256']) +{'user_id': '888f20d9-07ed-41bd-b329-17c6f058a14e', 'username': 'wangbm', 'exp': 1594434326} +>>> +``` + +验签同样也可以使用命令行 + +```shell +$ pyjwt --key="minggezuishuai" decode eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 +{"user_id": "888f20d9-07ed-41bd-b329-17c6f058a14e", "username": "wangbm", "exp": 1594434859} +``` + +如果不想验证签名及有效期,而只是想取下payload,只需加个`--no-verify` 参数即可 + +```shell +$ pyjwt --key="minggezuishuai" decode --no-verify {token} +``` + +更的详细使用方法,可以执行 `pyjwt --help` 学习或者前往官方文档:https://pyjwt.readthedocs.io/en/latest/index.html + +## 11. JWT 的最佳搭配 + +在真正的业务中,是有可能使用 payload 来存放一些用户的敏感信息的,由于 payload 是采用 Base64URL 转换而成,它是可逆的,因此当你在 payload 存放敏感信息时,需要保证 JWT 的安全性,不能让其暴露在 『阳光』下。 + +为此,JWT 最好与 HTTPS 配合使用,利用 HTTPS 的非对称加密来保证 JWT 的安全。 + +具体是如何保障的呢? + +HTTPS 是基于 SSL/TLS 的非对称加密算法工作的。 + +在非对称加密算法的规则下,服务器会拥有一个叫做『私钥』的东西,它是私有的,除了服务器之外,不能再有第二个人知道它。 + +而相对的,所有的客户端(浏览器)同时也会有一个叫做『公钥』的东西,它是对所有人公开的,任何人都可以拥有它。它与『私钥』合称为一个密钥对。 + +公钥和私钥的规则是: + +- **公钥加密的东西,只有私钥能解。**因此如果 JWT 的 payload 里有你的敏感信息,那也不要紧,只要把 JWT 用公钥(**前提是这个公钥得是正确的,下面会说到**)加密一下,那黑客就算拿到了这个密文,也无法解密,因为私钥只有服务器才有。 +- **私钥加密的东西,所有的公钥也都能解。**因此服务器发给客户端的 JWT 的payload 尽量不要有敏感信息。 + +![](http://image.iswbm.com/20200711141903.png) + +那么问题又来了,如果客户端拿到的公钥,是黑客伪造的,客户端拿着这个假公钥加密自己的敏感信息,然后发出去,黑客在拿到这个用自己伪造的公钥加密的数据,非常开心,因为这个公钥对应的私钥在自己手里,自己是可以解密得到里面的数据的。 + +因此如何保证服务器发给客户端(浏览器)的公钥是正确的呢? + +答案是通过**数字证书**来保证。但是由于这个不是本文的重点,因此我将这块内容放在后面的文章详细解释。 + +## 12. 总结写在最后 最后,我总结一下,本文的要点: @@ -372,7 +469,7 @@ payload 里同时还有用户的相关信息,有了这些信息后,后端就 7. payload 是经过 base64URL 算法转换而成的字符串,是可逆的,因此尽量不要存放敏感数据,如若非要存放敏感数据,最好与 HTTPS 协议搭配使用,避免数据泄露。 8. JWT 的保存位置与方式,没有绝对的方案,具体如何选择要视情况而定。 -## 12. 参考文章 +## 13. 参考文章 - [咱妈说别乱点链接之浅谈CSRF攻击](https://cloud.tencent.com/developer/article/1004943) - [JSON Web Token 入门教程](http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html) diff --git a/source/c10/c10_07.rst b/source/c10/c10_07.rst index 15971a4..cf49fe7 100644 --- a/source/c10/c10_07.rst +++ b/source/c10/c10_07.rst @@ -215,11 +215,7 @@ JWT 是本篇文章重要知识点之一,下面我会详细说说关于 JWT 6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 -.. figure:: http://image.iswbm.com/1*tW-8Y2edq04b4__zF0Jm9Q.png - :alt: JSON Web Token (JWT) — The right way of implementing, with - Node.js - - JSON Web Token (JWT) — The right way of implementing, with Node.js +|image5| JWT 的诞生并不是解决 CSRF 跨域攻击,而是解决跨域认证的难题。 @@ -246,13 +242,13 @@ JWT 其实就是一个字符串,比如下面这样 仔细观察,会发现它里面有三个 ``.`` ,以 ``.`` 为分界,可以将 JWT 分为三部分。 -|image5| +|image6| 1. **第一部分**\ :头部(Header) 2. **第二部分**\ :载荷(Payload) 3. **第三部分**\ :签名(Signature) -|image6| +|image7| 5.1 头部(Header) ~~~~~~~~~~~~~~~~~~ @@ -271,8 +267,7 @@ JWT 的头部承载两部分信息: "alg": "HS256" } -然后将头部进行 Base64URL -算法加密(该加密是可以对称解密的),构成了第一部分 +然后将头部进行 Base64URL 算法编码转换,构成了第一部分 .. code:: shell @@ -307,7 +302,7 @@ JWT 规定了7个官方字段,供选用: "admin": true } -然后将其进行 Base64URL 算法加密,得到 JWT 的第二部分。 +然后将其进行 Base64URL 算法转换,得到 JWT 的第二部分。 .. code:: shell @@ -335,8 +330,9 @@ SHA256),按照下面的公式产生签名。 6. 如何手动生成 JWT? --------------------- -如果你想手动生成一个 JWT 用于测试,可以使用 -``https://jwt.io/``\ 这个网站 。 +如果你想手动生成一个 JWT 用于测试,有两种方法 + +**第一种:使用 https://jwt.io/ 这个网站 。** 我使用前面的 header 和 payload,然后使用 secret 密钥:\ ``Python`` @@ -346,7 +342,48 @@ SHA256),按照下面的公式产生签名。 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.3wGDum3_A8tAt1bdal5CpYbIUlpHfPQxs96Ijx883kI -|image7| +|image8| + +**第二种:使用 Python 代码生成** + +首先安装一下 pyjwt 这个库 + +.. code:: shell + + $ pip install pyjwt + +然后就可以在代码中使用它 + +.. code:: python + + import jwt + import datetime + import uuid + + salt = 'minggezuishuai' + + # 构造header , 这里不写默认的也是 + headers = { + 'typ': 'JWT', + 'alg': 'HS256' + } + + # 构造payload + payload = { + 'user_id': str(uuid.uuid4()), # 自定义用户ID + 'username': "wangbm", # 自定义用户名 + 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间,取现在时间,五分钟后token失效 + } + token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8') + + # token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8 + +如果你只是测试使用,完全不用写那么多代码,用命令行即可 + +.. code:: shell + + $ pyjwt --key="minggezuishuai" encode user_id=888f20d9-07ed-41bd-b329-17c6f058a14e username=wangbm exp=+120 + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 7. Base64URL 算法 ----------------- @@ -373,13 +410,13 @@ storage,因为这样,第三方的脚本就能直接获取到。 作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 -|image8| +|image9| 再比如这一篇帖子(\ `JWT(JSON Web Token) : Implementation with Node `__\ )提到了要把 JWT 保存到 local-storage。 -|image9| +|image10| 因此,我决定不再看网络上关于 『应将 JWT 保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 @@ -412,7 +449,7 @@ CSRF 攻击风险。 如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 -|image10| +|image11| 是的,事实也确实如此。 @@ -471,7 +508,71 @@ payload 中,有 Token 的过期时间,可以通过它来检查 Token 是否 payload 里同时还有用户的相关信息,有了这些信息后,后端就可以知道这是哪个用户的请求了,到这里一切都验证通过,就可以执行相关的业务逻辑了。 -11. 总结写在最后 +前面我使用了 ``pyjwt`` 这个来生成 JWT ,事实上,这个库也可以用来验证 +token。 + +使用 jwt 的 decode 会先验签再解码取得 payload 的信息。 + +.. code:: python + + >>> import jwt + >>> token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8" + >>> jwt.decode(token, 'minggezuishuai', algorithms=['HS256']) + {'user_id': '888f20d9-07ed-41bd-b329-17c6f058a14e', 'username': 'wangbm', 'exp': 1594434326} + >>> + +验签同样也可以使用命令行 + +.. code:: shell + + $ pyjwt --key="minggezuishuai" decode eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 + {"user_id": "888f20d9-07ed-41bd-b329-17c6f058a14e", "username": "wangbm", "exp": 1594434859} + +如果不想验证签名及有效期,而只是想取下payload,只需加个\ ``--no-verify`` +参数即可 + +.. code:: shell + + $ pyjwt --key="minggezuishuai" decode --no-verify {token} + +更的详细使用方法,可以执行 ``pyjwt --help`` +学习或者前往官方文档:https://pyjwt.readthedocs.io/en/latest/index.html + +11. JWT 的最佳搭配 +------------------ + +在真正的业务中,是有可能使用 payload 来存放一些用户的敏感信息的,由于 +payload 是采用 Base64URL 转换而成,它是可逆的,因此当你在 payload +存放敏感信息时,需要保证 JWT 的安全性,不能让其暴露在 『阳光』下。 + +为此,JWT 最好与 HTTPS 配合使用,利用 HTTPS 的非对称加密来保证 JWT +的安全。 + +具体是如何保障的呢? + +HTTPS 是基于 SSL/TLS 的非对称加密算法工作的。 + +在非对称加密算法的规则下,服务器会拥有一个叫做『私钥』的东西,它是私有的,除了服务器之外,不能再有第二个人知道它。 + +而相对的,所有的客户端(浏览器)同时也会有一个叫做『公钥』的东西,它是对所有人公开的,任何人都可以拥有它。它与『私钥』合称为一个密钥对。 + +公钥和私钥的规则是: + +- **公钥加密的东西,只有私钥能解。**\ 因此如果 JWT 的 payload + 里有你的敏感信息,那也不要紧,只要把 JWT + 用公钥(\ **前提是这个公钥得是正确的,下面会说到**\ )加密一下,那黑客就算拿到了这个密文,也无法解密,因为私钥只有服务器才有。 +- **私钥加密的东西,所有的公钥也都能解。**\ 因此服务器发给客户端的 JWT + 的payload 尽量不要有敏感信息。 + +|image12| + +那么问题又来了,如果客户端拿到的公钥,是黑客伪造的,客户端拿着这个假公钥加密自己的敏感信息,然后发出去,黑客在拿到这个用自己伪造的公钥加密的数据,非常开心,因为这个公钥对应的私钥在自己手里,自己是可以解密得到里面的数据的。 + +因此如何保证服务器发给客户端(浏览器)的公钥是正确的呢? + +答案是通过\ **数字证书**\ 来保证。但是由于这个不是本文的重点,因此我将这块内容放在后面的文章详细解释。 + +12. 总结写在最后 ---------------- 最后,我总结一下,本文的要点: @@ -493,7 +594,7 @@ payload HTTPS 协议搭配使用,避免数据泄露。 8. JWT 的保存位置与方式,没有绝对的方案,具体如何选择要视情况而定。 -12. 参考文章 +13. 参考文章 ------------ - `咱妈说别乱点链接之浅谈CSRF攻击 `__ @@ -504,18 +605,20 @@ payload - `JWT 超详细分析 `__ -|image11| +|image13| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200708223651.png +.. |image1| image:: http://image.iswbm.com/20200711143644.png .. |image2| image:: http://image.iswbm.com/20200705171112.png .. |image3| image:: http://image.iswbm.com/image-20200707221742925.png .. |image4| image:: http://image.iswbm.com/image-20200707222024941.png -.. |image5| image:: http://image.iswbm.com/20200705212820.png -.. |image6| image:: http://image.iswbm.com/20200705215033.png -.. |image7| image:: http://image.iswbm.com/20200706005103.png -.. |image8| image:: http://image.iswbm.com/image-20200705233446534.png -.. |image9| image:: http://image.iswbm.com/image-20200705233925900.png -.. |image10| image:: http://image.iswbm.com/image-20200706001903273.png -.. |image11| image:: http://image.iswbm.com/20200607174235.png +.. |image5| image:: http://image.iswbm.com/20200711144042.png +.. |image6| image:: http://image.iswbm.com/20200705212820.png +.. |image7| image:: http://image.iswbm.com/20200705215033.png +.. |image8| image:: http://image.iswbm.com/20200706005103.png +.. |image9| image:: http://image.iswbm.com/image-20200705233446534.png +.. |image10| image:: http://image.iswbm.com/image-20200705233925900.png +.. |image11| image:: http://image.iswbm.com/image-20200706001903273.png +.. |image12| image:: http://image.iswbm.com/20200711141903.png +.. |image13| image:: http://image.iswbm.com/20200607174235.png From cbc4fbb1af232681798c5b1385a6e92d46fe84f5 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 12 Jul 2020 15:24:59 +0800 Subject: [PATCH 110/147] update --- source/c01/c01_02.md | 19 +++++++++------- source/c01/c01_02.rst | 53 +++++++++++++++++-------------------------- source/conf.py | 4 ++-- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/source/c01/c01_02.md b/source/c01/c01_02.md index a53cbc9..cb71e7a 100644 --- a/source/c01/c01_02.md +++ b/source/c01/c01_02.md @@ -19,29 +19,29 @@ Python 是一门动态语言,有了自省,就能让程序在运行时能够 在 console 模式下,输入 `help()` ,可以看到输出了一段帮助文档,教你如何使用这个 help,当你看到提示符变成了 `help>` 时,这时候就进入了 help 模式。 -![image-20200606121047415](/Users/MING/Library/Application Support/typora-user-images/image-20200606121047415.png) +![](http://image.iswbm.com/image-20200606121047415.png) 此时你可以键入你想要了解的模块、语法等,help 告诉你如何使用。 比如我输入 keywords ,就可以看到 Python 里所有的关键字。再输入 modules 就可以查看 Python 中所有的内置模块。 -![image-20200606121544062](/Users/MING/Library/Application Support/typora-user-images/image-20200606121544062.png) +![](http://image.iswbm.com/image-20200606121544062.png) 输入 modules + `指定包名`,就可以查看这个包下有哪些模块 -![image-20200606121942898](/Users/MING/Library/Application Support/typora-user-images/image-20200606121942898.png) +![](http://image.iswbm.com/image-20200606121942898.png) 如果你想学习某个包要如何使用,可以直接在 help 模式下输入 `包名`,就像下面这样,我就可以获得一份 json 的帮助文档。 -![image-20200606122408522](/Users/MING/Library/Application Support/typora-user-images/image-20200606122408522.png) +![](http://image.iswbm.com/image-20200606122408522.png) 如果你想学习某个关键字的用法,可以在 help 模式下直接键入 `关键字` 查询用法,比如我直接键入 `for` 。 -![image-20200606133933401](/Users/MING/Library/Application Support/typora-user-images/image-20200606133933401.png) +![](http://image.iswbm.com/image-20200606133933401.png) 查完后,使用 quit 就可以退出 help 模式了。 -![image-20200606123145109](/Users/MING/Library/Application Support/typora-user-images/image-20200606123145109.png) +![](http://image.iswbm.com/image-20200606123145109.png) 如果你觉得进入 help 模式太麻烦,可以在 console 模式下直接查询 @@ -55,13 +55,13 @@ Python 是一门动态语言,有了自省,就能让程序在运行时能够 dir() 函数可能是 Python 自省机制中最著名的部分了。它返回传递给它的任何对象的属性名称经过排序的列表。如果不指定对象,则 dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 keyword 模块,并观察它揭示了什么: -![image-20200606134519352](/Users/MING/Library/Application Support/typora-user-images/image-20200606134519352.png) +![](http://image.iswbm.com/image-20200606134519352.png) ### \__doc__ 使用 `__doc__` 这个魔法方法,可以查询该模块的文档,它输出的内容和 help() 一样。 -![image-20200606134858285](/Users/MING/Library/Application Support/typora-user-images/image-20200606134858285.png) +![](http://image.iswbm.com/image-20200606134858285.png) ## 2. 应用到实际开发中 @@ -158,3 +158,6 @@ True >>> ``` + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_02.rst b/source/c01/c01_02.rst index b4a9800..f59ecfb 100755 --- a/source/c01/c01_02.rst +++ b/source/c01/c01_02.rst @@ -26,50 +26,32 @@ help() ,可以看到输出了一段帮助文档,教你如何使用这个 help,当你看到提示符变成了 ``help>`` 时,这时候就进入了 help 模式。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606121047415.png - :alt: image-20200606121047415 - - image-20200606121047415 +|image0| 此时你可以键入你想要了解的模块、语法等,help 告诉你如何使用。 比如我输入 keywords ,就可以看到 Python 里所有的关键字。再输入 modules 就可以查看 Python 中所有的内置模块。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606121544062.png - :alt: image-20200606121544062 - - image-20200606121544062 +|image1| 输入 modules + ``指定包名``\ ,就可以查看这个包下有哪些模块 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606121942898.png - :alt: image-20200606121942898 - - image-20200606121942898 +|image2| 如果你想学习某个包要如何使用,可以直接在 help 模式下输入 ``包名``\ ,就像下面这样,我就可以获得一份 json 的帮助文档。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606122408522.png - :alt: image-20200606122408522 - - image-20200606122408522 +|image3| 如果你想学习某个关键字的用法,可以在 help 模式下直接键入 ``关键字`` 查询用法,比如我直接键入 ``for`` 。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606133933401.png - :alt: image-20200606133933401 - - image-20200606133933401 +|image4| 查完后,使用 quit 就可以退出 help 模式了。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606123145109.png - :alt: image-20200606123145109 - - image-20200606123145109 +|image5| 如果你觉得进入 help 模式太麻烦,可以在 console 模式下直接查询 @@ -85,10 +67,7 @@ dir() 函数可能是 Python dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 keyword 模块,并观察它揭示了什么: -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606134519352.png - :alt: image-20200606134519352 - - image-20200606134519352 +|image6| \__doc_\_ ~~~~~~~~~ @@ -96,10 +75,7 @@ dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 key 使用 ``__doc__`` 这个魔法方法,可以查询该模块的文档,它输出的内容和 help() 一样。 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200606134858285.png - :alt: image-20200606134858285 - - image-20200606134858285 +|image7| 2. 应用到实际开发中 ------------------- @@ -205,3 +181,16 @@ callable() >>> callable(str) True >>> + +|image8| + +.. |image0| image:: http://image.iswbm.com/image-20200606121047415.png +.. |image1| image:: http://image.iswbm.com/image-20200606121544062.png +.. |image2| image:: http://image.iswbm.com/image-20200606121942898.png +.. |image3| image:: http://image.iswbm.com/image-20200606122408522.png +.. |image4| image:: http://image.iswbm.com/image-20200606133933401.png +.. |image5| image:: http://image.iswbm.com/image-20200606123145109.png +.. |image6| image:: http://image.iswbm.com/image-20200606134519352.png +.. |image7| image:: http://image.iswbm.com/image-20200606134858285.png +.. |image8| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/conf.py b/source/conf.py index 68169c4..6fb6b49 100755 --- a/source/conf.py +++ b/source/conf.py @@ -140,11 +140,11 @@ file.write(content) author = '王炳明' -copyright = '2020, Go编程时光' +copyright = '2020, Python编程时光' exclude_patterns = ['_build'] extensions = ['sphinxcontrib.disqus'] # Add to this list. master_doc = 'index' -project = 'GolangCodingTime' +project = 'Python编程时光' release = '1.0' version = '1.0' From 03e78872186e6f569ea8b3cbe90b7f304d6a558d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 23 Jul 2020 18:01:27 +0800 Subject: [PATCH 111/147] Update --- source/c04/c04_15.md | 30 ++++++ source/c08/{c09_16.md => c08_16.md} | 145 +++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 3 deletions(-) rename source/c08/{c09_16.md => c08_16.md} (60%) diff --git a/source/c04/c04_15.md b/source/c04/c04_15.md index 3756bf9..6b53a7b 100644 --- a/source/c04/c04_15.md +++ b/source/c04/c04_15.md @@ -917,6 +917,36 @@ PyCharm 打开一个文件,就占用一个标签面。 ![](http://image.iswbm.com/20200420090428.png) +## 4.15.37 快速输入自定义代码片段 + +在 PyCharm 中有一个功能叫 Live Template,它可以用来自定义一些常用的代码片段。 + +比如下面这段,几乎是写 Python 脚本必备的 + +```python +if __name__ == '__main__': +``` + +当你在PyCharm 中编码 python 代码时,只要输入 main ,PyCharm 就会在 Live Template 里找到定义过的代码片段,然后只要直接键入回车,就可以生成这段代码。 + +再比如说,我通常会定义简单的装饰器代码 + +![](http://image.iswbm.com/20200723161209.png) + +这样当我要定义一个最简单的装饰器时,只要输入 `deco` 再直接敲入回车就行啦。 + +![](http://image.iswbm.com/20200723161307.png) + + + + + +[PyCharm 使用技巧](https://blog.csdn.net/xiemanr/category_6928127.html) + + + + + ## 附录 - [PyCharm 快捷键 Mac 版 ](https://resources.jetbrains.com/storage/products/pycharm/docs/PyCharm_ReferenceCard_mac.pdf) diff --git a/source/c08/c09_16.md b/source/c08/c08_16.md similarity index 60% rename from source/c08/c09_16.md rename to source/c08/c08_16.md index d8f3409..153d9e4 100644 --- a/source/c08/c09_16.md +++ b/source/c08/c08_16.md @@ -70,7 +70,7 @@ ToS 字段,总共 8 个 bit 为优选权子字段,现在已经废弃,这个字段默认值是000,从wireshark抓包结果看,表示的是: -![003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明](https://s4.51cto.com/images/blog/201804/25/ede8e1de3c98c2fdfeb044cb0cf74034.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) +![](http://image.iswbm.com/20200723155027.png) 但是在某些协议中仍然是有用的,比如 OSPFv2 协议 @@ -104,9 +104,9 @@ ToS 字段,总共 8 个 bit IP首部中的ToS字段,只能表示一种服务类别,也就是:这4bit字段中,最多只能有一个bit字段为1。 看下不同应用下该4bit字段对应的值: -![003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明](https://s4.51cto.com/images/blog/201804/25/3c42c64b7240ef12b991f69644a145ac.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) +![](http://image.iswbm.com/20200723154704.png) 翻译过来就是: -![003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明](https://s4.51cto.com/images/blog/201804/25/4f03b09e8081d8fc7073f29870bc1c95.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) +![](http://image.iswbm.com/20200723154742.png) **最小延迟**,对应于对延迟敏感的应用,如telnet和人login等。 **最大吞吐量**,对应于对吞吐量要求比较高的应用,如FTP文件应用,对文件传输吞吐量有比较高的要求。 **最高可靠性**,对网络传输可靠性要求高的应用,如使用SNMP的应用、路由协议等等。 @@ -221,6 +221,145 @@ $ ovs-appctl -t ovs-vswitchd qos/show vhu198063e9-97 ![](http://image.iswbm.com/20200709171517.png) + + +测试限速 + +average: 100M + + + + + +```shell +# burst:50 +$ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=7500 __system__vif_outbound_average=12500 __system__vif_outbound_burst=6000 + + +# burst:60 +$ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=7500 __system__vif_outbound_average=12500 __system__vif_outbound_burst=7500 + +# burst:70 +$ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=8750 __system__vif_outbound_average=12500 __system__vif_outbound_burst=8750 + +# burst:80 +$ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=10000 __system__vif_outbound_average=12500 __system__vif_outbound_burst=10000 +``` + + + +## 3. 带宽单位换算 + +带宽,英文名 Bandwidth,在不同领域的含义各不相同,而在网络服务中,带宽是指单位时间内的流经数据量。 + +数据量的单位一般有两种:bit 和 Byte。 + +因此,带宽的单位即为,b/s 或者 B/s,如果数值较大,可以使用 K 表示千(Kb/s 或者 KB/s),M 表示 百万(Mb/s 或者 MB/s)。 + +- Mb/s:Million bits per second的缩写,是一种传输速率单位,指每秒传输的位(比特)数量。通常用于运营商带宽速率的计量。 + +- MB/s:Million Bytes per second的缩写,是一种传输速率单位,指每秒传输的字节数量。即是我们平时说的下载速度。 + + + +Byte 和 bit 是不一样的,Byte = 8 bit,而不同的地方单位不同,需要进行换算,下表整理了在 VMP 中的各个位置的单位 + +| type | cloud | meta | xml | qos rule | ovs-vsctl/ovs-appctl | +| ---- | ----- | ---- | ---- | -------- | -------------------- | +| ovs | bit | Byte | Byte | | | +| dpdk | bit | Byte | | bit | bit | + +## 4. 带宽限制效果 + +### CentOS 安装 iperf3 + +源码编译安装 iperf3 + +```shell +yum -y install gcc make wget +cd /tmp +wget https://iperf.fr/download/source/iperf-3.1.3-source.tar.gz +tar zxvf iperf-3.1.3-source.tar.gz +cd iperf-3.1.3 +./configure +make +make install +``` + +使用 rpm 包安装(更简单,推荐) + +```shell +$ rpm -ih ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/updates/24/x86_64/i/iperf3-3.1.3-1.fc24.x86_64.rpm +``` + +### Windows 安装 iperf3 + +所有的 windows 可用的 iperf3 安装包都可以从这个页面下载:https://iperf.fr/iperf-download.php + +这里直接给出两个 windows 常用的 + +- windows 10 64位:https://iperf.fr/download/windows/iperf-3.1.3-win64.zip +- windows 10 32 位:https://iperf.fr/download/windows/iperf-3.1.3-win32.zip + +下载到电脑到,并解压后,会得到两个文件:`cygwin1.dll` 和 `iperf3.exe`,将这两个文件拷贝到 `c:\windows` 目录下。 + +最后打开 cmd,执行 `iperf3 --version` ,若安装成功,会打印出版本信息。 + +### iperf3 使用前关闭防火墙 + +CentOS 关闭防火墙,只要一条命令 + +```shell +$ systemctl stop firewalld +$ systemctl stop iptables +``` + +windows 10 手动设置一下 + +![](http://image.iswbm.com/20200716112516.png) + + + +### iperf3 的使用 + +iperf3 有客户端 和 服务端之别: + +- 服务端:收包,使用 `-s` 参数指定 + +```shell +$ iperf3 -s +``` + +- 客户端:发包,使用 `-c xx.xx.xx.xx` 来指定要往哪个服务端发包 + +```shell +$ iperf3 -c 172.20.20.200 +``` + +iperf3 还有更多的参数,其中有一些是客户端专用的,有一些是服务端专用的,也有一些是二者共用的。 + +具体可以前往这个地址,进行查阅:https://www.cnblogs.com/yingsong/p/5682080.html + +常用的参数有 + +- `-u`:发送 UDP 包,仅客户端 可用 +- `-p`:后接服务端监听的端口 +- `-i`:设置带宽报告的时间间隔,单位为秒 +- `-t`:设置测试的时长,单位为秒 +- `-w`:设置tcp窗口大小,一般可以不用设置,默认即可 + + + + + +## 5. 注意事项 + +1、vm 在 host1 上,从host2 上 iperf 比在 host1 上限速效果更好 + +2、udp 包限速效果差异很大。 + + + ## 3. 参考文章 - https://blog.51cto.com/mangguostudy/2107799 From 4164f0b3c7c237fe24d3952b1d65624937337910 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 28 Jul 2020 08:35:57 +0800 Subject: [PATCH 112/147] Update --- source/c04/c04_06.md | 13 ++ source/c04/c04_06.rst | 12 ++ source/c04/c04_15.rst | 34 +++- source/c08/c08_16.rst | 391 ++++++++++++++++++++++++++++++++++++++++-- source/c09/c09_03.md | 7 + source/c09/c09_03.rst | 8 + source/c10/c10_07.md | 4 +- source/c10/c10_07.rst | 4 +- 8 files changed, 450 insertions(+), 23 deletions(-) diff --git a/source/c04/c04_06.md b/source/c04/c04_06.md index 90e6dc5..9c8085c 100644 --- a/source/c04/c04_06.md +++ b/source/c04/c04_06.md @@ -383,6 +383,19 @@ $ git push origin HEAD --force +### 3.7 删除版本库里的文件 + +```shell +# 加了 --cached 就不会删除工作区的文件 +$ git rm --cached file1.txt + +$ git commit -m "remove file1.txt" + +$ git push origin branch_name +``` + + + ## 四、分支管理 ### 4.1 新建-切换-删除分支 diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 5f68c13..7bc9b85 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -387,6 +387,18 @@ Git中的upstream和downstream的概念是相对的。 # 回退一个版本 $ git push origin HEAD --force +3.7 删除版本库里的文件 +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: shell + + # 加了 --cached 就不会删除工作区的文件 + $ git rm --cached file1.txt + + $ git commit -m "remove file1.txt" + + $ git push origin branch_name + 四、分支管理 ------------ diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index 940729c..dab19b3 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -1075,6 +1075,34 @@ Debug ,而远程调试需要不少前置步骤。 |image103| +4.15.37 快速输入自定义代码片段 +------------------------------ + +在 PyCharm 中有一个功能叫 Live +Template,它可以用来自定义一些常用的代码片段。 + +比如下面这段,几乎是写 Python 脚本必备的 + +.. code:: python + + if __name__ == '__main__': + +当你在PyCharm 中编码 python 代码时,只要输入 main ,PyCharm 就会在 Live +Template +里找到定义过的代码片段,然后只要直接键入回车,就可以生成这段代码。 + +再比如说,我通常会定义简单的装饰器代码 + +|image104| + +这样当我要定义一个最简单的装饰器时,只要输入 ``deco`` +再直接敲入回车就行啦。 + +|image105| + +`PyCharm +使用技巧 `__ + 附录 ---- @@ -1086,7 +1114,7 @@ Debug ,而远程调试需要不少前置步骤。 -------------- -|image104| +|image106| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.python-online.cn/20190323164120.png @@ -1192,5 +1220,7 @@ Debug ,而远程调试需要不少前置步骤。 .. |image101| image:: http://image.iswbm.com/20200420085524.png .. |image102| image:: http://image.iswbm.com/20200420090117.png .. |image103| image:: http://image.iswbm.com/20200420090428.png -.. |image104| image:: http://image.iswbm.com/20200607174235.png +.. |image104| image:: http://image.iswbm.com/20200723161209.png +.. |image105| image:: http://image.iswbm.com/20200723161307.png +.. |image106| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_16.rst b/source/c08/c08_16.rst index 87f65c6..bb69764 100644 --- a/source/c08/c08_16.rst +++ b/source/c08/c08_16.rst @@ -1,29 +1,390 @@ -8.16 修改 KVM 镜像文件的三种方法 -================================ +9.16 详解 Neutron 的 QoS +======================== -如下工具的更多说明,请查看官方文档:http://libguestfs.org/guestfs-recipes.1.html +QoS 的全称是 Quality of Service,也就是服务质量。 -8.16.1 使用 guestfish ---------------------- +Neutron 支持的 +`QoS `__ +类型有 + +- bandwidth_limit(带宽限速):实现带宽速速 +- DSCP(差分服务代码点):给网络流量包添加一个 DSCP + 标记,以此实现流量的优先级 +- minimum_bandwidth:暂时没用过 + +1. 开户 QoS 支持 +---------------- + +在控制节点上执行两条命令修改配置 + +.. code:: shell + + $ openstack-config --set /etc/neutron/neutron.conf DEFAULT service_plugins neutron.services.qos.qos_plugin.QoSPlugin + + $ openstack-config --set /etc/neutron/plugins/ml2/ml2_conf.ini ml2 extension_drivers port_security,qos + +然后重启 neutron-server + +.. code:: shell + + $ crm resource cleanup openstack-neutron-server-clone + +在计算节点上执行命令修改配置 + +.. code:: shell + + $ openstack-config --set /etc/neutron/plugins/ml2/openvswitch_agent.ini agent extensions qos + +然后重启对应的 neutron-agent 服务 + +.. code:: shell + + $ systemctl restart neutron-openvswitch-agent + +DSCP 差分服务代码点 +------------------- + +DSCP 大小为 6 个 bit,也就是最大可表示 64 个分类。 + +但在 Neutron 里可用的标记只有这些 + +:: + + 0, 8, 10, 12, 14, 16, 18, 20, + 22, 24, 26, 28, 30, 32, 34, 36, + 38, 40, 46, 48, 56 + +可以看到,它们都是0-56 间的偶数,但是排除 2-6, 42, 44 和 50-54。 |image0| -guestfish 里的命令有限,大概会 600 多个,不能 -cd,操作文件时,需要使用绝对路径。 +原理 +~~~~ + +在 IP 协议分组里有一个 ToS(服务类型) 的字段,就是用来表示 ToS 的。 -8.16.2 使用 guestmount ----------------------- +ToS 字段,总共 8 个 bit |image1| -使用完后,记得 umount /home/wangbm/mount +**前面 3 个 bit** -8.16.3 使用 virt-\* 命令 ------------------------- +为优选权子字段,现在已经废弃,这个字段默认值是000,从wireshark抓包结果看,表示的是: |image2| -.. |image0| image:: http://image.python-online.cn/20191111112221.png -.. |image1| image:: http://image.python-online.cn/20191111112421.png -.. |image2| image:: http://image.python-online.cn/20191111112548.png +但是在某些协议中仍然是有用的,比如 OSPFv2 协议 + +|image3| + +关于 Precedence (优先级),有如下几种 + +.. code:: shell + + 111 - Network Control + 110 - Internetwork Control + 101 - CRITIC/ECP + 100 - Flash Override + 011 - Flash + 010 - Immediate + 001 - Priority + 000 – Routine + +**中间 4 个bit** + +这四个 bit +组合在一起,表示了该数据报对应的服务类别,这个应用层的服务类别是不同的。这里所说的服务类别,是指: + +:: + + 1000 -- minimize delay 最小延迟 + 0100 -- maximize throughput 最大吞吐量 + 0010 -- maximize reliability 最高可靠性 + 0001 -- minimize monetary cost 最小费用 + 0000 -- normal service 一般服务 + +IP首部中的ToS字段,只能表示一种服务类别,也就是:这4bit字段中,最多只能有一个bit字段为1。 + +看下不同应用下该4bit字段对应的值: |image4| 翻译过来就是: |image5| +**最小延迟**\ ,对应于对延迟敏感的应用,如telnet和人login等。 +**最大吞吐量**\ ,对应于对吞吐量要求比较高的应用,如FTP文件应用,对文件传输吞吐量有比较高的要求。 +**最高可靠性**\ ,对网络传输可靠性要求高的应用,如使用SNMP的应用、路由协议等等。 +**最小费用**\ ,如NNTP这种用户网络新闻等。 + +**最后 1 个bit** + +这个1bit末尾,没有被使用,必须强制设置为0 + +最后,很重要的一点,只有当\ **网络设备(如交换机等)能够支持**\ (能够识别IP首部中的ToS字段)识别ToS字段时,这给字段设置才有意义。 + +创建 policy +~~~~~~~~~~~ + +.. code:: shell + + $ neutron qos-policy-create qos-dscp --shared + Created a new policy: + +-----------------+--------------------------------------+ + | Field | Value | + +-----------------+--------------------------------------+ + | created_at | 2020-07-01T06:43:23Z | + | description | | + | id | ee7e7a83-c67d-4f27-b77c-3345553e5abe | + | name | qos-dscp | + | project_id | 2ac17c7c792d45eaa764c30bac37fad9 | + | revision_number | 1 | + | rules | | + | shared | True | + | tenant_id | 2ac17c7c792d45eaa764c30bac37fad9 | + | updated_at | 2020-07-01T06:43:23Z | + +-----------------+--------------------------------------+ + +创建 rule +~~~~~~~~~ + +创建时需要指定 QOS_POLICY,创建完后,就会自动添加到 QOS_POLICY + +.. code:: shell + + $ neutron qos-dscp-marking-rule-create --dscp-mark 14 ee7e7a83-c67d-4f27-b77c-3345553e5abe + Created a new dscp_marking_rule: + +-----------+--------------------------------------+ + | Field | Value | + +-----------+--------------------------------------+ + | dscp_mark | 14 | + | id | 1d045cf3-eb31-440b-9a74-a9d5fea6a7e0 | + +-----------+--------------------------------------+ + +绑定policy到 port +~~~~~~~~~~~~~~~~~ + +.. code:: shell + + $ neutron port-update --qos-policy qos-dscp + +关闭 QoS +~~~~~~~~ + +.. code:: shell + + $ neutron port-update --no-qos-policy + +查看规则 +~~~~~~~~ + +过滤出 tos 后就能看到 ToS(Type of Service) 的值 + +.. code:: shell + + $ ovs-ofctl dump-flows br-int | grep tos + +然后再对照这个表,找到对应的 DSCP 的 decimal ,如果 tos 是 64,那么 DSCP +mark 就是 16,其实除以 4 就可以了,也不用对照表。其中这个 16 +需要跟交换上支持的一样。 + +|image6| + +其他 +~~~~ + +DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时候将其绑定到 port +上就可以。 + +2. bandwidth_limit 带宽限速 +--------------------------- + +华云 QoS +:https://support.huawei.com/enterprise/zh/doc/EDOC1100055155/101c0e7b + +http://blog.chinaunix.net/uid-20530497-id-2490382.html + +https://www.zhihu.com/question/21053403 + +qos 查询命令 + +.. code:: shell + + $ neutron qos-policy-list + $ neutron qos-bandwidth-limit-rule-list 16fbb0c2-b7ac-4053-a85f-75fb72c3ab55 + +在 dpdk 宿主机上查询限速 + +.. code:: shell + + # 查询 ingress(宿主机角度) + $ virsh dumpxml 4f6a0708-aeb8-4208-bea8-2c51e6a94948 + + # 查询 egress(虚拟机角度) + $ ovs-vsctl list interface vhu198063e9-97 + + # 查询 ingress(虚拟机角度) + $ ovs-appctl -t ovs-vswitchd qos/show vhu198063e9-97 + +|image7| + +测试限速 + +average: 100M + +.. code:: shell + + # burst:50 + $ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=7500 __system__vif_outbound_average=12500 __system__vif_outbound_burst=6000 + + + # burst:60 + $ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=7500 __system__vif_outbound_average=12500 __system__vif_outbound_burst=7500 + + # burst:70 + $ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=8750 __system__vif_outbound_average=12500 __system__vif_outbound_burst=8750 + + # burst:80 + $ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=10000 __system__vif_outbound_average=12500 __system__vif_outbound_burst=10000 + +3. 带宽单位换算 +--------------- + +带宽,英文名 +Bandwidth,在不同领域的含义各不相同,而在网络服务中,带宽是指单位时间内的流经数据量。 + +数据量的单位一般有两种:bit 和 Byte。 + +因此,带宽的单位即为,b/s 或者 B/s,如果数值较大,可以使用 K +表示千(Kb/s 或者 KB/s),M 表示 百万(Mb/s 或者 MB/s)。 + +- Mb/s:Million bits per + second的缩写,是一种传输速率单位,指每秒传输的位(比特)数量。通常用于运营商带宽速率的计量。 + +- MB/s:Million Bytes per + second的缩写,是一种传输速率单位,指每秒传输的字节数量。即是我们平时说的下载速度。 + +Byte 和 bit 是不一样的,Byte = 8 +bit,而不同的地方单位不同,需要进行换算,下表整理了在 VMP +中的各个位置的单位 + ++------+-------+------+------+----------+----------------------+ +| type | cloud | meta | xml | qos rule | ovs-vsctl/ovs-appctl | ++======+=======+======+======+==========+======================+ +| ovs | bit | Byte | Byte | | | ++------+-------+------+------+----------+----------------------+ +| dpdk | bit | Byte | | bit | bit | ++------+-------+------+------+----------+----------------------+ + +4. 带宽限制效果 +--------------- + +CentOS 安装 iperf3 +~~~~~~~~~~~~~~~~~~ + +源码编译安装 iperf3 + +.. code:: shell + + yum -y install gcc make wget + cd /tmp + wget https://iperf.fr/download/source/iperf-3.1.3-source.tar.gz + tar zxvf iperf-3.1.3-source.tar.gz + cd iperf-3.1.3 + ./configure + make + make install + +使用 rpm 包安装(更简单,推荐) + +.. code:: shell + + $ rpm -ih ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/updates/24/x86_64/i/iperf3-3.1.3-1.fc24.x86_64.rpm + +Windows 安装 iperf3 +~~~~~~~~~~~~~~~~~~~ + +所有的 windows 可用的 iperf3 +安装包都可以从这个页面下载:https://iperf.fr/iperf-download.php + +这里直接给出两个 windows 常用的 + +- windows 10 + 64位:https://iperf.fr/download/windows/iperf-3.1.3-win64.zip +- windows 10 32 + 位:https://iperf.fr/download/windows/iperf-3.1.3-win32.zip + +下载到电脑到,并解压后,会得到两个文件:\ ``cygwin1.dll`` 和 +``iperf3.exe``\ ,将这两个文件拷贝到 ``c:\windows`` 目录下。 + +最后打开 cmd,执行 ``iperf3 --version`` ,若安装成功,会打印出版本信息。 + +iperf3 使用前关闭防火墙 +~~~~~~~~~~~~~~~~~~~~~~~ + +CentOS 关闭防火墙,只要一条命令 + +.. code:: shell + + $ systemctl stop firewalld + $ systemctl stop iptables + +windows 10 手动设置一下 + +|image8| + +iperf3 的使用 +~~~~~~~~~~~~~ + +iperf3 有客户端 和 服务端之别: + +- 服务端:收包,使用 ``-s`` 参数指定 + +.. code:: shell + + $ iperf3 -s + +- 客户端:发包,使用 ``-c xx.xx.xx.xx`` 来指定要往哪个服务端发包 + +.. code:: shell + + $ iperf3 -c 172.20.20.200 + +iperf3 +还有更多的参数,其中有一些是客户端专用的,有一些是服务端专用的,也有一些是二者共用的。 + +具体可以前往这个地址,进行查阅:https://www.cnblogs.com/yingsong/p/5682080.html + +常用的参数有 + +- ``-u``\ :发送 UDP 包,仅客户端 可用 +- ``-p``\ :后接服务端监听的端口 +- ``-i``\ :设置带宽报告的时间间隔,单位为秒 +- ``-t``\ :设置测试的时长,单位为秒 +- ``-w``\ :设置tcp窗口大小,一般可以不用设置,默认即可 + +5. 注意事项 +----------- + +1、vm 在 host1 上,从host2 上 iperf 比在 host1 上限速效果更好 + +2、udp 包限速效果差异很大。 + +3. 参考文章 +----------- + +- https://blog.51cto.com/mangguostudy/2107799 + +- https://www.jianshu.com/p/4b5cc3845f2c + +- https://blog.csdn.net/u011641885/article/details/45640313 + +-------------- + +|image9| + +.. |image0| image:: http://image.iswbm.com/20200701155207.png +.. |image1| image:: http://img.wandouip.com/crawler/article/2019411/546f47120fa14a2a1cfc44c9e8a48e71 +.. |image2| image:: http://image.iswbm.com/20200723155027.png +.. |image3| image:: http://image.iswbm.com/20200701170223.png +.. |image4| image:: http://image.iswbm.com/20200723154704.png +.. |image5| image:: http://image.iswbm.com/20200723154742.png +.. |image6| image:: http://image.iswbm.com/20200701155207.png +.. |image7| image:: http://image.iswbm.com/20200709171517.png +.. |image8| image:: http://image.iswbm.com/20200716112516.png +.. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md index 857c3cf..8085d21 100644 --- a/source/c09/c09_03.md +++ b/source/c09/c09_03.md @@ -58,3 +58,10 @@ http://asciiflow.com/ https://www.draw.io/ + + +## 其他工具 + +随机生成复杂 JSON:http://www.yyyweb.com/demo/inner-show/json-generator.html + +生成 Markdwon 目录:https://ecotrust-canada.github.io/markdown-toc/ \ No newline at end of file diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index 13f2eef..f0aee59 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -50,5 +50,13 @@ http://asciiflow.com/ https://www.draw.io/ +其他工具 +-------- + +随机生成复杂 +JSON:http://www.yyyweb.com/demo/inner-show/json-generator.html + +生成 Markdwon 目录:https://ecotrust-canada.github.io/markdown-toc/ + .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md index 5290a55..0978459 100644 --- a/source/c10/c10_07.md +++ b/source/c10/c10_07.md @@ -67,9 +67,7 @@ CSRF 的实际工作原理是怎样的? - 有的虽然安全,用户体验好,但是有缺点 -只有一种是最优解,具体是哪一种? - -不防继续往下看 +具体应该选哪一种呢,不妨继续往下看。 ### 3.1 同源策略 diff --git a/source/c10/c10_07.rst b/source/c10/c10_07.rst index cf49fe7..8249277 100644 --- a/source/c10/c10_07.rst +++ b/source/c10/c10_07.rst @@ -74,9 +74,7 @@ CSRF 攻击。 - 有的虽然安全,用户体验好,但是有缺点 -只有一种是最优解,具体是哪一种? - -不防继续往下看 +具体应该选哪一种呢,不妨继续往下看。 3.1 同源策略 ~~~~~~~~~~~~ From 95c7bac4c202cb6052f0021728a712ee7cbd8d3d Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 29 Jul 2020 08:42:02 +0800 Subject: [PATCH 113/147] =?UTF-8?q?Add=20=E6=95=B0=E5=AD=97=E8=AF=81?= =?UTF-8?q?=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_08.md | 679 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 679 insertions(+) create mode 100644 source/c10/c10_08.md diff --git a/source/c10/c10_08.md b/source/c10/c10_08.md new file mode 100644 index 0000000..245a2b3 --- /dev/null +++ b/source/c10/c10_08.md @@ -0,0 +1,679 @@ +# 10.8 终于把 HTTPS数字证书给整明白了 + +我们都知道 HTTP 协议都是明文传输内容,为了保证数据传输的安全,HTTPS 协议就应运而生了,但它其实并不是一个全新的协议,而是HTTP 协议基本之上 再加上SSL/TLS 协议。 + +因此当你访问一个支持 https 的网站时,是需要先进行 SSL/TLS 握手建立连接的。 + +SSL/TLS 握手的目的是为了 **安全** 地协商出一份**对称加密**的密钥,有了这个密钥之后,后续的数据全部使用这个密钥进行加密。 + +这个过程其实挺有趣的,涉及到的知识点,专业名词也很多,比如对称加密,非对称加密,信息摘要,数字签名,数字证书,公钥和私钥。本篇文章,会详细地介绍这些极易混淆的专业名词。 + +在讲解之前,我先给你提出几个摸底问题,如果你已经还不能够熟练回答,那么本篇文章会给你答案: + +1、 对称加密和非对称加密,各有什么优缺点? + +2、对称加密和非对称加密,是排他关系吗?是否可以搭配使用? + +3、摘要和加密有什么区别?有了摘要算法为什么还要有加密算法? + +4、如何在通信时同时做到保密性、高效性? + +5、如何申请数字证书?证书分为哪几种?SSL 证书如何部署? + +6、完整说下证书申请、证书签发、证书下发客户端、客户端验证证书、数据加密传输的整个流程? + +## 1. 对称加密与非对称加密 + +### 对称加密 + +`对称加密`是通信双方共同拥有一把密钥。 + +这把密钥可以把明文加密(encryption)成密文,也可以把密文解密(decryption)成明文。 + +常见的对称加密算法有AES、DES、RC4,其中最常用的是AES。 + +对称加密的优点是:速度快。 + +同时也有一个缺点,就是不那么安全,一旦你的密钥被别人窃取了,所有的数据就会在网络的世界里裸奔。 + +### 非对称加密 + +与 对称加密相对的是 `非对称加密`。 + +通信双方持有不同的密钥。 + +服务端的密钥,称之为 `私钥`(private key),客户端的密钥,称之为 `公钥` (public key)。 + +他们二者的区别是: + +1、私钥应仅在服务端保存,绝不可泄露。而公钥可以存在于任何的客户端,即使黑客拿到了也没有关系。 + +2、公钥加密的密文只有相对应的私钥才能解密 + +![](http://image.iswbm.com/image-20200723233619429.png) + +3、私钥加密的内容,所有与之相对应的公钥都能解密。 + +4、公钥通常用来生成签名,私钥用来验证签名。 + +![](http://image.iswbm.com/image-20200724121333485.png) + +5、公钥和私钥是相对的,两者本身并没有规定哪一个必须是公钥或私钥。这意味着,公钥只要不对外公开,那就可以做为私钥,私钥公开后也可以做为公钥。 + +典型的非对称加密算法有 RSA 。 + +非对称加密的优点,就是安全系数特别高。缺点就是速度会慢一些。 + +### 对称与非对称加密结合 + +当客户端收到的公钥是准确的时候,通信就是安全的。 + +因为用正确公钥加密过的密文,只有服务端的私钥能解。 + +那么如何保证,客户端收到正确的公钥呢? + +答案是:通过非对称加密来协商对称加密的密钥,服务端一旦把正确的公钥安全地送达到客户端后,后续的通信,为了保证高效通信,再采用对称加密来加密数据。 + +具体的过程,后面会更加详细的阐述这一过程。 + + + +## 2. 摘要、签名、证书是啥? + +### 信息摘要 + +一段信息,经过摘要算法得到一串哈希值,就是摘要(dijest)。 + +常见的摘要算法有MD5、SHA1、SHA256、SHA512等。 + +关于摘要,有几点需要你明白的: + +1、摘要算法,是把任意长度的信息,映射成一个定长的字符串。 + +2、摘要算法,两个不同的信息,是有可能算出同一个摘要值的。 + +3、摘要算法与加密算法不同,不存在解密的过程。 + +4、摘要算法不用于数据的保密,而是用于数据的完整性校验。 + +### 数字签名 + +摘要经过私钥的加密后,便有了一个新的名字 -- `数字签名`。 + +`签名` 是在发送方,这是一个加密的过程。 + +`验签` 是在接收方,这是一个解密的过程。 + +那搞懂数字签名的意义是什么?只要回答下面两个问题即可。 + +**第一个问题,有了信息摘要,为何还要有数字签名?** + +答:信息摘要,虽然也不可逆,但却容易却被伪造。所以信息摘要只用于校验完整性,而要保证信息摘要的正确性,就要依靠数字签名啦。 + +数字签名的签名和验签是非对称加密,其他人除非拿到私钥,不然没法伪造。 + +**第二个问题,为什么不对内容直接加密,而是对摘要进行加密。** + +答:由上面我们知道了非对称加密的速度非常慢,如果传输的数据量非常大,那这个加密再解密的时间要远比网络传输的时间来得长,这样反而会得不偿失。 + +如果我们对传输的内容只有完整性要求,而安全性没有要求(意思是传输的内容被人知道了也没关系)。那就可以对摘要进行加密,到客户端这里解密后得到摘要明文,再用这个摘要明文与传输的数据二次计算的摘要进行比较,若一致,则说明传输的内容是完整的,没有被篡改。 + +### 数字证书 + +在数字签名那里,不知道你有没有发现一个问题? + +数字签名是非对称加密,服务端有一个私钥,客户端一个公钥,只有这两个对上了验签。 + +那假如说你(客户端)拿到的公钥并不是服务端给的呢,而是黑客塞给你的呢?而你却把这个假公钥当成真的,那么当你使用这个假公钥加密一些敏感信息时,黑客就可以截取你的这段信息,由于这信息是用黑客自己的公钥加密的,这样一来,黑客拿自己的私钥就能解密得到你的敏感信息。 + +这就是问题所在。 + +要解决这个问题,其实只要保证『公钥』是可信的。只有服务端发给你的公钥你才能拿,而坏人给你的公钥,你要懂得识别并丢弃它。 + +数字证书就应运而生了。 + +要理解数字证书,同样只要搞懂两个问题即可。 + +1. 数字证书是什么东西?其实它就是一个 `.crt` 文件 +2. 数字证书是谁颁发的?由权威证书认证机构颁发,一般我们简称为 CA 机构 + +2. 数字证书如何申请的?或者说如何颁发的? + +为了让你理解这一过程,我画了下面这张图: + +![](http://image.iswbm.com/20200724124502.png) + +1、在自己的服务器上生成一对公钥和私钥。然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等其他信息整合在一起,生成`.csr` 文件。 + +2、将这个 `.csr` 文件发给 CA 机构,CA 机构收到申请后,会通过各种手段验证申请者的组织信息和个人信息,如无异常(组织存在,企业合法,确实是域名的拥有者),CA 就会使用散列算法对`.csr`里的明文信息先做一个HASH,得到一个信息摘要,再用 CA 自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的 **签名**。签名 + `.csr` 明文信息,即是 **证书**。CA 把这个证书返回给申请人 + + + +## 3. 数字证书(Certificate) + +在HTTPS的传输过程中,有一个非常关键的角色--`数字证书`,那什么是数字证书?又有什么作用呢? + +所谓数字证书,是一种用于电脑的身份识别机制。由数字证书颁发机构(CA)对使用私钥创建的签名请求文件做的签名(盖章),表示CA结构对证书持有者的认可。 + +### 3.1 数字证书拥有以下几个优点 + +- 使用数字证书能够提高用户的可信度; +- 数字证书中的公钥,能够与服务端的私钥配对使用,实现数据传输过程中的加密和解密; +- 在证认使用者身份期间,使用者的敏感个人数据并不会被传输至证书持有者的网络系统上; + +### 2.2. 证书类型 + +x509的证书编码格式有两种: + +1. PEM(Privacy-enhanced Electronic Mail)是明文格式的,以 -----BEGIN CERTIFICATE-----开头,已-----END CERTIFICATE-----结尾。中间是经过base64编码的内容,apache需要的证书就是这类编码的证书.查看这类证书的信息的命令为: `openssl x509 -noout -text -in server.pem`。其实PEM就是把DER的内容进行了一次base64编码 +2. DER是二进制格式的证书,查看这类证书的信息的命令为: `openssl x509 -noout -text -inform der -in server.der` + +### 2.3. 扩展名 + +- .crt证书文件,可以是DER(二进制)编码的,也可以是PEM(ASCII (Base64))编码的),在类unix系统中比较常见; +- .cer也是证书,常见于Windows系统。编码类型同样可以是DER或者PEM的,windows下有工具可以转换crt到cer; +- .csr证书签名请求文件,一般是生成请求以后发送给CA,然后CA会给您签名并发回证书 +- .key一般公钥或者密钥都会用这种扩展名,可以是DER编码的或者是PEM编码的。查看DER编码的(公钥或者密钥)的文件的命令为: `openssl rsa -inform DER -noout -text -in xxx.key`。查看PEM编码的(公钥或者密钥)的文件的命令为: `openssl rsa -inform PEM -noout -text -in xxx.key`; +- .p12证书文件,包含一个X509证书和一个被密码保护的私钥 + +### 2.4 证书的种类 + +安全证书主要分为DV、OV和EV三个种类,对应的安全等级为一般、较好和最高三个等级。三者的审核过程、审核标准和对应的域名数量也不同,所以价格在一两百元到几万元不等。 + +#### DV SSL + +DV SSL证书是只验证网站域名所有权的简易型(Class 1级)SSL证书,可10分钟快速颁发,能起到加密传输的作用,但无法向用户证明网站的真实身份。 + +目前市面上的免费证书都是这个类型的,只是提供了对数据的加密,但是对提供证书的个人和机构的身份不做验证。 + +#### OV SSL + +OV SSL,提供加密功能,对申请者做严格的身份审核验证,提供可信×××明。 + +和DV SSL的区别在于,OV SSL 提供了对个人或者机构的审核,能确认对方的身份,安全性更高。 + +所以这部分的证书申请是收费的~ + +#### EV SSL + +超安=EV=最安全、最严格 超安EV SSL证书遵循全球统一的严格身份验证标准,是目前业界安全级别最高的顶级 (Class 4级)SSL证书。 + +金融证券、银行、第三方支付、网上商城等,重点强调网站安全、企业可信形象的网站,涉及交易支付、客户隐私信息和账号密码的传输。 + +这部分的验证要求最高,申请费用也是最贵的。 + +选择签发机构时,最好选择行业认可的全球范围内都可以使用的ca机构签发的证书。目前我们国内的证书能够符合标准的还不是特别多,主要原因是有一些证书不能够被国外的浏览器所认可,在使用的时候需要进行一定的额外操作。 + + + +--- + + + +根据保护域名的数量需求,SSL证书又分为: + +**单域名版:**只保护一个域名,例如 [www.abc.com](http://www.abc.com/) 或者 login.abc.com 之类的单个域名 + +**多域名版:**一张证书可以保护多个域名,例如同时保护 [www.abc.com](http://www.abc.com/) , [www.bcd.com,](https://blog.csdn.net/) pay.efg.com 等 + +**通配符版:**一张证书保护同一个主域名下同一级的所有子域名,不限个数,形如 *.abc.com 。注意,通配符版只有 DVSSL 和 OVSSL 具有, EVSSL 不具有通配符版本。 + + + +### 2.5 证书在哪里 + +当你在下载并安装浏览器时,浏览器内部其实已经内嵌了全世界公认的根证书颁发机构的证书。 + +若一个网站的数字证书的证书颁发机构在浏览器中没有,则需要引导用户自行导入。 + +如果你想在 Chrome 中查看有哪些受信任的证书颁发机构,可以点击 `设置` -> `隐私设置与安全性` -> `安全` -> `管理证书` + +![](http://image.iswbm.com/20200717222206.png) + +### 2.6 证书里的信息 + +在上图的位置里,随便双击点开一个证书,就可以查看证书里的内容。 + +内容非常多,最主要的有 + +- 证书是哪个机构的? +- 证书里的公钥是什么? +- 证书有效期是什么时候? +- 采用的哪种加解密的算法? + +### 2.7 证书吊销 + +证书是有生命周期的,如果证书的私钥泄漏了那这个证书就得吊销,一般有两种吊销方式:CRL和OCSP。 + +CRL( Certificate Revocation List)是CA机构维护的一个已经被吊销的证书序列号列表,浏览器需要定时更新这个列表,浏览器在验证证书合法性的时候也会在证书吊销列表中查询是否已经被吊销,如果被吊销了那这个证书也是不可信的。可以看出,这个列表随着被吊销证书的增加而增加,列表会越来越大,浏览器还需要定时更新,实时性也比较差。 + +所以,后来就有了 OCSP (Online Certificate Status Protocol)在线证书状态协议,这个协议就是解决了 CRL 列表越来越大和实时性差的问题而生的。有了这个协议,浏览器就可以不用定期更新CRL了,在验证证书的时候直接去CA服务器实时校验一下证书有没有被吊销就可以,是解决了CRL的问题,但是每次都要去CA服务器上校验也会很慢,在网络环境较差的时候或者跨国访问的时候,体验就非常差了,OCSP虽然解决了CRL的问题但是性能却很差。 + + + +如果你想了解更多关于证书的内容,可以前往华为云的公开文档进行学习:https://support.huaweicloud.com/scm_faq/scm_01_0020.html + +## 4. 如何生成 CSR 文件 + +CSR是Certificate Signing Request的英文缩写,即证书签名请求文件。 + +当申请者申请数字证书时,CSP(加密服务提供者)生成私钥,同时也生成了CSR文件。申请者将CSR文件提交至Certificate Authority (CA)机构后,CA机构使用其根证书私钥签名,从而就生成了数字证书。 + +申请者通过CSR文件,向CA机构申请数字证书。获取证书后,就能证明申请者的网站是可信的,数据传输是加密的。 + +接下来来了解一下,CSR 文件是如何生成的? + +### 使用 OpenSSL 生成 + +假设申请的域名为 **python.iswbm.com**,公司名称为**派森时光科技**,部门是**IT部**,公司在**中国广东省深圳市**。可通过运行下方命令行生成CSR文件: + +```shell +$ openssl req -new –SHA256 -newkey rsa:2048 -nodes -keyout python.iswbm.com.key -out python.iswbm.com.csr -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=派森时光科技/OU=IT/CN=python.iswbm.com" +``` + +此命令行表示: + +- `req`参数:表示证书请求request,用于生成CSR文件。 +- `SHA256`参数:表示CSR签名时用的摘要算法。 +- `newkey`参数:表示指定证书的算法。参数 2048:表示密钥对的长度。 +- `nodes`参数:表示不对私钥加密。 +- `keyout`参数:表示生成的私钥文件。名为`iswbm.key`的私钥文件,需自行保管,用于获取证书后的部署过程。 +- `out`参数:表示生成的 CSR 文件。名为`iswbm.com.csr`的CSR文件,用于提交至CA机构验证信息,从而获取证书。 +- `subj`参数:表示CSR信息,具体有哪些参数,可以继续往下看。 + +subj参数说明: + +- `C`:Country,表示国家,申请者或申请企业所在国家的英文或两位大写国家代码。如:CN +- `ST`:State/Province,表示省份,申请者或申请企业所在地的省/市/自治区英文或拼音全称。如:Guangdong +- `L`:Locality,表示城市,申请者或申请企业所在城市的英文或拼音全称。如:Shenzhen +- `O`:Organization,表示申请者的姓名或申请企业的名称。 +- `OU`:Organizational Unit,表示申请人所在部门的英文或拼音全称。如:IT +- `CN`:Common Name,表示你要为哪个域名申请证书,可是单域名(比如 python.iswbm.com),也可以是泛域名(*.iswbm.com),也可以是为多个域名申请一个证书(具体我没操作过)。 + +上一条命令执行完后,会在你的本地目录下生成俩文件 + +- python.iswbm.com.csr:用于向CA机构申请证书 + +```shell +$ cat python.iswbm.com.csr +-----BEGIN CERTIFICATE REQUEST----- +MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ2Rvbmcx +ETAPBgNVBAcMCFNoZW56aGVuMS0wKwYDVQQKDCTDpsK0wr7DpsKjwq7DpsKXwrbD +... +7lgB4QC1aIFz8gi9TGMJU2LqTDJCj+tgM68LDBdMLeQ8XZ33C95Nl0qt7yG+zjlZ +01jBh+T882r8x9gKdwb7nZSWFQY4/YTq+sY++YW/QuCNRcJ2vbM18U/HlIRsZ3su +x6Neh08= +-----END CERTIFICATE REQUEST----- +``` + +- python.iswbm.com.key:私钥,自行保存,不要外泄 + +```shell +$ cat python.iswbm.com.key +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4OrcM9hTs9Hao +SzjsVJFX2Mmd+mToMG3u++o2Fd5yrPYq4COkT33lnL9kJNrDWqGp5TRkWqNwLaPl +... +a/lKBWLcvxE+IQ+mxNbN058kEJ3l8WAcAFCebLm5czUqmIVa3JR+cBDLvGFZVn6z +72AP5D/Evds4BOO+VzAiVLU6Ai78qhACuVExZNQCxdvJy4LxpeckUpCem9hAPiIY +LQfiTStBBU6t/+mnDyij+XreGQ== +-----END PRIVATE KEY----- +``` + + + +### 使用在线生成工具 + +使用 OpenSSL 工具生成 CSR 文件的方法固然简单,但使用时,需要你的了解代码中参数的意思。 + +如果你不想花心思去记这些,推荐你使用 [CSR在线生成工具](https://myssl.com/csr_create.html)(https://myssl.com/csr_create.html) + +你只需简单地输入如下信息,再点击 `OpenSSL生成`,你就可以获得一条 OpenSSL 命令,这下你再也不用自己拼凑参数啦,真的太方便了。为什么不点击 `生成` 让其直接生成 私钥文件 和 CSR文件 呢?当然是为了安全起见啦。 + +![](http://image.iswbm.com/20200718100052.png) + + + +## 5. 数字证书如何制作或申请? + +说到这里,到底数字证书是从哪里来的呢? + +数字证书,你可以自己制作,也可以向CA权威机构申请。 + +二者的区别是: + +1. 自己颁发的证书,需要客户端验证通过,可能需要客户端自己安装上受信任的根证书。 +2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是可信任的。 + +### 自己制作的证书 + +自签名脚本:create_self-signed-cert.sh + +```shell +#!/bin/bash -e + +help () +{ + echo ' ================================================================ ' + echo ' --ssl-domain: 生成ssl证书需要的主域名,如不指定则默认为www.rancher.local,如果是ip访问服务,则可忽略;' + echo ' --ssl-trusted-ip: 一般ssl证书只信任域名的访问请求,有时候需要使用ip去访问server,那么需要给ssl证书添加扩展IP,多个IP用逗号隔开;' + echo ' --ssl-trusted-domain: 如果想多个域名访问,则添加扩展域名(SSL_TRUSTED_DOMAIN),多个扩展域名用逗号隔开;' + echo ' --ssl-size: ssl加密位数,默认2048;' + echo ' --ssl-cn: 国家代码(2个字母的代号),默认CN;' + echo ' 使用示例:' + echo ' ./create_self-signed-cert.sh --ssl-domain=www.test.com --ssl-trusted-domain=www.test2.com \ ' + echo ' --ssl-trusted-ip=1.1.1.1,2.2.2.2,3.3.3.3 --ssl-size=2048 --ssl-date=3650' + echo ' ================================================================' +} + +case "$1" in + -h|--help) help; exit;; +esac + +if [[ $1 == '' ]];then + help; + exit; +fi + +CMDOPTS="$*" +for OPTS in $CMDOPTS; +do + key=$(echo ${OPTS} | awk -F"=" '{print $1}' ) + value=$(echo ${OPTS} | awk -F"=" '{print $2}' ) + case "$key" in + --ssl-domain) SSL_DOMAIN=$value ;; + --ssl-trusted-ip) SSL_TRUSTED_IP=$value ;; + --ssl-trusted-domain) SSL_TRUSTED_DOMAIN=$value ;; + --ssl-size) SSL_SIZE=$value ;; + --ssl-date) SSL_DATE=$value ;; + --ca-date) CA_DATE=$value ;; + --ssl-cn) CN=$value ;; + esac +done + +# CA相关配置 +CA_DATE=${CA_DATE:-3650} +CA_KEY=${CA_KEY:-cakey.pem} +CA_CERT=${CA_CERT:-cacerts.pem} +CA_DOMAIN=cattle-ca + +# ssl相关配置 +SSL_CONFIG=${SSL_CONFIG:-$PWD/openssl.cnf} +SSL_DOMAIN=${SSL_DOMAIN:-'www.rancher.local'} +SSL_DATE=${SSL_DATE:-3650} +SSL_SIZE=${SSL_SIZE:-2048} + +## 国家代码(2个字母的代号),默认CN; +CN=${CN:-CN} + +SSL_KEY=$SSL_DOMAIN.key +SSL_CSR=$SSL_DOMAIN.csr +SSL_CERT=$SSL_DOMAIN.crt + +echo -e "\033[32m ---------------------------- \033[0m" +echo -e "\033[32m | 生成 SSL Cert | \033[0m" +echo -e "\033[32m ---------------------------- \033[0m" + +if [[ -e ./${CA_KEY} ]]; then + echo -e "\033[32m ====> 1. 发现已存在CA私钥,备份"${CA_KEY}"为"${CA_KEY}"-bak,然后重新创建 \033[0m" + mv ${CA_KEY} "${CA_KEY}"-bak + openssl genrsa -out ${CA_KEY} ${SSL_SIZE} +else + echo -e "\033[32m ====> 1. 生成新的CA私钥 ${CA_KEY} \033[0m" + openssl genrsa -out ${CA_KEY} ${SSL_SIZE} +fi + +if [[ -e ./${CA_CERT} ]]; then + echo -e "\033[32m ====> 2. 发现已存在CA证书,先备份"${CA_CERT}"为"${CA_CERT}"-bak,然后重新创建 \033[0m" + mv ${CA_CERT} "${CA_CERT}"-bak + openssl req -x509 -sha256 -new -nodes -key ${CA_KEY} -days ${CA_DATE} -out ${CA_CERT} -subj "/C=${CN}/CN=${CA_DOMAIN}" +else + echo -e "\033[32m ====> 2. 生成新的CA证书 ${CA_CERT} \033[0m" + openssl req -x509 -sha256 -new -nodes -key ${CA_KEY} -days ${CA_DATE} -out ${CA_CERT} -subj "/C=${CN}/CN=${CA_DOMAIN}" +fi + +echo -e "\033[32m ====> 3. 生成Openssl配置文件 ${SSL_CONFIG} \033[0m" +cat > ${SSL_CONFIG} <> ${SSL_CONFIG} <> ${SSL_CONFIG} + done + + if [[ -n ${SSL_TRUSTED_IP} ]]; then + ip=(${SSL_TRUSTED_IP}) + for i in "${!ip[@]}"; do + echo IP.$((i+1)) = ${ip[$i]} >> ${SSL_CONFIG} + done + fi +fi + +echo -e "\033[32m ====> 4. 生成服务SSL KEY ${SSL_KEY} \033[0m" +openssl genrsa -out ${SSL_KEY} ${SSL_SIZE} + +echo -e "\033[32m ====> 5. 生成服务SSL CSR ${SSL_CSR} \033[0m" +openssl req -sha256 -new -key ${SSL_KEY} -out ${SSL_CSR} -subj "/C=${CN}/CN=${SSL_DOMAIN}" -config ${SSL_CONFIG} + +echo -e "\033[32m ====> 6. 生成服务SSL CERT ${SSL_CERT} \033[0m" +openssl x509 -sha256 -req -in ${SSL_CSR} -CA ${CA_CERT} \ + -CAkey ${CA_KEY} -CAcreateserial -out ${SSL_CERT} \ + -days ${SSL_DATE} -extensions v3_req \ + -extfile ${SSL_CONFIG} + +echo -e "\033[32m ====> 7. 证书制作完成 \033[0m" +echo +echo -e "\033[32m ====> 8. 以YAML格式输出结果 \033[0m" +echo "----------------------------------------------------------" +echo "ca_key: |" +cat $CA_KEY | sed 's/^/ /' +echo +echo "ca_cert: |" +cat $CA_CERT | sed 's/^/ /' +echo +echo "ssl_key: |" +cat $SSL_KEY | sed 's/^/ /' +echo +echo "ssl_csr: |" +cat $SSL_CSR | sed 's/^/ /' +echo +echo "ssl_cert: |" +cat $SSL_CERT | sed 's/^/ /' +echo + +echo -e "\033[32m ====> 9. 附加CA证书到Cert文件 \033[0m" +cat ${CA_CERT} >> ${SSL_CERT} +echo "ssl_cert: |" +cat $SSL_CERT | sed 's/^/ /' +echo + +echo -e "\033[32m ====> 10. 重命名服务证书 \033[0m" +echo "cp ${SSL_DOMAIN}.key tls.key" +cp ${SSL_DOMAIN}.key tls.key +echo "cp ${SSL_DOMAIN}.crt tls.crt" +cp ${SSL_DOMAIN}.crt tls.crt +``` + +执行脚本就好了 + +```shell +[root@localhost openssl] sh ./create_self-signed-cert.sh --ssl-domain=iswbm.com --ssl-trusted-ip=172.20.20.100 --ssl-trusted-domain=python.iswbm.com --ssl-size=2048 --ssl-cn=CN +... +... +... +[root@localhost openssl] ls -l +total 44 +-rw-r--r-- 1 root root 1131 Jul 27 22:40 cacerts.pem +-rw-r--r-- 1 root root 17 Jul 27 22:40 cacerts.srl +-rw-r--r-- 1 root root 1679 Jul 27 22:40 cakey.pem +-rw-r--r-- 1 root root 5220 Jul 27 22:40 create_self-signed-cert.sh +-rw-r--r-- 1 root root 2290 Jul 27 22:40 iswbm.com.crt +-rw-r--r-- 1 root root 1070 Jul 27 22:40 iswbm.com.csr +-rw-r--r-- 1 root root 1675 Jul 27 22:40 iswbm.com.key +-rw-r--r-- 1 root root 345 Jul 27 22:40 openssl.cnf +-rw-r--r-- 1 root root 2290 Jul 27 22:40 tls.crt +-rw-r--r-- 1 root root 1675 Jul 27 22:40 tls.key +``` + +验证证书:执行下面这条命令,要显示为 OK + +```shell +$ openssl verify -CAfile cacerts.pem tls.crt +``` + +查看服务器证书的内容 + +```shell +$ openssl x509 -in tls.crt -noout -text +``` + +不添加 CA 证书验证 + +```shell +$ openssl s_client -connect iswbm.com:443 -servername iswbm.com +``` + +添加 CA 证书验证 + +```shell +$ openssl s_client -connect iswbm.com:443 -servername iswbm.com -CAfile server-ca.crt +``` + + + +![](http://image.iswbm.com/20200728233602.png) + +![](http://image.iswbm.com/20200728231500.png) + + + + + +![](http://image.iswbm.com/20200728231548.png) + + + +![](http://image.iswbm.com/20200728234740.png) + +![](http://image.iswbm.com/20200728235331.png) + +![](http://image.iswbm.com/20200728235444.png) + +![](http://image.iswbm.com/20200728235531.png) + +### 向 CA 机构申请 + +在阿里云和腾讯云都可以 进行 SSL 证书的申请,由于阿里去没有免费SSL证书的申请选项,所以下面会以腾讯云为例。 + +登陆腾讯云,可以看到SSL 证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 + +![](http://image.iswbm.com/image-20200718102622663.png) + +为了方便演示,我这里选 `域名型免费版` 。 + +可以免费申请 DV 证书 + +![](http://image.iswbm.com/image-20200718101358755.png) + +点击 `免费快速申请` + +![](http://image.iswbm.com/20200729004207.png) + +再点击下一步,会需要你验证域名所有权,验证方式有如下三种 + +1. 自动DNS验证 +2. 手动DNS验证 +3. 文件验证 + +但由于我的域名不是腾讯云平台解析的,因此没有 自动DNS验证的选项,只有其他两种,你可以点击详细说明,跟着操作即可。 + +![](http://image.iswbm.com/image-20200718101652899.png) + + + +![](http://image.iswbm.com/20200729004207.png) + +最后确认申请,提交审核。 + +审核通过后,就会给你颁发证书,你可以从控制台点击证书下载。 + +![](http://image.iswbm.com/20200729004307.png) + +下载下来的会是一个 zip 包。 + +解压一下,会有不同的服务器类型的文件夹。 + +![](http://image.iswbm.com/20200729004456.png) + +每个文件夹里面都是三个文件 + +1. 根证书 +2. 域名证书 +3. 私钥文件 + + + +![](http://image.iswbm.com/20200729005349.png) + +## 6. 如何部署 SSL 证书 + +通过申请拿到了数字证书后,要想使用这个证书,需要在你的服务器上进行部署。 + +根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 + +![](http://image.iswbm.com/20200718105347.png) + + + +## 7. TLS/SSL 保证信息的安全 + +在信息安全性问题中,我们常常要做到三点才能保证信息的安全: + +1. 信息的保密性 +2. 信息的完整性 +3. 身份识别 + +将这三者结合起来,就是 TLS/SSL 做的事情 + +![img](https://img2018.cnblogs.com/blog/1169376/201910/1169376-20191008172456510-1302410435.png) + + + +1、客户端(浏览器)向服务端发出请求,服务端返回证书给客户端。 + +2、客户端拿到证书后,把证书里的签名与及明文信息分别取出来,然后会用自身携带的CA机构的公钥去解密签名,然后信息摘要1,然后再对明文信息进行HASH,得到一个信息摘要2,对比信息摘要1 和信息摘要2,如果一样,说明证书是合法的,也就是证书里的公钥是正确的。 + +以上采用的是非对称加密(CA的公钥和私钥),保证了客户端接收到服务端正确的公钥,有了服务端的公钥后,后面的信息加密都可以使用这个公钥,而用这个公钥加密过后的密文,只有服务端的私钥能解,就算黑客拿到了也没法解开。 + + + +[理解公钥与私钥](https://songlee24.github.io/2015/05/03/public-key-and-private-key/) + + + +http://www.humenggroup.com/newsInfo-34-263.html + +https://blog.caojun.xyz/posts/macos_trust_ssl/ + +https://www.cnblogs.com/xdyixia/p/11610102.html + +[前端须知的 Cookie 知识小结](https://juejin.im/post/5cd60dd751882520365ab55a) + +https://docs.rancher.cn/rancher2x/install-prepare/self-signed-ssl.html#_1-http-over-ssl + +[细说localStorage, sessionStorage, Cookie, Session](https://juejin.im/entry/5ac4d661f265da23a049c92a) \ No newline at end of file From e189ee19c4a764f28d4542f514e29e5d2228afe8 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Thu, 30 Jul 2020 23:29:24 +0800 Subject: [PATCH 114/147] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=A4=E7=AF=87?= =?UTF-8?q?=E6=95=B0=E5=AD=97=E8=AF=81=E4=B9=A6=E7=9A=84=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c10/c10_08.md | 328 +------------------------------- source/c10/c10_08.rst | 424 ++++++++++++++++++++++++++++++++++++++++++ source/c10/c10_09.md | 276 +++++++++++++++++++++++++++ source/c10/c10_09.rst | 324 ++++++++++++++++++++++++++++++++ 4 files changed, 1031 insertions(+), 321 deletions(-) create mode 100644 source/c10/c10_08.rst create mode 100644 source/c10/c10_09.md create mode 100644 source/c10/c10_09.rst diff --git a/source/c10/c10_08.md b/source/c10/c10_08.md index 245a2b3..f17ccc4 100644 --- a/source/c10/c10_08.md +++ b/source/c10/c10_08.md @@ -1,4 +1,6 @@ -# 10.8 终于把 HTTPS数字证书给整明白了 +# 10.8 数字证书、签名到底是什么? + +![](http://image.iswbm.com/20200602135014.png) 我们都知道 HTTP 协议都是明文传输内容,为了保证数据传输的安全,HTTPS 协议就应运而生了,但它其实并不是一个全新的协议,而是HTTP 协议基本之上 再加上SSL/TLS 协议。 @@ -334,313 +336,7 @@ LQfiTStBBU6t/+mnDyij+XreGQ== -## 5. 数字证书如何制作或申请? - -说到这里,到底数字证书是从哪里来的呢? - -数字证书,你可以自己制作,也可以向CA权威机构申请。 - -二者的区别是: - -1. 自己颁发的证书,需要客户端验证通过,可能需要客户端自己安装上受信任的根证书。 -2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是可信任的。 - -### 自己制作的证书 - -自签名脚本:create_self-signed-cert.sh - -```shell -#!/bin/bash -e - -help () -{ - echo ' ================================================================ ' - echo ' --ssl-domain: 生成ssl证书需要的主域名,如不指定则默认为www.rancher.local,如果是ip访问服务,则可忽略;' - echo ' --ssl-trusted-ip: 一般ssl证书只信任域名的访问请求,有时候需要使用ip去访问server,那么需要给ssl证书添加扩展IP,多个IP用逗号隔开;' - echo ' --ssl-trusted-domain: 如果想多个域名访问,则添加扩展域名(SSL_TRUSTED_DOMAIN),多个扩展域名用逗号隔开;' - echo ' --ssl-size: ssl加密位数,默认2048;' - echo ' --ssl-cn: 国家代码(2个字母的代号),默认CN;' - echo ' 使用示例:' - echo ' ./create_self-signed-cert.sh --ssl-domain=www.test.com --ssl-trusted-domain=www.test2.com \ ' - echo ' --ssl-trusted-ip=1.1.1.1,2.2.2.2,3.3.3.3 --ssl-size=2048 --ssl-date=3650' - echo ' ================================================================' -} - -case "$1" in - -h|--help) help; exit;; -esac - -if [[ $1 == '' ]];then - help; - exit; -fi - -CMDOPTS="$*" -for OPTS in $CMDOPTS; -do - key=$(echo ${OPTS} | awk -F"=" '{print $1}' ) - value=$(echo ${OPTS} | awk -F"=" '{print $2}' ) - case "$key" in - --ssl-domain) SSL_DOMAIN=$value ;; - --ssl-trusted-ip) SSL_TRUSTED_IP=$value ;; - --ssl-trusted-domain) SSL_TRUSTED_DOMAIN=$value ;; - --ssl-size) SSL_SIZE=$value ;; - --ssl-date) SSL_DATE=$value ;; - --ca-date) CA_DATE=$value ;; - --ssl-cn) CN=$value ;; - esac -done - -# CA相关配置 -CA_DATE=${CA_DATE:-3650} -CA_KEY=${CA_KEY:-cakey.pem} -CA_CERT=${CA_CERT:-cacerts.pem} -CA_DOMAIN=cattle-ca - -# ssl相关配置 -SSL_CONFIG=${SSL_CONFIG:-$PWD/openssl.cnf} -SSL_DOMAIN=${SSL_DOMAIN:-'www.rancher.local'} -SSL_DATE=${SSL_DATE:-3650} -SSL_SIZE=${SSL_SIZE:-2048} - -## 国家代码(2个字母的代号),默认CN; -CN=${CN:-CN} - -SSL_KEY=$SSL_DOMAIN.key -SSL_CSR=$SSL_DOMAIN.csr -SSL_CERT=$SSL_DOMAIN.crt - -echo -e "\033[32m ---------------------------- \033[0m" -echo -e "\033[32m | 生成 SSL Cert | \033[0m" -echo -e "\033[32m ---------------------------- \033[0m" - -if [[ -e ./${CA_KEY} ]]; then - echo -e "\033[32m ====> 1. 发现已存在CA私钥,备份"${CA_KEY}"为"${CA_KEY}"-bak,然后重新创建 \033[0m" - mv ${CA_KEY} "${CA_KEY}"-bak - openssl genrsa -out ${CA_KEY} ${SSL_SIZE} -else - echo -e "\033[32m ====> 1. 生成新的CA私钥 ${CA_KEY} \033[0m" - openssl genrsa -out ${CA_KEY} ${SSL_SIZE} -fi - -if [[ -e ./${CA_CERT} ]]; then - echo -e "\033[32m ====> 2. 发现已存在CA证书,先备份"${CA_CERT}"为"${CA_CERT}"-bak,然后重新创建 \033[0m" - mv ${CA_CERT} "${CA_CERT}"-bak - openssl req -x509 -sha256 -new -nodes -key ${CA_KEY} -days ${CA_DATE} -out ${CA_CERT} -subj "/C=${CN}/CN=${CA_DOMAIN}" -else - echo -e "\033[32m ====> 2. 生成新的CA证书 ${CA_CERT} \033[0m" - openssl req -x509 -sha256 -new -nodes -key ${CA_KEY} -days ${CA_DATE} -out ${CA_CERT} -subj "/C=${CN}/CN=${CA_DOMAIN}" -fi - -echo -e "\033[32m ====> 3. 生成Openssl配置文件 ${SSL_CONFIG} \033[0m" -cat > ${SSL_CONFIG} <> ${SSL_CONFIG} <> ${SSL_CONFIG} - done - - if [[ -n ${SSL_TRUSTED_IP} ]]; then - ip=(${SSL_TRUSTED_IP}) - for i in "${!ip[@]}"; do - echo IP.$((i+1)) = ${ip[$i]} >> ${SSL_CONFIG} - done - fi -fi - -echo -e "\033[32m ====> 4. 生成服务SSL KEY ${SSL_KEY} \033[0m" -openssl genrsa -out ${SSL_KEY} ${SSL_SIZE} - -echo -e "\033[32m ====> 5. 生成服务SSL CSR ${SSL_CSR} \033[0m" -openssl req -sha256 -new -key ${SSL_KEY} -out ${SSL_CSR} -subj "/C=${CN}/CN=${SSL_DOMAIN}" -config ${SSL_CONFIG} - -echo -e "\033[32m ====> 6. 生成服务SSL CERT ${SSL_CERT} \033[0m" -openssl x509 -sha256 -req -in ${SSL_CSR} -CA ${CA_CERT} \ - -CAkey ${CA_KEY} -CAcreateserial -out ${SSL_CERT} \ - -days ${SSL_DATE} -extensions v3_req \ - -extfile ${SSL_CONFIG} - -echo -e "\033[32m ====> 7. 证书制作完成 \033[0m" -echo -echo -e "\033[32m ====> 8. 以YAML格式输出结果 \033[0m" -echo "----------------------------------------------------------" -echo "ca_key: |" -cat $CA_KEY | sed 's/^/ /' -echo -echo "ca_cert: |" -cat $CA_CERT | sed 's/^/ /' -echo -echo "ssl_key: |" -cat $SSL_KEY | sed 's/^/ /' -echo -echo "ssl_csr: |" -cat $SSL_CSR | sed 's/^/ /' -echo -echo "ssl_cert: |" -cat $SSL_CERT | sed 's/^/ /' -echo - -echo -e "\033[32m ====> 9. 附加CA证书到Cert文件 \033[0m" -cat ${CA_CERT} >> ${SSL_CERT} -echo "ssl_cert: |" -cat $SSL_CERT | sed 's/^/ /' -echo - -echo -e "\033[32m ====> 10. 重命名服务证书 \033[0m" -echo "cp ${SSL_DOMAIN}.key tls.key" -cp ${SSL_DOMAIN}.key tls.key -echo "cp ${SSL_DOMAIN}.crt tls.crt" -cp ${SSL_DOMAIN}.crt tls.crt -``` - -执行脚本就好了 - -```shell -[root@localhost openssl] sh ./create_self-signed-cert.sh --ssl-domain=iswbm.com --ssl-trusted-ip=172.20.20.100 --ssl-trusted-domain=python.iswbm.com --ssl-size=2048 --ssl-cn=CN -... -... -... -[root@localhost openssl] ls -l -total 44 --rw-r--r-- 1 root root 1131 Jul 27 22:40 cacerts.pem --rw-r--r-- 1 root root 17 Jul 27 22:40 cacerts.srl --rw-r--r-- 1 root root 1679 Jul 27 22:40 cakey.pem --rw-r--r-- 1 root root 5220 Jul 27 22:40 create_self-signed-cert.sh --rw-r--r-- 1 root root 2290 Jul 27 22:40 iswbm.com.crt --rw-r--r-- 1 root root 1070 Jul 27 22:40 iswbm.com.csr --rw-r--r-- 1 root root 1675 Jul 27 22:40 iswbm.com.key --rw-r--r-- 1 root root 345 Jul 27 22:40 openssl.cnf --rw-r--r-- 1 root root 2290 Jul 27 22:40 tls.crt --rw-r--r-- 1 root root 1675 Jul 27 22:40 tls.key -``` - -验证证书:执行下面这条命令,要显示为 OK - -```shell -$ openssl verify -CAfile cacerts.pem tls.crt -``` - -查看服务器证书的内容 - -```shell -$ openssl x509 -in tls.crt -noout -text -``` - -不添加 CA 证书验证 - -```shell -$ openssl s_client -connect iswbm.com:443 -servername iswbm.com -``` - -添加 CA 证书验证 - -```shell -$ openssl s_client -connect iswbm.com:443 -servername iswbm.com -CAfile server-ca.crt -``` - - - -![](http://image.iswbm.com/20200728233602.png) - -![](http://image.iswbm.com/20200728231500.png) - - - - - -![](http://image.iswbm.com/20200728231548.png) - - - -![](http://image.iswbm.com/20200728234740.png) - -![](http://image.iswbm.com/20200728235331.png) - -![](http://image.iswbm.com/20200728235444.png) - -![](http://image.iswbm.com/20200728235531.png) - -### 向 CA 机构申请 - -在阿里云和腾讯云都可以 进行 SSL 证书的申请,由于阿里去没有免费SSL证书的申请选项,所以下面会以腾讯云为例。 - -登陆腾讯云,可以看到SSL 证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 - -![](http://image.iswbm.com/image-20200718102622663.png) - -为了方便演示,我这里选 `域名型免费版` 。 - -可以免费申请 DV 证书 - -![](http://image.iswbm.com/image-20200718101358755.png) - -点击 `免费快速申请` - -![](http://image.iswbm.com/20200729004207.png) - -再点击下一步,会需要你验证域名所有权,验证方式有如下三种 - -1. 自动DNS验证 -2. 手动DNS验证 -3. 文件验证 - -但由于我的域名不是腾讯云平台解析的,因此没有 自动DNS验证的选项,只有其他两种,你可以点击详细说明,跟着操作即可。 - -![](http://image.iswbm.com/image-20200718101652899.png) - - - -![](http://image.iswbm.com/20200729004207.png) - -最后确认申请,提交审核。 - -审核通过后,就会给你颁发证书,你可以从控制台点击证书下载。 - -![](http://image.iswbm.com/20200729004307.png) - -下载下来的会是一个 zip 包。 - -解压一下,会有不同的服务器类型的文件夹。 - -![](http://image.iswbm.com/20200729004456.png) - -每个文件夹里面都是三个文件 - -1. 根证书 -2. 域名证书 -3. 私钥文件 - - - -![](http://image.iswbm.com/20200729005349.png) - -## 6. 如何部署 SSL 证书 - -通过申请拿到了数字证书后,要想使用这个证书,需要在你的服务器上进行部署。 - -根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 - -![](http://image.iswbm.com/20200718105347.png) - - - -## 7. TLS/SSL 保证信息的安全 +## 5. TLS/SSL 保证信息的安全 在信息安全性问题中,我们常常要做到三点才能保证信息的安全: @@ -662,18 +358,8 @@ $ openssl s_client -connect iswbm.com:443 -servername iswbm.com -CAfile server-c -[理解公钥与私钥](https://songlee24.github.io/2015/05/03/public-key-and-private-key/) - - - -http://www.humenggroup.com/newsInfo-34-263.html - -https://blog.caojun.xyz/posts/macos_trust_ssl/ - -https://www.cnblogs.com/xdyixia/p/11610102.html - -[前端须知的 Cookie 知识小结](https://juejin.im/post/5cd60dd751882520365ab55a) +## 参考文章 -https://docs.rancher.cn/rancher2x/install-prepare/self-signed-ssl.html#_1-http-over-ssl +- [HTTPS中CA证书的签发及使用过程](https://www.cnblogs.com/xdyixia/p/11610102.html) -[细说localStorage, sessionStorage, Cookie, Session](https://juejin.im/entry/5ac4d661f265da23a049c92a) \ No newline at end of file +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_08.rst b/source/c10/c10_08.rst new file mode 100644 index 0000000..6e902f9 --- /dev/null +++ b/source/c10/c10_08.rst @@ -0,0 +1,424 @@ +10.8 数字证书、签名到底是什么? +=============================== + +|image0| + +我们都知道 HTTP 协议都是明文传输内容,为了保证数据传输的安全,HTTPS +协议就应运而生了,但它其实并不是一个全新的协议,而是HTTP 协议基本之上 +再加上SSL/TLS 协议。 + +因此当你访问一个支持 https 的网站时,是需要先进行 SSL/TLS +握手建立连接的。 + +SSL/TLS 握手的目的是为了 **安全** +地协商出一份\ **对称加密**\ 的密钥,有了这个密钥之后,后续的数据全部使用这个密钥进行加密。 + +这个过程其实挺有趣的,涉及到的知识点,专业名词也很多,比如对称加密,非对称加密,信息摘要,数字签名,数字证书,公钥和私钥。本篇文章,会详细地介绍这些极易混淆的专业名词。 + +在讲解之前,我先给你提出几个摸底问题,如果你已经还不能够熟练回答,那么本篇文章会给你答案: + +1、 对称加密和非对称加密,各有什么优缺点? + +2、对称加密和非对称加密,是排他关系吗?是否可以搭配使用? + +3、摘要和加密有什么区别?有了摘要算法为什么还要有加密算法? + +4、如何在通信时同时做到保密性、高效性? + +5、如何申请数字证书?证书分为哪几种?SSL 证书如何部署? + +6、完整说下证书申请、证书签发、证书下发客户端、客户端验证证书、数据加密传输的整个流程? + +1. 对称加密与非对称加密 +----------------------- + +对称加密 +~~~~~~~~ + +``对称加密``\ 是通信双方共同拥有一把密钥。 + +这把密钥可以把明文加密(encryption)成密文,也可以把密文解密(decryption)成明文。 + +常见的对称加密算法有AES、DES、RC4,其中最常用的是AES。 + +对称加密的优点是:速度快。 + +同时也有一个缺点,就是不那么安全,一旦你的密钥被别人窃取了,所有的数据就会在网络的世界里裸奔。 + +非对称加密 +~~~~~~~~~~ + +与 对称加密相对的是 ``非对称加密``\ 。 + +通信双方持有不同的密钥。 + +服务端的密钥,称之为 ``私钥``\ (private key),客户端的密钥,称之为 +``公钥`` (public key)。 + +他们二者的区别是: + +1、私钥应仅在服务端保存,绝不可泄露。而公钥可以存在于任何的客户端,即使黑客拿到了也没有关系。 + +2、公钥加密的密文只有相对应的私钥才能解密 + +|image1| + +3、私钥加密的内容,所有与之相对应的公钥都能解密。 + +4、公钥通常用来生成签名,私钥用来验证签名。 + +|image2| + +5、公钥和私钥是相对的,两者本身并没有规定哪一个必须是公钥或私钥。这意味着,公钥只要不对外公开,那就可以做为私钥,私钥公开后也可以做为公钥。 + +典型的非对称加密算法有 RSA 。 + +非对称加密的优点,就是安全系数特别高。缺点就是速度会慢一些。 + +对称与非对称加密结合 +~~~~~~~~~~~~~~~~~~~~ + +当客户端收到的公钥是准确的时候,通信就是安全的。 + +因为用正确公钥加密过的密文,只有服务端的私钥能解。 + +那么如何保证,客户端收到正确的公钥呢? + +答案是:通过非对称加密来协商对称加密的密钥,服务端一旦把正确的公钥安全地送达到客户端后,后续的通信,为了保证高效通信,再采用对称加密来加密数据。 + +具体的过程,后面会更加详细的阐述这一过程。 + +2. 摘要、签名、证书是啥? +------------------------- + +信息摘要 +~~~~~~~~ + +一段信息,经过摘要算法得到一串哈希值,就是摘要(dijest)。 + +常见的摘要算法有MD5、SHA1、SHA256、SHA512等。 + +关于摘要,有几点需要你明白的: + +1、摘要算法,是把任意长度的信息,映射成一个定长的字符串。 + +2、摘要算法,两个不同的信息,是有可能算出同一个摘要值的。 + +3、摘要算法与加密算法不同,不存在解密的过程。 + +4、摘要算法不用于数据的保密,而是用于数据的完整性校验。 + +数字签名 +~~~~~~~~ + +摘要经过私钥的加密后,便有了一个新的名字 – ``数字签名``\ 。 + +``签名`` 是在发送方,这是一个加密的过程。 + +``验签`` 是在接收方,这是一个解密的过程。 + +那搞懂数字签名的意义是什么?只要回答下面两个问题即可。 + +**第一个问题,有了信息摘要,为何还要有数字签名?** + +答:信息摘要,虽然也不可逆,但却容易却被伪造。所以信息摘要只用于校验完整性,而要保证信息摘要的正确性,就要依靠数字签名啦。 + +数字签名的签名和验签是非对称加密,其他人除非拿到私钥,不然没法伪造。 + +**第二个问题,为什么不对内容直接加密,而是对摘要进行加密。** + +答:由上面我们知道了非对称加密的速度非常慢,如果传输的数据量非常大,那这个加密再解密的时间要远比网络传输的时间来得长,这样反而会得不偿失。 + +如果我们对传输的内容只有完整性要求,而安全性没有要求(意思是传输的内容被人知道了也没关系)。那就可以对摘要进行加密,到客户端这里解密后得到摘要明文,再用这个摘要明文与传输的数据二次计算的摘要进行比较,若一致,则说明传输的内容是完整的,没有被篡改。 + +数字证书 +~~~~~~~~ + +在数字签名那里,不知道你有没有发现一个问题? + +数字签名是非对称加密,服务端有一个私钥,客户端一个公钥,只有这两个对上了验签。 + +那假如说你(客户端)拿到的公钥并不是服务端给的呢,而是黑客塞给你的呢?而你却把这个假公钥当成真的,那么当你使用这个假公钥加密一些敏感信息时,黑客就可以截取你的这段信息,由于这信息是用黑客自己的公钥加密的,这样一来,黑客拿自己的私钥就能解密得到你的敏感信息。 + +这就是问题所在。 + +要解决这个问题,其实只要保证『公钥』是可信的。只有服务端发给你的公钥你才能拿,而坏人给你的公钥,你要懂得识别并丢弃它。 + +数字证书就应运而生了。 + +要理解数字证书,同样只要搞懂两个问题即可。 + +1. 数字证书是什么东西?其实它就是一个 ``.crt`` 文件 +2. 数字证书是谁颁发的?由权威证书认证机构颁发,一般我们简称为 CA 机构 + +3. 数字证书如何申请的?或者说如何颁发的? + +为了让你理解这一过程,我画了下面这张图: + +|image3| + +1、在自己的服务器上生成一对公钥和私钥。然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等其他信息整合在一起,生成\ ``.csr`` +文件。 + +2、将这个 ``.csr`` 文件发给 CA 机构,CA +机构收到申请后,会通过各种手段验证申请者的组织信息和个人信息,如无异常(组织存在,企业合法,确实是域名的拥有者),CA +就会使用散列算法对\ ``.csr``\ 里的明文信息先做一个HASH,得到一个信息摘要,再用 +CA 自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的 +**签名**\ 。签名 + ``.csr`` 明文信息,即是 **证书**\ 。CA +把这个证书返回给申请人 + +3. 数字证书(Certificate) +------------------------ + +在HTTPS的传输过程中,有一个非常关键的角色–\ ``数字证书``\ ,那什么是数字证书?又有什么作用呢? + +所谓数字证书,是一种用于电脑的身份识别机制。由数字证书颁发机构(CA)对使用私钥创建的签名请求文件做的签名(盖章),表示CA结构对证书持有者的认可。 + +3.1 数字证书拥有以下几个优点 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- 使用数字证书能够提高用户的可信度; +- 数字证书中的公钥,能够与服务端的私钥配对使用,实现数据传输过程中的加密和解密; +- 在证认使用者身份期间,使用者的敏感个人数据并不会被传输至证书持有者的网络系统上; + +2.2. 证书类型 +~~~~~~~~~~~~~ + +x509的证书编码格式有两种: + +1. PEM(Privacy-enhanced Electronic Mail)是明文格式的,以 —–BEGIN + CERTIFICATE—–开头,已—–END + CERTIFICATE—–结尾。中间是经过base64编码的内容,apache需要的证书就是这类编码的证书.查看这类证书的信息的命令为: + ``openssl x509 -noout -text -in server.pem``\ 。其实PEM就是把DER的内容进行了一次base64编码 +2. DER是二进制格式的证书,查看这类证书的信息的命令为: + ``openssl x509 -noout -text -inform der -in server.der`` + +2.3. 扩展名 +~~~~~~~~~~~ + +- .crt证书文件,可以是DER(二进制)编码的,也可以是PEM(ASCII + (Base64))编码的),在类unix系统中比较常见; +- .cer也是证书,常见于Windows系统。编码类型同样可以是DER或者PEM的,windows下有工具可以转换crt到cer; +- .csr证书签名请求文件,一般是生成请求以后发送给CA,然后CA会给您签名并发回证书 +- .key一般公钥或者密钥都会用这种扩展名,可以是DER编码的或者是PEM编码的。查看DER编码的(公钥或者密钥)的文件的命令为: + ``openssl rsa -inform DER -noout -text -in xxx.key``\ 。查看PEM编码的(公钥或者密钥)的文件的命令为: + ``openssl rsa -inform PEM -noout -text -in xxx.key``; +- .p12证书文件,包含一个X509证书和一个被密码保护的私钥 + +2.4 证书的种类 +~~~~~~~~~~~~~~ + +安全证书主要分为DV、OV和EV三个种类,对应的安全等级为一般、较好和最高三个等级。三者的审核过程、审核标准和对应的域名数量也不同,所以价格在一两百元到几万元不等。 + +DV SSL +^^^^^^ + +DV SSL证书是只验证网站域名所有权的简易型(Class +1级)SSL证书,可10分钟快速颁发,能起到加密传输的作用,但无法向用户证明网站的真实身份。 + +目前市面上的免费证书都是这个类型的,只是提供了对数据的加密,但是对提供证书的个人和机构的身份不做验证。 + +OV SSL +^^^^^^ + +OV SSL,提供加密功能,对申请者做严格的身份审核验证,提供可信×××明。 + +和DV SSL的区别在于,OV SSL +提供了对个人或者机构的审核,能确认对方的身份,安全性更高。 + +所以这部分的证书申请是收费的~ + +EV SSL +^^^^^^ + +超安=EV=最安全、最严格 超安EV +SSL证书遵循全球统一的严格身份验证标准,是目前业界安全级别最高的顶级 +(Class 4级)SSL证书。 + +金融证券、银行、第三方支付、网上商城等,重点强调网站安全、企业可信形象的网站,涉及交易支付、客户隐私信息和账号密码的传输。 + +这部分的验证要求最高,申请费用也是最贵的。 + +选择签发机构时,最好选择行业认可的全球范围内都可以使用的ca机构签发的证书。目前我们国内的证书能够符合标准的还不是特别多,主要原因是有一些证书不能够被国外的浏览器所认可,在使用的时候需要进行一定的额外操作。 + +-------------- + +根据保护域名的数量需求,SSL证书又分为: + +**单域名版:**\ 只保护一个域名,例如 +`www.abc.com `__ 或者 login.abc.com 之类的单个域名 + +**多域名版:**\ 一张证书可以保护多个域名,例如同时保护 +`www.abc.com `__ , +`www.bcd.com, `__ pay.efg.com 等 + +**通配符版:**\ 一张证书保护同一个主域名下同一级的所有子域名,不限个数,形如 +\*.abc.com 。注意,通配符版只有 DVSSL 和 OVSSL 具有, EVSSL +不具有通配符版本。 + +2.5 证书在哪里 +~~~~~~~~~~~~~~ + +当你在下载并安装浏览器时,浏览器内部其实已经内嵌了全世界公认的根证书颁发机构的证书。 + +若一个网站的数字证书的证书颁发机构在浏览器中没有,则需要引导用户自行导入。 + +如果你想在 Chrome 中查看有哪些受信任的证书颁发机构,可以点击 ``设置`` -> +``隐私设置与安全性`` -> ``安全`` -> ``管理证书`` + +|image4| + +2.6 证书里的信息 +~~~~~~~~~~~~~~~~ + +在上图的位置里,随便双击点开一个证书,就可以查看证书里的内容。 + +内容非常多,最主要的有 + +- 证书是哪个机构的? +- 证书里的公钥是什么? +- 证书有效期是什么时候? +- 采用的哪种加解密的算法? + +2.7 证书吊销 +~~~~~~~~~~~~ + +证书是有生命周期的,如果证书的私钥泄漏了那这个证书就得吊销,一般有两种吊销方式:CRL和OCSP。 + +CRL( Certificate Revocation +List)是CA机构维护的一个已经被吊销的证书序列号列表,浏览器需要定时更新这个列表,浏览器在验证证书合法性的时候也会在证书吊销列表中查询是否已经被吊销,如果被吊销了那这个证书也是不可信的。可以看出,这个列表随着被吊销证书的增加而增加,列表会越来越大,浏览器还需要定时更新,实时性也比较差。 + +所以,后来就有了 OCSP (Online Certificate Status +Protocol)在线证书状态协议,这个协议就是解决了 CRL +列表越来越大和实时性差的问题而生的。有了这个协议,浏览器就可以不用定期更新CRL了,在验证证书的时候直接去CA服务器实时校验一下证书有没有被吊销就可以,是解决了CRL的问题,但是每次都要去CA服务器上校验也会很慢,在网络环境较差的时候或者跨国访问的时候,体验就非常差了,OCSP虽然解决了CRL的问题但是性能却很差。 + +如果你想了解更多关于证书的内容,可以前往华为云的公开文档进行学习:https://support.huaweicloud.com/scm_faq/scm_01_0020.html + +4. 如何生成 CSR 文件 +-------------------- + +CSR是Certificate Signing Request的英文缩写,即证书签名请求文件。 + +当申请者申请数字证书时,CSP(加密服务提供者)生成私钥,同时也生成了CSR文件。申请者将CSR文件提交至Certificate +Authority +(CA)机构后,CA机构使用其根证书私钥签名,从而就生成了数字证书。 + +申请者通过CSR文件,向CA机构申请数字证书。获取证书后,就能证明申请者的网站是可信的,数据传输是加密的。 + +接下来来了解一下,CSR 文件是如何生成的? + +使用 OpenSSL 生成 +~~~~~~~~~~~~~~~~~ + +假设申请的域名为 +**python.iswbm.com**\ ,公司名称为\ **派森时光科技**\ ,部门是\ **IT部**\ ,公司在\ **中国广东省深圳市**\ 。可通过运行下方命令行生成CSR文件: + +.. code:: shell + + $ openssl req -new –SHA256 -newkey rsa:2048 -nodes -keyout python.iswbm.com.key -out python.iswbm.com.csr -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=派森时光科技/OU=IT/CN=python.iswbm.com" + +此命令行表示: + +- ``req``\ 参数:表示证书请求request,用于生成CSR文件。 +- ``SHA256``\ 参数:表示CSR签名时用的摘要算法。 +- ``newkey``\ 参数:表示指定证书的算法。参数 2048:表示密钥对的长度。 +- ``nodes``\ 参数:表示不对私钥加密。 +- ``keyout``\ 参数:表示生成的私钥文件。名为\ ``iswbm.key``\ 的私钥文件,需自行保管,用于获取证书后的部署过程。 +- ``out``\ 参数:表示生成的 CSR + 文件。名为\ ``iswbm.com.csr``\ 的CSR文件,用于提交至CA机构验证信息,从而获取证书。 +- ``subj``\ 参数:表示CSR信息,具体有哪些参数,可以继续往下看。 + +subj参数说明: + +- ``C``\ :Country,表示国家,申请者或申请企业所在国家的英文或两位大写国家代码。如:CN +- ``ST``\ :State/Province,表示省份,申请者或申请企业所在地的省/市/自治区英文或拼音全称。如:Guangdong +- ``L``\ :Locality,表示城市,申请者或申请企业所在城市的英文或拼音全称。如:Shenzhen +- ``O``\ :Organization,表示申请者的姓名或申请企业的名称。 +- ``OU``\ :Organizational + Unit,表示申请人所在部门的英文或拼音全称。如:IT +- ``CN``\ :Common Name,表示你要为哪个域名申请证书,可是单域名(比如 + python.iswbm.com),也可以是泛域名(*.iswbm.com),也可以是为多个域名申请一个证书(具体我没操作过)。 + +上一条命令执行完后,会在你的本地目录下生成俩文件 + +- python.iswbm.com.csr:用于向CA机构申请证书 + +.. code:: shell + + $ cat python.iswbm.com.csr + -----BEGIN CERTIFICATE REQUEST----- + MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ2Rvbmcx + ETAPBgNVBAcMCFNoZW56aGVuMS0wKwYDVQQKDCTDpsK0wr7DpsKjwq7DpsKXwrbD + ... + 7lgB4QC1aIFz8gi9TGMJU2LqTDJCj+tgM68LDBdMLeQ8XZ33C95Nl0qt7yG+zjlZ + 01jBh+T882r8x9gKdwb7nZSWFQY4/YTq+sY++YW/QuCNRcJ2vbM18U/HlIRsZ3su + x6Neh08= + -----END CERTIFICATE REQUEST----- + +- python.iswbm.com.key:私钥,自行保存,不要外泄 + +.. code:: shell + + $ cat python.iswbm.com.key + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4OrcM9hTs9Hao + SzjsVJFX2Mmd+mToMG3u++o2Fd5yrPYq4COkT33lnL9kJNrDWqGp5TRkWqNwLaPl + ... + a/lKBWLcvxE+IQ+mxNbN058kEJ3l8WAcAFCebLm5czUqmIVa3JR+cBDLvGFZVn6z + 72AP5D/Evds4BOO+VzAiVLU6Ai78qhACuVExZNQCxdvJy4LxpeckUpCem9hAPiIY + LQfiTStBBU6t/+mnDyij+XreGQ== + -----END PRIVATE KEY----- + +使用在线生成工具 +~~~~~~~~~~~~~~~~ + +使用 OpenSSL 工具生成 CSR +文件的方法固然简单,但使用时,需要你的了解代码中参数的意思。 + +如果你不想花心思去记这些,推荐你使用 +`CSR在线生成工具 `__\ (https://myssl.com/csr_create.html) + +你只需简单地输入如下信息,再点击 ``OpenSSL生成``\ ,你就可以获得一条 +OpenSSL 命令,这下你再也不用自己拼凑参数啦,真的太方便了。为什么不点击 +``生成`` 让其直接生成 私钥文件 和 CSR文件 呢?当然是为了安全起见啦。 + +|image5| + +5. TLS/SSL 保证信息的安全 +------------------------- + +在信息安全性问题中,我们常常要做到三点才能保证信息的安全: + +1. 信息的保密性 +2. 信息的完整性 +3. 身份识别 + +将这三者结合起来,就是 TLS/SSL 做的事情 + +.. figure:: https://img2018.cnblogs.com/blog/1169376/201910/1169376-20191008172456510-1302410435.png + :alt: img + + img + +1、客户端(浏览器)向服务端发出请求,服务端返回证书给客户端。 + +2、客户端拿到证书后,把证书里的签名与及明文信息分别取出来,然后会用自身携带的CA机构的公钥去解密签名,然后信息摘要1,然后再对明文信息进行HASH,得到一个信息摘要2,对比信息摘要1 +和信息摘要2,如果一样,说明证书是合法的,也就是证书里的公钥是正确的。 + +以上采用的是非对称加密(CA的公钥和私钥),保证了客户端接收到服务端正确的公钥,有了服务端的公钥后,后面的信息加密都可以使用这个公钥,而用这个公钥加密过后的密文,只有服务端的私钥能解,就算黑客拿到了也没法解开。 + +参考文章 +-------- + +- `HTTPS中CA证书的签发及使用过程 `__ + +|image6| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/image-20200723233619429.png +.. |image2| image:: http://image.iswbm.com/image-20200724121333485.png +.. |image3| image:: http://image.iswbm.com/20200724124502.png +.. |image4| image:: http://image.iswbm.com/20200717222206.png +.. |image5| image:: http://image.iswbm.com/20200718100052.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c10/c10_09.md b/source/c10/c10_09.md new file mode 100644 index 0000000..e8e55b5 --- /dev/null +++ b/source/c10/c10_09.md @@ -0,0 +1,276 @@ +# 10.9 如何申请数字证书,点亮你的 HTTPS? + +![](http://image.iswbm.com/20200602135014.png) + +上一节讲解了关于 SSL 数字证书的基础知识,这一篇就来实战演示一下,如何将你的 HTTP 网站加上 HTTPS 。 + +要想使用 HTTPS ,要做的事情还是有点多的: + +1. 准备一个域名和服务器(自行准备) +2. 并将该域名解析到你的服务器ip上 +3. 生成 CSR 证书请求文件 +4. 拿着 CSR 文件去申请证书 +5. 把申请到的证书部署到你的服务器上 + +## 1. 设置 DNS 解析 + +我有一个域名 `iswbm.com`,其下有好多的子域名,比如写 Python 的在线博客 `python.iswbm.com`,还有写 Golang 的在线博客 `golang.iswbm.com`,还有我的图床 `image.iswbm.com` 等等。 + +这里新建了个子域名 `demo.iswbm.com` ,并设置其解析到 我的服务器上,如下图(服务器 ip 已码掉)。 + +![](http://image.iswbm.com/20200728233602.png) + +## 2. 搭建 HTTP 站点 + +为了方便,我这里就使用 Apache 放了一个 HTTP 的静态网页,方法很简单,大家百度即可。 + +![](http://image.iswbm.com/20200729230813.png) + + + +## 3. 申请 SSL 证书 + +SSL 数字证书怎么来的呢? + +你可以自己制作,也可以向CA权威机构申请。 + +二者的区别是: + +1. 自己颁发的证书,需要客户端验证通过,也就是需要用户手动安装证书,并将其设置为受信任的根证书。但即使如此,浏览器上( chrome, firefox)仍不认可这种自签名证书,会在地址栏前面提示连接不安全,手动安装证书后,也会提示该证书无效。若想要继续访问,并忽略该提示,可以选择继续访问。 +2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是绝对可信任的。因此你在访问网站时,不会提示连接不安全。 + +下面,我将分别向你展示这两种方法,都是如何操作的。 + + + +### 第一种:向权威CA机构申请 + +在阿里云和腾讯云都可以 进行 SSL 证书的申请,证书的申请有付费的(价格也不便宜),也有免费的,看了一圈,只有腾讯云有免费的 SSL 证书的申请渠道(阿里云听说以前也有,后来关闭了)。 + +本篇文章,仅以演示教学之用,所以只用腾讯云的免费证书的就足够啦。 + +登陆腾讯云,可以看到SSL 证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 + +![](http://image.iswbm.com/image-20200718102622663.png) + +点击选择 `域名型免费版` + +![](http://image.iswbm.com/image-20200718101358755.png) + +点击 `免费快速申请`后,填写域名和你的个人邮箱 + +![](http://image.iswbm.com/20200729232432.png) + +再点击下一步,会需要你验证域名所有权,验证方式有如下三种 + +1. 自动DNS验证 +2. 手动DNS验证 +3. 文件验证 + +但由于我的域名不是腾讯云平台解析的,因此没有 自动DNS验证的选项,只有其他两种 + +![](http://image.iswbm.com/image-20200718101652899.png) + +点击 `确认申请` 后,会提示你进入域名验证所有权的流程,这里我选择 手动DNS验证。 + +![](http://image.iswbm.com/20200729004207.png) + +审核通过后,3s 内就会给你颁发证书,你可以从控制台点击证书下载。 + +![](http://image.iswbm.com/20200729004307.png) + +下载下来的会是一个 zip 包。 + +解压一下,会有不同的服务器类型(有 Apache、IIS、Nginx、Tomcat)的文件夹。 + +![](http://image.iswbm.com/20200729004456.png) + +我使用的是 Apache ,在这个文件夹下面有三个文件: + +1. `1_root_bundle.crt`:根证书 +2. `2_demo.iswbm.com.crt`:域名证书 +3. `3_demo.iswbm.com.key`:私钥文件 + +这三个文件,下一步会部署于我的服务器上。 + +接下来讲第二种 SSL 证书申请方式。 + +### 第二种:自签名的 SSL 证书 + +没有权威的第三方 CA 机构给自己颁发证书,那就自己给自己颁发咯。 + +自签名 SSL 的证书制作过程,可以分为两步: + +1. 自己要当 CA 机构,那 CA 有的 CA 根证书、私钥 一样都不能少,因此第一步:生成 CA 的 crt 证书 和 CA 的私钥。 +2. 要申请证书,首先服务器自己要有一个密钥对(公钥和私钥) +3. 拿着上面生成的公钥,制作一个 CSR 证书申请文件 +4. 用第一步的 CA 私钥,给这个 CSR 签名,生成咱所需要的 SSL 数字证书文件。 + +步骤很多,命令很多,命令所带的参数更多,对于只想学习证书签发流程的你,把这些命令都背下来,并不是一个好的选择,毕竟这种事可能也干不了几次。 + +因此,我把这些步骤、命令,都整合成一个 shell 脚本,你只要执行这个脚本就行了。 + +```shell +$ bash create_self-signed-cert.sh --ssl-domain=demo.iswbm.com --ssl-trusted-domain=demo.iswbm.com --ssl-size=2048 --ssl-date=3650 +``` + +对应的参数的解释,在脚本中都有解释 + +![](http://image.iswbm.com/20200729235153.png) + +这个脚本过长,不好直接贴上来,我将它放在我的公众号(**Python编程时光**)后台,你可以直接回复『**证书签名**』直接获取下载。 + + 执行完成后,会在当前目录下生成好多个文件。 + +其中,只有两个文件对我们有用 + +![](http://image.iswbm.com/20200730000142.png) + + + +## 4. 部署 SSL 证书 + +根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 + +![](http://image.iswbm.com/20200718105347.png) + +这里我将以 CentOS 7.2 + Apache 为例,演示如何部署 SSL 证书。 + +先安装一下 mod_ssl + +```shell +$ yum install -y mod_ssl +``` + +安装完后,在 /etc/httpd/conf.d/ 目录下 会有个 ssl.conf 文件。 + +编辑修改这个文件,以下是我的配置供你参考 + +```shell + + DocumentRoot "/var/www/html" + #填写证书名称 + ServerName demo.iswbm.com + #启用 SSL 功能 + SSLEngine on + #证书文件的路径 + SSLCertificateFile /etc/pki/tls/certs/demo.iswbm.com.crt + #私钥文件的路径 + SSLCertificateKeyFile /etc/pki/tls/private/demo.iswbm.com.key + #根证书文件的路径 + SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt + +``` + +**如果你的证书是从权威 CA 机构上申请来的。** + +比如我上面从腾讯云上申请来的,那么这三个文件就是从已经从腾讯云的控制台上下载下来的那三个文件。 + +在修改完后,务必记得把下载的这三个文件,放到相应的目录中去。 + +![](http://image.iswbm.com/20200730214826.png) + +配置完 ssl.conf,可能还需要你 check 一下 `/etc/httpd/conf/httpd.conf` 的一些配置,这些配置一般用默认的就可以,但是以防万一,还是写一下吧 + +``` +Include conf.modules.d/*.conf +``` + +写这一行的目的,就是为了 httpd 去加载 mod_ssl 这个模块 + +```shell +$ cat /etc/httpd/conf.modules.d/00-ssl.conf +LoadModule ssl_module modules/mod_ssl.so +``` + +一切配置完成后,记得重启一下 httpd 服务 + +```shell +$ systemctl restart httpd +``` + +然后使用 chrome 访问一下 `https//demo.iswbm.com` 看看,大功告成。 + +![](http://image.iswbm.com/20200730215613.png) + + + +**而如果你的证书是自签名的。** + +ssl.conf 配置文件下的应该改成这样 + +``` + + DocumentRoot "/var/www/html" + #填写证书名称 + ServerName demo.iswbm.com + #启用 SSL 功能 + SSLEngine on + #证书文件的路径 + SSLCertificateFile /etc/pki/tls/certs/tls.crt + #私钥文件的路径 + SSLCertificateKeyFile /etc/pki/tls/private/tls.key + +``` + +同时记得把这两个文件也拷贝到相应的目录下 + +```shell +$ cp tls.crt /etc/pki/tls/certs/ +$ cp tls.key /etc/pki/tls/private/ +``` + +最后还是不要忘了重启 httpd + +```shell +$ systemctl restart httpd +``` + +试着用 chrome 访问一下,可以看到 chrome 提示该连接不安全 + +![](http://image.iswbm.com/20200730220835.png) + +如果执意要访问,可以点击左下方的 `继续前往`,这样以后再访问的时候,就不会再出现这个警告页面了。 + +![](http://image.iswbm.com/20200730221745.png) + +`不安全` 三个字,让人很没有安全感,那有没有办法去掉呢? + +答案是,没有,只要是自签名的证书,在 chrome ,firefox 等主流浏览器看来都是不安全的。 + +即使你把这个根证书添加到你的受信任的证书列表中,也是徒然。 + +下面就试着来安装一下这个根证书。 + +按照下图指示,拖动证书到本地磁盘上。 + +![](http://image.iswbm.com/20200728234740.png) + +打开 Mac 上的 `钥匙串访问` + +![](http://image.iswbm.com/20200730222441.png) + +点击 `登陆`,然后再拖动这个证书到窗口中进行安装 + +![](http://image.iswbm.com/20200728235331.png) + +右键该证书,点击 `显示简介`,跳出下面的界面后,再点击 `信任`,把 IP 安全选择选为 `始终信任`。 + +![](http://image.iswbm.com/20200730222700.png) + +设置完后,再访问下 `demo.iswbm.com` ,仍然显示连接不安全,并且证书是无效的 + +![](http://image.iswbm.com/20200730222827.png) + +点击证书,显示证书,该证书确实已经放入信任列表中了。 + +![](http://image.iswbm.com/20200730222928.png) + +## 参考文档 + +- [Apache 服务器证书安装](https://cloud.tencent.com/document/product/400/35243) +- [自签名 SSL 证书](https://docs.rancher.cn/rancher2x/install-prepare/self-signed-ssl.html#_2-3-%E6%89%A9%E5%B1%95%E5%90%8D) + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_09.rst b/source/c10/c10_09.rst new file mode 100644 index 0000000..e8c0715 --- /dev/null +++ b/source/c10/c10_09.rst @@ -0,0 +1,324 @@ +10.9 如何申请数字证书,点亮你的 HTTPS? +======================================= + +|image0| + +上一节讲解了关于 SSL +数字证书的基础知识,这一篇就来实战演示一下,如何将你的 HTTP 网站加上 +HTTPS 。 + +要想使用 HTTPS ,要做的事情还是有点多的: + +1. 准备一个域名和服务器(自行准备) +2. 并将该域名解析到你的服务器ip上 +3. 生成 CSR 证书请求文件 +4. 拿着 CSR 文件去申请证书 +5. 把申请到的证书部署到你的服务器上 + +1. 设置 DNS 解析 +---------------- + +我有一个域名 ``iswbm.com``\ ,其下有好多的子域名,比如写 Python +的在线博客 ``python.iswbm.com``\ ,还有写 Golang 的在线博客 +``golang.iswbm.com``\ ,还有我的图床 ``image.iswbm.com`` 等等。 + +这里新建了个子域名 ``demo.iswbm.com`` ,并设置其解析到 +我的服务器上,如下图(服务器 ip 已码掉)。 + +|image1| + +2. 搭建 HTTP 站点 +----------------- + +为了方便,我这里就使用 Apache 放了一个 HTTP +的静态网页,方法很简单,大家百度即可。 + +|image2| + +3. 申请 SSL 证书 +---------------- + +SSL 数字证书怎么来的呢? + +你可以自己制作,也可以向CA权威机构申请。 + +二者的区别是: + +1. 自己颁发的证书,需要客户端验证通过,也就是需要用户手动安装证书,并将其设置为受信任的根证书。但即使如此,浏览器上( + chrome, + firefox)仍不认可这种自签名证书,会在地址栏前面提示连接不安全,手动安装证书后,也会提示该证书无效。若想要继续访问,并忽略该提示,可以选择继续访问。 +2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是绝对可信任的。因此你在访问网站时,不会提示连接不安全。 + +下面,我将分别向你展示这两种方法,都是如何操作的。 + +第一种:向权威CA机构申请 +~~~~~~~~~~~~~~~~~~~~~~~~ + +在阿里云和腾讯云都可以 进行 SSL +证书的申请,证书的申请有付费的(价格也不便宜),也有免费的,看了一圈,只有腾讯云有免费的 +SSL 证书的申请渠道(阿里云听说以前也有,后来关闭了)。 + +本篇文章,仅以演示教学之用,所以只用腾讯云的免费证书的就足够啦。 + +登陆腾讯云,可以看到SSL +证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 + +|image3| + +点击选择 ``域名型免费版`` + +|image4| + +点击 ``免费快速申请``\ 后,填写域名和你的个人邮箱 + +|image5| + +再点击下一步,会需要你验证域名所有权,验证方式有如下三种 + +1. 自动DNS验证 +2. 手动DNS验证 +3. 文件验证 + +但由于我的域名不是腾讯云平台解析的,因此没有 +自动DNS验证的选项,只有其他两种 + +|image6| + +点击 ``确认申请`` 后,会提示你进入域名验证所有权的流程,这里我选择 +手动DNS验证。 + +|image7| + +审核通过后,3s 内就会给你颁发证书,你可以从控制台点击证书下载。 + +|image8| + +下载下来的会是一个 zip 包。 + +解压一下,会有不同的服务器类型(有 +Apache、IIS、Nginx、Tomcat)的文件夹。 + +|image9| + +我使用的是 Apache ,在这个文件夹下面有三个文件: + +1. ``1_root_bundle.crt``\ :根证书 +2. ``2_demo.iswbm.com.crt``\ :域名证书 +3. ``3_demo.iswbm.com.key``\ :私钥文件 + +这三个文件,下一步会部署于我的服务器上。 + +接下来讲第二种 SSL 证书申请方式。 + +第二种:自签名的 SSL 证书 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +没有权威的第三方 CA 机构给自己颁发证书,那就自己给自己颁发咯。 + +自签名 SSL 的证书制作过程,可以分为两步: + +1. 自己要当 CA 机构,那 CA 有的 CA 根证书、私钥 + 一样都不能少,因此第一步:生成 CA 的 crt 证书 和 CA 的私钥。 +2. 要申请证书,首先服务器自己要有一个密钥对(公钥和私钥) +3. 拿着上面生成的公钥,制作一个 CSR 证书申请文件 +4. 用第一步的 CA 私钥,给这个 CSR 签名,生成咱所需要的 SSL + 数字证书文件。 + +步骤很多,命令很多,命令所带的参数更多,对于只想学习证书签发流程的你,把这些命令都背下来,并不是一个好的选择,毕竟这种事可能也干不了几次。 + +因此,我把这些步骤、命令,都整合成一个 shell +脚本,你只要执行这个脚本就行了。 + +.. code:: shell + + $ bash create_self-signed-cert.sh --ssl-domain=demo.iswbm.com --ssl-trusted-domain=demo.iswbm.com --ssl-size=2048 --ssl-date=3650 + +对应的参数的解释,在脚本中都有解释 + +|image10| + +这个脚本过长,不好直接贴上来,我将它放在我的公众号(\ **Python编程时光**\ )后台,你可以直接回复『\ **证书签名**\ 』直接获取下载。 + +执行完成后,会在当前目录下生成好多个文件。 + +其中,只有两个文件对我们有用 + +|image11| + +4. 部署 SSL 证书 +---------------- + +根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 + +|image12| + +这里我将以 CentOS 7.2 + Apache 为例,演示如何部署 SSL 证书。 + +先安装一下 mod_ssl + +.. code:: shell + + $ yum install -y mod_ssl + +安装完后,在 /etc/httpd/conf.d/ 目录下 会有个 ssl.conf 文件。 + +编辑修改这个文件,以下是我的配置供你参考 + +.. code:: shell + + + DocumentRoot "/var/www/html" + #填写证书名称 + ServerName demo.iswbm.com + #启用 SSL 功能 + SSLEngine on + #证书文件的路径 + SSLCertificateFile /etc/pki/tls/certs/demo.iswbm.com.crt + #私钥文件的路径 + SSLCertificateKeyFile /etc/pki/tls/private/demo.iswbm.com.key + #根证书文件的路径 + SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt + + +**如果你的证书是从权威 CA 机构上申请来的。** + +比如我上面从腾讯云上申请来的,那么这三个文件就是从已经从腾讯云的控制台上下载下来的那三个文件。 + +在修改完后,务必记得把下载的这三个文件,放到相应的目录中去。 + +|image13| + +配置完 ssl.conf,可能还需要你 check 一下 ``/etc/httpd/conf/httpd.conf`` +的一些配置,这些配置一般用默认的就可以,但是以防万一,还是写一下吧 + +:: + + Include conf.modules.d/*.conf + +写这一行的目的,就是为了 httpd 去加载 mod_ssl 这个模块 + +.. code:: shell + + $ cat /etc/httpd/conf.modules.d/00-ssl.conf + LoadModule ssl_module modules/mod_ssl.so + +一切配置完成后,记得重启一下 httpd 服务 + +.. code:: shell + + $ systemctl restart httpd + +然后使用 chrome 访问一下 ``https//demo.iswbm.com`` 看看,大功告成。 + +|image14| + +**而如果你的证书是自签名的。** + +ssl.conf 配置文件下的应该改成这样 + +:: + + + DocumentRoot "/var/www/html" + #填写证书名称 + ServerName demo.iswbm.com + #启用 SSL 功能 + SSLEngine on + #证书文件的路径 + SSLCertificateFile /etc/pki/tls/certs/tls.crt + #私钥文件的路径 + SSLCertificateKeyFile /etc/pki/tls/private/tls.key + + +同时记得把这两个文件也拷贝到相应的目录下 + +.. code:: shell + + $ cp tls.crt /etc/pki/tls/certs/ + $ cp tls.key /etc/pki/tls/private/ + +最后还是不要忘了重启 httpd + +.. code:: shell + + $ systemctl restart httpd + +试着用 chrome 访问一下,可以看到 chrome 提示该连接不安全 + +|image15| + +如果执意要访问,可以点击左下方的 +``继续前往``\ ,这样以后再访问的时候,就不会再出现这个警告页面了。 + +|image16| + +``不安全`` 三个字,让人很没有安全感,那有没有办法去掉呢? + +答案是,没有,只要是自签名的证书,在 chrome ,firefox +等主流浏览器看来都是不安全的。 + +即使你把这个根证书添加到你的受信任的证书列表中,也是徒然。 + +下面就试着来安装一下这个根证书。 + +按照下图指示,拖动证书到本地磁盘上。 + +|image17| + +打开 Mac 上的 ``钥匙串访问`` + +|image18| + +点击 ``登陆``\ ,然后再拖动这个证书到窗口中进行安装 + +|image19| + +右键该证书,点击 ``显示简介``\ ,跳出下面的界面后,再点击 ``信任``\ ,把 +IP 安全选择选为 ``始终信任``\ 。 + +|image20| + +设置完后,再访问下 ``demo.iswbm.com`` +,仍然显示连接不安全,并且证书是无效的 + +|image21| + +点击证书,显示证书,该证书确实已经放入信任列表中了。 + +|image22| + +参考文档 +-------- + +- `Apache + 服务器证书安装 `__ +- `自签名 SSL + 证书 `__ + +|image23| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200728233602.png +.. |image2| image:: http://image.iswbm.com/20200729230813.png +.. |image3| image:: http://image.iswbm.com/image-20200718102622663.png +.. |image4| image:: http://image.iswbm.com/image-20200718101358755.png +.. |image5| image:: http://image.iswbm.com/20200729232432.png +.. |image6| image:: http://image.iswbm.com/image-20200718101652899.png +.. |image7| image:: http://image.iswbm.com/20200729004207.png +.. |image8| image:: http://image.iswbm.com/20200729004307.png +.. |image9| image:: http://image.iswbm.com/20200729004456.png +.. |image10| image:: http://image.iswbm.com/20200729235153.png +.. |image11| image:: http://image.iswbm.com/20200730000142.png +.. |image12| image:: http://image.iswbm.com/20200718105347.png +.. |image13| image:: http://image.iswbm.com/20200730214826.png +.. |image14| image:: http://image.iswbm.com/20200730215613.png +.. |image15| image:: http://image.iswbm.com/20200730220835.png +.. |image16| image:: http://image.iswbm.com/20200730221745.png +.. |image17| image:: http://image.iswbm.com/20200728234740.png +.. |image18| image:: http://image.iswbm.com/20200730222441.png +.. |image19| image:: http://image.iswbm.com/20200728235331.png +.. |image20| image:: http://image.iswbm.com/20200730222700.png +.. |image21| image:: http://image.iswbm.com/20200730222827.png +.. |image22| image:: http://image.iswbm.com/20200730222928.png +.. |image23| image:: http://image.iswbm.com/20200607174235.png + From 5de0741029fbdad91c5e78b8b982e418435a4e9a Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 2 Aug 2020 00:04:09 +0800 Subject: [PATCH 115/147] Add Post --- source/c09/c09_05.md | 54 +++++++++++++++++++++++++++++++++++ source/c09/c09_05.rst | 66 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 source/c09/c09_05.md create mode 100644 source/c09/c09_05.rst diff --git a/source/c09/c09_05.md b/source/c09/c09_05.md new file mode 100644 index 0000000..30ddb15 --- /dev/null +++ b/source/c09/c09_05.md @@ -0,0 +1,54 @@ +# 9.5 让Python 执行任意代码时,都会自动念一段 『平安经』 + +![](http://image.iswbm.com/20200602135014.png) + +最近的"平安经"可谓是引起了不小的风波啊。 + +作为一个正儿八经的程序员,最害怕的就是自己的代码上线出现各种各样的 BUG。 + +为此明哥就研究了一下,如何在你执行任意 Python 代码前,让 Python 解释器自动念上一段平安经,保佑代码不出 BUG 。 + +没想到还真被我研究出来了 + +做好心理准备了嘛? + +我要开始作妖了,噢不,是开始念经了。 + +![](http://image.iswbm.com/20200801221705.png) + +感谢佛祖保佑,Everything is ok,No bugs in the code. + + + +你一定很想知道这是如何的吧? + +如果你对 Linux 比较熟悉,就会知道,当你在使用 SSH 远程登陆 Linux 服务器的时候?会读取 `.bash_profile` 文件加载一些环境变量。 + +`.bash_profile` 你可以视其为一个 shell 脚本,可以在这里写一些 shell 代码达到你的定制化需求。 + +而在 Python 中,也有类似 `.bash_profile` 的文件,这个文件一般情况下是不存在的。 + +我们需要新建一个用户环境目录,这个目录比较长,不需要你死记硬背,使用 site 模块的方法就可以获取,然后使用 `mkdir -p` 命令创建它。 + +![](http://image.iswbm.com/20200801220819.png) + +在这个目录下,新建一个 `usercustomize.py` 文件,注意名字必须是这个,换成其他的可就识别不到啦。 + +这个 `usercustomize.py` 的内容如下(明哥注:佛祖只保佑几个 Python 的主要应用方向,毕竟咱是 Python 攻城狮嘛...) + +![](http://image.iswbm.com/20200801221413.png) + +这个文件我放在了我的 github 上,你可以点此前往获取。 + +一切都完成后,无论你是使用 `python xxx.py` 执行脚本 + +![](http://image.iswbm.com/20200801221705.png) + +还是使用 `python` 进入 Python Shell ,都会先念一下平安经保平安。 + +![](http://image.iswbm.com/20200801221457.png) + + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c09/c09_05.rst b/source/c09/c09_05.rst new file mode 100644 index 0000000..10668a7 --- /dev/null +++ b/source/c09/c09_05.rst @@ -0,0 +1,66 @@ +9.5 让Python 执行任意代码时,都会自动念一段 『平安经』 +====================================================== + +|image0| + +最近的“平安经”可谓是引起了不小的风波啊。 + +作为一个正儿八经的程序员,最害怕的就是自己的代码上线出现各种各样的 BUG。 + +为此明哥就研究了一下,如何在你执行任意 Python 代码前,让 Python +解释器自动念上一段平安经,保佑代码不出 BUG 。 + +没想到还真被我研究出来了 + +做好心理准备了嘛? + +我要开始作妖了,噢不,是开始念经了。 + +|image1| + +感谢佛祖保佑,Everything is ok,No bugs in the code. + +你一定很想知道这是如何的吧? + +如果你对 Linux 比较熟悉,就会知道,当你在使用 SSH 远程登陆 Linux +服务器的时候?会读取 ``.bash_profile`` 文件加载一些环境变量。 + +``.bash_profile`` 你可以视其为一个 shell 脚本,可以在这里写一些 shell +代码达到你的定制化需求。 + +而在 Python 中,也有类似 ``.bash_profile`` +的文件,这个文件一般情况下是不存在的。 + +我们需要新建一个用户环境目录,这个目录比较长,不需要你死记硬背,使用 +site 模块的方法就可以获取,然后使用 ``mkdir -p`` 命令创建它。 + +|image2| + +在这个目录下,新建一个 ``usercustomize.py`` +文件,注意名字必须是这个,换成其他的可就识别不到啦。 + +这个 ``usercustomize.py`` 的内容如下(明哥注:佛祖只保佑几个 Python +的主要应用方向,毕竟咱是 Python 攻城狮嘛…) + +|image3| + +这个文件我放在了我的 github 上,你可以点此前往获取。 + +一切都完成后,无论你是使用 ``python xxx.py`` 执行脚本 + +|image4| + +还是使用 ``python`` 进入 Python Shell ,都会先念一下平安经保平安。 + +|image5| + +|image6| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200801221705.png +.. |image2| image:: http://image.iswbm.com/20200801220819.png +.. |image3| image:: http://image.iswbm.com/20200801221413.png +.. |image4| image:: http://image.iswbm.com/20200801221705.png +.. |image5| image:: http://image.iswbm.com/20200801221457.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png + From 63714fb157624f64b7f8295658b04f9cdbc7735e Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 2 Aug 2020 00:04:28 +0800 Subject: [PATCH 116/147] Update --- source/c04/c04_18.md | 13 +++++++++++++ source/c04/c04_18.rst | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 50ee7ef..029ccdc 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -87,6 +87,19 @@ command + space 如果不仍想让访达窗口保持在最前面,就按住 command 键 ``` +显示控制台 + +``` +原生:ctrl+shift+L +由于与 MWeb 的插入链接冲突,而Mweb 又没有修改快捷键的入口,所以我将启动台的快捷键改成:ctrl+option+L + +要卸载 app 的话 +1. ctrl+option+L 显示启动台 +2. 再按 ctrl+option(默认,不需要设置) 就会出现抖动效果,点击 x 就可以卸载了。 +``` + + + 设置我自定义的系统偏好设置 ![](http://image.iswbm.com/image-20200704192441091.png) diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index 55620d7..c17bc76 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -92,6 +92,17 @@ 直接拖动图片 如果不仍想让访达窗口保持在最前面,就按住 command 键 +显示控制台 + +:: + + 原生:ctrl+shift+L + 由于与 MWeb 的插入链接冲突,而Mweb 又没有修改快捷键的入口,所以我将启动台的快捷键改成:ctrl+option+L + + 要卸载 app 的话 + 1. ctrl+option+L 显示启动台 + 2. 再按 ctrl+option(默认,不需要设置) 就会出现抖动效果,点击 x 就可以卸载了。 + 设置我自定义的系统偏好设置 |image1| From b34eaecaa3a572cfdee710b58f1b4efad404fd89 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 2 Aug 2020 23:44:25 +0800 Subject: [PATCH 117/147] Update --- source/c01/c01_30.md | 67 ------------------------------------ source/c01/c01_30.rst | 80 ------------------------------------------- source/c09/c09_02.md | 8 +++-- source/c09/c09_02.rst | 10 ++++-- source/c09/c09_03.md | 7 +++- source/c09/c09_03.rst | 2 ++ source/c09/c09_05.md | 46 ++++++++++++++++++++++++- source/c09/c09_05.rst | 55 +++++++++++++++++++++++++++-- 8 files changed, 119 insertions(+), 156 deletions(-) delete mode 100644 source/c01/c01_30.md delete mode 100644 source/c01/c01_30.rst diff --git a/source/c01/c01_30.md b/source/c01/c01_30.md deleted file mode 100644 index 1839db7..0000000 --- a/source/c01/c01_30.md +++ /dev/null @@ -1,67 +0,0 @@ -# 1.30 盘点程序员学习编程的那些网站 - -![](http://image.iswbm.com/20200602135014.png) - -## 书栈网 - -**网站链接**:https://www.bookstack.cn/rank?tab=popular - -![](http://image.python-online.cn/20200104144109.png) - -## 魔法学院 - - **网站链接**:http://www.nowamagic.net/academy/ - -![](http://image.python-online.cn/20200112210558.png) - - - -## Python 3 标准库实例教程 - -**网站链接**:https://learnku.com/docs/pymotw - -![](http://image.iswbm.com/20200508201333.png) - - - -## Django Web 框架 - -网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django - -该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django) - -![](http://image.iswbm.com/20200525080531.png) - -在服务端网页编程里,重点介绍了 Django - -![](http://image.iswbm.com/20200525080715.png) - - - -## Python3 源码剖析 - -网站链接:https://flaggo.github.io/python3-source-code-analysis/ - -![](http://image.iswbm.com/image-20200701123010074.png) - - - -## Linux 手册 - -网站链接:https://man.linuxde.net/ - -![](http://image.iswbm.com/image-20200704204307530.png) - -## 每天一个linux命令 - -网站链接:www.cnblogs.com/peida/archive/2012/12/05/2803591.html - -## 实验楼:Linux 基础入门 - -网站链接:https://www.shiyanlou.com/courses/1 - -![](http://image.iswbm.com/20200704204506.png) - -网站链接:https://www.shiyanlou.com/courses/68 - -![](http://image.iswbm.com/20200704204558.png) \ No newline at end of file diff --git a/source/c01/c01_30.rst b/source/c01/c01_30.rst deleted file mode 100644 index 1793161..0000000 --- a/source/c01/c01_30.rst +++ /dev/null @@ -1,80 +0,0 @@ -1.30 盘点程序员学习编程的那些网站 -================================= - -|image0| - -书栈网 ------- - -**网站链接**\ :https://www.bookstack.cn/rank?tab=popular - -|image1| - -魔法学院 --------- - -**网站链接**\ :http://www.nowamagic.net/academy/ - -|image2| - -Python 3 标准库实例教程 ------------------------ - -**网站链接**\ :https://learnku.com/docs/pymotw - -|image3| - -Django Web 框架 ---------------- - -网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django - -该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django) - -|image4| - -在服务端网页编程里,重点介绍了 Django - -|image5| - -Python3 源码剖析 ----------------- - -网站链接:https://flaggo.github.io/python3-source-code-analysis/ - -|image6| - -Linux 手册 ----------- - -网站链接:https://man.linuxde.net/ - -|image7| - -每天一个linux命令 ------------------ - -网站链接:www.cnblogs.com/peida/archive/2012/12/05/2803591.html - -实验楼:Linux 基础入门 ----------------------- - -网站链接:https://www.shiyanlou.com/courses/1 - -|image8| - -网站链接:https://www.shiyanlou.com/courses/68 - -|image9| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20200104144109.png -.. |image2| image:: http://image.python-online.cn/20200112210558.png -.. |image3| image:: http://image.iswbm.com/20200508201333.png -.. |image4| image:: http://image.iswbm.com/20200525080531.png -.. |image5| image:: http://image.iswbm.com/20200525080715.png -.. |image6| image:: http://image.iswbm.com/image-20200701123010074.png -.. |image7| image:: http://image.iswbm.com/image-20200704204307530.png -.. |image8| image:: http://image.iswbm.com/20200704204506.png -.. |image9| image:: http://image.iswbm.com/20200704204558.png - diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md index 3f658ee..1ca292a 100644 --- a/source/c09/c09_02.md +++ b/source/c09/c09_02.md @@ -1,4 +1,4 @@ -# 9.2 没有这 50 个APP,我的 Mac 将只是一块铁 +# 9.2 我在Mac 上使用哪些APP? ![](http://image.iswbm.com/20200602135014.png) @@ -125,6 +125,8 @@ GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 +PDFGuru:PDF 阅读器 + ## 5. 文件管理 @@ -151,7 +153,7 @@ Keta:解压缩软件 PicGo:图床上传 -Typora/Bear:Markdown写作工具 +Typora/Bear/MWeb:Markdown写作工具 WPS:Office套件 @@ -159,6 +161,8 @@ TeamViewer:远程控制工具 iText,精准的 OCR 文字识别工具。 +思维导图:MindNode,Xmind + ## 7. 系统管理 CCleaner:系统清理、软件卸载 diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index c1ad1be..e85037a 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -1,5 +1,5 @@ -9.2 没有这 50 个APP,我的 Mac 将只是一块铁 -========================================== +9.2 我在Mac 上使用哪些APP? +=========================== |image0| @@ -133,6 +133,8 @@ GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 +PDFGuru:PDF 阅读器 + 5. 文件管理 ----------- @@ -160,7 +162,7 @@ Keta:解压缩软件 PicGo:图床上传 -Typora/Bear:Markdown写作工具 +Typora/Bear/MWeb:Markdown写作工具 WPS:Office套件 @@ -168,6 +170,8 @@ TeamViewer:远程控制工具 iText,精准的 OCR 文字识别工具。 +思维导图:MindNode,Xmind + 7. 系统管理 ----------- diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md index 8085d21..3ca5c78 100644 --- a/source/c09/c09_03.md +++ b/source/c09/c09_03.md @@ -64,4 +64,9 @@ https://www.draw.io/ 随机生成复杂 JSON:http://www.yyyweb.com/demo/inner-show/json-generator.html -生成 Markdwon 目录:https://ecotrust-canada.github.io/markdown-toc/ \ No newline at end of file +生成 Markdwon 目录:https://ecotrust-canada.github.io/markdown-toc/ + +代码分享:https://paste.ubuntu.com/ + + + diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index f0aee59..62ba4f1 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -58,5 +58,7 @@ JSON:http://www.yyyweb.com/demo/inner-show/json-generator.html 生成 Markdwon 目录:https://ecotrust-canada.github.io/markdown-toc/ +代码分享:https://paste.ubuntu.com/ + .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c09/c09_05.md b/source/c09/c09_05.md index 30ddb15..5dc7121 100644 --- a/source/c09/c09_05.md +++ b/source/c09/c09_05.md @@ -20,7 +20,7 @@ -你一定很想知道这是如何的吧? +你一定很想知道这是如何实现的吧? 如果你对 Linux 比较熟悉,就会知道,当你在使用 SSH 远程登陆 Linux 服务器的时候?会读取 `.bash_profile` 文件加载一些环境变量。 @@ -48,7 +48,51 @@ ![](http://image.iswbm.com/20200801221457.png) +除此之外,可还有其他方法呢? +当然是有,只不过相对来说,会麻烦一点了。 + +先来看一下效果。 + +先查看在 `~/Library/Python/3.9/lib/python/site-packages` 目录下并没有 `usercustomize.py` 文件。 + +但是在执行 python 进入 Python Shell 模式后,还是会打印平安经。 + +![](http://image.iswbm.com/20200801225652.png) + +这又是如何做到的?真见鬼了呀。 + +方法其实也很简单,只要做两件事,就能实现这样的效果: + +**第一件事**,在任意你喜欢的目录下,新建 一个Python 脚本,名字也随意,比如我叫 `startup.py`,内容还是和上面一样 + +![](http://image.iswbm.com/20200801221413.png) + +**第二件事**,设置一个环境变量 PYTHONSTARTUP,指向你的脚本路径 + +```shell +$ export PYTHONSTARTUP=/Users/MING/startup.py +``` + +这样就可以了。 + +但是这种方法只适用于 Python Shell ,只不适合 Python 执行脚本的方法。 + +![](http://image.iswbm.com/20200801230230.png) + +如果要在脚本中实现这种效果,我目前想到最粗糙我笨拙的方法了 -- `手动加载执行` + +![](http://image.iswbm.com/20200801230503.png) + + + +本文分享了两个非常冷门 Python 的黑魔法技巧,可以实现在你执行任意的 Python 代码前,自动召唤佛祖念上一段平安经。 + +希望本篇分享能对你有用,更多关于 Python Shell 的玩法,我已经整理在了我的 Github 上(https://github.com/iswbm/magic-python),可以前往查看。 + + + +--- ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c09/c09_05.rst b/source/c09/c09_05.rst index 10668a7..b1e25a4 100644 --- a/source/c09/c09_05.rst +++ b/source/c09/c09_05.rst @@ -20,7 +20,7 @@ 感谢佛祖保佑,Everything is ok,No bugs in the code. -你一定很想知道这是如何的吧? +你一定很想知道这是如何实现的吧? 如果你对 Linux 比较熟悉,就会知道,当你在使用 SSH 远程登陆 Linux 服务器的时候?会读取 ``.bash_profile`` 文件加载一些环境变量。 @@ -54,13 +54,64 @@ site 模块的方法就可以获取,然后使用 ``mkdir -p`` 命令创建它 |image5| +除此之外,可还有其他方法呢? + +当然是有,只不过相对来说,会麻烦一点了。 + +先来看一下效果。 + +先查看在 ``~/Library/Python/3.9/lib/python/site-packages`` 目录下并没有 +``usercustomize.py`` 文件。 + +但是在执行 python 进入 Python Shell 模式后,还是会打印平安经。 + |image6| +这又是如何做到的?真见鬼了呀。 + +方法其实也很简单,只要做两件事,就能实现这样的效果: + +**第一件事**\ ,在任意你喜欢的目录下,新建 一个Python +脚本,名字也随意,比如我叫 ``startup.py``\ ,内容还是和上面一样 + +|image7| + +**第二件事**\ ,设置一个环境变量 PYTHONSTARTUP,指向你的脚本路径 + +.. code:: shell + + $ export PYTHONSTARTUP=/Users/MING/startup.py + +这样就可以了。 + +但是这种方法只适用于 Python Shell ,只不适合 Python 执行脚本的方法。 + +|image8| + +如果要在脚本中实现这种效果,我目前想到最粗糙我笨拙的方法了 – +``手动加载执行`` + +|image9| + +本文分享了两个非常冷门 Python 的黑魔法技巧,可以实现在你执行任意的 +Python 代码前,自动召唤佛祖念上一段平安经。 + +希望本篇分享能对你有用,更多关于 Python Shell 的玩法,我已经整理在了我的 +Github 上(https://github.com/iswbm/magic-python),可以前往查看。 + +-------------- + +|image10| + .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200801221705.png .. |image2| image:: http://image.iswbm.com/20200801220819.png .. |image3| image:: http://image.iswbm.com/20200801221413.png .. |image4| image:: http://image.iswbm.com/20200801221705.png .. |image5| image:: http://image.iswbm.com/20200801221457.png -.. |image6| image:: http://image.iswbm.com/20200607174235.png +.. |image6| image:: http://image.iswbm.com/20200801225652.png +.. |image7| image:: http://image.iswbm.com/20200801221413.png +.. |image8| image:: http://image.iswbm.com/20200801230230.png +.. |image9| image:: http://image.iswbm.com/20200801230503.png +.. |image10| image:: http://image.iswbm.com/20200607174235.png From 5ace5aa4f0a275b3bf210a91b309b0cdafb9a0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Thu, 27 Aug 2020 18:23:12 +0800 Subject: [PATCH 118/147] update --- source/c04/c04_20.md | 10 +++++- source/c04/c04_21.md | 14 ++++++++ source/c08/c08_06.md | 78 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/source/c04/c04_20.md b/source/c04/c04_20.md index b94eff8..1131867 100644 --- a/source/c04/c04_20.md +++ b/source/c04/c04_20.md @@ -10,10 +10,18 @@ 2、过滤关键字 用减号 +谷歌搜索 `python parse 库` ,出现的都是 `urllib.parse`,可我实际想要找是 parse 库的相关资料。 + +![](http://image.iswbm.com/20200826102731.png) + +那我可以使用 `-` ,过滤掉 urllib 的搜索结果 + ``` -Python协程 -CSDN +python parse -urllib ``` +![](http://image.iswbm.com/20200826102136.png) + 3、必须包含某关键字 用加号 ``` diff --git a/source/c04/c04_21.md b/source/c04/c04_21.md index 6127de9..a10e7a6 100644 --- a/source/c04/c04_21.md +++ b/source/c04/c04_21.md @@ -292,6 +292,20 @@ logout ``` +**3.6 延长超时时间** + +若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 + +对于这种情况,一般重试几次就好了。 + +但是这样难免有些麻烦,有没有更好的解决方法呢? + +有的,可以通过延长超时时间。 + +```shell +$ pip install --default-timeout=100 +``` + ## 4. 卸载软件包 diff --git a/source/c08/c08_06.md b/source/c08/c08_06.md index 39463be..79b2e5a 100644 --- a/source/c08/c08_06.md +++ b/source/c08/c08_06.md @@ -471,7 +471,9 @@ config=eni.parse_deb_config('/etc/network/interfaces.d/50-cloud-init.cfg') 如果是按照旧虚拟机创建新的快照镜像,然后使用这个镜像创建新的虚拟机,有可能会在同一块网卡上出现新旧两个ip,这是因为虚拟机在启动过程中,会先读取原网络配置配置ip,然后才会运行 cloud-init 进行新ip的配置,而新ip的配置是使用 `ifup` 这个命令![](http://image.python-online.cn/Fp1TeHSiIMIQoZygbW9VSfAagB_d) -使用这种方式并不会将第一次配置的旧ip给清除掉。![](http://image.python-online.cn/Fh-5SQ8qYjhJEKovI6LmIpabSy2c) +使用这种方式并不会将第一次配置的旧ip给清除掉。 + +![](http://image.python-online.cn/Fh-5SQ8qYjhJEKovI6LmIpabSy2c) 这个问题,目前我只在CentOS6 中遇到过。可以通过修改代码让其先 `ifdown` 再 `ifup` 就可以解决这个问题。![](http://image.python-online.cn/20190430231812.png) @@ -606,9 +608,81 @@ runcmd: 但这仅仅只是写入,若要执行这些命令,还需要你在 /etc/cloud/cloud.cfg 中配置 `scripts_user`,这样 cloud-init 才会去执行它。 +## 8.6.5 如何读取修改缓存数据 + +当虚拟机启动时,cloudinit 会将 configdrive 里的数据读取并缓存,缓存文件是一个被序列化的二进制文件,名字固定为 `obj.pkl`。 + +序列化的函数存在于 stages.py 文件 + +- `_pkl_store()`:序列化 +- `_pkl_load()`:反序列化 + +![](http://image.iswbm.com/20200807135831.png) + +读取演示 + +``` +>>> from cloudinit import stages; +>>> file=stages._pkl_load("/var/lib/cloud/instances/1242171a-bf72-4a3a-acb0-9e5393e97865/obj.pkl") +>>> file.network_json +>>> file.metadata +``` + +写入演示 + +``` +>>> file.metadata["vhost_queues"]=1 +>>> stages._pkl_store(file, "/home/obj.pkl") +True +``` + + + +## 8.6.16 如何判断是新虚拟机 + +在 stages.py 下有一个函数 `is_new_instance()` + +![](http://image.iswbm.com/20200807161244.png) + +其中 `previous` 和 `nw_timestramp` 都是从 `/var/lib/cloud/instances/[instance_uud]/` 目录下直接读取的,导致旧虚拟机的信息。 + +而在 `/var/lib/cloud/instance` 目录下的信息,都是从 `/dev/sr0` 从读取的最新信息。 + +通过二者对比,就可以知道该虚拟机是否是一台新的虚拟机。 + + + +## 8.6.17 CentOS 6.x 如何更改网卡名 + +参考文章:[Changing the ethX to Ethernet Device Mapping in EL6](https://www.alteeve.com/w/Changing_the_ethX_to_Ethernet_Device_Mapping_in_EL6) + +关闭network + +```shell +$ /etc/init.d/network stop +``` + +修改ifcfg 文件里的 device 和 hwaddr + +```shell +$ vim /etc/sysconfig/network-scripts/ifcfg-eth* +``` + +修改 udev 网卡规则文件 + +```shell +vim /etc/udev/rules.d/70-persistent-net.rules +``` + +重新加载 udev,启动 network + +```shell +$ start_udev && /etc/init.d/network start +``` + -## 8.6.15 相关命令 +## 8.6.18 相关命令 ```shell # 查看机器里有哪几张网卡 From 21a6a0bf57f9cca61f2fbce557c5c89e9f4c1980 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Thu, 3 Sep 2020 12:42:36 +0800 Subject: [PATCH 119/147] =?UTF-8?q?=E5=9B=BE=E5=BA=8A=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/.DS_Store | Bin 12292 -> 12292 bytes source/_static/js/readmore.js | 2 +- source/aboutme.rst | 4 +- source/c01/c01_01.md | 4 +- source/c01/c01_01.rst | 4 +- source/c01/c01_04.md | 4 +- source/c01/c01_04.rst | 4 +- source/c01/c01_10.md | 16 +- source/c01/c01_10.rst | 16 +- source/c01/c01_14.md | 2 +- source/c01/c01_14.rst | 2 +- source/c01/c01_17.md | 8 +- source/c01/c01_17.rst | 8 +- source/c01/c01_18.md | 14 +- source/c01/c01_18.rst | 14 +- source/c01/c01_20.md | 8 +- source/c01/c01_20.rst | 8 +- source/c01/c01_21.md | 6 + source/c01/c01_21.rst | 10 ++ source/c01/c01_22.md | 2 +- source/c01/c01_22.rst | 2 +- source/c01/c01_24.md | 2 +- source/c01/c01_24.rst | 2 +- source/c01/c01_27.md | 14 +- source/c01/c01_27.rst | 14 +- source/c01/c01_34.md | 2 +- source/c01/c01_34.rst | 2 +- source/c01/c01_35.md | 6 +- source/c01/c01_35.rst | 6 +- source/c01/c01_36.md | 20 +-- source/c01/c01_36.rst | 20 +-- source/c01/c01_38.md | 10 +- source/c01/c01_38.rst | 10 +- source/c02/c02_01.md | 4 +- source/c02/c02_01.rst | 4 +- source/c02/c02_07.md | 2 +- source/c02/c02_07.rst | 2 +- source/c03/c03_01.md | 4 +- source/c03/c03_01.rst | 4 +- source/c03/c03_05.md | 6 +- source/c03/c03_05.rst | 6 +- source/c03/c03_06.md | 46 ++--- source/c03/c03_06.rst | 46 ++--- source/c04/c04_01.md | 2 +- source/c04/c04_01.rst | 2 +- source/c04/c04_02.md | 10 +- source/c04/c04_02.rst | 10 +- source/c04/c04_03.md | 38 ++-- source/c04/c04_03.rst | 38 ++-- source/c04/c04_04.md | 18 +- source/c04/c04_04.rst | 18 +- source/c04/c04_05.md | 34 ++-- source/c04/c04_05.rst | 34 ++-- source/c04/c04_06.md | 10 +- source/c04/c04_06.rst | 10 +- source/c04/c04_07.md | 22 +-- source/c04/c04_07.rst | 22 +-- source/c04/c04_11.md | 34 ++-- source/c04/c04_11.rst | 34 ++-- source/c04/c04_12.md | 12 +- source/c04/c04_12.rst | 12 +- source/c04/c04_14.md | 16 +- source/c04/c04_14.rst | 16 +- source/c04/c04_15.md | 188 ++++++++++---------- source/c04/c04_15.rst | 188 ++++++++++---------- source/c04/c04_17.md | 8 +- source/c04/c04_17.rst | 8 +- source/c04/c04_18.md | 6 +- source/c04/c04_18.rst | 6 +- source/c04/c04_19.md | 4 +- source/c04/c04_19.rst | 4 +- source/c04/c04_20.md | 2 +- source/c04/c04_20.rst | 2 +- source/c04/c04_21.md | 2 +- source/c04/c04_21.rst | 2 +- source/c04/c04_22.md | 2 +- source/c04/c04_22.rst | 2 +- source/c04/c04_23.md | 2 +- source/c04/c04_23.rst | 2 +- source/c04/c04_25.md | 17 ++ source/c04/c04_25.rst | 21 +++ source/c05/c05_01.md | 24 +-- source/c05/c05_01.rst | 24 +-- source/c05/c05_03.md | 2 +- source/c05/c05_03.rst | 2 +- source/c06/c06_01.md | 2 +- source/c06/c06_01.rst | 2 +- source/c06/c06_02.md | 20 +-- source/c06/c06_02.rst | 20 +-- source/c06/c06_03.md | 10 +- source/c06/c06_03.rst | 10 +- source/c06/c06_04.md | 14 +- source/c06/c06_04.rst | 14 +- source/c06/c06_06.md | 2 +- source/c06/c06_06.rst | 2 +- source/c07/c07_01.md | 6 +- source/c07/c07_01.rst | 6 +- source/c07/c07_02.md | 44 ++--- source/c07/c07_02.rst | 44 ++--- source/c07/c07_03.md | 4 +- source/c07/c07_03.rst | 4 +- source/c07/c07_04.md | 18 +- source/c07/c07_04.rst | 18 +- source/c07/c07_05.md | 4 +- source/c07/c07_05.rst | 4 +- source/c07/c07_10.md | 8 +- source/c07/c07_10.rst | 8 +- source/c07/c07_11.md | 2 +- source/c07/c07_11.rst | 2 +- source/c07/c07_12.md | 6 +- source/c07/c07_12.rst | 6 +- source/c07/c07_15.md | 2 +- source/c07/c07_15.rst | 2 +- source/c07/c07_16.md | 8 +- source/c07/c07_16.rst | 8 +- source/c08/c08_02.md | 4 +- source/c08/c08_02.rst | 4 +- source/c08/c08_03.md | 6 +- source/c08/c08_03.rst | 6 +- source/c08/c08_04.md | 12 +- source/c08/c08_04.rst | 12 +- source/c08/c08_05.md | 120 ++++++------- source/c08/c08_05.rst | 120 ++++++------- source/c08/c08_06.md | 84 ++++----- source/c08/c08_06.rst | 84 ++++----- source/c08/c08_07.md | 22 +-- source/c08/c08_07.rst | 22 +-- source/c08/c08_08.md | 16 +- source/c08/c08_08.rst | 16 +- source/c08/c08_09.md | 44 ++--- source/c08/c08_09.rst | 44 ++--- source/c08/c08_10.md | 6 +- source/c08/c08_10.rst | 6 +- source/c08/c08_11.md | 2 +- source/c08/c08_11.rst | 2 +- source/c08/c08_12.md | 16 +- source/c08/c08_12.rst | 16 +- source/c08/c08_13.md | 8 +- source/c08/c08_13.rst | 8 +- source/c08/c08_14.md | 28 +-- source/c08/c08_14.rst | 28 +-- source/c08/c08_15.md | 24 +-- source/c08/c08_15.rst | 24 +-- source/c09/c09_01.md | 10 +- source/c09/c09_01.rst | 10 +- source/c09/c09_02.md | 12 +- source/c09/c09_02.rst | 7 + source/c09/c09_06.md | 149 ++++++++++++++++ source/c09/c09_06.rst | 176 ++++++++++++++++++ source/c10/c10_11.rst | 324 ++++++++++++++++++++++++++++++++++ source/c10/c10_12.md | 38 ++++ source/c10/c10_12.rst | 48 +++++ source/preface.rst | 4 - source/thanks.rst | 2 +- 154 files changed, 1968 insertions(+), 1176 deletions(-) create mode 100644 source/c04/c04_25.md create mode 100644 source/c04/c04_25.rst create mode 100644 source/c09/c09_06.md create mode 100644 source/c09/c09_06.rst create mode 100644 source/c10/c10_11.rst create mode 100644 source/c10/c10_12.md create mode 100644 source/c10/c10_12.rst diff --git a/source/.DS_Store b/source/.DS_Store index f66a82f92fda39d3b637c1750247e57a245e6021..30e178227a51cc5fee5acff79a4774f68ec2d071 100644 GIT binary patch delta 77 zcmZokXi1phUDU^hRb=w=>)MXVwWK)}q9%wWJ^k(N@NoRpuR!!UWh=xa_Gk8$$^ Xu~5#<+zLn7C$AJY-OQqK8q5FybY>U` delta 51 zcmZokXi1ph&nUk!U^hRb@Ma!?MXZdBlMji$-h4pJn{#4=+GcKrBkYsEikog`(Kroe F007ma5{du- diff --git a/source/_static/js/readmore.js b/source/_static/js/readmore.js index e91d28f..5d8f2e3 100644 --- a/source/_static/js/readmore.js +++ b/source/_static/js/readmore.js @@ -40,7 +40,7 @@ var setIdTimer = setInterval(function () { id: id, blogId: '15406-1578143418297-890', name: 'Python编程时光', - qrcode: 'http://image.python-online.cn/20200104210733.png', + qrcode: 'http://image.iswbm.com/20200104210733.png', keyword: 'vip' }); } diff --git a/source/aboutme.rst b/source/aboutme.rst index 33479e6..b32bed2 100755 --- a/source/aboutme.rst +++ b/source/aboutme.rst @@ -3,10 +3,10 @@ ============== * 姓名: 王炳明 -* 微信: mrbensonwon +* 微信: stromwbm * 公众号: 《Python编程时光》&《Go编程时光》 * Email: wongbingming@163.com -* GitHub: https://github.com/bingmingwong +* GitHub: https://github.com/iswbm -------------------------------------------- diff --git a/source/c01/c01_01.md b/source/c01/c01_01.md index ce91a40..085f1aa 100644 --- a/source/c01/c01_01.md +++ b/source/c01/c01_01.md @@ -24,10 +24,10 @@ 这个地址里,有所有Python历史版本(2.0+)。 点击左边,Release Version栏目 对应的版本。 -![](http://image.python-online.cn/20190511165542.png) +![](http://image.iswbm.com/20190511165542.png) 进入对应详情页后,找到如图 `what's new in Python xx` 就可以查看此版本的新特性。 -![](http://image.python-online.cn/20190511165551.png) +![](http://image.iswbm.com/20190511165551.png) 网页是全英文的,需要你有一定的英文阅读能力。快去感觉一下吧。 diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index 76e531c..d147956 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -319,7 +319,7 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 |image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511165542.png -.. |image2| image:: http://image.python-online.cn/20190511165551.png +.. |image1| image:: http://image.iswbm.com/20190511165542.png +.. |image2| image:: http://image.iswbm.com/20190511165551.png .. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_04.md b/source/c01/c01_04.md index 8dbacd2..eba6d75 100644 --- a/source/c01/c01_04.md +++ b/source/c01/c01_04.md @@ -49,7 +49,7 @@ del pd.DataFrame.just_foo_cols # you can also remove the new method 在openstack中的例子 -![](http://image.python-online.cn/20190404215330.png) +![](http://image.iswbm.com/20190404215330.png) 还有就是gevent中也有用到。 @@ -103,4 +103,4 @@ setattr(sys.modules[module], key, --- -![](http://image.python-online.cn/20191117142849.png) +![](http://image.iswbm.com/20191117142849.png) diff --git a/source/c01/c01_04.rst b/source/c01/c01_04.rst index 13384fd..b0a5d5b 100755 --- a/source/c01/c01_04.rst +++ b/source/c01/c01_04.rst @@ -120,6 +120,6 @@ |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190404215330.png -.. |image2| image:: http://image.python-online.cn/20191117142849.png +.. |image1| image:: http://image.iswbm.com/20190404215330.png +.. |image2| image:: http://image.iswbm.com/20191117142849.png diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md index fbe8fc3..efd4ac7 100644 --- a/source/c01/c01_10.md +++ b/source/c01/c01_10.md @@ -446,7 +446,7 @@ Python 中的 def 语句在每次执行的时候都初始化一个函数对象 对于参数中提供了初始值的参数,由于 Python 中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def 的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list 会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。 -![](http://image.python-online.cn/20190511165650.png) +![](http://image.iswbm.com/20190511165650.png) ## 12. 访问类中的私有方法 @@ -816,7 +816,7 @@ python -m SimpleHTTPServer 8888 python3 -m http.server 8888 ``` -![](http://image.python-online.cn/20190511165716.png) +![](http://image.iswbm.com/20190511165716.png) SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 @@ -1056,7 +1056,7 @@ Namespaces are one honking great idea -- let's do more of those! ``` 就会自动打开一个网页。 -![](http://image.python-online.cn/20190511165735.png) +![](http://image.iswbm.com/20190511165735.png) ## 30. 局部/全局变量傻傻分不清 @@ -1980,7 +1980,7 @@ else: 稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python` -![](http://image.python-online.cn/20200331184021.png) +![](http://image.iswbm.com/20200331184021.png) 而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。 @@ -1988,19 +1988,19 @@ else: 不加的话,你每次执行这个脚本时,都得这样: `python xx.py` , -![](http://image.python-online.cn/20200331185034.png) +![](http://image.iswbm.com/20200331185034.png) 有没有一种方式?可以省去每次都加 `python` 呢? 当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 -![](http://image.python-online.cn/20200331184755.png) +![](http://image.iswbm.com/20200331184755.png) 明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ? 当我执行 `env python` 时,自动进入了 python console 的模式。 -![](http://image.python-online.cn/20200331185741.png) +![](http://image.iswbm.com/20200331185741.png) 这是为什么?和 直接执行 python 好像没什么区别呀 @@ -2010,7 +2010,7 @@ else: 具体演示过程,你可以看下面。 -![](http://image.python-online.cn/20200331190224.png) +![](http://image.iswbm.com/20200331190224.png) 那么对于这两者,我们应该使用哪个呢? diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 1c8a7a6..2858656 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -2324,17 +2324,17 @@ python 解释器都是 ``/usr/bin/python`` 。 |image13| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511165650.png -.. |image2| image:: http://image.python-online.cn/20190511165716.png +.. |image1| image:: http://image.iswbm.com/20190511165650.png +.. |image2| image:: http://image.iswbm.com/20190511165716.png .. |image3| image:: http://image.iswbm.com/20200509172331.png -.. |image4| image:: http://image.python-online.cn/20190511165735.png +.. |image4| image:: http://image.iswbm.com/20190511165735.png .. |image5| image:: http://image.iswbm.com/20200509122954.png .. |image6| image:: http://image.iswbm.com/20200509123107.png .. |image7| image:: http://image.iswbm.com/20200510112133.png -.. |image8| image:: http://image.python-online.cn/20200331184021.png -.. |image9| image:: http://image.python-online.cn/20200331185034.png -.. |image10| image:: http://image.python-online.cn/20200331184755.png -.. |image11| image:: http://image.python-online.cn/20200331185741.png -.. |image12| image:: http://image.python-online.cn/20200331190224.png +.. |image8| image:: http://image.iswbm.com/20200331184021.png +.. |image9| image:: http://image.iswbm.com/20200331185034.png +.. |image10| image:: http://image.iswbm.com/20200331184755.png +.. |image11| image:: http://image.iswbm.com/20200331185741.png +.. |image12| image:: http://image.iswbm.com/20200331190224.png .. |image13| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_14.md b/source/c01/c01_14.md index efae821..62ff689 100644 --- a/source/c01/c01_14.md +++ b/source/c01/c01_14.md @@ -187,7 +187,7 @@ with open_func('/Users/MING/mytest.txt') as file_in: 代码是这样的 -![](http://image.python-online.cn/20190310172800.png) +![](http://image.iswbm.com/20190310172800.png) 总结起来,使用上下文管理器有三个好处: diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index 62ccd87..17ae72c 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -214,6 +214,6 @@ open)的上下文管理器。 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190310172800.png +.. |image1| image:: http://image.iswbm.com/20190310172800.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md index fb3f247..4138112 100644 --- a/source/c01/c01_17.md +++ b/source/c01/c01_17.md @@ -68,7 +68,7 @@ class Student: 这下程序稍微有点人工智能了,能够自己明辨是非了。 -![](http://image.python-online.cn/20190425221322.png) +![](http://image.iswbm.com/20190425221322.png) 程序是智能了,但在`__init__`里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过 Property 特性,可以很好的应用在这里。于是你将代码修改成如下,代码的可读性瞬间提升了不少 @@ -121,7 +121,7 @@ class Student: 程序还是一样的人工智能,非常好。 -![](http://image.python-online.cn/20190425221322.png) +![](http://image.iswbm.com/20190425221322.png) 你以为你写的代码,已经非常优秀,无懈可击了。 @@ -180,7 +180,7 @@ class Student: 实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等) -![](http://image.python-online.cn/20190425221233.png) +![](http://image.iswbm.com/20190425221233.png) 以上,我举了下具体的实例,从最原始的编码风格到 Property ,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 @@ -418,7 +418,7 @@ math = TestProperty(fget=math) 由上面的注释,可以看出 `staticmethod` 其实就相当于一个描述符类,而`myfunc` 在此刻变成了一个描述符。关于 `staticmethod` 的实现,你可以参照下面这段我自己写的代码,加以理解。 -![](http://image.python-online.cn/20190519001930.png) +![](http://image.iswbm.com/20190519001930.png) 调用这个方法可以知道,每调用一次,它都会经过描述符类的 `__get__` 。 diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index 2594d3b..0febc80 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -582,9 +582,9 @@ super 的实现原理,就交由你来自己完成。 |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190425221322.png -.. |image2| image:: http://image.python-online.cn/20190425221322.png -.. |image3| image:: http://image.python-online.cn/20190425221233.png -.. |image4| image:: http://image.python-online.cn/20190519001930.png +.. |image1| image:: http://image.iswbm.com/20190425221322.png +.. |image2| image:: http://image.iswbm.com/20190425221322.png +.. |image3| image:: http://image.iswbm.com/20190425221233.png +.. |image4| image:: http://image.iswbm.com/20190519001930.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_18.md b/source/c01/c01_18.md index d56d934..e65704a 100644 --- a/source/c01/c01_18.md +++ b/source/c01/c01_18.md @@ -50,25 +50,25 @@ brew install mysql@5.7 经过漫长的等待后,mysql 终于安装成功 -![](http://image.python-online.cn/20190615001340.png) +![](http://image.iswbm.com/20190615001340.png) 这时候,再 执行 pip install MySQL-python,发现还是报错。 -![](http://image.python-online.cn/20190615001414.png) +![](http://image.iswbm.com/20190615001414.png) 有经验的我,立马知道了 `mysql_config` 这个文件的路径可能没有在环境变量中。 -![](http://image.python-online.cn/20190615001633.png) +![](http://image.iswbm.com/20190615001633.png) 然后,我又重新执行 `pip install MySQL-python` ,发现还是报错。 -![](http://image.python-online.cn/20190615001706.png) +![](http://image.iswbm.com/20190615001706.png) 但是这个错误相对比较明显,明眼人一看就知道是权限不足。 那我就以 root 权限去安装好了。 -![](http://image.python-online.cn/20190615001908.png) +![](http://image.iswbm.com/20190615001908.png) 终于安装成功,折腾了两个晚上(主要是网速慢)。 @@ -100,7 +100,7 @@ cd /usr/local/Cellar/mysql@5.7/5.7.25/bin 选择密码强度,视情况而写,我这边选最强的,长度大于8,有数字,有大小写,有特殊字符。 -![](http://image.python-online.cn/20190615112422.png) +![](http://image.iswbm.com/20190615112422.png) 接下来还会问你,是否删除其他匿名用户,是否删除 test 数据库,是否允许远程使用root登陆(安全起见我选不允许)。 @@ -142,5 +142,5 @@ mysql -uroot -p ## 1.18.4 命令行使用技巧 -![](http://image.python-online.cn/20190705225651.png) +![](http://image.iswbm.com/20190705225651.png) diff --git a/source/c01/c01_18.rst b/source/c01/c01_18.rst index 0b18f82..2a5b942 100644 --- a/source/c01/c01_18.rst +++ b/source/c01/c01_18.rst @@ -157,11 +157,11 @@ mysql,这时也请将其卸载再重新安装吧。 |image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190615001340.png -.. |image2| image:: http://image.python-online.cn/20190615001414.png -.. |image3| image:: http://image.python-online.cn/20190615001633.png -.. |image4| image:: http://image.python-online.cn/20190615001706.png -.. |image5| image:: http://image.python-online.cn/20190615001908.png -.. |image6| image:: http://image.python-online.cn/20190615112422.png -.. |image7| image:: http://image.python-online.cn/20190705225651.png +.. |image1| image:: http://image.iswbm.com/20190615001340.png +.. |image2| image:: http://image.iswbm.com/20190615001414.png +.. |image3| image:: http://image.iswbm.com/20190615001633.png +.. |image4| image:: http://image.iswbm.com/20190615001706.png +.. |image5| image:: http://image.iswbm.com/20190615001908.png +.. |image6| image:: http://image.iswbm.com/20190615112422.png +.. |image7| image:: http://image.iswbm.com/20190705225651.png diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md index bc98f95..0f8309c 100644 --- a/source/c01/c01_20.md +++ b/source/c01/c01_20.md @@ -18,7 +18,7 @@ 首先先来 Python2 的(以下在 Python2.7中测试通过) -![](http://image.python-online.cn/20190630111243.png) +![](http://image.iswbm.com/20190630111243.png) 可以得出结论: @@ -54,7 +54,7 @@ 还是刚刚那段代码,我更改了解释器为Python3.6(以下在 Python3.6中测试通过) -![](http://image.python-online.cn/20190630104956.png) +![](http://image.iswbm.com/20190630104956.png) 和Python2的唯一区别是,`People.jump` 在Python3 中变成了函数。 @@ -66,13 +66,13 @@ 执行People.jump('hello'),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。 -![](http://image.python-online.cn/20190630105735.png) +![](http://image.iswbm.com/20190630105735.png) **在 Python3中** 你可以发现,这里的jump的首参不再要求是 People 的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。 -![](http://image.python-online.cn/20190630105600.png) +![](http://image.iswbm.com/20190630105600.png) 也就是说,当你往jump中传入的首参为People的实例时,jump 就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。 diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index de5e995..0194ae4 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -110,9 +110,9 @@ self.jump了,因为首参不是 self,而如果使用@staticmethod |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190630111243.png -.. |image2| image:: http://image.python-online.cn/20190630104956.png -.. |image3| image:: http://image.python-online.cn/20190630105735.png -.. |image4| image:: http://image.python-online.cn/20190630105600.png +.. |image1| image:: http://image.iswbm.com/20190630111243.png +.. |image2| image:: http://image.iswbm.com/20190630104956.png +.. |image3| image:: http://image.iswbm.com/20190630105735.png +.. |image4| image:: http://image.iswbm.com/20190630105600.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md index b4a4f30..3114688 100644 --- a/source/c01/c01_21.md +++ b/source/c01/c01_21.md @@ -83,6 +83,12 @@ with close_stdout(): 24 ``` +## 7. 在网页上为所欲为 +```javascript +document.body.contentEditable='true' +``` + +在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 Ctrl+Z 后退 。 ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index a3b4ae5..11d87ce 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -91,6 +91,16 @@ >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) 24 +7. 在网页上为所欲为 +------------------- + +.. code:: javascript + + document.body.contentEditable='true' + +在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 +Ctrl+Z 后退 。 + |image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c01/c01_22.md b/source/c01/c01_22.md index 6590721..89daee8 100644 --- a/source/c01/c01_22.md +++ b/source/c01/c01_22.md @@ -159,7 +159,7 @@ $ python setup.py install 上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit 的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ 目录下 -![](http://image.python-online.cn/20190831160317.png) +![](http://image.iswbm.com/20190831160317.png) 然后安装一些 cloudinit 的依赖包。 diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index ccd94f4..793d914 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -202,6 +202,6 @@ pip是python的安装工具,很多python的常用工具,都可以通过pip |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190831160317.png +.. |image1| image:: http://image.iswbm.com/20190831160317.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md index c82e2cf..8030d2a 100644 --- a/source/c01/c01_24.md +++ b/source/c01/c01_24.md @@ -12,7 +12,7 @@ 当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。 -![](http://image.python-online.cn/20191027192949.png) +![](http://image.iswbm.com/20191027192949.png) ## 1. 导入系统的基础 diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index e5c2fdd..fae70f7 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -798,6 +798,6 @@ sys.path)查找器 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191027192949.png +.. |image1| image:: http://image.iswbm.com/20191027192949.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index 5974033..e962010 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -141,7 +141,7 @@ Python 包的分发可以分为两种: 源码包的本质是一个压缩包,其常见的格式有: -![](http://image.python-online.cn/20191218202833.png) +![](http://image.iswbm.com/20191218202833.png) 2. 以二进制包形式发布 @@ -151,7 +151,7 @@ Python 包的分发可以分为两种: 二进制包的常见格式有: -![](http://image.python-online.cn/20191218203005.png) +![](http://image.iswbm.com/20191218203005.png) ## 6. eggs 与 wheels 有什么区别? @@ -193,7 +193,7 @@ setup( description="Learn to Pack Python Module -->公众号:Python编程时光", # 项目主页 - url="http://python-online.cn/", + url="http://iswbm.com/", # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包 packages=find_packages() @@ -253,7 +253,7 @@ setup( author="wangbm", author_email="wongbingming@163.com", description="Learn to Pack Python Module", - url="http://python-online.cn/", + url="http://iswbm.com/", packages=find_packages(), # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等 @@ -371,7 +371,7 @@ setup( author="wangbm", author_email="wongbingming@163.com", description="Learn to Pack Python Module", - url="http://python-online.cn/", + url="http://iswbm.com/", packages=find_packages(), # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件 @@ -454,7 +454,7 @@ python setup.py bdist_rpm --release=20200617 setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数: -![](http://image.python-online.cn/20191218203255.png) +![](http://image.iswbm.com/20191218203255.png) 更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html @@ -544,7 +544,7 @@ $ python setup.py sdist --formats=gztar,zip 创建一个压缩的tarball和一个zip文件。可用格式为: -![](http://image.python-online.cn/20191218203517.png) +![](http://image.iswbm.com/20191218203517.png) 对以上的格式,有几点需要注意一下: diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 7676aa6..df6b488 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -223,7 +223,7 @@ wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块, description="Learn to Pack Python Module -->公众号:Python编程时光", # 项目主页 - url="http://python-online.cn/", + url="http://iswbm.com/", # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包 packages=find_packages() @@ -282,7 +282,7 @@ wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块, author="wangbm", author_email="wongbingming@163.com", description="Learn to Pack Python Module", - url="http://python-online.cn/", + url="http://iswbm.com/", packages=find_packages(), # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等 @@ -406,7 +406,7 @@ Python author="wangbm", author_email="wongbingming@163.com", description="Learn to Pack Python Module", - url="http://python-online.cn/", + url="http://iswbm.com/", packages=find_packages(), # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件 @@ -697,9 +697,9 @@ Index)上,它是 Python |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191218202833.png -.. |image2| image:: http://image.python-online.cn/20191218203005.png -.. |image3| image:: http://image.python-online.cn/20191218203255.png -.. |image4| image:: http://image.python-online.cn/20191218203517.png +.. |image1| image:: http://image.iswbm.com/20191218202833.png +.. |image2| image:: http://image.iswbm.com/20191218203005.png +.. |image3| image:: http://image.iswbm.com/20191218203255.png +.. |image4| image:: http://image.iswbm.com/20191218203517.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_34.md b/source/c01/c01_34.md index e5ad8a0..b0df514 100644 --- a/source/c01/c01_34.md +++ b/source/c01/c01_34.md @@ -14,7 +14,7 @@ $ python3 -m pip install sh 这里要注意一点,虽然在 Windows 上也可以安装成功,但是并不能使用,如果你尝试在 Windows 导入 它,会友好地提示你,该库只适用于 Linux 及 OSX 系统,假如你也想要在 Windows 使用,它推荐你使用它的 兄弟库 - `pbs` (https://pypi.org/project/pbs/)。 -![](http://image.python-online.cn/20200227201644.png) +![](http://image.iswbm.com/20200227201644.png) diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst index c020d68..4ebfe1e 100644 --- a/source/c01/c01_34.rst +++ b/source/c01/c01_34.rst @@ -93,6 +93,6 @@ demo ,而不需要任何的中文解释就可以让你知道他是如何使用 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20200227201644.png +.. |image1| image:: http://image.iswbm.com/20200227201644.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md index 3c72d22..1ee1341 100644 --- a/source/c01/c01_35.md +++ b/source/c01/c01_35.md @@ -129,7 +129,7 @@ print(output) 通过调试查看源代码,仍然查不到问题所在,于是去 [Github](https://github.com/amoffat/sh/issues/393) 上搜了下,原来在 2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 `sh.ssh` 的人并不多,于是我又“追问”了下,期望能得到回复。 -![](http://image.python-online.cn/20200228085749.png) +![](http://image.iswbm.com/20200228085749.png) 以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 @@ -158,7 +158,7 @@ print(my_server.ifconfig()) 最重要的一点是, `sh` 这个模块,仅支持 Linxu/OSX ,在 Windows 你得使用它的兄弟库 - `pbs` ,然后我又去 pypi 看了一眼 [pbs](https://pypi.org/project/pbs/),已经 “年久失修”,没人维护了。 -![](http://image.python-online.cn/20200228093627.png) +![](http://image.iswbm.com/20200228093627.png) 至此,我离 “卒”,就差最后一根稻草了。 @@ -323,7 +323,7 @@ trans.close() 坏事就是:你需要做很多复杂的准备,你可 google 解决,但是我建议你直接放弃,坑太深了。 -![](http://image.python-online.cn/20200228111654.png) +![](http://image.iswbm.com/20200228111654.png) ### 注意事项 diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst index c2ae53d..d56a1c8 100644 --- a/source/c01/c01_35.rst +++ b/source/c01/c01_35.rst @@ -398,8 +398,8 @@ Windows,这里就有一件好事,一件坏事了,。 |image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20200228085749.png -.. |image2| image:: http://image.python-online.cn/20200228093627.png -.. |image3| image:: http://image.python-online.cn/20200228111654.png +.. |image1| image:: http://image.iswbm.com/20200228085749.png +.. |image2| image:: http://image.iswbm.com/20200228093627.png +.. |image3| image:: http://image.iswbm.com/20200228111654.png .. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md index e6f136b..bb15125 100644 --- a/source/c01/c01_36.md +++ b/source/c01/c01_36.md @@ -6,7 +6,7 @@ 就像这样子,天呐,密集恐惧症要犯了都 -![](http://image.python-online.cn/image-20200307210853246.png) +![](http://image.iswbm.com/image-20200307210853246.png) 上面这段 traceback @@ -41,11 +41,11 @@ $ python3 -m pip install pretty-errors 随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 -![](http://image.python-online.cn/image-20200307212823345.png) +![](http://image.iswbm.com/image-20200307212823345.png) 而使用了 pretty_errors 后,报错信息被美化成这样了。 -![](http://image.python-online.cn/image-20200307213534278.png) +![](http://image.iswbm.com/image-20200307213534278.png) 是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? @@ -71,21 +71,21 @@ pretty_errors 和其他库不太一样,在一定程度上(如果你使用全 $ python3 -m pretty_errors ``` -![](http://image.python-online.cn/image-20200307214742135.png) +![](http://image.iswbm.com/image-20200307214742135.png) 配置完成后,你再运行任何脚本,traceback 都会自动美化了。 不仅是在我的 iTerm 终端下 -![](http://image.python-online.cn/image-20200307213534278.png) +![](http://image.iswbm.com/image-20200307213534278.png) 在 PyCharm 中也会 -![](http://image.python-online.cn/image-20200307215530270.png) +![](http://image.iswbm.com/image-20200307215530270.png) 唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 `文件路径` 直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 下面自定义配置的方案解决这个问题(下面会讲到,参数是:`display_link`)。 -![](http://image.python-online.cn/image-20200307215834623.png) +![](http://image.iswbm.com/image-20200307215834623.png) 因此,有些情况下,你并不想设置 `pretty_errors` 全局可用。 @@ -93,7 +93,7 @@ $ python3 -m pretty_errors 只需要再次输出 `python -m pretty_errors`,输出入 `C` 即可清除。 -![](http://image.python-online.cn/image-20200307214600749.png) +![](http://image.iswbm.com/image-20200307214600749.png) @@ -163,7 +163,7 @@ if __name__ == "__main__": 在你像上面这样使用 `pretty_errrs.configure` 进行配置时,抛出的的异常信息就变成这样了。 -![](http://image.python-online.cn/image-20200308121949011.png) +![](http://image.iswbm.com/image-20200308121949011.png) @@ -195,7 +195,7 @@ if __name__ == "__main__": 其中,`_BACKGROUND` 用于设置背景色,举个例子如下。 -![](http://image.python-online.cn/image-20200308125431779.png) +![](http://image.iswbm.com/image-20200308125431779.png) ### 5.2 设置显示内容 diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst index bbed8dd..8222d98 100644 --- a/source/c01/c01_36.rst +++ b/source/c01/c01_36.rst @@ -271,16 +271,16 @@ PEP8 |image12| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/image-20200307210853246.png -.. |image2| image:: http://image.python-online.cn/image-20200307212823345.png -.. |image3| image:: http://image.python-online.cn/image-20200307213534278.png +.. |image1| image:: http://image.iswbm.com/image-20200307210853246.png +.. |image2| image:: http://image.iswbm.com/image-20200307212823345.png +.. |image3| image:: http://image.iswbm.com/image-20200307213534278.png .. |image4| image:: https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67 -.. |image5| image:: http://image.python-online.cn/image-20200307214742135.png -.. |image6| image:: http://image.python-online.cn/image-20200307213534278.png -.. |image7| image:: http://image.python-online.cn/image-20200307215530270.png -.. |image8| image:: http://image.python-online.cn/image-20200307215834623.png -.. |image9| image:: http://image.python-online.cn/image-20200307214600749.png -.. |image10| image:: http://image.python-online.cn/image-20200308121949011.png -.. |image11| image:: http://image.python-online.cn/image-20200308125431779.png +.. |image5| image:: http://image.iswbm.com/image-20200307214742135.png +.. |image6| image:: http://image.iswbm.com/image-20200307213534278.png +.. |image7| image:: http://image.iswbm.com/image-20200307215530270.png +.. |image8| image:: http://image.iswbm.com/image-20200307215834623.png +.. |image9| image:: http://image.iswbm.com/image-20200307214600749.png +.. |image10| image:: http://image.iswbm.com/image-20200308121949011.png +.. |image11| image:: http://image.iswbm.com/image-20200308125431779.png .. |image12| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_38.md b/source/c01/c01_38.md index 4303123..39af2dd 100644 --- a/source/c01/c01_38.md +++ b/source/c01/c01_38.md @@ -20,7 +20,7 @@ 稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python` -![](http://image.python-online.cn/20200331184021.png) +![](http://image.iswbm.com/20200331184021.png) 而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。 @@ -28,19 +28,19 @@ 不加的话,你每次执行这个脚本时,都得这样: `python xx.py` , -![](http://image.python-online.cn/20200331185034.png) +![](http://image.iswbm.com/20200331185034.png) 有没有一种方式?可以省去每次都加 `python` 呢? 当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 -![](http://image.python-online.cn/20200331184755.png) +![](http://image.iswbm.com/20200331184755.png) 明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ? 当我执行 `env python` 时,自动进入了 python console 的模式。 -![](http://image.python-online.cn/20200331185741.png) +![](http://image.iswbm.com/20200331185741.png) 这是为什么?和 直接执行 python 好像没什么区别呀 @@ -50,7 +50,7 @@ 具体演示过程,你可以看下面。 -![](http://image.python-online.cn/20200331190224.png) +![](http://image.iswbm.com/20200331190224.png) 那么对于这两者,我们应该使用哪个呢? diff --git a/source/c01/c01_38.rst b/source/c01/c01_38.rst index 94438e8..1b2c08d 100644 --- a/source/c01/c01_38.rst +++ b/source/c01/c01_38.rst @@ -67,10 +67,10 @@ python 解释器都是 ``/usr/bin/python`` 。 |image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20200331184021.png -.. |image2| image:: http://image.python-online.cn/20200331185034.png -.. |image3| image:: http://image.python-online.cn/20200331184755.png -.. |image4| image:: http://image.python-online.cn/20200331185741.png -.. |image5| image:: http://image.python-online.cn/20200331190224.png +.. |image1| image:: http://image.iswbm.com/20200331184021.png +.. |image2| image:: http://image.iswbm.com/20200331185034.png +.. |image3| image:: http://image.iswbm.com/20200331184755.png +.. |image4| image:: http://image.iswbm.com/20200331185741.png +.. |image5| image:: http://image.iswbm.com/20200331190224.png .. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_01.md b/source/c02/c02_01.md index 521c3cd..e4f8ef4 100644 --- a/source/c02/c02_01.md +++ b/source/c02/c02_01.md @@ -50,7 +50,7 @@ 首先,我的实验环境配置如下 -![](http://image.python-online.cn/20190112205155.png) +![](http://image.iswbm.com/20190112205155.png) **注意** 以下代码,若要理解,对小白有如下知识点要求: @@ -219,7 +219,7 @@ multi_process(io_simulation, type="模拟IO密集型") 将结果汇总一下,制成表格。 -![](http://image.python-online.cn/20190112204930.png) +![](http://image.iswbm.com/20190112204930.png) 我们来分析下这个表格。 diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index eeb14f5..c2021ea 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -256,7 +256,7 @@ .. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png .. |image2| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg .. |image3| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg -.. |image4| image:: http://image.python-online.cn/20190112205155.png -.. |image5| image:: http://image.python-online.cn/20190112204930.png +.. |image4| image:: http://image.iswbm.com/20190112205155.png +.. |image5| image:: http://image.iswbm.com/20190112204930.png .. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_07.md b/source/c02/c02_07.md index a743540..e50d9ad 100644 --- a/source/c02/c02_07.md +++ b/source/c02/c02_07.md @@ -89,7 +89,7 @@ False 2. 判断是否可迭代,不能仅看是否有`__iter__` 来草率决定,因为只实现了`__getitem__` 方法的也有可能是可迭代的。因为当没有`__iter__`时, Python 解释器会去找`__getitem__`,尝试按顺序(从索引0开始)获取元素,不抛异常,即是可迭代。 3. 所以,最好的判断方法应该是通过 `for循环`或者` iter()` 去真实运行。 -![](http://image.python-online.cn/20190527123516.png) +![](http://image.iswbm.com/20190527123516.png) diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index ec51a27..05a81da 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -395,7 +395,7 @@ |image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190527123516.png +.. |image1| image:: http://image.iswbm.com/20190527123516.png .. |image2| image:: https://i.loli.net/2018/05/19/5affd48c34e3f.png .. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_01.md b/source/c03/c03_01.md index b97b33f..a915b0d 100644 --- a/source/c03/c03_01.md +++ b/source/c03/c03_01.md @@ -18,7 +18,7 @@ 当时带着这两个问题,我就开始系统的学习装饰器的所有内容。这些一直整理在自己的博客中,今天对其进行了大量的补充和勘误,发表在这里分享给大家。希望对刚入门以及进阶的朋友可以提供一些参考。 -![](http://image.python-online.cn/20190811100737.png) +![](http://image.iswbm.com/20190811100737.png) ## 3.1.1 Hello,装饰器 @@ -352,7 +352,7 @@ class User: 其实例化的过程,你可以参考我这里的调试过程,加以理解。 -![](http://image.python-online.cn/20190512113917.png) +![](http://image.iswbm.com/20190512113917.png) ## 3.1.9 wraps 装饰器有啥用? 在 functools 标准库中有提供一个 wraps 装饰器,你应该也经常见过,那他有啥用呢? diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 305fde9..8ee4115 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -778,7 +778,7 @@ property |image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190811100737.png -.. |image2| image:: http://image.python-online.cn/20190512113917.png +.. |image1| image:: http://image.iswbm.com/20190811100737.png +.. |image2| image:: http://image.iswbm.com/20190512113917.png .. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_05.md b/source/c03/c03_05.md index da3aacc..cc2bb7d 100644 --- a/source/c03/c03_05.md +++ b/source/c03/c03_05.md @@ -124,11 +124,11 @@ self.__storage__['1'][k2] = v2 如果要用图来表示,最开始的Local对象就是一个空盒子 -![](http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX) +![](http://image.iswbm.com/Fuhww2CZdUv4mGqx-N0YqAuXUWlX) 当有不同的线程往里写数据时,Local 对象为每个线程分配了一个 micro-box。 -![](http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup) +![](http://image.iswbm.com/FgI6y-_Ka-S20VCjyufsCIczKjup) local 是需要被 `localmanager` 管理的,在请求结束后,会调用 `localmanager.cleanup()` 函数,其实是调用 `local.__release_local__ ` 进行数据清理。是如何做到的呢,看下面这段代码。 @@ -197,7 +197,7 @@ class Local(object): 同样用一张图来表示 -![](http://image.python-online.cn/FimULzWaeZWS2KJx_EQLAK_yRZ4A) +![](http://image.iswbm.com/FimULzWaeZWS2KJx_EQLAK_yRZ4A) 栈结构的特性,无非就是后进先出。这里就不说了,这里的重点是线程隔离的特性如何体现,还是以上面的例子,稍微做了下修改。 diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index a9de523..264c1ab 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -503,8 +503,8 @@ app 的上下文信息是否已经 push 进去了,如果没有的话,就会 |image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/Fuhww2CZdUv4mGqx-N0YqAuXUWlX -.. |image2| image:: http://image.python-online.cn/FgI6y-_Ka-S20VCjyufsCIczKjup -.. |image3| image:: http://image.python-online.cn/FimULzWaeZWS2KJx_EQLAK_yRZ4A +.. |image1| image:: http://image.iswbm.com/Fuhww2CZdUv4mGqx-N0YqAuXUWlX +.. |image2| image:: http://image.iswbm.com/FgI6y-_Ka-S20VCjyufsCIczKjup +.. |image3| image:: http://image.iswbm.com/FimULzWaeZWS2KJx_EQLAK_yRZ4A .. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c03/c03_06.md b/source/c03/c03_06.md index 728a25b..93696a6 100644 --- a/source/c03/c03_06.md +++ b/source/c03/c03_06.md @@ -20,7 +20,7 @@ 一个HTTP请求的过程可以分为两个阶段,第一阶段是从客户端到WSGI Server,第二阶段是从WSGI Server 到WSGI Application -![](http://image.python-online.cn/20190607131728.png) +![](http://image.iswbm.com/20190607131728.png) 今天主要是讲第二阶段,主要内容有以下几点: @@ -90,7 +90,7 @@ web 服务器 和 web 框架,分工不同,职责不同(web 服务器专注 我根据其架构组成的不同将这个过程的实现分为两种: -![](http://image.python-online.cn/20190607191954.png) +![](http://image.iswbm.com/20190607191954.png) **1、两级结构** 在这种结构里,uWSGI作为服务器,它用到了HTTP协议以及wsgi协议,flask应用作为application,实现了wsgi协议。当有客户端发来请求,uWSGI接受请求,调用flask app得到相应,之后相应给客户端。 @@ -125,7 +125,7 @@ server.serve_forever() 使用 lsof 命令可以查到确实开启了这个端口 -![](http://image.python-online.cn/20190607134310.png) +![](http://image.iswbm.com/20190607134310.png) 以上使用 wsgiref 写了一个demo,让你对wsgi有个初步的了解。其由于只适合在学习测试使用,在生产环境中应该另寻他道。 @@ -143,23 +143,23 @@ server.serve_forever() 从 Service 文件可以得知 nova-api 的入口是 `nova.cmd.api:main()` -![](http://image.python-online.cn/20190607140817.png) +![](http://image.iswbm.com/20190607140817.png) -![](http://image.python-online.cn/20190607140922.png) +![](http://image.iswbm.com/20190607140922.png) 打开`nova.cmd.api:main()` ,一起看看是 OpenStack Nova 的代码。 在如下的黄框里,可以看到在这里使用了service.WSGIService 启动了一个 server,就是我们所说的的 wsgi server -![](http://image.python-online.cn/20190530212557.png) +![](http://image.iswbm.com/20190530212557.png) 那这里的 WSGI Server 是依靠什么实现的呢?让我们继续深入源代码。 -![](http://image.python-online.cn/20190530212753.png) +![](http://image.iswbm.com/20190530212753.png) wsgi.py 可以看到这里使用了 eventlet 这个网络并发框架,它先开启了一个绿色线程池,从配置里可以看到这个服务器可以接收的请求并发量是 1000 。 -![](http://image.python-online.cn/20190530212956.png) +![](http://image.iswbm.com/20190530212956.png) 可是我们还没有看到 WSGI Server 的身影,上面使用eventlet 开启了线程池,那线程池里的每个线程应该都是一个服务器吧?它是如何接收请求的? @@ -194,7 +194,7 @@ wsgi_kwargs = { self._server = utils.spawn(**wsgi_kwargs) ``` -![](http://image.python-online.cn/20190530214820.png) +![](http://image.iswbm.com/20190530214820.png) 就这样,nova 开启了一个可以接受1000个绿色协程并发的 WSGI Server。 @@ -229,7 +229,7 @@ PasteDeploy 到底是做什么的呢? 具体可以,看下nova的实现。 -![](http://image.python-online.cn/20190530221101.png) +![](http://image.iswbm.com/20190530221101.png) 通过打印的 DEBUG 内容得知 config_url 和 app name 的值 @@ -389,7 +389,7 @@ server.serve_forever() applications 是URLMap 对象。 -![](http://image.python-online.cn/20190607154119.png) +![](http://image.iswbm.com/20190607154119.png) 完善并整合第二步和第三步的内容,写成一个 Python 文件(wsgi_server.py)。内容如下 @@ -449,7 +449,7 @@ if __name__ == "__main__": 一切都准备好后,在终端执行 ` python wsgi_server.py`来启动 web server -![](http://image.python-online.cn/20190607155432.png) +![](http://image.iswbm.com/20190607155432.png) 如果像上图一样一切正常,那么打开浏览器 @@ -464,13 +464,13 @@ if __name__ == "__main__": 经过了 PasteDeploy 的路由调度,我们找到了 `nova.api.openstack.compute:APIRouterV21.factory` 这个 application 的入口,看代码知道它其实返回了 APIRouterV21 类的一个实例。 -![](http://image.python-online.cn/20190602173212.png) +![](http://image.iswbm.com/20190602173212.png) WSGI规定 application 必须是一个 callable 的对象,函数、方法、类、实例,若是一个类实例,就要求这个实例所属的类实现 `__call__` 的方法。 APIRouterV21 本身没有实现 `__call__` ,但它的父类 Router实现了 `__call__` -![](http://image.python-online.cn/20190602173956.png) +![](http://image.iswbm.com/20190602173956.png) 我们知道,application 必须遵丛 WSGI 的规范 @@ -479,7 +479,7 @@ APIRouterV21 本身没有实现 `__call__` ,但它的父类 Router实现了 `_ 但从 Router 的 `__call__` 代码来看,它并没有遵从这个规范,它不接收这两个参数,也不返回 response,而只是返回另一个 callable 的对象,就这样我们的视线被一次又一次的转移,但没有关系,这些`__call__`都是外衣,只要扒掉这些外衣,我们就能看到核心app。 -而负责扒掉这层外衣的,就是其头上的装饰器 `@webob.dec.wsgify` ,wsgify 是一个类,其 `__call__` 源码实现如下:![](http://image.python-online.cn/20190605203016.png) +而负责扒掉这层外衣的,就是其头上的装饰器 `@webob.dec.wsgify` ,wsgify 是一个类,其 `__call__` 源码实现如下:![](http://image.iswbm.com/20190605203016.png) 可以看出,wsgify 在这里,会将 req 这个原始请求(dict对象)封装成 Request 对象(就是规范1里提到的 environ)。然后会一层一层地往里地执行被wsgify装饰的函数(self._route), 得到最内部的核心application。 @@ -497,7 +497,7 @@ APIRouterV21 本身没有实现 `__call__` ,但它的父类 Router实现了 `_ 在文章最开始处,我们给大家画了一张图。 -![](http://image.python-online.cn/20190607131728.png) +![](http://image.iswbm.com/20190607131728.png) 这张图把一个 HTTP 请求粗略简单地划分为两个过程。但事实上,整个过程远比这个过程要复杂得多。 @@ -513,7 +513,7 @@ APIRouterV21 本身没有实现 `__call__` ,但它的父类 Router实现了 `_ 在routes模块里有个中间件,叫 `routes.middleware.RoutesMiddleware` ,它将接受到的 url,自动调用 `map.match()`方法,对 url 进行路由匹配,并将匹配的结果存入request请求的环境变量`['wsgiorg.routing_args']`,最后会调用`self._dispatch`(dispatch返回真正的application)返回response,最后会将这个response返回给 WSGI Server。 -![](http://image.python-online.cn/20190608211233.png) +![](http://image.iswbm.com/20190608211233.png) 这个中间件的原理,看起来是挺简单的。并没有很复杂的逻辑。 @@ -521,7 +521,7 @@ APIRouterV21 本身没有实现 `__call__` ,但它的父类 Router实现了 `_ `self._dispatch` (也就上图中的self.app)函数里,我们看到了 app,controller 这几个很重要的字眼,其是否是我苦苦追寻的 application 对象呢? -![](http://image.python-online.cn/20190531211542.png) +![](http://image.iswbm.com/20190531211542.png) 要搞明白这个问题,只要看清 match 到是什么东西? @@ -545,7 +545,7 @@ APIRouterV21 本身没有实现 `__call__` ,但它的父类 Router实现了 `_ 从 Nova 代码中看出每个Resource 对应一个 Controller 对象,因为 Controller 对象本身就是对一种资源的操作集合。 -![](http://image.python-online.cn/20190531225529.png) +![](http://image.iswbm.com/20190531225529.png) 通过日志的打印,可以发现 nova 管理的 Resource 对象有多么的多而杂 @@ -723,7 +723,7 @@ nova show 1c250b15-a346-43c5-9b41-20767ec7c94b 图示地方,会从 environ 里获取中看到获取 action 的具体代码 -![](http://image.python-online.cn/20190602220246.png) +![](http://image.iswbm.com/20190602220246.png) 我将这边的 action_args打印出来 @@ -736,7 +736,7 @@ nova show 1c250b15-a346-43c5-9b41-20767ec7c94b 在 `__call__` 的最后,会 调用 `_process_stack` 方法 -![](http://image.python-online.cn/20190602220511.png) +![](http://image.iswbm.com/20190602220511.png) 在图标处,get_method 会根据 action(函数名) 取得处理函数对象。 @@ -747,11 +747,11 @@ meth : 快速命令 双击底部自定义快速命令。 -![](http://image.python-online.cn/20190511162524.png) +![](http://image.iswbm.com/20190511162524.png) **使用收藏栏** 点击最左侧按按钮添加收藏。 -![](http://image.python-online.cn/20190511162607.png) +![](http://image.iswbm.com/20190511162607.png) **快捷设置** @@ -41,14 +41,14 @@ ② 双击分隔符 ③ 选中即复制 -![](http://image.python-online.cn/20190511162716.png) +![](http://image.iswbm.com/20190511162716.png) **设置Meta键** 文件 -> 属性 -> 键盘 一定要打钩,这是后面诸多快捷使用的前提。 -![](http://image.python-online.cn/20190511162730.png) +![](http://image.iswbm.com/20190511162730.png) ## 移动光标 ``` diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index 1e9fadc..1dbdb5e 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -171,10 +171,10 @@ |image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511162815.png -.. |image2| image:: http://image.python-online.cn/20190511162524.png -.. |image3| image:: http://image.python-online.cn/20190511162607.png -.. |image4| image:: http://image.python-online.cn/20190511162716.png -.. |image5| image:: http://image.python-online.cn/20190511162730.png +.. |image1| image:: http://image.iswbm.com/20190511162815.png +.. |image2| image:: http://image.iswbm.com/20190511162524.png +.. |image3| image:: http://image.iswbm.com/20190511162607.png +.. |image4| image:: http://image.iswbm.com/20190511162716.png +.. |image5| image:: http://image.iswbm.com/20190511162730.png .. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_03.md b/source/c04/c04_03.md index bb9a984..051fac8 100644 --- a/source/c04/c04_03.md +++ b/source/c04/c04_03.md @@ -2,8 +2,6 @@ ![](http://image.iswbm.com/20200602135014.png) -《网络是怎样连接的》 豆瓣评分:9.1 - 10个优秀的程序员里,有9个人都有写博客的习惯。这是非常好的习惯,值得每个程序员,投入时间和精力去坚持做下去。 写博客的平台有很多,CSDN,博客园,51CTO,还有人会使用Hexo+GitHub,WordPress,比较会折腾的人还会自己使用Java,Python搭建,我就干过这样的事,不过每年还要支付域名和服务器,比较麻烦而且浪费钱。 @@ -30,18 +28,18 @@ 以我的博客(`python.iswbm.com`)为例,先给大家展示一下。 这是首页。显示了你所有的文章索引。 -![](http://image.python-online.cn/20190511160523.png) +![](http://image.iswbm.com/20190511160523.png) 这是我的导航栏。是不是结构很清晰,很方便索引。 -![](http://image.python-online.cn/20190511161056.png) +![](http://image.iswbm.com/20190511161056.png) 点击文章后,还可以很方便查看标题,跳转。 -![](http://image.python-online.cn/20190511161130.png) +![](http://image.iswbm.com/20190511161130.png) 体验下搜索功能,速度很快。 -![](http://image.python-online.cn/20190511161147.png) +![](http://image.iswbm.com/20190511161147.png) 看完这些你是不是也很想拥有这样一个博客呢? @@ -210,10 +208,10 @@ The HTML pages are in build\html. 执行完了后,你可以发现原先的build,不再是空文件夹了。 我们点进去 build\html\ 目录,使用浏览器打开index.html文件。 -![](http://image.python-online.cn/20190511161212.png) +![](http://image.iswbm.com/20190511161212.png) 真棒,已经完成了一半了。点击 我们刚写的 暴富指南。 -![](http://image.python-online.cn/20190511161240.png) +![](http://image.iswbm.com/20190511161240.png) @@ -244,20 +242,20 @@ build/ 你需要先去 Read the Docs 注册下帐号。 关联一下GitHub -![](http://image.python-online.cn/20190511161255.png) +![](http://image.iswbm.com/20190511161255.png) -![](http://image.python-online.cn/20190511161311.png) +![](http://image.iswbm.com/20190511161311.png) 导入代码库。填好与你对应的信息。 -![](http://image.python-online.cn/20190511161334.png) +![](http://image.iswbm.com/20190511161334.png) -![](http://image.python-online.cn/20190511161414.png) +![](http://image.iswbm.com/20190511161414.png) 构建网页后。右下方,你可以看见你的在线地址。 -![](http://image.python-online.cn/20190511161426.png) +![](http://image.iswbm.com/20190511161426.png) 这里要提醒一下的是,Sphinx的文档格式,默认是 rst 格式,如果你习惯了使用Markdown来写文章,可以使用 Pandoc 这个神器转换一下。 @@ -299,7 +297,7 @@ pandoc -V mainfont="SimSun" -f markdown -t rst hello.md -o hello.rst 游客无法阅读博客的全部内容,因为会有一半的内容会被隐藏,就像这样。 -![](http://image.python-online.cn/20191015230346.png) +![](http://image.iswbm.com/20191015230346.png) 如想要浏览完整内容,需要点击 “阅读全文” 进行解锁: @@ -309,7 +307,7 @@ pandoc -V mainfont="SimSun" -f markdown -t rst hello.md -o hello.rst 3. 在如下文本框中输入验证码。 这样就可以永久解锁本博客的所有干货文章。 -![](http://image.python-online.cn/20191015230502.png) +![](http://image.iswbm.com/20191015230502.png) 思路有了,那么如何实现呢? @@ -385,7 +383,7 @@ html_js_files = [ - 改 CPython 2.x 为 CPython 3.x - 指定我们的本地生成的 requirements.txt(使用 pip freeze >requirements.txt) -![](http://image.python-online.cn/20191015234452.png) +![](http://image.iswbm.com/20191015234452.png) 同时你如果之前是看过我写的教程,使用过我的中文检索插件,那你要注意了。 @@ -415,11 +413,11 @@ html_js_files = [ 然后在网站列表新增一个你的网站,我的信息如下: -![](http://image.python-online.cn/20191016205336.png) +![](http://image.iswbm.com/20191016205336.png) 填写完成,就可以获取一段属于你的网站的专属 js 代码(下面第一步)。 -![](http://image.python-online.cn/20191016205653.png) +![](http://image.iswbm.com/20191016205653.png) 第二步内容,是教你如何安装这段 js 代码。 @@ -448,11 +446,11 @@ html_js_files = [ 构建完成后,去执行第三步,代码安装检查。像我下面这样,就是安装完成了。 -![](http://image.python-online.cn/20191015225652.png) +![](http://image.iswbm.com/20191015225652.png) 这个插件安装完成后,如果你的网站有流量,可以过个一个小时,点击一下查看报告查看你网站的详细访问数据。 -![](http://image.python-online.cn/20191016211012.png) +![](http://image.iswbm.com/20191016211012.png) 数据真的非常全面,你可以知道,访客都是从哪里访问(直接访问,Google等),每篇文章的点击量(你就知道哪篇是爆款?),每天有多少老访问客,多少新访客等等,更多维度的数据你可以自己去体验一下。 diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 890f43a..6d03640 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -3,8 +3,6 @@ |image0| -《网络是怎样连接的》 豆瓣评分:9.1 - 10个优秀的程序员里,有9个人都有写博客的习惯。这是非常好的习惯,值得每个程序员,投入时间和精力去坚持做下去。 写博客的平台有很多,CSDN,博客园,51CTO,还有人会使用Hexo+GitHub,WordPress,比较会折腾的人还会自己使用Java,Python搭建,我就干过这样的事,不过每年还要支付域名和服务器,比较麻烦而且浪费钱。 @@ -509,24 +507,24 @@ Python 3.x ,所以这里的代码也要对应修改。 |image20| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511160523.png -.. |image2| image:: http://image.python-online.cn/20190511161056.png -.. |image3| image:: http://image.python-online.cn/20190511161130.png -.. |image4| image:: http://image.python-online.cn/20190511161147.png -.. |image5| image:: http://image.python-online.cn/20190511161212.png -.. |image6| image:: http://image.python-online.cn/20190511161240.png -.. |image7| image:: http://image.python-online.cn/20190511161255.png -.. |image8| image:: http://image.python-online.cn/20190511161311.png -.. |image9| image:: http://image.python-online.cn/20190511161334.png -.. |image10| image:: http://image.python-online.cn/20190511161414.png -.. |image11| image:: http://image.python-online.cn/20190511161426.png -.. |image12| image:: http://image.python-online.cn/20191015230346.png -.. |image13| image:: http://image.python-online.cn/20191015230502.png -.. |image14| image:: http://image.python-online.cn/20191015234452.png -.. |image15| image:: http://image.python-online.cn/20191016205336.png -.. |image16| image:: http://image.python-online.cn/20191016205653.png -.. |image17| image:: http://image.python-online.cn/20191015225652.png -.. |image18| image:: http://image.python-online.cn/20191016211012.png +.. |image1| image:: http://image.iswbm.com/20190511160523.png +.. |image2| image:: http://image.iswbm.com/20190511161056.png +.. |image3| image:: http://image.iswbm.com/20190511161130.png +.. |image4| image:: http://image.iswbm.com/20190511161147.png +.. |image5| image:: http://image.iswbm.com/20190511161212.png +.. |image6| image:: http://image.iswbm.com/20190511161240.png +.. |image7| image:: http://image.iswbm.com/20190511161255.png +.. |image8| image:: http://image.iswbm.com/20190511161311.png +.. |image9| image:: http://image.iswbm.com/20190511161334.png +.. |image10| image:: http://image.iswbm.com/20190511161414.png +.. |image11| image:: http://image.iswbm.com/20190511161426.png +.. |image12| image:: http://image.iswbm.com/20191015230346.png +.. |image13| image:: http://image.iswbm.com/20191015230502.png +.. |image14| image:: http://image.iswbm.com/20191015234452.png +.. |image15| image:: http://image.iswbm.com/20191016205336.png +.. |image16| image:: http://image.iswbm.com/20191016205653.png +.. |image17| image:: http://image.iswbm.com/20191015225652.png +.. |image18| image:: http://image.iswbm.com/20191016211012.png .. |image19| image:: http://image.iswbm.com/image-20200704154427375.png .. |image20| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_04.md b/source/c04/c04_04.md index 7ecbff2..d6c3a95 100644 --- a/source/c04/c04_04.md +++ b/source/c04/c04_04.md @@ -29,7 +29,7 @@ Anaconda 号称是适用于企业级大数据分析的Python工具。它包含 ## 4.4.1 下载安装 到官网下载 [Anaconda](https://www.anaconda.com/download/) -![](http://image.python-online.cn/20190511163102.png) +![](http://image.iswbm.com/20190511163102.png) 我这里选择下载 Py3.6,下载下来是 exe 文件(600多M)。安装即可。 安装完成后,还会提示你是否安装 VS Code,自行选择。不安装直接 Skip 就好。 @@ -37,20 +37,20 @@ Anaconda 号称是适用于企业级大数据分析的Python工具。它包含 ## 4.4.2 启动程序 点击打开 Anaconda 。会出现这个界面。第一眼,我看到了很多工具,包含 NoteBook,qtconsole,VS Ccode等。 -![](http://image.python-online.cn/20190511163123.png) +![](http://image.iswbm.com/20190511163123.png) 这里就介绍一下,我最经常使用的 NoteBook 就好了。其他的大家自行尝试。 选择 NoteBook,点击 Launch,会启动浏览器打开一个 web 页面。 -![](http://image.python-online.cn/20190511163137.png) +![](http://image.iswbm.com/20190511163137.png) 点击右上角的new,就可以进入 Python3 的交互界面。 -![](http://image.python-online.cn/20190511163145.png) +![](http://image.iswbm.com/20190511163145.png) 来感受一下,如何使用这个工具。 每一行的命令,都在一个可编辑的输入框。即使你的命令输入有误,你也不用从头写代码,直接编辑后重新执行即可。 -![](http://image.python-online.cn/20190511163200.png) +![](http://image.iswbm.com/20190511163200.png) 还有一点,命令的执行是可间断的,某个命令执行错误,不会导致整个程序中断,这将很方便我们调试代码,只要改完代码,再重新执行该行代码即可,而不用重新执行全部代码。 @@ -153,18 +153,18 @@ Anaconda 号称是适用于企业级大数据分析的Python工具。它包含 其实以上快捷键,在非编辑模式下,按 h 就会出现快捷键帮助菜单。 -![](http://image.python-online.cn/20190511163245.png) -![](http://image.python-online.cn/20190511163253.png) +![](http://image.iswbm.com/20190511163245.png) +![](http://image.iswbm.com/20190511163253.png) ## 4.4.4 导出笔记文件 NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学习笔记。 它提供多种常用的文件格式,md,rst,pdf等。如果你希望再次编辑,可以保存为ipynb,这是Jupyter的文件格式,可以再次打开进行编辑。 -![](http://image.python-online.cn/20190511163304.png) +![](http://image.iswbm.com/20190511163304.png) 以前我学习 Pandas 的时候,也曾经使用它做过笔记,输出的是PDF文件,可以按目录导航,相当方便。 -![](http://image.python-online.cn/20190511163311.png) +![](http://image.iswbm.com/20190511163311.png) 好了,大概就是这些内容。 diff --git a/source/c04/c04_04.rst b/source/c04/c04_04.rst index bbe77dd..a694a1d 100755 --- a/source/c04/c04_04.rst +++ b/source/c04/c04_04.rst @@ -242,14 +242,14 @@ NoteBook 既然支持 Markdown ,你已经也能想到它可以用来记录学 |image10| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511163102.png -.. |image2| image:: http://image.python-online.cn/20190511163123.png -.. |image3| image:: http://image.python-online.cn/20190511163137.png -.. |image4| image:: http://image.python-online.cn/20190511163145.png -.. |image5| image:: http://image.python-online.cn/20190511163200.png -.. |image6| image:: http://image.python-online.cn/20190511163245.png -.. |image7| image:: http://image.python-online.cn/20190511163253.png -.. |image8| image:: http://image.python-online.cn/20190511163304.png -.. |image9| image:: http://image.python-online.cn/20190511163311.png +.. |image1| image:: http://image.iswbm.com/20190511163102.png +.. |image2| image:: http://image.iswbm.com/20190511163123.png +.. |image3| image:: http://image.iswbm.com/20190511163137.png +.. |image4| image:: http://image.iswbm.com/20190511163145.png +.. |image5| image:: http://image.iswbm.com/20190511163200.png +.. |image6| image:: http://image.iswbm.com/20190511163245.png +.. |image7| image:: http://image.iswbm.com/20190511163253.png +.. |image8| image:: http://image.iswbm.com/20190511163304.png +.. |image9| image:: http://image.iswbm.com/20190511163311.png .. |image10| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_05.md b/source/c04/c04_05.md index ccfc64b..405113f 100644 --- a/source/c04/c04_05.md +++ b/source/c04/c04_05.md @@ -38,15 +38,15 @@ Linux 的发行版本有很多,这里选择 Ubuntu 并没有什么特殊的缘 这里上一张我安装硬盘的记念图。 -![](http://image.python-online.cn/20190511163441.png) +![](http://image.iswbm.com/20190511163441.png) 你没有看错,我的是台式机。说起这台电脑,还是前年我自己搜罗配置单,自己从各大电商平台,有京东,淘宝,还有天猫买了所有的配件,然后自己一件一件组装起来的。还是挺有感情的,虽然也是渣渣配置,但是这个过程还是很愉快的。在有了每一次装体验后,后面我还给别人装过好几台,如果说高三是我知识储备最高的时期,那前年就是我动手能力最强的时期的。组装过将近十台的电脑。这都是题外话了。 第一次装好硬盘后呢,要进入原先的 Windows 系统,检查一下,我们安装的硬盘有没有装成功。由于我装了工具,开始键和你们的可能不一样(不过真的是Win10),你也可以右击桌面 「我的电脑」,再点击「管理」。 -![](http://image.python-online.cn/20190511163457.png) +![](http://image.iswbm.com/20190511163457.png) 如果硬盘安装成功,这里会有一块未分配的盘,如图中的 硬盘0。 第一次使用,需要初始化硬盘,记得选 GPT。 -![](http://image.python-online.cn/20190511163510.png) +![](http://image.iswbm.com/20190511163510.png) 这里可以不用急着分区(在后面安装系统时会让你分的),如果你要提前分好(使用DiskGenius),也没有关系。 @@ -62,11 +62,11 @@ iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 首先,打开软件,点击 文件 - 打开,选择你所下载的 iso 文件。出现如下界面 -![](http://image.python-online.cn/20190511163520.png) +![](http://image.iswbm.com/20190511163520.png) 再点击 启动 - 写入硬盘映像 - 写入 -![](http://image.python-online.cn/20190511163531.png) +![](http://image.iswbm.com/20190511163531.png) 如一切顺利,U盘就制作完成,一般 99.99% 都不会在这地方出错。 @@ -76,7 +76,7 @@ iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 但在这里,必须关闭 win10的快速启动功能。方法如下:取消勾选「启用快速启动」,点击保存修改。然后就可以正常关机了。 -![](http://image.python-online.cn/20190511163542.png) +![](http://image.iswbm.com/20190511163542.png) ## 4.5.5 安装Ubuntu @@ -86,7 +86,7 @@ iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 查找完后,你可以对电脑开机了,按住你的快捷键,选择启动方式。由于我们要从U盘启动,所以这里选择 这里一定要注意,选择最后一个,自定义选项。 -![](http://image.python-online.cn/20190511163550.png) +![](http://image.iswbm.com/20190511163550.png) 终于到了分区的这一步了,这是最关键的一步。对于Linux不熟悉的人,到这里可能会懵逼。不用怕,这里给你提供一个最简单的分区配置。具体为什么这么分,我想你并不关心吧? @@ -98,35 +98,35 @@ iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 对,就是这么简单粗暴。如果你想更加精细一点,你还可以自定义 /home,/usr等。 分区完成后,一定要注意如下这二个红框,选择安装启动引导器的设备为 我们刚刚设置的efi分区。检查无误后,就可以点击「现在安装」。 -![](http://image.python-online.cn/20190511163559.png) +![](http://image.iswbm.com/20190511163559.png) 接下来就是 选择时区 - 配置键盘。 -![](http://image.python-online.cn/20190511163612.png) +![](http://image.iswbm.com/20190511163612.png) -![](http://image.python-online.cn/20190511163633.png) +![](http://image.iswbm.com/20190511163633.png) 到了这里,你应该可以长舒第一口气了。成功了一半了。 -![](http://image.python-online.cn/20190511163700.png) +![](http://image.iswbm.com/20190511163700.png) 如果你和我一样使用 SSD ,应该不出5分钟系统就可以安装完毕。弹出如下界面。点击现在重启。 -![](http://image.python-online.cn/20190511163711.png) +![](http://image.iswbm.com/20190511163711.png) 重启的过程,记住还是一样按住你的快捷键,我这里仍然是 F11,看到没有,已经有一个叫 ubuntu的启动设备。就它了,选择进入系统。接下来,就是选择要以哪种模式进入ubuntu,你根据需要去选吧。 -![](http://image.python-online.cn/20190511163722.png) +![](http://image.iswbm.com/20190511163722.png) ## 4.5.6 效果展示 由于默认的Ubuntu主题也是丑得可以,经过一个晚上的美化,它变成如下这般帅气逼人。 -![](http://image.python-online.cn/20190511163731.png) +![](http://image.iswbm.com/20190511163731.png) -![](http://image.python-online.cn/20190511163750.png) +![](http://image.iswbm.com/20190511163750.png) -![](http://image.python-online.cn/20190511163757.png) +![](http://image.iswbm.com/20190511163757.png) -![](http://image.python-online.cn/20190511163805.png) +![](http://image.iswbm.com/20190511163805.png) ---- ![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c04/c04_05.rst b/source/c04/c04_05.rst index e4093c4..51104c5 100755 --- a/source/c04/c04_05.rst +++ b/source/c04/c04_05.rst @@ -162,22 +162,22 @@ ubuntu的启动设备。就它了,选择进入系统。接下来,就是选 |image18| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511163441.png -.. |image2| image:: http://image.python-online.cn/20190511163457.png -.. |image3| image:: http://image.python-online.cn/20190511163510.png -.. |image4| image:: http://image.python-online.cn/20190511163520.png -.. |image5| image:: http://image.python-online.cn/20190511163531.png -.. |image6| image:: http://image.python-online.cn/20190511163542.png -.. |image7| image:: http://image.python-online.cn/20190511163550.png -.. |image8| image:: http://image.python-online.cn/20190511163559.png -.. |image9| image:: http://image.python-online.cn/20190511163612.png -.. |image10| image:: http://image.python-online.cn/20190511163633.png -.. |image11| image:: http://image.python-online.cn/20190511163700.png -.. |image12| image:: http://image.python-online.cn/20190511163711.png -.. |image13| image:: http://image.python-online.cn/20190511163722.png -.. |image14| image:: http://image.python-online.cn/20190511163731.png -.. |image15| image:: http://image.python-online.cn/20190511163750.png -.. |image16| image:: http://image.python-online.cn/20190511163757.png -.. |image17| image:: http://image.python-online.cn/20190511163805.png +.. |image1| image:: http://image.iswbm.com/20190511163441.png +.. |image2| image:: http://image.iswbm.com/20190511163457.png +.. |image3| image:: http://image.iswbm.com/20190511163510.png +.. |image4| image:: http://image.iswbm.com/20190511163520.png +.. |image5| image:: http://image.iswbm.com/20190511163531.png +.. |image6| image:: http://image.iswbm.com/20190511163542.png +.. |image7| image:: http://image.iswbm.com/20190511163550.png +.. |image8| image:: http://image.iswbm.com/20190511163559.png +.. |image9| image:: http://image.iswbm.com/20190511163612.png +.. |image10| image:: http://image.iswbm.com/20190511163633.png +.. |image11| image:: http://image.iswbm.com/20190511163700.png +.. |image12| image:: http://image.iswbm.com/20190511163711.png +.. |image13| image:: http://image.iswbm.com/20190511163722.png +.. |image14| image:: http://image.iswbm.com/20190511163731.png +.. |image15| image:: http://image.iswbm.com/20190511163750.png +.. |image16| image:: http://image.iswbm.com/20190511163757.png +.. |image17| image:: http://image.iswbm.com/20190511163805.png .. |image18| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_06.md b/source/c04/c04_06.md index 9c8085c..feaf6e4 100644 --- a/source/c04/c04_06.md +++ b/source/c04/c04_06.md @@ -192,7 +192,7 @@ $ git log --graph --all --decorate # 以可视化图的形式展示 使用 diff 进行查看 -![](http://image.python-online.cn/20191217150942.png) +![](http://image.iswbm.com/20191217150942.png) 查看两个 commit 之间的修改 @@ -365,11 +365,11 @@ $ git reset --hard HEAD^ 很简单,先使用 `git reflog` 找到你的 reset 的 commit id -![](http://image.python-online.cn/20191231165152.png) +![](http://image.iswbm.com/20191231165152.png) 然后再次使用 `git reset` 指定 commit id 回到一次修改add前的状态 -![](http://image.python-online.cn/20191231165239.png) +![](http://image.iswbm.com/20191231165239.png) ### 3.6 回退远程提交 @@ -529,7 +529,7 @@ ssh-keygen -t rsa -C "wongbingming@163.com" 生成的仅钥 `/c/Users/wangbm/.ssh/id_rsa.pub`,需要在Github上添加。 在github上添加ssh keys方法如下: -![](http://image.python-online.cn/20190511163855.png) +![](http://image.iswbm.com/20190511163855.png) 添加完后,可以使用这个命令测试一下。 ``` @@ -567,7 +567,7 @@ Git 的使用全都是命令行的,由于没有那么直观,所以很容易 需要注意的是,使用它需要你进行一步操作。在 7.2 章节,我在机器上生成了一对私钥和公钥,在这里需要配置一下才能正常访问和提交我们的Github仓库。 -![](http://image.python-online.cn/20190430235625.png) +![](http://image.iswbm.com/20190430235625.png) diff --git a/source/c04/c04_06.rst b/source/c04/c04_06.rst index 7bc9b85..a3e83b5 100755 --- a/source/c04/c04_06.rst +++ b/source/c04/c04_06.rst @@ -628,12 +628,12 @@ Desktop真心觉得不好用)。 |image8| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191217150942.png -.. |image2| image:: http://image.python-online.cn/20191231165152.png -.. |image3| image:: http://image.python-online.cn/20191231165239.png +.. |image1| image:: http://image.iswbm.com/20191217150942.png +.. |image2| image:: http://image.iswbm.com/20191231165152.png +.. |image3| image:: http://image.iswbm.com/20191231165239.png .. |image4| image:: https://i.loli.net/2018/04/15/5ad2c06e8893d.png -.. |image5| image:: http://image.python-online.cn/20190511163855.png +.. |image5| image:: http://image.iswbm.com/20190511163855.png .. |image6| image:: https://i.loli.net/2018/04/15/5ad2c2a9813b9.png -.. |image7| image:: http://image.python-online.cn/20190430235625.png +.. |image7| image:: http://image.iswbm.com/20190430235625.png .. |image8| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_07.md b/source/c04/c04_07.md index ef3fb8a..4d509af 100644 --- a/source/c04/c04_07.md +++ b/source/c04/c04_07.md @@ -14,14 +14,14 @@ - 下载安装git(官网下载安装) - 下载安装hexo。方法:打开终端 运行`npm install -g hexo`(要翻墙) -![](http://image.python-online.cn/image-20200321163152876.png) +![](http://image.iswbm.com/image-20200321163152876.png) ### 1.2 初始化项目 - 新创建一个目录,比如叫做 `iswbm-blog` - 使用终端进入该文件夹内,执行`hexo init` (初始化项目) -![](http://image.python-online.cn/image-20200321163746032.png) +![](http://image.iswbm.com/image-20200321163746032.png) - 运行你的博客项目(用于调试),查看生成的文章是否符合自己的预期。 @@ -40,7 +40,7 @@ $ hexo s # 运行本地web服务器 - 在Github上创建名字为XXX.github.io的项目,XXX为自己的github用户名。然后 clone 到你的本地电脑上。 - ![](http://image.python-online.cn/image-20200321165634287.png) + ![](http://image.iswbm.com/image-20200321165634287.png) - 打开该文件夹内的`_config.yml`配置文件,将其中的type设置为git,其他配置如下 @@ -68,7 +68,7 @@ $ hexo d 在第一次部署后,请到 github 后台,补充你的 网站信息,否则无法访问。 -![](http://image.python-online.cn/image-20200321171008622.png) +![](http://image.iswbm.com/image-20200321171008622.png) 一切完成之后,你就可以通过上面的网址来访问我的博客了。 @@ -79,10 +79,10 @@ $ hexo d 我们可以自己去阿里云购买一个域名(我的是 iswbm.com),然后将其 CNAME 到你的博客地址上。 - 去阿里云 域名云解析 - ![](http://image.python-online.cn/image-20200321171939919.png) + ![](http://image.iswbm.com/image-20200321171939919.png) - 然后到对应的GitHub仓库,绑定我的个性域名 - ![](http://image.python-online.cn/image-20200321171821683.png) + ![](http://image.iswbm.com/image-20200321171821683.png) - 然后再在你本地的项目里的 `source` 目录下新建 `CNAME`文件,内容就是我的域名 @@ -128,7 +128,7 @@ Do not just seek happiness for yourself. Seek happiness for all. Through kindnes {% endblockquote %} ``` 效果如下 -![](http://image.python-online.cn/17-9-10/85269241.jpg) +![](http://image.iswbm.com/17-9-10/85269241.jpg) ### 3.2 一键生成md头格式 @@ -149,7 +149,7 @@ password: 然后使用 `hexo new`就可以一键生成新文章的头格式了,不用手动去搬运或者书写。相当方便。 -![image-20200321201555321](http://image.python-online.cn/image-20200321201555321.png) +![image-20200321201555321](http://image.iswbm.com/image-20200321201555321.png) ## 四、美化博客 @@ -209,7 +209,7 @@ social: GitHub: https://github.com/iswbm || github E-Mail: mailto:wongbingming@163.com || envelope-o 微博: http://weibo.com/942663728 || weibo - WeChat: http://image.python-online.cn/17-9-9/58657236.jpg || weixin + WeChat: http://image.iswbm.com/17-9-9/58657236.jpg || weixin 知乎: https://www.zhihu.com/people/wongbingming/activities || chain-broken CnBlog: http://www.cnblogs.com/wongbingming/ || file-text-o @@ -350,7 +350,7 @@ post_copyright: ``` 接着打开`themes/next/layout/_macro/post.swig`文件,添加如下下代码,注意位置 -![](http://image.python-online.cn/17-9-9/63041495.jpg) +![](http://image.iswbm.com/17-9-9/63041495.jpg) 代码如下: ``` @@ -670,7 +670,7 @@ permalink: Database-MySQL-Basic_usage 在web界面新建分支,命名为`hexo` 在web界面设置 `hexo` 为默认分支,因为我们只会在这个分支上进行操作。 -![](http://image.python-online.cn/image-20200321193444320.png) +![](http://image.iswbm.com/image-20200321193444320.png) ### 6.2 本地PC操作 diff --git a/source/c04/c04_07.rst b/source/c04/c04_07.rst index a38ede7..0179476 100755 --- a/source/c04/c04_07.rst +++ b/source/c04/c04_07.rst @@ -168,7 +168,7 @@ 然后使用 ``hexo new``\ 就可以一键生成新文章的头格式了,不用手动去搬运或者书写。相当方便。 -.. figure:: http://image.python-online.cn/image-20200321201555321.png +.. figure:: http://image.iswbm.com/image-20200321201555321.png :alt: image-20200321201555321 image-20200321201555321 @@ -245,7 +245,7 @@ GitHub: https://github.com/iswbm || github E-Mail: mailto:wongbingming@163.com || envelope-o 微博: http://weibo.com/942663728 || weibo - WeChat: http://image.python-online.cn/17-9-9/58657236.jpg || weixin + WeChat: http://image.iswbm.com/17-9-9/58657236.jpg || weixin 知乎: https://www.zhihu.com/people/wongbingming/activities || chain-broken CnBlog: http://www.cnblogs.com/wongbingming/ || file-text-o @@ -894,16 +894,16 @@ bash窗口,不然会提示找不到npm命令) |image12| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/image-20200321163152876.png -.. |image2| image:: http://image.python-online.cn/image-20200321163746032.png -.. |image3| image:: http://image.python-online.cn/image-20200321165634287.png -.. |image4| image:: http://image.python-online.cn/image-20200321171008622.png -.. |image5| image:: http://image.python-online.cn/image-20200321171939919.png -.. |image6| image:: http://image.python-online.cn/image-20200321171821683.png -.. |image7| image:: http://image.python-online.cn/17-9-10/85269241.jpg -.. |image8| image:: http://image.python-online.cn/17-9-9/63041495.jpg +.. |image1| image:: http://image.iswbm.com/image-20200321163152876.png +.. |image2| image:: http://image.iswbm.com/image-20200321163746032.png +.. |image3| image:: http://image.iswbm.com/image-20200321165634287.png +.. |image4| image:: http://image.iswbm.com/image-20200321171008622.png +.. |image5| image:: http://image.iswbm.com/image-20200321171939919.png +.. |image6| image:: http://image.iswbm.com/image-20200321171821683.png +.. |image7| image:: http://image.iswbm.com/17-9-10/85269241.jpg +.. |image8| image:: http://image.iswbm.com/17-9-9/63041495.jpg .. |image9| image:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200321210014963.png -.. |image10| image:: http://image.python-online.cn/image-20200321193444320.png +.. |image10| image:: http://image.iswbm.com/image-20200321193444320.png .. |image11| image:: https://i.loli.net/2018/04/15/5ad31888232e9.png .. |image12| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_11.md b/source/c04/c04_11.md index 9ec0689..6a476e4 100644 --- a/source/c04/c04_11.md +++ b/source/c04/c04_11.md @@ -23,13 +23,13 @@ 首先,要在Pycharm中新建一个空的项目,后面我们拉服务器上的项目代码就会放置在这个项目目录下。我这边的名字是 NOVA,你可以自己定义。 -![](http://image.python-online.cn/20190113104817.png) +![](http://image.iswbm.com/20190113104817.png) ### 4.11.2 配置连接服务器 Tools -> Deployment -> configuration -![](http://image.python-online.cn/20190113105512.png) +![](http://image.iswbm.com/20190113105512.png) 添加一个`Server` @@ -37,7 +37,7 @@ Tools -> Deployment -> configuration - Type:设定为SFTP -![](http://image.python-online.cn/20190113105858.png) +![](http://image.iswbm.com/20190113105858.png) 点击`OK`后,进入如下界面,你可以按我的备注,填写信息: @@ -50,36 +50,36 @@ Tools -> Deployment -> configuration 这里请注意,要确保你的电脑可以ssh连接到你的服务器,不管是密钥登陆还是密码登陆,如果开启了白名单限制要先解除。 -![](http://image.python-online.cn/20190113105931.png) +![](http://image.iswbm.com/20190113105931.png) 填写完成后,切换到`Mappings`选项卡,在箭头位置,填写`\` -![](http://image.python-online.cn/20190113110928.png) +![](http://image.iswbm.com/20190113110928.png) 以上服务器信息配置,全部正确填写完成后,点击`OK` 接下来,我们要连接远程服务器了。 Tools -> Deployment -> Browse Remote Host -![](http://image.python-online.cn/20190113111042.png) +![](http://image.iswbm.com/20190113111042.png) ### 4.11.3 下载项目代码 如果之前填写的服务器登陆信息准确无误的话,现在就可以看到远程的项目代码。 -![](http://image.python-online.cn/20190113111151.png) +![](http://image.iswbm.com/20190113111151.png) 选择下载远程代码要本地。 -![](http://image.python-online.cn/20190113111217.png) +![](http://image.iswbm.com/20190113111217.png) 下载完成提示。 -![](http://image.python-online.cn/20190113111248.png) +![](http://image.iswbm.com/20190113111248.png) 现在的IDE界面应该是这样子的。 -![](http://image.python-online.cn/20190113111307.png) +![](http://image.iswbm.com/20190113111307.png) ### 4.11.4 下载远程解释器 @@ -90,11 +90,11 @@ Tools -> Deployment -> Browse Remote Host 进入 File -> Settings 按图示,添加远程解释器。 -![](http://image.python-online.cn/20190113111747.png) +![](http://image.iswbm.com/20190113111747.png) 填写远程服务器信息,跟之前的一样,不再赘述。 -![](http://image.python-online.cn/20190113111828.png) +![](http://image.iswbm.com/20190113111828.png) 点击`OK`后,会自动下载远程解释器。如果你的项目比较大,这个时间可能会比较久,请耐心等待。 @@ -128,17 +128,17 @@ WantedBy=multi-user.target 看到那个`ExecStart`没有?那个就是我们程序的入口。 我们只要将其拷贝至我们的Pycharm中,并向远程同步该文件。 -![](http://image.python-online.cn/20190113112004.png) +![](http://image.iswbm.com/20190113112004.png) ### 4.11.6 调试前设置 开启代码自动同步,这样,我们对代码的修改Pycharm都能识别,并且为我们提交到远程服务器。 -![](http://image.python-online.cn/20190113112055.png) +![](http://image.iswbm.com/20190113112055.png) 开启 `Gevent compatible`,如果不开启,在调试过程中,很可能出现无法调试,或者无法追踪/查看变量等问题。 -![](http://image.python-online.cn/20190113113211.png) +![](http://image.iswbm.com/20190113113211.png) ### 4.11.7 开始调试代码 @@ -146,11 +146,11 @@ WantedBy=multi-user.target 如果你的程序入口,需要引入参数,这是经常有的事,可以的这里配置。 -![](http://image.python-online.cn/20190113112456.png) +![](http://image.iswbm.com/20190113112456.png) 配置完点击保存即可。 -![](http://image.python-online.cn/20190113112649.png) +![](http://image.iswbm.com/20190113112649.png) ### 4.11.8 友情提醒 diff --git a/source/c04/c04_11.rst b/source/c04/c04_11.rst index 61adafe..fa378e4 100644 --- a/source/c04/c04_11.rst +++ b/source/c04/c04_11.rst @@ -178,22 +178,22 @@ Host |image18| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190113104817.png -.. |image2| image:: http://image.python-online.cn/20190113105512.png -.. |image3| image:: http://image.python-online.cn/20190113105858.png -.. |image4| image:: http://image.python-online.cn/20190113105931.png -.. |image5| image:: http://image.python-online.cn/20190113110928.png -.. |image6| image:: http://image.python-online.cn/20190113111042.png -.. |image7| image:: http://image.python-online.cn/20190113111151.png -.. |image8| image:: http://image.python-online.cn/20190113111217.png -.. |image9| image:: http://image.python-online.cn/20190113111248.png -.. |image10| image:: http://image.python-online.cn/20190113111307.png -.. |image11| image:: http://image.python-online.cn/20190113111747.png -.. |image12| image:: http://image.python-online.cn/20190113111828.png -.. |image13| image:: http://image.python-online.cn/20190113112004.png -.. |image14| image:: http://image.python-online.cn/20190113112055.png -.. |image15| image:: http://image.python-online.cn/20190113113211.png -.. |image16| image:: http://image.python-online.cn/20190113112456.png -.. |image17| image:: http://image.python-online.cn/20190113112649.png +.. |image1| image:: http://image.iswbm.com/20190113104817.png +.. |image2| image:: http://image.iswbm.com/20190113105512.png +.. |image3| image:: http://image.iswbm.com/20190113105858.png +.. |image4| image:: http://image.iswbm.com/20190113105931.png +.. |image5| image:: http://image.iswbm.com/20190113110928.png +.. |image6| image:: http://image.iswbm.com/20190113111042.png +.. |image7| image:: http://image.iswbm.com/20190113111151.png +.. |image8| image:: http://image.iswbm.com/20190113111217.png +.. |image9| image:: http://image.iswbm.com/20190113111248.png +.. |image10| image:: http://image.iswbm.com/20190113111307.png +.. |image11| image:: http://image.iswbm.com/20190113111747.png +.. |image12| image:: http://image.iswbm.com/20190113111828.png +.. |image13| image:: http://image.iswbm.com/20190113112004.png +.. |image14| image:: http://image.iswbm.com/20190113112055.png +.. |image15| image:: http://image.iswbm.com/20190113113211.png +.. |image16| image:: http://image.iswbm.com/20190113112456.png +.. |image17| image:: http://image.iswbm.com/20190113112649.png .. |image18| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_12.md b/source/c04/c04_12.md index a3838d8..64d312f 100644 --- a/source/c04/c04_12.md +++ b/source/c04/c04_12.md @@ -4,7 +4,7 @@ 上一篇文章,讲的是 Pycharm 的远程调试,若你还没学会,可以点击这里进行查看。 -[不能不会的远程调试技巧](http://python-online.cn/zh_CN/latest/c04/c04_11.html) +[不能不会的远程调试技巧](http://iswbm.com/zh_CN/latest/c04/c04_11.html) Pycharm 的图形化界面虽然好用,但是在某些场景中,是无法使用的。而 Python 本身已经给我们提供了一个调试神器 -- pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文章来帮助你轻松的理解它。 @@ -52,7 +52,7 @@ ptyhon -m pdb pdb_demo.py 使用这个方式进入调试模式,会在脚本的第一行开始单步调试。 -![](http://image.python-online.cn/20190118000111.png) +![](http://image.iswbm.com/20190118000111.png) 对于单文件的脚本并没有什么问题,如果是一个大型的项目,项目里有很多的文件,使用这种方式只能大大降低我们的效率。 @@ -67,9 +67,9 @@ pdb.set_trace() 然后执行时,也不需要再指定`-m pdb`了,直接`python pdb_demo.py ` ,就会直接在这个地方暂停。 -![](http://image.python-online.cn/20190118000234.png) +![](http://image.iswbm.com/20190118000234.png) -![](http://image.python-online.cn/20190118000557.png) +![](http://image.iswbm.com/20190118000557.png) @@ -129,7 +129,7 @@ pdb.set_trace() 其实你大可不必死记这些命令,忘记的时候,只要敲入`help`并回车,就可以看所有的指令了。 -![](http://image.python-online.cn/20190118083809.png) +![](http://image.iswbm.com/20190118083809.png) @@ -137,7 +137,7 @@ pdb.set_trace() 这里就几个最常用的指定,来演示一遍。 -![](http://image.python-online.cn/20190118005507.png) +![](http://image.iswbm.com/20190118005507.png) diff --git a/source/c04/c04_12.rst b/source/c04/c04_12.rst index d9e56e9..1cd10d2 100644 --- a/source/c04/c04_12.rst +++ b/source/c04/c04_12.rst @@ -6,7 +6,7 @@ 上一篇文章,讲的是 Pycharm 的远程调试,若你还没学会,可以点击这里进行查看。 -`不能不会的远程调试技巧 `__ +`不能不会的远程调试技巧 `__ Pycharm 的图形化界面虽然好用,但是在某些场景中,是无法使用的。而 Python 本身已经给我们提供了一个调试神器 – @@ -179,10 +179,10 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 |image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190118000111.png -.. |image2| image:: http://image.python-online.cn/20190118000234.png -.. |image3| image:: http://image.python-online.cn/20190118000557.png -.. |image4| image:: http://image.python-online.cn/20190118083809.png -.. |image5| image:: http://image.python-online.cn/20190118005507.png +.. |image1| image:: http://image.iswbm.com/20190118000111.png +.. |image2| image:: http://image.iswbm.com/20190118000234.png +.. |image3| image:: http://image.iswbm.com/20190118000557.png +.. |image4| image:: http://image.iswbm.com/20190118083809.png +.. |image5| image:: http://image.iswbm.com/20190118005507.png .. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_14.md b/source/c04/c04_14.md index 259e3e2..f595119 100644 --- a/source/c04/c04_14.md +++ b/source/c04/c04_14.md @@ -26,11 +26,11 @@ pip install [--user] pipenv 如果你的电脑是 windows 的。 -![](http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn) +![](http://image.iswbm.com/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn) 需要将如标示路径,加入到 环境变量 PATH 中。 -![](http://image.python-online.cn/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX) +![](http://image.iswbm.com/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX) 然后需要重启一下,CMD 终端才能够刷新环境变量。 @@ -57,11 +57,11 @@ pipenv install --python 2 这边以安装 python2 版本的虚拟环境为例说明。 -![](http://image.python-online.cn/20190612211330.png) +![](http://image.iswbm.com/20190612211330.png) 如果你原项目使用的是 requirements.txt 这个管理包的方式,这时候执行 `pipenv --tow` 创建一个虚拟环境后,会找到 requirements.txt ,并根据这里面的依赖包生成 Pipfile文件。 -![](http://image.python-online.cn/20190612213015.png) +![](http://image.iswbm.com/20190612213015.png) ## 4.14.3 查询虚拟环境 @@ -78,7 +78,7 @@ $ pipenv --py 演示如下: -![](http://image.python-online.cn/20190612213950.png) +![](http://image.iswbm.com/20190612213950.png) ## 4.14.4 操作虚拟环境 @@ -96,7 +96,7 @@ $ pipenv --rm 执行 `pipenv shell` 就可以进入这个虚拟环境,在头部会有虚拟环境的标识名称。有这个标识,说明已经进入虚拟环境。 -![](http://image.python-online.cn/20190612211925.png) +![](http://image.iswbm.com/20190612211925.png) ```python # 在当前虚拟环境中运行 @@ -149,11 +149,11 @@ $ pipenv check 打印该虚拟环境下所有包的依赖关系图 -![](http://image.python-online.cn/20190614000336.png) +![](http://image.iswbm.com/20190614000336.png) 有的python第三方包旧版本会有安全漏洞,使用 pipenv check 可以检查安全漏洞。 -![](http://image.python-online.cn/20190612215924.png) +![](http://image.iswbm.com/20190612215924.png) .env`文件,用来存放一些环境变量。 diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 7fa8d79..cff6e02 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -173,13 +173,13 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 |image9| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn -.. |image2| image:: http://image.python-online.cn/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX -.. |image3| image:: http://image.python-online.cn/20190612211330.png -.. |image4| image:: http://image.python-online.cn/20190612213015.png -.. |image5| image:: http://image.python-online.cn/20190612213950.png -.. |image6| image:: http://image.python-online.cn/20190612211925.png -.. |image7| image:: http://image.python-online.cn/20190614000336.png -.. |image8| image:: http://image.python-online.cn/20190612215924.png +.. |image1| image:: http://image.iswbm.com/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn +.. |image2| image:: http://image.iswbm.com/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX +.. |image3| image:: http://image.iswbm.com/20190612211330.png +.. |image4| image:: http://image.iswbm.com/20190612213015.png +.. |image5| image:: http://image.iswbm.com/20190612213950.png +.. |image6| image:: http://image.iswbm.com/20190612211925.png +.. |image7| image:: http://image.iswbm.com/20190614000336.png +.. |image8| image:: http://image.iswbm.com/20190612215924.png .. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_15.md b/source/c04/c04_15.md index 6b53a7b..02718b4 100644 --- a/source/c04/c04_15.md +++ b/source/c04/c04_15.md @@ -29,19 +29,19 @@ Working directory: $ProjectFileDir$ Output filters: $FILE_PATH$\:$LINE$\:$COLUMN$\:.* ``` -![](http://image.python-online.cn/20190323164120.png) +![](http://image.iswbm.com/20190323164120.png) 我随意写了一段不符合 pep8 规范的代码。 -![](http://image.python-online.cn/20190323211635.png) +![](http://image.iswbm.com/20190323211635.png) 点击右键,选择 `External Tools` -> `AutoPep8` -![](http://image.python-online.cn/20190323211301.png) +![](http://image.iswbm.com/20190323211301.png) 看一下效果,还是挺明显的。 -![](http://image.python-online.cn/20190324111603.png) +![](http://image.iswbm.com/20190324111603.png) 你可能会说,Pycharm 本身就自带这个功能了呀,快捷键 `Command`+`Option`+`L` ,就可以实现一键pep8了。你可以对比一下,Pycharm 自带的代码 pep8 化功能 并没有像这个`autopep8` 来得彻底。 我相信你最终的选择肯定是后者。 @@ -61,7 +61,7 @@ Output filters: $FILE_PATH$\:$LINE$\:$COLUMN$\:.* 此时你可以在你的项目目录里,点击右键,有个 `Local History` 的选项,再点击子选项 `Show History`,你可以看到这里有个记录板。如果你想恢复删除的文件,就在删除的记录项点击右键,选择 `Revert` 即可恢复。 -![](http://image.python-online.cn/20190323153643.png) +![](http://image.iswbm.com/20190323153643.png) @@ -73,7 +73,7 @@ Vi 可以满足你对文本操作的所有需求,比可视化界面更加效 安装方法如下,安装完后需要重启 Pycharm 生效。 -![](http://image.python-online.cn/20190323214545.png) +![](http://image.iswbm.com/20190323214545.png) @@ -81,17 +81,17 @@ Vi 可以满足你对文本操作的所有需求,比可视化界面更加效 Pycharm 提供的这个代码模板,可以说是相当实用的一个功能了。它可以在你新建一个文件时,按照你预设的模板给你生成一段内容,比如解释器路径,编码方法,作者详细信息等 -![](http://image.python-online.cn/20190323225704.png) +![](http://image.iswbm.com/20190323225704.png) 按照上图模板,生成的效果如下。 -![](http://image.python-online.cn/20190323225631.png) +![](http://image.iswbm.com/20190323225631.png) 除了新建文件时可以初始化文件,在开发编写代码时,也同样使用 Pycharm 中自带的实用的代码模板,提高你的编码效率。 当你在键盘中敲入 `Command` + `J` 时,就可以调出一个面板,从下图可以看出里面有许多预设的模板。 -![](http://image.python-online.cn/20190323232017.png) +![](http://image.iswbm.com/20190323232017.png) 如果我们想选择最后一个 main ,可以继续键入 main,然后就可以直接生成如下这段平时都要手动敲入的代码。 @@ -124,7 +124,7 @@ Ctrl + Shift + 1 添加“1”序号的标签 在你要打书签的位置,按下 `Command` + `F11` ,你可以给这个位置加个序号,可以是数字也可以是字母,假如在下面这个位置 加了 `1` 这个序号,下次你就可以使用 `Control` + `1` 直接跳转到这个位置。 -![](http://image.python-online.cn/20190324111429.png) +![](http://image.iswbm.com/20190324111429.png) 当然你也可以不加,不加的话就是匿名书签了。你可以使用 `Shift` + `F11` 展示所有的书签,再进行跳转。 @@ -140,25 +140,25 @@ Ctrl + Shift + 1 添加“1”序号的标签 假如我在调试如下几行简单的代码。在第 3 行处打了个断点。然后点击图示位置 `Show Python Prompt` 按钮。 -![](http://image.python-online.cn/Fi3N02x9OeOPatGdaReam_icn9G_) +![](http://image.iswbm.com/Fi3N02x9OeOPatGdaReam_icn9G_) 就进入了 `Python Shell` 的界面,这个Shell 环境和我们当前运行的程序环境是打通的,变量之间可以互相访问,这下你可以轻松地进行调试了。 -![](http://image.python-online.cn/Fj1W53Txj0iFs5eYhFYh_dHlPtIL) +![](http://image.iswbm.com/Fj1W53Txj0iFs5eYhFYh_dHlPtIL) 上面我们打了个断点,是为了方便说明这个效果。并不是说一定要打断点。如果不打断点,在脚本执行完成后,也仍然可以在这个界面查看并操作所有变量。 -![](http://image.python-online.cn/FlMsB7B1x6ET9mLOgydTWuTEXuOe) +![](http://image.iswbm.com/FlMsB7B1x6ET9mLOgydTWuTEXuOe) 现在我们已经可以满足我们的调试的需求,但是每次运行脚本,都要手动点击 `Show Python Prompt` ,有点麻烦。嗯?其实这个有地方可以设置默认打开的。这个开关还比较隐秘,一般人还真发现不了。 你需要点击图示位置 `Edit Configurations` 处。 -![](http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId) +![](http://image.iswbm.com/FmfL3r0iWx_srT_xMASBEp1ZaaId) 然后在这里打勾选中。 -![](http://image.python-online.cn/FiNCYpVlI93gk1zhOdQn4c0A8FMX) +![](http://image.iswbm.com/FiNCYpVlI93gk1zhOdQn4c0A8FMX) 设置上之后,之后你每次运行后脚本后,都会默认为你存储所有变量的值,并为你打开 console 命令行调试界面。 @@ -166,11 +166,11 @@ Ctrl + Shift + 1 添加“1”序号的标签 使用方法就是,在你打了断点后,在图示位置处,点击右键使用 `Evaluate Expression` -![](http://image.python-online.cn/FrAq1tVRM7Bz948wRqZFzU2PQnI0) +![](http://image.iswbm.com/FrAq1tVRM7Bz948wRqZFzU2PQnI0) 就弹出了一个 `Evaluate Expression` 窗口,这里 可以运行命令表达式,直接操作变量。 -![](http://image.python-online.cn/Fo2aEraqbj_2KqDt44EzJTVe8pEf) +![](http://image.iswbm.com/Fo2aEraqbj_2KqDt44EzJTVe8pEf) @@ -190,15 +190,15 @@ python main.py init --local 对于刚使用 Pycharm 的同学,可能并不知道 Pycharm 也是可以指定参数的。点击下图位置 -![](http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId) +![](http://image.iswbm.com/FmfL3r0iWx_srT_xMASBEp1ZaaId) 进入设置面板,在 `Script parameters` 中填入参数即可。 -![](http://image.python-online.cn/FujczKwTUPa8l5EEmS0eoh-zL1Nk) +![](http://image.iswbm.com/FujczKwTUPa8l5EEmS0eoh-zL1Nk) 同时在上图的底部,你可以看到,这里可以很方便的切换 解释器,比你跑到这边来要容易得多吧 -![](http://image.python-online.cn/Fq60WOdcRJopqV6MVoRcIuZclYKx) +![](http://image.iswbm.com/Fq60WOdcRJopqV6MVoRcIuZclYKx) @@ -208,17 +208,17 @@ python main.py init --local 我平时会看的框架是 OpenStack ,我不知道其他框架是怎样的,但在 OpenStack 里面带有大量(真的很多)的单元测试文件。这给我在使用 `Find in Path` 时带来了不小的困扰,你可以从下图的搜索结果中感受一下,搜索一个函数,test 文件里的结果比 正常文件要多很多。 -![](http://image.python-online.cn/FlXynbyxh8tTrCpc4tVLqycL7JQm) +![](http://image.iswbm.com/FlXynbyxh8tTrCpc4tVLqycL7JQm) 这些测试文件的搜索结果,对于我们看源代码不仅没有任何帮助的,更重要的是还干扰视线。于是我就研究了一下,从文件名入手,只要在 `File mask` 里填写 `!test*` 可以将这些test文件过滤掉。搜索结果一下子清晰很多。 -![](http://image.python-online.cn/FiD91PR1hUu0Ruc6cmZ7EGNM6Be_) +![](http://image.iswbm.com/FiD91PR1hUu0Ruc6cmZ7EGNM6Be_) ## 4.15.9 关闭烦人的灯泡提示 本来没有想写这个的,但是知乎上有一位朋友有这个需求,那我研究了下。 -![](http://image.python-online.cn/FhkX5Ko3LVZL_p7YfitDsTDxvHmL) +![](http://image.iswbm.com/FhkX5Ko3LVZL_p7YfitDsTDxvHmL) 先来说下这个灯泡提示是什么,有什么用? @@ -230,27 +230,27 @@ python main.py init --local 我研究了下,Pycharm (2018版本)里是有开关按钮的,将下图中的这个选项(`Show intention bulb`)取消勾选,就可以关闭这个功能。 -![](http://image.python-online.cn/FuSSVa-aMqkfCaf62sbUoX2PLaYM) +![](http://image.iswbm.com/FuSSVa-aMqkfCaf62sbUoX2PLaYM) ## 4.15.10 关闭碍眼的波浪线 下面我先给出了一小段代码示例,思考一下,为什么name,my_name 不会有波浪线,而 myname 和 wangbm 会有波浪线呢? -![](http://image.python-online.cn/FtFPI89AOKmPLNpNxf-jdkn1BDLW) +![](http://image.iswbm.com/FtFPI89AOKmPLNpNxf-jdkn1BDLW) Pycharm 本身会实时地对变量名进行检查,如果变量名不是一个已存在的英文单词,就会出现一条波浪线,当一个变量里有多个单词时,Python 推荐的写法是用下划线来分隔(其他语言可能会习惯使用`驼峰式命名法` ,但 Python 是使用下划线),所以在 Pycharm 看来 my_name 是规范的,而 myname 会被当成是一个单词对待,由于它在单词库里并没有它,所以 myname 是不规范的。 每个人的变量命名习惯不一样,如何你在项目里大量使用了 myname 这种风格的变量命名方法,像下面这样(随便找了一段 cloudinit 的代码),是让人挺不舒服的,总有一种代码有 bug 的错觉。 -![](http://image.python-online.cn/FiKyU6tjQauWXfaVfKLhwi3NkXBf) +![](http://image.iswbm.com/FiKyU6tjQauWXfaVfKLhwi3NkXBf) 那么如何关闭这个非语法级别的波浪线呢?很简单,它的开关就在你的右下角那个像 人头像 一样的按钮 -![](http://image.python-online.cn/FsAM-8HyzSrLWZJ_lg3ofw84_ibf) +![](http://image.iswbm.com/FsAM-8HyzSrLWZJ_lg3ofw84_ibf) 然后选择 `Syntax` 级别的即可。同样一段代码,效果如下,干净了很多。 -![](http://image.python-online.cn/FgJCtNYkjPfBaTbRxwb3Z6icHqkf) +![](http://image.iswbm.com/FgJCtNYkjPfBaTbRxwb3Z6icHqkf) ## 4.15.11 一键进行代码性能分析 @@ -289,11 +289,11 @@ fun5() 点击 Run -> Profile '程序' ,即可进行性能分析。 -![](http://image.python-online.cn/20190507222856.png) +![](http://image.iswbm.com/20190507222856.png) 运行完毕后,会自动跳出一个性能统计界面。 -![](http://image.python-online.cn/20190507222119.png) +![](http://image.iswbm.com/20190507222119.png) 性能统计界面由Name、Call Count、Time(ms)、Own Time(ms) ,4列组成一个表格,见下图。 @@ -304,7 +304,7 @@ fun5() 点击 Call Graph(调用关系图)界面直观展示了各函数直接的调用关系、运行时间和时间百分比,见下图。 -![](http://image.python-online.cn/20190507223313.png) +![](http://image.iswbm.com/20190507223313.png) 右上角的4个按钮表示放大、缩小、真实大小、合适大小; @@ -318,25 +318,25 @@ fun5() 按照如下提示点击 Git 仓库配置 -![](http://image.python-online.cn/20190507215525.png) +![](http://image.iswbm.com/20190507215525.png) 接着输入仓库地址 -![](http://image.python-online.cn/20190507220101.png) +![](http://image.iswbm.com/20190507220101.png) 点击 Test,测试连通性,会要求输入密码 -![](http://image.python-online.cn/20190419152120.png) +![](http://image.iswbm.com/20190419152120.png) 若一切顺利,则会看到如下界面 -![](http://image.python-online.cn/20190419152145.png) +![](http://image.iswbm.com/20190419152145.png) 测试连接成功后,点击 Clone 就可以克隆下来了。 对于以前使用 Git 命令来管理的,现在可以直接使用 PyCharm 的菜单栏来操作,这些功能已经可以满足大多数人的日常需求了,应该是够用了。 -![](http://image.python-online.cn/20190507220740.png) +![](http://image.iswbm.com/20190507220740.png) ## 4.15.13 Tab轻松转空格 @@ -346,7 +346,7 @@ fun5() 在图示位置打勾即可开启自动检测。 -![](http://image.python-online.cn/20190423162328.png) +![](http://image.iswbm.com/20190423162328.png) 上面是对一个旧的 Python 模块进行修改时,如何决定当前编辑的缩进方式。 @@ -354,7 +354,7 @@ fun5() 如下图,若在 `Use tab character` 打上勾,则你新建一个 Python 后,就会使用 TAB 进行缩进,反之,则使用四个空格进行缩进。 -![](http://image.python-online.cn/20190423163341.png) +![](http://image.iswbm.com/20190423163341.png) 5. @@ -362,7 +362,7 @@ fun5() PyCharm 有分两个版本,一个是社区版(免费功能有限),一个是专业版(有一些增强功能),详细差异你可以参考这个图,一般来说,社区版用作学习用途是没有问题的。 -![](http://image.python-online.cn/20190506150523.png) +![](http://image.iswbm.com/20190506150523.png) 如果需要使用专业版,网上也有一些注册服务器使用,非常方便,缺点是过一段时间,可能就会失效。这里有一种一劳永逸的方法,但可能仅对早期的 PyCharm 版本有效,可以实现永久激活(到 2099 / 2100年,一定意义上是永久了吧)。 @@ -384,21 +384,21 @@ PyCharm 有分两个版本,一个是社区版(免费功能有限),一个 将第一步下载的 jar 包放入这个目录,并打开如下两个以 `vmoptions` 后缀结尾的文件: -![](http://image.python-online.cn/20190506150010.png) +![](http://image.iswbm.com/20190506150010.png) 添加如下这一行(请根据你的实际安装目录自行调整) -![](http://image.python-online.cn/20190506150100.png) +![](http://image.iswbm.com/20190506150100.png) 若是 Mac OS 系统,请找到并进入你的 Pycharm 安装启动目录(以我的为例) 将第一步下载的 jar 包放入这个目录 -![](http://image.python-online.cn/20190507000850.png) +![](http://image.iswbm.com/20190507000850.png) 并打开如下一个以 `vmoptions` 后缀结尾的文件: -![](http://image.python-online.cn/20190507001025.png) +![](http://image.iswbm.com/20190507001025.png) @@ -418,11 +418,11 @@ BIG3CLIK6F-eyJsaWNlbnNlSWQiOiJCSUczQ0xJSzZGIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiY 若是 Windows 系统,重启 PyCharm 后,查看激活信息:Help -> About -![](http://image.python-online.cn/20190507001422.png) +![](http://image.iswbm.com/20190507001422.png) 如果是 Mac OS 系统,重启 PyCharm 后,查看激活信息:PyCharm -> About PyCharm -![](http://image.python-online.cn/20190507001350.png) +![](http://image.iswbm.com/20190507001350.png) 另外,以上仅做交流和个人学习使用,请勿商用,有能力的朋友还是希望多支持正版! @@ -436,19 +436,19 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 鼠标左键),可以实现函 在编写代码时,顺便写好函数的接口文档,是一个很好的编码习惯。它介绍了该函数的参数类型及说明,返回值类型及范例,写得好一点的还会写出 几个简单的 Example Usage 有助于理解使用。在这一点上,Flask 可以说做得相当好。这边随便截一个 Werkzeug 的例子。 -![](http://image.python-online.cn/20190507152911.png) +![](http://image.iswbm.com/20190507152911.png) 假如我们在使用这个类的时候,忘记了这个用法,可以按住 Ctrl + q(Mac暂时未找到对应快捷键),在当前页面就可以快速预览 LocalStack 的接口文档。 -![](http://image.python-online.cn/20190507152840.png) +![](http://image.iswbm.com/20190507152840.png) 同样的,如果你对这个类或者函数的代码逻辑感兴趣,也可以使用快速预览的方式在当前页面展示源代码。快捷键是:Ctrl + shift + i (Mac:Command + shift + i)。效果如下 -![](http://image.python-online.cn/20190507153847.png) +![](http://image.iswbm.com/20190507153847.png) 如果 PyCharm 检测到的同名函数有多个,可以点这里进行选择查看 -![](http://image.python-online.cn/20190507154027.png) +![](http://image.iswbm.com/20190507154027.png) 这两个快捷键比起使用 Ctrl + 鼠标左键 跳进源代码来说,更加方便,,就像微信小程序一样,用完即焚,不会新产生一个标签页,也不需要来回跳转页面。 @@ -456,7 +456,7 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 鼠标左键),可以实现函 前几天打开 PyCharm,发现在导航栏这里出现了很多波浪线,有过 PyCharm 使用经验的同学,就会知道,这是代码中出现了错误。 -![](http://image.python-online.cn/20190613154147.png) +![](http://image.iswbm.com/20190613154147.png) 顺着波浪线,我一层一层地展开目录树,终于找到了那个包含错误的文件。由于是手误,我也不知道我改动了哪一行,看了下这个文件,有将近8000行的代码,难道一行一行地去找? @@ -468,11 +468,11 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 鼠标左键),可以实现函 我在搜索框输入 Error,就找到了快速定位到错误位置的快捷键 `F2` 和 `Shift+F2` 可以快速的定位到错误行。 -![](http://image.python-online.cn/20190613154401.png) +![](http://image.iswbm.com/20190613154401.png) 使用快捷键 F2 查看了下原来是这里缩进有问题。 -![](http://image.python-online.cn/20190613160905.png) +![](http://image.iswbm.com/20190613160905.png) ## 4.15.17 快速查看最近的修改 @@ -482,7 +482,7 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 鼠标左键),可以实现函 太巧的是,今天我打开 PyCharm ,就给我推了这条 tip,(在Mac上)使用 option+shift+C 可以快速查看最近修改的内容(windows 上应该是alt+shift+c吧) -![](http://image.python-online.cn/20190614235120.png) +![](http://image.iswbm.com/20190614235120.png) ## 4.15.18 静态代码分析检查 @@ -500,11 +500,11 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 鼠标左键),可以实现函 你只需要像下面这样点击项目文件夹,然后右键,选择 `Inspect Code`,就可以开启静态检查。 -![](http://image.python-online.cn/20190616211359.png) +![](http://image.iswbm.com/20190616211359.png) 我对开源组件 nova 的静态检查发现,其有不规范的地方有数千处。 -![](http://image.python-online.cn/20190616214310.png) +![](http://image.iswbm.com/20190616214310.png) ## 4.15.19 全方位无死角精准定位 @@ -518,25 +518,25 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 鼠标左键),可以实现函 - 精准定位到文件:Windows(Ctrl+Shift+N),Mac(Command+ shift +N) -![](http://image.python-online.cn/20190616221620.png) +![](http://image.iswbm.com/20190616221620.png) - 精准定位到类:Windows(Ctrl+N),Mac(Command+N) -![](http://image.python-online.cn/20190616232746.png) +![](http://image.iswbm.com/20190616232746.png) - 精准定位到符号:类的所有成员(函数、变量等)都可以称之为符号,Windows(Ctrl+Alt+Shift+N),Mac(Option+Shift+Command+N) -![](http://image.python-online.cn/20190616233827.png) +![](http://image.iswbm.com/20190616233827.png) - 精准定位到文件结构:文件结构包括类、函数、变量,这说明上面定位到类和定位到符号的方法,你都可以用这个来代替。 Windows:Ctrl+F12,Mac:Command+F12,如果和我一样是Mac是带touchbar的,键盘上是没有F12的,那你应该先按住 Command + fn,这时 touchbar 上会出现 F12,再按F12即可。 -![](http://image.python-online.cn/20190616235007.png) +![](http://image.iswbm.com/20190616235007.png) - 精准定位到某行:Windows(Ctrl+G),Mac(Command+G),如下图定位到第510行第9个字符处。 -![](http://image.python-online.cn/20190616234038.png) +![](http://image.iswbm.com/20190616234038.png) ## 4.15.20 TODO 解救“中年痴呆” @@ -560,13 +560,13 @@ Ctrl + 鼠标左键 (Mac 上是:Command + 鼠标左键),可以实现函 使用方法跟注释差不多,只要固定要以 TODO 开头。然后,你要查看全局项目中的所有 TODO 事项的时候,可以使用快捷键调出 TODO 面板。如果你是 Mac, 快捷键 是Command + 6,而 Windows 是 Alt+6。 -![](http://image.python-online.cn/20190616231649.png) +![](http://image.iswbm.com/20190616231649.png) 另外,我还使用这个来记录下个版本要优化的代码逻辑,要添加的功能。 如果是比较紧急的 BUG,可以使用类似 TODO 的标记 — `FIXME` 来区分紧急程度。 -![](http://image.python-online.cn/20190616232527.png) +![](http://image.iswbm.com/20190616232527.png) ## 4.15.21 随处折叠,实现代码自由 @@ -574,7 +574,7 @@ PyCharm 里代码块的折叠功能,相当的显眼,在代码编辑框的左 如果你和我一样是个键盘党,你可以使用快捷(Mac:按住Command键,再按`+`或者`-` ,Windows:按住Ctrl键,再按`+`或者`-` )进行快速反折叠/折叠。 -![](http://image.python-online.cn/20190629183430.png) +![](http://image.iswbm.com/20190629183430.png) 代码块的折叠和反折叠,应该是一个代码编辑器的基本功能。在这一点上, PyCharm 做为一个 IDE,在这一点上势必要做得更出色,事实证明,它做到了。 @@ -602,7 +602,7 @@ PyCharm 里代码块的折叠功能,相当的显眼,在代码编辑框的左 比如下面这段代码,我只想改myfun 里的的test_name,而对于全局下的同名变量是不应该修改的。如果你全局替换,就会有误伤。 -![](http://image.python-online.cn/20190629211910.png) +![](http://image.iswbm.com/20190629211910.png) 这时候,我们如何做呢? @@ -634,13 +634,13 @@ PyCharm 里代码块的折叠功能,相当的显眼,在代码编辑框的左 这样播放宏显得有点繁琐,个人建议你为这个宏定义一个快捷键,这样会更方便播放宏。 -![](http://image.python-online.cn/20190629221224.png) +![](http://image.iswbm.com/20190629221224.png) 设置快捷键时,注意不要和已有的快捷键冲突。 设置好后,查看 Macro,发现PyCharm已经将这个快捷键绑定给这个宏。 -![](http://image.python-online.cn/20190629221547.png) +![](http://image.iswbm.com/20190629221547.png) 之后你就可以使用这个快捷键删除一个函数(其实这只是删除一个代码块,但是这里只讨论设置方法)。 @@ -650,7 +650,7 @@ PyCharm 打开一个文件,就占用一个标签面。 你有没有发现,不知不觉地,打开的文件越来越多,多到一行标签都装不下,装不下的标签页 PyCharm 会将其隐藏起来,并以数字的形式告诉你隐藏了几个文件。 -![](http://image.python-online.cn/20190629223534.png) +![](http://image.iswbm.com/20190629223534.png) 点击数字5,你才可以查看隐藏了哪些文件。 @@ -660,11 +660,11 @@ PyCharm 打开一个文件,就占用一个标签面。 如下图,将单行显示取消勾选即可。 -![](http://image.python-online.cn/20190629224229.png) +![](http://image.iswbm.com/20190629224229.png) 设置完后,有哪些文件就非常清晰了。 -![](http://image.python-online.cn/20190629224430.png) +![](http://image.iswbm.com/20190629224430.png) ## 4.15.25 应用搜索,阅读源码必备 @@ -682,7 +682,7 @@ PyCharm 打开一个文件,就占用一个标签面。 如下图所示,按下快捷键后可以很轻松地看见调用列表。 -![](http://image.python-online.cn/20190629231322.png) +![](http://image.iswbm.com/20190629231322.png) 如果你嫌这快捷键太长了,可以使用 `鼠标中键` 点击这个类,可以达到同样的效果。 @@ -698,17 +698,17 @@ PyCharm 打开一个文件,就占用一个标签面。 对比示例,可以查看下面这张图,UI做的还是挺好看的。 -![](http://image.python-online.cn/20190721125739.png) +![](http://image.iswbm.com/20190721125739.png) ## 4.15.27 以列为单位的块编辑 先给你出道小题,像下面这段代码,如果在不影响代码的情况下,快速删除后面代码后面的注释呢? -![](http://image.python-online.cn/20190721132238.png) +![](http://image.iswbm.com/20190721132238.png) 我能想到的有两种方法,如果像如上这种有规律的注释,可以使用 `正则匹配` + `替换` 来实现。 -![](http://image.python-online.cn/20190721133403.png) +![](http://image.iswbm.com/20190721133403.png) 对于这个场景我想到了可以用 vim来轻松的解决,vim 支持块编辑,可以以列为单位选择区域然后进行操作,这在vim中是很常用的一个取消注释的操作。 @@ -724,17 +724,17 @@ PyCharm 打开一个文件,就占用一个标签面。 当你的对象是以大写字母开头时,而你使用小写字母编写代码时,是不能查找到该函数的,你必须得先切换成大写再输入一遍。 -![](http://image.python-online.cn/20190721141327.png) +![](http://image.iswbm.com/20190721141327.png) 如何避免这种尴尬的情况? 只要在配置中关闭大小写匹配即可。 -![](http://image.python-online.cn/20190721141653.png) +![](http://image.iswbm.com/20190721141653.png) 效果如下: -![](http://image.python-online.cn/20190721141751.png) +![](http://image.iswbm.com/20190721141751.png) ## 4.15.29 保护眼睛,从PyCharm开始 @@ -746,7 +746,7 @@ PyCharm 打开一个文件,就占用一个标签面。 这里就教大家如何设置 PyCharm 的背景色为护眼色,方法如下: -![](http://image.python-online.cn/20190721143450.png) +![](http://image.iswbm.com/20190721143450.png) 设置护眼色,会降低 PyCharm 的顔值,这需要你从中取一个取舍。 @@ -785,7 +785,7 @@ PyCharm 打开一个文件,就占用一个标签面。 这里我提前准备了几种编程语言的 Hello World ,效果如下: -![](http://image.python-online.cn/20191211210012.png) +![](http://image.iswbm.com/20191211210012.png) @@ -799,17 +799,17 @@ PyCharm 打开一个文件,就占用一个标签面。 以前我经常使用一些在线的网站,比如:https://tool.oschina.net/codeformat/json -![](http://image.python-online.cn/20191211211309.png) +![](http://image.iswbm.com/20191211211309.png) 如果你的电脑无法连网,或者不喜欢多记一个网址,完全可以使用 PyCharnm 来解决这一诉求 没有经过美化是这样的: -![](http://image.python-online.cn/20191211211334.png) +![](http://image.iswbm.com/20191211211334.png) 按住 `Ctrl+Alt+L`经过美化后是这样的 -![](http://image.python-online.cn/20191211211626.png) +![](http://image.iswbm.com/20191211211626.png) @@ -819,11 +819,11 @@ PyCharm 打开一个文件,就占用一个标签面。 对于像我这样熟悉 Linux 的开发者来说,Windows 的 那些 CMD 命令带来的糟糕体验是无法忍受的。 -![](http://image.python-online.cn/20191211212546.png) +![](http://image.iswbm.com/20191211212546.png) 在弹出的 Bash 窗口,你可以敲入你想使用的 Linux 命令,是不是舒服多了。 -![](http://image.python-online.cn/20191222143741.png) +![](http://image.iswbm.com/20191222143741.png) @@ -845,57 +845,57 @@ PyCharm 打开一个文件,就占用一个标签面。 假如,我现在有如下一段代码,红框标出的代码放在主函数中,有些不太合适,况且这段代码不能让人一眼就看出它是在做什么事情。如何将其进行封装,对我们理清整个主程序的逻辑会有帮助。 -![](http://image.python-online.cn/20191222141905.png) +![](http://image.iswbm.com/20191222141905.png) 选中你要封装的代码,然后按住 `Ctrl`+`Alt`+`M` 后,会跳出如下界面,根据自己的需要,修改函数名,选择参数和返回值 -![](http://image.python-online.cn/20191222141955.png) +![](http://image.iswbm.com/20191222141955.png) 一切就绪点击 `OK`,PyCharm 会自动在合适的位置为你定义一个函数名,并将你选中的代码放到里面,其中参数名和返回值也都是按照你的要求,效果如下: -![](http://image.python-online.cn/20191222142223.png) +![](http://image.iswbm.com/20191222142223.png) ## 4.15.35 使用 Git 进行版本管理 点击 `VCS` -> `Git` -> `Clone` -![](http://image.python-online.cn/20191211100048.png) +![](http://image.iswbm.com/20191211100048.png) 填写git仓库相关信息 -![](http://image.python-online.cn/20191211100657.png) +![](http://image.iswbm.com/20191211100657.png) 点击 `Test`,会尝试连接 git 服务器,中间会让你输入登陆的帐号和密码。 -![](http://image.python-online.cn/20191211101706.png) +![](http://image.iswbm.com/20191211101706.png) 点击`OK` 后,若一切正常会提示连接成功。 -![](http://image.python-online.cn/20191211101845.png) +![](http://image.iswbm.com/20191211101845.png) 点击 `OK` 后,PyCharm 需要你选择如何打开这个 Git 仓库目录,是在当前窗口中打开,还是新建一个窗口? 由于我在一个 PyCharm 下会有多个 Git 仓库,为了方便,我选择在当前窗口中打开(注意勾选 `Add to currently opened projects`)。 -![](http://image.python-online.cn/20191211102501.png) +![](http://image.iswbm.com/20191211102501.png) 至此,Git 配置完成。 此时你可以 `VCS` -> `Git` 查看,发现之前这些灰色不可用的按钮都可以使用了。 -![](http://image.python-online.cn/20191211102826.png) +![](http://image.iswbm.com/20191211102826.png) 本篇重在讲解 PyCharm 的配置,关于Git 的操作,不属于本篇重点,就不再展开讲了。 若你想对已配置的Git仓库进行修改,可点击 `File` -> `Setting` -> `Version Control` 调出如下界面。 -![](http://image.python-online.cn/20191211133836.png) +![](http://image.iswbm.com/20191211133836.png) 不得不说 PyCharm 的这 UI 做得可以,随便改了个东西提交一下 -![](http://image.python-online.cn/20191211143510.png) +![](http://image.iswbm.com/20191211143510.png) diff --git a/source/c04/c04_15.rst b/source/c04/c04_15.rst index dab19b3..2c25fb8 100644 --- a/source/c04/c04_15.rst +++ b/source/c04/c04_15.rst @@ -1117,106 +1117,106 @@ Template |image106| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190323164120.png -.. |image2| image:: http://image.python-online.cn/20190323211635.png -.. |image3| image:: http://image.python-online.cn/20190323211301.png -.. |image4| image:: http://image.python-online.cn/20190324111603.png -.. |image5| image:: http://image.python-online.cn/20190323153643.png -.. |image6| image:: http://image.python-online.cn/20190323214545.png -.. |image7| image:: http://image.python-online.cn/20190323225704.png -.. |image8| image:: http://image.python-online.cn/20190323225631.png -.. |image9| image:: http://image.python-online.cn/20190323232017.png +.. |image1| image:: http://image.iswbm.com/20190323164120.png +.. |image2| image:: http://image.iswbm.com/20190323211635.png +.. |image3| image:: http://image.iswbm.com/20190323211301.png +.. |image4| image:: http://image.iswbm.com/20190324111603.png +.. |image5| image:: http://image.iswbm.com/20190323153643.png +.. |image6| image:: http://image.iswbm.com/20190323214545.png +.. |image7| image:: http://image.iswbm.com/20190323225704.png +.. |image8| image:: http://image.iswbm.com/20190323225631.png +.. |image9| image:: http://image.iswbm.com/20190323232017.png .. |image10| image:: https://i.loli.net/2019/03/23/5c965275bf0d7.gif .. |image11| image:: https://i.loli.net/2019/03/23/5c9653e1b757a.gif -.. |image12| image:: http://image.python-online.cn/20190324111429.png -.. |image13| image:: http://image.python-online.cn/Fi3N02x9OeOPatGdaReam_icn9G_ -.. |image14| image:: http://image.python-online.cn/Fj1W53Txj0iFs5eYhFYh_dHlPtIL -.. |image15| image:: http://image.python-online.cn/FlMsB7B1x6ET9mLOgydTWuTEXuOe -.. |image16| image:: http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId -.. |image17| image:: http://image.python-online.cn/FiNCYpVlI93gk1zhOdQn4c0A8FMX -.. |image18| image:: http://image.python-online.cn/FrAq1tVRM7Bz948wRqZFzU2PQnI0 -.. |image19| image:: http://image.python-online.cn/Fo2aEraqbj_2KqDt44EzJTVe8pEf -.. |image20| image:: http://image.python-online.cn/FmfL3r0iWx_srT_xMASBEp1ZaaId -.. |image21| image:: http://image.python-online.cn/FujczKwTUPa8l5EEmS0eoh-zL1Nk -.. |image22| image:: http://image.python-online.cn/Fq60WOdcRJopqV6MVoRcIuZclYKx -.. |image23| image:: http://image.python-online.cn/FlXynbyxh8tTrCpc4tVLqycL7JQm -.. |image24| image:: http://image.python-online.cn/FiD91PR1hUu0Ruc6cmZ7EGNM6Be_ -.. |image25| image:: http://image.python-online.cn/FhkX5Ko3LVZL_p7YfitDsTDxvHmL -.. |image26| image:: http://image.python-online.cn/FuSSVa-aMqkfCaf62sbUoX2PLaYM -.. |image27| image:: http://image.python-online.cn/FtFPI89AOKmPLNpNxf-jdkn1BDLW -.. |image28| image:: http://image.python-online.cn/FiKyU6tjQauWXfaVfKLhwi3NkXBf -.. |image29| image:: http://image.python-online.cn/FsAM-8HyzSrLWZJ_lg3ofw84_ibf -.. |image30| image:: http://image.python-online.cn/FgJCtNYkjPfBaTbRxwb3Z6icHqkf -.. |image31| image:: http://image.python-online.cn/20190507222856.png -.. |image32| image:: http://image.python-online.cn/20190507222119.png -.. |image33| image:: http://image.python-online.cn/20190507223313.png -.. |image34| image:: http://image.python-online.cn/20190507215525.png -.. |image35| image:: http://image.python-online.cn/20190507220101.png -.. |image36| image:: http://image.python-online.cn/20190419152120.png -.. |image37| image:: http://image.python-online.cn/20190419152145.png -.. |image38| image:: http://image.python-online.cn/20190507220740.png -.. |image39| image:: http://image.python-online.cn/20190423162328.png -.. |image40| image:: http://image.python-online.cn/20190423163341.png -.. |image41| image:: http://image.python-online.cn/20190506150523.png -.. |image42| image:: http://image.python-online.cn/20190506150010.png -.. |image43| image:: http://image.python-online.cn/20190506150100.png -.. |image44| image:: http://image.python-online.cn/20190507000850.png -.. |image45| image:: http://image.python-online.cn/20190507001025.png -.. |image46| image:: http://image.python-online.cn/20190507001422.png -.. |image47| image:: http://image.python-online.cn/20190507001350.png -.. |image48| image:: http://image.python-online.cn/20190507152911.png -.. |image49| image:: http://image.python-online.cn/20190507152840.png -.. |image50| image:: http://image.python-online.cn/20190507153847.png -.. |image51| image:: http://image.python-online.cn/20190507154027.png -.. |image52| image:: http://image.python-online.cn/20190613154147.png -.. |image53| image:: http://image.python-online.cn/20190613154401.png -.. |image54| image:: http://image.python-online.cn/20190613160905.png -.. |image55| image:: http://image.python-online.cn/20190614235120.png -.. |image56| image:: http://image.python-online.cn/20190616211359.png -.. |image57| image:: http://image.python-online.cn/20190616214310.png -.. |image58| image:: http://image.python-online.cn/20190616221620.png -.. |image59| image:: http://image.python-online.cn/20190616232746.png -.. |image60| image:: http://image.python-online.cn/20190616233827.png -.. |image61| image:: http://image.python-online.cn/20190616235007.png -.. |image62| image:: http://image.python-online.cn/20190616234038.png -.. |image63| image:: http://image.python-online.cn/20190616231649.png -.. |image64| image:: http://image.python-online.cn/20190616232527.png -.. |image65| image:: http://image.python-online.cn/20190629183430.png +.. |image12| image:: http://image.iswbm.com/20190324111429.png +.. |image13| image:: http://image.iswbm.com/Fi3N02x9OeOPatGdaReam_icn9G_ +.. |image14| image:: http://image.iswbm.com/Fj1W53Txj0iFs5eYhFYh_dHlPtIL +.. |image15| image:: http://image.iswbm.com/FlMsB7B1x6ET9mLOgydTWuTEXuOe +.. |image16| image:: http://image.iswbm.com/FmfL3r0iWx_srT_xMASBEp1ZaaId +.. |image17| image:: http://image.iswbm.com/FiNCYpVlI93gk1zhOdQn4c0A8FMX +.. |image18| image:: http://image.iswbm.com/FrAq1tVRM7Bz948wRqZFzU2PQnI0 +.. |image19| image:: http://image.iswbm.com/Fo2aEraqbj_2KqDt44EzJTVe8pEf +.. |image20| image:: http://image.iswbm.com/FmfL3r0iWx_srT_xMASBEp1ZaaId +.. |image21| image:: http://image.iswbm.com/FujczKwTUPa8l5EEmS0eoh-zL1Nk +.. |image22| image:: http://image.iswbm.com/Fq60WOdcRJopqV6MVoRcIuZclYKx +.. |image23| image:: http://image.iswbm.com/FlXynbyxh8tTrCpc4tVLqycL7JQm +.. |image24| image:: http://image.iswbm.com/FiD91PR1hUu0Ruc6cmZ7EGNM6Be_ +.. |image25| image:: http://image.iswbm.com/FhkX5Ko3LVZL_p7YfitDsTDxvHmL +.. |image26| image:: http://image.iswbm.com/FuSSVa-aMqkfCaf62sbUoX2PLaYM +.. |image27| image:: http://image.iswbm.com/FtFPI89AOKmPLNpNxf-jdkn1BDLW +.. |image28| image:: http://image.iswbm.com/FiKyU6tjQauWXfaVfKLhwi3NkXBf +.. |image29| image:: http://image.iswbm.com/FsAM-8HyzSrLWZJ_lg3ofw84_ibf +.. |image30| image:: http://image.iswbm.com/FgJCtNYkjPfBaTbRxwb3Z6icHqkf +.. |image31| image:: http://image.iswbm.com/20190507222856.png +.. |image32| image:: http://image.iswbm.com/20190507222119.png +.. |image33| image:: http://image.iswbm.com/20190507223313.png +.. |image34| image:: http://image.iswbm.com/20190507215525.png +.. |image35| image:: http://image.iswbm.com/20190507220101.png +.. |image36| image:: http://image.iswbm.com/20190419152120.png +.. |image37| image:: http://image.iswbm.com/20190419152145.png +.. |image38| image:: http://image.iswbm.com/20190507220740.png +.. |image39| image:: http://image.iswbm.com/20190423162328.png +.. |image40| image:: http://image.iswbm.com/20190423163341.png +.. |image41| image:: http://image.iswbm.com/20190506150523.png +.. |image42| image:: http://image.iswbm.com/20190506150010.png +.. |image43| image:: http://image.iswbm.com/20190506150100.png +.. |image44| image:: http://image.iswbm.com/20190507000850.png +.. |image45| image:: http://image.iswbm.com/20190507001025.png +.. |image46| image:: http://image.iswbm.com/20190507001422.png +.. |image47| image:: http://image.iswbm.com/20190507001350.png +.. |image48| image:: http://image.iswbm.com/20190507152911.png +.. |image49| image:: http://image.iswbm.com/20190507152840.png +.. |image50| image:: http://image.iswbm.com/20190507153847.png +.. |image51| image:: http://image.iswbm.com/20190507154027.png +.. |image52| image:: http://image.iswbm.com/20190613154147.png +.. |image53| image:: http://image.iswbm.com/20190613154401.png +.. |image54| image:: http://image.iswbm.com/20190613160905.png +.. |image55| image:: http://image.iswbm.com/20190614235120.png +.. |image56| image:: http://image.iswbm.com/20190616211359.png +.. |image57| image:: http://image.iswbm.com/20190616214310.png +.. |image58| image:: http://image.iswbm.com/20190616221620.png +.. |image59| image:: http://image.iswbm.com/20190616232746.png +.. |image60| image:: http://image.iswbm.com/20190616233827.png +.. |image61| image:: http://image.iswbm.com/20190616235007.png +.. |image62| image:: http://image.iswbm.com/20190616234038.png +.. |image63| image:: http://image.iswbm.com/20190616231649.png +.. |image64| image:: http://image.iswbm.com/20190616232527.png +.. |image65| image:: http://image.iswbm.com/20190629183430.png .. |image66| image:: https://i.loli.net/2019/06/29/5d17589c1603755790.gif -.. |image67| image:: http://image.python-online.cn/20190629211910.png +.. |image67| image:: http://image.iswbm.com/20190629211910.png .. |image68| image:: https://i.loli.net/2019/06/29/5d1764b94d11128912.gif .. |image69| image:: https://i.loli.net/2019/06/29/5d176e9ba92e916696.gif -.. |image70| image:: http://image.python-online.cn/20190629221224.png -.. |image71| image:: http://image.python-online.cn/20190629221547.png -.. |image72| image:: http://image.python-online.cn/20190629223534.png -.. |image73| image:: http://image.python-online.cn/20190629224229.png -.. |image74| image:: http://image.python-online.cn/20190629224430.png -.. |image75| image:: http://image.python-online.cn/20190629231322.png -.. |image76| image:: http://image.python-online.cn/20190721125739.png -.. |image77| image:: http://image.python-online.cn/20190721132238.png -.. |image78| image:: http://image.python-online.cn/20190721133403.png +.. |image70| image:: http://image.iswbm.com/20190629221224.png +.. |image71| image:: http://image.iswbm.com/20190629221547.png +.. |image72| image:: http://image.iswbm.com/20190629223534.png +.. |image73| image:: http://image.iswbm.com/20190629224229.png +.. |image74| image:: http://image.iswbm.com/20190629224430.png +.. |image75| image:: http://image.iswbm.com/20190629231322.png +.. |image76| image:: http://image.iswbm.com/20190721125739.png +.. |image77| image:: http://image.iswbm.com/20190721132238.png +.. |image78| image:: http://image.iswbm.com/20190721133403.png .. |image79| image:: https://i.loli.net/2019/07/21/5d3401410087b61815.gif -.. |image80| image:: http://image.python-online.cn/20190721141327.png -.. |image81| image:: http://image.python-online.cn/20190721141653.png -.. |image82| image:: http://image.python-online.cn/20190721141751.png -.. |image83| image:: http://image.python-online.cn/20190721143450.png -.. |image84| image:: http://image.python-online.cn/20191211210012.png -.. |image85| image:: http://image.python-online.cn/20191211211309.png -.. |image86| image:: http://image.python-online.cn/20191211211334.png -.. |image87| image:: http://image.python-online.cn/20191211211626.png -.. |image88| image:: http://image.python-online.cn/20191211212546.png -.. |image89| image:: http://image.python-online.cn/20191222143741.png -.. |image90| image:: http://image.python-online.cn/20191222141905.png -.. |image91| image:: http://image.python-online.cn/20191222141955.png -.. |image92| image:: http://image.python-online.cn/20191222142223.png -.. |image93| image:: http://image.python-online.cn/20191211100048.png -.. |image94| image:: http://image.python-online.cn/20191211100657.png -.. |image95| image:: http://image.python-online.cn/20191211101706.png -.. |image96| image:: http://image.python-online.cn/20191211101845.png -.. |image97| image:: http://image.python-online.cn/20191211102501.png -.. |image98| image:: http://image.python-online.cn/20191211102826.png -.. |image99| image:: http://image.python-online.cn/20191211133836.png -.. |image100| image:: http://image.python-online.cn/20191211143510.png +.. |image80| image:: http://image.iswbm.com/20190721141327.png +.. |image81| image:: http://image.iswbm.com/20190721141653.png +.. |image82| image:: http://image.iswbm.com/20190721141751.png +.. |image83| image:: http://image.iswbm.com/20190721143450.png +.. |image84| image:: http://image.iswbm.com/20191211210012.png +.. |image85| image:: http://image.iswbm.com/20191211211309.png +.. |image86| image:: http://image.iswbm.com/20191211211334.png +.. |image87| image:: http://image.iswbm.com/20191211211626.png +.. |image88| image:: http://image.iswbm.com/20191211212546.png +.. |image89| image:: http://image.iswbm.com/20191222143741.png +.. |image90| image:: http://image.iswbm.com/20191222141905.png +.. |image91| image:: http://image.iswbm.com/20191222141955.png +.. |image92| image:: http://image.iswbm.com/20191222142223.png +.. |image93| image:: http://image.iswbm.com/20191211100048.png +.. |image94| image:: http://image.iswbm.com/20191211100657.png +.. |image95| image:: http://image.iswbm.com/20191211101706.png +.. |image96| image:: http://image.iswbm.com/20191211101845.png +.. |image97| image:: http://image.iswbm.com/20191211102501.png +.. |image98| image:: http://image.iswbm.com/20191211102826.png +.. |image99| image:: http://image.iswbm.com/20191211133836.png +.. |image100| image:: http://image.iswbm.com/20191211143510.png .. |image101| image:: http://image.iswbm.com/20200420085524.png .. |image102| image:: http://image.iswbm.com/20200420090117.png .. |image103| image:: http://image.iswbm.com/20200420090428.png diff --git a/source/c04/c04_17.md b/source/c04/c04_17.md index 7b1963d..8f1ed7c 100644 --- a/source/c04/c04_17.md +++ b/source/c04/c04_17.md @@ -22,7 +22,7 @@ - Stragety:策略基类 - ConcreteStragety:具体策略 -![](http://image.python-online.cn/20190414144511.png) +![](http://image.iswbm.com/20190414144511.png) 以第一个超市做活动的场景来举个例子。 @@ -276,7 +276,7 @@ class User: 验证结果 -![](http://image.python-online.cn/20190512113846.png) +![](http://image.iswbm.com/20190512113846.png) - 使用装饰器 @@ -305,7 +305,7 @@ class User: 验证结果 -![](http://image.python-online.cn/20190512113917.png) +![](http://image.iswbm.com/20190512113917.png) - 使用元类 @@ -328,7 +328,7 @@ class User(metaclass=MetaSingleton): 验证结果 -![](http://image.python-online.cn/20190512114028.png) +![](http://image.iswbm.com/20190512114028.png) 以上的代码,一般情况下没有问题,但在并发场景中,就会出现线程安全的问题。 diff --git a/source/c04/c04_17.rst b/source/c04/c04_17.rst index 9a457f4..8e812cb 100644 --- a/source/c04/c04_17.rst +++ b/source/c04/c04_17.rst @@ -477,9 +477,9 @@ Order |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190414144511.png -.. |image2| image:: http://image.python-online.cn/20190512113846.png -.. |image3| image:: http://image.python-online.cn/20190512113917.png -.. |image4| image:: http://image.python-online.cn/20190512114028.png +.. |image1| image:: http://image.iswbm.com/20190414144511.png +.. |image2| image:: http://image.iswbm.com/20190512113846.png +.. |image3| image:: http://image.iswbm.com/20190512113917.png +.. |image4| image:: http://image.iswbm.com/20190512114028.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 029ccdc..8480088 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -131,7 +131,7 @@ command + space finder的显示 -![](http://image.python-online.cn/20190810161513.png) +![](http://image.iswbm.com/20190810161513.png) @@ -141,11 +141,11 @@ finder的显示 2. 不将电脑放在软的地方,如沙发,枕头等,可以买个散热支架。 - ![来自Mac派](http://image.python-online.cn/20190810162000.png) + ![来自Mac派](http://image.iswbm.com/20190810162000.png) 3. 打开「活动监视器」(Alfred就可以打开),杀掉暂没用且cpu使用率最高的程序 - ![](http://image.python-online.cn/20190810162315.png) + ![](http://image.iswbm.com/20190810162315.png) 4. MacBook Pro CPU 温度在 5、60℃ 的时候,风扇会转到两三千转每分钟,只有 CPU 温度达到 70 多度或更高时,才会高速运转降温。但这时 Mac 已经很热了。 diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index c17bc76..af90b56 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -147,7 +147,7 @@ finder的显示 2. 不将电脑放在软的地方,如沙发,枕头等,可以买个散热支架。 - .. figure:: http://image.python-online.cn/20190810162000.png + .. figure:: http://image.iswbm.com/20190810162000.png :alt: 来自Mac派 来自Mac派 @@ -379,8 +379,8 @@ finder的显示 .. |image1| image:: http://image.iswbm.com/image-20200704192441091.png .. |image2| image:: http://image.iswbm.com/image-20200704194215498.png .. |image3| image:: http://image.iswbm.com/image-20200704195122336.png -.. |image4| image:: http://image.python-online.cn/20190810161513.png -.. |image5| image:: http://image.python-online.cn/20190810162315.png +.. |image4| image:: http://image.iswbm.com/20190810161513.png +.. |image5| image:: http://image.iswbm.com/20190810162315.png .. |image6| image:: http://image.iswbm.com/image-20200704192031119.png .. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_19.md b/source/c04/c04_19.md index b4f3fb9..64423b9 100644 --- a/source/c04/c04_19.md +++ b/source/c04/c04_19.md @@ -678,11 +678,11 @@ set cindent(cin) 设置C语言风格缩进 你可以**关注本公众号「Python编程时光」**,在后台回复“**vim**” ,即可获取高清大图。 -![图1](http://image.python-online.cn/20190804222221.png) +![图1](http://image.iswbm.com/20190804222221.png) -![图2](http://image.python-online.cn/20190804222247.png) +![图2](http://image.iswbm.com/20190804222247.png) --- diff --git a/source/c04/c04_19.rst b/source/c04/c04_19.rst index 19f0945..bceb761 100644 --- a/source/c04/c04_19.rst +++ b/source/c04/c04_19.rst @@ -709,12 +709,12 @@ n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识 你可以\ **关注本公众号「Python编程时光」**\ ,在后台回复“**vim**” ,即可获取高清大图。 -.. figure:: http://image.python-online.cn/20190804222221.png +.. figure:: http://image.iswbm.com/20190804222221.png :alt: 图1 图1 -.. figure:: http://image.python-online.cn/20190804222247.png +.. figure:: http://image.iswbm.com/20190804222247.png :alt: 图2 图2 diff --git a/source/c04/c04_20.md b/source/c04/c04_20.md index b94eff8..0e1a468 100644 --- a/source/c04/c04_20.md +++ b/source/c04/c04_20.md @@ -23,7 +23,7 @@ linux常用命令 +centos 4、搜索指定网站 加site ``` -Python冷知识 site:python-online.cn +Python冷知识 site:iswbm.com ``` 5、链接中包含字符串 加inurl diff --git a/source/c04/c04_20.rst b/source/c04/c04_20.rst index 72b74f8..2e86d63 100644 --- a/source/c04/c04_20.rst +++ b/source/c04/c04_20.rst @@ -25,7 +25,7 @@ :: - Python冷知识 site:python-online.cn + Python冷知识 site:iswbm.com 5、链接中包含字符串 加inurl diff --git a/source/c04/c04_21.md b/source/c04/c04_21.md index 6127de9..412e08c 100644 --- a/source/c04/c04_21.md +++ b/source/c04/c04_21.md @@ -354,7 +354,7 @@ trusted-host=tsinghua.edu.cn 以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 -![](http://image.python-online.cn/20191105200041.png) +![](http://image.iswbm.com/20191105200041.png) diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index 36aeff6..cc67eb1 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -373,6 +373,6 @@ pip 的文件夹,若没有则创建之。 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191105200041.png +.. |image1| image:: http://image.iswbm.com/20191105200041.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_22.md b/source/c04/c04_22.md index a9d1f88..02a0c79 100644 --- a/source/c04/c04_22.md +++ b/source/c04/c04_22.md @@ -6,7 +6,7 @@ 开启阅读模式:chrome://flags/#enable-reader-mode -![](http://image.python-online.cn/20191201103653.png) +![](http://image.iswbm.com/20191201103653.png) 开启完成后,需要重启浏览器,你可以随便打开一篇博客,然后在地址栏右边会有一个阅读模式的按钮。 diff --git a/source/c04/c04_22.rst b/source/c04/c04_22.rst index a179e21..088ec9d 100644 --- a/source/c04/c04_22.rst +++ b/source/c04/c04_22.rst @@ -13,5 +13,5 @@ 开启完成后,需要重启浏览器,你可以随便打开一篇博客,然后在地址栏右边会有一个阅读模式的按钮。 .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191201103653.png +.. |image1| image:: http://image.iswbm.com/20191201103653.png diff --git a/source/c04/c04_23.md b/source/c04/c04_23.md index 7c4195e..0bfd154 100644 --- a/source/c04/c04_23.md +++ b/source/c04/c04_23.md @@ -52,5 +52,5 @@ schema_list: 在你的安裝目錄下,找到`WeaselDeployer.exe` 點擊,選擇你想要添加的輸入方案,比如這裏選擇 五筆-拼音,再點中,然後選擇皮膚,最後就會觸發重新部署,配置生效。 -![](http://image.python-online.cn/20200119143952.png) +![](http://image.iswbm.com/20200119143952.png) diff --git a/source/c04/c04_23.rst b/source/c04/c04_23.rst index 08803c5..61f4373 100644 --- a/source/c04/c04_23.rst +++ b/source/c04/c04_23.rst @@ -60,5 +60,5 @@ |image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20200119143952.png +.. |image1| image:: http://image.iswbm.com/20200119143952.png diff --git a/source/c04/c04_25.md b/source/c04/c04_25.md new file mode 100644 index 0000000..65fceee --- /dev/null +++ b/source/c04/c04_25.md @@ -0,0 +1,17 @@ +# 4.25 如何写好 readme? + +## 贡献者名单 + +一个如下的 HTML 代码,代表一个头像。[点击参考](https://raw.githubusercontent.com/Snailclimb/JavaGuide/master/README.md) + +```html + + + +``` + +## 添加表情 + +参考这个github:https://gist.github.com/rxaviers/7360908 + +或者看这个网站:https://www.webfx.com/tools/emoji-cheat-sheet/ \ No newline at end of file diff --git a/source/c04/c04_25.rst b/source/c04/c04_25.rst new file mode 100644 index 0000000..066f97f --- /dev/null +++ b/source/c04/c04_25.rst @@ -0,0 +1,21 @@ +4.25 如何写好 readme? +===================== + +贡献者名单 +---------- + +一个如下的 HTML +代码,代表一个头像。\ `点击参考 `__ + +.. code:: html + + + + + +添加表情 +-------- + +参考这个github:https://gist.github.com/rxaviers/7360908 + +或者看这个网站:https://www.webfx.com/tools/emoji-cheat-sheet/ diff --git a/source/c05/c05_01.md b/source/c05/c05_01.md index 0abc12c..30bed32 100644 --- a/source/c05/c05_01.md +++ b/source/c05/c05_01.md @@ -19,7 +19,7 @@ **排序原理** :选取一个数组中的某个数,将整个数组分为两个子数组(小于此数的为一组,大于此数的为一组)。然后对分出的子数组,重复以上步骤。 **原理图解**: -![快速排序](http://image.python-online.cn/Fpj4DFN_YCtfmJwb_85QnsuIVLqk) +![快速排序](http://image.iswbm.com/Fpj4DFN_YCtfmJwb_85QnsuIVLqk) 代码实现: @@ -50,7 +50,7 @@ def quick_sort(array, reverse=False): **排序原理** :对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序。 **原理图解** : -![|冒泡排序|](http://image.python-online.cn/FvbrVECeq58hY8TptG4ilkL5Owcc) +![|冒泡排序|](http://image.iswbm.com/FvbrVECeq58hY8TptG4ilkL5Owcc) 代码实现: @@ -81,7 +81,7 @@ def bubble_sort(array, reverse=False): **排序原理** :遍历n趟(n为数组的长度),每一趟都从「待排序」的元素中排出最小(或最大)的元素。 **原理图解** : -![](http://image.python-online.cn/FmZ_24t62gF32Dg3AgtZe-U5OuLY) +![](http://image.iswbm.com/FmZ_24t62gF32Dg3AgtZe-U5OuLY) 代码实现: ```python @@ -110,7 +110,7 @@ def select_sort(array, reverse=False): **排序原理** :每次将一个待排序的元素与已排序的元素进行逐一比较,直到找到合适的位置按大小插入。通俗地说,就类似我们打牌的时候,给牌进行排序。 **原理图解** : -![](http://image.python-online.cn/FmLrNuhfNcnYnLGoYJv-YbpBPV7n) +![](http://image.iswbm.com/FmLrNuhfNcnYnLGoYJv-YbpBPV7n) 代码实现: ```python @@ -154,8 +154,8 @@ def insert_sort(array): **原理图解**: -![](http://image.python-online.cn/Fm44FD0KE9Y4RM7MF3knlGVCJba4) -![希尔排序](http://image.python-online.cn/FqTP6YjNoM52fA-bA8pSPdbLgcZh) +![](http://image.iswbm.com/Fm44FD0KE9Y4RM7MF3knlGVCJba4) +![希尔排序](http://image.iswbm.com/FqTP6YjNoM52fA-bA8pSPdbLgcZh) **代码实现**: @@ -203,7 +203,7 @@ def shell_sort(array, reverse=False): 其实难点,在于如何在剩余堆里,找到最大数(或者最小数)。 **原理图解**: -![堆排序](http://image.python-online.cn/FgRFOfPhrL0yeUuGzly5309APCnD) +![堆排序](http://image.iswbm.com/FgRFOfPhrL0yeUuGzly5309APCnD) **代码实现**: @@ -283,11 +283,11 @@ def build_min_heap(arr, start, end): 第一次接触到这个分治思想,是在《算法图解》这本书里,里面举的一个「分割土地」的例子非常生动形象。精髓就是,不断将问题的规模缩小化,然后逐步往上解决问题。 **原理图解**: -![分而治之思想](http://image.python-online.cn/FjQebwFfa2tDYS78_CUxy1rXmufj) +![分而治之思想](http://image.iswbm.com/FjQebwFfa2tDYS78_CUxy1rXmufj) 重点其实是这个“治”的过程,如何实现将两个有序数组合并起来?思路大概是这样的。 -![](http://image.python-online.cn/FjP-_a66OUAZtTpU1ytqZ66My80C) -![合并两个有序数组](http://image.python-online.cn/FnCZ-3Pj39T_ELROSsGRDN31yWtY) +![](http://image.iswbm.com/FjP-_a66OUAZtTpU1ytqZ66My80C) +![合并两个有序数组](http://image.iswbm.com/FnCZ-3Pj39T_ELROSsGRDN31yWtY) **代码实现**: @@ -329,7 +329,7 @@ def merge(left, right): 桶排序是有局限性的,一般情况下,他并不能对有负数或者有小数的数组进行排序。另一方面,在无法预知数组的真实情况下,其实排序性能是非常不稳定的。比如,你可能遇到这样一个数组[1,4,5,1000000],按照桶算法以下面的代码运行,你需要1000000个桶,非常慢,而实际上,这个数组很小,使用任意比较排序算法很快就能结果。 **原理图解**: -![桶排序](http://image.python-online.cn/FljGa3F3wM_YGACETeuRHCiERXKb) +![桶排序](http://image.iswbm.com/FljGa3F3wM_YGACETeuRHCiERXKb) **代码实现**: ```python @@ -379,7 +379,7 @@ def bucket_sort(array, reverse=False): 这里图解下,正序的过程。 原始数组:22, 33, 43, 55, 14, 28, 65, 39, 81, 33, 100 -![基数排序](http://image.python-online.cn/FhwWVp4LVABIMPHqNo_cpjJA9kHV) +![基数排序](http://image.iswbm.com/FhwWVp4LVABIMPHqNo_cpjJA9kHV) **代码实现**: ```python diff --git a/source/c05/c05_01.rst b/source/c05/c05_01.rst index 2f1233f..210f381 100755 --- a/source/c05/c05_01.rst +++ b/source/c05/c05_01.rst @@ -424,17 +424,17 @@ sgnificant digital),LSD 的排序方式由键值的最右边开始,而 MSD |image13| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |快速排序| image:: http://image.python-online.cn/Fpj4DFN_YCtfmJwb_85QnsuIVLqk -.. |\|冒泡排序\|| image:: http://image.python-online.cn/FvbrVECeq58hY8TptG4ilkL5Owcc -.. |image3| image:: http://image.python-online.cn/FmZ_24t62gF32Dg3AgtZe-U5OuLY -.. |image4| image:: http://image.python-online.cn/FmLrNuhfNcnYnLGoYJv-YbpBPV7n -.. |image5| image:: http://image.python-online.cn/Fm44FD0KE9Y4RM7MF3knlGVCJba4 -.. |希尔排序| image:: http://image.python-online.cn/FqTP6YjNoM52fA-bA8pSPdbLgcZh -.. |堆排序| image:: http://image.python-online.cn/FgRFOfPhrL0yeUuGzly5309APCnD -.. |分而治之思想| image:: http://image.python-online.cn/FjQebwFfa2tDYS78_CUxy1rXmufj -.. |image9| image:: http://image.python-online.cn/FjP-_a66OUAZtTpU1ytqZ66My80C -.. |合并两个有序数组| image:: http://image.python-online.cn/FnCZ-3Pj39T_ELROSsGRDN31yWtY -.. |桶排序| image:: http://image.python-online.cn/FljGa3F3wM_YGACETeuRHCiERXKb -.. |基数排序| image:: http://image.python-online.cn/FhwWVp4LVABIMPHqNo_cpjJA9kHV +.. |快速排序| image:: http://image.iswbm.com/Fpj4DFN_YCtfmJwb_85QnsuIVLqk +.. |\|冒泡排序\|| image:: http://image.iswbm.com/FvbrVECeq58hY8TptG4ilkL5Owcc +.. |image3| image:: http://image.iswbm.com/FmZ_24t62gF32Dg3AgtZe-U5OuLY +.. |image4| image:: http://image.iswbm.com/FmLrNuhfNcnYnLGoYJv-YbpBPV7n +.. |image5| image:: http://image.iswbm.com/Fm44FD0KE9Y4RM7MF3knlGVCJba4 +.. |希尔排序| image:: http://image.iswbm.com/FqTP6YjNoM52fA-bA8pSPdbLgcZh +.. |堆排序| image:: http://image.iswbm.com/FgRFOfPhrL0yeUuGzly5309APCnD +.. |分而治之思想| image:: http://image.iswbm.com/FjQebwFfa2tDYS78_CUxy1rXmufj +.. |image9| image:: http://image.iswbm.com/FjP-_a66OUAZtTpU1ytqZ66My80C +.. |合并两个有序数组| image:: http://image.iswbm.com/FnCZ-3Pj39T_ELROSsGRDN31yWtY +.. |桶排序| image:: http://image.iswbm.com/FljGa3F3wM_YGACETeuRHCiERXKb +.. |基数排序| image:: http://image.iswbm.com/FhwWVp4LVABIMPHqNo_cpjJA9kHV .. |image13| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c05/c05_03.md b/source/c05/c05_03.md index 12445d6..798dad7 100644 --- a/source/c05/c05_03.md +++ b/source/c05/c05_03.md @@ -104,7 +104,7 @@ echo -n hello | md5sum 彩虹表确实像它的名字一样美好,至少黑客眼里是这样。下表是7位以内密码在不同字符集下构造出的彩虹表的情况,彩虹表中哈希链的长度和个数随着字符集的增长而增长,彩虹表的大小和生成时间也随之成倍增加。7位数字组合在彩虹表面前简直就是秒破,即使最复杂的7位密码不到一个小时就能破解,如果采用普通的暴力攻击,破解时间可能需要三周。 -![](http://image.python-online.cn/20190112181126.png) +![](http://image.iswbm.com/20190112181126.png) diff --git a/source/c05/c05_03.rst b/source/c05/c05_03.rst index 2de0e8d..6b47675 100755 --- a/source/c05/c05_03.rst +++ b/source/c05/c05_03.rst @@ -164,6 +164,6 @@ B 可以读取和更改用户 A 的信息,这无疑带来了很大的安全隐 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190112181126.png +.. |image1| image:: http://image.iswbm.com/20190112181126.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_01.md b/source/c06/c06_01.md index a76e1b3..6e403b1 100644 --- a/source/c06/c06_01.md +++ b/source/c06/c06_01.md @@ -110,7 +110,7 @@ plt.show() 以上的注释,可以说是很直白啦。一张图表该有的东西都有了,不花哨,但实用。 看看我们的代码输出的图表是啥样的。 -![](http://image.python-online.cn/20190511164650.png) +![](http://image.iswbm.com/20190511164650.png) ---- ![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c06/c06_01.rst b/source/c06/c06_01.rst index 9fd8245..121250c 100755 --- a/source/c06/c06_01.rst +++ b/source/c06/c06_01.rst @@ -137,6 +137,6 @@ ticks(由Locator对象定义),还有ticklabel(由Formatter对象定义 .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/08/12/5b6ff3716fdc0.png -.. |image2| image:: http://image.python-online.cn/20190511164650.png +.. |image2| image:: http://image.iswbm.com/20190511164650.png .. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_02.md b/source/c06/c06_02.md index be1ee80..864efae 100644 --- a/source/c06/c06_02.md +++ b/source/c06/c06_02.md @@ -35,7 +35,7 @@ plt.show() ``` show image -![](http://image.python-online.cn/20190511164738.png) +![](http://image.iswbm.com/20190511164738.png) ## 02. 散点图 @@ -54,7 +54,7 @@ plt.plot(x, x, 'r--', x, x**2, 'bs', x, x**3, 'g^') plt.show() ``` show image -![](http://image.python-online.cn/20190511164753.png) +![](http://image.iswbm.com/20190511164753.png) ## 03. 直方图 @@ -95,7 +95,7 @@ plt.show() show image -![](http://image.python-online.cn/20190511164802.png) +![](http://image.iswbm.com/20190511164802.png) ## 04. 柱状图 @@ -128,7 +128,7 @@ plt.show() ``` show image -![](http://image.python-online.cn/20190511164814.png) +![](http://image.iswbm.com/20190511164814.png) ### 4.2 叠加柱状图 ```python @@ -154,7 +154,7 @@ plt.show() ``` show image -![](http://image.python-online.cn/20190511164825.png) +![](http://image.iswbm.com/20190511164825.png) ## 05. 饼图 @@ -180,7 +180,7 @@ plt.show() ``` show image -![](http://image.python-online.cn/20190511164835.png) +![](http://image.iswbm.com/20190511164835.png) ### 5.2 嵌套饼图 @@ -214,7 +214,7 @@ plt.axis('equal') plt.show() ``` show image -![](http://image.python-online.cn/20190511164843.png) +![](http://image.iswbm.com/20190511164843.png) ### 5.3 极轴饼图 @@ -245,7 +245,7 @@ plt.show() ``` show image -![](http://image.python-online.cn/20190511164852.png) +![](http://image.iswbm.com/20190511164852.png) ## 06. 三维图 @@ -272,7 +272,7 @@ plt.show() ``` show image -![](http://image.python-online.cn/20190511164900.png) +![](http://image.iswbm.com/20190511164900.png) ### 6.2 绘制三维平面图 @@ -295,7 +295,7 @@ ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='rainbow') plt.show() ``` show image -![](http://image.python-online.cn/20190511164915.png) +![](http://image.iswbm.com/20190511164915.png) --- diff --git a/source/c06/c06_02.rst b/source/c06/c06_02.rst index d7e5895..11dbcf0 100755 --- a/source/c06/c06_02.rst +++ b/source/c06/c06_02.rst @@ -314,15 +314,15 @@ show image |image10| |image11| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511164738.png -.. |image2| image:: http://image.python-online.cn/20190511164753.png -.. |image3| image:: http://image.python-online.cn/20190511164802.png -.. |image4| image:: http://image.python-online.cn/20190511164814.png -.. |image5| image:: http://image.python-online.cn/20190511164825.png -.. |image6| image:: http://image.python-online.cn/20190511164835.png -.. |image7| image:: http://image.python-online.cn/20190511164843.png -.. |image8| image:: http://image.python-online.cn/20190511164852.png -.. |image9| image:: http://image.python-online.cn/20190511164900.png -.. |image10| image:: http://image.python-online.cn/20190511164915.png +.. |image1| image:: http://image.iswbm.com/20190511164738.png +.. |image2| image:: http://image.iswbm.com/20190511164753.png +.. |image3| image:: http://image.iswbm.com/20190511164802.png +.. |image4| image:: http://image.iswbm.com/20190511164814.png +.. |image5| image:: http://image.iswbm.com/20190511164825.png +.. |image6| image:: http://image.iswbm.com/20190511164835.png +.. |image7| image:: http://image.iswbm.com/20190511164843.png +.. |image8| image:: http://image.iswbm.com/20190511164852.png +.. |image9| image:: http://image.iswbm.com/20190511164900.png +.. |image10| image:: http://image.iswbm.com/20190511164915.png .. |image11| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_03.md b/source/c06/c06_03.md index 6072c64..bd7e68c 100644 --- a/source/c06/c06_03.md +++ b/source/c06/c06_03.md @@ -25,7 +25,7 @@ plot(x,S) show() ``` show image -![](http://image.python-online.cn/20190511164936.png) +![](http://image.iswbm.com/20190511164936.png) ## 6.3.2 设置基本元素 @@ -65,7 +65,7 @@ plt.legend() plt.show() ``` show image -![](http://image.python-online.cn/20190511164949.png) +![](http://image.iswbm.com/20190511164949.png) ## 6.3.3 移动轴线 还记得我们在初高中学习的三角函数图象,可不是这样,它应该是有四个象限的。而这里却是一个四四方方的图表。 @@ -89,7 +89,7 @@ ax.spines['bottom'].set_position(('data',0)) ax.spines['left'].set_position(('data',0)) ``` 关于`set_position()`这个函数中的data是啥意思?我查了下官网。解释如下 -![](http://image.python-online.cn/20190511165003.png) +![](http://image.iswbm.com/20190511165003.png) 然后最后发现,上面的写法可以用一定更简洁的方式设置,是等价的。 ```python ax.spines['bottom'].set_position('zero') @@ -97,7 +97,7 @@ ax.spines['left'].set_position('zero') ``` show image -![](http://image.python-online.cn/20190511165013.png) +![](http://image.iswbm.com/20190511165013.png) ## 6.3.4 添加注释 现在的图形部分已经成型,接下让我们现在使用annotate命令注解一些我们感兴趣的点。 @@ -137,7 +137,7 @@ plt.annotate(r'$cos(\frac{2\pi}{3})=-\frac{1}{2}$', 第七个参数,`arrowprops`,对箭头的类型的一些设置。 show image -![](http://image.python-online.cn/20190511165020.png) +![](http://image.iswbm.com/20190511165020.png) ## 6.3.5 完整代码 diff --git a/source/c06/c06_03.rst b/source/c06/c06_03.rst index c5bd3db..09ce0dc 100755 --- a/source/c06/c06_03.rst +++ b/source/c06/c06_03.rst @@ -210,10 +210,10 @@ show image |image5| |image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511164936.png -.. |image2| image:: http://image.python-online.cn/20190511164949.png -.. |image3| image:: http://image.python-online.cn/20190511165003.png -.. |image4| image:: http://image.python-online.cn/20190511165013.png -.. |image5| image:: http://image.python-online.cn/20190511165020.png +.. |image1| image:: http://image.iswbm.com/20190511164936.png +.. |image2| image:: http://image.iswbm.com/20190511164949.png +.. |image3| image:: http://image.iswbm.com/20190511165003.png +.. |image4| image:: http://image.iswbm.com/20190511165013.png +.. |image5| image:: http://image.iswbm.com/20190511165020.png .. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_04.md b/source/c06/c06_04.md index 133413c..90c027a 100644 --- a/source/c06/c06_04.md +++ b/source/c06/c06_04.md @@ -19,7 +19,7 @@ plt.subplot(2, 2, 1) ``` 是将当前图像(figure)按 2 行 2 列的布局进行分割,然后取索引为 1 的子图。注意 matlibplot 是完全借鉴了 MATLAB 的思想,所以的起始索引为 1,不像 Python 的起始索引为 0。 -![](http://image.python-online.cn/20190511165103.png) +![](http://image.iswbm.com/20190511165103.png) 他有好几种写法,这里写我在官网学到的几个方法。 @@ -59,7 +59,7 @@ plt.subplot(212) plt.plot(t2, np.cos(2*np.pi*t2), 'r--') plt.show() ``` -![](http://image.python-online.cn/20190511165132.png) +![](http://image.iswbm.com/20190511165132.png) @@ -68,7 +68,7 @@ plt.show() 子图(axes),和子区(subplot)非常相似,一个子图可能是由一个或多个子区域构成的。它比子区更加灵活。 它可以是这样 -![](http://image.python-online.cn/20190511165152.png) +![](http://image.iswbm.com/20190511165152.png) 要实现如上这个效果。常用的有两种方法。 @@ -95,7 +95,7 @@ ax5 = plt.subplot(gs[-1, -2]) 这个比较规则的划分我们举个例子看看。 -![](http://image.python-online.cn/20190511165159.png) +![](http://image.iswbm.com/20190511165159.png) 代码如下: ```python @@ -128,10 +128,10 @@ plt.show() 为什么说,子图的灵活性更高呢,因为它允许把图片放置到图像(figure)中的任何地方(如下图)。所以如果我们想要在一个大图片中嵌套一个小点的图片,我们通过子图(axes)来完成它。 -![](http://image.python-online.cn/20190511165211.png) +![](http://image.iswbm.com/20190511165211.png) 图中的 axes 是如何实现的,刚开始我也有点懵逼,在查阅了官方文档后,我才明白。 -![](http://image.python-online.cn/20190511165221.png) +![](http://image.iswbm.com/20190511165221.png) `left` 是指,离左边界的距离。 `bottom` 是指,离底边的距离。 @@ -145,7 +145,7 @@ plt.show() 同样地,这个我们也来看一个例子。 这个图的亮点,在于中间,多了两个子图,就像往图中贴上了两个插画一样。 -![](http://image.python-online.cn/20190511165229.png) +![](http://image.iswbm.com/20190511165229.png) 那么这个如何实现呢? ```python diff --git a/source/c06/c06_04.rst b/source/c06/c06_04.rst index 7e2b655..3b08843 100755 --- a/source/c06/c06_04.rst +++ b/source/c06/c06_04.rst @@ -204,12 +204,12 @@ subplot,一个是axes。这两个概念将贯穿整个 matplotlib |image8| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511165103.png -.. |image2| image:: http://image.python-online.cn/20190511165132.png -.. |image3| image:: http://image.python-online.cn/20190511165152.png -.. |image4| image:: http://image.python-online.cn/20190511165159.png -.. |image5| image:: http://image.python-online.cn/20190511165211.png -.. |image6| image:: http://image.python-online.cn/20190511165221.png -.. |image7| image:: http://image.python-online.cn/20190511165229.png +.. |image1| image:: http://image.iswbm.com/20190511165103.png +.. |image2| image:: http://image.iswbm.com/20190511165132.png +.. |image3| image:: http://image.iswbm.com/20190511165152.png +.. |image4| image:: http://image.iswbm.com/20190511165159.png +.. |image5| image:: http://image.iswbm.com/20190511165211.png +.. |image6| image:: http://image.iswbm.com/20190511165221.png +.. |image7| image:: http://image.iswbm.com/20190511165229.png .. |image8| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c06/c06_06.md b/source/c06/c06_06.md index b5e274d..cbba0e1 100644 --- a/source/c06/c06_06.md +++ b/source/c06/c06_06.md @@ -10,7 +10,7 @@ 由于我使用的是 Anaconda ,我需要将其安装到我的环境中。首先打开我们的命令行(注意不是一般的CMD),使用windows 的查找入口: -![](http://image.python-online.cn/20190511165315.png) +![](http://image.iswbm.com/20190511165315.png) 然后执行如下命令安装 ``` diff --git a/source/c06/c06_06.rst b/source/c06/c06_06.rst index 6a76a83..2e97d4a 100755 --- a/source/c06/c06_06.rst +++ b/source/c06/c06_06.rst @@ -142,6 +142,6 @@ Jupyter NoteBook 里观察整个变化的过程。 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190511165315.png +.. |image1| image:: http://image.iswbm.com/20190511165315.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_01.md b/source/c07/c07_01.md index ae97523..8abe598 100644 --- a/source/c07/c07_01.md +++ b/source/c07/c07_01.md @@ -479,7 +479,7 @@ nl -b a -n rz -w 3 text 搜索并筛选显示结果。 该命令经常配合管道命令来控制输出。 以下 是常用的选项: -![](http://image.python-online.cn/17-9-20/47469030.jpg) +![](http://image.iswbm.com/17-9-20/47469030.jpg) 【非常好用:不打开文件,直接搜索指定目录下文件内的内容】 ``` @@ -671,7 +671,7 @@ $ ftp ftp> help ``` -![](http://image.python-online.cn/20190705182629.png) +![](http://image.iswbm.com/20190705182629.png) @@ -799,7 +799,7 @@ Linux的分区的过程经历以下几个步骤 3. 挂载:将分区挂载到目录上,才能访问数据 ``` 关于硬件对应的设备文件名,可以参照下图 -![](http://image.python-online.cn/17-10-15/97911325.jpg) +![](http://image.iswbm.com/17-10-15/97911325.jpg) 其中以硬盘为例来说明 ``` diff --git a/source/c07/c07_01.rst b/source/c07/c07_01.rst index dd8dcd4..dab1083 100755 --- a/source/c07/c07_01.rst +++ b/source/c07/c07_01.rst @@ -1940,8 +1940,8 @@ url** 即可。 |image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/17-9-20/47469030.jpg -.. |image2| image:: http://image.python-online.cn/20190705182629.png -.. |image3| image:: http://image.python-online.cn/17-10-15/97911325.jpg +.. |image1| image:: http://image.iswbm.com/17-9-20/47469030.jpg +.. |image2| image:: http://image.iswbm.com/20190705182629.png +.. |image3| image:: http://image.iswbm.com/17-10-15/97911325.jpg .. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_02.md b/source/c07/c07_02.md index 9475501..93fd94a 100644 --- a/source/c07/c07_02.md +++ b/source/c07/c07_02.md @@ -8,11 +8,11 @@ zabbix 的使用架构主要有两种,如果你的监控环境比较单一,可以使用简单的` Server` -> `Agent `,由zabbix-agent 直接上报给 zabbix-server。 -![](http://image.python-online.cn/20190404193811.png) +![](http://image.iswbm.com/20190404193811.png) 如果监控的环境比较复杂,就比如我们的线上的生产环境是分布式集群,最好使用的 ` Server`-> `Proxy` -> `Agent ` -![](http://image.python-online.cn/20190404194416.png) +![](http://image.iswbm.com/20190404194416.png) Zabbix Proxy 可以代替 Zabbix Server 检索客户端的数据,然后把数据汇报给 Zabbix Server, 并且在一定程度上分担了 Zabbix Server 的压力。Zabbix Proxy 可以非常简便的实现了集中式、分布式监控。 @@ -207,7 +207,7 @@ $ systemctl start zabbix-proxy 在使用 Proxy 的时候,需要在 Server 端的web 界面上,先注册一下。 -![](http://image.python-online.cn/20190404201313.png) +![](http://image.iswbm.com/20190404201313.png) @@ -256,9 +256,9 @@ ServerActive=172.20.20.200 # proxy 的ip,用内网和公网vip都可以 按照如下图点击,Event source注意选择`Auto registration` -![](http://image.python-online.cn/20190404205221.png) +![](http://image.iswbm.com/20190404205221.png) -![](http://image.python-online.cn/20190404205617.png) +![](http://image.iswbm.com/20190404205617.png) @@ -266,15 +266,15 @@ ServerActive=172.20.20.200 # proxy 的ip,用内网和公网vip都可以 一般情况下,我们的监控项,不会独立存在,而是依托于模板而存在。所以我们在创建监控项前,要首先创建一个模板。 -![](http://image.python-online.cn/20190404202122.png) +![](http://image.iswbm.com/20190404202122.png) 创建完模板后,点进模板的 items 按钮,正常情况下,这里会有很多监控项,但由于我们还没有创建,所以现在一个也没有。现在你可以自己点右上角(`Create Item`)自己创建一个,创建界面是这样的 -![](http://image.python-online.cn/20190404202353.png) +![](http://image.iswbm.com/20190404202353.png) 这里说一下,zabbix 自带有许多的模板,其实对于一些党规的监控项(说白了,就是zabbix给我们造好了轮子),这些模板已经足够了。这边只截了一小部分。 -![](http://image.python-online.cn/20190404210213.png) +![](http://image.iswbm.com/20190404210213.png) 如果以上这些不能满足你的需要,也没有关系,你也可以自己写脚本获取监控数据。 @@ -288,7 +288,7 @@ UserParameter=openstack.service.status[*],sh /usr/lib/zabbix/externalscripts/isA 那我在 web 界面上配置 监控项,就可以这样写 -![](http://image.python-online.cn/20190404213125.png) +![](http://image.iswbm.com/20190404213125.png) openstack-nova-api 是一个服务名,它将作为一个参数,传递给`isActive.sh` 这个脚本。 @@ -303,11 +303,11 @@ openstack-nova-api 是一个服务名,它将作为一个参数,传递给`isA 你可以通过点击下图的操作,查看最近上报的数据(我这里选择value直接查看值,你可以选择Graph,按图表的形式查看) -![](http://image.python-online.cn/20190404202855.png) +![](http://image.iswbm.com/20190404202855.png) 数据是这样的。 -![](http://image.python-online.cn/20190404202937.png) +![](http://image.iswbm.com/20190404202937.png) @@ -315,11 +315,11 @@ openstack-nova-api 是一个服务名,它将作为一个参数,传递给`isA 假如,我们要监控当 CPU 使用率超过90% 就发个通知邮件,那我们就要新增一个发邮件的动作。 -![](http://image.python-online.cn/20190404203425.png) +![](http://image.iswbm.com/20190404203425.png) 然后点击 `Operations`, 添加触发的动作类型,比如发邮件 -![](http://image.python-online.cn/20190404203805.png) +![](http://image.iswbm.com/20190404203805.png) 其中的HTML样式,也是我网上找来的,我觉得还挺不错,这里也贴出来 @@ -355,7 +355,7 @@ openstack-nova-api 是一个服务名,它将作为一个参数,传递给`isA 你有没有看到,旁边有个`Recovery operation` 按钮,它是说当我们的问题解决后,要让zabbix做些什么,比如我想让 当CPU的使用率降下来后,发一个邮件通知一下。 -![](http://image.python-online.cn/20190404204212.png) +![](http://image.iswbm.com/20190404204212.png) 邮件的 HTML 样式 @@ -391,7 +391,7 @@ openstack-nova-api 是一个服务名,它将作为一个参数,传递给`isA 上面邮件中,有设置了一些颜色的自定义宏,我是在这里设置的。 -![](http://image.python-online.cn/20190404205837.png) +![](http://image.iswbm.com/20190404205837.png) 上面的动作,都是写的发邮件,也可以远程执行脚本,比如,我们监控服务,当服务被人为关闭了,我们可以让Zabbix Agent执行重启服务的命令。 @@ -420,7 +420,7 @@ zabbix ALL=NOPASSWD: ALL 那在ACTION 里如何配置呢,看下图。你可以选择在 agent或者proxy,或者server执行都可以,非常灵活。 -![](http://image.python-online.cn/20190404212423.png) +![](http://image.iswbm.com/20190404212423.png) ### 7.2.3.5 配置发件人 @@ -428,7 +428,7 @@ Zabbix 要能发送邮件,需要配置邮箱发送方。这里以163邮箱为 vim /etc/mail.rc -![](http://image.python-online.cn/20190411205822.png) +![](http://image.iswbm.com/20190411205822.png) 配置完成后,可用以下命令测试一下配置是否有效,若 yyy@163.com 能收到一条来自xxx@163.com 的邮件,则说明配置成功。 @@ -459,7 +459,7 @@ rm -rf /tmp/mailtmp.txt 先添加媒介:Administration - Media Types - Create media type -![](http://image.python-online.cn/20190417202834.png) +![](http://image.iswbm.com/20190417202834.png) 上面用到 sendmail.sh 是一个脚本,需要我们来自己写,内容就是如何将我们的告警内容发送出去。 @@ -477,11 +477,11 @@ chmod +x sendmail.sh 然后再创建用户,并添加邮箱:Administration - Users - Admin - Media -![](http://image.python-online.cn/20190404204534.png) +![](http://image.iswbm.com/20190404204534.png) 在创建用户的时候,要指定用户组,要注意这个用户组的权限一定要有相应主机组的权限,否则无法发送告警邮件。 -![](http://image.python-online.cn/20190605173956.png) +![](http://image.iswbm.com/20190605173956.png) 配置好收件人后 ,发件人呢? @@ -503,7 +503,7 @@ service postfix stop zabbix 自带 mysql 的监控模板,监控项不多,只有 14 项。 -![](http://image.python-online.cn/20190409103417.png) +![](http://image.iswbm.com/20190409103417.png) 这个模板在使用前,需要进行两个配置。 @@ -529,7 +529,7 @@ port= 其实这个`.my.cnf`文件 可以放在任何地方,只要你在 `userparameter_mysql.conf` 能够及时将配置文件路径修改过来就行。如下图所示,你只要修改下方 `HOME` 变量值。 -![](http://image.python-online.cn/20190409104026.png) +![](http://image.iswbm.com/20190409104026.png) 然后记得去 web界面在 该host主机上link到 `Template DB MySQL` 这个模板上。 diff --git a/source/c07/c07_02.rst b/source/c07/c07_02.rst index 07c75fb..513c842 100755 --- a/source/c07/c07_02.rst +++ b/source/c07/c07_02.rst @@ -698,27 +698,27 @@ float ,log, text 等,所以计算存在一定的误差,需留有冗余 |image23| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190404193811.png -.. |image2| image:: http://image.python-online.cn/20190404194416.png -.. |image3| image:: http://image.python-online.cn/20190404201313.png -.. |image4| image:: http://image.python-online.cn/20190404205221.png -.. |image5| image:: http://image.python-online.cn/20190404205617.png -.. |image6| image:: http://image.python-online.cn/20190404202122.png -.. |image7| image:: http://image.python-online.cn/20190404202353.png -.. |image8| image:: http://image.python-online.cn/20190404210213.png -.. |image9| image:: http://image.python-online.cn/20190404213125.png -.. |image10| image:: http://image.python-online.cn/20190404202855.png -.. |image11| image:: http://image.python-online.cn/20190404202937.png -.. |image12| image:: http://image.python-online.cn/20190404203425.png -.. |image13| image:: http://image.python-online.cn/20190404203805.png -.. |image14| image:: http://image.python-online.cn/20190404204212.png -.. |image15| image:: http://image.python-online.cn/20190404205837.png -.. |image16| image:: http://image.python-online.cn/20190404212423.png -.. |image17| image:: http://image.python-online.cn/20190411205822.png -.. |image18| image:: http://image.python-online.cn/20190417202834.png -.. |image19| image:: http://image.python-online.cn/20190404204534.png -.. |image20| image:: http://image.python-online.cn/20190605173956.png -.. |image21| image:: http://image.python-online.cn/20190409103417.png -.. |image22| image:: http://image.python-online.cn/20190409104026.png +.. |image1| image:: http://image.iswbm.com/20190404193811.png +.. |image2| image:: http://image.iswbm.com/20190404194416.png +.. |image3| image:: http://image.iswbm.com/20190404201313.png +.. |image4| image:: http://image.iswbm.com/20190404205221.png +.. |image5| image:: http://image.iswbm.com/20190404205617.png +.. |image6| image:: http://image.iswbm.com/20190404202122.png +.. |image7| image:: http://image.iswbm.com/20190404202353.png +.. |image8| image:: http://image.iswbm.com/20190404210213.png +.. |image9| image:: http://image.iswbm.com/20190404213125.png +.. |image10| image:: http://image.iswbm.com/20190404202855.png +.. |image11| image:: http://image.iswbm.com/20190404202937.png +.. |image12| image:: http://image.iswbm.com/20190404203425.png +.. |image13| image:: http://image.iswbm.com/20190404203805.png +.. |image14| image:: http://image.iswbm.com/20190404204212.png +.. |image15| image:: http://image.iswbm.com/20190404205837.png +.. |image16| image:: http://image.iswbm.com/20190404212423.png +.. |image17| image:: http://image.iswbm.com/20190411205822.png +.. |image18| image:: http://image.iswbm.com/20190417202834.png +.. |image19| image:: http://image.iswbm.com/20190404204534.png +.. |image20| image:: http://image.iswbm.com/20190605173956.png +.. |image21| image:: http://image.iswbm.com/20190409103417.png +.. |image22| image:: http://image.iswbm.com/20190409104026.png .. |image23| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_03.md b/source/c07/c07_03.md index dc75f2c..aeab306 100644 --- a/source/c07/c07_03.md +++ b/source/c07/c07_03.md @@ -245,9 +245,9 @@ namespace 有下面六种 ``` 正在运行的容器 -![](http://image.python-online.cn/17-12-23/44035514.jpg) +![](http://image.iswbm.com/17-12-23/44035514.jpg) 文件夹内容 -![](http://image.python-online.cn/17-12-23/20133481.jpg) +![](http://image.iswbm.com/17-12-23/20133481.jpg) diff --git a/source/c07/c07_03.rst b/source/c07/c07_03.rst index bda175a..edce98a 100755 --- a/source/c07/c07_03.rst +++ b/source/c07/c07_03.rst @@ -292,7 +292,7 @@ namespace 有下面六种 |image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/17-12-23/44035514.jpg -.. |image2| image:: http://image.python-online.cn/17-12-23/20133481.jpg +.. |image1| image:: http://image.iswbm.com/17-12-23/44035514.jpg +.. |image2| image:: http://image.iswbm.com/17-12-23/20133481.jpg .. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_04.md b/source/c07/c07_04.md index f1b7a16..7337b1f 100644 --- a/source/c07/c07_04.md +++ b/source/c07/c07_04.md @@ -33,7 +33,7 @@ CMD ["/hello"] # 当容器启动后执行hello文件内容 ``` 而hello的内容,也很简单就是打印这样一段内容 -![](http://image.python-online.cn/17-12-23/49304868.jpg) +![](http://image.iswbm.com/17-12-23/49304868.jpg) ## 三、理解base镜像 ``` @@ -43,7 +43,7 @@ CMD ["/hello"] # 当容器启动后执行hello文件内容 这个怎么理解呢?很重要。 比如说,我们下载的CentOS镜像 -![](http://image.python-online.cn/17-12-23/36753853.jpg) +![](http://image.iswbm.com/17-12-23/36753853.jpg) 你一定很纳闷。怎么才200M不到?我们平时下载的都几个G的。 @@ -66,7 +66,7 @@ ADD centos-7.2.1511-docker.tar.xz / CMD ["/bin/bash"] ``` 对于容器来说,他底层使用的Host的kernel,所以假如我们的容器是CentOS的版本(内核是3.x),当我们把容器放到Ubuntu(内核是4.x)上使用是,他实际上使用的是Ubuntu的内核。 -![](http://image.python-online.cn/17-12-23/70731434.jpg) +![](http://image.iswbm.com/17-12-23/70731434.jpg) 容器只能使用 Host 的 kernel,并且不能修改。 所有容器都共用 host 的 kernel,在容器中没办法对 kernel 升级。如果容器对 kernel 版本有要求(比如应用只能在某个 kernel 版本下运行),则不建议用容器,这种场景虚拟机可能更合适。 @@ -110,7 +110,7 @@ RUN yum install -y tree ``` $ docker build -t centos-tree . ``` -![](http://image.python-online.cn/17-12-24/39646473.jpg) +![](http://image.iswbm.com/17-12-24/39646473.jpg) 第四步,在原始的Dockerfile上添加内容 ``` FROM centos @@ -121,7 +121,7 @@ COPY testfile / ``` docker build -t centos-tree-testfile . ``` -![](http://image.python-online.cn/17-12-24/85625734.jpg) +![](http://image.iswbm.com/17-12-24/85625734.jpg) 注意点,镜像的缓存要求构建的命令顺序要严格一致,只要有一行命令不同,缓存就会失效,即使最后的容器内容完全一致,也无法使用缓存。 比如,我们把第二次的Dockerfile改成如下,也是需要重新构建 @@ -134,13 +134,13 @@ RUN yum install -y tree 当我们build一个新的镜像,也许会有很多的很复杂的步骤,如果一次性build很有可能遇到各种意外情况,导致build失败,这时候,我们就需要进行调试,找出失败原因,修改Dockerfile,最终成功。 那么如何调试?在我们build镜像的时候,每一步都会有一个镜像id,通过这个id我们就可以进入该层镜像。 -![](http://image.python-online.cn/17-12-24/21127582.jpg) +![](http://image.iswbm.com/17-12-24/21127582.jpg) 先来看看第二步的结果,`/`目录下没有testfile文件 ``` $ docker run -it e316b390cf2a ``` 再来看看第三步,`/`目录下已经有testfile文件 -![](http://image.python-online.cn/17-12-24/42825662.jpg) +![](http://image.iswbm.com/17-12-24/42825662.jpg) 友情提示 ``` @@ -215,13 +215,13 @@ CMD:两个功能 exec:CMD ["/bin/echo", "hello world"] bash:CMD echo "hello world" ``` -![](http://image.python-online.cn/17-12-24/80077038.jpg) +![](http://image.iswbm.com/17-12-24/80077038.jpg) ``` 2. 给ENTRYPOINT传递参数 - 若run时,指定了参数,CMD传递的参数同样被覆盖,而ENTRYPOINT永远不会被覆盖。 - 注意:如果要传递参数,必须使用exec格式,诸如CMD [""] ``` -![](http://image.python-online.cn/17-12-24/98318652.jpg) +![](http://image.iswbm.com/17-12-24/98318652.jpg) **推荐用法** ``` diff --git a/source/c07/c07_04.rst b/source/c07/c07_04.rst index 4d5c5e7..fce8b33 100755 --- a/source/c07/c07_04.rst +++ b/source/c07/c07_04.rst @@ -363,14 +363,14 @@ CMD:两个功能 |image10| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/17-12-23/49304868.jpg -.. |image2| image:: http://image.python-online.cn/17-12-23/36753853.jpg -.. |image3| image:: http://image.python-online.cn/17-12-23/70731434.jpg -.. |image4| image:: http://image.python-online.cn/17-12-24/39646473.jpg -.. |image5| image:: http://image.python-online.cn/17-12-24/85625734.jpg -.. |image6| image:: http://image.python-online.cn/17-12-24/21127582.jpg -.. |image7| image:: http://image.python-online.cn/17-12-24/42825662.jpg -.. |image8| image:: http://image.python-online.cn/17-12-24/80077038.jpg -.. |image9| image:: http://image.python-online.cn/17-12-24/98318652.jpg +.. |image1| image:: http://image.iswbm.com/17-12-23/49304868.jpg +.. |image2| image:: http://image.iswbm.com/17-12-23/36753853.jpg +.. |image3| image:: http://image.iswbm.com/17-12-23/70731434.jpg +.. |image4| image:: http://image.iswbm.com/17-12-24/39646473.jpg +.. |image5| image:: http://image.iswbm.com/17-12-24/85625734.jpg +.. |image6| image:: http://image.iswbm.com/17-12-24/21127582.jpg +.. |image7| image:: http://image.iswbm.com/17-12-24/42825662.jpg +.. |image8| image:: http://image.iswbm.com/17-12-24/80077038.jpg +.. |image9| image:: http://image.iswbm.com/17-12-24/98318652.jpg .. |image10| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_05.md b/source/c07/c07_05.md index 1440b0d..b31e72b 100644 --- a/source/c07/c07_05.md +++ b/source/c07/c07_05.md @@ -118,7 +118,7 @@ $ docker run -d -p 8500:8500 -h consul --name consul progrium/consul -server -bo # 这里的 eth1 是可以和 192.168.2.55 通信的网卡 --cluster-store=consul://192.168.2.55:8500 --cluster-advertise=eth1:2376 ``` -![](http://image.python-online.cn/18-1-28/92519416.jpg) +![](http://image.iswbm.com/18-1-28/92519416.jpg) 然后重启 ``` @@ -127,7 +127,7 @@ systemctl restart docker.service ``` 在浏览器上输入地址 -![](http://image.python-online.cn/18-1-28/37395940.jpg) +![](http://image.iswbm.com/18-1-28/37395940.jpg) 至此,`Consul` 安装成功。 diff --git a/source/c07/c07_05.rst b/source/c07/c07_05.rst index 4ca28c1..23440b7 100755 --- a/source/c07/c07_05.rst +++ b/source/c07/c07_05.rst @@ -322,8 +322,8 @@ Socket发些“奇怪”的数据。 |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/18-1-28/92519416.jpg -.. |image2| image:: http://image.python-online.cn/18-1-28/37395940.jpg +.. |image1| image:: http://image.iswbm.com/18-1-28/92519416.jpg +.. |image2| image:: http://image.iswbm.com/18-1-28/37395940.jpg .. |image3| image:: https://i.loli.net/2018/01/28/5a6de8702428c.png .. |image4| image:: https://i.loli.net/2018/01/28/5a6de73776390.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_10.md b/source/c07/c07_10.md index ff003a0..64bee83 100644 --- a/source/c07/c07_10.md +++ b/source/c07/c07_10.md @@ -12,7 +12,7 @@ master 与 节点间使用ssh通信,也不需要像salt那样需要在客户 无意中使用 pip search 进行搜索,还真的有 ansible-api -![](http://image.python-online.cn/20190716111523.png) +![](http://image.iswbm.com/20190716111523.png) 这是 2018 年10月份才开始的项目,目前来说成熟度还不够。 @@ -120,7 +120,7 @@ ansible-api 会调用 ansible 库的命令,这个过程不能指定 ansible.cf 然后由于原生的 ansible-api 的bug,需要修改代码,在如下函数位置(`/usr/local/python3/lib/python3.7/site-packages/ansible_api/callback.py`)添加一个参数 -![](http://image.python-online.cn/20190716112113.png) +![](http://image.iswbm.com/20190716112113.png) 通过执行命令,即可开启 ansible server @@ -163,13 +163,13 @@ shell :echo -n 'wangbmlocalhostbackup_info.ymlwangbm'|md5sum |cut -d ' ' -f1 发送了请求后,返回的结果如下 -![](http://image.python-online.cn/20190716112824.png) +![](http://image.iswbm.com/20190716112824.png) rc 为0,表示所有节点都没有出现 fatal 致命错误(有设置 ignore_errors 的错误也会返回0). rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级失败。 -![](http://image.python-online.cn/20190716112838.png) +![](http://image.iswbm.com/20190716112838.png) --- diff --git a/source/c07/c07_10.rst b/source/c07/c07_10.rst index 1620ef6..5d1f203 100644 --- a/source/c07/c07_10.rst +++ b/source/c07/c07_10.rst @@ -189,9 +189,9 @@ rc 为非0,表示有 fatal 致命错误,说明有部分节点部署/升级 |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190716111523.png -.. |image2| image:: http://image.python-online.cn/20190716112113.png -.. |image3| image:: http://image.python-online.cn/20190716112824.png -.. |image4| image:: http://image.python-online.cn/20190716112838.png +.. |image1| image:: http://image.iswbm.com/20190716111523.png +.. |image2| image:: http://image.iswbm.com/20190716112113.png +.. |image3| image:: http://image.iswbm.com/20190716112824.png +.. |image4| image:: http://image.iswbm.com/20190716112838.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_11.md b/source/c07/c07_11.md index d971a32..ac60ad7 100644 --- a/source/c07/c07_11.md +++ b/source/c07/c07_11.md @@ -66,7 +66,7 @@ kubectl get namespace K8s 角色详解 -![](http://image.python-online.cn/20190907162015.png) +![](http://image.iswbm.com/20190907162015.png) 其中 Controller 还分为几种: diff --git a/source/c07/c07_11.rst b/source/c07/c07_11.rst index 68c4150..ffe862f 100644 --- a/source/c07/c07_11.rst +++ b/source/c07/c07_11.rst @@ -80,6 +80,6 @@ K8s 角色详解 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190907162015.png +.. |image1| image:: http://image.iswbm.com/20190907162015.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_12.md b/source/c07/c07_12.md index 099d935..577fcd2 100644 --- a/source/c07/c07_12.md +++ b/source/c07/c07_12.md @@ -360,7 +360,7 @@ $ rpm -e glibc-common-2.17-196.el7_4.2.x86_64 经常在安装一个包的时候,会报如下的错误,找不到某 so 文件 -![](http://image.python-online.cn/20191219152328.png) +![](http://image.iswbm.com/20191219152328.png) 如果是缺一个包,那我们安装它就行了,缺 so 文件,那咋弄? @@ -389,7 +389,7 @@ $ yum history list python-nova-tests $ rpm -qa --last | grep python-nova-tests ``` -![](http://image.python-online.cn/20191225173340.png) +![](http://image.iswbm.com/20191225173340.png) @@ -399,7 +399,7 @@ $ rpm -qa --last | grep python-nova-tests $ yumdb info python-nova-tests ``` -![](http://image.python-online.cn/20191225175350.png) +![](http://image.iswbm.com/20191225175350.png) diff --git a/source/c07/c07_12.rst b/source/c07/c07_12.rst index 0a34867..d489a16 100644 --- a/source/c07/c07_12.rst +++ b/source/c07/c07_12.rst @@ -392,8 +392,8 @@ yum-utils 使用 |image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191219152328.png -.. |image2| image:: http://image.python-online.cn/20191225173340.png -.. |image3| image:: http://image.python-online.cn/20191225175350.png +.. |image1| image:: http://image.iswbm.com/20191219152328.png +.. |image2| image:: http://image.iswbm.com/20191225173340.png +.. |image3| image:: http://image.iswbm.com/20191225175350.png .. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_15.md b/source/c07/c07_15.md index 0efaa7a..9860a01 100644 --- a/source/c07/c07_15.md +++ b/source/c07/c07_15.md @@ -19,7 +19,7 @@ Galera 是一个MySQL(也支持MariaDB,Percona) 的同步多主集群软件, 5. 用户连接集群后,使用跟 MySQL 基本一致。 6. 不会写binlog -关于Galera是如何做到多主的,可以借助这张图来看看![](http://image.python-online.cn/20191213162259.png) +关于Galera是如何做到多主的,可以借助这张图来看看![](http://image.iswbm.com/20191213162259.png) 来源:https://blog.csdn.net/weixin_42867972/article/details/84198696 diff --git a/source/c07/c07_15.rst b/source/c07/c07_15.rst index 8037826..82889cc 100644 --- a/source/c07/c07_15.rst +++ b/source/c07/c07_15.rst @@ -222,6 +222,6 @@ wsrep_flow_control_sent 和 wsrep_local_recv_queue_avg |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191213162259.png +.. |image1| image:: http://image.iswbm.com/20191213162259.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c07/c07_16.md b/source/c07/c07_16.md index b63f093..2778025 100644 --- a/source/c07/c07_16.md +++ b/source/c07/c07_16.md @@ -4,7 +4,7 @@ ## 7.16.1 如何查看并计算 CPU 使用率 -有很多的工具可以查看CPU使用率,使用Linux 自带的 top 是最常用的方式。![](http://image.python-online.cn/20191220202103.png) +有很多的工具可以查看CPU使用率,使用Linux 自带的 top 是最常用的方式。![](http://image.iswbm.com/20191220202103.png) 图中标注解释 @@ -20,13 +20,13 @@ - si,software interrupt: 处理软件中断的CPU时间 - st:这个虚拟机被hypervisor偷去的CPU时间(译注:如果当前处于一个hypervisor下的vm,实际上hypervisor也是要消耗一部分CPU处理时间的)。 -若你觉得这样不够直观,可以按 `t` ,切换显示![](http://image.python-online.cn/20191220203403.png) +若你觉得这样不够直观,可以按 `t` ,切换显示![](http://image.iswbm.com/20191220203403.png) -若这是多核机器,输入 `1` ,可以显示单核CPU使用情况。![](http://image.python-online.cn/20191220202408.png) +若这是多核机器,输入 `1` ,可以显示单核CPU使用情况。![](http://image.iswbm.com/20191220202408.png) -若你觉得这样不够直观,可以按 `t`切换成直方图显示![](http://image.python-online.cn/20191220203205.png) +若你觉得这样不够直观,可以按 `t`切换成直方图显示![](http://image.iswbm.com/20191220203205.png) 标为 `2` 的 栏目,表示各个进程的 cpu 使用率,比如第一个进程 348% 就是占满了3.48个核。 diff --git a/source/c07/c07_16.rst b/source/c07/c07_16.rst index b11e5ac..7eb96f7 100644 --- a/source/c07/c07_16.rst +++ b/source/c07/c07_16.rst @@ -86,9 +86,9 @@ |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191220202103.png -.. |image2| image:: http://image.python-online.cn/20191220203403.png -.. |image3| image:: http://image.python-online.cn/20191220202408.png -.. |image4| image:: http://image.python-online.cn/20191220203205.png +.. |image1| image:: http://image.iswbm.com/20191220202103.png +.. |image2| image:: http://image.iswbm.com/20191220203403.png +.. |image3| image:: http://image.iswbm.com/20191220202408.png +.. |image4| image:: http://image.iswbm.com/20191220203205.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_02.md b/source/c08/c08_02.md index 30a0018..ed1b4a2 100644 --- a/source/c08/c08_02.md +++ b/source/c08/c08_02.md @@ -227,7 +227,7 @@ Sriov虚拟机在openstack原生是不支持挂卸网卡操作的,即nova inte 1、使用neutron port-show ,查看并记录原 port 的 `binding:profile` 信息,如果有多个port,把每个port的信息都记录下来。 -![](http://image.python-online.cn/20190529202132.png) +![](http://image.iswbm.com/20190529202132.png) 2、nova interface-detach卸载原来的port。 @@ -252,7 +252,7 @@ nova interface-attach 5a1c1828-4190-43fa-8e05-ae51b0196656 --port-id 3f0668f4-4b select * from pci_devices where instance_uuid='5a1c1828-4190-43fa-8e05-ae51b0196656'; ``` -![](http://image.python-online.cn/20190529202440.png) +![](http://image.iswbm.com/20190529202440.png) ## 附录:参考文档 diff --git a/source/c08/c08_02.rst b/source/c08/c08_02.rst index 14b0539..18e56ee 100755 --- a/source/c08/c08_02.rst +++ b/source/c08/c08_02.rst @@ -314,7 +314,7 @@ port-update命令不支持,只能使用curl,需要修改port-id及binding:pr .. |image4| image:: https://i.loli.net/2018/01/19/5a61c1cf51b58.png .. |image5| image:: https://i.loli.net/2018/01/19/5a61c1faac447.png .. |image6| image:: https://i.loli.net/2018/01/19/5a61c246451e7.png -.. |image7| image:: http://image.python-online.cn/20190529202132.png -.. |image8| image:: http://image.python-online.cn/20190529202440.png +.. |image7| image:: http://image.iswbm.com/20190529202132.png +.. |image8| image:: http://image.iswbm.com/20190529202440.png .. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_03.md b/source/c08/c08_03.md index 23e8cb0..18a1d18 100644 --- a/source/c08/c08_03.md +++ b/source/c08/c08_03.md @@ -457,7 +457,7 @@ shutdown -h now 通过 guestfish 工具可以实现不用创建虚拟机就可以修改镜像里的文件内容。 -![](http://image.python-online.cn/20190827200522.png) +![](http://image.iswbm.com/20190827200522.png) ## 8.3.3 KVM 镜像快照 @@ -470,9 +470,9 @@ $ virsh blockcommit ws_controller01 hda --active --verbose --pivot -![](http://image.python-online.cn/20191211174659.png) +![](http://image.iswbm.com/20191211174659.png) -![](http://image.python-online.cn/20191211174956.png) +![](http://image.iswbm.com/20191211174956.png) ## 附录:参考文档 diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index 4899e3f..758198f 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -535,8 +535,8 @@ CentOS6 创建快照前需要先删除\ ``75-persistent-net-generator.rules`` .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: https://i.loli.net/2018/01/27/5a6c34714685d.png .. |image2| image:: https://i.loli.net/2018/01/27/5a6c34b14c6ec.png -.. |image3| image:: http://image.python-online.cn/20190827200522.png -.. |image4| image:: http://image.python-online.cn/20191211174659.png -.. |image5| image:: http://image.python-online.cn/20191211174956.png +.. |image3| image:: http://image.iswbm.com/20190827200522.png +.. |image4| image:: http://image.iswbm.com/20191211174659.png +.. |image5| image:: http://image.iswbm.com/20191211174956.png .. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_04.md b/source/c08/c08_04.md index 12ade3c..5aaac02 100644 --- a/source/c08/c08_04.md +++ b/source/c08/c08_04.md @@ -12,7 +12,7 @@ 为了让你多了解一些云计算的内容,我想着写这么一篇文章,介绍一下我从事的领域,同时也对云计算和虚拟化这块入门级知识做一个梳理,如果刚好你也想进入这个领域,这份入门通识指南,应该挺适合你的。 -![](http://image.python-online.cn/20190714161353.png) +![](http://image.iswbm.com/20190714161353.png) ## 8.4.1 云计算是什么? @@ -24,7 +24,7 @@ 云,就是将这些零散实体资源变成一个巨大无比的资源池子,有了这个池子,做为个人用户,你不再需要自己你买一个电脑放在家里,做为小型公司,你不需要自己整一个机房,花很多的人力和设备成本去运营这些基础设施。一旦你需要,你就向池子拥有者申请即可。这极大的提高了资源的利用率,以及分配的灵活性。 -![](http://image.python-online.cn/20190716004341.png) +![](http://image.iswbm.com/20190716004341.png) 还有人说,云就像是天上的云一样,聚焦的水汽多了就会下雨,落到地面的雨水又会蒸发到天上,继续等待下一次下雨。云计算里的云,正如大自然里的云一样,可以实现资源的循环利用。你在公有云提供商那里,购买了一年的云主机,一年后资源被回收,可以再分配给其他人使用。 @@ -109,7 +109,7 @@ QEMU是一套由Fabrice Bellard所编写的模拟处理器的自由软件。Qemu 说到了QEMU,其实它也是一个虚拟化软件。作用是什么呢,它相当于一个路由器,当Guest OS的内核想要操作物理硬件时,必须先经由Qemu转发,将操作指令转给真实的硬件。由于所有的指令都要从Qemu里面过一手,因而性能比较差。 -![](http://image.python-online.cn/FjlPaQLTiYCde92WhurWsRx6z8CK) +![](http://image.iswbm.com/FjlPaQLTiYCde92WhurWsRx6z8CK) **总结** @@ -131,7 +131,7 @@ QEMU是一套由Fabrice Bellard所编写的模拟处理器的自由软件。Qemu 目前,libvirt 已经成为使用最为广泛的对各种虚拟机进行管理的工具和应用程序接口(API),而且一些常用的虚拟机管理工具(如virsh、virt-install、virt-manager等)和云计算框架平台(如OpenStack、OpenNebula、Eucalyptus等)都在底层使用libvirt的应用程序接口。 -![](http://image.python-online.cn/20190716005951.png) +![](http://image.iswbm.com/20190716005951.png) ## 8.4.5 虚拟化分类 @@ -167,7 +167,7 @@ QEMU是一套由Fabrice Bellard所编写的模拟处理器的自由软件。Qemu 根据虚拟化层是直接位于硬件之上还是位于操作系统之上,可以分为 Type 1 虚拟化和 Type 2 虚拟化。 -![](http://image.python-online.cn/20190714141644.png) +![](http://image.iswbm.com/20190714141644.png) Type 1:Xen,VMWare ESX @@ -408,7 +408,7 @@ virsh start guest_vm 有了OpenStack,你可以使用 Horizon提供的界面进行虚拟机的管理 -![来源网络,侵删](http://image.python-online.cn/20190714151716.png) +![来源网络,侵删](http://image.iswbm.com/20190714151716.png) 也可以使用nova 的 cli 命令进行创建。 diff --git a/source/c08/c08_04.rst b/source/c08/c08_04.rst index e57be47..ca07413 100644 --- a/source/c08/c08_04.rst +++ b/source/c08/c08_04.rst @@ -453,7 +453,7 @@ OpenStack 有了OpenStack,你可以使用 Horizon提供的界面进行虚拟机的管理 -.. figure:: http://image.python-online.cn/20190714151716.png +.. figure:: http://image.iswbm.com/20190714151716.png :alt: 来源网络,侵删 来源网络,侵删 @@ -488,11 +488,11 @@ OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到 |image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190714161353.png -.. |image2| image:: http://image.python-online.cn/20190716004341.png -.. |image3| image:: http://image.python-online.cn/FjlPaQLTiYCde92WhurWsRx6z8CK -.. |image4| image:: http://image.python-online.cn/20190716005951.png -.. |image5| image:: http://image.python-online.cn/20190714141644.png +.. |image1| image:: http://image.iswbm.com/20190714161353.png +.. |image2| image:: http://image.iswbm.com/20190716004341.png +.. |image3| image:: http://image.iswbm.com/FjlPaQLTiYCde92WhurWsRx6z8CK +.. |image4| image:: http://image.iswbm.com/20190716005951.png +.. |image5| image:: http://image.iswbm.com/20190714141644.png .. |image6| image:: https://i.loli.net/2019/02/25/5c73e6160764a.png .. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_05.md b/source/c08/c08_05.md index 050988a..7a057ea 100644 --- a/source/c08/c08_05.md +++ b/source/c08/c08_05.md @@ -8,13 +8,13 @@ 生成xml,准备网络(plug_vif)创建domain 是在 `_create_domain_and_network` 这个函数中 -![](http://image.python-online.cn/20190526144846.png) +![](http://image.iswbm.com/20190526144846.png) 这个函数,只有在 `spawn` 、`_hard_reboot` 和 `finish_migration` 才会执行。 通过libvirt的接口创建虚拟机 -![](http://image.python-online.cn/20190529135942.png) +![](http://image.iswbm.com/20190529135942.png) ## 8.5.2 同步虚机电源状态 @@ -35,11 +35,11 @@ class ComputeManager(manager.Manager): 默认是 600s,就是10分钟同步一次数据库的状态。 -![](http://image.python-online.cn/20190530204839.png) +![](http://image.iswbm.com/20190530204839.png) 具体的函数是在这个 `_sync_instance_power_state` -![](http://image.python-online.cn/20190530204505.png) +![](http://image.iswbm.com/20190530204505.png) 注意这个函数只有两个地方会调用 @@ -47,7 +47,7 @@ class ComputeManager(manager.Manager): 还有一个是由 libvirt 触发的`lifecycle event` 事件,这个只要在虚拟机在hypervisor层发生状态变动时,就会调用。 -![](http://image.python-online.cn/20190530210912.png) +![](http://image.iswbm.com/20190530210912.png) ## 8.5.3 创建快照代码解读? @@ -103,19 +103,19 @@ nova 提供了一个配置项:notify_on_state_change,本意是想,如果 nova-api 的入口如下 -![](http://image.python-online.cn/20190508110723.png) +![](http://image.iswbm.com/20190508110723.png) 接着会调用 nova/compute/api.py -![](http://image.python-online.cn/20190508111109.png) +![](http://image.iswbm.com/20190508111109.png) 在nova-compute 层面:nova/compute/manager.py:_snapshot_instance() -![](http://image.python-online.cn/20190508095028.png) +![](http://image.iswbm.com/20190508095028.png) 接下来会调用 `nova/virt/libvirt/driver.py:snapshot()` -![](http://image.python-online.cn/20190508111527.png) +![](http://image.iswbm.com/20190508111527.png) 先获取imagebackend的类型,然后找到对应的backend @@ -130,11 +130,11 @@ snapshot_backend = self.image_backend.snapshot(instance, 接下来,会调用对应的imagebackend的`snapshot_extract` 方法。 -![](http://image.python-online.cn/FhRPy4B1xEI9SfoD2RcunJl15ZE3) +![](http://image.iswbm.com/FhRPy4B1xEI9SfoD2RcunJl15ZE3) `snapshot_extract` 方法最终会调用`nova/virt/images.py:_convert_image()` ,可以看出其实底层调用的是 `qemu-img` 提供的`convert` 接口。 -![](http://image.python-online.cn/FuyMWZS6HF4g3rPwTlLcereZxg4L) +![](http://image.iswbm.com/FuyMWZS6HF4g3rPwTlLcereZxg4L) 如果是qcow2的backend,不是调用这边,而是调用 `nova/virt/libvirt/utils.py:extract_snapshot()` @@ -152,11 +152,11 @@ grep -E "Start executing commands|End executing commands" /var/log/nova/nova-com 在`libvirt_utils.get_disk_type_from_path` 里没有相应的修改,导致返回的是lvm。 -![](http://image.python-online.cn/FnJA8RNIvJN2lAEXbKtJDpOLg1vg) +![](http://image.iswbm.com/FnJA8RNIvJN2lAEXbKtJDpOLg1vg) 后面的imagebackend也相应的变成 lvm的 -![](http://image.python-online.cn/FnGyI8jCQFLCGi0pGVmI3SV6pDrv) +![](http://image.iswbm.com/FnGyI8jCQFLCGi0pGVmI3SV6pDrv) 然后会进入 lvm这个backend的init函数。由于`path` 是`/dev/sdb` 并不是一个lv,所以这边会报错。 @@ -170,7 +170,7 @@ compute的资源上报,是在 `nova/compute/resource_tracker.py:_init_compute_ 从宿主机上获取数据:`update_available_resource` 函数下的 `resources = self.driver.get_available_resource(self.nodename)` 其调用的函数是`virt/libvirt/driver.py` 里的 `get_available_resource` 函数 -![](http://image.python-online.cn/FrbE6oEZ3vtTWwDfMNQ16MGi6SWr) +![](http://image.iswbm.com/FrbE6oEZ3vtTWwDfMNQ16MGi6SWr) 从数据库获取旧数据 `self.compute_node = self._get_compute_node(context)` @@ -194,31 +194,31 @@ compute的资源上报,是在 `nova/compute/resource_tracker.py:_init_compute_ 虚拟机创建资源的代码是在 `nova/compute/manager.py` 里的 `_build_resource()` 里 -![](http://image.python-online.cn/20190906093751.png) +![](http://image.iswbm.com/20190906093751.png) 这里只讲一下 `network_info`,其他的我都忽略了。 -![](http://image.python-online.cn/20190906094703.png) +![](http://image.iswbm.com/20190906094703.png) 在这个函数`_allocate_network()` ,主要做如下两件事。 -![](http://image.python-online.cn/20190906214536.png) +![](http://image.iswbm.com/20190906214536.png) 继续进入这个函数,如果不指定重试次数,默认是一次。 -![](http://image.python-online.cn/20190906100119.png) +![](http://image.iswbm.com/20190906100119.png) 接下来就是调用我们非常熟悉的 `_create_port_minimal` 函数去创建port -![](http://image.python-online.cn/20190906213038.png) +![](http://image.iswbm.com/20190906213038.png) 如果你全局搜索,你会发现,在 network/rpc.py 下也有这个函数,这个是通过 nova interface-attach 为虚拟机添加网卡,从 nova-api 那边发起的 rpc 请求,才会走到这里 -![](http://image.python-online.cn/20190906210825.png) +![](http://image.iswbm.com/20190906210825.png) 上面创建完port了,后面最后一个函数,我已经标出来了,开始组装获取 network_info 对象。 -![](http://image.python-online.cn/20190906213823.png) +![](http://image.iswbm.com/20190906213823.png) @@ -238,11 +238,11 @@ network_info = compute_utils.get_nw_info_for_instance(instance) 1. 如果有请求req(在nova-api里),可以使用这种 -![](http://image.python-online.cn/20190426153322.png) +![](http://image.iswbm.com/20190426153322.png) 2. 其他地方可以使用这种 -![](http://image.python-online.cn/20190426152148.png) +![](http://image.iswbm.com/20190426152148.png) ## 8.5.9 指定ip时检查allocation_pools @@ -250,17 +250,17 @@ network_info = compute_utils.get_nw_info_for_instance(instance) 先来看看,port 是如何创建的 -![](http://image.python-online.cn/20190526141815.png) +![](http://image.iswbm.com/20190526141815.png) 若要解决这个问题,可以参考原生代码中,在为子网添加allocation_pool时,验证是否合法的的逻辑,代码如下 -![](http://image.python-online.cn/20190526142453.png) +![](http://image.iswbm.com/20190526142453.png) 然后在 `neutron\neutron\db\ipam_pluggable_backend.py` 文件中添加我们检查 ip是否在 allocation_pools 中的逻辑代码。 -![](http://image.python-online.cn/20190526134519.png) +![](http://image.iswbm.com/20190526134519.png) ```python # 代码如下:方便复制 @@ -283,15 +283,15 @@ network_info = compute_utils.get_nw_info_for_instance(instance) 然后还要定义一个异常类型 -![](http://image.python-online.cn/20190526141226.png) +![](http://image.iswbm.com/20190526141226.png) 若指定的ip在allocation pool 里,则正常创建,若不在allocation 里,就会在 nova-compute 日志中报错。 -![](http://image.python-online.cn/20190526134543.png) +![](http://image.iswbm.com/20190526134543.png) 可以发现我们的ip 172.20.22.64 并不在子网的allocation pool,理所当然在nova的日志中可以看到相应的报错。 -![](http://image.python-online.cn/20190526134618.png) +![](http://image.iswbm.com/20190526134618.png) ## 8.5.10 attach port时ip占用提示 @@ -305,7 +305,7 @@ nova-api 返回的结果令人无法理解: 究其原因,是 nova 在调用neutron的api 创建port时,如果ip已被占用,必须neutron会抛出 IpAddressAlreadyAllocated,而在 neutronclient 只有 IpAddressInUseClient 的异常,并不匹配,在neutronclient 端与neutron 对应的异常应该为 IpAddressAlreadyAllocatedClient 。 -![](http://image.python-online.cn/20190526140213.png) +![](http://image.iswbm.com/20190526140213.png) 如何让nova-api能够返回具体的错误信息呢? @@ -315,39 +315,39 @@ nova-api 返回的结果令人无法理解: 并且在nova 创建port的代码处,捕获这个异常 -![](http://image.python-online.cn/20190526140301.png) +![](http://image.iswbm.com/20190526140301.png) 这种要改两个组件,而且要将neutronclient 的代码也管理起来,较为麻烦 一种是,只改neutron,在neutron/ipam/exceptions.py 添加一个与 neutronclient 相对应的异常。 -![](http://image.python-online.cn/20190526140315.png) +![](http://image.iswbm.com/20190526140315.png) 然后修改 neutron/ipam/drivers/neutrondb_ipam/drivers.py 修改异常类型 -![](http://image.python-online.cn/20190526140336.png) +![](http://image.iswbm.com/20190526140336.png) 通过 postman 进行模拟,已经可以返回具体的信息 -![](http://image.python-online.cn/20190526140410.png) +![](http://image.iswbm.com/20190526140410.png) 另附:neutron 是如何判断ip是否已经占用?代码如下 -![](http://image.python-online.cn/20190526143235.png) +![](http://image.iswbm.com/20190526143235.png) ## 8.5.11 nova-compute 如何启动的? 从 /usr/bin/nova-compute 这个文件可以了解到nova-compute的入口是 `nova.cmd.compute:main()` -![](http://image.python-online.cn/20190526205152.png) +![](http://image.iswbm.com/20190526205152.png) 从这个入口进去,会开启一个 `nova-compute` 的服务。 -![](http://image.python-online.cn/20190526165007.png) +![](http://image.iswbm.com/20190526165007.png) 当调用 service.Service.create 时(create 是一个工厂函数),实际是返回实例化的 service.Service 对象。当没有传入 manager 时,就会以binary 里的为准。比如binary 是` nova-compute`,那manager_cls 就是 `compute_manager`,对应的manager 导入路径,会从配置里读取。 -![](http://image.python-online.cn/20190526204328.png) +![](http://image.iswbm.com/20190526204328.png) ## 8.5.12 往 spec_obj 里添加对象 @@ -356,7 +356,7 @@ nova-api 返回的结果令人无法理解: - host_state:包含每台 host 的所有信息 - spec_obj:包含创建虚拟机请求的所有信息 -![](http://image.python-online.cn/20191008173211.png) +![](http://image.iswbm.com/20191008173211.png) 有时候,spec_obj 里并没有我们想要的信息(比如虚拟机的 metadata),这时候,我们就要手动添加。 @@ -366,11 +366,11 @@ nova-api 返回的结果令人无法理解: 要往这个对象加属性,第一步是要定义这个字段。 -![](http://image.python-online.cn/20191008174046.png) +![](http://image.iswbm.com/20191008174046.png) 往 instance_fields 追加属性名,完了后,这个属性会出现在 `request_spec['instance_properties']` 里 -![](http://image.python-online.cn/20191008210127.png) +![](http://image.iswbm.com/20191008210127.png) 最后在 `from_primitives` 这个函数里,把这个新属性赋值给 request_spec 对象。 @@ -386,13 +386,13 @@ spec.metadata = request_spec['instance_properties'].get('metadata', {}) 在 nova-api 接收请求处。 -![](http://image.python-online.cn/20190529203441.png) +![](http://image.iswbm.com/20190529203441.png) -![](http://image.python-online.cn/20190529215953.png) +![](http://image.iswbm.com/20190529215953.png) 对 network_info 进行解析,然后塞给 request 对象返回。 -![](http://image.python-online.cn/20190529215825.png) +![](http://image.iswbm.com/20190529215825.png) ## 8.5.14 HTTP 状态码 @@ -512,7 +512,7 @@ isolate 然后为这个LV,创建一个软连接,通过 `df -Th` 可以看到 这个LV 挂载到 一个目录下。这个目录下有一个名为 disk 的qcow2文件,而这个qcow2 文件的backing file 是指向一个 base 镜像文件(raw格式)。 -![](http://image.python-online.cn/20190627213044.png) +![](http://image.iswbm.com/20190627213044.png) ## 8.5.16 独立磁盘与LVM @@ -522,7 +522,7 @@ isolate 缺点:无法像 LVM 存储池那样,做到精准而灵活的资源分配,有可能造成资源浪费或资源不足。 -![](http://image.python-online.cn/20190627213609.png) +![](http://image.iswbm.com/20190627213609.png) **LVM** @@ -538,17 +538,17 @@ isolate 只要在主机组上设置的metadata 的 key-value 和 extra_spec 的key-value一样就可以实现宿主机的过滤。 -![](http://image.python-online.cn/20190627215038.png) +![](http://image.iswbm.com/20190627215038.png) ## 8.5.18 生成config drive -![](http://image.python-online.cn/20190708100902.png) +![](http://image.iswbm.com/20190708100902.png) -![](http://image.python-online.cn/20190708103119.png) +![](http://image.iswbm.com/20190708103119.png) ``` network_metadata @@ -563,7 +563,7 @@ inst_md.ip_info nova api 的接口参数是在 nova/api/openstack/compute/schemas/ 目录下,如创建虚拟机的接口如下: -![](http://image.python-online.cn/20190719170825.png) +![](http://image.iswbm.com/20190719170825.png) @@ -571,7 +571,7 @@ nova api 的接口参数是在 nova/api/openstack/compute/schemas/ 目录下, 在application 函数的头部,可以发现有如下这几种装饰器, -![](http://image.python-online.cn/20190730140551.png) +![](http://image.iswbm.com/20190730140551.png) 装饰器里会传入一个参数,就是 schema 的内容,通过它可以约束一个请求内的参数合法性。 @@ -581,11 +581,11 @@ schemas 的文件都统一放在 `nova\api\openstack\compute\schemas` 目录下 但并不是说,创建虚拟机的所有参数校验规则只在这里,nova 这边引入了 stevedore,它是 oslo 项目中为OpenStack其他项目提供动态加载功能的公共组件库(详细可以看这篇文章:https://blog.csdn.net/bill_xiang_/article/details/78852717)。通过它可以用添加扩展的方式,给 servers 更新 schemas。 -![](http://image.python-online.cn/20190826210813.png) +![](http://image.iswbm.com/20190826210813.png) 关键在于这个 map 函数,它会循环所有的扩展() -![](http://image.python-online.cn/20190826211542.png) +![](http://image.iswbm.com/20190826211542.png) 将调用传进去的 `self._create_extension_schema` 将已加载到的扩展schemas 更新到主schemas里去。 @@ -596,15 +596,15 @@ schemas 的文件都统一放在 `nova\api\openstack\compute\schemas` 目录下 -![](http://image.python-online.cn/20190826211737.png) +![](http://image.iswbm.com/20190826211737.png) 装饰器 schemas 的定义如下: -![](http://image.python-online.cn/20190730142527.png) +![](http://image.iswbm.com/20190730142527.png) 比如我们使用的 novaclient 发出的请求,是 v2.3.7的,所以 create() 顶部的五个schema 装饰器,上面四个都会空跑,不会进行校验,只有最后一个才会进入检验逻辑。 -![](http://image.python-online.cn/20190730143003.png) +![](http://image.iswbm.com/20190730143003.png) @@ -616,17 +616,17 @@ schemas 的文件都统一放在 `nova\api\openstack\compute\schemas` 目录下 就像下面指明了 server 的 create 接口会去加载这9个扩展资源。 -![](http://image.python-online.cn/20190830091540.png) +![](http://image.iswbm.com/20190830091540.png) 搜索一下 server_create 方法 还真的只有这 9 个资源里才会定义。 -![](http://image.python-online.cn/20190830092203.png) +![](http://image.iswbm.com/20190830092203.png) 这就神奇了,servers 这个核心资源是如何加载到这些资源的,看了下代码是使用 stevedore 这个模块去动态加载,然后还会校验这些资源是否都有 `server_create` ,只有有这个函数,才会被正常加载进来。 -![](http://image.python-online.cn/20190830093613.png) +![](http://image.iswbm.com/20190830093613.png) ## 8.5.20 往数据库中加字段 @@ -664,7 +664,7 @@ self._record_action_start(context, instance, instance_actions.REBOOT) ## 8.5.22 如何生成ConfigDrive -![](http://image.python-online.cn/20190912135302.png) +![](http://image.iswbm.com/20190912135302.png) diff --git a/source/c08/c08_05.rst b/source/c08/c08_05.rst index 0ca7934..b8f1503 100644 --- a/source/c08/c08_05.rst +++ b/source/c08/c08_05.rst @@ -743,65 +743,65 @@ stevedore 这个模块去动态加载,然后还会校验这些资源是否都 |image61| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190526144846.png -.. |image2| image:: http://image.python-online.cn/20190529135942.png -.. |image3| image:: http://image.python-online.cn/20190530204839.png -.. |image4| image:: http://image.python-online.cn/20190530204505.png -.. |image5| image:: http://image.python-online.cn/20190530210912.png -.. |image6| image:: http://image.python-online.cn/20190508110723.png -.. |image7| image:: http://image.python-online.cn/20190508111109.png -.. |image8| image:: http://image.python-online.cn/20190508095028.png -.. |image9| image:: http://image.python-online.cn/20190508111527.png -.. |image10| image:: http://image.python-online.cn/FhRPy4B1xEI9SfoD2RcunJl15ZE3 -.. |image11| image:: http://image.python-online.cn/FuyMWZS6HF4g3rPwTlLcereZxg4L -.. |image12| image:: http://image.python-online.cn/FnJA8RNIvJN2lAEXbKtJDpOLg1vg -.. |image13| image:: http://image.python-online.cn/FnGyI8jCQFLCGi0pGVmI3SV6pDrv -.. |image14| image:: http://image.python-online.cn/FrbE6oEZ3vtTWwDfMNQ16MGi6SWr -.. |image15| image:: http://image.python-online.cn/20190906093751.png -.. |image16| image:: http://image.python-online.cn/20190906094703.png -.. |image17| image:: http://image.python-online.cn/20190906214536.png -.. |image18| image:: http://image.python-online.cn/20190906100119.png -.. |image19| image:: http://image.python-online.cn/20190906213038.png -.. |image20| image:: http://image.python-online.cn/20190906210825.png -.. |image21| image:: http://image.python-online.cn/20190906213823.png -.. |image22| image:: http://image.python-online.cn/20190426153322.png -.. |image23| image:: http://image.python-online.cn/20190426152148.png -.. |image24| image:: http://image.python-online.cn/20190526141815.png -.. |image25| image:: http://image.python-online.cn/20190526142453.png -.. |image26| image:: http://image.python-online.cn/20190526134519.png -.. |image27| image:: http://image.python-online.cn/20190526141226.png -.. |image28| image:: http://image.python-online.cn/20190526134543.png -.. |image29| image:: http://image.python-online.cn/20190526134618.png -.. |image30| image:: http://image.python-online.cn/20190526140213.png -.. |image31| image:: http://image.python-online.cn/20190526140301.png -.. |image32| image:: http://image.python-online.cn/20190526140315.png -.. |image33| image:: http://image.python-online.cn/20190526140336.png -.. |image34| image:: http://image.python-online.cn/20190526140410.png -.. |image35| image:: http://image.python-online.cn/20190526143235.png -.. |image36| image:: http://image.python-online.cn/20190526205152.png -.. |image37| image:: http://image.python-online.cn/20190526165007.png -.. |image38| image:: http://image.python-online.cn/20190526204328.png -.. |image39| image:: http://image.python-online.cn/20191008173211.png -.. |image40| image:: http://image.python-online.cn/20191008174046.png -.. |image41| image:: http://image.python-online.cn/20191008210127.png -.. |image42| image:: http://image.python-online.cn/20190529203441.png -.. |image43| image:: http://image.python-online.cn/20190529215953.png -.. |image44| image:: http://image.python-online.cn/20190529215825.png -.. |image45| image:: http://image.python-online.cn/20190627213044.png -.. |image46| image:: http://image.python-online.cn/20190627213609.png -.. |image47| image:: http://image.python-online.cn/20190627215038.png -.. |image48| image:: http://image.python-online.cn/20190708100902.png -.. |image49| image:: http://image.python-online.cn/20190708103119.png -.. |image50| image:: http://image.python-online.cn/20190719170825.png -.. |image51| image:: http://image.python-online.cn/20190730140551.png -.. |image52| image:: http://image.python-online.cn/20190826210813.png -.. |image53| image:: http://image.python-online.cn/20190826211542.png -.. |image54| image:: http://image.python-online.cn/20190826211737.png -.. |image55| image:: http://image.python-online.cn/20190730142527.png -.. |image56| image:: http://image.python-online.cn/20190730143003.png -.. |image57| image:: http://image.python-online.cn/20190830091540.png -.. |image58| image:: http://image.python-online.cn/20190830092203.png -.. |image59| image:: http://image.python-online.cn/20190830093613.png -.. |image60| image:: http://image.python-online.cn/20190912135302.png +.. |image1| image:: http://image.iswbm.com/20190526144846.png +.. |image2| image:: http://image.iswbm.com/20190529135942.png +.. |image3| image:: http://image.iswbm.com/20190530204839.png +.. |image4| image:: http://image.iswbm.com/20190530204505.png +.. |image5| image:: http://image.iswbm.com/20190530210912.png +.. |image6| image:: http://image.iswbm.com/20190508110723.png +.. |image7| image:: http://image.iswbm.com/20190508111109.png +.. |image8| image:: http://image.iswbm.com/20190508095028.png +.. |image9| image:: http://image.iswbm.com/20190508111527.png +.. |image10| image:: http://image.iswbm.com/FhRPy4B1xEI9SfoD2RcunJl15ZE3 +.. |image11| image:: http://image.iswbm.com/FuyMWZS6HF4g3rPwTlLcereZxg4L +.. |image12| image:: http://image.iswbm.com/FnJA8RNIvJN2lAEXbKtJDpOLg1vg +.. |image13| image:: http://image.iswbm.com/FnGyI8jCQFLCGi0pGVmI3SV6pDrv +.. |image14| image:: http://image.iswbm.com/FrbE6oEZ3vtTWwDfMNQ16MGi6SWr +.. |image15| image:: http://image.iswbm.com/20190906093751.png +.. |image16| image:: http://image.iswbm.com/20190906094703.png +.. |image17| image:: http://image.iswbm.com/20190906214536.png +.. |image18| image:: http://image.iswbm.com/20190906100119.png +.. |image19| image:: http://image.iswbm.com/20190906213038.png +.. |image20| image:: http://image.iswbm.com/20190906210825.png +.. |image21| image:: http://image.iswbm.com/20190906213823.png +.. |image22| image:: http://image.iswbm.com/20190426153322.png +.. |image23| image:: http://image.iswbm.com/20190426152148.png +.. |image24| image:: http://image.iswbm.com/20190526141815.png +.. |image25| image:: http://image.iswbm.com/20190526142453.png +.. |image26| image:: http://image.iswbm.com/20190526134519.png +.. |image27| image:: http://image.iswbm.com/20190526141226.png +.. |image28| image:: http://image.iswbm.com/20190526134543.png +.. |image29| image:: http://image.iswbm.com/20190526134618.png +.. |image30| image:: http://image.iswbm.com/20190526140213.png +.. |image31| image:: http://image.iswbm.com/20190526140301.png +.. |image32| image:: http://image.iswbm.com/20190526140315.png +.. |image33| image:: http://image.iswbm.com/20190526140336.png +.. |image34| image:: http://image.iswbm.com/20190526140410.png +.. |image35| image:: http://image.iswbm.com/20190526143235.png +.. |image36| image:: http://image.iswbm.com/20190526205152.png +.. |image37| image:: http://image.iswbm.com/20190526165007.png +.. |image38| image:: http://image.iswbm.com/20190526204328.png +.. |image39| image:: http://image.iswbm.com/20191008173211.png +.. |image40| image:: http://image.iswbm.com/20191008174046.png +.. |image41| image:: http://image.iswbm.com/20191008210127.png +.. |image42| image:: http://image.iswbm.com/20190529203441.png +.. |image43| image:: http://image.iswbm.com/20190529215953.png +.. |image44| image:: http://image.iswbm.com/20190529215825.png +.. |image45| image:: http://image.iswbm.com/20190627213044.png +.. |image46| image:: http://image.iswbm.com/20190627213609.png +.. |image47| image:: http://image.iswbm.com/20190627215038.png +.. |image48| image:: http://image.iswbm.com/20190708100902.png +.. |image49| image:: http://image.iswbm.com/20190708103119.png +.. |image50| image:: http://image.iswbm.com/20190719170825.png +.. |image51| image:: http://image.iswbm.com/20190730140551.png +.. |image52| image:: http://image.iswbm.com/20190826210813.png +.. |image53| image:: http://image.iswbm.com/20190826211542.png +.. |image54| image:: http://image.iswbm.com/20190826211737.png +.. |image55| image:: http://image.iswbm.com/20190730142527.png +.. |image56| image:: http://image.iswbm.com/20190730143003.png +.. |image57| image:: http://image.iswbm.com/20190830091540.png +.. |image58| image:: http://image.iswbm.com/20190830092203.png +.. |image59| image:: http://image.iswbm.com/20190830093613.png +.. |image60| image:: http://image.iswbm.com/20190912135302.png .. |image61| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_06.md b/source/c08/c08_06.md index 39463be..70a350f 100644 --- a/source/c08/c08_06.md +++ b/source/c08/c08_06.md @@ -15,7 +15,7 @@ cloud-init 是 linux 的一个工具,当系统启动时,cloud-init 可从 no 这些任务都是以服务存在,并且安装完成后自动加入了开机自启。 -![CentOS 7.x](http://image.python-online.cn/20190430203920.png) +![CentOS 7.x](http://image.iswbm.com/20190430203920.png) 那么你一定会奇怪,这些任务都是分离的,如何控制其启动顺序呢? @@ -23,15 +23,15 @@ cloud-init 是 linux 的一个工具,当系统启动时,cloud-init 可从 no 比如 local 阶段作为第一个执行的。 -![](http://image.python-online.cn/20190430204707.png) +![](http://image.iswbm.com/20190430204707.png) 而后的几个服务(如init阶段),都需要保证 后于 local 已经启动过才能运行。 -![](http://image.python-online.cn/20190430204933.png) +![](http://image.iswbm.com/20190430204933.png) 在 **CentOS 6.x** 中,由于系统过于古老并没有 systemd ,它的服务启动顺序是由 `/etc/rc.d/rc3.d` 目录进行管理的,按照编号进行从小到大执行。 -![](http://image.python-online.cn/20190430205449.png) +![](http://image.iswbm.com/20190430205449.png) 这个启动顺序相当重要,在排查并修复虚拟机创建时 ip 的配置问题的时候会有帮助,主要是 CentOS6.x 上的问题(我曾经碰到过的),这种也顺便讲讲。 @@ -41,7 +41,7 @@ cloud-init 是 linux 的一个工具,当系统启动时,cloud-init 可从 no 从以信息可知,如果创建静态ip的虚拟机,NetworkManager 这个服务必须在 cloudinit-local 之后启动才可正常从配置文件中读取 ip 并配置。而当你在镜像里安装 NetworkManager后,默认情况下它的启动顺序是会在 cloudinit-local 之前的。 -![](http://image.python-online.cn/20190430211900.png) +![](http://image.iswbm.com/20190430211900.png) 解决方法也很简单,将 `S23NetworkManager` 重命名为 `S54NetworkManager` 即可。 @@ -68,19 +68,19 @@ cloud-init 是 linux 的一个工具,当系统启动时,cloud-init 可从 no 这里再顺便说一点,在使用 pdb 进行调试时,你会发现一旦执行就会报错或者无法调试。 -![](http://image.python-online.cn/20190430213012.png) +![](http://image.iswbm.com/20190430213012.png) 这里就需要一个小技巧。你只需修改cloudinit 的入口文件(CentOS6.x 是在/usr/bin/cloud-init,CentOS7.x 是在 /usr/lib/python2.7/site-packages/cloudinit/cmd/main.py)将红框这行注释。 -![](http://image.python-online.cn/20190430213337.png) +![](http://image.iswbm.com/20190430213337.png) 再尝试即可正常调试。 -![](http://image.python-online.cn/20190430213429.png) +![](http://image.iswbm.com/20190430213429.png) 从这里也学到了一点,如何要关闭调试功能,只要关闭标准输入就行了。 -![](http://image.python-online.cn/20190430213729.png) +![](http://image.iswbm.com/20190430213729.png) 如果你在调试 single,而且你把 /var/lib/cloud 目录删除过,请务必要执行一下 cloud-init init ,不然会取不到 user_data @@ -92,29 +92,29 @@ centos 6.5 的cloudinit 使用的是 python2.6,没有 format 语法,请注 数据源的读取入口是在 入口文件(CentOS6.x 是在 `/usr/bin/cloud-init`,CentOS7.x 是在 `/usr/lib/python2.7/site-packages/cloudinit/cmd/main.py`)的 main_init 函数中,会调用 `stages.py:fetch()` 函数。 -![](http://image.python-online.cn/20190430225605.png) +![](http://image.iswbm.com/20190430225605.png) 支持哪些源:是在代码里(settings.py)定义写死的 -![](http://image.python-online.cn/20190430225726.png) +![](http://image.iswbm.com/20190430225726.png) 如果你的虚拟机固定只使用其中一种,那可以只留一种,加快cloudinit 的执行速度。 find_source 是通过一个一个去执行 对应source模块的 get_data(),如果获取到了数据就直接返回。 -![](http://image.python-online.cn/20190430230214.png) +![](http://image.iswbm.com/20190430230214.png) 最经常使用的是 ConfigDrive,来看一下它是如何获取数据的。 -![](http://image.python-online.cn/FpqcyL4hWwpaAGzsdreQwXvH4Rx8) +![](http://image.iswbm.com/FpqcyL4hWwpaAGzsdreQwXvH4Rx8) 它会先创建一个临时目录,尝试将 ` /dev/sr0` 挂载到这个目录下。 -![](http://image.python-online.cn/20190430230839.png) +![](http://image.iswbm.com/20190430230839.png) 然后将读取的数据存入 `/var/lib/cloud/instance/obj.pkl`, 而后续执行都将从这里反序列化,提高速度。 -![](http://image.python-online.cn/20190430231108.png) +![](http://image.iswbm.com/20190430231108.png) @@ -283,7 +283,7 @@ myjob.cfg:text/upstart-job 这就是cloudinit搞的鬼,在cloudinit的local阶段,好像会记录之前的mac地址,如果发现不一致,就会触发rename_interface。 -![](http://image.python-online.cn/20190623091911.png) +![](http://image.iswbm.com/20190623091911.png) @@ -291,7 +291,7 @@ myjob.cfg:text/upstart-job 当创建一个有数据盘的虚拟机,nova会在configdrive里的ec2目录下生成 meta-data.json ,其中有一个字段是block-device-mapping,包含磁盘信息。在虚拟机创建后,如果 /etc/cloud/cloud.cfg里配置了 mounts,cloudinit会根据这个这下面文件中的 ephemeral0 拿到对应的 /dev/vdb,并将其写入 /etc/fstab 中。在下次重启时,会根据 fstab 挂载磁盘,如果挂载不上,就会导致虚拟机启动卡住。 -![](http://image.python-online.cn/20190708175813.png) +![](http://image.iswbm.com/20190708175813.png) @@ -386,20 +386,20 @@ def run(self, name, functor, args, freq=None, clear_on_fail=False): 对于 `PER_INSTANCE` 和 `PER_ONCE` 和区别,从名字和代码实现上看,似乎没有区别,继续看 cloudinit 里的代码,只有一个模块使用了 PER_ONCE ,那就是 `cc_scripts_per_once.py`。 -![](http://image.python-online.cn/20190910160035.png) +![](http://image.iswbm.com/20190910160035.png) 其实他们还是有区别的,由下面的代码来看 - `PER_ONCE` 获取的 sem 文件是从 `/var/lib/cloud/` 下的文件获取的,这个目录一个镜像只会生成一次(若你不删除的话)。 - `PER_INSTANCE` 获取 sem 文件是从 `/var/lib/cloud/instances/` 下的文件获取的,这个目录每个虚拟机一个。 -![](http://image.python-online.cn/20190910150305.png) +![](http://image.iswbm.com/20190910150305.png) 如果你的模块里已经配置了频率,则以此为准。若没有配置,则默认为 `PER_INSTANCE`。 具体函数:`_run_modules` -![](http://image.python-online.cn/20190910142222.png) +![](http://image.iswbm.com/20190910142222.png) @@ -407,15 +407,15 @@ def run(self, name, functor, args, freq=None, clear_on_fail=False): 通过代码可以发现,其在运行时,会先调用 `has_run` 函数,在这里会去取对应目录下(上面的 sem_path)的sem文件,如果有存在 sem 文件,说明已经执行过(返回 False ),如果不存在 sem 文件(返回 True) 说明未执行过。 -![](http://image.python-online.cn/20190910153637.png) +![](http://image.iswbm.com/20190910153637.png) sem 文件是怎样的? 这是 `PER_ONCE` 的 sem 文件: -![](http://image.python-online.cn/20190910171359.png) +![](http://image.iswbm.com/20190910171359.png) -这是 `PER_INSTANCE` 的 sem 文件![](http://image.python-online.cn/20190910171538.png) +这是 `PER_INSTANCE` 的 sem 文件![](http://image.iswbm.com/20190910171538.png) @@ -430,7 +430,7 @@ from cloudinit.net import eni config=eni.parse_deb_config('/etc/network/interfaces.d/50-cloud-init.cfg') ``` -![](http://image.python-online.cn/20190906091102.png) +![](http://image.iswbm.com/20190906091102.png) ## 8.6.11 低版本的日志输出 @@ -438,7 +438,7 @@ config=eni.parse_deb_config('/etc/network/interfaces.d/50-cloud-init.cfg') 经过排查,你可以在 `cloudinit/log.py` 中按下面的改法修改解决 -![](http://image.python-online.cn/20190909172153.png) +![](http://image.iswbm.com/20190909172153.png) ## 8.6.12 旧版本网络配置的三个巨坑 @@ -450,11 +450,11 @@ config=eni.parse_deb_config('/etc/network/interfaces.d/50-cloud-init.cfg') 在 cloud-init 0.7.5中,网络信息的读取与配置都是在且仅能在 local 阶段进行的,代码如下,只在 dsmode 为 local 时才会执行 on_first_boot。 -![](http://image.python-online.cn/20190429104357.png) +![](http://image.iswbm.com/20190429104357.png) 而如果是虚拟机重启的话,是在stages.py 这边判断是否为新虚拟机的,这也和旧版本有所区别。 -![](http://image.python-online.cn/20190829141059.png) +![](http://image.iswbm.com/20190829141059.png) 为了让你能更加清晰的了解这个网络配置过程,我阅读了这块的源代码。 @@ -469,11 +469,11 @@ config=eni.parse_deb_config('/etc/network/interfaces.d/50-cloud-init.cfg') **坑一** -如果是按照旧虚拟机创建新的快照镜像,然后使用这个镜像创建新的虚拟机,有可能会在同一块网卡上出现新旧两个ip,这是因为虚拟机在启动过程中,会先读取原网络配置配置ip,然后才会运行 cloud-init 进行新ip的配置,而新ip的配置是使用 `ifup` 这个命令![](http://image.python-online.cn/Fp1TeHSiIMIQoZygbW9VSfAagB_d) +如果是按照旧虚拟机创建新的快照镜像,然后使用这个镜像创建新的虚拟机,有可能会在同一块网卡上出现新旧两个ip,这是因为虚拟机在启动过程中,会先读取原网络配置配置ip,然后才会运行 cloud-init 进行新ip的配置,而新ip的配置是使用 `ifup` 这个命令![](http://image.iswbm.com/Fp1TeHSiIMIQoZygbW9VSfAagB_d) -使用这种方式并不会将第一次配置的旧ip给清除掉。![](http://image.python-online.cn/Fh-5SQ8qYjhJEKovI6LmIpabSy2c) +使用这种方式并不会将第一次配置的旧ip给清除掉。![](http://image.iswbm.com/Fh-5SQ8qYjhJEKovI6LmIpabSy2c) -这个问题,目前我只在CentOS6 中遇到过。可以通过修改代码让其先 `ifdown` 再 `ifup` 就可以解决这个问题。![](http://image.python-online.cn/20190430231812.png) +这个问题,目前我只在CentOS6 中遇到过。可以通过修改代码让其先 `ifdown` 再 `ifup` 就可以解决这个问题。![](http://image.iswbm.com/20190430231812.png) **坑二** @@ -481,7 +481,7 @@ config=eni.parse_deb_config('/etc/network/interfaces.d/50-cloud-init.cfg') 具体的创建逻辑是在这 -![](http://image.python-online.cn/20190430232309.png) +![](http://image.iswbm.com/20190430232309.png) **坑三** @@ -502,11 +502,11 @@ Apr 29 11:13:29 localhost NetworkManager[1365]: exiting (error) 一个是会自动DHCP获取到一个以ip命名的hostname,并将原来的覆盖掉。 -![](http://image.python-online.cn/20190429205735.png) +![](http://image.iswbm.com/20190429205735.png) 为了避免出现这些情况,请务必保证这些包都安装完整(左为 CentOS 7.2,右为 CentOS 6.5)。 -![](http://image.python-online.cn/20190430232911.png) +![](http://image.iswbm.com/20190430232911.png) ## 8.6.13 网络是如何启动的?(新版本) @@ -519,15 +519,15 @@ Apr 29 11:13:29 localhost NetworkManager[1365]: exiting (error) 在 local 阶段的时候,`existing` 是 `check` ,这是合理的。 -![](http://image.python-online.cn/20190911175423.png) +![](http://image.iswbm.com/20190911175423.png) -![](http://image.python-online.cn/20190911174648.png) +![](http://image.iswbm.com/20190911174648.png) 在local阶段,on_first_boot 的函数 network 是 False ,所以这里也不会写网卡配置文件,自然也不会配置。 -![](http://image.python-online.cn/20190911173615.png) +![](http://image.iswbm.com/20190911173615.png) -![](http://image.python-online.cn/20190911195024.png) +![](http://image.iswbm.com/20190911195024.png) @@ -551,11 +551,11 @@ init.apply_network_config(bring_up=bool(mode != sources.DSMODE_LOCAL)) 2. 校正网卡名,以 ConfigDrive 的配置为准 3. 将ip信息写入配置文件,并启动网卡 -![](http://image.python-online.cn/20190911202425.png) +![](http://image.iswbm.com/20190911202425.png) 那它是如何对网卡进行重命令的呢?看了代码,其实是用ip命令实现的。 -![](http://image.python-online.cn/20190911202551.png) +![](http://image.iswbm.com/20190911202551.png) 提取出来其实就三条命令,要注意的是,这三条命令执行是有顺序的 @@ -567,11 +567,11 @@ init.apply_network_config(bring_up=bool(mode != sources.DSMODE_LOCAL)) 还有一点要说的是,cloudinit 是如何取到本机真实的网卡信息的呢?他是从 `/sys/class/net/` 目录下获取的。每个网卡一个目录,每个目录下都有相应的文件记录相应的信息,比如 `/sys/class/net/ens3/address` 记录的是网卡的 mac 地址。 -![](http://image.python-online.cn/20190911203953.png) +![](http://image.iswbm.com/20190911203953.png) 接下来就要开始配置网络了,先写网络配置文件,再根据参数选择是否启用网络。 -![](http://image.python-online.cn/20190911204805.png) +![](http://image.iswbm.com/20190911204805.png) 如果是重启虚拟机或者 init 阶段进入这里呢,会不会又重复配置网络了呢? @@ -579,7 +579,7 @@ init.apply_network_config(bring_up=bool(mode != sources.DSMODE_LOCAL)) cloudinit 会根据缓存中的虚拟机的uuid来与ConfigDrive 的对比,如果不一样,则认为这台虚拟机是新创建的虚拟机,只有新的虚拟机才会走入这里去配置网络。 -![](http://image.python-online.cn/20190911205518.png) +![](http://image.iswbm.com/20190911205518.png) 那问题又来了,虽然上面有个 bring_up 的参数,实际上,通过代码可以发现,在 local 阶段,bring_up 为 False 不会去启用网卡,而在 init 阶段呢,虽然 bring_up 为 True,但是此时,经过 local 阶段后,代码逻辑会认为这是台旧虚拟机,不会再走后面配置网络的函数,那就很奇怪了,网卡的ip是如何配置上的呢? diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index 7b04f5b..109d5d2 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -19,7 +19,7 @@ metadata,完成一些虚拟机的初始化工作,这些工作有可能是每 这些任务都是以服务存在,并且安装完成后自动加入了开机自启。 -.. figure:: http://image.python-online.cn/20190430203920.png +.. figure:: http://image.iswbm.com/20190430203920.png :alt: CentOS 7.x CentOS 7.x @@ -710,46 +710,46 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 |image42| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190430204707.png -.. |image2| image:: http://image.python-online.cn/20190430204933.png -.. |image3| image:: http://image.python-online.cn/20190430205449.png -.. |image4| image:: http://image.python-online.cn/20190430211900.png -.. |image5| image:: http://image.python-online.cn/20190430213012.png -.. |image6| image:: http://image.python-online.cn/20190430213337.png -.. |image7| image:: http://image.python-online.cn/20190430213429.png -.. |image8| image:: http://image.python-online.cn/20190430213729.png -.. |image9| image:: http://image.python-online.cn/20190430225605.png -.. |image10| image:: http://image.python-online.cn/20190430225726.png -.. |image11| image:: http://image.python-online.cn/20190430230214.png -.. |image12| image:: http://image.python-online.cn/FpqcyL4hWwpaAGzsdreQwXvH4Rx8 -.. |image13| image:: http://image.python-online.cn/20190430230839.png -.. |image14| image:: http://image.python-online.cn/20190430231108.png -.. |image15| image:: http://image.python-online.cn/20190623091911.png -.. |image16| image:: http://image.python-online.cn/20190708175813.png -.. |image17| image:: http://image.python-online.cn/20190910160035.png -.. |image18| image:: http://image.python-online.cn/20190910150305.png -.. |image19| image:: http://image.python-online.cn/20190910142222.png -.. |image20| image:: http://image.python-online.cn/20190910153637.png -.. |image21| image:: http://image.python-online.cn/20190910171359.png -.. |image22| image:: http://image.python-online.cn/20190910171538.png -.. |image23| image:: http://image.python-online.cn/20190906091102.png -.. |image24| image:: http://image.python-online.cn/20190909172153.png -.. |image25| image:: http://image.python-online.cn/20190429104357.png -.. |image26| image:: http://image.python-online.cn/20190829141059.png -.. |image27| image:: http://image.python-online.cn/Fp1TeHSiIMIQoZygbW9VSfAagB_d -.. |image28| image:: http://image.python-online.cn/Fh-5SQ8qYjhJEKovI6LmIpabSy2c -.. |image29| image:: http://image.python-online.cn/20190430231812.png -.. |image30| image:: http://image.python-online.cn/20190430232309.png -.. |image31| image:: http://image.python-online.cn/20190429205735.png -.. |image32| image:: http://image.python-online.cn/20190430232911.png -.. |image33| image:: http://image.python-online.cn/20190911175423.png -.. |image34| image:: http://image.python-online.cn/20190911174648.png -.. |image35| image:: http://image.python-online.cn/20190911173615.png -.. |image36| image:: http://image.python-online.cn/20190911195024.png -.. |image37| image:: http://image.python-online.cn/20190911202425.png -.. |image38| image:: http://image.python-online.cn/20190911202551.png -.. |image39| image:: http://image.python-online.cn/20190911203953.png -.. |image40| image:: http://image.python-online.cn/20190911204805.png -.. |image41| image:: http://image.python-online.cn/20190911205518.png +.. |image1| image:: http://image.iswbm.com/20190430204707.png +.. |image2| image:: http://image.iswbm.com/20190430204933.png +.. |image3| image:: http://image.iswbm.com/20190430205449.png +.. |image4| image:: http://image.iswbm.com/20190430211900.png +.. |image5| image:: http://image.iswbm.com/20190430213012.png +.. |image6| image:: http://image.iswbm.com/20190430213337.png +.. |image7| image:: http://image.iswbm.com/20190430213429.png +.. |image8| image:: http://image.iswbm.com/20190430213729.png +.. |image9| image:: http://image.iswbm.com/20190430225605.png +.. |image10| image:: http://image.iswbm.com/20190430225726.png +.. |image11| image:: http://image.iswbm.com/20190430230214.png +.. |image12| image:: http://image.iswbm.com/FpqcyL4hWwpaAGzsdreQwXvH4Rx8 +.. |image13| image:: http://image.iswbm.com/20190430230839.png +.. |image14| image:: http://image.iswbm.com/20190430231108.png +.. |image15| image:: http://image.iswbm.com/20190623091911.png +.. |image16| image:: http://image.iswbm.com/20190708175813.png +.. |image17| image:: http://image.iswbm.com/20190910160035.png +.. |image18| image:: http://image.iswbm.com/20190910150305.png +.. |image19| image:: http://image.iswbm.com/20190910142222.png +.. |image20| image:: http://image.iswbm.com/20190910153637.png +.. |image21| image:: http://image.iswbm.com/20190910171359.png +.. |image22| image:: http://image.iswbm.com/20190910171538.png +.. |image23| image:: http://image.iswbm.com/20190906091102.png +.. |image24| image:: http://image.iswbm.com/20190909172153.png +.. |image25| image:: http://image.iswbm.com/20190429104357.png +.. |image26| image:: http://image.iswbm.com/20190829141059.png +.. |image27| image:: http://image.iswbm.com/Fp1TeHSiIMIQoZygbW9VSfAagB_d +.. |image28| image:: http://image.iswbm.com/Fh-5SQ8qYjhJEKovI6LmIpabSy2c +.. |image29| image:: http://image.iswbm.com/20190430231812.png +.. |image30| image:: http://image.iswbm.com/20190430232309.png +.. |image31| image:: http://image.iswbm.com/20190429205735.png +.. |image32| image:: http://image.iswbm.com/20190430232911.png +.. |image33| image:: http://image.iswbm.com/20190911175423.png +.. |image34| image:: http://image.iswbm.com/20190911174648.png +.. |image35| image:: http://image.iswbm.com/20190911173615.png +.. |image36| image:: http://image.iswbm.com/20190911195024.png +.. |image37| image:: http://image.iswbm.com/20190911202425.png +.. |image38| image:: http://image.iswbm.com/20190911202551.png +.. |image39| image:: http://image.iswbm.com/20190911203953.png +.. |image40| image:: http://image.iswbm.com/20190911204805.png +.. |image41| image:: http://image.iswbm.com/20190911205518.png .. |image42| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_07.md b/source/c08/c08_07.md index adf8987..42abc54 100644 --- a/source/c08/c08_07.md +++ b/source/c08/c08_07.md @@ -8,11 +8,11 @@ (若没有lspci命令,则安装 yum install pciutils -y) -![](http://image.python-online.cn/20190419144135.png) +![](http://image.iswbm.com/20190419144135.png) 此时查看,驱动是 nvidia -![](http://image.python-online.cn/20190419144044.png) +![](http://image.iswbm.com/20190419144044.png) ``` [root@ws_compute11 ~]# lspci -nnk -d 10de:1bb3 @@ -36,15 +36,15 @@ 未创建虚拟机 -![](http://image.python-online.cn/20190422201117.png) +![](http://image.iswbm.com/20190422201117.png) 创建gpu虚拟机成功,driver 由 nvidia 变为 vfio-pci -![](http://image.python-online.cn/20190422201041.png) +![](http://image.iswbm.com/20190422201041.png) 删除虚拟机成功 -![](http://image.python-online.cn/20190422201117.png) +![](http://image.iswbm.com/20190422201117.png) ### 8.7.2.2 隐藏虚拟机标识 @@ -52,7 +52,7 @@ (若没有cpuid,则安装 yum install -y cpuid) -![](http://image.python-online.cn/20190422205222.png) +![](http://image.iswbm.com/20190422205222.png) 如何隐藏 hypervisor id,只需要在xml的feature加上这段。 @@ -64,7 +64,7 @@ 然后destroy再start一下虚拟机,在虚拟机内部再次查看。 -![](http://image.python-online.cn/20190422204755.png) +![](http://image.iswbm.com/20190422204755.png) 在Pike 版本,OpenStack 已经可以根据镜像里是否有`img_hide_hypervisor_id=true` 的property,来选择是否加上隐藏hypervisor_id的xml。 @@ -82,15 +82,15 @@ openstack image set IMG-UUID --property img_hide_hypervisor_id=true 具体 Pike版的代码如何实现,主要函数是这段 -![](http://image.python-online.cn/20190528105408.png) +![](http://image.iswbm.com/20190528105408.png) 记得在镜像meta里添加 img_hide_hypervisor_id 的字段 -![](http://image.python-online.cn/20190528105021.png) +![](http://image.iswbm.com/20190528105021.png) ### 8.7.2.3. 是否直通成功 -在虚拟机内部执行 nvidia-smi![](http://image.python-online.cn/20190528114526.png) +在虚拟机内部执行 nvidia-smi![](http://image.iswbm.com/20190528114526.png) ### 8.7.2.4 称重器 @@ -98,7 +98,7 @@ openstack image set IMG-UUID --property img_hide_hypervisor_id=true 因此,需要增加一个称重器,,当创建普通类型的虚拟机时,含 GPU (通过pci_device 设备数来决定)的宿主机的权重为最低,为了保证这点,这个称重器的权重系数应最大,暂定为200(可以通过配置项 ws_gpu_weight_multiplier 设置)。 -![](http://image.python-online.cn/20190606185531.png) +![](http://image.iswbm.com/20190606185531.png) ## 8.7.3 使用说明 diff --git a/source/c08/c08_07.rst b/source/c08/c08_07.rst index 15c40f9..38c58d3 100644 --- a/source/c08/c08_07.rst +++ b/source/c08/c08_07.rst @@ -153,16 +153,16 @@ GPU 作为一种硬件资源,同样需要在模板中配置,配置方式是 |image12| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190419144135.png -.. |image2| image:: http://image.python-online.cn/20190419144044.png -.. |image3| image:: http://image.python-online.cn/20190422201117.png -.. |image4| image:: http://image.python-online.cn/20190422201041.png -.. |image5| image:: http://image.python-online.cn/20190422201117.png -.. |image6| image:: http://image.python-online.cn/20190422205222.png -.. |image7| image:: http://image.python-online.cn/20190422204755.png -.. |image8| image:: http://image.python-online.cn/20190528105408.png -.. |image9| image:: http://image.python-online.cn/20190528105021.png -.. |image10| image:: http://image.python-online.cn/20190528114526.png -.. |image11| image:: http://image.python-online.cn/20190606185531.png +.. |image1| image:: http://image.iswbm.com/20190419144135.png +.. |image2| image:: http://image.iswbm.com/20190419144044.png +.. |image3| image:: http://image.iswbm.com/20190422201117.png +.. |image4| image:: http://image.iswbm.com/20190422201041.png +.. |image5| image:: http://image.iswbm.com/20190422201117.png +.. |image6| image:: http://image.iswbm.com/20190422205222.png +.. |image7| image:: http://image.iswbm.com/20190422204755.png +.. |image8| image:: http://image.iswbm.com/20190528105408.png +.. |image9| image:: http://image.iswbm.com/20190528105021.png +.. |image10| image:: http://image.iswbm.com/20190528114526.png +.. |image11| image:: http://image.iswbm.com/20190606185531.png .. |image12| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_08.md b/source/c08/c08_08.md index 14161ec..f0ca601 100644 --- a/source/c08/c08_08.md +++ b/source/c08/c08_08.md @@ -19,7 +19,7 @@ 如下图所示 -![](http://image.python-online.cn/20190514202013.png) +![](http://image.iswbm.com/20190514202013.png) 红色部分是虚拟机内部需要增加的网卡设备。 @@ -80,7 +80,7 @@ TYPE=OVSBridge 确保控制节点都安装了 `neutron-openvswitch-agent` 和 `neutron-dhcp-agent` 等几个包 -![](http://image.python-online.cn/20190514202442.png) +![](http://image.iswbm.com/20190514202442.png) 确保如下几个配置无误 @@ -115,7 +115,7 @@ systemctl restart neutron-openvswitch-agent 通过 ovs-vsctl show 检查环境是否正常 -![](http://image.python-online.cn/20190514202736.png) +![](http://image.iswbm.com/20190514202736.png) @@ -129,7 +129,7 @@ systemctl restart neutron-openvswitch-agent 1. 启用DHCP后,dhcp server 会占用一个allocation_pools中的ip。 2. 子网启用了dhcp,ConfigDrive不会携带静态的ip地址(类型为ipv4,左图),而是携带了将类型变更为ipv4_dhcp(右图),虚拟机启动时发送广播消息DHCP discover从dhcp server获得真正的ip地址。 - ![](http://image.python-online.cn/20190514203612.png) + ![](http://image.iswbm.com/20190514203612.png) 3. 若子网启用了dhcp,并且创建了虚拟机,此时 disable dhcp,只是表象上 dhcp 被关了,而实际 dhcp port 被是占用着,因为 dhcp server 还要给之前创建的虚拟机提供服务。如果再用这个子网创建的虚拟机会使用dhcp ip而不是使用 static ip,而当有删除(新增)其他子网的动作时,这个dhcp-port 又会被删除。如果一个子网开启了dhcp,并且这个子网下**没有虚拟机**,更新子网 disable dhcp,dhcp-port**会立即删除**。再用这个子网创建的虚拟机会使用static ip。如果一个子网开启了dhcp,并且这个子网下**有虚拟机**,更新子网 disable dhcp,dhcp-port**不会立即删除**。再用这个子网创建的虚拟机会使用dhcp获取ip。 4. dnsmasq进程是由dhcp-agent 服务管理的。如果停用 dhcp-agent 时,dnsmasq 进程并不会关闭。如果同一个网络下有多个dnsmasq ,不会影响正常的dhcp获取ip。 5. 如果三个控制节点上的 dhcp-agent 都是关闭状态,此时创建虚拟机时,ip仍然会正常分配、port会正常创建,但由于nova-compute等不到neutron的port plugged事件,过一段时间就超时导致创建虚拟机失败。而如果在超时范围内将dhcp-agent启动起来,就可以立即创建成功。 @@ -151,15 +151,15 @@ systemctl restart neutron-openvswitch-agent 比如 local 阶段作为第一个执行的。 -![](http://image.python-online.cn/20190430204707.png) +![](http://image.iswbm.com/20190430204707.png) 而后的几个服务(如init阶段),都需要保证 后于 local 已经启动过才能运行。 -![](http://image.python-online.cn/20190430204933.png) +![](http://image.iswbm.com/20190430204933.png) 在 **CentOS 6.x** 中,由于系统过于古老并没有 systemd ,它的服务启动顺序是由 `/etc/rc.d/rc3.d` 目录进行管理的,按照编号进行从小到大执行。 -![](http://image.python-online.cn/20190430205449.png) +![](http://image.iswbm.com/20190430205449.png) 这个启动顺序相当重要,在 CentOS6.x 上会因此出现问题 @@ -169,7 +169,7 @@ systemctl restart neutron-openvswitch-agent 从以信息可知,如果创建静态ip的虚拟机,NetworkManager 这个服务必须在 cloudinit-local 之后启动才可正常从配置文件中读取 ip 并配置。而当你在镜像里安装 NetworkManager后,默认情况下它的启动顺序是会在 cloudinit-local 之前的。 -![img](http://image.python-online.cn/20190430211900.png) +![img](http://image.iswbm.com/20190430211900.png) **解决方法**也很简单,将 `S23NetworkManager` 重命名为 `S54NetworkManager` 即可。 diff --git a/source/c08/c08_08.rst b/source/c08/c08_08.rst index f23daba..64457db 100644 --- a/source/c08/c08_08.rst +++ b/source/c08/c08_08.rst @@ -225,7 +225,7 @@ cloudinit-local 之后启动才可正常从配置文件中读取 ip 并配置。而当你在镜像里安装 NetworkManager后,默认情况下它的启动顺序是会在 cloudinit-local 之前的。 -.. figure:: http://image.python-online.cn/20190430211900.png +.. figure:: http://image.iswbm.com/20190430211900.png :alt: img img @@ -254,12 +254,12 @@ setup_dhcp_port(),从这个函数里可以知道,dhcp-port的创建顺序: |image8| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190514202013.png -.. |image2| image:: http://image.python-online.cn/20190514202442.png -.. |image3| image:: http://image.python-online.cn/20190514202736.png -.. |image4| image:: http://image.python-online.cn/20190514203612.png -.. |image5| image:: http://image.python-online.cn/20190430204707.png -.. |image6| image:: http://image.python-online.cn/20190430204933.png -.. |image7| image:: http://image.python-online.cn/20190430205449.png +.. |image1| image:: http://image.iswbm.com/20190514202013.png +.. |image2| image:: http://image.iswbm.com/20190514202442.png +.. |image3| image:: http://image.iswbm.com/20190514202736.png +.. |image4| image:: http://image.iswbm.com/20190514203612.png +.. |image5| image:: http://image.iswbm.com/20190430204707.png +.. |image6| image:: http://image.iswbm.com/20190430204933.png +.. |image7| image:: http://image.iswbm.com/20190430205449.png .. |image8| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_09.md b/source/c08/c08_09.md index 39d5279..3a1a520 100644 --- a/source/c08/c08_09.md +++ b/source/c08/c08_09.md @@ -201,7 +201,7 @@ import jsonrpclib server = jsonrpclib.Server("http://localhost:8080") ``` -![](http://image.python-online.cn/20190623185008.png) +![](http://image.iswbm.com/20190623185008.png) 再来看第二种python-jsonrpc,写起来貌似有些复杂。 @@ -237,13 +237,13 @@ http_client = pyjsonrpc.HttpClient( ) ``` -![](http://image.python-online.cn/20190623165341.png) +![](http://image.iswbm.com/20190623165341.png) 还记得上面我提到过的 zabbix API,因为我有接触过,所以也拎出来讲讲。zabbix API 也是基于 json-rpc 2.0协议实现的。 因为内容较多,这里只带大家打个,zabbix 是如何调用的:直接指明要调用 zabbix server 的哪个方法,要传给这个方法的参数有哪些。 -![](http://image.python-online.cn/20190623171138.png) +![](http://image.iswbm.com/20190623171138.png) **03、基于 zerorpc** @@ -293,11 +293,11 @@ c = zerorpc.Client() c.connect("tcp://127.0.0.1:4242") ``` -![](http://image.python-online.cn/20190623155955.png) +![](http://image.iswbm.com/20190623155955.png) 客户端除了可以使用zerorpc框架实现代码调用之外,它还支持使用“命令行”的方式调用。 -![](http://image.python-online.cn/20190623162725.png) +![](http://image.iswbm.com/20190623162725.png) 客户端可以使用命令行,那服务端是不是也可以呢? @@ -315,7 +315,7 @@ zerorpc --server --bind tcp://127.0.0.1:1234 time zerorpc --client --connect tcp://127.0.0.1:1234 strftime %Y/%m/%d ``` -![](http://image.python-online.cn/20190623191042.png) +![](http://image.iswbm.com/20190623191042.png) ## 8.9.3 往rpc中引入消息中间件 @@ -375,7 +375,7 @@ AMQP(Advanced Message Queuing Protocol)是一种基于队列的可靠消息服 为了让你明白这几者的关系,我画了一张模型图。 -![](http://image.python-online.cn/20190630160025.png) +![](http://image.iswbm.com/20190630160025.png) 关于AMQP,有几下几点值得注意: @@ -450,7 +450,7 @@ def consumer(): 其他模型我不太清楚,在 OpenStack 中的应用模型是这样的 -![](http://image.python-online.cn/20190623201427.png) +![](http://image.iswbm.com/20190623201427.png) 至于为什么要如此设计,前面我已经给出了自己的观点。 @@ -465,7 +465,7 @@ openstack.common.rpc是旧的实现,oslo messaging是对openstack.common.rpc 1. oslo.messaging.rpc(实现了客户端-服务器远程过程调用) 2. oslo.messaging.notify(实现了事件的通知机制) -因为 notify 实现是太简单了,所以这里我就不多说了,如果有人想要看这方面内容,可以收藏我的博客(http://python-online.cn) ,我会更新补充 notify 的内容。 +因为 notify 实现是太简单了,所以这里我就不多说了,如果有人想要看这方面内容,可以收藏我的博客(http://iswbm.com) ,我会更新补充 notify 的内容。 OpenStack RPC 模块提供了 rpc.call,rpc.cast, rpc.fanout_cast 三种 RPC 调用方法,发送和接收 RPC 请求。 @@ -485,7 +485,7 @@ rpc.call 和 rpc.cast 从实现代码上看,他们的区别很小,就是call transport_url=rabbit://user:passwd@host:5672 ``` - ![](http://image.python-online.cn/20190526182125.png) + ![](http://image.iswbm.com/20190526182125.png) ```python def get_transport(conf, url=None, allowed_remote_exmods=None): @@ -513,25 +513,25 @@ rpc.call 和 rpc.cast 从实现代码上看,他们的区别很小,就是call rpc server 要获取消息,需要定义target,就像一个门牌号一样。 - ![](http://image.python-online.cn/20190526184854.png) + ![](http://image.iswbm.com/20190526184854.png) rpc client 要发送消息,也需要有target,说明消息要发到哪去。 - ![](http://image.python-online.cn/20190526185217.png) + ![](http://image.iswbm.com/20190526185217.png) - endpoints:是可供别人远程调用的对象 - RPC服务器暴露出endpoint,每个 endpoint 包涵一系列的可被远程客户端通过 transport 调用的方法。直观理解,可以参考nova-conductor创建rpc server的代码,这边的endpoints就是 `nova/manager.py:ConductorManager()`![](http://image.python-online.cn/20190526221219.png) + RPC服务器暴露出endpoint,每个 endpoint 包涵一系列的可被远程客户端通过 transport 调用的方法。直观理解,可以参考nova-conductor创建rpc server的代码,这边的endpoints就是 `nova/manager.py:ConductorManager()`![](http://image.iswbm.com/20190526221219.png) -- dispatcher:分发器,这是 rpc server 才有的概念 ![](http://image.python-online.cn/20190526220809.png)只有通过它 server 端才知道接收到的rpc请求,要交给谁处理,怎么处理? +- dispatcher:分发器,这是 rpc server 才有的概念 ![](http://image.iswbm.com/20190526220809.png)只有通过它 server 端才知道接收到的rpc请求,要交给谁处理,怎么处理? 在client端,是这样指定要调用哪个方法的。 - ![](http://image.python-online.cn/20190527220820.png) + ![](http://image.iswbm.com/20190527220820.png) 而在server端,是如何知道要执行这个方法的呢?这就是dispatcher 要干的事,它从 endpoint 里找到这个方法,然后执行,最后返回。 - ![](http://image.python-online.cn/20190527220012.png) + ![](http://image.iswbm.com/20190527220012.png) - Serializer:在 python 对象和message(notification) 之间数据做序列化或是反序列化的基类。 @@ -636,29 +636,29 @@ server.wait() 首先在这里先定义合法的 `notification_event_types`,相当于添加白名单。 -![](http://image.python-online.cn/20190526172514.png) +![](http://image.iswbm.com/20190526172514.png) 然后在调用处,使用 `rpc.get_notifier` 来发送消息给ceilometer。 -![](http://image.python-online.cn/20190526172725.png) +![](http://image.iswbm.com/20190526172725.png) 继续查看 `rpc.get_notifier` 做了什么事?如何实现直接info 就能发送消息的。 -![](http://image.python-online.cn/20190526173314.png) +![](http://image.iswbm.com/20190526173314.png) 当你使用的event_types 不在白名单内,或者是异常信息。就会给打印warn日志 -![](http://image.python-online.cn/20190526175100.png) +![](http://image.iswbm.com/20190526175100.png) 在rabbit里查看队列,notification 是 topic -![](http://image.python-online.cn/20190526180708.png) +![](http://image.iswbm.com/20190526180708.png) 而 debug ,info 等是event priority -![](http://image.python-online.cn/20190526181433.png) +![](http://image.iswbm.com/20190526181433.png) diff --git a/source/c08/c08_09.rst b/source/c08/c08_09.rst index f0c6701..8309c93 100644 --- a/source/c08/c08_09.rst +++ b/source/c08/c08_09.rst @@ -543,7 +543,7 @@ messaging也对RPC API 进行了重新设计,对多种 transport 2. oslo.messaging.notify(实现了事件的通知机制) 因为 notify -实现是太简单了,所以这里我就不多说了,如果有人想要看这方面内容,可以收藏我的博客(http://python-online.cn) +实现是太简单了,所以这里我就不多说了,如果有人想要看这方面内容,可以收藏我的博客(http://iswbm.com) ,我会更新补充 notify 的内容。 OpenStack RPC 模块提供了 rpc.call,rpc.cast, rpc.fanout_cast 三种 RPC @@ -787,26 +787,26 @@ rpc server 和rpc client 的四个重要方法 |image22| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190623185008.png -.. |image2| image:: http://image.python-online.cn/20190623165341.png -.. |image3| image:: http://image.python-online.cn/20190623171138.png -.. |image4| image:: http://image.python-online.cn/20190623155955.png -.. |image5| image:: http://image.python-online.cn/20190623162725.png -.. |image6| image:: http://image.python-online.cn/20190623191042.png -.. |image7| image:: http://image.python-online.cn/20190630160025.png -.. |image8| image:: http://image.python-online.cn/20190623201427.png -.. |image9| image:: http://image.python-online.cn/20190526182125.png -.. |image10| image:: http://image.python-online.cn/20190526184854.png -.. |image11| image:: http://image.python-online.cn/20190526185217.png -.. |image12| image:: http://image.python-online.cn/20190526221219.png -.. |image13| image:: http://image.python-online.cn/20190526220809.png -.. |image14| image:: http://image.python-online.cn/20190527220820.png -.. |image15| image:: http://image.python-online.cn/20190527220012.png -.. |image16| image:: http://image.python-online.cn/20190526172514.png -.. |image17| image:: http://image.python-online.cn/20190526172725.png -.. |image18| image:: http://image.python-online.cn/20190526173314.png -.. |image19| image:: http://image.python-online.cn/20190526175100.png -.. |image20| image:: http://image.python-online.cn/20190526180708.png -.. |image21| image:: http://image.python-online.cn/20190526181433.png +.. |image1| image:: http://image.iswbm.com/20190623185008.png +.. |image2| image:: http://image.iswbm.com/20190623165341.png +.. |image3| image:: http://image.iswbm.com/20190623171138.png +.. |image4| image:: http://image.iswbm.com/20190623155955.png +.. |image5| image:: http://image.iswbm.com/20190623162725.png +.. |image6| image:: http://image.iswbm.com/20190623191042.png +.. |image7| image:: http://image.iswbm.com/20190630160025.png +.. |image8| image:: http://image.iswbm.com/20190623201427.png +.. |image9| image:: http://image.iswbm.com/20190526182125.png +.. |image10| image:: http://image.iswbm.com/20190526184854.png +.. |image11| image:: http://image.iswbm.com/20190526185217.png +.. |image12| image:: http://image.iswbm.com/20190526221219.png +.. |image13| image:: http://image.iswbm.com/20190526220809.png +.. |image14| image:: http://image.iswbm.com/20190527220820.png +.. |image15| image:: http://image.iswbm.com/20190527220012.png +.. |image16| image:: http://image.iswbm.com/20190526172514.png +.. |image17| image:: http://image.iswbm.com/20190526172725.png +.. |image18| image:: http://image.iswbm.com/20190526173314.png +.. |image19| image:: http://image.iswbm.com/20190526175100.png +.. |image20| image:: http://image.iswbm.com/20190526180708.png +.. |image21| image:: http://image.iswbm.com/20190526181433.png .. |image22| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_10.md b/source/c08/c08_10.md index b5fdd36..cb3c029 100644 --- a/source/c08/c08_10.md +++ b/source/c08/c08_10.md @@ -6,7 +6,7 @@ ## 8.16.1 使用 guestfish -![](http://image.python-online.cn/20191111112221.png) +![](http://image.iswbm.com/20191111112221.png) guestfish 里的命令有限,大概会 600 多个,不能 cd,操作文件时,需要使用绝对路径。 @@ -14,10 +14,10 @@ guestfish 里的命令有限,大概会 600 多个,不能 cd,操作文件 ## 8.16.2 使用 guestmount -![](http://image.python-online.cn/20191111112421.png) +![](http://image.iswbm.com/20191111112421.png) 使用完后,记得 umount /home/wangbm/mount ## 8.16.3 使用 virt-* 命令 -![](http://image.python-online.cn/20191111112548.png) \ No newline at end of file +![](http://image.iswbm.com/20191111112548.png) \ No newline at end of file diff --git a/source/c08/c08_10.rst b/source/c08/c08_10.rst index 46e08f3..8f66fff 100644 --- a/source/c08/c08_10.rst +++ b/source/c08/c08_10.rst @@ -26,7 +26,7 @@ cd,操作文件时,需要使用绝对路径。 |image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20191111112221.png -.. |image2| image:: http://image.python-online.cn/20191111112421.png -.. |image3| image:: http://image.python-online.cn/20191111112548.png +.. |image1| image:: http://image.iswbm.com/20191111112221.png +.. |image2| image:: http://image.iswbm.com/20191111112421.png +.. |image3| image:: http://image.iswbm.com/20191111112548.png diff --git a/source/c08/c08_11.md b/source/c08/c08_11.md index 05883df..9bf4b88 100644 --- a/source/c08/c08_11.md +++ b/source/c08/c08_11.md @@ -14,7 +14,7 @@ error: Domain not found: no domain with matching name 'instance-00000699' `/var/log/messages` 报错如下 -![](http://image.python-online.cn/20190530175817.png) +![](http://image.iswbm.com/20190530175817.png) 解决方法 diff --git a/source/c08/c08_11.rst b/source/c08/c08_11.rst index 432b22e..a40a42e 100644 --- a/source/c08/c08_11.rst +++ b/source/c08/c08_11.rst @@ -87,6 +87,6 @@ ConfigDrive 是一个 iso9660 格式的文件,只读。 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190530175817.png +.. |image1| image:: http://image.iswbm.com/20190530175817.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_12.md b/source/c08/c08_12.md index 1ad11bb..5a3ef06 100644 --- a/source/c08/c08_12.md +++ b/source/c08/c08_12.md @@ -18,11 +18,11 @@ 从源代码中看,最开始是 nova-conductor (nova/conductor/manager.py)在给 nova-compute 发创建请求前,会先让 nova-scheduler 选出一台资源充足的计算节点。 -![](http://image.python-online.cn/20190424212211.png) +![](http://image.iswbm.com/20190424212211.png) nova-scheduler 的调度主要由两部分组成(nova/scheduler/filter_scheduler.py:FilterScheduler._schedule()) -![](http://image.python-online.cn/20190424213430.png) +![](http://image.iswbm.com/20190424213430.png) - 过滤器:filter,将不满足条件(硬性条件,比如内存,cpu,磁盘,pci设备等)的计算节点,直接过滤掉。意义:从过滤器出来的那些计算节点,理论上都可以创建虚拟机。 - 称重器:weigher,对满足硬性条件的众多主机按照一定的规则进行权重配比。意义:经过称重器计算,选出你更希望在哪台节点上创建虚拟机。 @@ -31,23 +31,23 @@ nova-scheduler 的调度主要由两部分组成(nova/scheduler/filter_schedul - hosts:多个 host_state 的集合,包含有当前可用的计算节点信息(资源,ip等)。其中单个元素是 HostState (nova/scheduler/host_manager.py)类的实例。如果你想添加其他原来没有的信息,比如 compute 的 id,可以在 `_update_from_compute_node` 函数中添加。它会从compute_nodes 表中取得你想要的信息。 - ![](http://image.python-online.cn/20190424214653.png) + ![](http://image.iswbm.com/20190424214653.png) - spec_obj:你所要请求创建的虚拟机信息(模板,镜像等)。它是从 objects.RequestSpec.from_primitives 中取得的 - ![](http://image.python-online.cn/20190424214540.png) + ![](http://image.iswbm.com/20190424214540.png) 过滤器,它的代码如下: -![](http://image.python-online.cn/20190424221602.png) +![](http://image.iswbm.com/20190424221602.png) 称重器,它的规则主要看这段代码。 -![](http://image.python-online.cn/20190424215735.png) +![](http://image.iswbm.com/20190424215735.png) 我在代码中,加了几段日志。从左到右,三个不同颜色的内容分别为,原始权值,配重系数(越高说明越占比越大,越影响最终结果),经过 nomalize 后的权值(只有 0 和 1)。 -![](http://image.python-online.cn/20190424220008.png) +![](http://image.iswbm.com/20190424220008.png) 那最终的权值如何计算呢? @@ -66,7 +66,7 @@ LOG.debug("Selected host: %(host)s", {'host': chosen_host}) 当指定宿主机进行虚拟机的创建后,以上所有的过滤器都会无效(不会走代码)。 -![](http://image.python-online.cn/20191011103832.png) +![](http://image.iswbm.com/20191011103832.png) --- diff --git a/source/c08/c08_12.rst b/source/c08/c08_12.rst index be4958c..aca845c 100644 --- a/source/c08/c08_12.rst +++ b/source/c08/c08_12.rst @@ -91,13 +91,13 @@ nova-scheduler 选择到主机后,在日志中会打印三条DEBUG信息,可 |image9| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190424212211.png -.. |image2| image:: http://image.python-online.cn/20190424213430.png -.. |image3| image:: http://image.python-online.cn/20190424214653.png -.. |image4| image:: http://image.python-online.cn/20190424214540.png -.. |image5| image:: http://image.python-online.cn/20190424221602.png -.. |image6| image:: http://image.python-online.cn/20190424215735.png -.. |image7| image:: http://image.python-online.cn/20190424220008.png -.. |image8| image:: http://image.python-online.cn/20191011103832.png +.. |image1| image:: http://image.iswbm.com/20190424212211.png +.. |image2| image:: http://image.iswbm.com/20190424213430.png +.. |image3| image:: http://image.iswbm.com/20190424214653.png +.. |image4| image:: http://image.iswbm.com/20190424214540.png +.. |image5| image:: http://image.iswbm.com/20190424221602.png +.. |image6| image:: http://image.iswbm.com/20190424215735.png +.. |image7| image:: http://image.iswbm.com/20190424220008.png +.. |image8| image:: http://image.iswbm.com/20191011103832.png .. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_13.md b/source/c08/c08_13.md index a472ab7..246870f 100644 --- a/source/c08/c08_13.md +++ b/source/c08/c08_13.md @@ -56,7 +56,7 @@ Netfilter是Linux操作系统核心层内部的一个数据包处理模块,它 **动作** -![](http://image.python-online.cn/20190706114314.png) +![](http://image.iswbm.com/20190706114314.png) ### 命令相关 @@ -87,7 +87,7 @@ Chain POSTROUTING (policy ACCEPT 21M packets, 1241M bytes) 多出来的几个字段如何理解呢?我摘取《[朱双印的个人日志](http://www.zsythink.net/archives/1493)》的解释到这边。 -![](http://image.python-online.cn/20190706093904.png) +![](http://image.iswbm.com/20190706093904.png) iptables 默认为我们配置了域名反解(根据ip解析成域名),这个过程效率很低,我们可以指定参数 `-n` 跳过这个过程,从下面的例子可以看到 `in` 从上面的 `any` 变成了 `*` (0.0.0.0) @@ -195,7 +195,7 @@ iptables -t filter -I INPUT 2 -s 172.20.20.202 -j REJECT iptables -t filter -P FORWARD DROP ``` -![](http://image.python-online.cn/20190706160632.png) +![](http://image.iswbm.com/20190706160632.png) 保存规则 @@ -229,7 +229,7 @@ arp的中文释义是地址解析协议,全英文 address resolution protocol `arp -e` 是一个很常用的命令,用于查看与本机有过通信的机器的arp table,主要是 ip与mac地址的映射。如果在 /etc/hosts 里有填写ip与域名的对应关系,Address一列就会显示域名。你也可以使用 `arp -a` 实现相同功能,只是 `-a` 是标准输出格式,没有像使用 `-e` 一样类似表格一样的输出效果。 -![](http://image.python-online.cn/20190804162402.png) +![](http://image.iswbm.com/20190804162402.png) 此时,我们ping一下 172.20.22.3 这个ip,显然是可以的,因为这里的其对应的mac地址是真实准确的。 diff --git a/source/c08/c08_13.rst b/source/c08/c08_13.rst index c61670f..cd83c95 100644 --- a/source/c08/c08_13.rst +++ b/source/c08/c08_13.rst @@ -317,9 +317,9 @@ cache里没有这个ip,就会重新发送arp广播,获取到正确的mac地 |image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190706114314.png -.. |image2| image:: http://image.python-online.cn/20190706093904.png -.. |image3| image:: http://image.python-online.cn/20190706160632.png -.. |image4| image:: http://image.python-online.cn/20190804162402.png +.. |image1| image:: http://image.iswbm.com/20190706114314.png +.. |image2| image:: http://image.iswbm.com/20190706093904.png +.. |image3| image:: http://image.iswbm.com/20190706160632.png +.. |image4| image:: http://image.iswbm.com/20190804162402.png .. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_14.md b/source/c08/c08_14.md index c12b178..a8fc4fc 100644 --- a/source/c08/c08_14.md +++ b/source/c08/c08_14.md @@ -112,7 +112,7 @@ use_ipv6=true 才能将 IPv6 的 Port 信息写入ConfigDrive,也只有这样cloudinit才能自动为我们配置上 IPv6 的 ip。 -![](http://image.python-online.cn/20190716175250.png) +![](http://image.iswbm.com/20190716175250.png) 而对于 Neutron 来说,要使用 IPv6,当然要先创建一个 IPv6 的子网,这里要注意,IPv6 子网所属的网络,必须为flat,这个一定要注意,因为你不指定的话,默认就为 vlan。 @@ -195,11 +195,11 @@ neutron port-create --fixed-ip \ 在一张网卡上配置多个版本的IP(一个IPv4和一个IPv6),只在一个配置文件中配置就可以支持,因此cloudinit的处理的时候,会将同一个tap设备的归为一个同一张网卡。 -![](http://image.python-online.cn/20190716180655.png) +![](http://image.iswbm.com/20190716180655.png) 也就是说,cloudinit本身很轻松地就可以支持单网卡多版本IP的配置。 -![](http://image.python-online.cn/20190716180952.png) +![](http://image.iswbm.com/20190716180952.png) 而对于多线的IP,由于 Nova 写入ConfigDrive时,尽管一个Port上有多个IPv4或者多个IPv6,同个版本的都会只取第一个写入,自然从cloudinit那侧就无法进一步操作。 @@ -233,7 +233,7 @@ neutron port-create --fixed-ip \ }], ``` -- Neutron 接口可以不改,因为创建Port的接口参数中的 fixed_ips 是一个数组类型,所以我们可以把这些版本信息,运营商信息放进这个数组的每一个元素中。![](http://image.python-online.cn/20190804110647.png) +- Neutron 接口可以不改,因为创建Port的接口参数中的 fixed_ips 是一个数组类型,所以我们可以把这些版本信息,运营商信息放进这个数组的每一个元素中。![](http://image.iswbm.com/20190804110647.png) 所以我们只要修改Neutron 创建 Port 的代码逻辑。 @@ -245,7 +245,7 @@ neutron port-create --fixed-ip \ 最后另外再说一点无关紧要的,cloudinit 解析后,每张网卡一个ip与一张网卡两个ip的区别如下。 -![](http://image.python-online.cn/20190716180726.png) +![](http://image.iswbm.com/20190716180726.png) @@ -259,39 +259,39 @@ ovs-dpctl del-dp ovs-system && rmmod openvswitch && insmod ./openvswitch.ko && CentOS 6.x 在使用 ipv6 方面有问题 -![](http://image.python-online.cn/20190829103805.png) +![](http://image.iswbm.com/20190829103805.png) 导致在网卡配置文件里的配置的ip 都为none -![](http://image.python-online.cn/20190829104544.png) +![](http://image.iswbm.com/20190829104544.png) 上面的 callbak 是 这个函数 -![](http://image.python-online.cn/20190829104806.png) +![](http://image.iswbm.com/20190829104806.png) centos6.x 的网络信息不是从 latest 里的 network_data.json 里读取的,而是从 content/0000 里读取 -![](http://image.python-online.cn/20190829110541.png) +![](http://image.iswbm.com/20190829110541.png) 这是因为 centos 6.x 配置网络是在 local 阶段做的,而local阶段做的话,就会从 content/0000 -![](http://image.python-online.cn/20190829105558.png) +![](http://image.iswbm.com/20190829105558.png) 而 centos7.x 其实也会走 on_first_boot 去配置网络,但是cloudinit 在centos7.x 里 sources.DSMODE_PASS -![](http://image.python-online.cn/20190829112446.png) +![](http://image.iswbm.com/20190829112446.png) 导致在 local 阶段,这里配置网络被pass掉,自然也就不会从 content/0000 读取了。 -![](http://image.python-online.cn/20190829111917.png) +![](http://image.iswbm.com/20190829111917.png) centos 6.x 配置网络是在 on_first_boot 函数里,这是 local 阶段就会执行的。 -![](http://image.python-online.cn/20190829161243.png) +![](http://image.iswbm.com/20190829161243.png) @@ -299,7 +299,7 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local 阶段就 如果你给你的ubuntu配置上了ipv6的ip,那当你使用 apt-get install 软件包的时候,会使用ipv6去源安装,这将会使你的安装过程卡住: -![](http://image.python-online.cn/20190926171038.png) +![](http://image.iswbm.com/20190926171038.png) 如何解决这个问题呢? diff --git a/source/c08/c08_14.rst b/source/c08/c08_14.rst index aec531c..43552b4 100644 --- a/source/c08/c08_14.rst +++ b/source/c08/c08_14.rst @@ -328,19 +328,19 @@ centos 6.x 配置网络是在 on_first_boot 函数里,这是 local |image15| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190716175250.png -.. |image2| image:: http://image.python-online.cn/20190716180655.png -.. |image3| image:: http://image.python-online.cn/20190716180952.png -.. |image4| image:: http://image.python-online.cn/20190804110647.png -.. |image5| image:: http://image.python-online.cn/20190716180726.png -.. |image6| image:: http://image.python-online.cn/20190829103805.png -.. |image7| image:: http://image.python-online.cn/20190829104544.png -.. |image8| image:: http://image.python-online.cn/20190829104806.png -.. |image9| image:: http://image.python-online.cn/20190829110541.png -.. |image10| image:: http://image.python-online.cn/20190829105558.png -.. |image11| image:: http://image.python-online.cn/20190829112446.png -.. |image12| image:: http://image.python-online.cn/20190829111917.png -.. |image13| image:: http://image.python-online.cn/20190829161243.png -.. |image14| image:: http://image.python-online.cn/20190926171038.png +.. |image1| image:: http://image.iswbm.com/20190716175250.png +.. |image2| image:: http://image.iswbm.com/20190716180655.png +.. |image3| image:: http://image.iswbm.com/20190716180952.png +.. |image4| image:: http://image.iswbm.com/20190804110647.png +.. |image5| image:: http://image.iswbm.com/20190716180726.png +.. |image6| image:: http://image.iswbm.com/20190829103805.png +.. |image7| image:: http://image.iswbm.com/20190829104544.png +.. |image8| image:: http://image.iswbm.com/20190829104806.png +.. |image9| image:: http://image.iswbm.com/20190829110541.png +.. |image10| image:: http://image.iswbm.com/20190829105558.png +.. |image11| image:: http://image.iswbm.com/20190829112446.png +.. |image12| image:: http://image.iswbm.com/20190829111917.png +.. |image13| image:: http://image.iswbm.com/20190829161243.png +.. |image14| image:: http://image.iswbm.com/20190926171038.png .. |image15| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c08/c08_15.md b/source/c08/c08_15.md index c1b1bfb..8a302ce 100644 --- a/source/c08/c08_15.md +++ b/source/c08/c08_15.md @@ -2,27 +2,27 @@ ![](http://image.iswbm.com/20200602135014.png) -neutron api 的入口是在这里![](http://image.python-online.cn/20190804111844.png) +neutron api 的入口是在这里![](http://image.iswbm.com/20190804111844.png) -在这里会校验并打印请求的信息![](http://image.python-online.cn/20190804111715.png) +在这里会校验并打印请求的信息![](http://image.iswbm.com/20190804111715.png) -而对网络、子网、port操作的逻辑处理代码的入口都是从这里开始的![](http://image.python-online.cn/20190803181706.png) +而对网络、子网、port操作的逻辑处理代码的入口都是从这里开始的![](http://image.iswbm.com/20190803181706.png) ## 8.15.1 创建Port -从 neutron api 请求过来后,就会经过这里![](http://image.python-online.cn/20190803182042.png) +从 neutron api 请求过来后,就会经过这里![](http://image.iswbm.com/20190803182042.png) -把 port 信息打印一下![](http://image.python-online.cn/20190803182223.png) +把 port 信息打印一下![](http://image.iswbm.com/20190803182223.png) 点进上面的` self._create_port_db()`,可以看到这里先是创建了一个空壳的port -![](http://image.python-online.cn/20190804091016.png) +![](http://image.iswbm.com/20190804091016.png) -再分配ip![](http://image.python-online.cn/20190804091226.png) +再分配ip![](http://image.iswbm.com/20190804091226.png) 上图有一个函数 `_allocate_ips_for_port`,相当重要,一般人只要从这里关注即可 -![](http://image.python-online.cn/20190804094131.png) +![](http://image.iswbm.com/20190804094131.png) 它会先根据port创建请求里的内容,去数据库中一一比对,找出符合条件的子网,当然在找的过程中,会校验请求参数的准确性,比如它是指定ip和subnet创建的port,那么会检查这个ip是否在subnet内。 @@ -30,9 +30,9 @@ neutron api 的入口是在这里![](http://image.python-online.cn/2019080411184 校验完参数后,会把创建这个port所需的信息都整理到最后返回的 fixed_ip_list 里。如果指定了ip,这个list里的元素就会有 ip_address,否则就只有 subnet_id。 -![](http://image.python-online.cn/20190804092214.png) +![](http://image.iswbm.com/20190804092214.png) -![](http://image.python-online.cn/20190804091911.png) +![](http://image.iswbm.com/20190804091911.png) @@ -42,11 +42,11 @@ neutron api 的入口是在这里![](http://image.python-online.cn/2019080411184 当不指定子网、也不指定ip时(也就是不传fixed_ip),而且 cluster 里 即 有ipv4 的子网也有ipv6的子网,这时候 neutron 会去创建一个既有ipv4也有ipv6的port,只要有一个version的子网里没有可用ip,都会失败。 -![](http://image.python-online.cn/20190809213209.png) +![](http://image.iswbm.com/20190809213209.png) 这里是遍历一个version的所有的子网列表,只要能找到一个子网有可用ip,就返回,如果遍历完都没有找到ip,那就要抛出异常了。 -![](http://image.python-online.cn/20190809213223.png) +![](http://image.iswbm.com/20190809213223.png) ```python class IpAddressGenerationFailureAllSubnets(IpAddressGenerationFailure): diff --git a/source/c08/c08_15.rst b/source/c08/c08_15.rst index 68fb625..219142c 100644 --- a/source/c08/c08_15.rst +++ b/source/c08/c08_15.rst @@ -57,16 +57,16 @@ subnet_id。 message = _("No more IP addresses available.") .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20190804111844.png -.. |image2| image:: http://image.python-online.cn/20190804111715.png -.. |image3| image:: http://image.python-online.cn/20190803181706.png -.. |image4| image:: http://image.python-online.cn/20190803182042.png -.. |image5| image:: http://image.python-online.cn/20190803182223.png -.. |image6| image:: http://image.python-online.cn/20190804091016.png -.. |image7| image:: http://image.python-online.cn/20190804091226.png -.. |image8| image:: http://image.python-online.cn/20190804094131.png -.. |image9| image:: http://image.python-online.cn/20190804092214.png -.. |image10| image:: http://image.python-online.cn/20190804091911.png -.. |image11| image:: http://image.python-online.cn/20190809213209.png -.. |image12| image:: http://image.python-online.cn/20190809213223.png +.. |image1| image:: http://image.iswbm.com/20190804111844.png +.. |image2| image:: http://image.iswbm.com/20190804111715.png +.. |image3| image:: http://image.iswbm.com/20190803181706.png +.. |image4| image:: http://image.iswbm.com/20190803182042.png +.. |image5| image:: http://image.iswbm.com/20190803182223.png +.. |image6| image:: http://image.iswbm.com/20190804091016.png +.. |image7| image:: http://image.iswbm.com/20190804091226.png +.. |image8| image:: http://image.iswbm.com/20190804094131.png +.. |image9| image:: http://image.iswbm.com/20190804092214.png +.. |image10| image:: http://image.iswbm.com/20190804091911.png +.. |image11| image:: http://image.iswbm.com/20190809213209.png +.. |image12| image:: http://image.iswbm.com/20190809213223.png diff --git a/source/c09/c09_01.md b/source/c09/c09_01.md index a4321dd..e8ec5cc 100644 --- a/source/c09/c09_01.md +++ b/source/c09/c09_01.md @@ -25,11 +25,11 @@ 前段时间,在微博上刷到了一条推荐。内容是这样的 -![微博截图](http://image.python-online.cn/20200211211522.png) +![微博截图](http://image.iswbm.com/20200211211522.png) 出于好奇,我点开了,放大再放大,emmm,有点意思吖… -![img](http://image.python-online.cn/20200211211657.png) +![img](http://image.iswbm.com/20200211211657.png) 这四个字,对于像我这样腼腆的DS男来说,还真不好意思说,说出来,万一被拒绝了咋办? @@ -41,11 +41,11 @@ 这里我以一张高圆圆的图来做一下演示,原图是这样的(分辨率是:2000*1328)。 -![](http://image.python-online.cn/20200214104413.png) +![](http://image.iswbm.com/20200214104413.png) 使用我写好的脚本运行后,就生成了这样一张图,请你点击,放大再放大。(惊喜? -![](http://image.python-online.cn/save.jpeg) +![](http://image.iswbm.com/save.jpeg) 然后将这张图片发给你的女神,具体话术你自己想咯。 @@ -67,7 +67,7 @@ 用 Excel 画了个图,每一方格代表一个像素,其中若我的字体的大小设置 5(非字号5,而是每个字占用5个像素),效果大概就是如下这样子。 -![](http://image.python-online.cn/20200214104646.png) +![](http://image.iswbm.com/20200214104646.png) 我只要每个像素取出一个像素值,并使用这个像素做为该字的颜色即可,在像素量够多的情况下,从远处看,是能看到我们原来图像的轮廓的。 diff --git a/source/c09/c09_01.rst b/source/c09/c09_01.rst index 79a9a17..f016d28 100644 --- a/source/c09/c09_01.rst +++ b/source/c09/c09_01.rst @@ -25,14 +25,14 @@ 前段时间,在微博上刷到了一条推荐。内容是这样的 -.. figure:: http://image.python-online.cn/20200211211522.png +.. figure:: http://image.iswbm.com/20200211211522.png :alt: 微博截图 微博截图 出于好奇,我点开了,放大再放大,emmm,有点意思吖… -.. figure:: http://image.python-online.cn/20200211211657.png +.. figure:: http://image.iswbm.com/20200211211657.png :alt: img img @@ -155,8 +155,8 @@ |image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.python-online.cn/20200214104413.png -.. |image2| image:: http://image.python-online.cn/save.jpeg -.. |image3| image:: http://image.python-online.cn/20200214104646.png +.. |image1| image:: http://image.iswbm.com/20200214104413.png +.. |image2| image:: http://image.iswbm.com/save.jpeg +.. |image3| image:: http://image.iswbm.com/20200214104646.png .. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c09/c09_02.md b/source/c09/c09_02.md index 1ca292a..1e66691 100644 --- a/source/c09/c09_02.md +++ b/source/c09/c09_02.md @@ -99,13 +99,13 @@ Hazeover:干扰调节神器,把活动窗口除久的其他范围调暗 ## 4. 图片影音 -Snipaste:截图工具,支持钉图 +[Snipaste](https://bitbucket.org/liule/snipaste/downloads/):截图工具,支持钉图 iShot:截图工具,支持长截图/滚动截图,钉图 Capture Gif:Gif 录制(不推荐) -Kap Beta:录屏开源免费软件,支持GIF导出,快捷键:`Command Shift 5` +[Kap](https://getkap.co/):录屏开源免费软件,支持GIF导出,快捷键:`Command Shift 5` ScreenFlow:视频录制 @@ -121,14 +121,16 @@ Permute 3,精致小巧的视频格式转换工具。 PP 鸭,好用的多格式图片压缩软件。 +Squash for Mac:图片压缩(不能压缩GIF) + +ImageOptim:用过最好用的图片压缩软件(支持 Gif ) + GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 PDFGuru:PDF 阅读器 - - ## 5. 文件管理 FreeDownloadManagger:下载管理 @@ -163,6 +165,8 @@ iText,精准的 OCR 文字识别工具。 思维导图:MindNode,Xmind +[KeyCastr](https://github.com/keycastr/keycastr/releases):在录制视频或者 Gif 时显示你所按的键位 + ## 7. 系统管理 CCleaner:系统清理、软件卸载 diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index e85037a..2c68237 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -129,6 +129,10 @@ Permute 3,精致小巧的视频格式转换工具。 PP 鸭,好用的多格式图片压缩软件。 +Squash for Mac:图片压缩(不能压缩GIF) + +ImageOptim:用过最好用的图片压缩软件(支持 Gif ) + GIF Brewery 3,视频转Gif动图。Mac App Store 免费下载 PhotoBulk,图片批处理修改大小、格式、加水印 Mac App Store 购买下载 @@ -172,6 +176,9 @@ iText,精准的 OCR 文字识别工具。 思维导图:MindNode,Xmind +`KeyCastr `__\ :在录制视频或者 +Gif 时显示你所按的键位 + 7. 系统管理 ----------- diff --git a/source/c09/c09_06.md b/source/c09/c09_06.md new file mode 100644 index 0000000..90382c2 --- /dev/null +++ b/source/c09/c09_06.md @@ -0,0 +1,149 @@ +# 9.6 学习编程的好网站 + +![](http://image.iswbm.com/20200602135014.png) + +## 学习 Python + +### Python 中文学习大本营 + +网站连接:http://www.pythondoc.com/ + +![](http://image.iswbm.com/20200802110436.png) + +### Python Cookbook + +网站链接:https://python3-cookbook.readthedocs.io/zh_CN/latest/preface.html + +![](http://image.iswbm.com/20200802111158.png) + + + +### Scrapy Cookbook + +网站链接:https://scrapy-cookbook.readthedocs.io/zh_CN/latest/ + +![](http://image.iswbm.com/20200802111532.png) + + + +### PyCrumbs + +搜集了各种免费Python的资料。 + +网站链接:https://github.com/kirang89/pycrumbs + +![](http://image.iswbm.com/20200802113311.png) + + + +### 实验楼:Python + +网站链接:https://www.shiyanlou.com/ + +![](http://image.iswbm.com/20200802113658.png) + +### 魔法学院:Python + +网站链接:http://www.nowamagic.net/academy/category/13/ + +![](http://image.iswbm.com/20200802114241.png) + + + +### Python 3 标准库实例教程 + +**网站链接**:https://learnku.com/docs/pymotw + +![](http://image.iswbm.com/20200508201333.png) + + + +### Django Web 框架 + +网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django + +该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django) + +![](http://image.iswbm.com/20200525080531.png) + +在服务端网页编程里,重点介绍了 Django + +![](http://image.iswbm.com/20200525080715.png) + + + +### Python3 源码剖析 + +网站链接:https://flaggo.github.io/python3-source-code-analysis/ + +![](http://image.iswbm.com/image-20200701123010074.png) + +### 书栈网:Python + +网站链接:https://www.bookstack.cn/explore?cid=13&tab=popular + +![](http://image.iswbm.com/20200802114734.png) + +### 追梦人物:DRF 实战教程 + +专栏链接:https://zhuanlan.zhihu.com/djstudyteam + +![](http://image.iswbm.com/20200802120804.png) + +### Python Tips 刷题挑战 + +网站链接:http://www.pythontip.com/coding/code_oj + +![](http://image.iswbm.com/20200802121125.png) + + + +### Python Tips 设计模式 + +网站链接:http://www.pythontip.com/python-patterns/detail/abstract_factory + +![](http://image.iswbm.com/20200802121331.png) + +## 综合性网站 + +### 书栈网 + +在这个网站里,收录了很多优秀的技术书籍,你想要,想找的,基本上都能搜得出来。 + +**网站链接**:https://www.bookstack.cn/rank?tab=popular + +![](http://image.iswbm.com/20200104144109.png) + +### 魔法学院 + + **网站链接**:http://www.nowamagic.net/academy/ + +![](http://image.iswbm.com/20200112210558.png) + + + +## 学习 Linux + +### Linux 手册 + +网站链接:https://man.linuxde.net/ + +![](http://image.iswbm.com/image-20200704204307530.png) + +### 每天一个linux命令 + +网站链接:www.cnblogs.com/peida/archive/2012/12/05/2803591.html + +### 实验楼:Linux 基础入门 + +网站链接:https://www.shiyanlou.com/courses/1 + +![](http://image.iswbm.com/20200704204506.png) + +网站链接:https://www.shiyanlou.com/courses/68 + +![](http://image.iswbm.com/20200704204558.png) + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c09/c09_06.rst b/source/c09/c09_06.rst new file mode 100644 index 0000000..5819b52 --- /dev/null +++ b/source/c09/c09_06.rst @@ -0,0 +1,176 @@ +9.6 学习编程的好网站 +==================== + +|image0| + +学习 Python +----------- + +Python 中文学习大本营 +~~~~~~~~~~~~~~~~~~~~~ + +网站连接:http://www.pythondoc.com/ + +|image1| + +Python Cookbook +~~~~~~~~~~~~~~~ + +网站链接:https://python3-cookbook.readthedocs.io/zh_CN/latest/preface.html + +|image2| + +Scrapy Cookbook +~~~~~~~~~~~~~~~ + +网站链接:https://scrapy-cookbook.readthedocs.io/zh_CN/latest/ + +|image3| + +PyCrumbs +~~~~~~~~ + +搜集了各种免费Python的资料。 + +网站链接:https://github.com/kirang89/pycrumbs + +|image4| + +实验楼:Python +~~~~~~~~~~~~~~ + +网站链接:https://www.shiyanlou.com/ + +|image5| + +魔法学院:Python +~~~~~~~~~~~~~~~~ + +网站链接:http://www.nowamagic.net/academy/category/13/ + +|image6| + +Python 3 标准库实例教程 +~~~~~~~~~~~~~~~~~~~~~~~ + +**网站链接**\ :https://learnku.com/docs/pymotw + +|image7| + +Django Web 框架 +~~~~~~~~~~~~~~~ + +网站链接:https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Django + +该网站可以让你从0开始学习Web,包括前端(HTML,CSS,JS)、后端(Django) + +|image8| + +在服务端网页编程里,重点介绍了 Django + +|image9| + +Python3 源码剖析 +~~~~~~~~~~~~~~~~ + +网站链接:https://flaggo.github.io/python3-source-code-analysis/ + +|image10| + +书栈网:Python +~~~~~~~~~~~~~~ + +网站链接:https://www.bookstack.cn/explore?cid=13&tab=popular + +|image11| + +追梦人物:DRF 实战教程 +~~~~~~~~~~~~~~~~~~~~~~ + +专栏链接:https://zhuanlan.zhihu.com/djstudyteam + +|image12| + +Python Tips 刷题挑战 +~~~~~~~~~~~~~~~~~~~~ + +网站链接:http://www.pythontip.com/coding/code_oj + +|image13| + +Python Tips 设计模式 +~~~~~~~~~~~~~~~~~~~~ + +网站链接:http://www.pythontip.com/python-patterns/detail/abstract_factory + +|image14| + +综合性网站 +---------- + +书栈网 +~~~~~~ + +在这个网站里,收录了很多优秀的技术书籍,你想要,想找的,基本上都能搜得出来。 + +**网站链接**\ :https://www.bookstack.cn/rank?tab=popular + +|image15| + +魔法学院 +~~~~~~~~ + +**网站链接**\ :http://www.nowamagic.net/academy/ + +|image16| + +学习 Linux +---------- + +Linux 手册 +~~~~~~~~~~ + +网站链接:https://man.linuxde.net/ + +|image17| + +每天一个linux命令 +~~~~~~~~~~~~~~~~~ + +网站链接:www.cnblogs.com/peida/archive/2012/12/05/2803591.html + +实验楼:Linux 基础入门 +~~~~~~~~~~~~~~~~~~~~~~ + +网站链接:https://www.shiyanlou.com/courses/1 + +|image18| + +网站链接:https://www.shiyanlou.com/courses/68 + +|image19| + +|image20| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200802110436.png +.. |image2| image:: http://image.iswbm.com/20200802111158.png +.. |image3| image:: http://image.iswbm.com/20200802111532.png +.. |image4| image:: http://image.iswbm.com/20200802113311.png +.. |image5| image:: http://image.iswbm.com/20200802113658.png +.. |image6| image:: http://image.iswbm.com/20200802114241.png +.. |image7| image:: http://image.iswbm.com/20200508201333.png +.. |image8| image:: http://image.iswbm.com/20200525080531.png +.. |image9| image:: http://image.iswbm.com/20200525080715.png +.. |image10| image:: http://image.iswbm.com/image-20200701123010074.png +.. |image11| image:: http://image.iswbm.com/20200802114734.png +.. |image12| image:: http://image.iswbm.com/20200802120804.png +.. |image13| image:: http://image.iswbm.com/20200802121125.png +.. |image14| image:: http://image.iswbm.com/20200802121331.png +.. |image15| image:: http://image.iswbm.com/20200104144109.png +.. |image16| image:: http://image.iswbm.com/20200112210558.png +.. |image17| image:: http://image.iswbm.com/image-20200704204307530.png +.. |image18| image:: http://image.iswbm.com/20200704204506.png +.. |image19| image:: http://image.iswbm.com/20200704204558.png +.. |image20| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c10/c10_11.rst b/source/c10/c10_11.rst new file mode 100644 index 0000000..d9ac3d8 --- /dev/null +++ b/source/c10/c10_11.rst @@ -0,0 +1,324 @@ +10.11 如何申请数字证书,点亮你的 HTTPS? +======================================== + +|image0| + +上一节讲解了关于 SSL +数字证书的基础知识,这一篇就来实战演示一下,如何将你的 HTTP 网站加上 +HTTPS 。 + +要想使用 HTTPS ,要做的事情还是有点多的: + +1. 准备一个域名和服务器(自行准备) +2. 并将该域名解析到你的服务器ip上 +3. 生成 CSR 证书请求文件 +4. 拿着 CSR 文件去申请证书 +5. 把申请到的证书部署到你的服务器上 + +1. 设置 DNS 解析 +---------------- + +我有一个域名 ``iswbm.com``\ ,其下有好多的子域名,比如写 Python +的在线博客 ``python.iswbm.com``\ ,还有写 Golang 的在线博客 +``golang.iswbm.com``\ ,还有我的图床 ``image.iswbm.com`` 等等。 + +这里新建了个子域名 ``demo.iswbm.com`` ,并设置其解析到 +我的服务器上,如下图(服务器 ip 已码掉)。 + +|image1| + +2. 搭建 HTTP 站点 +----------------- + +为了方便,我这里就使用 Apache 放了一个 HTTP +的静态网页,方法很简单,大家百度即可。 + +|image2| + +3. 申请 SSL 证书 +---------------- + +SSL 数字证书怎么来的呢? + +你可以自己制作,也可以向CA权威机构申请。 + +二者的区别是: + +1. 自己颁发的证书,需要客户端验证通过,也就是需要用户手动安装证书,并将其设置为受信任的根证书。但即使如此,浏览器上( + chrome, + firefox)仍不认可这种自签名证书,会在地址栏前面提示连接不安全,手动安装证书后,也会提示该证书无效。若想要继续访问,并忽略该提示,可以选择继续访问。 +2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是绝对可信任的。因此你在访问网站时,不会提示连接不安全。 + +下面,我将分别向你展示这两种方法,都是如何操作的。 + +第一种:向权威CA机构申请 +~~~~~~~~~~~~~~~~~~~~~~~~ + +在阿里云和腾讯云都可以 进行 SSL +证书的申请,证书的申请有付费的(价格也不便宜),也有免费的,看了一圈,只有腾讯云有免费的 +SSL 证书的申请渠道(阿里云听说以前也有,后来关闭了)。 + +本篇文章,仅以演示教学之用,所以只用腾讯云的免费证书的就足够啦。 + +登陆腾讯云,可以看到SSL +证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 + +|image3| + +点击选择 ``域名型免费版`` + +|image4| + +点击 ``免费快速申请``\ 后,填写域名和你的个人邮箱 + +|image5| + +再点击下一步,会需要你验证域名所有权,验证方式有如下三种 + +1. 自动DNS验证 +2. 手动DNS验证 +3. 文件验证 + +但由于我的域名不是腾讯云平台解析的,因此没有 +自动DNS验证的选项,只有其他两种 + +|image6| + +点击 ``确认申请`` 后,会提示你进入域名验证所有权的流程,这里我选择 +手动DNS验证。 + +|image7| + +审核通过后,3s 内就会给你颁发证书,你可以从控制台点击证书下载。 + +|image8| + +下载下来的会是一个 zip 包。 + +解压一下,会有不同的服务器类型(有 +Apache、IIS、Nginx、Tomcat)的文件夹。 + +|image9| + +我使用的是 Apache ,在这个文件夹下面有三个文件: + +1. ``1_root_bundle.crt``\ :根证书 +2. ``2_demo.iswbm.com.crt``\ :域名证书 +3. ``3_demo.iswbm.com.key``\ :私钥文件 + +这三个文件,下一步会部署于我的服务器上。 + +接下来讲第二种 SSL 证书申请方式。 + +第二种:自签名的 SSL 证书 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +没有权威的第三方 CA 机构给自己颁发证书,那就自己给自己颁发咯。 + +自签名 SSL 的证书制作过程,可以分为两步: + +1. 自己要当 CA 机构,那 CA 有的 CA 根证书、私钥 + 一样都不能少,因此第一步:生成 CA 的 crt 证书 和 CA 的私钥。 +2. 要申请证书,首先服务器自己要有一个密钥对(公钥和私钥) +3. 拿着上面生成的公钥,制作一个 CSR 证书申请文件 +4. 用第一步的 CA 私钥,给这个 CSR 签名,生成咱所需要的 SSL + 数字证书文件。 + +步骤很多,命令很多,命令所带的参数更多,对于只想学习证书签发流程的你,把这些命令都背下来,并不是一个好的选择,毕竟这种事可能也干不了几次。 + +因此,我把这些步骤、命令,都整合成一个 shell +脚本,你只要执行这个脚本就行了。 + +.. code:: shell + + $ bash create_self-signed-cert.sh --ssl-domain=demo.iswbm.com --ssl-trusted-domain=demo.iswbm.com --ssl-size=2048 --ssl-date=3650 + +对应的参数的解释,在脚本中都有解释 + +|image10| + +这个脚本过长,不好直接贴上来,我将它放在我的公众号(\ **Python编程时光**\ )后台,你可以直接回复『\ **证书签名**\ 』直接获取下载。 + +执行完成后,会在当前目录下生成好多个文件。 + +其中,只有两个文件对我们有用 + +|image11| + +4. 部署 SSL 证书 +---------------- + +根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 + +|image12| + +这里我将以 CentOS 7.2 + Apache 为例,演示如何部署 SSL 证书。 + +先安装一下 mod_ssl + +.. code:: shell + + $ yum install -y mod_ssl + +安装完后,在 /etc/httpd/conf.d/ 目录下 会有个 ssl.conf 文件。 + +编辑修改这个文件,以下是我的配置供你参考 + +.. code:: shell + + + DocumentRoot "/var/www/html" + #填写证书名称 + ServerName demo.iswbm.com + #启用 SSL 功能 + SSLEngine on + #证书文件的路径 + SSLCertificateFile /etc/pki/tls/certs/demo.iswbm.com.crt + #私钥文件的路径 + SSLCertificateKeyFile /etc/pki/tls/private/demo.iswbm.com.key + #根证书文件的路径 + SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt + + +**如果你的证书是从权威 CA 机构上申请来的。** + +比如我上面从腾讯云上申请来的,那么这三个文件就是从已经从腾讯云的控制台上下载下来的那三个文件。 + +在修改完后,务必记得把下载的这三个文件,放到相应的目录中去。 + +|image13| + +配置完 ssl.conf,可能还需要你 check 一下 ``/etc/httpd/conf/httpd.conf`` +的一些配置,这些配置一般用默认的就可以,但是以防万一,还是写一下吧 + +:: + + Include conf.modules.d/*.conf + +写这一行的目的,就是为了 httpd 去加载 mod_ssl 这个模块 + +.. code:: shell + + $ cat /etc/httpd/conf.modules.d/00-ssl.conf + LoadModule ssl_module modules/mod_ssl.so + +一切配置完成后,记得重启一下 httpd 服务 + +.. code:: shell + + $ systemctl restart httpd + +然后使用 chrome 访问一下 ``https//demo.iswbm.com`` 看看,大功告成。 + +|image14| + +**而如果你的证书是自签名的。** + +ssl.conf 配置文件下的应该改成这样 + +:: + + + DocumentRoot "/var/www/html" + #填写证书名称 + ServerName demo.iswbm.com + #启用 SSL 功能 + SSLEngine on + #证书文件的路径 + SSLCertificateFile /etc/pki/tls/certs/tls.crt + #私钥文件的路径 + SSLCertificateKeyFile /etc/pki/tls/private/tls.key + + +同时记得把这两个文件也拷贝到相应的目录下 + +.. code:: shell + + $ cp tls.crt /etc/pki/tls/certs/ + $ cp tls.key /etc/pki/tls/private/ + +最后还是不要忘了重启 httpd + +.. code:: shell + + $ systemctl restart httpd + +试着用 chrome 访问一下,可以看到 chrome 提示该连接不安全 + +|image15| + +如果执意要访问,可以点击左下方的 +``继续前往``\ ,这样以后再访问的时候,就不会再出现这个警告页面了。 + +|image16| + +``不安全`` 三个字,让人很没有安全感,那有没有办法去掉呢? + +答案是,没有,只要是自签名的证书,在 chrome ,firefox +等主流浏览器看来都是不安全的。 + +即使你把这个根证书添加到你的受信任的证书列表中,也是徒然。 + +下面就试着来安装一下这个根证书。 + +按照下图指示,拖动证书到本地磁盘上。 + +|image17| + +打开 Mac 上的 ``钥匙串访问`` + +|image18| + +点击 ``登陆``\ ,然后再拖动这个证书到窗口中进行安装 + +|image19| + +右键该证书,点击 ``显示简介``\ ,跳出下面的界面后,再点击 ``信任``\ ,把 +IP 安全选择选为 ``始终信任``\ 。 + +|image20| + +设置完后,再访问下 ``demo.iswbm.com`` +,仍然显示连接不安全,并且证书是无效的 + +|image21| + +点击证书,显示证书,该证书确实已经放入信任列表中了。 + +|image22| + +参考文档 +-------- + +- `Apache + 服务器证书安装 `__ +- `自签名 SSL + 证书 `__ + +|image23| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200728233602.png +.. |image2| image:: http://image.iswbm.com/20200729230813.png +.. |image3| image:: http://image.iswbm.com/image-20200718102622663.png +.. |image4| image:: http://image.iswbm.com/image-20200718101358755.png +.. |image5| image:: http://image.iswbm.com/20200729232432.png +.. |image6| image:: http://image.iswbm.com/image-20200718101652899.png +.. |image7| image:: http://image.iswbm.com/20200729004207.png +.. |image8| image:: http://image.iswbm.com/20200729004307.png +.. |image9| image:: http://image.iswbm.com/20200729004456.png +.. |image10| image:: http://image.iswbm.com/20200729235153.png +.. |image11| image:: http://image.iswbm.com/20200730000142.png +.. |image12| image:: http://image.iswbm.com/20200718105347.png +.. |image13| image:: http://image.iswbm.com/20200730214826.png +.. |image14| image:: http://image.iswbm.com/20200730215613.png +.. |image15| image:: http://image.iswbm.com/20200730220835.png +.. |image16| image:: http://image.iswbm.com/20200730221745.png +.. |image17| image:: http://image.iswbm.com/20200728234740.png +.. |image18| image:: http://image.iswbm.com/20200730222441.png +.. |image19| image:: http://image.iswbm.com/20200728235331.png +.. |image20| image:: http://image.iswbm.com/20200730222700.png +.. |image21| image:: http://image.iswbm.com/20200730222827.png +.. |image22| image:: http://image.iswbm.com/20200730222928.png +.. |image23| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c10/c10_12.md b/source/c10/c10_12.md new file mode 100644 index 0000000..3066705 --- /dev/null +++ b/source/c10/c10_12.md @@ -0,0 +1,38 @@ +# 10.12 数据包的网络之旅 + +![](http://image.iswbm.com/20200602135014.png) + +当你在学习枯燥的网络基础知识的时候,一定有过这样的疑问吧? + +1、数据包是如何从你的电脑上,到达远端的服务器上的? + +3、OSI 七层到底是如何工作的?怎么把这么抽象的概念化为具体呢? + + + +这些个问题,在我刚刚接触计算机网络的时候,也是困扰了我许久,今天呢,我就围绕【当你在浏览器上敲入网址时,在计算机网络的世界里,都发生了哪些事?】这个问题,把上面的那些问题一个一个地给你解决了。 + + + +## 1. 应用层:浏览器 + +浏览器作用在 OSI 模型里的第七层,它的主要工作就是将你的请求封装成 HTTP 报文然后交给它的下一层,也就是传输层。 + +通过之前的学习,我们知道了 + +1. 传输层的 TCP 报文,需要浏览器进程的端口号(源端口)以及服务器端口号 (目标端口)。 +2. 网络层的 IP 报文,需要有本机的 ip 地址(源ip)以及服务器的ip地址(目标ip)。 + +这意味着,在浏览器调用 socket 模块发送 HTTP 报文前,需要提前准备好目标端口号和服务器的ip地址。 + +- **目标端口**:如果往地址栏输入 `localhost:8080`时,浏览器就知道你要请求的服务器端口是 8080 了,但是我们看到的更多是不带的,比如 `htpps:/www.baidu.com`,因为它们有默认端口号,http 协议的默认端口号是 80,而 https 的默认端口号是 443。 +- **目标ip**:正常我们往浏览器的地址栏输入的都不会直接输入ip,而是域名,比如 `https://www.baidu.com `,浏览器发现你输入了个域名后,会先封装一个DNS 报文, 走的 UDP 协议,去 DNS 服务器获取 `www.baidu.com` 的 ip 地址。 + +有了目标端口和目标 ip 后,自然就能封装 TCP 报文了。 + +## 2. 传输层:TCP 协议 + +HTTP 是基于 TCP 进行可靠传输的。 + + + diff --git a/source/c10/c10_12.rst b/source/c10/c10_12.rst new file mode 100644 index 0000000..cb1bb46 --- /dev/null +++ b/source/c10/c10_12.rst @@ -0,0 +1,48 @@ +10.12 数据包的网络之旅 +====================== + +|image0| + +当你在学习枯燥的网络基础知识的时候,一定有过这样的疑问吧? + +1、数据包是如何从你的电脑上,到达远端的服务器上的? + +3、OSI 七层到底是如何工作的?怎么把这么抽象的概念化为具体呢? + +这些个问题,在我刚刚接触计算机网络的时候,也是困扰了我许久,今天呢,我就围绕【当你在浏览器上敲入网址时,在计算机网络的世界里,都发生了哪些事?】这个问题,把上面的那些问题一个一个地给你解决了。 + +1. 应用层:浏览器 +----------------- + +浏览器作用在 OSI 模型里的第七层,它的主要工作就是将你的请求封装成 HTTP +报文然后交给它的下一层,也就是传输层。 + +通过之前的学习,我们知道了 + +1. 传输层的 TCP 报文,需要浏览器进程的端口号(源端口)以及服务器端口号 + (目标端口)。 +2. 网络层的 IP 报文,需要有本机的 ip + 地址(源ip)以及服务器的ip地址(目标ip)。 + +这意味着,在浏览器调用 socket 模块发送 HTTP +报文前,需要提前准备好目标端口号和服务器的ip地址。 + +- **目标端口**\ :如果往地址栏输入 + ``localhost:8080``\ 时,浏览器就知道你要请求的服务器端口是 8080 + 了,但是我们看到的更多是不带的,比如 + ``htpps:/www.baidu.com``\ ,因为它们有默认端口号,http + 协议的默认端口号是 80,而 https 的默认端口号是 443。 +- **目标ip**\ :正常我们往浏览器的地址栏输入的都不会直接输入ip,而是域名,比如 + ``https://www.baidu.com``\ ,浏览器发现你输入了个域名后,会先封装一个DNS + 报文, 走的 UDP 协议,去 DNS 服务器获取 ``www.baidu.com`` 的 ip + 地址。 + +有了目标端口和目标 ip 后,自然就能封装 TCP 报文了。 + +2. 传输层:TCP 协议 +------------------- + +HTTP 是基于 TCP 进行可靠传输的。 + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/preface.rst b/source/preface.rst index 4651dd5..3f37edf 100755 --- a/source/preface.rst +++ b/source/preface.rst @@ -7,8 +7,6 @@ ---------------------------------- 这个博客于2018年6月29日发布完成,使用的是 Sphinx 来生成文档,使用 Github 托管文档,并使用 Read the Doc 发布文档。 -具体搭建教程请查阅 博客构建教程_ - ---------------------------------- 作者的话 ---------------------------------- @@ -32,5 +30,3 @@ ------------------------------ .. figure:: http://image.iswbm.com/20200607174235.png - -.. _博客构建教程: http://python-online.cn/zh_CN/latest/c04/c04_03.html diff --git a/source/thanks.rst b/source/thanks.rst index b3f0b61..731c695 100644 --- a/source/thanks.rst +++ b/source/thanks.rst @@ -11,7 +11,7 @@ 所以在这里准备开一个页面,感谢这些朋友。另外,对文章有任何疑问的同学都可以加我微信交流。 -.. figure:: http://image.python-online.cn/20190704205721.png +.. figure:: http://image.iswbm.com/20190704205721.png :alt: 添加明哥好友 From 119823590bd1ae1fa64726d42e916bb705aad564 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 20 Sep 2020 19:54:47 +0800 Subject: [PATCH 120/147] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=98=9F=E5=88=97?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E4=B8=AD=E6=A1=88=E4=BE=8B=E7=9A=84=E5=B0=8F?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c02/c02_04.md | 101 +++++++++++++++++++++++++++++++----------- source/c02/c02_04.rst | 98 +++++++++++++++++++++++++++++----------- 2 files changed, 145 insertions(+), 54 deletions(-) diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 4049c74..643eac3 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -171,7 +171,7 @@ seeker: 被你找到了,哎~~~ 最后一个,队列,它是本节的重点,因为它是我们日常开发中最使用频率最高的。 -从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue 对象,这些线程通过使用`put()` 和 `get()` 操作来向队列中添加或者删除元素。 +从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue 对象,这些线程通过使用`put()` 和 `get()` 操作来向队列中发送和获取元素。 同样,对于Queue,我们也只需要掌握几个函数即可。 ```python @@ -180,73 +180,119 @@ from queue import Queue # 一旦>0,而消息数又达到限制,q.put()也将阻塞 q = Queue(maxsize=0) -# 阻塞程序,等待队列消息。 -q.get() +# 默认阻塞程序,等待队列消息,可设置超时时间 +q.get(block=True, timeout=None) -# 获取消息,设置超时时间 -q.get(timeout=5.0) - -# 发送消息 -q.put() +# 发送消息:默认会阻塞程序至队列中有空闲位置放入数据 +q.put(item, block=True, timeout=None) # 等待所有的消息都被消费完 q.join() -# 以下三个方法,知道就好,代码中不要使用 +# 通知队列任务处理已经完成,当所有任务都处理完成时,join() 阻塞将会解除 +q.task_done() + +``` +以下三个方法,知道就好,一般不需要使用 + +```python # 查询当前队列的消息个数 q.qsize() -# 队列消息是否都被消费完,True/False +# 队列消息是否都被消费完,返回 True/False q.empty() # 检测队列里消息是否已满 q.full() ``` + + + 函数会比之前的多一些,同时也从另一方面说明了其功能更加丰富。 我来举个老师点名的例子。 ```python +# coding=utf-8 +# /usr/bin/env python + +''' +Author: wangbm +Email: wongbingming@163.com +Wechat: mrbensonwon +Blog: python-online.cn +公众号:Python编程时光 + + +date: 2020/9/20 下午7:30 +desc: +''' + +__author__ = 'wangbm' + + from queue import Queue from threading import Thread import time -class Student(Thread): - def __init__(self, name, queue): - super().__init__() +class Student: + def __init__(self, name): self.name = name - self.queue = queue - def run(self): - while True: - # 阻塞程序,时刻监听老师,接收消息 - msg = self.queue.get() - # 一旦发现点到自己名字,就赶紧答到 - if msg == self.name: - print("{}:到!".format(self.name)) + def speak(self): + print("{}:到!".format(self.name)) class Teacher: def __init__(self, queue): + super().__init__() self.queue=queue def call(self, student_name): - print("老师:{}来了没?".format(student_name)) - # 发送消息,要点谁的名 + if student_name == "exit": + print("点名结束,开始上课..") + else: + print("老师:{}来了没?".format(student_name)) + # 发送消息,要点谁的名 self.queue.put(student_name) +class CallManager(Thread): + def __init__(self, queue): + super().__init__() + self.students = {} + self.queue = queue + + def put(self, student): + self.students.setdefault(student.name, student) + + def run(self): + while True: + # 阻塞程序,时刻监听老师,接收消息 + student_name = queue.get() + if student_name == "exit": + break + elif student_name in self.students: + self.students[student_name].speak() + else: + print("老师,咱班,没有 {} 这个人".format(student_name)) queue = Queue() teacher = Teacher(queue=queue) -s1 = Student(name="小明", queue=queue) -s2 = Student(name="小亮", queue=queue) -s1.start() -s2.start() + +s1 = Student(name="小明") +s2 = Student(name="小亮") + +cm = CallManager(queue) +cm.put(s1) +cm.put(s2) +cm.start() print('开始点名~') teacher.call('小明') time.sleep(1) teacher.call('小亮') +time.sleep(1) +teacher.call("exit") ``` 运行结果如下 ```python @@ -255,6 +301,7 @@ teacher.call('小亮') 小明:到! 老师:小亮来了没? 小亮:到! +点名结束,开始上课.. ``` 其实 queue 还有一个很重要的方法,Queue.task_done() diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 85e6579..38ee9d5 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -187,7 +187,7 @@ Condition和Event 是类似的,并没有多大区别。 从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue 对象,这些线程通过使用\ ``put()`` 和 ``get()`` -操作来向队列中添加或者删除元素。 +操作来向队列中发送和获取元素。 同样,对于Queue,我们也只需要掌握几个函数即可。 @@ -198,24 +198,27 @@ Condition和Event 是类似的,并没有多大区别。 # 一旦>0,而消息数又达到限制,q.put()也将阻塞 q = Queue(maxsize=0) - # 阻塞程序,等待队列消息。 - q.get() + # 默认阻塞程序,等待队列消息,可设置超时时间 + q.get(block=True, timeout=None) - # 获取消息,设置超时时间 - q.get(timeout=5.0) - - # 发送消息 - q.put() + # 发送消息:默认会阻塞程序至队列中有空闲位置放入数据 + q.put(item, block=True, timeout=None) # 等待所有的消息都被消费完 q.join() - # 以下三个方法,知道就好,代码中不要使用 + + # 通知队列任务处理已经完成,当所有任务都处理完成时,join() 阻塞将会解除 + q.task_done() + +以下三个方法,知道就好,一般不需要使用 + +.. code:: python # 查询当前队列的消息个数 q.qsize() - # 队列消息是否都被消费完,True/False + # 队列消息是否都被消费完,返回 True/False q.empty() # 检测队列里消息是否已满 @@ -227,46 +230,86 @@ Condition和Event 是类似的,并没有多大区别。 .. code:: python + # coding=utf-8 + # /usr/bin/env python + + ''' + Author: wangbm + Email: wongbingming@163.com + Wechat: mrbensonwon + Blog: python-online.cn + 公众号:Python编程时光 + + + date: 2020/9/20 下午7:30 + desc: + ''' + + __author__ = 'wangbm' + + from queue import Queue from threading import Thread import time - class Student(Thread): - def __init__(self, name, queue): - super().__init__() + class Student: + def __init__(self, name): self.name = name - self.queue = queue - def run(self): - while True: - # 阻塞程序,时刻监听老师,接收消息 - msg = self.queue.get() - # 一旦发现点到自己名字,就赶紧答到 - if msg == self.name: - print("{}:到!".format(self.name)) + def speak(self): + print("{}:到!".format(self.name)) class Teacher: def __init__(self, queue): + super().__init__() self.queue=queue def call(self, student_name): - print("老师:{}来了没?".format(student_name)) - # 发送消息,要点谁的名 + if student_name == "exit": + print("点名结束,开始上课..") + else: + print("老师:{}来了没?".format(student_name)) + # 发送消息,要点谁的名 self.queue.put(student_name) + class CallManager(Thread): + def __init__(self, queue): + super().__init__() + self.students = {} + self.queue = queue + + def put(self, student): + self.students.setdefault(student.name, student) + + def run(self): + while True: + # 阻塞程序,时刻监听老师,接收消息 + student_name = queue.get() + if student_name == "exit": + break + elif student_name in self.students: + self.students[student_name].speak() + else: + print("老师,咱班,没有 {} 这个人".format(student_name)) queue = Queue() teacher = Teacher(queue=queue) - s1 = Student(name="小明", queue=queue) - s2 = Student(name="小亮", queue=queue) - s1.start() - s2.start() + + s1 = Student(name="小明") + s2 = Student(name="小亮") + + cm = CallManager(queue) + cm.put(s1) + cm.put(s2) + cm.start() print('开始点名~') teacher.call('小明') time.sleep(1) teacher.call('小亮') + time.sleep(1) + teacher.call("exit") 运行结果如下 @@ -277,6 +320,7 @@ Condition和Event 是类似的,并没有多大区别。 小明:到! 老师:小亮来了没? 小亮:到! + 点名结束,开始上课.. 其实 queue 还有一个很重要的方法,Queue.task_done() From 6d2759540e67efdcbc895586a72f05c13d9b50c9 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 20 Sep 2020 19:57:18 +0800 Subject: [PATCH 121/147] update --- source/c01/c01_02.md | 126 ++++++++++++++++++++++++++++++++++++-- source/c01/c01_02.rst | 137 +++++++++++++++++++++++++++++++++++++++--- source/c01/c01_27.rst | 6 +- source/c04/c04_20.rst | 13 +++- source/c04/c04_21.rst | 14 +++++ source/c08/c08_06.rst | 89 +++++++++++++++++++++++++-- source/c09/c09_02.rst | 4 +- 7 files changed, 366 insertions(+), 23 deletions(-) diff --git a/source/c01/c01_02.md b/source/c01/c01_02.md index cb71e7a..ee7cb66 100644 --- a/source/c01/c01_02.md +++ b/source/c01/c01_02.md @@ -57,11 +57,7 @@ dir() 函数可能是 Python 自省机制中最著名的部分了。它返回传 ![](http://image.iswbm.com/image-20200606134519352.png) -### \__doc__ -使用 `__doc__` 这个魔法方法,可以查询该模块的文档,它输出的内容和 help() 一样。 - -![](http://image.iswbm.com/image-20200606134858285.png) ## 2. 应用到实际开发中 @@ -160,4 +156,126 @@ True +## 模块(Modules) + +### \__doc__ + +使用 `__doc__` 这个魔法方法,可以查询该模块的文档,它输出的内容和 help() 一样。 + +![](http://image.iswbm.com/image-20200606134858285.png) + +### \__name__ + +始终是定义时的模块名;即使你使用import .. as 为它取了别名,或是赋值给了另一个变量名。 + +```python +>>> import json +>>> json.__name__ +'json' +>>> +>>> import json as js +>>> js.__name__ +'json' +``` + +### \__file__ + +包含了该模块的文件路径。需要注意的是内建的模块没有这个属性,访问它会抛出异常! + +```python +>>> import json +>>> json.__file__ +'/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/json/__init__.py' +``` + + + +### \__dict__ + +包含了模块里可用的属性名-属性的字典;也就是可以使用模块名.属性名访问的对象。 + + + +## 类(Class) + +### \__doc__ + +文档字符串。如果类没有文档,这个值是None。 + +```python +>>> class People: +... ''' +... people class +... ''' +... +>>> p = People() +>>> +>>> print(p.__doc__) + + people class + +>>> +``` + + + +### \__name__ + +始终是定义时的类名。 + +```python +>>> People.__name__ +'People' +``` + + + +### \__dict__ + +包含了类里可用的属性名-属性的字典;也就是可以使用类名.属性名访问的对象。 + +```python +>>> People.__dict__ +mappingproxy({'__module__': '__main__', '__doc__': '\n people class\n ', '__dict__': , '__weakref__': }) +``` + + + +### \__module__ + +包含该类的定义的模块名;需要注意,是字符串形式的模块名而不是模块对象。 + +由于我是在 交互式命令行的环境下,所以模块是 `__main__` + +```python +>>> People.__module__ +'__main__' +``` + +如果将上面的代码放入 demo.py,并且从 people 模块导入 People 类,其值就是 people 模块 + +![](http://image.iswbm.com/image-20200905115039771.png) + + + +### \__bases__ + +直接父类对象的元组;但不包含继承树更上层的其他类,比如父类的父类。 + +```python +>>> class People: pass +... +>>> class Teenager: pass +... +>>> class Student(Teenager): pass +... +>>> Student.__bases__ +(,) +>>> +``` + + + +## + ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_02.rst b/source/c01/c01_02.rst index f59ecfb..485f7e3 100755 --- a/source/c01/c01_02.rst +++ b/source/c01/c01_02.rst @@ -69,14 +69,6 @@ dir() 返回当前作用域中的名称。让我们将 dir() 函数应用于 key |image6| -\__doc_\_ -~~~~~~~~~ - -使用 ``__doc__`` 这个魔法方法,可以查询该模块的文档,它输出的内容和 -help() 一样。 - -|image7| - 2. 应用到实际开发中 ------------------- @@ -182,8 +174,134 @@ callable() True >>> +模块(Modules) +--------------- + +\__doc_\_ +~~~~~~~~~ + +使用 ``__doc__`` 这个魔法方法,可以查询该模块的文档,它输出的内容和 +help() 一样。 + +|image7| + +\__name_\_ +~~~~~~~~~~ + +始终是定义时的模块名;即使你使用import .. as +为它取了别名,或是赋值给了另一个变量名。 + +.. code:: python + + >>> import json + >>> json.__name__ + 'json' + >>> + >>> import json as js + >>> js.__name__ + 'json' + +\__file_\_ +~~~~~~~~~~ + +包含了该模块的文件路径。需要注意的是内建的模块没有这个属性,访问它会抛出异常! + +.. code:: python + + >>> import json + >>> json.__file__ + '/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/json/__init__.py' + +\__dict_\_ +~~~~~~~~~~ + +包含了模块里可用的属性名-属性的字典;也就是可以使用模块名.属性名访问的对象。 + +类(Class) +----------- + +.. _doc__-1: + +\__doc_\_ +~~~~~~~~~ + +文档字符串。如果类没有文档,这个值是None。 + +.. code:: python + + >>> class People: + ... ''' + ... people class + ... ''' + ... + >>> p = People() + >>> + >>> print(p.__doc__) + + people class + + >>> + +.. _name__-1: + +\__name_\_ +~~~~~~~~~~ + +始终是定义时的类名。 + +.. code:: python + + >>> People.__name__ + 'People' + +.. _dict__-1: + +\__dict_\_ +~~~~~~~~~~ + +包含了类里可用的属性名-属性的字典;也就是可以使用类名.属性名访问的对象。 + +.. code:: python + + >>> People.__dict__ + mappingproxy({'__module__': '__main__', '__doc__': '\n people class\n ', '__dict__': , '__weakref__': }) + +\__module_\_ +~~~~~~~~~~~~ + +包含该类的定义的模块名;需要注意,是字符串形式的模块名而不是模块对象。 + +由于我是在 交互式命令行的环境下,所以模块是 ``__main__`` + +.. code:: python + + >>> People.__module__ + '__main__' + +如果将上面的代码放入 demo.py,并且从 people 模块导入 People 类,其值就是 +people 模块 + |image8| +\__bases_\_ +~~~~~~~~~~~ + +直接父类对象的元组;但不包含继承树更上层的其他类,比如父类的父类。 + +.. code:: python + + >>> class People: pass + ... + >>> class Teenager: pass + ... + >>> class Student(Teenager): pass + ... + >>> Student.__bases__ + (,) + >>> + +|image9| + .. |image0| image:: http://image.iswbm.com/image-20200606121047415.png .. |image1| image:: http://image.iswbm.com/image-20200606121544062.png .. |image2| image:: http://image.iswbm.com/image-20200606121942898.png @@ -192,5 +310,6 @@ callable() .. |image5| image:: http://image.iswbm.com/image-20200606123145109.png .. |image6| image:: http://image.iswbm.com/image-20200606134519352.png .. |image7| image:: http://image.iswbm.com/image-20200606134858285.png -.. |image8| image:: http://image.iswbm.com/20200607174235.png +.. |image8| image:: http://image.iswbm.com/image-20200905115039771.png +.. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index df6b488..c5a08e5 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -223,7 +223,7 @@ wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块, description="Learn to Pack Python Module -->公众号:Python编程时光", # 项目主页 - url="http://iswbm.com/", + url="http://iswbm.com/", # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包 packages=find_packages() @@ -282,7 +282,7 @@ wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块, author="wangbm", author_email="wongbingming@163.com", description="Learn to Pack Python Module", - url="http://iswbm.com/", + url="http://iswbm.com/", packages=find_packages(), # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等 @@ -406,7 +406,7 @@ Python author="wangbm", author_email="wongbingming@163.com", description="Learn to Pack Python Module", - url="http://iswbm.com/", + url="http://iswbm.com/", packages=find_packages(), # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件 diff --git a/source/c04/c04_20.rst b/source/c04/c04_20.rst index 2e86d63..e393898 100644 --- a/source/c04/c04_20.rst +++ b/source/c04/c04_20.rst @@ -11,9 +11,18 @@ 2、过滤关键字 用减号 +谷歌搜索 ``python parse 库`` ,出现的都是 +``urllib.parse``\ ,可我实际想要找是 parse 库的相关资料。 + +|image1| + +那我可以使用 ``-`` ,过滤掉 urllib 的搜索结果 + :: - Python协程 -CSDN + python parse -urllib + +|image2| 3、必须包含某关键字 用加号 @@ -40,4 +49,6 @@ "装饰器进阶用法详解" .. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200826102731.png +.. |image2| image:: http://image.iswbm.com/20200826102136.png diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst index cc67eb1..f8323f2 100644 --- a/source/c04/c04_21.rst +++ b/source/c04/c04_21.rst @@ -302,6 +302,20 @@ pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎 '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] >>> +**3.6 延长超时时间** + +若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 + +对于这种情况,一般重试几次就好了。 + +但是这样难免有些麻烦,有没有更好的解决方法呢? + +有的,可以通过延长超时时间。 + +.. code:: shell + + $ pip install --default-timeout=100 + 4. 卸载软件包 ------------- diff --git a/source/c08/c08_06.rst b/source/c08/c08_06.rst index 109d5d2..c5af0f5 100644 --- a/source/c08/c08_06.rst +++ b/source/c08/c08_06.rst @@ -533,7 +533,9 @@ ubuntu 的网卡配置不是正常我们常见的 json 或者 yaml cloud-init 进行新ip的配置,而新ip的配置是使用 ``ifup`` 这个命令\ |image27| -使用这种方式并不会将第一次配置的旧ip给清除掉。\ |image28| +使用这种方式并不会将第一次配置的旧ip给清除掉。 + +|image28| 这个问题,目前我只在CentOS6 中遇到过。可以通过修改代码让其先 ``ifdown`` 再 ``ifup`` 就可以解决这个问题。\ |image29| @@ -690,7 +692,84 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 但这仅仅只是写入,若要执行这些命令,还需要你在 /etc/cloud/cloud.cfg 中配置 ``scripts_user``\ ,这样 cloud-init 才会去执行它。 -8.6.15 相关命令 +8.6.5 如何读取修改缓存数据 +-------------------------- + +当虚拟机启动时,cloudinit 会将 configdrive +里的数据读取并缓存,缓存文件是一个被序列化的二进制文件,名字固定为 +``obj.pkl``\ 。 + +序列化的函数存在于 stages.py 文件 + +- ``_pkl_store()``\ :序列化 +- ``_pkl_load()``\ :反序列化 + +|image42| + +读取演示 + +:: + + >>> from cloudinit import stages; + >>> file=stages._pkl_load("/var/lib/cloud/instances/1242171a-bf72-4a3a-acb0-9e5393e97865/obj.pkl") + >>> file.network_json + >>> file.metadata + +写入演示 + +:: + + >>> file.metadata["vhost_queues"]=1 + >>> stages._pkl_store(file, "/home/obj.pkl") + True + +8.6.16 如何判断是新虚拟机 +------------------------- + +在 stages.py 下有一个函数 ``is_new_instance()`` + +|image43| + +其中 ``previous`` 和 ``nw_timestramp`` 都是从 +``/var/lib/cloud/instances/[instance_uud]/`` +目录下直接读取的,导致旧虚拟机的信息。 + +而在 ``/var/lib/cloud/instance`` 目录下的信息,都是从 ``/dev/sr0`` +从读取的最新信息。 + +通过二者对比,就可以知道该虚拟机是否是一台新的虚拟机。 + +8.6.17 CentOS 6.x 如何更改网卡名 +-------------------------------- + +参考文章:\ `Changing the ethX to Ethernet Device Mapping in +EL6 `__ + +关闭network + +.. code:: shell + + $ /etc/init.d/network stop + +修改ifcfg 文件里的 device 和 hwaddr + +.. code:: shell + + $ vim /etc/sysconfig/network-scripts/ifcfg-eth* + +修改 udev 网卡规则文件 + +.. code:: shell + + vim /etc/udev/rules.d/70-persistent-net.rules + +重新加载 udev,启动 network + +.. code:: shell + + $ start_udev && /etc/init.d/network start + +8.6.18 相关命令 --------------- .. code:: shell @@ -707,7 +786,7 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 -------------- -|image42| +|image44| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20190430204707.png @@ -751,5 +830,7 @@ cloudinit 允许通过 user_data 指定你想在虚拟机启动时,执行的 .. |image39| image:: http://image.iswbm.com/20190911203953.png .. |image40| image:: http://image.iswbm.com/20190911204805.png .. |image41| image:: http://image.iswbm.com/20190911205518.png -.. |image42| image:: http://image.iswbm.com/20200607174235.png +.. |image42| image:: http://image.iswbm.com/20200807135831.png +.. |image43| image:: http://image.iswbm.com/20200807161244.png +.. |image44| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c09/c09_02.rst b/source/c09/c09_02.rst index 2c68237..cc5857a 100644 --- a/source/c09/c09_02.rst +++ b/source/c09/c09_02.rst @@ -107,13 +107,13 @@ Hazeover:干扰调节神器,把活动窗口除久的其他范围调暗 4. 图片影音 ----------- -Snipaste:截图工具,支持钉图 +`Snipaste `__\ :截图工具,支持钉图 iShot:截图工具,支持长截图/滚动截图,钉图 Capture Gif:Gif 录制(不推荐) -Kap Beta:录屏开源免费软件,支持GIF导出,快捷键:\ ``Command Shift 5`` +`Kap `__\ :录屏开源免费软件,支持GIF导出,快捷键:\ ``Command Shift 5`` ScreenFlow:视频录制 From 04067cdd6a12cf8ee28b147dc9d19bb5378c614e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Fri, 25 Sep 2020 10:10:13 +0800 Subject: [PATCH 122/147] update --- source/c08/c08_16.md | 2 +- source/c08/c08_16.rst | 2 +- source/c08/c09_16.rst | 252 ------------------------------------------ 3 files changed, 2 insertions(+), 254 deletions(-) delete mode 100644 source/c08/c09_16.rst diff --git a/source/c08/c08_16.md b/source/c08/c08_16.md index 153d9e4..ad70dbe 100644 --- a/source/c08/c08_16.md +++ b/source/c08/c08_16.md @@ -1,4 +1,4 @@ -# 9.16 详解 Neutron 的 QoS +# 8.16 详解 Neutron 的 QoS QoS 的全称是 Quality of Service,也就是服务质量。 diff --git a/source/c08/c08_16.rst b/source/c08/c08_16.rst index bb69764..aeb792a 100644 --- a/source/c08/c08_16.rst +++ b/source/c08/c08_16.rst @@ -1,4 +1,4 @@ -9.16 详解 Neutron 的 QoS +8.16 详解 Neutron 的 QoS ======================== QoS 的全称是 Quality of Service,也就是服务质量。 diff --git a/source/c08/c09_16.rst b/source/c08/c09_16.rst deleted file mode 100644 index 337a7ac..0000000 --- a/source/c08/c09_16.rst +++ /dev/null @@ -1,252 +0,0 @@ -9.16 详解 Neutron 的 QoS -======================== - -QoS 的全称是 Quality of Service,也就是服务质量。 - -Neutron 支持的 -`QoS `__ -类型有 - -- bandwidth_limit(带宽限速):实现带宽速速 -- DSCP(差分服务代码点):给网络流量包添加一个 DSCP - 标记,以此实现流量的优先级 -- minimum_bandwidth:暂时没用过 - -1. 开户 QoS 支持 ----------------- - -在控制节点上执行两条命令修改配置 - -.. code:: shell - - $ openstack-config --set /etc/neutron/neutron.conf DEFAULT service_plugins neutron.services.qos.qos_plugin.QoSPlugin - - $ openstack-config --set /etc/neutron/plugins/ml2/ml2_conf.ini ml2 extension_drivers port_security,qos - -然后重启 neutron-server - -.. code:: shell - - $ crm resource cleanup openstack-neutron-server-clone - -在计算节点上执行命令修改配置 - -.. code:: shell - - $ openstack-config --set /etc/neutron/plugins/ml2/openvswitch_agent.ini agent extensions qos - -然后重启对应的 neutron-agent 服务 - -.. code:: shell - - $ systemctl restart neutron-openvswitch-agent - -DSCP 差分服务代码点 -------------------- - -DSCP 大小为 6 个 bit,也就是最大可表示 64 个分类。 - -但在 Neutron 里可用的标记只有这些 - -:: - - 0, 8, 10, 12, 14, 16, 18, 20, - 22, 24, 26, 28, 30, 32, 34, 36, - 38, 40, 46, 48, 56 - -可以看到,它们都是0-56 间的偶数,但是排除 2-6, 42, 44 和 50-54。 - -|image0| - -原理 -~~~~ - -在 IP 协议分组里有一个 ToS(服务类型) 的字段,就是用来表示 ToS 的。 - -ToS 字段,总共 8 个 bit - -|image1| - -**前面 3 个 bit** - -为优选权子字段,现在已经废弃,这个字段默认值是000,从wireshark抓包结果看,表示的是: - -.. figure:: https://s4.51cto.com/images/blog/201804/25/ede8e1de3c98c2fdfeb044cb0cf74034.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= - :alt: 003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明 - - 003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明 - -但是在某些协议中仍然是有用的,比如 OSPFv2 协议 - -|image2| - -关于 Precedence (优先级),有如下几种 - -.. code:: shell - - 111 - Network Control - 110 - Internetwork Control - 101 - CRITIC/ECP - 100 - Flash Override - 011 - Flash - 010 - Immediate - 001 - Priority - 000 – Routine - -**中间 4 个bit** - -这四个 bit -组合在一起,表示了该数据报对应的服务类别,这个应用层的服务类别是不同的。这里所说的服务类别,是指: - -:: - - 1000 -- minimize delay 最小延迟 - 0100 -- maximize throughput 最大吞吐量 - 0010 -- maximize reliability 最高可靠性 - 0001 -- minimize monetary cost 最小费用 - 0000 -- normal service 一般服务 - -IP首部中的ToS字段,只能表示一种服务类别,也就是:这4bit字段中,最多只能有一个bit字段为1。 - -看下不同应用下该4bit字段对应的值: -|003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| -翻译过来就是: -|003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| -**最小延迟**\ ,对应于对延迟敏感的应用,如telnet和人login等。 -**最大吞吐量**\ ,对应于对吞吐量要求比较高的应用,如FTP文件应用,对文件传输吞吐量有比较高的要求。 -**最高可靠性**\ ,对网络传输可靠性要求高的应用,如使用SNMP的应用、路由协议等等。 -**最小费用**\ ,如NNTP这种用户网络新闻等。 - -**最后 1 个bit** - -这个1bit末尾,没有被使用,必须强制设置为0 - -最后,很重要的一点,只有当\ **网络设备(如交换机等)能够支持**\ (能够识别IP首部中的ToS字段)识别ToS字段时,这给字段设置才有意义。 - -创建 policy -~~~~~~~~~~~ - -.. code:: shell - - $ neutron qos-policy-create qos-dscp --shared - Created a new policy: - +-----------------+--------------------------------------+ - | Field | Value | - +-----------------+--------------------------------------+ - | created_at | 2020-07-01T06:43:23Z | - | description | | - | id | ee7e7a83-c67d-4f27-b77c-3345553e5abe | - | name | qos-dscp | - | project_id | 2ac17c7c792d45eaa764c30bac37fad9 | - | revision_number | 1 | - | rules | | - | shared | True | - | tenant_id | 2ac17c7c792d45eaa764c30bac37fad9 | - | updated_at | 2020-07-01T06:43:23Z | - +-----------------+--------------------------------------+ - -创建 rule -~~~~~~~~~ - -创建时需要指定 QOS_POLICY,创建完后,就会自动添加到 QOS_POLICY - -.. code:: shell - - $ neutron qos-dscp-marking-rule-create --dscp-mark 14 ee7e7a83-c67d-4f27-b77c-3345553e5abe - Created a new dscp_marking_rule: - +-----------+--------------------------------------+ - | Field | Value | - +-----------+--------------------------------------+ - | dscp_mark | 14 | - | id | 1d045cf3-eb31-440b-9a74-a9d5fea6a7e0 | - +-----------+--------------------------------------+ - -绑定policy到 port -~~~~~~~~~~~~~~~~~ - -.. code:: shell - - $ neutron port-update --qos-policy qos-dscp - -关闭 QoS -~~~~~~~~ - -.. code:: shell - - $ neutron port-update --no-qos-policy - -查看规则 -~~~~~~~~ - -过滤出 tos 后就能看到 ToS(Type of Service) 的值 - -.. code:: shell - - $ ovs-ofctl dump-flows br-int | grep tos - -然后再对照这个表,找到对应的 DSCP 的 decimal ,如果 tos 是 64,那么 DSCP -mark 就是 16,其实除以 4 就可以了,也不用对照表。其中这个 16 -需要跟交换上支持的一样。 - -|image5| - -其他 -~~~~ - -DSCP 是以集群为粒度,一个集群只要创建一个就行,需要的时候将其绑定到 port -上就可以。 - -2. bandwidth_limit 带宽限速 ---------------------------- - -华云 QoS -:https://support.huawei.com/enterprise/zh/doc/EDOC1100055155/101c0e7b - -http://blog.chinaunix.net/uid-20530497-id-2490382.html - -https://www.zhihu.com/question/21053403 - -qos 查询命令 - -.. code:: shell - - $ neutron qos-policy-list - $ neutron qos-bandwidth-limit-rule-list 16fbb0c2-b7ac-4053-a85f-75fb72c3ab55 - -在 dpdk 宿主机上查询限速 - -.. code:: shell - - # 查询 ingress(宿主机角度) - $ virsh dumpxml 4f6a0708-aeb8-4208-bea8-2c51e6a94948 - - # 查询 egress(虚拟机角度) - $ ovs-vsctl list interface vhu198063e9-97 - - # 查询 ingress(虚拟机角度) - $ ovs-appctl -t ovs-vswitchd qos/show vhu198063e9-97 - -|image6| - -3. 参考文章 ------------ - -- https://blog.51cto.com/mangguostudy/2107799 - -- https://www.jianshu.com/p/4b5cc3845f2c - -- https://blog.csdn.net/u011641885/article/details/45640313 - --------------- - -|image7| - -.. |image0| image:: http://image.iswbm.com/20200701155207.png -.. |image1| image:: http://img.wandouip.com/crawler/article/2019411/546f47120fa14a2a1cfc44c9e8a48e71 -.. |image2| image:: http://image.iswbm.com/20200701170223.png -.. |003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| image:: https://s4.51cto.com/images/blog/201804/25/3c42c64b7240ef12b991f69644a145ac.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= -.. |003::每天五分钟入门TCP/IP协议栈::IP协议之TOS字段说明| image:: https://s4.51cto.com/images/blog/201804/25/4f03b09e8081d8fc7073f29870bc1c95.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= -.. |image5| image:: http://image.iswbm.com/20200701155207.png -.. |image6| image:: http://image.iswbm.com/20200709171517.png -.. |image7| image:: http://image.iswbm.com/20200607174235.png - From 828c52d2646c1471f6c2f33ac3dc14e9bbd34f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Mon, 28 Sep 2020 11:40:40 +0800 Subject: [PATCH 123/147] update --- source/c09/c09_03.md | 3 + source/c10/c10_11.rst | 324 ------------------------------------------ 2 files changed, 3 insertions(+), 324 deletions(-) delete mode 100644 source/c10/c10_11.rst diff --git a/source/c09/c09_03.md b/source/c09/c09_03.md index 3ca5c78..848f6b7 100644 --- a/source/c09/c09_03.md +++ b/source/c09/c09_03.md @@ -70,3 +70,6 @@ https://www.draw.io/ +## 黑科技 + +果核剥壳(破解工具):https://www.ghpym.com/special/softlist \ No newline at end of file diff --git a/source/c10/c10_11.rst b/source/c10/c10_11.rst deleted file mode 100644 index d9ac3d8..0000000 --- a/source/c10/c10_11.rst +++ /dev/null @@ -1,324 +0,0 @@ -10.11 如何申请数字证书,点亮你的 HTTPS? -======================================== - -|image0| - -上一节讲解了关于 SSL -数字证书的基础知识,这一篇就来实战演示一下,如何将你的 HTTP 网站加上 -HTTPS 。 - -要想使用 HTTPS ,要做的事情还是有点多的: - -1. 准备一个域名和服务器(自行准备) -2. 并将该域名解析到你的服务器ip上 -3. 生成 CSR 证书请求文件 -4. 拿着 CSR 文件去申请证书 -5. 把申请到的证书部署到你的服务器上 - -1. 设置 DNS 解析 ----------------- - -我有一个域名 ``iswbm.com``\ ,其下有好多的子域名,比如写 Python -的在线博客 ``python.iswbm.com``\ ,还有写 Golang 的在线博客 -``golang.iswbm.com``\ ,还有我的图床 ``image.iswbm.com`` 等等。 - -这里新建了个子域名 ``demo.iswbm.com`` ,并设置其解析到 -我的服务器上,如下图(服务器 ip 已码掉)。 - -|image1| - -2. 搭建 HTTP 站点 ------------------ - -为了方便,我这里就使用 Apache 放了一个 HTTP -的静态网页,方法很简单,大家百度即可。 - -|image2| - -3. 申请 SSL 证书 ----------------- - -SSL 数字证书怎么来的呢? - -你可以自己制作,也可以向CA权威机构申请。 - -二者的区别是: - -1. 自己颁发的证书,需要客户端验证通过,也就是需要用户手动安装证书,并将其设置为受信任的根证书。但即使如此,浏览器上( - chrome, - firefox)仍不认可这种自签名证书,会在地址栏前面提示连接不安全,手动安装证书后,也会提示该证书无效。若想要继续访问,并忽略该提示,可以选择继续访问。 -2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是绝对可信任的。因此你在访问网站时,不会提示连接不安全。 - -下面,我将分别向你展示这两种方法,都是如何操作的。 - -第一种:向权威CA机构申请 -~~~~~~~~~~~~~~~~~~~~~~~~ - -在阿里云和腾讯云都可以 进行 SSL -证书的申请,证书的申请有付费的(价格也不便宜),也有免费的,看了一圈,只有腾讯云有免费的 -SSL 证书的申请渠道(阿里云听说以前也有,后来关闭了)。 - -本篇文章,仅以演示教学之用,所以只用腾讯云的免费证书的就足够啦。 - -登陆腾讯云,可以看到SSL -证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 - -|image3| - -点击选择 ``域名型免费版`` - -|image4| - -点击 ``免费快速申请``\ 后,填写域名和你的个人邮箱 - -|image5| - -再点击下一步,会需要你验证域名所有权,验证方式有如下三种 - -1. 自动DNS验证 -2. 手动DNS验证 -3. 文件验证 - -但由于我的域名不是腾讯云平台解析的,因此没有 -自动DNS验证的选项,只有其他两种 - -|image6| - -点击 ``确认申请`` 后,会提示你进入域名验证所有权的流程,这里我选择 -手动DNS验证。 - -|image7| - -审核通过后,3s 内就会给你颁发证书,你可以从控制台点击证书下载。 - -|image8| - -下载下来的会是一个 zip 包。 - -解压一下,会有不同的服务器类型(有 -Apache、IIS、Nginx、Tomcat)的文件夹。 - -|image9| - -我使用的是 Apache ,在这个文件夹下面有三个文件: - -1. ``1_root_bundle.crt``\ :根证书 -2. ``2_demo.iswbm.com.crt``\ :域名证书 -3. ``3_demo.iswbm.com.key``\ :私钥文件 - -这三个文件,下一步会部署于我的服务器上。 - -接下来讲第二种 SSL 证书申请方式。 - -第二种:自签名的 SSL 证书 -~~~~~~~~~~~~~~~~~~~~~~~~~ - -没有权威的第三方 CA 机构给自己颁发证书,那就自己给自己颁发咯。 - -自签名 SSL 的证书制作过程,可以分为两步: - -1. 自己要当 CA 机构,那 CA 有的 CA 根证书、私钥 - 一样都不能少,因此第一步:生成 CA 的 crt 证书 和 CA 的私钥。 -2. 要申请证书,首先服务器自己要有一个密钥对(公钥和私钥) -3. 拿着上面生成的公钥,制作一个 CSR 证书申请文件 -4. 用第一步的 CA 私钥,给这个 CSR 签名,生成咱所需要的 SSL - 数字证书文件。 - -步骤很多,命令很多,命令所带的参数更多,对于只想学习证书签发流程的你,把这些命令都背下来,并不是一个好的选择,毕竟这种事可能也干不了几次。 - -因此,我把这些步骤、命令,都整合成一个 shell -脚本,你只要执行这个脚本就行了。 - -.. code:: shell - - $ bash create_self-signed-cert.sh --ssl-domain=demo.iswbm.com --ssl-trusted-domain=demo.iswbm.com --ssl-size=2048 --ssl-date=3650 - -对应的参数的解释,在脚本中都有解释 - -|image10| - -这个脚本过长,不好直接贴上来,我将它放在我的公众号(\ **Python编程时光**\ )后台,你可以直接回复『\ **证书签名**\ 』直接获取下载。 - -执行完成后,会在当前目录下生成好多个文件。 - -其中,只有两个文件对我们有用 - -|image11| - -4. 部署 SSL 证书 ----------------- - -根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 - -|image12| - -这里我将以 CentOS 7.2 + Apache 为例,演示如何部署 SSL 证书。 - -先安装一下 mod_ssl - -.. code:: shell - - $ yum install -y mod_ssl - -安装完后,在 /etc/httpd/conf.d/ 目录下 会有个 ssl.conf 文件。 - -编辑修改这个文件,以下是我的配置供你参考 - -.. code:: shell - - - DocumentRoot "/var/www/html" - #填写证书名称 - ServerName demo.iswbm.com - #启用 SSL 功能 - SSLEngine on - #证书文件的路径 - SSLCertificateFile /etc/pki/tls/certs/demo.iswbm.com.crt - #私钥文件的路径 - SSLCertificateKeyFile /etc/pki/tls/private/demo.iswbm.com.key - #根证书文件的路径 - SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt - - -**如果你的证书是从权威 CA 机构上申请来的。** - -比如我上面从腾讯云上申请来的,那么这三个文件就是从已经从腾讯云的控制台上下载下来的那三个文件。 - -在修改完后,务必记得把下载的这三个文件,放到相应的目录中去。 - -|image13| - -配置完 ssl.conf,可能还需要你 check 一下 ``/etc/httpd/conf/httpd.conf`` -的一些配置,这些配置一般用默认的就可以,但是以防万一,还是写一下吧 - -:: - - Include conf.modules.d/*.conf - -写这一行的目的,就是为了 httpd 去加载 mod_ssl 这个模块 - -.. code:: shell - - $ cat /etc/httpd/conf.modules.d/00-ssl.conf - LoadModule ssl_module modules/mod_ssl.so - -一切配置完成后,记得重启一下 httpd 服务 - -.. code:: shell - - $ systemctl restart httpd - -然后使用 chrome 访问一下 ``https//demo.iswbm.com`` 看看,大功告成。 - -|image14| - -**而如果你的证书是自签名的。** - -ssl.conf 配置文件下的应该改成这样 - -:: - - - DocumentRoot "/var/www/html" - #填写证书名称 - ServerName demo.iswbm.com - #启用 SSL 功能 - SSLEngine on - #证书文件的路径 - SSLCertificateFile /etc/pki/tls/certs/tls.crt - #私钥文件的路径 - SSLCertificateKeyFile /etc/pki/tls/private/tls.key - - -同时记得把这两个文件也拷贝到相应的目录下 - -.. code:: shell - - $ cp tls.crt /etc/pki/tls/certs/ - $ cp tls.key /etc/pki/tls/private/ - -最后还是不要忘了重启 httpd - -.. code:: shell - - $ systemctl restart httpd - -试着用 chrome 访问一下,可以看到 chrome 提示该连接不安全 - -|image15| - -如果执意要访问,可以点击左下方的 -``继续前往``\ ,这样以后再访问的时候,就不会再出现这个警告页面了。 - -|image16| - -``不安全`` 三个字,让人很没有安全感,那有没有办法去掉呢? - -答案是,没有,只要是自签名的证书,在 chrome ,firefox -等主流浏览器看来都是不安全的。 - -即使你把这个根证书添加到你的受信任的证书列表中,也是徒然。 - -下面就试着来安装一下这个根证书。 - -按照下图指示,拖动证书到本地磁盘上。 - -|image17| - -打开 Mac 上的 ``钥匙串访问`` - -|image18| - -点击 ``登陆``\ ,然后再拖动这个证书到窗口中进行安装 - -|image19| - -右键该证书,点击 ``显示简介``\ ,跳出下面的界面后,再点击 ``信任``\ ,把 -IP 安全选择选为 ``始终信任``\ 。 - -|image20| - -设置完后,再访问下 ``demo.iswbm.com`` -,仍然显示连接不安全,并且证书是无效的 - -|image21| - -点击证书,显示证书,该证书确实已经放入信任列表中了。 - -|image22| - -参考文档 --------- - -- `Apache - 服务器证书安装 `__ -- `自签名 SSL - 证书 `__ - -|image23| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200728233602.png -.. |image2| image:: http://image.iswbm.com/20200729230813.png -.. |image3| image:: http://image.iswbm.com/image-20200718102622663.png -.. |image4| image:: http://image.iswbm.com/image-20200718101358755.png -.. |image5| image:: http://image.iswbm.com/20200729232432.png -.. |image6| image:: http://image.iswbm.com/image-20200718101652899.png -.. |image7| image:: http://image.iswbm.com/20200729004207.png -.. |image8| image:: http://image.iswbm.com/20200729004307.png -.. |image9| image:: http://image.iswbm.com/20200729004456.png -.. |image10| image:: http://image.iswbm.com/20200729235153.png -.. |image11| image:: http://image.iswbm.com/20200730000142.png -.. |image12| image:: http://image.iswbm.com/20200718105347.png -.. |image13| image:: http://image.iswbm.com/20200730214826.png -.. |image14| image:: http://image.iswbm.com/20200730215613.png -.. |image15| image:: http://image.iswbm.com/20200730220835.png -.. |image16| image:: http://image.iswbm.com/20200730221745.png -.. |image17| image:: http://image.iswbm.com/20200728234740.png -.. |image18| image:: http://image.iswbm.com/20200730222441.png -.. |image19| image:: http://image.iswbm.com/20200728235331.png -.. |image20| image:: http://image.iswbm.com/20200730222700.png -.. |image21| image:: http://image.iswbm.com/20200730222827.png -.. |image22| image:: http://image.iswbm.com/20200730222928.png -.. |image23| image:: http://image.iswbm.com/20200607174235.png - From 2c5bc557ee6bd4057571f215c7b38e59f073fa88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=82=B3=E6=98=8E?= Date: Wed, 30 Sep 2020 18:15:59 +0800 Subject: [PATCH 124/147] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/c01_01.md | 44 ++++++++---------- source/c01/c01_02.md | 6 +-- source/c01/c01_05.md | 8 ++-- source/c01/c01_06.md | 4 +- source/c01/c01_09.md | 4 +- source/c01/c01_11.md | 6 +-- source/c01/c01_12.md | 14 +++--- source/c01/c01_13.md | 14 +++--- source/c01/c01_14.md | 10 ++--- source/c01/c01_15.md | 14 +++--- source/c01/c01_17.md | 105 +++++++++++++++++++++++++++++++++++-------- source/c01/c01_18.md | 8 ++-- source/c01/c01_20.md | 14 +++--- source/c07/c07_22.md | 2 +- source/c08/c08_01.md | 8 ---- 15 files changed, 157 insertions(+), 104 deletions(-) diff --git a/source/c01/c01_01.md b/source/c01/c01_01.md index 085f1aa..d3b620f 100644 --- a/source/c01/c01_01.md +++ b/source/c01/c01_01.md @@ -2,19 +2,11 @@ ![](http://image.iswbm.com/20200602135014.png) ---- +## 0. 去哪里找 -从今天开始,小明将和你一起过一下,那些在面试「Python开发」岗位时面试官喜欢问的问题。内容基础,但是你不一定会噢。 +这块的内容,你随便使用搜索引擎都可以查到。 -这些问题全部来自个人经验,群友推荐以及网络上的帖子。如果你有好的问题,也可以随时向我提出(不要觉得简单),我会筛选后整理出来在这里,供大家学习取经,给大家在求职路上贡献一份力。 - -开篇讲些什么好呢? - -今天就来罗列一下,Python2.x和3.x到底有哪些区别吧。 - -## 去哪里找 - -你随便全使用搜索引擎都可以查到这些资料,但是大家说的都是一些普遍都知道的事儿。或者都是抄来抄去,内容相差无几。 +但是大家好像都在抄来抄去,内容相差无几。 授人以鱼,不如授人以渔。 @@ -34,7 +26,7 @@ 接下来。和大家一起过一下,Python2.x和3.x到底有哪些区别,这不仅在你开发过程中需要考虑的,也是面试过程面试官经常会问及的。 -## 1.1.1 print +## 1. print 在Python 2.6之前,只支持 ``` @@ -52,7 +44,7 @@ print("hello") print ("hello") ``` -## 1.1.2 编码方式 +## 2. 编码方式 在Python2.x中,默认使用`ASCII`编码。 @@ -76,7 +68,7 @@ print str1 所以我们可以在程序中,随意的使用中文(但并不推荐),不会报错。 -## 1.2.3 除法运算 +## 3. 除法运算 Python 2.x中除法运算,整数间运算只保留整数(向下取整)。 ```python @@ -108,7 +100,7 @@ Python 3.x中除法运算,全部保留小数(即使能被整除)。 -3.0 ``` -## 1.2.4 异常捕获 +## 4. 异常捕获 在 Python 3 中,只能使用 `as` 作为关键词。而在Python 2中经常使用 `except Exception, e` 使用语法except (exc1, exc2) as var可以同时捕获多种类别的异常。 @@ -120,7 +112,7 @@ Python 2.6已经支持这两种语法。 在2.x时代,异常在代码中除了表示程序错误,还经常做一些普通控制结构应该做的事情,在3.x中可以看出,设计者让异常变的更加专一,只有在错误发生的情况才能去用异常捕获语句来处理。 -## 1.2.5 xrange +## 5. xrange 首先,要了解的是,xrange是只有在Python2.x中才有的产物。 @@ -139,7 +131,7 @@ xrange(1, 5) range(0, 10) ``` -## 1.2.6 用户输入 +## 6. 用户输入 在2.x 中,有两个函数。raw_input()和input()。 - raw_input():将所有输入作为字符串看待,返回字符串类型。 @@ -148,7 +140,7 @@ range(0, 10) 在3.x 中,对这两个函数进行整合,只留下一个`input()`,既可输入数字,也可输入字符串,返回的是字符串类型。 -## 1.2.7 数据类型 +## 7. 数据类型 Python 3.x 一个很重要的特性是,对字符串和二进制数据流做了明确的区分。 @@ -160,7 +152,7 @@ Python 3不会以任意隐式的方式混用str和bytes,你不能拼接字符 还有一点是,3.X去除了long类型,取代它的是整型(int)。3.x的整型是没有限制大小的,可以当做long类型使用, 但实际上由于机器内存的有限,我们使用的整数是不可能无限大的。 -## 1.2.8 函数式编程 +## 8. 函数式编程 在Python中,我们常常使用到的map,filter,reduce,在2.x和3.x中也有所不同。 @@ -195,13 +187,13 @@ Python 3不会以任意隐式的方式混用str和bytes,你不能拼接字符 ``` 对于 reduce 函数,它在 Python 3.x 中已经不属于 built-in 了,被挪到 functools 模块当中。 -## 1.2.9 协程关键字 +## 9. 协程关键字 在Python3.3后,协程中,新增了yield from 和 async/await 关键字,这在2.x中是没有。 关于yield from的语法剖析,可以前往查看我的另一篇文章。 -## 1.2.10 类的类型 +## 10. 类的类型 Python2.x 默认使用经典类,只有显示继承object才是新式类。 @@ -217,7 +209,7 @@ class Cls(object): pass ``` -## 1.2.11 变量作用域 +## 11. 变量作用域 - 在2.x中无法将局部变量声明为全局变量。 - 在3.x中可以使用nonlocal语法将局部变量声明为全局变量。 @@ -236,7 +228,7 @@ foo() # 3.x输出:200 ``` -## 1.2.12 元类的使用 +## 12. 元类的使用 在2.x 中 ```python @@ -257,7 +249,7 @@ class Person(metaclass=MetaPerson): pass ``` -## 1.2.13 模块变化 +## 13. 模块变化 - 去掉了一些模块。由于不常用,这里就不列举了。 - 新增了一些模块。比如:concurrent.futures,asyncio等 @@ -265,9 +257,9 @@ class Person(metaclass=MetaPerson): ---- -大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。但是那些对于我们普通开发者来说,并不那么重要。完全可以不去关注。 +大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。 -实际上,当我熟悉一个版本后,基本上是可以无缝过渡到另一个版本的。这篇文章,更多的是为了科普和应对面试。 +但是那些对于我们普通开发者来说,并不那么重要,个人感觉可以不去关注。 ---- diff --git a/source/c01/c01_02.md b/source/c01/c01_02.md index ee7cb66..d2e4c2d 100644 --- a/source/c01/c01_02.md +++ b/source/c01/c01_02.md @@ -156,7 +156,7 @@ True -## 模块(Modules) +## 3. 模块(Modules) ### \__doc__ @@ -196,7 +196,7 @@ True -## 类(Class) +## 4. 类(Class) ### \__doc__ @@ -276,6 +276,4 @@ mappingproxy({'__module__': '__main__', '__doc__': '\n people class\n ', ' -## - ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_05.md b/source/c01/c01_05.md index 9afedee..21f0807 100644 --- a/source/c01/c01_05.md +++ b/source/c01/c01_05.md @@ -4,7 +4,7 @@ --- -## 1.5.1 作用域 +## 1. 作用域 Python的作用域可以分为四种: - L (Local) 局部作用域 @@ -54,7 +54,7 @@ print(name) ``` -## 1.5.2 闭包 +## 2. 闭包 闭包这个概念很重要噢。你一定要掌握。 >在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。其实装饰函数,很多都是闭包。 @@ -77,7 +77,7 @@ deco()() # 输出:MING ``` -## 1.5.3 改变作用域 +## 3. 改变作用域 变量的作用域,与其定义(或赋值)的位置有关,但不是绝对相关。 因为我们可以在某种程度上去改变`向上`的作用范围。 @@ -129,7 +129,7 @@ deco()() ``` -## 1.5.4 变量集合 +## 4. 变量集合 在Python中,有两个内建函数,你可能用不到,但是需要掌握它们。 - globals() :以dict的方式存储所有全局变量 diff --git a/source/c01/c01_06.md b/source/c01/c01_06.md index ca2f673..69e7887 100644 --- a/source/c01/c01_06.md +++ b/source/c01/c01_06.md @@ -10,7 +10,7 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 以下两点,第一点是大家所熟知的,而第二点可能只有老司机才会知道,只有学习了第二点,才算真正理解了元组存在的价值和意义。 -## 2.6.1 不可变列表 +## 1. 不可变列表 这是`元组`区别于`列表`最显著的特征。 @@ -54,7 +54,7 @@ s1*n ``` -## 2.6.2 具名元组 +## 2. 具名元组 这个特性,我个人认为,才是元组存在的意义所在。 diff --git a/source/c01/c01_09.md b/source/c01/c01_09.md index 44f3738..697f7e0 100644 --- a/source/c01/c01_09.md +++ b/source/c01/c01_09.md @@ -8,7 +8,7 @@ -## 1.9.1 认识Mixin模式 +## 1. 认识Mixin模式 那我们今天就来讲讲这个 Mixin,对于这个Mixin,如何理解?它其实是一种设计模式,如果开发者之间没有产生这样一种设计模式的共识,那么设计模式将不复存在。 @@ -44,7 +44,7 @@ class Airplane(Vehicle, PlaneMixin): -## 1.9.2 不使用Mixin的弊端 +## 2. 不使用Mixin的弊端 你肯定会问,不使用 Mixin 行吗? diff --git a/source/c01/c01_11.md b/source/c01/c01_11.md index f2c9867..9d06f86 100644 --- a/source/c01/c01_11.md +++ b/source/c01/c01_11.md @@ -4,7 +4,7 @@ --- -## 一、正则表达式先导 +## 1. 正则表达式先导 ### 1.1 正则基础知识 @@ -72,7 +72,7 @@ re_match.group(2) `{n,m}?` :重复n到m次,但尽可能少重复 `{n,}?`: 重复n次以上,但尽可能少重复 -## 二、Python中的正则 +## 2. Python中的正则 在Python中,自带了re模块,这是专门用来做正则表达式的匹配的。 @@ -176,7 +176,7 @@ match.span() (0, 3) ``` -## 三、检验表达式 +## 3. 检验表达式 ### 3.1 校验数字 ``` 1. 数字:^[0-9]*$ diff --git a/source/c01/c01_12.md b/source/c01/c01_12.md index 58e6dd7..9d0c0af 100644 --- a/source/c01/c01_12.md +++ b/source/c01/c01_12.md @@ -6,7 +6,7 @@ 初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码? 这些内容是在去年整理的,现在重新整理下,发布在博客,搞懂字符串编码,这一篇文章足矣 -## 1.12.1 前言必知 +## 1. 前言必知 初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码? @@ -15,7 +15,7 @@ bit,位,一个bit可以表示两个数字,0和1 byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,255] -## 1.12.2 ASCII编码 +## 2. ASCII编码 ### 原始ASCII编码 @@ -31,7 +31,7 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2 ![原始ASCII编码](https://ooo.0o0.ooo/2017/08/02/59815bd96dd8a.gif) -## 1.12.3 ANSI标准 +## 3. ANSI标准 >ANSI:美国国家标准学会(AMERICAN NATIONAL STANDARDS INSTITUTE) 随着计算机的全世界普及,世界各国都用的上计算机了。但是每个国家都有各自的语言,而传统的计算机只支持ASCII编码表的字符。这对于不以英语为母语的人来说,使用计算机是非常吃力的。为了解决这个问题,各个国家的人都出了一套收录自己文字的字符编码(当然包含了ASCII里所有字符)。 @@ -48,7 +48,7 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2 * 在日文Windows中,ANSI编码是`Shift_JIS` * ... -## 1.12.4 Unicode编码 +## 4. Unicode编码 上节讲到各国都有了自己的编码,已经可以正常使用电脑了。 但是随着国际交流的日益频繁和迫切需要,我们中国人也要和美国人进行信息交流,美国人还要和日本人进行信息交流。假设,我们把一篇中文论文发到网上,美国人在用自己的计算机查看这个网页的时候,由于本地计算机不支持GB2312,结果无法显示正确信息,或者乱码。你会说,那很简单啊,让美国人在电脑上装上GB2312编码不就OK。真的OK吗?世界上那么多国家,那么多语言,那么多ANSI编码,都装上是不是要疯了。好吧,假如你真的不厌其烦的装上了,再假设,有一个中国人,他不仅会说汉语,还会说日语。他发表了一篇既有汉语也有日文的文章,而美国人在看这篇文章的时候,是用GB2312来解码呢,还是用Shift_JIS来解码呢?无论用哪个解码都会出现乱码的情况。 @@ -61,7 +61,7 @@ Unicode是由统一码联盟(英语:The Unicode Consortium),一个统筹 Unicode至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为2017年6月20日公布的10.0.0,已经收入超过十万个字符(第十万个字符在2005年获采纳)。[Unicode-维基百科](https://zh.wikipedia.org/wiki/Unicode) -## 1.12.5 UTF-8编码 +## 5. UTF-8编码 到目前为止,世界各国人民,都能愉快的无语言障碍的使用计算机了。 但是随着信息化时代的来临,人们越来越追求资源的传输速度和硬盘的存储效率。 @@ -85,7 +85,7 @@ UTF-8(UTF:Unicode TransferFormat,即把Unicode转做某种格式的意思) 当在txt输入输入'联通',保存再次打开就乱码,输入'你好联通'就不会出现这个情况。 ![](https://i.loli.net/2017/08/02/59816d652aeb9.png) -## 1.12.6 编码之于Python +## 6. 编码之于Python **Python2** Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首。但这也不怪Python,在Python诞生的时候,Unicode还没出现。 @@ -113,7 +113,7 @@ print '\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') 在Python3中,已经默认使用Unicode编码了。解决了很多编码的问题。 如果py文件中,含有中文还是得在文件头出加入 `# coding=utf-8` -## 1.12.7 扩展阅读 +## 7. 扩展阅读 中文编码的发展 GB2312-> GBK -> GB18030 diff --git a/source/c01/c01_13.md b/source/c01/c01_13.md index ec0ee13..42ac859 100644 --- a/source/c01/c01_13.md +++ b/source/c01/c01_13.md @@ -4,7 +4,7 @@ --- -## 1.13.1 lambda 表达式 +## 1. lambda 表达式 匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。 @@ -51,7 +51,7 @@ mySum(2, 3) 首先我们要知道 lambda 是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。 -## 1.13.2 map 函数 +## 2. map 函数 map 函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。 @@ -69,7 +69,7 @@ for i in [1,2,3,4,5]: mylist.append(i*2) ``` -## 1.13.3 filter 函数 +## 3. filter 函数 filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda 表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回 True,则元素会被保留下来,当表达式返回 False ,则元素会被丢弃。 @@ -79,12 +79,14 @@ filter 函数,和 map 函数相似。同样也是接收两个参数,一个la [-5, -4, -3, -2, -1] ``` -## 1.13.4 reduce 函数 +## 4. reduce 函数 reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 lambda 函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。 -![reduce 逻辑演示](https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg) +![](http://image.iswbm.com/20200930175131.png) + 这边举个例子你也就明白了。 + ``` >>>reduce(lambda x,y: x+y, [1,2,3,4,5]) 15 @@ -97,7 +99,7 @@ reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 个 10+5=15 ``` -## 1.13.5 注意点 +## 5. 注意点 以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的 Pythonic ,在某一程度上代码看起来更加的简洁。 diff --git a/source/c01/c01_14.md b/source/c01/c01_14.md index 62ff689..d47444a 100644 --- a/source/c01/c01_14.md +++ b/source/c01/c01_14.md @@ -2,8 +2,6 @@ ![](http://image.iswbm.com/20200602135014.png) -> 提示:前面的内容较为基础,重点知识在后半段。 - `with` 这个关键字,对于每一学习Python的人,都不会陌生。 操作文本对象的时候,几乎所有的人都会让我们要用 `with open` ,这就是一个上下文管理的例子。你一定已经相当熟悉了,我就不再废话了。 @@ -13,7 +11,7 @@ with open('test.txt') as f: print f.readlines() ``` -## 1.14.1 what context manager? +## 1. what context manager? **基本语法** @@ -30,7 +28,7 @@ with EXPR as VAR: 3. f 不是上下文管理器,应该是资源对象。 ``` -## 1.14.2 how context manager? +## 2. how context manager? 要自己实现这样一个上下文管理,要先知道上下文管理协议。 @@ -63,7 +61,7 @@ with Resource() as res: 从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在`__enter__`中,而将资源的关闭写在`__exit__` 中。 -## 1.14.3 why context manager? +## 3. why context manager? 学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。 @@ -113,7 +111,7 @@ with Resource() as res: 当主逻辑代码没有报异常时,这三个参数将都为None。 -## 1.14.4 how contextlib? +## 4. how contextlib? 在上面的例子中,我们只是为了构建一个上下文管理器,却写了一个类。如果只是要实现一个简单的功能,写一个类未免有点过于繁杂。这时候,我们就想,如果只写一个函数就可以实现上下文管理器就好了。 diff --git a/source/c01/c01_15.md b/source/c01/c01_15.md index 03ca8f1..39a6bea 100644 --- a/source/c01/c01_15.md +++ b/source/c01/c01_15.md @@ -6,13 +6,13 @@ > 转载自:https://zhuanlan.zhihu.com/p/38160586 -## 1.15.1 使用局部变量 +## 1. 使用局部变量 尽量使用局部变量代替全局变量:便于维护,提高性能并节省内存。 使用局部变量替换模块名字空间中的变量,例如 ls = os.linesep。一方面可以提高程序性能,局部变量查找速度更快;另一方面可用简短标识符替代冗长的模块变量,提高可读性。 -## 1.15.2 减少函数调用次数 +## 2. 减少函数调用次数 对象类型判断时,采用isinstance()最优,采用对象类型身份(id())次之,采用对象值(type())比较最次。 @@ -28,7 +28,7 @@ 如需使用模块X中的某个函数或对象Y,应直接使用from X import Y,而不是import X; X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查找到X模块,然后在X模块的字典中查找Y)。 -## 1.15.3 采用映射替代条件查找 +## 3. 采用映射替代条件查找 映射(比如dict等)的搜索速度远快于条件语句(如if等)。Python中也没有select-case语句。 @@ -36,7 +36,7 @@ #if查找if a == 1: b = 10elif a == 2: b = 20...#dict查找,性能更优d = {1:10,2:20,...}b = d[a] ``` -## 1.15.4 直接迭代序列元素 +## 4. 直接迭代序列元素 对序列(str、list、tuple等),直接迭代序列元素,比迭代元素的索引速度要更快。 @@ -44,7 +44,7 @@ a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range(len(a)): print(a[i]) ``` -## 1.15.5 采用生成器表达式替代列表解析 +## 5. 采用生成器表达式替代列表解析 列表解析(list comprehension),会产生整个列表,对大量数据的迭代会产生负面效应。 @@ -54,13 +54,13 @@ a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range #计算文件f的非空字符个数#生成器表达式l = sum([len(word) for line in f for word in line.split()])#列表解析l = sum(len(word) for line in f for word in line.split()) ``` -## 1.15.6 先编译后调用 +## 6. 先编译后调用 使用eval()、exec()函数执行代码时,最好调用代码对象(提前通过compile()函数编译成字节码),而不是直接调用str,可以避免多次执行重复编译过程,提高程序性能。 正则表达式模式匹配也类似,也最好先将正则表达式模式编译成regex对象(通过re.complie()函数),然后再执行比较和匹配。 -## 1.15.7 模块编程习惯 +## 7. 模块编程习惯 模块中的最高级别Python语句(没有缩进的代码)会在模块导入(import)时执行(不论其是否真的必要执行)。因此,应尽量将模块所有的功能代码放到函数中,包括主程序相关的功能代码也可放到main()函数中,主程序本身调用main()函数。 diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md index 4138112..5cab543 100644 --- a/source/c01/c01_17.md +++ b/source/c01/c01_17.md @@ -4,13 +4,16 @@ 学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Descriptor(描述符)特性可以排得上号。 -描述符 是Python 语言独有的特性,它不仅在应用层使用,在语言的基础设施中也有涉及。 +描述符 是Python 语言独有的特性,它不仅在应用层使用,在语言语法糖的实现上也有使用到(在下面的文章会一一介绍)。 -我可以大胆地猜测,你对于描述符的了解是始于诸如 Django ORM 和 SQLAlchemy 中的字段对象,是的,它们都是描述符。你的它的认识,可能也止步于此,如果你没有去深究,它为何要如此设计?也就加体会不到 Python 给我们带来的便利与优雅。 +当你点进这篇文章时 -由于 描述符的内容较多,长篇大论,容易让你倦怠,所以我打算分几篇来讲。 +- 你也许没学过描述符,甚至没听过描述符。 +- 或者你对描述符只是一知半解 -## 1.17.1 为什么要使用描述符? +无论你是哪种,本篇都将带你全面的学习描述符,一起来感受 Python 语言的优雅。 + +### 1. 为什么要使用描述符? 假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。 @@ -125,13 +128,15 @@ class Student: 你以为你写的代码,已经非常优秀,无懈可击了。 + + 没想到,人外有天,你的主管看了你的代码后,深深地叹了口气:类里的三个属性,math、chinese、english,都使用了 Property 对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100 就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢,这代码简直没法忍。去了解一下 Python 的描述符吧。 经过主管的指点,你知道了「描述符」这个东西。怀着一颗敬畏之心,你去搜索了下关于 描述符的用法。 其实也很简单,一个实现了 `描述符协议` 的类就是一个描述符。 -什么描述符协议:实现了 `__get__()`、`__set__()`、`__delete__()` 其中至少一个方法的类,就是一个描述符。 +什么描述符协议:在类里实现了 `__get__()`、`__set__()`、`__delete__()` 其中至少一个方法。 - `__get__`: 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。 - `__set__ `:将在属性分配操作中调用。不会返回任何内容。 @@ -139,7 +144,7 @@ class Student: 对描述符有了大概的了解后,你开始重写上面的方法。 -如前所述,Score 类是一个描述器,当从 Student 的实例访问 math、chinese、english这三个属性的时候,都会经过 Score 类里的三个特殊的方法。这里的 Score 避免了 使用Property 出现大量的代码无法复用的尴尬。 +如前所述,Score 类是一个描述符,当从 Student 的实例访问 math、chinese、english这三个属性的时候,都会经过 Score 类里的三个特殊的方法。这里的 Score 避免了 使用Property 出现大量的代码无法复用的尴尬。 ```python class Score: @@ -184,11 +189,11 @@ class Student: 以上,我举了下具体的实例,从最原始的编码风格到 Property ,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 -通过此文,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 `保护属性不受修改`、`属性类型检查` 的基本功能,同时有大大提高代码的复用率。 +到这里,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 `保护属性不受修改`、`属性类型检查` 的基本功能,同时有大大提高代码的复用率。 + ---- -## 1.17.2 描述符的访问规则 +### 2. 描述符的访问规则 描述符分两种: @@ -199,7 +204,7 @@ class Student: 其实就一句话,**数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**。 -如果实例字典中有与描述器同名的属性,如果描述器是数据描述器,优先使用数据描述器,如果是非数据描述器,优先使用字典中的属性。 +如果实例字典中有与描述符同名的属性,如果描述符是数据描述符,优先使用数据描述符,如果是非数据描述符,优先使用字典中的属性。 这边还是以上节的成绩管理的例子来说明,方便你理解。 @@ -262,15 +267,15 @@ class Student: 当我们对一个实例属性进行访问时,Python 会按 `obj.__dict__` → `type(obj).__dict__` → `type(obj)的父类.__dict__` 顺序进行查找,如果查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。 -## 1.17.3 基于描述符如何实现property +### 3. 基于描述符如何实现property 经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。 -正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Python 的特性的底层实现机制都是基于 `描述符协议` 的,比如我们熟悉的`@property` 、`@classmethod` 、`@staticmethod` 和 `super` 等。 +正常人所见过的描述符的用法就是上面提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Python 的特性的底层实现机制都是基于 `描述符协议` 的,比如我们熟悉的`@property` 、`@classmethod` 、`@staticmethod` 和 `super` 等。 先来说说 `property` 吧。 -有了第一篇的基础,我们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。 +有了前面的基础,我们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。 ```python class Student: @@ -362,6 +367,8 @@ class Student: raise ValueError("Valid value must be in [0, 100]") ``` + + 为了尽量让你少产生一点疑惑,我这里做两点说明: 1. 使用`TestProperty`装饰后,`math` 不再是一个函数,而是`TestProperty` 类的一个实例。所以第二个math函数可以使用 `math.setter` 来装饰,本质是调用`TestProperty.setter` 来产生一个新的 `TestProperty` 实例赋值给第二个`math`。 @@ -383,7 +390,7 @@ in __get__ 对于以上理解 `property` 的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。 -## 1.17.4 基于描述符如何实现staticmethod +### 4. 基于描述符如何实现staticmethod 说完了 `property` ,这里再来讲讲 `@classmethod` 和 `@staticmethod` 的实现原理。 @@ -431,7 +438,7 @@ in staticmethod __get__ hello ``` -## 1.17.4 基于描述符如何实现classmethod +### 5. 基于描述符如何实现classmethod 同样的 ` classmethod` 也是一样。 @@ -468,9 +475,13 @@ hello 讲完了 `property`、`staticmethod`和`classmethod` 与 描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 super 的实现原理,就交由你来自己完成。 -## 1.17.5 所有实例共享描述符 +### 6. 所有实例共享描述符 + +通过以上内容的学习,你是不是觉得自己已经对描述符足够了解了呢? + +可在这里,我想说以上的描述符代码都有问题。 -若要合理使用描述符,利用描述符给我们带来的编码上的便利。有一个坑,需要注意,比如下面这个Student我们没有定义构造函数 +问题在哪里呢?请看下面这个例子。 ```python class Score: @@ -496,7 +507,9 @@ class Student: return "".format(self.math, self.chinese, self.english) ``` -看一下会出现什么样的问题,std2 居然共享了std1 的属性值,因为它被当成了一个类变量了,而每个实例都没有自己的实例变量,自然访问的是同一个变量。这样是很难达到我们使用描述符的初衷。 +Student 里没有像前面那样写了构造函数,但是关键不在这儿,没写只是因为没必要写。 + +然后来看一下会出现什么样的问题呢 ```python >>> std1 = Student() @@ -513,7 +526,61 @@ class Student: ``` -而正确的做法应该是,所有的实例数据只属于该实例本身(通过实例初始化传入),具体写法可参考上一节。 +从结果上来看,std2 居然共享了 std1 的属性值,只要其中一个实例的变量发生改变,另一个实例的变量也会跟着改变。 + +探其根因,是由于此时 math,chinese,english 三个全部是类变量,导致 std2 和 std1 在访问 math,chinese,english 这三个变量时,其实都是访问类变量。 + +问题是不是来了?小明和小强的分数怎么可能是绑定的呢?这很明显与实际业务不符。 + +使用描述符给我们制造了便利,却无形中给我们带来了麻烦,难道这也是描述符的特性吗? + +描述符是个很好用的特性,会出现这个问题,是由于我们之前写的描述符代码都是错误的。 + +描述符的机制,在我看来,只是抢占了访问顺序,而具体的逻辑却要因地制宜,视情况而定。 + +如果要把 math,chinese,english 这三个变量变成实例之间相互隔离的属性,应该这么写。 + +```python +class Score: + def __init__(self, subject): + self.name = subject + + def __get__(self, instance, owner): + return instance.__dict__[self.name] + + def __set__(self, instance, value): + if 0 <= value <= 100: + instance.__dict__[self.name] = value + else: + raise ValueError + + +class Student: + math = Score("math") + chinese = Score("chinese") + english = Score("english") + + def __init__(self, math, chinese, english): + self.math = math + self.chinese = chinese + self.english = english + + def __repr__(self): + return "".format(self.math, self.chinese, self.english) +``` + +引导程序逻辑进入描述符之后,不管你是获取属性,还是设置属性,都是直接作用于 instance 的。 + +![](http://image.iswbm.com/20200812085823.png) + +这段代码,你可以仔细和前面的对比一下。 + +不难看出: + +- 之前的错误代码,更像是把描述符当做了存储节点。 +- 之后的正确代码,则是把描述符直接当做代理,本身不存储值。 + +以上便是我对描述符的全部分享,希望能对你有所帮助。 diff --git a/source/c01/c01_18.md b/source/c01/c01_18.md index e65704a..0a20d4b 100644 --- a/source/c01/c01_18.md +++ b/source/c01/c01_18.md @@ -2,7 +2,7 @@ ![](http://image.iswbm.com/20200602135014.png) -## 1.18.1 安装MySQL-python +## 1. 安装MySQL-python MySQL-python 这玩意实在是太难装了,为了以防后面再踩坑,这里还是记录一下吧。 @@ -72,7 +72,7 @@ brew install mysql@5.7 终于安装成功,折腾了两个晚上(主要是网速慢)。 -## 1.18.2 Mac 启动MySQL服务 +## 2. Mac 启动MySQL服务 使用 brew 安装 mysql 成功后,又陷入了一个坑。。 @@ -112,7 +112,7 @@ mysql -uroot -p -## 1.18.3 Win上忘记密码 +## 3. Win上忘记密码 ```shell @@ -140,7 +140,7 @@ mysql -uroot -p -## 1.18.4 命令行使用技巧 +## 4. 命令行使用技巧 ![](http://image.iswbm.com/20190705225651.png) diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md index 0f8309c..bbf32c0 100644 --- a/source/c01/c01_20.md +++ b/source/c01/c01_20.md @@ -2,15 +2,19 @@ ![](http://image.iswbm.com/20200602135014.png) -这个标题「**静态方法其实暗藏玄机**」其实只是该文章的一个知识点。或许有些标题党,但没有关系,我相信有不少人对此并没有深入研究他们,不信我问你三个问题,你看能否答上来。 +静态方法,应该没人不知道吧? -1、Python2.x和3.x中,函数和方法的区分有什么不同? +它可以算是一个很基础、简单的知识点。 -2、有了类/实例方法和普通函数,为什么还会有静态方法? +但是就是这样一个知识点,也有不少可以探究的东西。 -3、Python3.x 中,静态方法有几种写法? +不信我问你三个问题,你看能否答上来。 -带着这三个问题,你可以尝试在下文中寻找答案。 +1. Python2.x和3.x中,函数和方法的区分有什么不同? +2. 有了类/实例方法和普通函数,为什么还会有静态方法? +3. Python3.x 中,静态方法有几种写法? + +如果你没能答上来,那你可以尝试在下文中寻找答案。 --- diff --git a/source/c07/c07_22.md b/source/c07/c07_22.md index a4ad156..5e753d4 100644 --- a/source/c07/c07_22.md +++ b/source/c07/c07_22.md @@ -166,7 +166,7 @@ $ rpm -Vf /etc/path/file rm -f /var/lib/rpm/__db.00* # 重建rpm数据文件 -rpm –rebuilddb +rpm -rebuilddb ``` diff --git a/source/c08/c08_01.md b/source/c08/c08_01.md index 33f3ea1..3123f05 100644 --- a/source/c08/c08_01.md +++ b/source/c08/c08_01.md @@ -349,15 +349,7 @@ $ vgreduce --removemissing --force hdd-volumes # 添加 pv 到 vg 中 $ vgextend hdd-volumes /dev/sda ``` -### 1.3 QEMU命令 -```shell -# 查看img镜像信息 -$ qemu-img info - -# 创建qcow2文件 -$ qemu-img create -f qcow2 openstack-name.qcow2 100G -``` ## 三、集群相关 From 8c88d1bd88cc066215b890ffa9838025ff64142e Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Thu, 1 Oct 2020 07:38:55 +0800 Subject: [PATCH 125/147] update --- source/c01/c01_01.rst | 72 +++++++++++------------ source/c01/c01_02.rst | 8 +-- source/c01/c01_05.rst | 16 +++--- source/c01/c01_06.rst | 8 +-- source/c01/c01_09.rst | 8 +-- source/c01/c01_11.rst | 12 ++-- source/c01/c01_12.rst | 28 ++++----- source/c01/c01_13.rst | 26 +++++---- source/c01/c01_14.rst | 18 +++--- source/c01/c01_15.rst | 28 ++++----- source/c01/c01_17.rst | 129 +++++++++++++++++++++++++++++++----------- source/c01/c01_18.rst | 16 +++--- source/c01/c01_20.rst | 14 +++-- source/c07/c07_22.rst | 2 +- source/c08/c08_01.rst | 11 ---- source/c09/c09_03.rst | 5 ++ source/c10/c10_08.md | 2 +- source/c10/c10_08.rst | 10 ++-- source/c10/c10_09.md | 95 +++++++++++++++++++++++++++++++ source/c10/c10_09.rst | 113 +++++++++++++++++++++++++++++++++++- 20 files changed, 439 insertions(+), 182 deletions(-) diff --git a/source/c01/c01_01.rst b/source/c01/c01_01.rst index d147956..90abe6b 100755 --- a/source/c01/c01_01.rst +++ b/source/c01/c01_01.rst @@ -3,20 +3,12 @@ |image0| --------------- - -从今天开始,小明将和你一起过一下,那些在面试「Python开发」岗位时面试官喜欢问的问题。内容基础,但是你不一定会噢。 - -这些问题全部来自个人经验,群友推荐以及网络上的帖子。如果你有好的问题,也可以随时向我提出(不要觉得简单),我会筛选后整理出来在这里,供大家学习取经,给大家在求职路上贡献一份力。 - -开篇讲些什么好呢? - -今天就来罗列一下,Python2.x和3.x到底有哪些区别吧。 +0. 去哪里找 +----------- -去哪里找 --------- +这块的内容,你随便使用搜索引擎都可以查到。 -你随便全使用搜索引擎都可以查到这些资料,但是大家说的都是一些普遍都知道的事儿。或者都是抄来抄去,内容相差无几。 +但是大家好像都在抄来抄去,内容相差无几。 授人以鱼,不如授人以渔。 @@ -34,8 +26,8 @@ 接下来。和大家一起过一下,Python2.x和3.x到底有哪些区别,这不仅在你开发过程中需要考虑的,也是面试过程面试官经常会问及的。 -1.1.1 print ------------ +1. print +-------- 在Python 2.6之前,只支持 @@ -58,8 +50,8 @@ print("hello") print ("hello") -1.1.2 编码方式 --------------- +2. 编码方式 +----------- 在Python2.x中,默认使用\ ``ASCII``\ 编码。 @@ -89,8 +81,8 @@ Python 2的正确使用方法,如下 所以我们可以在程序中,随意的使用中文(但并不推荐),不会报错。 -1.2.3 除法运算 --------------- +3. 除法运算 +----------- Python 2.x中除法运算,整数间运算只保留整数(向下取整)。 @@ -126,8 +118,8 @@ Python 3.x中除法运算,全部保留小数(即使能被整除)。 >>> -8//3.0 -3.0 -1.2.4 异常捕获 --------------- +4. 异常捕获 +----------- 在 Python 3 中,只能使用 ``as`` 作为关键词。而在Python 2中经常使用 ``except Exception, e`` 使用语法except (exc1, exc2) as @@ -141,8 +133,8 @@ Python 2.6已经支持这两种语法。 在2.x时代,异常在代码中除了表示程序错误,还经常做一些普通控制结构应该做的事情,在3.x中可以看出,设计者让异常变的更加专一,只有在错误发生的情况才能去用异常捕获语句来处理。 -1.2.5 xrange ------------- +5. xrange +--------- 首先,要了解的是,xrange是只有在Python2.x中才有的产物。 @@ -165,8 +157,8 @@ Python 2.6已经支持这两种语法。 >>> range(10) range(0, 10) -1.2.6 用户输入 --------------- +6. 用户输入 +----------- 在2.x 中,有两个函数。raw_input()和input()。 - raw_input():将所有输入作为字符串看待,返回字符串类型。 - @@ -175,8 +167,8 @@ input():只能接收“数字”的输入。 在3.x 中,对这两个函数进行整合,只留下一个\ ``input()``\ ,既可输入数字,也可输入字符串,返回的是字符串类型。 -1.2.7 数据类型 --------------- +7. 数据类型 +----------- Python 3.x 一个很重要的特性是,对字符串和二进制数据流做了明确的区分。 @@ -190,8 +182,8 @@ Python 还有一点是,3.X去除了long类型,取代它的是整型(int)。3.x的整型是没有限制大小的,可以当做long类型使用, 但实际上由于机器内存的有限,我们使用的整数是不可能无限大的。 -1.2.8 函数式编程 ----------------- +8. 函数式编程 +------------- 在Python中,我们常常使用到的map,filter,reduce,在2.x和3.x中也有所不同。 @@ -231,16 +223,16 @@ Python 对于 reduce 函数,它在 Python 3.x 中已经不属于 built-in 了,被挪到 functools 模块当中。 -1.2.9 协程关键字 ----------------- +9. 协程关键字 +------------- 在Python3.3后,协程中,新增了yield from 和 async/await 关键字,这在2.x中是没有。 关于yield from的语法剖析,可以前往查看我的另一篇文章。 -1.2.10 类的类型 ---------------- +10. 类的类型 +------------ Python2.x 默认使用经典类,只有显示继承object才是新式类。 @@ -257,8 +249,8 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 class Cls(object): pass -1.2.11 变量作用域 ------------------ +11. 变量作用域 +-------------- - 在2.x中无法将局部变量声明为全局变量。 - 在3.x中可以使用nonlocal语法将局部变量声明为全局变量。 @@ -277,8 +269,8 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 # 2.x输出:100 # 3.x输出:200 -1.2.12 元类的使用 ------------------ +12. 元类的使用 +-------------- 在2.x 中 @@ -301,8 +293,8 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 class Person(metaclass=MetaPerson): pass -1.2.13 模块变化 ---------------- +13. 模块变化 +------------ - 去掉了一些模块。由于不常用,这里就不列举了。 - 新增了一些模块。比如:concurrent.futures,asyncio等 @@ -310,9 +302,9 @@ Python3.x 没有经典类,只有新式类,而且有三种写法 -------------- -大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。但是那些对于我们普通开发者来说,并不那么重要。完全可以不去关注。 +大概就是这些内容,可能还有更细微的差别,这些内容要前往官网查看。 -实际上,当我熟悉一个版本后,基本上是可以无缝过渡到另一个版本的。这篇文章,更多的是为了科普和应对面试。 +但是那些对于我们普通开发者来说,并不那么重要,个人感觉可以不去关注。 -------------- diff --git a/source/c01/c01_02.rst b/source/c01/c01_02.rst index 485f7e3..7ea82dc 100755 --- a/source/c01/c01_02.rst +++ b/source/c01/c01_02.rst @@ -174,8 +174,8 @@ callable() True >>> -模块(Modules) ---------------- +3. 模块(Modules) +------------------ \__doc_\_ ~~~~~~~~~ @@ -217,8 +217,8 @@ help() 一样。 包含了模块里可用的属性名-属性的字典;也就是可以使用模块名.属性名访问的对象。 -类(Class) ------------ +4. 类(Class) +-------------- .. _doc__-1: diff --git a/source/c01/c01_05.rst b/source/c01/c01_05.rst index a1712cc..c1f7d89 100755 --- a/source/c01/c01_05.rst +++ b/source/c01/c01_05.rst @@ -5,8 +5,8 @@ -------------- -1.5.1 作用域 ------------- +1. 作用域 +--------- Python的作用域可以分为四种: - L (Local) 局部作用域 - E (Enclosing) 闭包函数外的函数中 - G (Global) 全局作用域 - B (Built-in) 内建作用域 @@ -54,8 +54,8 @@ Python的作用域可以分为四种: - L (Local) 局部作用域 - E (E print(name) # NameError: name 'name' is not defined -1.5.2 闭包 ----------- +2. 闭包 +------- 闭包这个概念很重要噢。你一定要掌握。 >在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。其实装饰函数,很多都是闭包。 @@ -79,8 +79,8 @@ Python的作用域可以分为四种: - L (Local) 局部作用域 - E (E deco()() # 输出:MING -1.5.3 改变作用域 ----------------- +3. 改变作用域 +------------- 变量的作用域,与其定义(或赋值)的位置有关,但不是绝对相关。 因为我们可以在某种程度上去改变\ ``向上``\ 的作用范围。 @@ -138,8 +138,8 @@ global好理解,这里只讲下nonlocal。 deco()() # 输出:10 -1.5.4 变量集合 --------------- +4. 变量集合 +----------- 在Python中,有两个内建函数,你可能用不到,但是需要掌握它们。 - globals() :以dict的方式存储所有全局变量 - locals():以dict的方式存储所有局部变量 diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index 9a21991..3a1df17 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -11,8 +11,8 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 以下两点,第一点是大家所熟知的,而第二点可能只有老司机才会知道,只有学习了第二点,才算真正理解了元组存在的价值和意义。 -2.6.1 不可变列表 ----------------- +1. 不可变列表 +------------- 这是\ ``元组``\ 区别于\ ``列表``\ 最显著的特征。 @@ -55,8 +55,8 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 # 重复拼接 s1*n -2.6.2 具名元组 --------------- +2. 具名元组 +----------- 这个特性,我个人认为,才是元组存在的意义所在。 diff --git a/source/c01/c01_09.rst b/source/c01/c01_09.rst index 7924b47..ddc0e8e 100755 --- a/source/c01/c01_09.rst +++ b/source/c01/c01_09.rst @@ -8,8 +8,8 @@ 类的单继承,是我们再熟悉不过的,写起来也毫不费力。而多继承呢,见得很多,写得很少。在很多的项目代码里,你还会见到一种很奇怪的类,他们有一个命名上的共同点,就是在类名的结尾,都喜欢用 Mixin。 -1.9.1 认识Mixin模式 -------------------- +1. 认识Mixin模式 +---------------- 那我们今天就来讲讲这个 Mixin,对于这个Mixin,如何理解?它其实是一种设计模式,如果开发者之间没有产生这样一种设计模式的共识,那么设计模式将不复存在。 @@ -48,8 +48,8 @@ Mixin 类,一般都要求开发者遵循规范,在类名末尾加上 Mixin - 功能单一:若有多个功能,那就写多个Mixin类; - 绝对独立:不能依赖于子类的实现;子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。 -1.9.2 不使用Mixin的弊端 ------------------------ +2. 不使用Mixin的弊端 +-------------------- 你肯定会问,不使用 Mixin 行吗? diff --git a/source/c01/c01_11.rst b/source/c01/c01_11.rst index 0334634..7113dfd 100755 --- a/source/c01/c01_11.rst +++ b/source/c01/c01_11.rst @@ -5,8 +5,8 @@ -------------- -一、正则表达式先导 ------------------- +1. 正则表达式先导 +----------------- 1.1 正则基础知识 ~~~~~~~~~~~~~~~~ @@ -77,8 +77,8 @@ | ``{n,m}?`` :重复n到m次,但尽可能少重复 | ``{n,}?``\ : 重复n次以上,但尽可能少重复 -二、Python中的正则 ------------------- +2. Python中的正则 +----------------- 在Python中,自带了re模块,这是专门用来做正则表达式的匹配的。 @@ -190,8 +190,8 @@ start(),end(),end() match.span() (0, 3) -三、检验表达式 --------------- +3. 检验表达式 +------------- 3.1 校验数字 ~~~~~~~~~~~~ diff --git a/source/c01/c01_12.rst b/source/c01/c01_12.rst index 540dd6e..695d245 100755 --- a/source/c01/c01_12.rst +++ b/source/c01/c01_12.rst @@ -8,8 +8,8 @@ 初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码? 这些内容是在去年整理的,现在重新整理下,发布在博客,搞懂字符串编码,这一篇文章足矣 -1.12.1 前言必知 ---------------- +1. 前言必知 +----------- 初学计算机的人,肯定对众多字符编码感到头疼。为什么会那么多字符串编码? @@ -18,8 +18,8 @@ bit,位,一个bit可以表示两个数字,0和1 byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,255] -1.12.2 ASCII编码 ----------------- +2. ASCII编码 +------------ 原始ASCII编码 ~~~~~~~~~~~~~ @@ -44,8 +44,8 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2 原始ASCII编码 -1.12.3 ANSI标准 ---------------- +3. ANSI标准 +----------- ANSI:美国国家标准学会(AMERICAN NATIONAL STANDARDS INSTITUTE) @@ -63,8 +63,8 @@ byte,字节,一个byte由8个bit表示,一个byte表示的数字区间[0,2 - 在日文Windows中,ANSI编码是\ ``Shift_JIS`` - … -1.12.4 Unicode编码 ------------------- +4. Unicode编码 +-------------- 上节讲到各国都有了自己的编码,已经可以正常使用电脑了。 但是随着国际交流的日益频繁和迫切需要,我们中国人也要和美国人进行信息交流,美国人还要和日本人进行信息交流。假设,我们把一篇中文论文发到网上,美国人在用自己的计算机查看这个网页的时候,由于本地计算机不支持GB2312,结果无法显示正确信息,或者乱码。你会说,那很简单啊,让美国人在电脑上装上GB2312编码不就OK。真的OK吗?世界上那么多国家,那么多语言,那么多ANSI编码,都装上是不是要疯了。好吧,假如你真的不厌其烦的装上了,再假设,有一个中国人,他不仅会说汉语,还会说日语。他发表了一篇既有汉语也有日文的文章,而美国人在看这篇文章的时候,是用GB2312来解码呢,还是用Shift_JIS来解码呢?无论用哪个解码都会出现乱码的情况。 @@ -78,8 +78,8 @@ Consortium),一个统筹统一码(Unicode)发展的非营利机构,其 Unicode至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为2017年6月20日公布的10.0.0,已经收入超过十万个字符(第十万个字符在2005年获采纳)。\ `Unicode-维基百科 `__ -1.12.5 UTF-8编码 ----------------- +5. UTF-8编码 +------------ | 到目前为止,世界各国人民,都能愉快的无语言障碍的使用计算机了。 | 但是随着信息化时代的来临,人们越来越追求资源的传输速度和硬盘的存储效率。 @@ -103,8 +103,8 @@ TransferFormat,即把Unicode转做某种格式的意思)应运而生 当在txt输入输入’联通’,保存再次打开就乱码,输入’你好联通’就不会出现这个情况。 |image2| -1.12.6 编码之于Python ---------------------- +6. 编码之于Python +----------------- **Python2** Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首。但这也不怪Python,在Python诞生的时候,Unicode还没出现。 @@ -132,8 +132,8 @@ Python2默认是使用ASCII编码,这也是出现编码问题的罪魁祸首 在Python3中,已经默认使用Unicode编码了。解决了很多编码的问题。 如果py文件中,含有中文还是得在文件头出加入 ``# coding=utf-8`` -1.12.7 扩展阅读 ---------------- +7. 扩展阅读 +----------- 中文编码的发展 GB2312-> GBK -> GB18030 diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index e5f826c..d84ac18 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -5,8 +5,8 @@ -------------- -1.13.1 lambda 表达式 --------------------- +1. lambda 表达式 +---------------- 匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。 @@ -66,8 +66,8 @@ function)是指一类无需定义标识符(函数名)的函数。通俗来 首先我们要知道 lambda 是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。 -1.13.2 map 函数 ---------------- +2. map 函数 +----------- map 函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。 @@ -89,8 +89,8 @@ map for i in [1,2,3,4,5]: mylist.append(i*2) -1.13.3 filter 函数 ------------------- +3. filter 函数 +-------------- filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda 表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回 @@ -103,14 +103,16 @@ True,则元素会被保留下来,当表达式返回 False ,则元素会被 >>>filter(lambda x: x < 0, range(-5, 5)) [-5, -4, -3, -2, -1] -1.13.4 reduce 函数 ------------------- +4. reduce 函数 +-------------- reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 lambda 函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。 -|reduce 逻辑演示| 这边举个例子你也就明白了。 +|image1| + +这边举个例子你也就明白了。 :: @@ -126,8 +128,8 @@ reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 6+4+10 10+5=15 -1.13.5 注意点 -------------- +5. 注意点 +--------- 以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的 Pythonic ,在某一程度上代码看起来更加的简洁。 @@ -162,6 +164,6 @@ Pythonic ,在某一程度上代码看起来更加的简洁。 |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |reduce 逻辑演示| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyx6i8q3anj208c04u3yu.jpg +.. |image1| image:: http://image.iswbm.com/20200930175131.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_14.rst b/source/c01/c01_14.rst index 17ae72c..bb99084 100644 --- a/source/c01/c01_14.rst +++ b/source/c01/c01_14.rst @@ -3,8 +3,6 @@ |image0| - 提示:前面的内容较为基础,重点知识在后半段。 - ``with`` 这个关键字,对于每一学习Python的人,都不会陌生。 操作文本对象的时候,几乎所有的人都会让我们要用 ``with open`` @@ -15,8 +13,8 @@ with open('test.txt') as f: print f.readlines() -1.14.1 what context manager? ------------------------------ +1. what context manager? +------------------------- **基本语法** @@ -33,8 +31,8 @@ 2. 上下文管理器:open('test.txt') 3. f 不是上下文管理器,应该是资源对象。 -1.14.2 how context manager? ----------------------------- +2. how context manager? +------------------------ 要自己实现这样一个上下文管理,要先知道上下文管理协议。 @@ -68,8 +66,8 @@ 从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在\ ``__enter__``\ 中,而将资源的关闭写在\ ``__exit__`` 中。 -1.14.3 why context manager? ----------------------------- +3. why context manager? +------------------------ 学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。 @@ -124,8 +122,8 @@ Python解释器,这个异常我们已经捕获了,不需要再往外抛了 当主逻辑代码没有报异常时,这三个参数将都为None。 -1.14.4 how contextlib? ----------------------- +4. how contextlib? +------------------ 在上面的例子中,我们只是为了构建一个上下文管理器,却写了一个类。如果只是要实现一个简单的功能,写一个类未免有点过于繁杂。这时候,我们就想,如果只写一个函数就可以实现上下文管理器就好了。 diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index 1ed39f1..4cf7517 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -7,16 +7,16 @@ 转载自:https://zhuanlan.zhihu.com/p/38160586 -1.15.1 使用局部变量 -------------------- +1. 使用局部变量 +--------------- 尽量使用局部变量代替全局变量:便于维护,提高性能并节省内存。 使用局部变量替换模块名字空间中的变量,例如 ls = os.linesep。一方面可以提高程序性能,局部变量查找速度更快;另一方面可用简短标识符替代冗长的模块变量,提高可读性。 -1.15.2 减少函数调用次数 ------------------------ +2. 减少函数调用次数 +------------------- 对象类型判断时,采用isinstance()最优,采用对象类型身份(id())次之,采用对象值(type())比较最次。 @@ -34,8 +34,8 @@ os.linesep。一方面可以提高程序性能,局部变量查找速度更快 Y,而不是import X; X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查找到X模块,然后在X模块的字典中查找Y)。 -1.15.3 采用映射替代条件查找 ---------------------------- +3. 采用映射替代条件查找 +----------------------- 映射(比如dict等)的搜索速度远快于条件语句(如if等)。Python中也没有select-case语句。 @@ -43,8 +43,8 @@ X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查 #if查找if a == 1: b = 10elif a == 2: b = 20...#dict查找,性能更优d = {1:10,2:20,...}b = d[a] -1.15.4 直接迭代序列元素 ------------------------ +4. 直接迭代序列元素 +------------------- 对序列(str、list、tuple等),直接迭代序列元素,比迭代元素的索引速度要更快。 @@ -52,8 +52,8 @@ X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查 a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range(len(a)): print(a[i]) -1.15.5 采用生成器表达式替代列表解析 ------------------------------------ +5. 采用生成器表达式替代列表解析 +------------------------------- 列表解析(list comprehension),会产生整个列表,对大量数据的迭代会产生负面效应。 @@ -64,15 +64,15 @@ comprehension),会产生整个列表,对大量数据的迭代会产生负 #计算文件f的非空字符个数#生成器表达式l = sum([len(word) for line in f for word in line.split()])#列表解析l = sum(len(word) for line in f for word in line.split()) -1.15.6 先编译后调用 -------------------- +6. 先编译后调用 +--------------- 使用eval()、exec()函数执行代码时,最好调用代码对象(提前通过compile()函数编译成字节码),而不是直接调用str,可以避免多次执行重复编译过程,提高程序性能。 正则表达式模式匹配也类似,也最好先将正则表达式模式编译成regex对象(通过re.complie()函数),然后再执行比较和匹配。 -1.15.7 模块编程习惯 -------------------- +7. 模块编程习惯 +--------------- 模块中的最高级别Python语句(没有缩进的代码)会在模块导入(import)时执行(不论其是否真的必要执行)。因此,应尽量将模块所有的功能代码放到函数中,包括主程序相关的功能代码也可放到main()函数中,主程序本身调用main()函数。 diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index 0febc80..8875596 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -7,16 +7,18 @@ Descriptor(描述符)特性可以排得上号。 描述符 是Python -语言独有的特性,它不仅在应用层使用,在语言的基础设施中也有涉及。 +语言独有的特性,它不仅在应用层使用,在语言语法糖的实现上也有使用到(在下面的文章会一一介绍)。 -我可以大胆地猜测,你对于描述符的了解是始于诸如 Django ORM 和 SQLAlchemy -中的字段对象,是的,它们都是描述符。你的它的认识,可能也止步于此,如果你没有去深究,它为何要如此设计?也就加体会不到 -Python 给我们带来的便利与优雅。 +当你点进这篇文章时 -由于 描述符的内容较多,长篇大论,容易让你倦怠,所以我打算分几篇来讲。 +- 你也许没学过描述符,甚至没听过描述符。 +- 或者你对描述符只是一知半解 -1.17.1 为什么要使用描述符? ---------------------------- +无论你是哪种,本篇都将带你全面的学习描述符,一起来感受 Python +语言的优雅。 + +1. 为什么要使用描述符? +~~~~~~~~~~~~~~~~~~~~~~~ 假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。 @@ -144,9 +146,9 @@ Python 的描述符吧。 其实也很简单,一个实现了 ``描述符协议`` 的类就是一个描述符。 -什么描述符协议:实现了 +什么描述符协议:在类里实现了 ``__get__()``\ 、\ ``__set__()``\ 、\ ``__delete__()`` -其中至少一个方法的类,就是一个描述符。 +其中至少一个方法。 - ``__get__``\ : 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。 @@ -155,7 +157,7 @@ Python 的描述符吧。 对描述符有了大概的了解后,你开始重写上面的方法。 -如前所述,Score 类是一个描述器,当从 Student 的实例访问 +如前所述,Score 类是一个描述符,当从 Student 的实例访问 math、chinese、english这三个属性的时候,都会经过 Score 类里的三个特殊的方法。这里的 Score 避免了 使用Property 出现大量的代码无法复用的尴尬。 @@ -204,14 +206,12 @@ math、chinese、english这三个属性的时候,都会经过 Score 以上,我举了下具体的实例,从最原始的编码风格到 Property ,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 -通过此文,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 +到这里,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 ``保护属性不受修改``\ 、\ ``属性类型检查`` 的基本功能,同时有大大提高代码的复用率。 --------------- - -1.17.2 描述符的访问规则 ------------------------ +2. 描述符的访问规则 +~~~~~~~~~~~~~~~~~~~ 描述符分两种: @@ -222,7 +222,7 @@ math、chinese、english这三个属性的时候,都会经过 Score 其实就一句话,\ **数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**\ 。 -如果实例字典中有与描述器同名的属性,如果描述器是数据描述器,优先使用数据描述器,如果是非数据描述器,优先使用字典中的属性。 +如果实例字典中有与描述符同名的属性,如果描述符是数据描述符,优先使用数据描述符,如果是非数据描述符,优先使用字典中的属性。 这边还是以上节的成绩管理的例子来说明,方便你理解。 @@ -289,19 +289,19 @@ math、chinese、english这三个属性的时候,都会经过 Score 顺序进行查找,如果查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。 -1.17.3 基于描述符如何实现property ---------------------------------- +3. 基于描述符如何实现property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。 -正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 +正常人所见过的描述符的用法就是上面提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Python 的特性的底层实现机制都是基于 ``描述符协议`` 的,比如我们熟悉的\ ``@property`` 、\ ``@classmethod`` 、\ ``@staticmethod`` 和 ``super`` 等。 先来说说 ``property`` 吧。 -有了第一篇的基础,我们知道了 property +有了前面的基础,我们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。 .. code:: python @@ -430,8 +430,8 @@ property 对于以上理解 ``property`` 的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。 -1.17.4 基于描述符如何实现staticmethod -------------------------------------- +4. 基于描述符如何实现staticmethod +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 说完了 ``property`` ,这里再来讲讲 ``@classmethod`` 和 ``@staticmethod`` 的实现原理。 @@ -483,8 +483,8 @@ property in staticmethod __get__ hello -1.17.4 基于描述符如何实现classmethod ------------------------------------- +5. 基于描述符如何实现classmethod +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 同样的 ``classmethod`` 也是一样。 @@ -523,10 +523,14 @@ property 描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 super 的实现原理,就交由你来自己完成。 -1.17.5 所有实例共享描述符 -------------------------- +6. 所有实例共享描述符 +~~~~~~~~~~~~~~~~~~~~~ -若要合理使用描述符,利用描述符给我们带来的编码上的便利。有一个坑,需要注意,比如下面这个Student我们没有定义构造函数 +通过以上内容的学习,你是不是觉得自己已经对描述符足够了解了呢? + +可在这里,我想说以上的描述符代码都有问题。 + +问题在哪里呢?请看下面这个例子。 .. code:: python @@ -552,8 +556,10 @@ super 的实现原理,就交由你来自己完成。 def __repr__(self): return "".format(self.math, self.chinese, self.english) -看一下会出现什么样的问题,std2 居然共享了std1 -的属性值,因为它被当成了一个类变量了,而每个实例都没有自己的实例变量,自然访问的是同一个变量。这样是很难达到我们使用描述符的初衷。 +Student +里没有像前面那样写了构造函数,但是关键不在这儿,没写只是因为没必要写。 + +然后来看一下会出现什么样的问题呢 .. code:: python @@ -570,7 +576,65 @@ super 的实现原理,就交由你来自己完成。 >>> std1 # std2 也会改变std1 的属性值 -而正确的做法应该是,所有的实例数据只属于该实例本身(通过实例初始化传入),具体写法可参考上一节。 +从结果上来看,std2 居然共享了 std1 +的属性值,只要其中一个实例的变量发生改变,另一个实例的变量也会跟着改变。 + +探其根因,是由于此时 math,chinese,english 三个全部是类变量,导致 std2 +和 std1 在访问 math,chinese,english 这三个变量时,其实都是访问类变量。 + +问题是不是来了?小明和小强的分数怎么可能是绑定的呢?这很明显与实际业务不符。 + +使用描述符给我们制造了便利,却无形中给我们带来了麻烦,难道这也是描述符的特性吗? + +描述符是个很好用的特性,会出现这个问题,是由于我们之前写的描述符代码都是错误的。 + +描述符的机制,在我看来,只是抢占了访问顺序,而具体的逻辑却要因地制宜,视情况而定。 + +如果要把 math,chinese,english +这三个变量变成实例之间相互隔离的属性,应该这么写。 + +.. code:: python + + class Score: + def __init__(self, subject): + self.name = subject + + def __get__(self, instance, owner): + return instance.__dict__[self.name] + + def __set__(self, instance, value): + if 0 <= value <= 100: + instance.__dict__[self.name] = value + else: + raise ValueError + + + class Student: + math = Score("math") + chinese = Score("chinese") + english = Score("english") + + def __init__(self, math, chinese, english): + self.math = math + self.chinese = chinese + self.english = english + + def __repr__(self): + return "".format(self.math, self.chinese, self.english) + +引导程序逻辑进入描述符之后,不管你是获取属性,还是设置属性,都是直接作用于 +instance 的。 + +|image5| + +这段代码,你可以仔细和前面的对比一下。 + +不难看出: + +- 之前的错误代码,更像是把描述符当做了存储节点。 +- 之后的正确代码,则是把描述符直接当做代理,本身不存储值。 + +以上便是我对描述符的全部分享,希望能对你有所帮助。 参考文档 -------- @@ -579,12 +643,13 @@ super 的实现原理,就交由你来自己完成。 -------------- -|image5| +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20190425221322.png .. |image2| image:: http://image.iswbm.com/20190425221322.png .. |image3| image:: http://image.iswbm.com/20190425221233.png .. |image4| image:: http://image.iswbm.com/20190519001930.png -.. |image5| image:: http://image.iswbm.com/20200607174235.png +.. |image5| image:: http://image.iswbm.com/20200812085823.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_18.rst b/source/c01/c01_18.rst index 2a5b942..add8a60 100644 --- a/source/c01/c01_18.rst +++ b/source/c01/c01_18.rst @@ -3,8 +3,8 @@ |image0| -1.18.1 安装MySQL-python ------------------------ +1. 安装MySQL-python +------------------- MySQL-python 这玩意实在是太难装了,为了以防后面再踩坑,这里还是记录一下吧。 @@ -83,8 +83,8 @@ mysql,这时也请将其卸载再重新安装吧。 终于安装成功,折腾了两个晚上(主要是网速慢)。 -1.18.2 Mac 启动MySQL服务 ------------------------- +2. Mac 启动MySQL服务 +-------------------- 使用 brew 安装 mysql 成功后,又陷入了一个坑。。 @@ -124,8 +124,8 @@ mysql,这时也请将其卸载再重新安装吧。 mysql -uroot -p -1.18.3 Win上忘记密码 --------------------- +3. Win上忘记密码 +---------------- .. code:: shell @@ -151,8 +151,8 @@ mysql,这时也请将其卸载再重新安装吧。 # 再重新登陆,用新的密码登陆,发现可以生效 mysql -uroot -p -1.18.4 命令行使用技巧 ---------------------- +4. 命令行使用技巧 +----------------- |image7| diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index 0194ae4..5776837 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -3,15 +3,19 @@ |image0| -这个标题「\ **静态方法其实暗藏玄机**\ 」其实只是该文章的一个知识点。或许有些标题党,但没有关系,我相信有不少人对此并没有深入研究他们,不信我问你三个问题,你看能否答上来。 +静态方法,应该没人不知道吧? -1、Python2.x和3.x中,函数和方法的区分有什么不同? +它可以算是一个很基础、简单的知识点。 -2、有了类/实例方法和普通函数,为什么还会有静态方法? +但是就是这样一个知识点,也有不少可以探究的东西。 -3、Python3.x 中,静态方法有几种写法? +不信我问你三个问题,你看能否答上来。 -带着这三个问题,你可以尝试在下文中寻找答案。 +1. Python2.x和3.x中,函数和方法的区分有什么不同? +2. 有了类/实例方法和普通函数,为什么还会有静态方法? +3. Python3.x 中,静态方法有几种写法? + +如果你没能答上来,那你可以尝试在下文中寻找答案。 -------------- diff --git a/source/c07/c07_22.rst b/source/c07/c07_22.rst index bcaffe2..0563e93 100644 --- a/source/c07/c07_22.rst +++ b/source/c07/c07_22.rst @@ -165,7 +165,7 @@ rpmdb rm -f /var/lib/rpm/__db.00* # 重建rpm数据文件 - rpm –rebuilddb + rpm -rebuilddb 可选参数 -------- diff --git a/source/c08/c08_01.rst b/source/c08/c08_01.rst index 170c046..b67a69f 100755 --- a/source/c08/c08_01.rst +++ b/source/c08/c08_01.rst @@ -361,17 +361,6 @@ aggregate管理 # 添加 pv 到 vg 中 $ vgextend hdd-volumes /dev/sda -1.3 QEMU命令 -~~~~~~~~~~~~ - -.. code:: shell - - # 查看img镜像信息 - $ qemu-img info - - # 创建qcow2文件 - $ qemu-img create -f qcow2 openstack-name.qcow2 100G - 三、集群相关 ------------ diff --git a/source/c09/c09_03.rst b/source/c09/c09_03.rst index 62ba4f1..5c1f7e6 100644 --- a/source/c09/c09_03.rst +++ b/source/c09/c09_03.rst @@ -60,5 +60,10 @@ JSON:http://www.yyyweb.com/demo/inner-show/json-generator.html 代码分享:https://paste.ubuntu.com/ +黑科技 +------ + +果核剥壳(破解工具):https://www.ghpym.com/special/softlist + .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c10/c10_08.md b/source/c10/c10_08.md index f17ccc4..5b157be 100644 --- a/source/c10/c10_08.md +++ b/source/c10/c10_08.md @@ -346,7 +346,7 @@ LQfiTStBBU6t/+mnDyij+XreGQ== 将这三者结合起来,就是 TLS/SSL 做的事情 -![img](https://img2018.cnblogs.com/blog/1169376/201910/1169376-20191008172456510-1302410435.png) +![](http://image.iswbm.com/1169376-20191008172456510-1302410435.png) diff --git a/source/c10/c10_08.rst b/source/c10/c10_08.rst index 6e902f9..90ecf3c 100644 --- a/source/c10/c10_08.rst +++ b/source/c10/c10_08.rst @@ -395,10 +395,7 @@ OpenSSL 命令,这下你再也不用自己拼凑参数啦,真的太方便了 将这三者结合起来,就是 TLS/SSL 做的事情 -.. figure:: https://img2018.cnblogs.com/blog/1169376/201910/1169376-20191008172456510-1302410435.png - :alt: img - - img +|image6| 1、客户端(浏览器)向服务端发出请求,服务端返回证书给客户端。 @@ -412,7 +409,7 @@ OpenSSL 命令,这下你再也不用自己拼凑参数啦,真的太方便了 - `HTTPS中CA证书的签发及使用过程 `__ -|image6| +|image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/image-20200723233619429.png @@ -420,5 +417,6 @@ OpenSSL 命令,这下你再也不用自己拼凑参数啦,真的太方便了 .. |image3| image:: http://image.iswbm.com/20200724124502.png .. |image4| image:: http://image.iswbm.com/20200717222206.png .. |image5| image:: http://image.iswbm.com/20200718100052.png -.. |image6| image:: http://image.iswbm.com/20200607174235.png +.. |image6| image:: http://image.iswbm.com/1169376-20191008172456510-1302410435.png +.. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_09.md b/source/c10/c10_09.md index e8e55b5..3b17ff7 100644 --- a/source/c10/c10_09.md +++ b/source/c10/c10_09.md @@ -266,6 +266,101 @@ $ systemctl restart httpd ![](http://image.iswbm.com/20200730222928.png) +## 5. 宝塔申请证书 + +注册并登陆宝塔(https://bt.cn),然后进入控制面板。 + +![](http://image.iswbm.com/image-20200929123223096.png) + +点击申请证书 + +![](http://image.iswbm.com/image-20200929123355037.png) + +选择免费的就好 + +![](http://image.iswbm.com/image-20200929123418789.png) + +填写你的域名后,支付订单(其实不要钱)。 + +![](http://image.iswbm.com/image-20200929123459545.png) + +然后登陆我的阿里去域名解析,根据如下提示去添加 DNS解析规则 + +![](http://image.iswbm.com/image-20200929123956070.png) + +然后静待一段时间验证成功了,就可以点击如下按钮进行下载 ![](http://image.iswbm.com/image-20200929194100905.png) + +下载到本地后,你会得到一个 zip 包,解压一下,就可以看到证书文件及私钥。 + +![](http://image.iswbm.com/image-20200929201114297.png) + +因为我的博客使用的是 Nginx,因此我该 Nginx 下的两个文件上传到我的服务器上的 nginx 目录下. + +具体怎么上传呢?你可以使用远程拷贝软件,例如 WinSCP,也可以使用 `lrzsz` (推荐使用)。 + +传到哪个目录下呢? + +先使用 find 命令查找一下你的 nginx.conf 路径 + +```shell +$ find / -name nginx.conf +/usr/local/nginx/conf/nginx.conf +``` + +你的证书文件可以和 nginx.conf 放在同一目录下 + +```shell +/usr/local/nginx/conf +``` + +接下来使用 vim 编辑该文件,找到 server,添加如下行( server 原本的内容 我使用 `...` 表示,意思是不需要去动。 ) + +```shell +server + { + listen 443 ssl; + # 注释掉该行 + # listen 80 default_server reuseport; + + #证书文件名称 + ssl_certificate 1_iswbm.com_bundle.pem; + #私钥文件名称 + ssl_certificate_key 0_iswbm.com.key; + + ... + } +``` + +尝试着登陆一下 + +## 6. 七牛去图床开启https + +登陆七牛云 + +然后进入 [证书管理](https://portal.qiniu.com/certificate/ssl ) 上传证书。 + +![](http://image.iswbm.com/image-20200929210645477.png) + +进入对象存储 -> 域名管理,找到 HTTPS 配置的位置,点击 `修改配置`。 + +![](http://image.iswbm.com/image-20200929205400898.png) + + + +将按钮置为开启状态,选择我们刚刚上传的证书。 + +![](http://image.iswbm.com/image-20200929205611442.png) + +设置完后,并不能立马使用 + +![](http://image.iswbm.com/image-20200929210431735.png) + +域名的升级需要一定时间,等待即可。 + +![](http://image.iswbm.com/image-20200929210900562.png) + + + ## 参考文档 - [Apache 服务器证书安装](https://cloud.tencent.com/document/product/400/35243) diff --git a/source/c10/c10_09.rst b/source/c10/c10_09.rst index e8c0715..161d0b7 100644 --- a/source/c10/c10_09.rst +++ b/source/c10/c10_09.rst @@ -287,6 +287,103 @@ IP 安全选择选为 ``始终信任``\ 。 |image22| +5. 宝塔申请证书 +--------------- + +注册并登陆宝塔(https://bt.cn),然后进入控制面板。 + +|image23| + +点击申请证书 + +|image24| + +选择免费的就好 + +|image25| + +填写你的域名后,支付订单(其实不要钱)。 + +|image26| + +然后登陆我的阿里去域名解析,根据如下提示去添加 DNS解析规则 + +|image27| + +然后静待一段时间验证成功了,就可以点击如下按钮进行下载 |image28| + +下载到本地后,你会得到一个 zip 包,解压一下,就可以看到证书文件及私钥。 + +|image29| + +因为我的博客使用的是 Nginx,因此我该 Nginx +下的两个文件上传到我的服务器上的 nginx 目录下. + +具体怎么上传呢?你可以使用远程拷贝软件,例如 WinSCP,也可以使用 +``lrzsz`` (推荐使用)。 + +传到哪个目录下呢? + +先使用 find 命令查找一下你的 nginx.conf 路径 + +.. code:: shell + + $ find / -name nginx.conf + /usr/local/nginx/conf/nginx.conf + +你的证书文件可以和 nginx.conf 放在同一目录下 + +.. code:: shell + + /usr/local/nginx/conf + +接下来使用 vim 编辑该文件,找到 server,添加如下行( server 原本的内容 +我使用 ``...`` 表示,意思是不需要去动。 ) + +.. code:: shell + + server + { + listen 443 ssl; + # 注释掉该行 + # listen 80 default_server reuseport; + + #证书文件名称 + ssl_certificate 1_iswbm.com_bundle.pem; + #私钥文件名称 + ssl_certificate_key 0_iswbm.com.key; + + ... + } + +尝试着登陆一下 + +6. 七牛去图床开启https +---------------------- + +登陆七牛云 + +然后进入 `证书管理 `__ +上传证书。 + +|image30| + +进入对象存储 -> 域名管理,找到 HTTPS 配置的位置,点击 ``修改配置``\ 。 + +|image31| + +将按钮置为开启状态,选择我们刚刚上传的证书。 + +|image32| + +设置完后,并不能立马使用 + +|image33| + +域名的升级需要一定时间,等待即可。 + +|image34| + 参考文档 -------- @@ -295,7 +392,7 @@ IP 安全选择选为 ``始终信任``\ 。 - `自签名 SSL 证书 `__ -|image23| +|image35| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200728233602.png @@ -320,5 +417,17 @@ IP 安全选择选为 ``始终信任``\ 。 .. |image20| image:: http://image.iswbm.com/20200730222700.png .. |image21| image:: http://image.iswbm.com/20200730222827.png .. |image22| image:: http://image.iswbm.com/20200730222928.png -.. |image23| image:: http://image.iswbm.com/20200607174235.png +.. |image23| image:: http://image.iswbm.com/image-20200929123223096.png +.. |image24| image:: http://image.iswbm.com/image-20200929123355037.png +.. |image25| image:: http://image.iswbm.com/image-20200929123418789.png +.. |image26| image:: http://image.iswbm.com/image-20200929123459545.png +.. |image27| image:: http://image.iswbm.com/image-20200929123956070.png +.. |image28| image:: http://image.iswbm.com/image-20200929194100905.png +.. |image29| image:: http://image.iswbm.com/image-20200929201114297.png +.. |image30| image:: http://image.iswbm.com/image-20200929210645477.png +.. |image31| image:: http://image.iswbm.com/image-20200929205400898.png +.. |image32| image:: http://image.iswbm.com/image-20200929205611442.png +.. |image33| image:: http://image.iswbm.com/image-20200929210431735.png +.. |image34| image:: http://image.iswbm.com/image-20200929210900562.png +.. |image35| image:: http://image.iswbm.com/20200607174235.png From a43a024cb629ab1c3d155c56abea9d7813d191f3 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 4 Oct 2020 15:34:20 +0800 Subject: [PATCH 126/147] =?UTF-8?q?=E6=95=B4=E7=90=86=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/c01/{c01_38.md => c01_03.md} | 22 +- source/c01/c01_03.rst | 83 +- source/c01/c01_08.md | 18 +- source/c01/c01_08.rst | 24 +- source/c01/c01_10.md | 2193 ------------------------- source/c01/c01_13.md | 394 ++++- source/c01/c01_13.rst | 432 +++-- source/c01/c01_17.md | 645 ++------ source/c01/c01_17.rst | 744 +++------ source/c01/{c01_33.md => c01_19.md} | 2 +- source/c01/{c01_33.rst => c01_19.rst} | 2 +- source/c01/c01_20.md | 113 +- source/c01/c01_20.rst | 124 +- source/c01/c01_21.md | 51 +- source/c01/c01_21.rst | 67 +- source/c01/c01_23.md | 266 +-- source/c01/c01_23.rst | 202 ++- source/c01/c01_24.md | 743 +-------- source/c01/c01_24.rst | 830 ++-------- source/c01/c01_25.md | 297 +++- source/c01/c01_25.rst | 333 +++- source/c01/c01_26.md | 323 ++-- source/c01/c01_26.rst | 375 ++--- source/c01/c01_27.md | 662 +------- source/c01/c01_27.rst | 745 ++------- source/c01/{c01_41.md => c01_28.md} | 0 source/c01/{c01_41.rst => c01_28.rst} | 0 source/c01/c01_29.md | 168 +- source/c01/c01_29.rst | 179 +- source/c01/c01_31.md | 25 - source/c01/c01_31.rst | 26 - source/c01/c01_32.md | 39 - source/c01/c01_32.rst | 40 - source/c01/c01_34.md | 92 -- source/c01/c01_34.rst | 98 -- source/c01/c01_35.md | 384 ----- source/c01/c01_35.rst | 405 ----- source/c01/c01_36.md | 253 --- source/c01/c01_36.rst | 286 ---- source/c01/c01_37.md | 161 -- source/c01/c01_37.rst | 182 -- source/c01/c01_38.rst | 76 - source/c01/c01_39.md | 220 --- source/c01/c01_39.rst | 231 --- source/c01/c01_40.md | 143 -- source/c01/c01_40.rst | 154 -- source/c01/c01_42.md | 175 -- source/c01/c01_42.rst | 183 --- source/c01/c01_43.md | 353 ---- source/c01/c01_43.rst | 391 ----- source/c01/c01_44.md | 274 --- source/c01/c01_44.rst | 295 ---- source/c01/c01_45.md | 290 ---- source/c01/c01_45.rst | 309 ---- source/c01/c01_47.md | 185 --- source/c01/c01_47.rst | 187 --- source/c03/c03_08.md | 597 +++++++ source/c03/c03_08.rst | 655 ++++++++ source/c03/c03_09.md | 725 ++++++++ source/c03/c03_09.rst | 803 +++++++++ source/c03/c03_10.md | 148 ++ source/c03/c03_10.rst | 169 ++ source/c03/c03_11.md | 198 +++ source/c03/c03_11.rst | 217 +++ source/c03/c03_12.md | 106 ++ source/c03/c03_12.rst | 122 ++ source/c03/c03_13.md | 138 ++ source/c03/c03_13.rst | 140 ++ source/c03/c03_14.md | 651 ++++++++ source/c03/c03_14.rst | 703 ++++++++ source/c04/c04_02.md | 209 +-- source/c04/c04_02.rst | 229 +-- source/c04/c04_14.md | 209 ++- source/c04/c04_14.rst | 229 ++- source/c04/c04_18.md | 387 ++--- source/c04/c04_18.rst | 455 ++--- source/c04/c04_20.md | 364 +++- source/c04/c04_20.rst | 390 ++++- source/c04/c04_21.md | 378 ----- source/c04/c04_22.md | 8 - source/c04/c04_22.rst | 10 - source/c04/c04_23.md | 56 - source/c04/c04_23.rst | 64 - source/c04/c04_24.md | 171 -- source/c04/c04_24.rst | 197 --- source/c04/c04_25.md | 17 - source/c04/c04_25.rst | 21 - source/c09/c09_06.md | 8 +- source/c09/c09_06.rst | 51 +- source/c09/c09_07.md | 19 + source/c09/c09_07.rst | 26 + source/c10/c10_01.md | 405 +++-- source/c10/c10_01.rst | 540 +++--- source/c10/c10_02.md | 234 ++- source/c10/c10_02.rst | 302 +++- source/c10/c10_03.md | 611 ++----- source/c10/c10_03.rst | 741 ++------- source/c10/c10_04.md | 789 +-------- source/c10/c10_04.rst | 950 +---------- source/c10/c10_05.md | 36 - source/c10/c10_05.rst | 38 - source/c10/c10_06.md | 41 - source/c10/c10_06.rst | 44 - source/c10/c10_07.md | 478 ------ source/c10/c10_07.rst | 622 ------- source/c10/c10_08.md | 365 ---- source/c10/c10_08.rst | 422 ----- source/c10/c10_09.md | 371 ----- source/c10/c10_09.rst | 433 ----- source/c10/c10_12.md | 38 - source/c10/c10_12.rst | 48 - source/chapters/p10.rst | 8 +- 112 files changed, 11032 insertions(+), 20848 deletions(-) rename source/c01/{c01_38.md => c01_03.md} (68%) delete mode 100644 source/c01/c01_10.md rename source/c01/{c01_33.md => c01_19.md} (96%) rename source/c01/{c01_33.rst => c01_19.rst} (96%) rename source/c01/{c01_41.md => c01_28.md} (100%) rename source/c01/{c01_41.rst => c01_28.rst} (100%) delete mode 100644 source/c01/c01_31.md delete mode 100644 source/c01/c01_31.rst delete mode 100644 source/c01/c01_32.md delete mode 100644 source/c01/c01_32.rst delete mode 100644 source/c01/c01_34.md delete mode 100644 source/c01/c01_34.rst delete mode 100644 source/c01/c01_35.md delete mode 100644 source/c01/c01_35.rst delete mode 100644 source/c01/c01_36.md delete mode 100644 source/c01/c01_36.rst delete mode 100644 source/c01/c01_37.md delete mode 100644 source/c01/c01_37.rst delete mode 100644 source/c01/c01_38.rst delete mode 100644 source/c01/c01_39.md delete mode 100644 source/c01/c01_39.rst delete mode 100644 source/c01/c01_40.md delete mode 100644 source/c01/c01_40.rst delete mode 100644 source/c01/c01_42.md delete mode 100644 source/c01/c01_42.rst delete mode 100644 source/c01/c01_43.md delete mode 100644 source/c01/c01_43.rst delete mode 100644 source/c01/c01_44.md delete mode 100644 source/c01/c01_44.rst delete mode 100644 source/c01/c01_45.md delete mode 100644 source/c01/c01_45.rst delete mode 100644 source/c01/c01_47.md delete mode 100644 source/c01/c01_47.rst create mode 100644 source/c03/c03_08.md create mode 100644 source/c03/c03_08.rst create mode 100644 source/c03/c03_09.md create mode 100644 source/c03/c03_09.rst create mode 100644 source/c03/c03_10.md create mode 100644 source/c03/c03_10.rst create mode 100644 source/c03/c03_11.md create mode 100644 source/c03/c03_11.rst create mode 100644 source/c03/c03_12.md create mode 100644 source/c03/c03_12.rst create mode 100644 source/c03/c03_13.md create mode 100644 source/c03/c03_13.rst create mode 100644 source/c03/c03_14.md create mode 100644 source/c03/c03_14.rst delete mode 100644 source/c04/c04_21.md delete mode 100644 source/c04/c04_23.md delete mode 100644 source/c04/c04_23.rst delete mode 100644 source/c04/c04_24.md delete mode 100644 source/c04/c04_24.rst delete mode 100644 source/c04/c04_25.md delete mode 100644 source/c04/c04_25.rst create mode 100644 source/c09/c09_07.md create mode 100644 source/c09/c09_07.rst delete mode 100644 source/c10/c10_05.md delete mode 100644 source/c10/c10_05.rst delete mode 100644 source/c10/c10_06.md delete mode 100644 source/c10/c10_06.rst delete mode 100644 source/c10/c10_07.md delete mode 100644 source/c10/c10_07.rst delete mode 100644 source/c10/c10_08.md delete mode 100644 source/c10/c10_08.rst delete mode 100644 source/c10/c10_09.md delete mode 100644 source/c10/c10_09.rst delete mode 100644 source/c10/c10_12.md delete mode 100644 source/c10/c10_12.rst diff --git a/source/c01/c01_38.md b/source/c01/c01_03.md similarity index 68% rename from source/c01/c01_38.md rename to source/c01/c01_03.md index 39af2dd..726d0e2 100644 --- a/source/c01/c01_38.md +++ b/source/c01/c01_03.md @@ -1,32 +1,36 @@ -# 1.38 /usr/bin/env python 有什么用? +# 1.3 /usr/bin/env python 有什么用? ![](http://image.iswbm.com/20200602135014.png) 我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 -```json +``` #!/usr/bin/python ``` 或者这样 -```json +``` #!/usr/bin/env python ``` -这两者有什么区别呢? +那么他们有什么用呢? +要理解它,得把这一行语句拆成两部分。 +第一部分是 `#!` -稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python` +第二部分是 `/usr/bin/python` 或者 `/usr/bin/env python` -![](http://image.iswbm.com/20200331184021.png) +关于 `#!` 这个符号,其实它是有名字的,叫做 `Shebang` 或者`Sha-bang` ,有的翻译组将它译作 `释伴`,即“解释伴随行”的简称,同时又是Shebang的音译。 -而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。 +Shebang通常出现在类Unix系统的脚本中第一行,作为前两个字符。在Shebang之后,可以有一个或数个空白字符,后接解释器的绝对路径,用于指明执行这个脚本文件的解释器。 + +![](http://image.iswbm.com/20200331184021.png) -那么加和不加有什么区别呢? +**那么加和不加有什么区别呢?** -不加的话,你每次执行这个脚本时,都得这样: `python xx.py` , +如果不加 `#!` 的话,你每次执行这个脚本时,都得这样 `python xx.py` , ![](http://image.iswbm.com/20200331185034.png) diff --git a/source/c01/c01_03.rst b/source/c01/c01_03.rst index 513c244..66dfc5f 100755 --- a/source/c01/c01_03.rst +++ b/source/c01/c01_03.rst @@ -1,3 +1,82 @@ -1.3 谈谈深贝和浅拷贝的区别 -================================ +1.3 /usr/bin/env python 有什么用? +================================== + +|image0| + +我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 + +:: + + #!/usr/bin/python + +或者这样 + +:: + + #!/usr/bin/env python + +那么他们有什么用呢? + +要理解它,得把这一行语句拆成两部分。 + +第一部分是 ``#!`` + +第二部分是 ``/usr/bin/python`` 或者 ``/usr/bin/env python`` + +关于 ``#!`` 这个符号,其实它是有名字的,叫做 ``Shebang`` +或者\ ``Sha-bang`` ,有的翻译组将它译作 +``释伴``\ ,即“解释伴随行”的简称,同时又是Shebang的音译。 + +Shebang通常出现在类Unix系统的脚本中第一行,作为前两个字符。在Shebang之后,可以有一个或数个空白字符,后接解释器的绝对路径,用于指明执行这个脚本文件的解释器。 + +|image1| + +**那么加和不加有什么区别呢?** + +如果不加 ``#!`` 的话,你每次执行这个脚本时,都得这样 ``python xx.py`` , + +|image2| + +有没有一种方式?可以省去每次都加 ``python`` 呢? + +当然有,你可以文件头里加上\ ``#!/usr/bin/python`` +,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 + +|image3| + +明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? + +当我执行 ``env python`` 时,自动进入了 python console 的模式。 + +|image4| + +这是为什么?和 直接执行 python 好像没什么区别呀 + +当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是 +/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin +)这几个路径里去依次查找名为python的可执行文件。 + +找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python`` +里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin`` +下创建一个名字也为 python 的可执行文件时,就会执行 ``/usr/bin/python`` +了。 + +具体演示过程,你可以看下面。 + +|image5| + +那么对于这两者,我们应该使用哪个呢? + +个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 +python 解释器都是 ``/usr/bin/python`` 。 + +|image6| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200331184021.png +.. |image2| image:: http://image.iswbm.com/20200331185034.png +.. |image3| image:: http://image.iswbm.com/20200331184755.png +.. |image4| image:: http://image.iswbm.com/20200331185741.png +.. |image5| image:: http://image.iswbm.com/20200331190224.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_08.md b/source/c01/c01_08.md index e651615..1c1730f 100644 --- a/source/c01/c01_08.md +++ b/source/c01/c01_08.md @@ -4,7 +4,7 @@ --- -## 2.8.1 版本支持 / 写法差异 +## 1. 版本支持 / 写法差异 在Python 2.x 中 @@ -38,12 +38,12 @@ class Ming(object): pass ``` -## 2.8.2 使用方法 / 独特属性 +## 2. 使用方法 / 独特属性 经典类无法使用super() -![](https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg) +![](http://image.iswbm.com/20201004123025.png) 经典类的类型是 classobj -![](https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg) +![](http://image.iswbm.com/20201004123036.png) 新式类的类型是 type,保持class与type的统一。 @@ -80,7 +80,7 @@ AttributeError: Kls01 instance has no attribute 'name' -## 2.8.3 MRO 查找算法的演变 +## 3. MRO 查找算法的演变 **经典类中** @@ -110,7 +110,7 @@ print inspect.getmro(D) 非常好理解,但是在菱形继承时,方法的调用会出现问题。 -![](https://ws1.sinaimg.cn/large/8f640247gy1fyi77urc3lj206108n74e.jpg) +![](http://image.iswbm.com/20201004123106.png) 假设 d 是 D 的一个实例,那么执行 d.show()是调用 A.show() 呢 还是调用 C.show()呢? @@ -122,7 +122,7 @@ print inspect.getmro(D) Python 2.2 的新式类 MRO 计算方式和经典类 MRO 的计算方式非常相似:它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。重新考虑上面「菱形继承」的例子: -![](https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg) +![](http://image.iswbm.com/20201004123056.png) 同样地,我们也来验证一下。另说明,在新式类中,除用inspect外,可以直接通过__mro__属性获取类的 MRO。 @@ -152,7 +152,7 @@ print inspect.getmro(D) 再来看一个复杂一点的例子。 -![](https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg) +![](http://image.iswbm.com/20201004123115.png) 如果只依靠上面的算法,我们来一起算下,其继承关系是怎样的。 @@ -194,7 +194,7 @@ order (MRO) for bases X, Y 例如下面这张图。 -![](https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg) +![](http://image.iswbm.com/20201004123126.png) 计算过程,会采用一种 merge算法。它的基本思想如下: diff --git a/source/c01/c01_08.rst b/source/c01/c01_08.rst index 14ff6f1..33aaf4c 100755 --- a/source/c01/c01_08.rst +++ b/source/c01/c01_08.rst @@ -5,8 +5,8 @@ -------------- -2.8.1 版本支持 / 写法差异 -------------------------- +1. 版本支持 / 写法差异 +---------------------- 在Python 2.x 中 @@ -45,8 +45,8 @@ class Ming(object): pass -2.8.2 使用方法 / 独特属性 -------------------------- +2. 使用方法 / 独特属性 +---------------------- 经典类无法使用super() |image1| 经典类的类型是 classobj |image2| @@ -85,8 +85,8 @@ kls01.name AttributeError: Kls01 instance has no attribute 'name' -2.8.3 MRO 查找算法的演变 ------------------------- +3. MRO 查找算法的演变 +--------------------- **经典类中** @@ -274,11 +274,11 @@ C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 |image7| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi765tblqj20cy05cwfx.jpg -.. |image2| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi76mgwcbj20b708cmyo.jpg -.. |image3| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi77urc3lj206108n74e.jpg -.. |image4| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78drp24j20680bjaaa.jpg -.. |image5| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78odu23j20740bomxh.jpg -.. |image6| image:: https://ws1.sinaimg.cn/large/8f640247gy1fyi78xuzibj20940ayq39.jpg +.. |image1| image:: http://image.iswbm.com/20201004123025.png +.. |image2| image:: http://image.iswbm.com/20201004123036.png +.. |image3| image:: http://image.iswbm.com/20201004123106.png +.. |image4| image:: http://image.iswbm.com/20201004123056.png +.. |image5| image:: http://image.iswbm.com/20201004123115.png +.. |image6| image:: http://image.iswbm.com/20201004123126.png .. |image7| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md deleted file mode 100644 index efd4ac7..0000000 --- a/source/c01/c01_10.md +++ /dev/null @@ -1,2193 +0,0 @@ -# 1.10 Python 黑魔法指南 50 例 - -![](http://image.iswbm.com/20200602135014.png) - ---- - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - - -## 01. 默默无闻的省略号很好用 - -在Python中,一切皆对象,省略号也不例外。 - -在 Python 3 中你可以直接写 `...` 来得到它 -```python ->>> ... -Ellipsis ->>> type(...) - -``` - -而在 Python 2 中没有`...` 这个语法,只能直接写Ellipsis来获取。 -```python ->>> Ellipsis -Ellipsis ->>> type(Ellipsis) - ->>> -``` - -它转为布尔值时为真 -```python ->>> bool(...) -True -``` -最后,这东西是一个单例。 -```python ->>> id(...) -4362672336 ->>> id(...) -4362672336 -``` - -那这东西有啥用呢? - -1. 它是 Numpy 的一个语法糖 -2. 在 Python 3 中可以使用 ... 代替 pass - -```shell -$ cat demo.py -def func01(): - ... - -def func02(): - pass - -func01() -func02() - -print("ok") - -$ python3 demo.py -ok -``` - - - -## 02. 使用 end 来结束代码块 - -有不少编程语言,循环、判断代码块需要用 end 标明结束,这样一定程度上会使代码逻辑更加清晰一点。 - -但是其实在 Python 这种严格缩进的语言里并没有必要这样做。 - -如果你真的想用,也不是没有办法,具体你看下面这个例子。 - -```python -__builtins__.end = None - - -def my_abs(x): - if x > 0: - return x - else: - return -x - end -end - -print(my_abs(10)) -print(my_abs(-10)) -``` - -执行后,输出如下 - -```shell -[root@localhost ~]$ python demo.py -10 -10 -``` - -## 03. 可直接运行的 zip 包 - -我们可以经常看到有 Python 包,居然可以以 zip 包进行发布,并且可以不用解压直接使用。 - -这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 是 egg,要嘛是whl 格式。 - -那么这个zip 是如何制作的呢,请看下面的示例。 - -```shell -[root@localhost ~]# ls -l demo -total 8 --rw-r--r-- 1 root root 30 May 8 19:27 calc.py --rw-r--r-- 1 root root 35 May 8 19:33 __main__.py -[root@localhost ~]# -[root@localhost ~]# cat demo/__main__.py -import calc - -print(calc.add(2, 3)) -[root@localhost ~]# -[root@localhost ~]# cat demo/calc.py -def add(x, y): - return x+y -[root@localhost ~]# -[root@localhost ~]# python -m zipfile -c demo.zip demo/* -[root@localhost ~]# -``` - -制作完成后,我们可以执行用 python 去执行它 - -```shell -[root@localhost ~]# python demo.zip -5 -[root@localhost ~]# -``` - -## 04. 反斜杠的倔强: 不写最后 - -`\` 在 Python 中的用法主要有两种 - -**1、在行尾时,用做续行符** - - ```python -[root@localhost ~]$ cat demo.py -print("hello "\ - "world") -[root@localhost ~]$ -[root@localhost ~]$ python demo.py -hello world - ``` - - - -**2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。** - -```python ->>> str1='\nhello'  #换行 ->>> print(str1) - -hello ->>> str2='\thello'  #tab ->>> print(str2) - hello -``` - -但是如果你用单`\`结尾是会报语法错误的 - -```python ->>> str3="\" - File "", line 1 - str3="\" - ^ -SyntaxError: EOL while scanning string literal -``` - -就算你指定它是个 raw 字符串,也不行。 - -```python ->>> str3=r"\" - File "", line 1 - str3=r"\" - ^ -SyntaxError: EOL while scanning string literal -``` - -## 05. 单行实现 for 死循环如何写? - -如果让你在不借助 while ,只使用 for 来写一个死循环? - -**你会写吗?** - -**如果你还说简单,你可以自己试一下。** - -... - -如果你尝试后,仍然写不出来,那我给出自己的做法。 - -```python -for i in iter(int, 1):pass -``` - - - -**是不是傻了?iter 还有这种用法?这为啥是个死循环?** - -关于这个问题,你如果看中文网站,可能找不到相关资料。 - -还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 - -原来iter有两种使用方法。 - -- 通常我们的认知是第一种,将一个列表转化为一个迭代器。 - -- 而第二种方法,他接收一个 callable对象,和一个sentinel 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 - -那`int` 呢? - -这又是一个知识点,int 是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console 模式下输入 `int()` 看看是不是返回0。 - -由于int() 永远返回0,永远返回不了1,所以这个 for 循环会没有终点。一直运行下去。 - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 06. 懒人必备技能:使用 “_” - -对于 `_` ,大家对于他的印象都是用于 **占位符**,省得为一个不需要用到的变量,绞尽脑汁的想变量名。 - -今天要介绍的是他的第二种用法,就是在 console 模式下的应用。 - -示例如下: - -```python ->>> 3 + 4 -7 ->>> _ -7 ->>> name='公众号: Python编程时光' ->>> name -'公众号: Python编程时光' ->>> _ -'公众号: Python编程时光' -``` - -它可以返回上一次的运行结果。 - -但是,如果是print函数打印出来的就不行了。 - -```python ->>> 3 + 4 -7 ->>> _ -7 ->>> print("公众号: Python编程时光") -ming ->>> _ -7 -``` - -我自己写了个例子,验证了下,用`__repr__`输出的内容可以被获取到的。 -首先,在我们的目录下,写一个文件 demo.py。内容如下 - -```python -# demo.py -class mytest(): - def __str__(self): - return "hello" - - def __repr__(self): - return "world" -``` - -然后在这个目录下进入交互式环境。 - -```python ->>> import demo ->>> mt=demo.mytest() ->>> mt -world ->>> print(mt) -hello ->>> _ -world -``` - -知道这两个魔法方法的人,一看就明白了,这里不再解释啦。 - -## 07. 最快查看包搜索路径的方式 - -当你使用 import 导入一个包或模块时,Python 会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path 查看。 - -```python ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.path) -['', - '/usr/local/Python3.7/lib/python37.zip', - '/usr/local/Python3.7/lib/python3.7', - '/usr/local/Python3.7/lib/python3.7/lib-dynload', - '/home/wangbm/.local/lib/python3.7/site-packages', - '/usr/local/Python3.7/lib/python3.7/site-packages'] ->>> -``` - -那有没有更快的方式呢? - -我这有一种连 console 模式都不用进入的方法,一行命令即可解决 - -```shell -[wangbm@localhost ~]$ python3 -m site -sys.path = [ - '/home/wangbm', - '/usr/local/Python3.7/lib/python37.zip', - '/usr/local/Python3.7/lib/python3.7', - '/usr/local/Python3.7/lib/python3.7/lib-dynload', - '/home/wangbm/.local/lib/python3.7/site-packages', - '/usr/local/Python3.7/lib/python3.7/site-packages', -] -USER_BASE: '/home/wangbm/.local' (exists) -USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) -ENABLE_USER_SITE: True -``` - -从输出你可以发现,这个列的路径会比 sys.path 更全,它包含了用户环境的目录。 - -## 08. and 和 or 的取值顺序 - -and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它有妙用。 - -- 当一个 **or 表达式**中所有值都为真,Python会选择第一个值 - -- 当一个 **and 表达式** 所有值都为真,Python 会选择第二个值。 - -示例如下: - -```python ->>>(2 or 3) * (5 and 7) -14 # 2*7 -``` - -## 09. 如何修改解释器提示符 - -这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。 - -正常情况下,我们在 终端下 执行Python 命令是这样的。 -```python ->>> for i in range(2): -... print (i) -... -0 -1 -``` - -你是否想过 `>>>` 和 `...` 这两个提示符也是可以修改的呢? -```python ->>> import sys ->>> sys.ps1 -'>>> ' ->>> sys.ps2 -'... ' ->>> ->>> sys.ps2 = '---------------- ' ->>> sys.ps1 = 'Python编程时光>>>' -Python编程时光>>>for i in range(2): ----------------- print (i) ----------------- -0 -1 -``` - -## 10. 逗号也有它独特的用法 - -逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 - -**第一个用法** - -元组的转化 - -```shell -[root@localhost ~]# cat demo.py -def func(): - return "ok", - -print(func()) -[root@localhost ~]# python3 demo.py -('ok',) -``` - -**第二个用法** - -print 的取消换行 - -```shell -[root@localhost ~]# cat demo.py -for i in range(3): - print i -[root@localhost ~]# -[root@localhost ~]# python demo.py -0 -1 -2 -[root@localhost ~]# -[root@localhost ~]# vim demo.py -[root@localhost ~]# -[root@localhost ~]# cat demo.py -for i in range(3): - print i, -[root@localhost ~]# -[root@localhost ~]# python demo.py -0 1 2 -[root@localhost ~]# -``` - - - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 11. 默认参数最好不为可变对象 - -函数的参数分三种 -- 可变参数 -- 默认参数 -- 关键字参数 - -当你在传递默认参数时,有新手很容易踩雷的一个坑。 - -先来看一个示例 -```python -def func(item, item_list=[]): - item_list.append(item) - print(item_list) - -func('iphone') -func('xiaomi', item_list=['oppo','vivo']) -func('huawei') -``` -在这里,你可以暂停一下,思考一下会输出什么? - -思考过后,你的答案是否和下面的一致呢 -``` -['iphone'] -['oppo', 'vivo', 'xiaomi'] -['iphone', 'huawei'] -``` - -如果是,那你可以跳过这部分内容,如果不是,请接着往下看,这里来分析一下。 - -Python 中的 def 语句在每次执行的时候都初始化一个函数对象,这个函数对象就是我们要调用的函数,可以把它当成一个一般的对象,只不过这个对象拥有一个可执行的方法和部分属性。 - -对于参数中提供了初始值的参数,由于 Python 中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def 的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list 会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。 - -![](http://image.iswbm.com/20190511165650.png) - -## 12. 访问类中的私有方法 - -大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。 - -这里先看一下例子 -```python -class Kls(): - def public(self): - print('Hello public world!') - - def __private(self): - print('Hello private world!') - - def call_private(self): - self.__private() - -ins = Kls() - -# 调用公有方法,没问题 -ins.public() - -# 直接调用私有方法,不行 -ins.__private() - -# 但你可以通过内部公有方法,进行代理 -ins.call_private() -``` - -既然都是方法,那我们真的没有方法可以直接调用吗? - -当然有啦,只是建议你千万不要这样弄,这里只是普及,让你了解一下。 -```python -# 调用私有方法,以下两种等价 -ins._Kls__private() -ins.call_private() -``` - -## 13. 时有时无的切片异常 - -这是个简单例子,alist 只有5 个元素,当你取第 6 个元素时,会抛出索引异常。这与我们的认知一致。 -```python ->>> alist = [0, 1, 2, 3, 4] ->>> alist[5] -Traceback (most recent call last): - File "", line 1, in -IndexError: list index out of range -``` -但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 6个元素,也不抛出异常,而是会返回一个新的列表。 -```python ->>> alist = [0, 1, 2, 3, 4] ->>> alist[5:] -[] ->>> alist[100:] -[] -``` - -## 14. 哪些情况下不需要续行符? - -在写代码时,为了代码的可读性,代码的排版是尤为重要的。 - -为了实现高可读性的代码,我们常常使用到的就是续行符 `\`。 -``` ->>> a = 'talk is cheap,'\ -... 'show me the code.' ->>> ->>> print(a) -talk is cheap,show me the code. -``` - -那有哪些情况下,是不需要写续行符的呢? - -经过总结,在这些符号中间的代码换行可以省略掉续行符:`[]`,`()`,`{}` - -``` ->>> my_list=[1,2,3, -... 4,5,6] - ->>> my_tuple=(1,2,3, -... 4,5,6) - ->>> my_dict={"name": "MING", -... "gender": "male"} -``` -另外还有,在多行文本注释中 `'''` ,续行符也是可以不写的。 -``` ->>> text = '''talk is cheap, -... show me the code''' -``` - -## 15. Python2下 也能使用 print(“”) - -可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 只能使用`print ""`。 - -但是其实并不是这样的。 - -在Python 2.6之前,只支持 -```python -print "hello" -``` - -在Python 2.6和2.7中,可以支持如下三种 -```python -print "hello" -print("hello") -print ("hello") -``` - -在Python3.x中,可以支持如下两种 -```python -print("hello") -print ("hello") -``` - -虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print ,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 python2.6+下使用 print() 后,就成了函数。 - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 16. 迷一样的字符串 - -示例一 - -```python -# Python2.7 ->>> a = "Hello_Python" ->>> id(a) -32045616 ->>> id("Hello" + "_" + "Python") -32045616 - -# Python3.7 ->>> a = "Hello_Python" ->>> id(a) -38764272 ->>> id("Hello" + "_" + "Python") -32045616 -``` - -示例二 - -```python ->>> a = "MING" ->>> b = "MING" ->>> a is b -True - -# Python2.7 ->>> a, b = "MING!", "MING!" ->>> a is b -True - -# Python3.7 ->>> a, b = "MING!", "MING!" ->>> a is b -False -``` - -示例三 - -```python -# Python2.7 ->>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' -True ->>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' -False - -# Python3.7 ->>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' -True ->>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' -True -``` - - - -## 17. return不一定都是函数的终点 - -众所周知,try…finally… 的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。 - -同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。 - -那问题就来了,以上这两种规则,如果同时存在,Python 解释器会如何选择?哪个优先级更高? - -写个示例验证一下,就明白啦 - -```python ->>> def func(): -... try: -... return 'try' -... finally: -... return 'finally' -... ->>> func() -'finally' -``` - -从输出中,我们可以发现:在try…finally…语句中,try中的 return 会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally 能够执行。 - -**如果 try 里的 return 真的是直接被忽视吗?** - -我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的 return 真的是直接被忽视,那当finally 下没有显式的 return 的时候,是不是会返回None呢? - -还是写个 示例来验证一下: - -```python ->>> def func(): -... try: -... return 'try' -... finally: -... print('finally') -... ->>> ->>> func() -finally -'try' ->>> -``` - -从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return 仍然还是有效的。 - -那结论就出来了,如果 finally 里有显式的 return,那么这个 return 会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 try 里的 return 仍然有效。 - -## 18. 用户无感知的小整数池 - -为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。 - -以上代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE 的影响,效果会有所不同。 - -```python ->>> a = -6 ->>> b = -6 ->>> a is b -False - ->>> a = 256 ->>> b = 256 ->>> a is b -True - ->>> a = 257 ->>> b = 257 ->>> a is b -False - ->>> a = 257; b = 257 ->>> a is b -True -``` - -**问题又来了:最后一个示例,为啥是True?** - -因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两成的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。 - -## 19. 神奇的 intern 机制 - -字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化. - -例如:Python解释器中使用了 intern(字符串驻留)的技术来提高字符串效率,什么是intern机制?就是同样的字符串对象仅仅会保存一份,放在一个字符串储蓄池中,是共用的,当然,肯定不能改变,这也决定了字符串必须是不可变对象。 - -``` ->>> s1="hello" ->>> s2="hello" ->>> s1 is s2 -True - -# 如果有空格,默认不启用intern机制 ->>> s1="hell o" ->>> s2="hell o" ->>> s1 is s2 -False - -# 如果一个字符串长度超过20个字符,不启动intern机制 ->>> s1 = "a" * 20 ->>> s2 = "a" * 20 ->>> s1 is s2 -True - ->>> s1 = "a" * 21 ->>> s2 = "a" * 21 ->>> s1 is s2 -False - ->>> s1 = "ab" * 10 ->>> s2 = "ab" * 10 ->>> s1 is s2 -True - ->>> s1 = "ab" * 11 ->>> s2 = "ab" * 11 ->>> s1 is s2 -False -``` - - - -## 20. 反转字符串/列表最优雅的方式 - -反转序列并不难,但是如何做到最优雅呢? - -先来看看,正常是如何反转的。 - -最简单的方法是使用列表自带的reverse()方法。 - -```python ->>> ml = [1,2,3,4,5] ->>> ml.reverse() ->>> ml -[5, 4, 3, 2, 1] -``` - -但如果你要处理的是字符串,reverse就无能为力了。你可以尝试将其转化成list,再reverse,然后再转化成str。转来转去,也太麻烦了吧?需要这么多行代码(后面三行是不能合并成一行的),一点都Pythonic。 -```python -mstr1 = 'abc' -ml1 = list(mstr1) -ml1.reverse() -mstr2 = str(ml1) -``` -对于字符串还有一种稍微复杂一点的,是自定义递归函数来实现。 -```python -def my_reverse(str): - if str == "": - return str - else: - return my_reverse(str[1:]) + str[0] -``` - -在这里,介绍一种最优雅的反转方式,使用切片,不管你是字符串,还是列表,简直通杀。 -```python ->>> mstr = 'abc' ->>> ml = [1,2,3] ->>> mstr[::-1] -'cba' ->>> ml[::-1] -[3, 2, 1] -``` - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 21. 改变默认递归次数限制 - -上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。 - -那到底,默认递归次数限制是多少呢? -```python ->>> import sys ->>> sys.getrecursionlimit() -1000 -``` - -可以查,当然也可以自定义修改次数,退出即失效。 -```python ->>> sys.setrecursionlimit(2000) ->>> sys.getrecursionlimit() -2000 -``` - -## 22. 一行代码实现FTP服务器 - -搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。 - -SimpleHTTPServer是Python 2自带的一个模块,是Python的Web服务器。它在Python 3已经合并到http.server模块中。具体例子如下,如不指定端口,则默认是8000端口。 -```python -# python2 -python -m SimpleHTTPServer 8888 - -# python3 -python3 -m http.server 8888 -``` - -![](http://image.iswbm.com/20190511165716.png) - -SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 - -## 23. 让你晕头转向的 else 用法 - -if else 用法可以说最基础的语法表达式之一,但是今天不是讲这个的,一定要讲点不一样的。 - -if else 早已烂大街,但可能有很多人都不曾见过 for else 和 try else 的用法。为什么说它曾让我晕头转向,因为它不像 if else 那么直白,非黑即白,脑子经常要想一下才能才反应过来代码怎么走。反正我是这样的。 - -先来说说,for else -```python -def check_item(source_list, target): - for item in source_list: - if item == target: - print("Exists!") - break - - else: - print("Does not exist") - -``` -在往下看之前,你可以思考一下,什么情况下才会走 else。是循环被 break,还是没有break? - -给几个例子,你体会一下。 -```python -check_item(["apple", "huawei", "oppo"], "oppo") -# Exists! - -check_item(["apple", "huawei", "oppo"], "vivo") -# Does not exist -``` -可以看出,没有被 break 的程序才会正常走else流程。 - -再来看看,try else 用法。 -```python -def test_try_else(attr1 = None): - try: - if attr1: - pass - else: - raise - except: - print("Exception occurred...") - else: - print("No Exception occurred...") -``` - -同样来几个例子。当不传参数时,就抛出异常。 -```python -test_try_else() -# Exception occurred... - -test_try_else("ming") -# No Exception occurred... -``` - -可以看出,没有 try 里面的代码块没有抛出异常的,会正常走else。 - -总结一下,for else 和 try else 相同,只要代码正常走下去不被 break,不抛出异常,就可以走else。 - - -## 24. 字符串里的缝隙是什么? - -在Python中求一个字符串里,某子字符(串)出现的次数。 - -大家都懂得使用 count() 函数,比如下面几个常规例子: - -```python ->>> "aabb".count("a") -2 ->>> "aabb".count("b") -2 ->>> "aabb".count("ab") -1 -``` - -但是如果我想计算空字符串的个数呢? -```python ->>> "aabb".count("") -5 -``` - -**奇怪了吧?** - -不是应该返回 0 吗?怎么会返回 5? - -实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。 - -因此 对于 `aabb` 这个字符串在 Python 来看应该是这样的 - -![](http://image.iswbm.com/20200509172331.png) - -理解了这个“**缝隙**” 的概念后,以下这些就好理解了。 - -```python ->>> (" " * 10).count("") -11 ->>> ->>> "" in "" -True ->>> ->>> "" in "M" -True -``` - - - -## 25. 正负得正,负负得正 - -从初中开始,我们就开始接触了`负数` ,并且都知道了`负负得正` 的思想。 - -Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 `负负得正` 。 - -```python ->>> 5-3 -2 ->>> 5--3 -8 ->>> 5+-3 -2 ->>> 5++3 -8 ->>> 5---3 -2 -``` - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 26. 数值与字符串的比较 - -在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。 -```Python ->>> 100000000 < "" -True ->>> 100000000 < "hello" -True -``` - -但在 Python3 中,却不行。 -```python ->>> 100000000 < "" -TypeError: '<' not supported between instances of 'int' and 'str' -``` - -## 27. 循环中的局部变量泄露 - -在Python 2中 x 的值在一个循环执行之后被改变了。 -```python -# Python2 ->>> x = 1 ->>> [x for x in range(5)] -[0, 1, 2, 3, 4] ->>> x -4 -``` -不过在Python3 中这个问题已经得到解决了。 -```python -# Python3 ->>> x = 1 ->>> [x for x in range(5)] -[0, 1, 2, 3, 4] ->>> x -1 -``` - -## 28. 字典居然是可以排序的? - -在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。 - -```python -# Python2.7.10 ->>> mydict = {str(i):i for i in range(5)} ->>> mydict -{'1': 1, '0': 0, '3': 3, '2': 2, '4': 4} -``` - -假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的 - -在 Python3.6 + 中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。 - -```python -# Python3.6.7 ->>> mydict = {str(i):i for i in range(5)} ->>> mydict -{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} -``` - - - -## 29. 有趣但没啥用的 import 用法 - -import 是 Python 导包的方式。 - -你知道 Python 中内置了一些很有(wu)趣(liao)的包吗? - -**Hello World** - -``` ->>> import __hello__ -Hello World! -``` - -**Python之禅** - -``` ->>> import this - -The Zen of Python, by Tim Peters - -Beautiful is better than ugly. -Explicit is better than implicit. -Simple is better than complex. -Complex is better than complicated. -Flat is better than nested. -Sparse is better than dense. -Readability counts. -Special cases aren't special enough to break the rules. -Although practicality beats purity. -Errors should never pass silently. -Unless explicitly silenced. -In the face of ambiguity, refuse the temptation to guess. -There should be one-- and preferably only one --obvious way to do it. -Although that way may not be obvious at first unless you're Dutch. -Now is better than never. -Although never is often better than *right* now. -If the implementation is hard to explain, it's a bad idea. -If the implementation is easy to explain, it may be a good idea. -Namespaces are one honking great idea -- let's do more of those! -``` - -**反地心引力漫画** - -在 cmd 窗口中导入`antigravity` - -``` ->>> import antigravity -``` - -就会自动打开一个网页。 -![](http://image.iswbm.com/20190511165735.png) - -## 30. 局部/全局变量傻傻分不清 - -在开始讲之前,你可以试着运行一下下面这小段代码。 - -```python -# demo.py -a = 1 - -def add(): - a += 1 - -add() -``` - -看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下: - -```python -$ python demo.py -Traceback (most recent call last): - File "demo.py", line 6, in - add() - File "demo.py", line 4, in add - a += 1 -UnboundLocalError: local variable 'a' referenced before assignment -``` - -回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。 - -当程序运行到 `a += 1` 时,Python 解释器就认为在函数内部要给 `a` 这个变量赋值,当然就把 `a` 当做局部变量了,但是做为局部变量的 a 还没有被还没被定义。 - -因此报错是正常的。 - -理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢? - -```python -$ cat demo.py -a = 1 - -def output(): - print(a) - -output() - -$ python demo.py -1 -``` - - - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 31. 字母也玩起了障眼法 - -以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。 - -**在Python 2.x 中** - -``` ->>> valuе = 32 - File "", line 1 - valuе = 32 - ^ -SyntaxError: invalid syntax -``` - -**在Python 3.x 中** - -``` ->>> valuе = 32 ->>> value -11 -``` - -什么?没有截图你不信? - -![](http://image.iswbm.com/20200509122954.png) - - - -如果你在自己的电脑上尝试一下,结果可能是这样的 - -![](http://image.iswbm.com/20200509123107.png) - - - -**怎么又好了呢?** - -如果你想复现的话,请复制我这边给出的代码:`valuе = 32` - - - -**这是为什么呢?** - -原因在于,我上面使用的 value 变量名里的 `е` 又不是我们熟悉的 `e`,它是 Cyrillic(西里尔)字母。 - -``` ->>> ord('е') # cyrillic 'e' (Ye) -1077 ->>> ord('e') # latin 'e', as used in English and typed using standard keyboard -101 ->>> 'е' == 'e' -False -``` - -细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的 `e` 全局替换成 `e`,到时候你就哭去吧,肉眼根本看不出来嘛。 - -## 32. 字符串的分割技巧 - -当我们对字符串进行分割时,且分割符是 `\n`,有可能会出现这样一个窘境: - -```python ->>> str = "a\nb\n" ->>> print(str) -a -b - ->>> str.split('\n') -['a', 'b', ''] ->>> -``` - -会在最后一行多出一个元素,为了应对这种情况,你可以会多加一步处理。 - -但我想说的是,完成没有必要,对于这个场景,你可以使用 `splitlines` - -```python ->>> str.splitlines() -['a', 'b'] -``` - - - -## 33. 嵌套上下文管理的另类写法 - -当我们要写一个嵌套的上下文管理器时,可能会这样写 - -```python -import contextlib - -@contextlib.contextmanager -def test_context(name): - print('enter, my name is {}'.format(name)) - - yield - - print('exit, my name is {}'.format(name)) - -with test_context('aaa'): - with test_context('bbb'): - print('========== in main ============') -``` - -输出结果如下 - -```python -enter, my name is aaa -enter, my name is bbb -========== in main ============ -exit, my name is bbb -exit, my name is aaa -``` - -除此之外,你可知道,还有另一种嵌套写法 - -```python -with test_context('aaa'), test_context('bbb'): - print('========== in main ============') -``` - -## 34. += 不等同于=+ - -对列表 进行`+=` 操作相当于 extend,而使用 `=+` 操作是新增了一个列表。 - -因此会有如下两者的差异。 - -```python -# =+ ->>> a = [1, 2, 3, 4] ->>> b = a ->>> a = a + [5, 6, 7, 8] ->>> a -[1, 2, 3, 4, 5, 6, 7, 8] ->>> b -[1, 2, 3, 4] - - -# += ->>> a = [1, 2, 3, 4] ->>> b = a ->>> a += [5, 6, 7, 8] ->>> a -[1, 2, 3, 4, 5, 6, 7, 8] ->>> b -[1, 2, 3, 4, 5, 6, 7, 8] -``` - -## 35. 增量赋值的性能更好 - -诸如 `+=` 和 `*=` 这些运算符,叫做 增量赋值运算符。 - -这里使用用 += 举例,以下两种写法,在效果上是等价的。 - -``` -# 第一种 -a = 1 ; a += 1 - -# 第二种 -a = 1; a = a + 1 -``` - -`+=` 其背后使用的魔法方法是 \__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add__ 。 - -这两种写法有什么区别呢? - -用列表举例 a += b,使用 \__add__ 的话就像是使用了a.extend(b),如果使用 \__add__ 的话,则是 a = a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 - -所以在能使用增量赋值的时候尽量使用它。 - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 36. x == +x 吗? - -在大多数情况下,这个等式是成立的。 - -``` ->>> n1 = 10086 ->>> n2 = +n1 ->>> ->>> n1 == n2 -True -``` - -什么情况下,这个等式会不成立呢? - -由于Counter的机制,`+` 用于两个 Counter 实例相加,而相加的结果如果元素的个数 `<=` 0,就会被丢弃。 - -``` ->>> from collections import Counter ->>> ct = Counter('abcdbcaa') ->>> ct -Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) ->>> ct['c'] = 0 ->>> ct['d'] = -2 ->>> ->>> ct -Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) ->>> ->>> +ct -Counter({'a': 3, 'b': 2}) -``` - - - -## 37. 如何将 print 内容输出到文件 - -Python 3 中的 print 作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。 - -比如今天要说的使用 print 将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。 - -```python ->>> with open('test.log', mode='w') as f: -... print('hello, python', file=f, flush=True) ->>> exit() - -$ cat test.log -hello, python -``` - - - -## 38. site-packages和 dist-packages - -如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages** 下,而有些包安装在 **dist-packages** 下。 - -**它们有什么区别呢?** - -一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在 这个目录下。 - -而 dist-packages 其实是 debian 系的 Linux 系统(如 Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用 dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在 site-packages 下。 - -Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。 - -如何查找 Python 安装目录 - -```python ->>> from distutils.sysconfig import get_python_lib ->>> print(get_python_lib()) -/usr/lib/python2.7/site-packages -``` - -## 39. argument 和 parameter 的区别 - -arguments 和 parameter 的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。 - -但若要严格再进行区分,它们实际上还有各自的叫法 - -- parameter:形参(**formal parameter**),体现在函数内部,作用域是这个函数体。 -- argument :实参(**actual parameter**),调用函数实际传递的参数。 - -举个例子,如下这段代码,`"error"` 为 argument,而 msg 为 `parameter`。 - -```python -def output_msg(msg): - print(msg) - -output_msg("error") -``` - -## 40. 简洁而优雅的链式比较 - -先给你看一个示例: - -```python ->>> False == False == True -False -``` - -你知道这个表达式为什么会会返回 False 吗? - -它的运行原理与下面这个类似,是不是有点头绪了: - -```python -if 80 < score <= 90: - print("成绩良好") -``` - -如果你还是不明白,那我再给你整个第一个例子的等价写法。 - -```python ->>> False == False and False == True -False -``` - -这个用法叫做链式比较。 - - - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 41. 连接多个列表最极客的方式 - -```python ->>> a = [1,2] ->>> b = [3,4] ->>> c = [5,6] ->>> ->>> sum((a,b,c), []) -[1, 2, 3, 4, 5, 6] -``` - -## 42. 另外 8 种连接列表的方式 - -**1. 最直观的相加** - -使用 `+` 对多个列表进行相加,你应该懂,不多说了。 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> list01 + list02 + list03 -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - -**2. 借助 itertools** - -itertools 在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 - -在前面的文章中也介绍过,使用 `itertools.chain()` 函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 - -最后你再利用 list 将其转化为 列表。 - -```python ->>> from itertools import chain ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> list(chain(list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - -**3. 使用 * 解包** - -使用 `*` 可以解包列表,解包后再合并。 - -示例如下: - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> ->>> [*list01, *list02] -[1, 2, 3, 4, 5, 6] ->>> -``` - - - -**4. 使用 extend** - -在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> ->>> list01.extend(list02) ->>> list01 -[1, 2, 3, 4, 5, 6] -``` - -**5. 使用列表推导式** - -Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 - -那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? - -当然可以,具体示例代码如下: - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> [x for l in (list01, list02, list03) for x in l] -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - -**6. 使用 heapq** - -heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 - -该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> from heapq import merge ->>> ->>> list(merge(list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - -要注意的是,heapq.merge 除了合并多个列表外,它还会将合并后的最终的列表进行排序。 - -```python ->>> list01 = [2,5,3] ->>> list02 = [1,4,6] ->>> list03 = [7,9,8] ->>> ->>> from heapq import merge ->>> ->>> list(merge(list01, list02, list03)) -[1, 2, 4, 5, 3, 6, 7, 9, 8] ->>> -``` - -它的效果等价于下面这行代码: - -```python -sorted(itertools.chain(*iterables)) -``` - -如果你希望得到一个始终有序的列表,那请第一时间想到 heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 - -**7. 借助魔法方法** - -有一个魔法方法叫 `__add__`,当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的。 - -所以以下两种方法其实是等价的 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> ->>> list01 + list02 -[1, 2, 3, 4, 5, 6] ->>> ->>> ->>> list01.__add__(list02) -[1, 2, 3, 4, 5, 6] ->>> -``` - -借用这个魔法特性,我们可以 reduce 这个方法来对多个列表进行合并,示例代码如下 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> from functools import reduce ->>> reduce(list.__add__, (list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - -**8. 使用 yield from** - -在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 - -因此,我们可以像下面这样自定义一个合并列表的工具函数。 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> def merge(*lists): -... for l in lists: -... yield from l -... ->>> list(merge(list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - -## 43. 在程序退出前执行代码的技巧 - -使用 atexit 这个内置模块,可以很方便的注册退出函数。 - -不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 - -示例如下 - -![](http://image.iswbm.com/20200510112133.png) - -如果`clean()`函数有参数,那么你可以不用装饰器,而是直接调用`atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')`。 - -可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit 来得优雅,来得方便,并且它很容易扩展。 - -但是使用 atexit 仍然有一些局限性,比如: - -- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 -- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 -- 如果你手动调用了`os._exit()`,你注册的函数无法正常执行。 - -## 44. 合并字典的 8 种方法 - -**1. 最简单的原地更新** - -字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> profile.update(ext_info) ->>> print(profile) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -如果想使用 update 这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> from copy import deepcopy ->>> ->>> full_profile = deepcopy(profile) ->>> full_profile.update(ext_info) ->>> ->>> print(full_profile) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ->>> print(profile) -{"name": "xiaoming", "age": 27} -``` - - - -**2. 先解包再合并字典** - -使用 `**` 可以解包字典,解包完后再使用 dict 或者 `{}` 就可以合并。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> full_profile01 = {**profile, **ext_info} ->>> print(full_profile01) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ->>> ->>> full_profile02 = dict(**profile, **ext_info) ->>> print(full_profile02) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -若你不知道 `dict(**profile, **ext_info)` 做了啥,你可以将它等价于 - -```python ->>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -**3. 借助 itertools** - -在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 - -正好我们字典也是可迭代对象,自然就可以想到,可以使用 `itertools.chain()` 函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 dict 转成字典。 - -```python ->>> import itertools ->>> ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> ->>> dict(itertools.chain(profile.items(), ext_info.items())) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -**4. 借助 ChainMap** - -如果可以引入一个辅助包,那我就再提一个, `ChainMap` 也可以达到和 `itertools` 同样的效果。 - -```python ->>> from collections import ChainMap ->>> ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> dict(ChainMap(profile, ext_info)) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -使用 ChainMap 有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 itertools 就不会有这个问题)。 - -```python ->>> from collections import ChainMap ->>> ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info={"age": 30} ->>> dict(ChainMap(profile, ext_info)) -{'name': 'xiaoming', 'age': 27} -``` - - - -**5. 使用dict.items() 合并** - -在 Python 3.9 之前,其实就已经有 `|` 操作符了,只不过它通常用于对集合(set)取并集。 - -利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 - -你得先利用 `items` 方法将 dict 转成 dict_items,再对这两个 dict_items 取并集,最后利用 dict 函数,转成字典。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> full_profile = dict(profile.items() | ext_info.items()) ->>> full_profile -{'gender': 'male', 'age': 27, 'name': 'xiaoming'} -``` - - - -当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list 函数再合并(示例为 Python 3.x ) - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> dict(list(profile.items()) + list(ext_info.items())) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -若你在 Python 2.x 下,可以直接省去 list 函数。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> dict(profile.items() + ext_info.items()) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -**6. 最酷炫的字典解析式** - -Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 - -那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? - -当然可以,具体示例代码如下: - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> {k:v for d in [profile, ext_info] for k,v in d.items()} -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -**7. Python 3.9 新特性** - -在 2 月份发布的 Python 3.9.04a 版本中,新增了一个抓眼球的新操作符操作符: `|`, PEP584 将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> profile | ext_info -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ->>> ->>> ext_info | profile -{'gender': 'male', 'name': 'xiaoming', 'age': 27} ->>> ->>> -``` - -除了 `|` 操作符之外,还有另外一个操作符 `|=`,类似于原地更新。 - -```python ->>> ext_info |= profile ->>> ext_info -{'gender': 'male', 'name': 'xiaoming', 'age': 27} ->>> ->>> ->>> profile |= ext_info ->>> profile -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -看到这里,有没有涨姿势了,学了这么久的 Python ,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 7 种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 - - - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 45. 条件语句的七种写法 - -**第一种:原代码** - -这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。 - -```python -if age > 18: - return "已成年" -else: - return "未成年" -``` - -下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外) - -**第二种** - -语法: - -```python - if else -``` - -例子 - -```python ->>> age1 = 20 ->>> age2 = 17 ->>> ->>> ->>> msg1 = "已成年" if age1 > 18 else "未成年" ->>> print msg1 -已成年 ->>> ->>> msg2 = "已成年" if age2 > 18 else "未成年" ->>> print msg2 -未成年 ->>> -``` - -**第三种** - -语法 - -```python - and or -``` - -例子 - -```python ->>> msg1 = age1 > 18 and "已成年" or "未成年" ->>> msg2 = "已成年" if age2 > 18 else "未成年" ->>> ->>> print(msg1) -已成年 ->>> ->>> print(msg2) -未成年 -``` - -**第四种** - -语法 - -```python -(, )[condition] -``` - -例子 - -```python ->>> msg1 = ("未成年", "已成年")[age1 > 18] ->>> print(msg1) -已成年 ->>> ->>> ->>> msg2 = ("未成年", "已成年")[age2 > 18] ->>> print(msg2) -未成年 -``` - -**第五种** - -语法 - -```python -(lambda: , lambda:)[]() -``` - -例子 - -```python ->>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() ->>> print(msg1) -已成年 ->>> ->>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() ->>> print(msg2) -未成年 -``` - -**第六种** - -语法: - -```python -{True: , False: }[] -``` - -例子: - -```python ->>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] ->>> print(msg1) -已成年 ->>> ->>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] ->>> print(msg2) -未成年 -``` - -**第七种** - -语法 - -```python -(() and (,) or (,))[0] -``` - -例子 - -```python ->>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] ->>> print(msg1) -已成年 ->>> ->>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] ->>> print(msg2) -未成年 -``` - -以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 - -看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 - -> 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -## 46. /usr/bin/env python 有什么用? - -我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 - -```shell -#!/usr/bin/python -``` - -或者这样 - -```sh e llsh el -#!/usr/bin/env python -``` - -这两者有什么区别呢? - -稍微接触过 linux 的人都知道 `/usr/bin/python` 就是我们执行 `python` 进入console 模式里的 `python` - -![](http://image.iswbm.com/20200331184021.png) - -而当你在可执行文件头里使用 `#!` + `/usr/bin/python` ,意思就是说你得用哪个软件 (python)来执行这个文件。 - -那么加和不加有什么区别呢? - -不加的话,你每次执行这个脚本时,都得这样: `python xx.py` , - -![](http://image.iswbm.com/20200331185034.png) - -有没有一种方式?可以省去每次都加 `python` 呢? - -当然有,你可以文件头里加上`#!/usr/bin/python` ,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 - -![](http://image.iswbm.com/20200331184755.png) - -明白了这个后,再来看看 `!/usr/bin/env python` 这个 又是什么意思 ? - -当我执行 `env python` 时,自动进入了 python console 的模式。 - -![](http://image.iswbm.com/20200331185741.png) - -这是为什么?和 直接执行 python 好像没什么区别呀 - -当你执行 `env python` 时,它其实会去 `env | grep PATH` 里(也就是 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin )这几个路径里去依次查找名为python的可执行文件。 - -找到一个就直接执行,上面我们的 python 路径是在 `/usr/bin/python` 里,在 `PATH` 列表里倒数第二个目录下,所以当我在 `/usr/local/sbin` 下创建一个名字也为 python 的可执行文件时,就会执行 `/usr/bin/python` 了。 - -具体演示过程,你可以看下面。 - -![](http://image.iswbm.com/20200331190224.png) - -那么对于这两者,我们应该使用哪个呢? - -个人感觉应该优先使用 `#!/usr/bin/env python`,因为不是所有的机器的 python 解释器都是 `/usr/bin/python` 。 - -## 47. 让我爱不释手的用户环境 - -当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢? - -可以使用 `pip install --user pkg` 将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。 - -```shell -# 在全局环境中未安装 requests -[root@localhost ~]$ pip list | grep requests -[root@localhost ~]$ su - wangbm - -# 由于用户环境继承自全局环境,这里也未安装 -[wangbm@localhost ~]$ pip list | grep requests -[wangbm@localhost ~]$ pip install --user requests -[wangbm@localhost ~]$ pip list | grep requests -requests (2.22.0) -[wangbm@localhost ~]$ - -# 从 Location 属性可发现 requests 只安装在当前用户环境中 -[wangbm@localhost ~]$ pip show requests ---- -Metadata-Version: 2.1 -Name: requests -Version: 2.22.0 -Summary: Python HTTP for Humans. -Home-page: http://python-requests.org -Author: Kenneth Reitz -Author-email: me@kennethreitz.org -Installer: pip -License: Apache 2.0 -Location: /home/wangbm/.local/lib/python2.7/site-packages -[wangbm@localhost ~]$ exit -logout - -# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 -[root@localhost ~]$ pip list | grep requests -[root@localhost ~]$ -``` - - - -## 48. 实现类似 defer 的延迟调用 - -在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 - -```go -import "fmt" - -func myfunc() { - fmt.Println("B") -} - -func main() { - defer myfunc() - fmt.Println("A") -} -``` - -输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc 的调用写在函数的第一行,这就是延迟调用。 - -``` -A -B -``` - -那么在 Python 中否有这种机制呢? - -当然也有,只不过并没有 Golang 这种简便。 - -在 Python 可以使用 **上下文管理器** 达到这种效果 - -```python -import contextlib - -def callback(): - print('B') - -with contextlib.ExitStack() as stack: - stack.callback(callback) - print('A') -``` - -输出如下 - -``` -A -B -``` - -## 49. 自带的缓存机制不用白不用 - -缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 - -数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 - -为了实现这个需求,Python 3.2 + 中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。 - -这个机制实现于 functool 模块中的 lru_cache 装饰器。 - -```python -@functools.lru_cache(maxsize=None, typed=False) -``` - -参数解读: - -- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 2 的幂时,性能最佳 -- typed:若为 True,则不同参数类型的调用将分别缓存。 - -举个例子 - -```python -from functools import lru_cache - -@lru_cache(None) -def add(x, y): - print("calculating: %s + %s" % (x, y)) - return x + y - -print(add(1, 2)) -print(add(1, 2)) -print(add(2, 3)) -``` - -输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果 - -```shell -calculating: 1 + 2 -3 -3 -calculating: 2 + 3 -5 -``` - -## 50. 重定向标准输出到日志 - -假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。 - -检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。 - -如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。 - -由于最初约定的脚本返回方式是以 JSON 的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。 - -如何避免这种情况的发生呢? - -我们可以这样做,把你的标准错误输出到日志文件中。 - -```python -import contextlib - -log_file="/var/log/you.log" - -def you_task(): - pass - -@contextlib.contextmanager -def close_stdout(): - raw_stdout = sys.stdout - file = open(log_file, 'a+') - sys.stdout = file - - yield - - sys.stdout = raw_stdout - file.close() - -with close_stdout(): - you_task() -``` - - - ---- - -![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_13.md b/source/c01/c01_13.md index 42ac859..8cb56c2 100644 --- a/source/c01/c01_13.md +++ b/source/c01/c01_13.md @@ -1,131 +1,353 @@ -# 1.13 Python几个高阶函数 +# 1.13 别再使用 pprint 打印字典了 ![](http://image.iswbm.com/20200602135014.png) ---- +## 1. 吐槽问题 -## 1. lambda 表达式 +pprint 你应该很熟悉了吧? -匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。 +随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货 。 -正常情况下,我们定义一个函数,使用的是 `def` 关键字,而当你学会使用匿名函数后,替代 `def` 的是 `lambda`。 +比如这下面这个 json 字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。 -这边使用`def` 和 `lambda` 分别举个例子,你很快就能理解。 +```json +[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] ``` -def mySum(x, y): - return x+y -mySum(2, 3) -# 5 -(lambda x, y: x+y)(2, 4) -# 6 +如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint 看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。 + +```python +>>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> from pprint import pprint +>>> pprint(info) +[{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] ``` -从上面的示例,我们可以看到匿名函数直接运行,省下了很多行的代码,有没有? -接下来,我们的仔细看一下它的用法 - -带 if/else -``` ->>>( lambda x, y: x if x < y else y )( 1, 2 ) -1 -``` -嵌套函数 -``` ->>>( lambda x: ( lambda y: ( lambda z: x + y + z )( 1 ) )( 2 ) )( 3 ) -6 +好像有点效果,真的是 “神器”呀。 + +但是你告诉我, **\xe4\xbd\xa0\xe7\x9a** 这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。 + +好在我懂点 Python 2 的编码,知道 Python 2 中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte 存储的。 + +行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。 + +```python +>>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> from pprint import pprint +>>> pprint(info) +[{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': u'\u76ae\u7684\u561b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': u'\u4e0d\u5b58\u5728\u7684', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] ``` -递归函数 -``` ->>> func = lambda n:1 if n == 0 else n * func(n-1) ->>> func(5) -120 + +确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀? + ``` -或者 +u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f' ``` ->>> f = lambda func, n: 1 if n == 0 else n * func( func, n - 1 ) ->>> f(f,4) -24 + +除此之外,我们知道 json 的严格要求必须使用 **双引号**,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是 **单引号**?我也太难了吧,我连自己的代码都无法控制了吗? + +到这里,我们知道了 pprint 带来的两个问题: + +1. 没法在 Python 2 下正常打印中文 +2. 没法输出 JSON 标准格式的格式化内容(双引号) + +## 2. 解决问题 + +### 打印中文 + +如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。 + +```python +# Python3.7 +>>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> from pprint import pprint +>>> pprint(info) +[{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '皮的嘛', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '不存在的', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] +>>> + ``` -从以上示例来看,lambda 表达式和常规的函数相比,写法比较怪异,可读性相对较差。除了可以直接运行之外,好像并没有其他较为突出的功能,为什么在今天我们要介绍它呢? +但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的 Python,本来我可以选择不用的,因为有更好的替代方案(**这个后面会讲**)。 -首先我们要知道 lambda 是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。 +但是我出于猎奇,正好前两天不是写过一篇关于 编码 的文章吗,我自认为自己对于 编码还是掌握比较熟练的,就想着来解决一下这个问题。 -## 2. map 函数 +索性就来看下 pprint 的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。 -map 函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。 +**以下是我的解决方案,供你参考**: -它可以实现怎样的功能呢,我举个例子你就明白了。 -``` ->>> map(lambda x: x*2, [1,2,3,4,5]) -[2, 4, 6, 8, 10] -``` -可以很清楚地看到,它可以将后面序列中的每一个元素做为参数传入lambda中。 +写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer) -当我们不使用 map 函数时,你也许会这样子写。 -``` -mylist=[] -for i in [1,2,3,4,5]: - mylist.append(i*2) -``` +并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str 类型,而是 unicode 类型,就用 uft8 编码成 str 类型。 -## 3. filter 函数 +```python +# coding: utf-8 +from pprint import PrettyPrinter -filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda 表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回 True,则元素会被保留下来,当表达式返回 False ,则元素会被丢弃。 +# 继承 PrettyPrinter,复写 format 方法 +class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) -下面这个例子,将过滤出一个列表中小于0的元素。 -``` ->>>filter(lambda x: x < 0, range(-5, 5)) -[-5, -4, -3, -2, -1] +info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + +MyPrettyPrinter().pprint(info) ``` -## 4. reduce 函数 +输出如下,已经解决了中文的显示问题: -reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 lambda 函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。 +![](http://image.iswbm.com/20200507171451.png) -![](http://image.iswbm.com/20200930175131.png) +### 打印双引号 -这边举个例子你也就明白了。 +解决了中文问题后,再来看看如何让 pprint 打印双引号。 -``` ->>>reduce(lambda x,y: x+y, [1,2,3,4,5]) -15 -``` -它的运算过程分解一下是这样的。 -``` -1+2=3 -3+3=6 -6+4+10 -10+5=15 -``` +在实例化 PrettyPrinter 对象的时候,可以接收一个 stream 对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个 stream,也就是标准输出。 + +现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。 + +那我们完全可以自己定义一个 stream 类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。 -## 5. 注意点 +有了思路,就可以开始写代码了,如下: -以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的 Pythonic ,在某一程度上代码看起来更加的简洁。 +```python +# coding: utf-8 +from pprint import PrettyPrinter -如果你是新手呢,你需要注意的是,以上示例是在 Python2.x 环境下演示的。而在 Python3.x 中,却有所不同,你可以自己尝试一下。 +class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) -这里总结一下: +class MyStream(): + def write(self, text): + print text.replace('\'', '"') -第一点,map 和 filter 函数返回的都不再是一个列表,而是一个迭代器对象。这里以map为例 +info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +MyPrettyPrinter(stream=MyStream()).pprint(info) ``` ->>> map_obj = map(lambda x: x*2, [1,2,3,4,5]) ->>> from collections.abc import Iterator ->>> isinstance(map_obj, Iterator) -True ->>> next(map_obj) + +尝试执行了下,我的天,怎么是这样子的。 + +```json +[ +{ +"des" +: +2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青 +, + "downloadUrl": +"app/com.renren.mobile.android/com.renren.mobile.android.apk" +, + "iconUrl": +"app/com.renren.mobile.android/icon.jpg" +, + "id": +1580615 +, + "name": +皮的嘛 +, + "packageName": +"com.renren.mobile.android" +, + "size": +21803987 +, + "stars": +2 +} +, + +{ +"des" +: +斗鱼271934走过路过不要错过,这里有最好的鸡儿 +, + "downloadUrl": +"app/com.ct.client/com.ct.client.apk" +, + "iconUrl": +"app/com.ct.client/icon.jpg" +, + "id": +1540629 +, + "name": +不存在的 +, + "packageName": +"com.ct.client" +, + "size": +4794202 +, + "stars": 2 ->>> list(map_obj) -[4, 6, 8, 10] +} +] ``` -第二点,reduce 不可以直接调用,而是要先导入才能使用, +经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个 **换行符**。 + +那如何将使 print 函数打印的内容,不进行换行呢? + +方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个 **逗号** 就行。 + +就像下面这样。 + +![](http://image.iswbm.com/20200507174459.png) + +知道了问题所在,再修改下代码 + +```python +# coding: utf-8 +from pprint import PrettyPrinter + +class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + +class MyStream(): + def write(self, text): + print text.replace('\'', '"'), + +info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + +MyPrettyPrinter(stream=MyStream()).pprint(info) ``` -from functools import reduce + +终于成功了,太不容易了吧。 + +![](http://image.iswbm.com/20200507174802.png) + +## 3. 何必折腾 + +通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。 + +代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。 + +**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**。 + +为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python ,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗? + +如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用 pprint 了。 + +### 打印中文 + +其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。 + +但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint 简单得不要太多。 + +具体的代码示例如下: + +```python +>>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] +>>> +>>> import json +>>> +>>> +>>> print json.dumps(info, indent=4, ensure_ascii=False) +[ + { + "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk", + "iconUrl": "app/com.renren.mobile.android/icon.jpg", + "name": "皮的嘛", + "stars": 2, + "packageName": "com.renren.mobile.android", + "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青", + "id": 1580615, + "size": 21803987 + }, + { + "downloadUrl": "app/com.ct.client/com.ct.client.apk", + "iconUrl": "app/com.ct.client/icon.jpg", + "name": "不存在的", + "stars": 2, + "packageName": "com.ct.client", + "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿", + "id": 1540629, + "size": 4794202 + } +] +>>> ``` +json.dumps 的关键参数有两个: + +- **indent=4**:以 4 个空格缩进单位 +- **ensure_ascii=False**:接收非 ASCII 编码的字符,这样才能使用中文 + +与 pprint 相比 json.dumps 可以说完胜: + +1. 两个参数就能实现所有我的需求(打印中文与双引号) +2. 就算在 Python 2 下,使用中文也不需要用 `u'中文'` 这种写法 +3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题 + +## 4. 总结一下 + +本来很简单的一个观点,我为了证明 pprint 实现那两个需求有多么困难,花了很多的时间去研究了 pprint 的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。 + +本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点 + +1. 核心观点:Python2 下不要再使用 pprint +2. 若真要使用,且有和一样的改造需求,可以参考我的实现 +3. Python 2 中的 print 语句后居然可以加 逗号 + +以上。希望此文能对你有帮助。 ---- -![](http://image.iswbm.com/20200607174235.png) +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_13.rst b/source/c01/c01_13.rst index d84ac18..99db502 100755 --- a/source/c01/c01_13.rst +++ b/source/c01/c01_13.rst @@ -1,169 +1,391 @@ -1.13 Python几个高阶函数 -======================= +1.13 别再使用 pprint 打印字典了 +=============================== |image0| --------------- +1. 吐槽问题 +----------- -1. lambda 表达式 ----------------- +pprint 你应该很熟悉了吧? + +随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货 +。 + +比如这下面这个 json +字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。 + +.. code:: json + + [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] + +如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint +看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。 + +.. code:: python + + >>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> from pprint import pprint + >>> pprint(info) + [{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] + +好像有点效果,真的是 “神器”呀。 + +但是你告诉我, +**:raw-latex:`\xe`4:raw-latex:`\xbd`:raw-latex:`\xa`0:raw-latex:`\xe`7:raw-latex:`\x`9a** +这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。 + +好在我懂点 Python 2 的编码,知道 Python 2 +中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte +存储的。 + +行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。 + +.. code:: python + + >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> from pprint import pprint + >>> pprint(info) + [{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': u'\u76ae\u7684\u561b', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': u'\u4e0d\u5b58\u5728\u7684', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] + +确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀? -匿名函数(英语:anonymous -function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。 +:: -正常情况下,我们定义一个函数,使用的是 ``def`` -关键字,而当你学会使用匿名函数后,替代 ``def`` 的是 ``lambda``\ 。 + u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f' -这边使用\ ``def`` 和 ``lambda`` 分别举个例子,你很快就能理解。 +除此之外,我们知道 json 的严格要求必须使用 +**双引号**\ ,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是 +**单引号**\ ?我也太难了吧,我连自己的代码都无法控制了吗? -:: +到这里,我们知道了 pprint 带来的两个问题: - def mySum(x, y): - return x+y - mySum(2, 3) - # 5 +1. 没法在 Python 2 下正常打印中文 +2. 没法输出 JSON 标准格式的格式化内容(双引号) - (lambda x, y: x+y)(2, 4) - # 6 +2. 解决问题 +----------- -从上面的示例,我们可以看到匿名函数直接运行,省下了很多行的代码,有没有? +打印中文 +~~~~~~~~ -接下来,我们的仔细看一下它的用法 +如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。 -带 if/else +.. code:: python -:: + # Python3.7 + >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> from pprint import pprint + >>> pprint(info) + [{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青', + 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', + 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', + 'id': 1580615, + 'name': '皮的嘛', + 'packageName': 'com.renren.mobile.android', + 'size': 21803987, + 'stars': 2}, + {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿', + 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', + 'iconUrl': 'app/com.ct.client/icon.jpg', + 'id': 1540629, + 'name': '不存在的', + 'packageName': 'com.ct.client', + 'size': 4794202, + 'stars': 2}] + >>> - >>>( lambda x, y: x if x < y else y )( 1, 2 ) - 1 +但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的 +Python,本来我可以选择不用的,因为有更好的替代方案(\ **这个后面会讲**\ )。 -嵌套函数 +但是我出于猎奇,正好前两天不是写过一篇关于 编码 +的文章吗,我自认为自己对于 +编码还是掌握比较熟练的,就想着来解决一下这个问题。 -:: +索性就来看下 pprint +的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。 - >>>( lambda x: ( lambda y: ( lambda z: x + y + z )( 1 ) )( 2 ) )( 3 ) - 6 +**以下是我的解决方案,供你参考**\ : -递归函数 +写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer) -:: +并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str +类型,而是 unicode 类型,就用 uft8 编码成 str 类型。 - >>> func = lambda n:1 if n == 0 else n * func(n-1) - >>> func(5) - 120 +.. code:: python -或者 + # coding: utf-8 + from pprint import PrettyPrinter -:: + # 继承 PrettyPrinter,复写 format 方法 + class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) - >>> f = lambda func, n: 1 if n == 0 else n * func( func, n - 1 ) - >>> f(f,4) - 24 + info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] -从以上示例来看,lambda -表达式和常规的函数相比,写法比较怪异,可读性相对较差。除了可以直接运行之外,好像并没有其他较为突出的功能,为什么在今天我们要介绍它呢? + MyPrettyPrinter().pprint(info) -首先我们要知道 lambda -是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。 +输出如下,已经解决了中文的显示问题: -2. map 函数 ------------ +|image1| -map -函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。 +打印双引号 +~~~~~~~~~~ + +解决了中文问题后,再来看看如何让 pprint 打印双引号。 + +在实例化 PrettyPrinter 对象的时候,可以接收一个 stream +对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个 +stream,也就是标准输出。 + +现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。 + +那我们完全可以自己定义一个 stream +类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。 + +有了思路,就可以开始写代码了,如下: + +.. code:: python + + # coding: utf-8 + from pprint import PrettyPrinter + + class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) + + class MyStream(): + def write(self, text): + print text.replace('\'', '"') + + info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + MyPrettyPrinter(stream=MyStream()).pprint(info) + +尝试执行了下,我的天,怎么是这样子的。 + +.. code:: json + + [ + { + "des" + : + 2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青 + , + "downloadUrl": + "app/com.renren.mobile.android/com.renren.mobile.android.apk" + , + "iconUrl": + "app/com.renren.mobile.android/icon.jpg" + , + "id": + 1580615 + , + "name": + 皮的嘛 + , + "packageName": + "com.renren.mobile.android" + , + "size": + 21803987 + , + "stars": + 2 + } + , + + { + "des" + : + 斗鱼271934走过路过不要错过,这里有最好的鸡儿 + , + "downloadUrl": + "app/com.ct.client/com.ct.client.apk" + , + "iconUrl": + "app/com.ct.client/icon.jpg" + , + "id": + 1540629 + , + "name": + 不存在的 + , + "packageName": + "com.ct.client" + , + "size": + 4794202 + , + "stars": + 2 + } + ] -它可以实现怎样的功能呢,我举个例子你就明白了。 +经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个 +**换行符**\ 。 -:: +那如何将使 print 函数打印的内容,不进行换行呢? - >>> map(lambda x: x*2, [1,2,3,4,5]) - [2, 4, 6, 8, 10] +方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个 +**逗号** 就行。 -可以很清楚地看到,它可以将后面序列中的每一个元素做为参数传入lambda中。 +就像下面这样。 -当我们不使用 map 函数时,你也许会这样子写。 +|image2| -:: +知道了问题所在,再修改下代码 - mylist=[] - for i in [1,2,3,4,5]: - mylist.append(i*2) +.. code:: python -3. filter 函数 --------------- + # coding: utf-8 + from pprint import PrettyPrinter -filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda -表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回 -True,则元素会被保留下来,当表达式返回 False ,则元素会被丢弃。 + class MyPrettyPrinter(PrettyPrinter): + def format(self, object, context, maxlevels, level): + if isinstance(object, unicode): + return (object.encode('utf8'), True, False) + return PrettyPrinter.format(self, object, context, maxlevels, level) -下面这个例子,将过滤出一个列表中小于0的元素。 + class MyStream(): + def write(self, text): + print text.replace('\'', '"'), -:: + info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - >>>filter(lambda x: x < 0, range(-5, 5)) - [-5, -4, -3, -2, -1] + MyPrettyPrinter(stream=MyStream()).pprint(info) -4. reduce 函数 --------------- +终于成功了,太不容易了吧。 -reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 -个元素进行操作,得到的结果再与第三个数据用 lambda -函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。 +|image3| -|image1| +3. 何必折腾 +----------- -这边举个例子你也就明白了。 +通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。 -:: +代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。 - >>>reduce(lambda x,y: x+y, [1,2,3,4,5]) - 15 +**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**\ 。 -它的运算过程分解一下是这样的。 +为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python +,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗? -:: +如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用 +pprint 了。 - 1+2=3 - 3+3=6 - 6+4+10 - 10+5=15 +.. _打印中文-1: -5. 注意点 ---------- +打印中文 +~~~~~~~~ -以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的 -Pythonic ,在某一程度上代码看起来更加的简洁。 +其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。 -如果你是新手呢,你需要注意的是,以上示例是在 Python2.x -环境下演示的。而在 Python3.x 中,却有所不同,你可以自己尝试一下。 +但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint +简单得不要太多。 -这里总结一下: +具体的代码示例如下: -第一点,map 和 filter -函数返回的都不再是一个列表,而是一个迭代器对象。这里以map为例 +.. code:: python -:: + >>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] + >>> + >>> import json + >>> + >>> + >>> print json.dumps(info, indent=4, ensure_ascii=False) + [ + { + "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk", + "iconUrl": "app/com.renren.mobile.android/icon.jpg", + "name": "皮的嘛", + "stars": 2, + "packageName": "com.renren.mobile.android", + "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青", + "id": 1580615, + "size": 21803987 + }, + { + "downloadUrl": "app/com.ct.client/com.ct.client.apk", + "iconUrl": "app/com.ct.client/icon.jpg", + "name": "不存在的", + "stars": 2, + "packageName": "com.ct.client", + "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿", + "id": 1540629, + "size": 4794202 + } + ] + >>> - >>> map_obj = map(lambda x: x*2, [1,2,3,4,5]) - >>> from collections.abc import Iterator - >>> isinstance(map_obj, Iterator) - True - >>> next(map_obj) - 2 - >>> list(map_obj) - [4, 6, 8, 10] +json.dumps 的关键参数有两个: -第二点,reduce 不可以直接调用,而是要先导入才能使用, +- **indent=4**\ :以 4 个空格缩进单位 +- **ensure_ascii=False**\ :接收非 ASCII 编码的字符,这样才能使用中文 -:: +与 pprint 相比 json.dumps 可以说完胜: + +1. 两个参数就能实现所有我的需求(打印中文与双引号) +2. 就算在 Python 2 下,使用中文也不需要用 ``u'中文'`` 这种写法 +3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题 - from functools import reduce +4. 总结一下 +----------- --------------- +本来很简单的一个观点,我为了证明 pprint +实现那两个需求有多么困难,花了很多的时间去研究了 pprint +的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。 -|image2| +本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点 + +1. 核心观点:Python2 下不要再使用 pprint +2. 若真要使用,且有和一样的改造需求,可以参考我的实现 +3. Python 2 中的 print 语句后居然可以加 逗号 + +以上。希望此文能对你有帮助。 + +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200930175131.png -.. |image2| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200507171451.png +.. |image2| image:: http://image.iswbm.com/20200507174459.png +.. |image3| image:: http://image.iswbm.com/20200507174802.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_17.md b/source/c01/c01_17.md index 5cab543..9e13038 100644 --- a/source/c01/c01_17.md +++ b/source/c01/c01_17.md @@ -1,597 +1,274 @@ -# 1.17 深入理解「描述符」 +# 1.17 详解 Python 中的编码问题 ![](http://image.iswbm.com/20200602135014.png) -学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Descriptor(描述符)特性可以排得上号。 +Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 Python 开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 -描述符 是Python 语言独有的特性,它不仅在应用层使用,在语言语法糖的实现上也有使用到(在下面的文章会一一介绍)。 +一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式? -当你点进这篇文章时 +反反复复,这个过程真是太痛苦了。 -- 你也许没学过描述符,甚至没听过描述符。 -- 或者你对描述符只是一知半解 +今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再 Google,收藏这篇文章就行。 -无论你是哪种,本篇都将带你全面的学习描述符,一起来感受 Python 语言的优雅。 -### 1. 为什么要使用描述符? -假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。 +## 1. Python 3 中 str 与 bytes -```python -class Student: - def __init__(self, name, math, chinese, english): - self.name = name - self.math = math - self.chinese = chinese - self.english = english - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) -``` - -看起来一切都很合理 +在 Python3中,字符串有两种类型 ,str 和 bytes。 -```python ->>> std1 = Student('小明', 76, 87, 68) ->>> std1 - -``` +今天就来说一说这二者的区别: -但是程序并不像人那么智能,不会自动根据使用场景判断数据的合法性,如果老师在录入成绩的时候,不小心录入了将成绩录成了负数,或者超过100,程序是无法感知的。 +- `unicode string(str 类型)`:以 Unicode code points 形式存储,**人类认识的形式** +- `byte string(bytes 类型)`:以 byte 形式存储,**机器认识的形式** -聪明的你,马上在代码中加入了判断逻辑。 +在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 `type` 和 `isinstance` 可以判别 ```python -class Student: - def __init__(self, name, math, chinese, english): - self.name = name - if 0 <= math <= 100: - self.math = math - else: - raise ValueError("Valid value must be in [0, 100]") - - if 0 <= chinese <= 100: - self.chinese = chinese - else: - raise ValueError("Valid value must be in [0, 100]") - - if 0 <= chinese <= 100: - self.english = english - else: - raise ValueError("Valid value must be in [0, 100]") - - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) -``` +# python3 -这下程序稍微有点人工智能了,能够自己明辨是非了。 - -![](http://image.iswbm.com/20190425221322.png) +>>> str_obj = "你好" +>>> +>>> type(str_obj) + +>>> +>>> isinstance("你好", str) +True +>>> +>>> isinstance("你好", bytes) +False +>>> +``` -程序是智能了,但在`__init__`里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过 Property 特性,可以很好的应用在这里。于是你将代码修改成如下,代码的可读性瞬间提升了不少 +而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个 `b`,就表示你要定义一个 bytes 类型的字符串对象。 ```python -class Student: - def __init__(self, name, math, chinese, english): - self.name = name - self.math = math - self.chinese = chinese - self.english = english - - @property - def math(self): - return self._math - - @math.setter - def math(self, value): - if 0 <= value <= 100: - self._math = value - else: - raise ValueError("Valid value must be in [0, 100]") - - @property - def chinese(self): - return self._chinese - - @chinese.setter - def chinese(self, value): - if 0 <= value <= 100: - self._chinese = value - else: - raise ValueError("Valid value must be in [0, 100]") - - @property - def english(self): - return self._english - - @english.setter - def english(self, value): - if 0 <= value <= 100: - self._english = value - else: - raise ValueError("Valid value must be in [0, 100]") - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) +# python3 +>>> byte_obj = b"Hello World!" +>>> type(byte_obj) + +>>> +>>> isinstance(byte_obj, str) +False +>>> +>>> isinstance(byte_obj, bytes) +True +>>> ``` -程序还是一样的人工智能,非常好。 - -![](http://image.iswbm.com/20190425221322.png) - -你以为你写的代码,已经非常优秀,无懈可击了。 - - - -没想到,人外有天,你的主管看了你的代码后,深深地叹了口气:类里的三个属性,math、chinese、english,都使用了 Property 对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100 就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢,这代码简直没法忍。去了解一下 Python 的描述符吧。 - -经过主管的指点,你知道了「描述符」这个东西。怀着一颗敬畏之心,你去搜索了下关于 描述符的用法。 - -其实也很简单,一个实现了 `描述符协议` 的类就是一个描述符。 - -什么描述符协议:在类里实现了 `__get__()`、`__set__()`、`__delete__()` 其中至少一个方法。 - -- `__get__`: 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。 -- `__set__ `:将在属性分配操作中调用。不会返回任何内容。 -- `__delete__ `:控制删除操作。不会返回内容。 - -对描述符有了大概的了解后,你开始重写上面的方法。 - -如前所述,Score 类是一个描述符,当从 Student 的实例访问 math、chinese、english这三个属性的时候,都会经过 Score 类里的三个特殊的方法。这里的 Score 避免了 使用Property 出现大量的代码无法复用的尴尬。 +但是在定义中文字符串时,你就不能直接在前面加 `b` 了,而应该使用 `encode` 转一下。 ```python -class Score: - def __init__(self, default=0): - self._score = default - - def __set__(self, instance, value): - if not isinstance(value, int): - raise TypeError('Score must be integer') - if not 0 <= value <= 100: - raise ValueError('Valid value must be in [0, 100]') - - self._score = value - - def __get__(self, instance, owner): - return self._score - - def __delete__(self): - del self._score - -class Student: - math = Score(0) - chinese = Score(0) - english = Score(0) - - def __init__(self, name, math, chinese, english): - self.name = name - self.math = math - self.chinese = chinese - self.english = english - - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) +>>> byte_obj=b"你好" + File "", line 1 +SyntaxError: bytes can only contain ASCII literal characters. +>>> +>>> str_obj="你好" +>>> +>>> str_obj.encode("utf-8") +b'\xe4\xbd\xa0\xe5\xa5\xbd' +>>> ``` -实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等) - -![](http://image.iswbm.com/20190425221233.png) - -以上,我举了下具体的实例,从最原始的编码风格到 Property ,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 - -到这里,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 `保护属性不受修改`、`属性类型检查` 的基本功能,同时有大大提高代码的复用率。 - - - -### 2. 描述符的访问规则 +## 2. Python 2 中 str 与 unicode -描述符分两种: +而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。 -- 数据描述符:实现了`__get__` 和 `__set__` 两种方法的描述符 -- 非数据描述符:只实现了`__get__` 一种方法的描述符 +在 Python2 里,字符串也只有两种类型,unicode 和 str 。 -你一定会问,他们有什么区别呢?网上的讲解,我看过几个,很多都把一个简单的东西讲得复杂了。 +只有 unicode object 和 非unicode object(其实应该叫 str object) 的区别: -其实就一句话,**数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**。 +- `unicode string(unicode类型)`:以 Unicode code points 形式存储,**人类认识的形式** +- `byte string(str 类型)`:以 byte 形式存储,**机器认识的形式** -如果实例字典中有与描述符同名的属性,如果描述符是数据描述符,优先使用数据描述符,如果是非数据描述符,优先使用字典中的属性。 - -这边还是以上节的成绩管理的例子来说明,方便你理解。 +当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str 字符串对象,比如这样 ```python -# 数据描述符 -class DataDes: - def __init__(self, default=0): - self._score = default - - def __set__(self, instance, value): - self._score = value - - def __get__(self, instance, owner): - print("访问数据描述符里的 __get__") - return self._score - -# 非数据描述符 -class NoDataDes: - def __init__(self, default=0): - self._score = default - - def __get__(self, instance, owner): - print("访问非数据描述符里的 __get__") - return self._score - - -class Student: - math = DataDes(0) - chinese = NoDataDes(0) - - def __init__(self, name, math, chinese): - self.name = name - self.math = math - self.chinese = chinese - - def __getattribute__(self, item): - print("调用 __getattribute__") - return super(Student, self).__getattribute__(item) - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese) -``` - -需要注意的是,math 是数据描述符,而 chinese 是非数据描述符。从下面的验证中,可以看出,当实例属性和数据描述符同名时,会优先访问数据描述符(如下面的math),而当实例属性和非数据描述符同名时,会优先访问实例属性(`__getattribute__`) +# python2 -```python ->>> std = Student('xm', 88, 99) ->>> ->>> std.math -调用 __getattribute__ -访问数据描述符里的 __get__ -88 ->>> std.chinese -调用 __getattribute__ -99 +>>> str_obj="你好" +>>> +>>> type(str_obj) + +>>> +>>> str_obj +'\xe4\xbd\xa0\xe5\xa5\xbd' +>>> +>>> isinstance(str_obj, bytes) +True +>>> isinstance(str_obj, str) +True +>>> isinstance(str_obj, unicode) +False +>>> +>>> str is bytes +True ``` -讲完了数据描述符和非数据描述符,我们还需要了解的对象属性的查找规律。 - -当我们对一个实例属性进行访问时,Python 会按 `obj.__dict__` → `type(obj).__dict__` → `type(obj)的父类.__dict__` 顺序进行查找,如果查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。 - -### 3. 基于描述符如何实现property +而当我们在双引号或单引号前面加个 `u`,就表明我们定义的是 unicode 字符串对象,比如这样 -经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。 +```python +# python2 -正常人所见过的描述符的用法就是上面提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Python 的特性的底层实现机制都是基于 `描述符协议` 的,比如我们熟悉的`@property` 、`@classmethod` 、`@staticmethod` 和 `super` 等。 +>>> unicode_obj = u"你好" +>>> +>>> unicode_obj +u'\u4f60\u597d' +>>> +>>> type(unicode_obj) + +>>> +>>> isinstance(unicode_obj, bytes) +False +>>> isinstance(unicode_obj, str) +False +>>> +>>> isinstance(unicode_obj, unicode) +True +``` -先来说说 `property` 吧。 -有了前面的基础,我们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。 -```python -class Student: - def __init__(self, name): - self.name = name - - @property - def math(self): - return self._math - - @math.setter - def math(self, value): - if 0 <= value <= 100: - self._math = value - else: - raise ValueError("Valid value must be in [0, 100]") -``` +## 3. 如何检测对象的编码 -不防再简单回顾一下它的用法,通过property装饰的函数,如例子中的 math 会变成 Student 实例的属性。而对 math 属性赋值会进入 使用 `math.setter` 装饰函数的逻辑代码块。 +所有的字符,在 unicode 字符集中都有对应的编码值(英文叫做:`code point`) -为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入 property 的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。 +而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312 等。 -不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。 +也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。 -这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类 `property` 特性。 +那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码? -代码如下: +这时候就要介绍到一个 python 的库 -- `chardet` ,使用它之前 需要先安装 -```python -class TestProperty(object): - - def __init__(self, fget=None, fset=None, fdel=None, doc=None): - self.fget = fget - self.fset = fset - self.fdel = fdel - self.__doc__ = doc - - def __get__(self, obj, objtype=None): - print("in __get__") - if obj is None: - return self - if self.fget is None: - raise AttributeError - return self.fget(obj) - - def __set__(self, obj, value): - print("in __set__") - if self.fset is None: - raise AttributeError - self.fset(obj, value) - - def __delete__(self, obj): - print("in __delete__") - if self.fdel is None: - raise AttributeError - self.fdel(obj) - - - def getter(self, fget): - print("in getter") - return type(self)(fget, self.fset, self.fdel, self.__doc__) - - def setter(self, fset): - print("in setter") - return type(self)(self.fget, fset, self.fdel, self.__doc__) - - def deleter(self, fdel): - print("in deleter") - return type(self)(self.fget, self.fset, fdel, self.__doc__) +``` +python3 -m pip install chardet ``` -然后 Student 类,我们也相应改成如下 +chardet 有一个 detect 方法,可以 `预测`其其编码格式 ```python -class Student: - def __init__(self, name): - self.name = name - - # 其实只有这里改变 - @TestProperty - def math(self): - return self._math - - @math.setter - def math(self, value): - if 0 <= value <= 100: - self._math = value - else: - raise ValueError("Valid value must be in [0, 100]") +>>> import chardet +>>> chardet.detect('微信公众号:Python编程时光'.encode('gbk')) +{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} ``` +为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence 字段,其表示预测的可信度,或者说成功率。 - -为了尽量让你少产生一点疑惑,我这里做两点说明: - -1. 使用`TestProperty`装饰后,`math` 不再是一个函数,而是`TestProperty` 类的一个实例。所以第二个math函数可以使用 `math.setter` 来装饰,本质是调用`TestProperty.setter` 来产生一个新的 `TestProperty` 实例赋值给第二个`math`。 - -2. 第一个 `math` 和第二个 `math` 是两个不同 `TestProperty` 实例。但他们都属于同一个描述符类(TestProperty),当对 math 对于赋值时,就会进入 `TestProperty.__set__`,当对math 进行取值里,就会进入 `TestProperty.__get__`。仔细一看,其实最终访问的还是Student实例的 `_math` 属性。 - -说了这么多,还是运行一下,更加直观一点。 +但是使用它时,若你的字符数较少,就有可能 “`误诊`”),比如只有 `中文` 两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成 KOI8-R 编码。 ```python -# 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math -in setter +>>> str_obj = "中文" +>>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes >>> ->>> s1.math = 90 -in __set__ ->>> s1.math -in __get__ -90 +>>> chardet.detect(byte_obj) +{'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'} +>>> +>>> str_obj2 = str(byte_obj, encoding='KOI8-R') +>>> str_obj2 +'жпнд' ``` -对于以上理解 `property` 的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。 +所以为了编码诊断的准确,要尽量使用足够多的字符。 -### 4. 基于描述符如何实现staticmethod +chardet 支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html) -说完了 `property` ,这里再来讲讲 `@classmethod` 和 `@staticmethod` 的实现原理。 +![](http://image.iswbm.com/20200423185819.png) -我这里定义了一个类,用了两种方式来实现静态方法。 -```python -class Test: - @staticmethod - def myfunc(): - print("hello") - -# 上下两种写法等价 - -class Test: - def myfunc(): - print("hello") - # 重点:这就是描述符的体现 - myfunc = staticmethod(myfunc) -``` -这两种写法是等价的,就好像在 `property` 一样,其实以下两种写法也是等价的。 +## 4. 编码与解码的区别 -```python -@TestProperty -def math(self): - return self._math - -math = TestProperty(fget=math) -``` +编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2 已经远去,这里以及后面都只用 Python 3 举例) -话题还是转回到 `staticmethod` 这边来吧。 +- **编码**:encode 方法,把字符串对象转化为二进制字节序列 -由上面的注释,可以看出 `staticmethod` 其实就相当于一个描述符类,而`myfunc` 在此刻变成了一个描述符。关于 `staticmethod` 的实现,你可以参照下面这段我自己写的代码,加以理解。 +- **解码**:decode 方法,把二进制字节序列转化为字符串对象 -![](http://image.iswbm.com/20190519001930.png) +![](http://image.iswbm.com/20200423190331.png) -调用这个方法可以知道,每调用一次,它都会经过描述符类的 `__get__` 。 -```python ->>> Test.myfunc() -in staticmethod __get__ -hello ->>> Test().myfunc() -in staticmethod __get__ -hello -``` -### 5. 基于描述符如何实现classmethod +那么假如我们真知道了其编码格式,如何来转成 unicode 呢? -同样的 ` classmethod` 也是一样。 - -```python -class classmethod(object): - def __init__(self, f): - self.f = f - - def __get__(self, instance, owner=None): - print("in classmethod __get__") - - def newfunc(*args): - return self.f(owner, *args) - return newfunc - -class Test: - def myfunc(cls): - print("hello") - - # 重点:这就是描述符的体现 - myfunc = classmethod(myfunc) -``` +**有两种方法** -验证结果如下 +**第一种**是,直接使用 decode 方法 ```python ->>> Test.myfunc() -in classmethod __get__ -hello ->>> Test().myfunc() -in classmethod __get__ -hello +>>> byte_obj.decode('gbk') +'中文' +>>> ``` -讲完了 `property`、`staticmethod`和`classmethod` 与 描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 super 的实现原理,就交由你来自己完成。 - -### 6. 所有实例共享描述符 - -通过以上内容的学习,你是不是觉得自己已经对描述符足够了解了呢? - -可在这里,我想说以上的描述符代码都有问题。 - -问题在哪里呢?请看下面这个例子。 +**第二种**是,使用 str 类来转 ```python -class Score: - def __init__(self, default=0): - self._value = default +>>> str_obj = str(byte_obj, encoding='gbk') +>>> str_obj +'中文' +>>> +``` - def __get__(self, instance, owner): - return self._value - def __set__(self, instance, value): - if 0 <= value <= 100: - self._value = value - else: - raise ValueError +## 5. 如何设置文件编码 -class Student: - math = Score(0) - chinese = Score(0) - english = Score(0) +在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python 2 的时候,如果你的 python 文件里有中文,运行是会报错的。 - def __repr__(self): - return "".format(self.math, self.chinese, self.english) ``` - -Student 里没有像前面那样写了构造函数,但是关键不在这儿,没写只是因为没必要写。 - -然后来看一下会出现什么样的问题呢 - -```python ->>> std1 = Student() ->>> std1 - ->>> std1.math = 85 ->>> std1 - ->>> std2 = Student() ->>> std2 # std2 居然共享了std1 的属性值 - ->>> std2.math = 100 ->>> std1 # std2 也会改变std1 的属性值 - +SyntaxError: Non-ASCII character '\xe4' in file demo.py ``` -从结果上来看,std2 居然共享了 std1 的属性值,只要其中一个实例的变量发生改变,另一个实例的变量也会跟着改变。 +原因就是 ASCII 编码表太小,无法解释中文。 -探其根因,是由于此时 math,chinese,english 三个全部是类变量,导致 std2 和 std1 在访问 math,chinese,english 这三个变量时,其实都是访问类变量。 +而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。 -问题是不是来了?小明和小强的分数怎么可能是绑定的呢?这很明显与实际业务不符。 +对于这个问题,通常解决方法有两种: -使用描述符给我们制造了便利,却无形中给我们带来了麻烦,难道这也是描述符的特性吗? +**第一种方法** -描述符是个很好用的特性,会出现这个问题,是由于我们之前写的描述符代码都是错误的。 +在 python2 中,可以使用在头部指定 -描述符的机制,在我看来,只是抢占了访问顺序,而具体的逻辑却要因地制宜,视情况而定。 +可以这样写,虽然很好看 -如果要把 math,chinese,english 这三个变量变成实例之间相互隔离的属性,应该这么写。 - -```python -class Score: - def __init__(self, subject): - self.name = subject +``` +# -*- coding: utf-8 -*- +``` - def __get__(self, instance, owner): - return instance.__dict__[self.name] +但这样写太麻烦了,我通常使用下面两种写法 - def __set__(self, instance, value): - if 0 <= value <= 100: - instance.__dict__[self.name] = value - else: - raise ValueError +``` +# coding:utf-8 +# coding=utf-8 +``` -class Student: - math = Score("math") - chinese = Score("chinese") - english = Score("english") - def __init__(self, math, chinese, english): - self.math = math - self.chinese = chinese - self.english = english +**第二种方法** - def __repr__(self): - return "".format(self.math, self.chinese, self.english) ``` +import sys -引导程序逻辑进入描述符之后,不管你是获取属性,还是设置属性,都是直接作用于 instance 的。 - -![](http://image.iswbm.com/20200812085823.png) - -这段代码,你可以仔细和前面的对比一下。 +reload(sys) +sys.setdefaultencoding('utf-8') +``` -不难看出: +这里在调用sys.setdefaultencoding(‘utf-8’) 设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除 sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用 sys.setdefaultencoding 这个方法。 -- 之前的错误代码,更像是把描述符当做了存储节点。 -- 之后的正确代码,则是把描述符直接当做代理,本身不存储值。 -以上便是我对描述符的全部分享,希望能对你有所帮助。 +## 6. 参考文章 -## 参考文档 -- [Python描述器引导(翻译)](https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html#python) +- [阮一峰老师文章的常识性错误之 Unicode 与 UTF-8](https://foofish.net/unicode_utf-8.html) +- [Strings, Bytes, and Unicode in Python 2 and 3](https://timothybramlett.com/Strings_Bytes_and_Unicode_in_Python_2_and_3.html) +- [字符编码笔记:ASCII,Unicode 和 UTF-8](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) +--- ---- -![](http://image.iswbm.com/20200607174235.png) +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_17.rst b/source/c01/c01_17.rst index 8875596..b8f6202 100644 --- a/source/c01/c01_17.rst +++ b/source/c01/c01_17.rst @@ -1,655 +1,295 @@ -1.17 深入理解「描述符」 -======================= +1.17 详解 Python 中的编码问题 +============================= |image0| -学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, -Descriptor(描述符)特性可以排得上号。 +Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 +Python +开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 -描述符 是Python -语言独有的特性,它不仅在应用层使用,在语言语法糖的实现上也有使用到(在下面的文章会一一介绍)。 +一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 +unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 +里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式? -当你点进这篇文章时 +反反复复,这个过程真是太痛苦了。 -- 你也许没学过描述符,甚至没听过描述符。 -- 或者你对描述符只是一知半解 +今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再 +Google,收藏这篇文章就行。 -无论你是哪种,本篇都将带你全面的学习描述符,一起来感受 Python -语言的优雅。 +1. Python 3 中 str 与 bytes +--------------------------- -1. 为什么要使用描述符? -~~~~~~~~~~~~~~~~~~~~~~~ +在 Python3中,字符串有两种类型 ,str 和 bytes。 -假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。 +今天就来说一说这二者的区别: -.. code:: python - - class Student: - def __init__(self, name, math, chinese, english): - self.name = name - self.math = math - self.chinese = chinese - self.english = english - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) - -看起来一切都很合理 - -.. code:: python - - >>> std1 = Student('小明', 76, 87, 68) - >>> std1 - - -但是程序并不像人那么智能,不会自动根据使用场景判断数据的合法性,如果老师在录入成绩的时候,不小心录入了将成绩录成了负数,或者超过100,程序是无法感知的。 - -聪明的你,马上在代码中加入了判断逻辑。 - -.. code:: python - - class Student: - def __init__(self, name, math, chinese, english): - self.name = name - if 0 <= math <= 100: - self.math = math - else: - raise ValueError("Valid value must be in [0, 100]") - - if 0 <= chinese <= 100: - self.chinese = chinese - else: - raise ValueError("Valid value must be in [0, 100]") - - if 0 <= chinese <= 100: - self.english = english - else: - raise ValueError("Valid value must be in [0, 100]") - - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) - -这下程序稍微有点人工智能了,能够自己明辨是非了。 - -|image1| - -程序是智能了,但在\ ``__init__``\ 里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过 -Property -特性,可以很好的应用在这里。于是你将代码修改成如下,代码的可读性瞬间提升了不少 - -.. code:: python - - class Student: - def __init__(self, name, math, chinese, english): - self.name = name - self.math = math - self.chinese = chinese - self.english = english - - @property - def math(self): - return self._math - - @math.setter - def math(self, value): - if 0 <= value <= 100: - self._math = value - else: - raise ValueError("Valid value must be in [0, 100]") - - @property - def chinese(self): - return self._chinese - - @chinese.setter - def chinese(self, value): - if 0 <= value <= 100: - self._chinese = value - else: - raise ValueError("Valid value must be in [0, 100]") - - @property - def english(self): - return self._english - - @english.setter - def english(self, value): - if 0 <= value <= 100: - self._english = value - else: - raise ValueError("Valid value must be in [0, 100]") - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) - -程序还是一样的人工智能,非常好。 - -|image2| - -你以为你写的代码,已经非常优秀,无懈可击了。 - -没想到,人外有天,你的主管看了你的代码后,深深地叹了口气:类里的三个属性,math、chinese、english,都使用了 -Property -对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100 -就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢,这代码简直没法忍。去了解一下 -Python 的描述符吧。 - -经过主管的指点,你知道了「描述符」这个东西。怀着一颗敬畏之心,你去搜索了下关于 -描述符的用法。 - -其实也很简单,一个实现了 ``描述符协议`` 的类就是一个描述符。 - -什么描述符协议:在类里实现了 -``__get__()``\ 、\ ``__set__()``\ 、\ ``__delete__()`` -其中至少一个方法。 - -- ``__get__``\ : - 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。 -- ``__set__``\ :将在属性分配操作中调用。不会返回任何内容。 -- ``__delete__``\ :控制删除操作。不会返回内容。 - -对描述符有了大概的了解后,你开始重写上面的方法。 - -如前所述,Score 类是一个描述符,当从 Student 的实例访问 -math、chinese、english这三个属性的时候,都会经过 Score -类里的三个特殊的方法。这里的 Score 避免了 使用Property -出现大量的代码无法复用的尴尬。 - -.. code:: python - - class Score: - def __init__(self, default=0): - self._score = default - - def __set__(self, instance, value): - if not isinstance(value, int): - raise TypeError('Score must be integer') - if not 0 <= value <= 100: - raise ValueError('Valid value must be in [0, 100]') - - self._score = value - - def __get__(self, instance, owner): - return self._score - - def __delete__(self): - del self._score - - class Student: - math = Score(0) - chinese = Score(0) - english = Score(0) - - def __init__(self, name, math, chinese, english): - self.name = name - self.math = math - self.chinese = chinese - self.english = english - - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese, self.english - ) - -实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等) - -|image3| - -以上,我举了下具体的实例,从最原始的编码风格到 Property -,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 - -到这里,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 -``保护属性不受修改``\ 、\ ``属性类型检查`` -的基本功能,同时有大大提高代码的复用率。 - -2. 描述符的访问规则 -~~~~~~~~~~~~~~~~~~~ +- ``unicode string(str 类型)``\ :以 Unicode code points + 形式存储,\ **人类认识的形式** +- ``byte string(bytes 类型)``\ :以 byte + 形式存储,\ **机器认识的形式** -描述符分两种: - -- 数据描述符:实现了\ ``__get__`` 和 ``__set__`` 两种方法的描述符 -- 非数据描述符:只实现了\ ``__get__`` 一种方法的描述符 - -你一定会问,他们有什么区别呢?网上的讲解,我看过几个,很多都把一个简单的东西讲得复杂了。 - -其实就一句话,\ **数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**\ 。 - -如果实例字典中有与描述符同名的属性,如果描述符是数据描述符,优先使用数据描述符,如果是非数据描述符,优先使用字典中的属性。 - -这边还是以上节的成绩管理的例子来说明,方便你理解。 +在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 ``type`` +和 ``isinstance`` 可以判别 .. code:: python - # 数据描述符 - class DataDes: - def __init__(self, default=0): - self._score = default - - def __set__(self, instance, value): - self._score = value - - def __get__(self, instance, owner): - print("访问数据描述符里的 __get__") - return self._score + # python3 - # 非数据描述符 - class NoDataDes: - def __init__(self, default=0): - self._score = default - - def __get__(self, instance, owner): - print("访问非数据描述符里的 __get__") - return self._score - - - class Student: - math = DataDes(0) - chinese = NoDataDes(0) - - def __init__(self, name, math, chinese): - self.name = name - self.math = math - self.chinese = chinese - - def __getattribute__(self, item): - print("调用 __getattribute__") - return super(Student, self).__getattribute__(item) - - def __repr__(self): - return "".format( - self.name, self.math, self.chinese) + >>> str_obj = "你好" + >>> + >>> type(str_obj) + + >>> + >>> isinstance("你好", str) + True + >>> + >>> isinstance("你好", bytes) + False + >>> -需要注意的是,math 是数据描述符,而 chinese -是非数据描述符。从下面的验证中,可以看出,当实例属性和数据描述符同名时,会优先访问数据描述符(如下面的math),而当实例属性和非数据描述符同名时,会优先访问实例属性(\ ``__getattribute__``\ ) +而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个 +``b``\ ,就表示你要定义一个 bytes 类型的字符串对象。 .. code:: python - >>> std = Student('xm', 88, 99) + # python3 + >>> byte_obj = b"Hello World!" + >>> type(byte_obj) + + >>> + >>> isinstance(byte_obj, str) + False + >>> + >>> isinstance(byte_obj, bytes) + True >>> - >>> std.math - 调用 __getattribute__ - 访问数据描述符里的 __get__ - 88 - >>> std.chinese - 调用 __getattribute__ - 99 - -讲完了数据描述符和非数据描述符,我们还需要了解的对象属性的查找规律。 - -当我们对一个实例属性进行访问时,Python 会按 ``obj.__dict__`` → -``type(obj).__dict__`` → ``type(obj)的父类.__dict__`` -顺序进行查找,如果查找到目标属性并发现是一个描述符,Python -会调用描述符协议来改变默认的控制行为。 - -3. 基于描述符如何实现property -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。 - -正常人所见过的描述符的用法就是上面提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 -Python 的特性的底层实现机制都是基于 ``描述符协议`` -的,比如我们熟悉的\ ``@property`` 、\ ``@classmethod`` -、\ ``@staticmethod`` 和 ``super`` 等。 - -先来说说 ``property`` 吧。 -有了前面的基础,我们知道了 property -的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。 +但是在定义中文字符串时,你就不能直接在前面加 ``b`` 了,而应该使用 +``encode`` 转一下。 .. code:: python - class Student: - def __init__(self, name): - self.name = name - - @property - def math(self): - return self._math + >>> byte_obj=b"你好" + File "", line 1 + SyntaxError: bytes can only contain ASCII literal characters. + >>> + >>> str_obj="你好" + >>> + >>> str_obj.encode("utf-8") + b'\xe4\xbd\xa0\xe5\xa5\xbd' + >>> - @math.setter - def math(self, value): - if 0 <= value <= 100: - self._math = value - else: - raise ValueError("Valid value must be in [0, 100]") +2. Python 2 中 str 与 unicode +----------------------------- -不防再简单回顾一下它的用法,通过property装饰的函数,如例子中的 math -会变成 Student 实例的属性。而对 math 属性赋值会进入 使用 ``math.setter`` -装饰函数的逻辑代码块。 +而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。 -为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入 -property -的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。 +在 Python2 里,字符串也只有两种类型,unicode 和 str 。 -不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。 +只有 unicode object 和 非unicode object(其实应该叫 str object) +的区别: -这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类 -``property`` 特性。 +- ``unicode string(unicode类型)``\ :以 Unicode code points + 形式存储,\ **人类认识的形式** +- ``byte string(str 类型)``\ :以 byte 形式存储,\ **机器认识的形式** -代码如下: +当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str +字符串对象,比如这样 .. code:: python - class TestProperty(object): - - def __init__(self, fget=None, fset=None, fdel=None, doc=None): - self.fget = fget - self.fset = fset - self.fdel = fdel - self.__doc__ = doc - - def __get__(self, obj, objtype=None): - print("in __get__") - if obj is None: - return self - if self.fget is None: - raise AttributeError - return self.fget(obj) + # python2 - def __set__(self, obj, value): - print("in __set__") - if self.fset is None: - raise AttributeError - self.fset(obj, value) - - def __delete__(self, obj): - print("in __delete__") - if self.fdel is None: - raise AttributeError - self.fdel(obj) - - - def getter(self, fget): - print("in getter") - return type(self)(fget, self.fset, self.fdel, self.__doc__) - - def setter(self, fset): - print("in setter") - return type(self)(self.fget, fset, self.fdel, self.__doc__) - - def deleter(self, fdel): - print("in deleter") - return type(self)(self.fget, self.fset, fdel, self.__doc__) + >>> str_obj="你好" + >>> + >>> type(str_obj) + + >>> + >>> str_obj + '\xe4\xbd\xa0\xe5\xa5\xbd' + >>> + >>> isinstance(str_obj, bytes) + True + >>> isinstance(str_obj, str) + True + >>> isinstance(str_obj, unicode) + False + >>> + >>> str is bytes + True -然后 Student 类,我们也相应改成如下 +而当我们在双引号或单引号前面加个 ``u``\ ,就表明我们定义的是 unicode +字符串对象,比如这样 .. code:: python - class Student: - def __init__(self, name): - self.name = name - - # 其实只有这里改变 - @TestProperty - def math(self): - return self._math - - @math.setter - def math(self, value): - if 0 <= value <= 100: - self._math = value - else: - raise ValueError("Valid value must be in [0, 100]") - -为了尽量让你少产生一点疑惑,我这里做两点说明: - -1. 使用\ ``TestProperty``\ 装饰后,\ ``math`` - 不再是一个函数,而是\ ``TestProperty`` - 类的一个实例。所以第二个math函数可以使用 ``math.setter`` - 来装饰,本质是调用\ ``TestProperty.setter`` 来产生一个新的 - ``TestProperty`` 实例赋值给第二个\ ``math``\ 。 - -2. 第一个 ``math`` 和第二个 ``math`` 是两个不同 ``TestProperty`` - 实例。但他们都属于同一个描述符类(TestProperty),当对 math - 对于赋值时,就会进入 ``TestProperty.__set__``\ ,当对math - 进行取值里,就会进入 - ``TestProperty.__get__``\ 。仔细一看,其实最终访问的还是Student实例的 - ``_math`` 属性。 + # python2 -说了这么多,还是运行一下,更加直观一点。 - -.. code:: python - - # 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math - in setter + >>> unicode_obj = u"你好" + >>> + >>> unicode_obj + u'\u4f60\u597d' >>> - >>> s1.math = 90 - in __set__ - >>> s1.math - in __get__ - 90 + >>> type(unicode_obj) + + >>> + >>> isinstance(unicode_obj, bytes) + False + >>> isinstance(unicode_obj, str) + False + >>> + >>> isinstance(unicode_obj, unicode) + True -对于以上理解 ``property`` -的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。 +3. 如何检测对象的编码 +--------------------- -4. 基于描述符如何实现staticmethod -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +所有的字符,在 unicode +字符集中都有对应的编码值(英文叫做:\ ``code point``\ ) -说完了 ``property`` ,这里再来讲讲 ``@classmethod`` 和 ``@staticmethod`` -的实现原理。 +而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312 +等。 -我这里定义了一个类,用了两种方式来实现静态方法。 +也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。 -.. code:: python +那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码? - class Test: - @staticmethod - def myfunc(): - print("hello") +这时候就要介绍到一个 python 的库 – ``chardet`` ,使用它之前 需要先安装 - # 上下两种写法等价 +:: - class Test: - def myfunc(): - print("hello") - # 重点:这就是描述符的体现 - myfunc = staticmethod(myfunc) + python3 -m pip install chardet -这两种写法是等价的,就好像在 ``property`` -一样,其实以下两种写法也是等价的。 +chardet 有一个 detect 方法,可以 ``预测``\ 其其编码格式 .. code:: python - @TestProperty - def math(self): - return self._math - - math = TestProperty(fget=math) - -话题还是转回到 ``staticmethod`` 这边来吧。 + >>> import chardet + >>> chardet.detect('微信公众号:Python编程时光'.encode('gbk')) + {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} -由上面的注释,可以看出 ``staticmethod`` -其实就相当于一个描述符类,而\ ``myfunc`` 在此刻变成了一个描述符。关于 -``staticmethod`` 的实现,你可以参照下面这段我自己写的代码,加以理解。 +为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence +字段,其表示预测的可信度,或者说成功率。 -|image4| - -调用这个方法可以知道,每调用一次,它都会经过描述符类的 ``__get__`` 。 +但是使用它时,若你的字符数较少,就有可能 “``误诊``”),比如只有 ``中文`` +两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成 +KOI8-R 编码。 .. code:: python - >>> Test.myfunc() - in staticmethod __get__ - hello - >>> Test().myfunc() - in staticmethod __get__ - hello - -5. 基于描述符如何实现classmethod -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -同样的 ``classmethod`` 也是一样。 - -.. code:: python + >>> str_obj = "中文" + >>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes + >>> + >>> chardet.detect(byte_obj) + {'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'} + >>> + >>> str_obj2 = str(byte_obj, encoding='KOI8-R') + >>> str_obj2 + 'жпнд' - class classmethod(object): - def __init__(self, f): - self.f = f +所以为了编码诊断的准确,要尽量使用足够多的字符。 - def __get__(self, instance, owner=None): - print("in classmethod __get__") - - def newfunc(*args): - return self.f(owner, *args) - return newfunc +chardet +支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html) - class Test: - def myfunc(cls): - print("hello") - - # 重点:这就是描述符的体现 - myfunc = classmethod(myfunc) +|image1| -验证结果如下 +4. 编码与解码的区别 +------------------- -.. code:: python +编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2 +已经远去,这里以及后面都只用 Python 3 举例) - >>> Test.myfunc() - in classmethod __get__ - hello - >>> Test().myfunc() - in classmethod __get__ - hello +- **编码**\ :encode 方法,把字符串对象转化为二进制字节序列 -讲完了 ``property``\ 、\ ``staticmethod``\ 和\ ``classmethod`` 与 -描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 -super 的实现原理,就交由你来自己完成。 +- **解码**\ :decode 方法,把二进制字节序列转化为字符串对象 -6. 所有实例共享描述符 -~~~~~~~~~~~~~~~~~~~~~ +|image2| -通过以上内容的学习,你是不是觉得自己已经对描述符足够了解了呢? +那么假如我们真知道了其编码格式,如何来转成 unicode 呢? -可在这里,我想说以上的描述符代码都有问题。 +**有两种方法** -问题在哪里呢?请看下面这个例子。 +**第一种**\ 是,直接使用 decode 方法 .. code:: python - class Score: - def __init__(self, default=0): - self._value = default - - def __get__(self, instance, owner): - return self._value - - def __set__(self, instance, value): - if 0 <= value <= 100: - self._value = value - else: - raise ValueError - - - class Student: - math = Score(0) - chinese = Score(0) - english = Score(0) - - def __repr__(self): - return "".format(self.math, self.chinese, self.english) - -Student -里没有像前面那样写了构造函数,但是关键不在这儿,没写只是因为没必要写。 + >>> byte_obj.decode('gbk') + '中文' + >>> -然后来看一下会出现什么样的问题呢 +**第二种**\ 是,使用 str 类来转 .. code:: python - >>> std1 = Student() - >>> std1 - - >>> std1.math = 85 - >>> std1 - - >>> std2 = Student() - >>> std2 # std2 居然共享了std1 的属性值 - - >>> std2.math = 100 - >>> std1 # std2 也会改变std1 的属性值 - - -从结果上来看,std2 居然共享了 std1 -的属性值,只要其中一个实例的变量发生改变,另一个实例的变量也会跟着改变。 + >>> str_obj = str(byte_obj, encoding='gbk') + >>> str_obj + '中文' + >>> -探其根因,是由于此时 math,chinese,english 三个全部是类变量,导致 std2 -和 std1 在访问 math,chinese,english 这三个变量时,其实都是访问类变量。 +5. 如何设置文件编码 +------------------- -问题是不是来了?小明和小强的分数怎么可能是绑定的呢?这很明显与实际业务不符。 +在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python +2 的时候,如果你的 python 文件里有中文,运行是会报错的。 -使用描述符给我们制造了便利,却无形中给我们带来了麻烦,难道这也是描述符的特性吗? +:: -描述符是个很好用的特性,会出现这个问题,是由于我们之前写的描述符代码都是错误的。 + SyntaxError: Non-ASCII character '\xe4' in file demo.py -描述符的机制,在我看来,只是抢占了访问顺序,而具体的逻辑却要因地制宜,视情况而定。 +原因就是 ASCII 编码表太小,无法解释中文。 -如果要把 math,chinese,english -这三个变量变成实例之间相互隔离的属性,应该这么写。 +而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。 -.. code:: python +对于这个问题,通常解决方法有两种: - class Score: - def __init__(self, subject): - self.name = subject +**第一种方法** - def __get__(self, instance, owner): - return instance.__dict__[self.name] +在 python2 中,可以使用在头部指定 - def __set__(self, instance, value): - if 0 <= value <= 100: - instance.__dict__[self.name] = value - else: - raise ValueError +可以这样写,虽然很好看 +:: - class Student: - math = Score("math") - chinese = Score("chinese") - english = Score("english") + # -*- coding: utf-8 -*- - def __init__(self, math, chinese, english): - self.math = math - self.chinese = chinese - self.english = english +但这样写太麻烦了,我通常使用下面两种写法 - def __repr__(self): - return "".format(self.math, self.chinese, self.english) +:: -引导程序逻辑进入描述符之后,不管你是获取属性,还是设置属性,都是直接作用于 -instance 的。 + # coding:utf-8 + # coding=utf-8 -|image5| +**第二种方法** -这段代码,你可以仔细和前面的对比一下。 +:: -不难看出: + import sys -- 之前的错误代码,更像是把描述符当做了存储节点。 -- 之后的正确代码,则是把描述符直接当做代理,本身不存储值。 + reload(sys) + sys.setdefaultencoding('utf-8') -以上便是我对描述符的全部分享,希望能对你有所帮助。 +这里在调用sys.setdefaultencoding(‘utf-8’) +设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除 +sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用 +sys.setdefaultencoding 这个方法。 -参考文档 --------- +6. 参考文章 +----------- -- `Python描述器引导(翻译) `__ +- `阮一峰老师文章的常识性错误之 Unicode 与 + UTF-8 `__ +- `Strings, Bytes, and Unicode in Python 2 and + 3 `__ +- `字符编码笔记:ASCII,Unicode 和 + UTF-8 `__ -------------- -|image6| +|image3| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20190425221322.png -.. |image2| image:: http://image.iswbm.com/20190425221322.png -.. |image3| image:: http://image.iswbm.com/20190425221233.png -.. |image4| image:: http://image.iswbm.com/20190519001930.png -.. |image5| image:: http://image.iswbm.com/20200812085823.png -.. |image6| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200423185819.png +.. |image2| image:: http://image.iswbm.com/20200423190331.png +.. |image3| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_33.md b/source/c01/c01_19.md similarity index 96% rename from source/c01/c01_33.md rename to source/c01/c01_19.md index d5acadd..8d3745b 100644 --- a/source/c01/c01_33.md +++ b/source/c01/c01_19.md @@ -1,4 +1,4 @@ -# 1.33 如何调试已经运行中的程序 +# 1.19 如何调试已经运行中的程序 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c01/c01_33.rst b/source/c01/c01_19.rst similarity index 96% rename from source/c01/c01_33.rst rename to source/c01/c01_19.rst index b0eded2..65fc111 100644 --- a/source/c01/c01_33.rst +++ b/source/c01/c01_19.rst @@ -1,4 +1,4 @@ -1.33 如何调试已经运行中的程序 +1.19 如何调试已经运行中的程序 ============================= |image0| diff --git a/source/c01/c01_20.md b/source/c01/c01_20.md index bbf32c0..1e9fde1 100644 --- a/source/c01/c01_20.md +++ b/source/c01/c01_20.md @@ -1,106 +1,39 @@ -# 1.20 静态方法其实暗藏玄机 +# 1.20 在 CentOS 7.2 上安装 Python3.7 ![](http://image.iswbm.com/20200602135014.png) -静态方法,应该没人不知道吧? +首先下载 python3.7的源码包,然后解压 -它可以算是一个很基础、简单的知识点。 +```shell +$ cd ~ +$ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz +$ tar xf Python-3.7.1.tgz && cd Python-3.7.1 +``` -但是就是这样一个知识点,也有不少可以探究的东西。 +安装 一些依赖包 -不信我问你三个问题,你看能否答上来。 +```shell +$ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y +``` -1. Python2.x和3.x中,函数和方法的区分有什么不同? -2. 有了类/实例方法和普通函数,为什么还会有静态方法? -3. Python3.x 中,静态方法有几种写法? +编译安装 -如果你没能答上来,那你可以尝试在下文中寻找答案。 +```shell +$ ./configure +$ make +$ sudo make install +``` ---- +至此,你已经成功安装 了 Python3, pip3,setuptools -在 Python 2 中的函数和方法的区别,十分清晰,很好分辨。但在 Python3中,我却发现完全又是另一套准则。 +requests.get("https://www.baidu.com") -首先先来 Python2 的(以下在 Python2.7中测试通过) +``` +python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple +``` -![](http://image.iswbm.com/20190630111243.png) -可以得出结论: -1、普通函数(未定位在类里),都是函数。 -2、静态方法(@staticmethod),都是函数。 - -3、类方法(@classmethod),都是方法。 - -4、实例方法(首参数为self且非静态、非类方法的),都是方法。 - -你一定想说,类方法和实例方法,是方法没错呀,毕竟名字本身就有方法,普通函数是函数,我也理解呀。那静态方法,为什么不是方法而是函数呢? - -名字只是一个外在的表面称呼,你能说「赵铁男」就一定是汉子吗? - -我的理解是:方法是一种和对象(实例或者类)绑定后的函数。 - -类方法的首参是`cls`,调用时,无需显示传入。实例方法首参是self,调用时,也无需显示传入。 - -而静态方法,其实和普通函数没啥区别,唯一的区别就是,他定义的位置被放在了类里,做为类或者实例的一个函数。 - -那你肯定又要问了,既然静态方法和普通函数都是一样的,为什么要刻意将它放在类里呢? - -我上面说了,放在类里定义,就可以让它成为类的一个工具函数,这就像你身上随身携带了一把刀,这把刀与你本人并没有什么直接关系,唯一的联系就是这把刀是你的,而你把它带在身上,无论你去到哪里,只要需要,你就可以直接拿出来用上。对比将函数放在类外面,缺点是什么呢?就是当你出门在外(在别的模块里)发现你要用刀的时候,还要特地跑一趟去商店买一把刀(import 这个函数)。 - -另外,我觉得静态方法在业务和设计上的意义,会更多一些。 - -一般静态方法是做为类或者实例的一个工具函数,比如对变量的做一个合法性的检验,对数据进行序列化反序列化操作等等。 - -说完了 Python2 ,再来说说Python3. - -以前我觉得 Python2 对于方法和函数的界线更加清晰,但接触了 Python3,我反而觉得Python3里方法和函数的区分似乎更加合理。 - -还是刚刚那段代码,我更改了解释器为Python3.6(以下在 Python3.6中测试通过) - -![](http://image.iswbm.com/20190630104956.png) - -和Python2的唯一区别是,`People.jump` 在Python3 中变成了函数。 - -这一下颠覆了你刚刚才建立起来的知识体系有木有? - -先别急,我再做个试验,也许你就知道了。 - -**在 Python2中** - -执行People.jump('hello'),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。 - -![](http://image.iswbm.com/20190630105735.png) - -**在 Python3中** - -你可以发现,这里的jump的首参不再要求是 People 的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。 - -![](http://image.iswbm.com/20190630105600.png) - -也就是说,当你往jump中传入的首参为People的实例时,jump 就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。 - -你看,多么灵活呀。 - -再总结一下,在Python3中: - -1、普通函数(未定位在类里),都是函数。 - -2、静态方法(@staticmethod),都是函数。 - -3、类方法(@classmethod),都是方法。 - -4、方法和函数区分没有那么明确,而是更加灵活了,一个函数有可能是方法也有可能是函数。 - -你肯定又要问了,那这是不是就意味着,Python3 中静态方法,可以不用再使用@staticmethod 装饰了呢,反正Python3都可以识别。 - -这是个好问题,是的,可以不用指定,但是最好指定,如果你不指定,你调用这个方法只能通过People.jump,而不能通过 self.jump了,因为首参不是 self,而如果使用@staticmethod 就可以使用self.jump。 - -所以说这是一个规范,就像类的私有方法,规范要求外部最好不要调用,但这不是强制要求,不是说外部就不能调用。 - -写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 - - - -![](http://image.iswbm.com/20200607174235.png) +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_20.rst b/source/c01/c01_20.rst index 5776837..843d931 100644 --- a/source/c01/c01_20.rst +++ b/source/c01/c01_20.rst @@ -1,122 +1,40 @@ -1.20 静态方法其实暗藏玄机 -========================= +1.20 在 CentOS 7.2 上安装 Python3.7 +=================================== |image0| -静态方法,应该没人不知道吧? +首先下载 python3.7的源码包,然后解压 -它可以算是一个很基础、简单的知识点。 +.. code:: shell -但是就是这样一个知识点,也有不少可以探究的东西。 + $ cd ~ + $ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz + $ tar xf Python-3.7.1.tgz && cd Python-3.7.1 -不信我问你三个问题,你看能否答上来。 +安装 一些依赖包 -1. Python2.x和3.x中,函数和方法的区分有什么不同? -2. 有了类/实例方法和普通函数,为什么还会有静态方法? -3. Python3.x 中,静态方法有几种写法? +.. code:: shell -如果你没能答上来,那你可以尝试在下文中寻找答案。 + $ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y --------------- +编译安装 -在 Python 2 中的函数和方法的区别,十分清晰,很好分辨。但在 -Python3中,我却发现完全又是另一套准则。 +.. code:: shell -首先先来 Python2 的(以下在 Python2.7中测试通过) + $ ./configure + $ make + $ sudo make install -|image1| - -可以得出结论: - -1、普通函数(未定位在类里),都是函数。 - -2、静态方法(@staticmethod),都是函数。 - -3、类方法(@classmethod),都是方法。 - -4、实例方法(首参数为self且非静态、非类方法的),都是方法。 - -你一定想说,类方法和实例方法,是方法没错呀,毕竟名字本身就有方法,普通函数是函数,我也理解呀。那静态方法,为什么不是方法而是函数呢? - -名字只是一个外在的表面称呼,你能说「赵铁男」就一定是汉子吗? - -我的理解是:方法是一种和对象(实例或者类)绑定后的函数。 - -类方法的首参是\ ``cls``\ ,调用时,无需显示传入。实例方法首参是self,调用时,也无需显示传入。 - -而静态方法,其实和普通函数没啥区别,唯一的区别就是,他定义的位置被放在了类里,做为类或者实例的一个函数。 - -那你肯定又要问了,既然静态方法和普通函数都是一样的,为什么要刻意将它放在类里呢? - -我上面说了,放在类里定义,就可以让它成为类的一个工具函数,这就像你身上随身携带了一把刀,这把刀与你本人并没有什么直接关系,唯一的联系就是这把刀是你的,而你把它带在身上,无论你去到哪里,只要需要,你就可以直接拿出来用上。对比将函数放在类外面,缺点是什么呢?就是当你出门在外(在别的模块里)发现你要用刀的时候,还要特地跑一趟去商店买一把刀(import -这个函数)。 - -另外,我觉得静态方法在业务和设计上的意义,会更多一些。 - -一般静态方法是做为类或者实例的一个工具函数,比如对变量的做一个合法性的检验,对数据进行序列化反序列化操作等等。 - -说完了 Python2 ,再来说说Python3. - -以前我觉得 Python2 对于方法和函数的界线更加清晰,但接触了 -Python3,我反而觉得Python3里方法和函数的区分似乎更加合理。 - -还是刚刚那段代码,我更改了解释器为Python3.6(以下在 -Python3.6中测试通过) - -|image2| +至此,你已经成功安装 了 Python3, pip3,setuptools -和Python2的唯一区别是,\ ``People.jump`` 在Python3 中变成了函数。 +requests.get(“https://www.baidu.com”) -这一下颠覆了你刚刚才建立起来的知识体系有木有? +:: -先别急,我再做个试验,也许你就知道了。 + python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple -**在 Python2中** - -执行People.jump(‘hello’),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。 - -|image3| - -**在 Python3中** - -你可以发现,这里的jump的首参不再要求是 People -的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。 - -|image4| - -也就是说,当你往jump中传入的首参为People的实例时,jump -就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。 - -你看,多么灵活呀。 - -再总结一下,在Python3中: - -1、普通函数(未定位在类里),都是函数。 - -2、静态方法(@staticmethod),都是函数。 - -3、类方法(@classmethod),都是方法。 - -4、方法和函数区分没有那么明确,而是更加灵活了,一个函数有可能是方法也有可能是函数。 - -你肯定又要问了,那这是不是就意味着,Python3 -中静态方法,可以不用再使用@staticmethod -装饰了呢,反正Python3都可以识别。 - -这是个好问题,是的,可以不用指定,但是最好指定,如果你不指定,你调用这个方法只能通过People.jump,而不能通过 -self.jump了,因为首参不是 self,而如果使用@staticmethod -就可以使用self.jump。 - -所以说这是一个规范,就像类的私有方法,规范要求外部最好不要调用,但这不是强制要求,不是说外部就不能调用。 - -写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 - -|image5| +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20190630111243.png -.. |image2| image:: http://image.iswbm.com/20190630104956.png -.. |image3| image:: http://image.iswbm.com/20190630105735.png -.. |image4| image:: http://image.iswbm.com/20190630105600.png -.. |image5| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md index 3114688..d6561cd 100644 --- a/source/c01/c01_21.md +++ b/source/c01/c01_21.md @@ -1,45 +1,6 @@ -# 1.21 开发小技巧 +# 1.21 Python 开发的几个小 Tips -![](http://image.iswbm.com/20200602135014.png) - -## 1. 解决网页鼠标限制 - -``` -解决网页不能选中,在console中输入:document.onselectstart=true -解决网页不能复制,在console中输入:document.oncopy=true -解决网页不能右键,在console中输入:document.oncontextmenu=true -``` - -## 2. 在 linux 上看 json 文件 - -``` -cat test.json | python -m json.tool -``` - -## 3. 在 Mac 上多开微信 - -``` -open -n /Applications/WeChat.app -``` - -## 3. 微信测试是否被删除 - -``` -1. 转账(不是好友不能点) -2. 拉群(不说话不会提示) -``` - -## 4. Mac 启动台 app 大小调整 - -``` -1.终端运行命令:10代表一行显示10个图标,几个可以自定义 -defaults write com.apple.dock springboard-columns -int 10 - -2.设置完需要重新启动一下 启动台 -killall Dock -``` - -## 5. 重定向标准输出到文件 +## 1. 重定向标准输出到文件 ``` import contextlib @@ -62,7 +23,7 @@ with close_stdout(): unshelve_task() ``` -## 6. 将子网掩码转换为cidr +## 2. 将子网掩码转换为cidr 如何使用netaddr库将ipv4子网掩码转换为cidr表示法? 示例:255.255.255.0到/ 24 @@ -83,12 +44,6 @@ with close_stdout(): 24 ``` -## 7. 在网页上为所欲为 - -```javascript -document.body.contentEditable='true' -``` -在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 Ctrl+Z 后退 。 ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index 11d87ce..97bab2d 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -1,51 +1,7 @@ -1.21 开发小技巧 -=============== +1.21 Python 开发的几个小 Tips +============================= -|image0| - -1. 解决网页鼠标限制 -------------------- - -:: - - 解决网页不能选中,在console中输入:document.onselectstart=true - 解决网页不能复制,在console中输入:document.oncopy=true - 解决网页不能右键,在console中输入:document.oncontextmenu=true - -2. 在 linux 上看 json 文件 --------------------------- - -:: - - cat test.json | python -m json.tool - -3. 在 Mac 上多开微信 --------------------- - -:: - - open -n /Applications/WeChat.app - -3. 微信测试是否被删除 ---------------------- - -:: - - 1. 转账(不是好友不能点) - 2. 拉群(不说话不会提示) - -4. Mac 启动台 app 大小调整 --------------------------- - -:: - - 1.终端运行命令:10代表一行显示10个图标,几个可以自定义 - defaults write com.apple.dock springboard-columns -int 10 - - 2.设置完需要重新启动一下 启动台 - killall Dock - -5. 重定向标准输出到文件 +1. 重定向标准输出到文件 ----------------------- :: @@ -69,7 +25,7 @@ with close_stdout(): unshelve_task() -6. 将子网掩码转换为cidr +2. 将子网掩码转换为cidr ----------------------- 如何使用netaddr库将ipv4子网掩码转换为cidr表示法? 示例:255.255.255.0到/ @@ -91,18 +47,7 @@ >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) 24 -7. 在网页上为所欲为 -------------------- - -.. code:: javascript - - document.body.contentEditable='true' - -在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 -Ctrl+Z 后退 。 - -|image1| +|image0| -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png +.. |image0| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_23.md b/source/c01/c01_23.md index 1c7b47f..08841b5 100644 --- a/source/c01/c01_23.md +++ b/source/c01/c01_23.md @@ -1,128 +1,140 @@ -# 1.23 Pythonista 学习 Js +# 1.48 Python 炫技操作:模块重载的五种方法 + +## 环境准备 + +新建一个 foo 文件夹,其下包含一个 bar.py 文件 + +``` +$ tree foo +foo +└── bar.py + +0 directories, 1 file +``` + +bar.py 的内容非常简单,只写了个 print 语句 + +``` +print("successful to be imported") +``` + +只要 bar.py 被导入一次,就被执行一次 print + +## 禁止重复导入 + +'由于有 sys.modules 的存在,当你导入一个已导入的模块时,实际上是没有效果的。' + +``` +>>> from foo import bar +successful to be imported +>>> from foo import bar +>>> +``` + +## 重复导入方法一 + +如果你使用的 python2(记得前面在 foo 文件夹下加一个 `__init__.py`),有一个 reload 的方法可以直接使用 + +``` +>>> from foo import bar +successful to be imported +>>> from foo import bar +>>> +>>> reload(bar) +successful to be imported + +``` + +如果你使用的 python3 那方法就多了,详细请看下面 + +## 重复导入方法二 + +如果你使用 Python3.0 -> 3.3,那么可以使用 imp.reload 方法 + +``` +>>> from foo import bar +successful to be imported +>>> from foo import bar +>>> +>>> import imp +>>> imp.reload(bar) +successful to be imported + +``` + +但是这个方法在 Python 3.4+,就不推荐使用了 + +``` +:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses +``` + +## 重复导入方法三 + +如果你使用的 Python 3.4+,请使用 importlib.reload 方法 + +``` +>>> from foo import bar +successful to be imported +>>> from foo import bar +>>> +>>> import importlib +>>> importlib.reload(bar) +successful to be imported + +``` + +## 重复导入方法四 + +如果你对包的加载器有所了解(详细可以翻阅我以前写的文章:https://iswbm.com/84.html) + +还可以使用下面的方法 + +``` +>>> from foo import bar +successful to be imported +>>> from foo import bar +>>> +>>> bar.__spec__.loader.load_module() +successful to be imported + +``` + +## 重复导入方法五 + +既然影响我们重复导入的是 sys.modules,那我们只要将已导入的包从其中移除是不是就好了呢? + +``` +>>> import foo.bar +successful to be imported +>>> +>>> import foo.bar +>>> +>>> import sys +>>> sys.modules['foo.bar'] + +>>> del sys.modules['foo.bar'] +>>> +>>> import foo.bar +successful to be imported +``` + +有没有发现在前面的例子里我使用的都是 `from foo import bar`,在这个例子里,却使用 `import foo.bar`,这是为什么呢? + +这是因为如果你使用 `from foo import bar` 这种方式,想使用移除 sys.modules 来重载模块这种方法是失效的。 + +这应该算是一个小坑,不知道的人,会掉入坑中爬不出来。 + +``` +>>> import foo.bar +successful to be imported +>>> +>>> import foo.bar +>>> +>>> import sys +>>> del sys.modules['foo.bar'] +>>> from foo import bar +>>> +``` + + -![](http://image.iswbm.com/20200602135014.png) - -1. JavaScript的设计者希望用`null`表示一个空的值,而`undefined`表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用`null`。`undefined`仅仅在判断函数参数是否传递的情况下有用。 - - - -2. 在JavaScript中,使用等号`=`对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用`var`申明一次。 - - - -3. var 申明的变量不是全局变量,如果不使用 var 申明变量,就会变成全局变量,多个js文件会共享这个变量,互相影响。这个语言设计弊端,在 ECMA 推出 strict 模式后得到解决,不使用 var 申明变量的会运行出错。 - - - -4. 直接操作数组的长度,或者对超出数组长度的索引赋值,可以扩容和压缩数组的大小。 - - - -5. 对象之间的比较和Python差异巨大,`=` 表示值的比较,`===` 表示对象类型的比较,主要分为下面三种情况: - - ```javascript - // 1. 对于string,number等【基础类型】,==和===是有区别的。 - // 如果是不同类型,== 会将两边转化成同一类型再看值是否相等 - // 如果是相同类型,直接对值进行比较。 - alert('1'==1);//结果是true - alert('1'===1);//结果是false - - - // 2. 如果是 Array,Object等高级类型,==和===是没有区别的。 - // 直接比较指针地址。,跟 Python 里的 is 等同。 - - - // 3. 基础类型与高级类型,==和===是有区别的。 - // 对于==,将高级转化为基础类型,进行“值”比较。 - // 因为类型不同,===结果为false。 - var a = new String('1');//定义一个string的高级类型 - var b = '1';//定一个基础类型字符串 - alert(b==a);//为true - alert(b===a);//为false - ``` - -6. 定义一个函数,其参数个数可以少于传入的参数个数,基于可以不定义参数,还可以传入参数。那传入的多余的参数如何获取呢?可以使用 arguments 这个变量取得所有的参数,但这个变量仅在参数内起作用。 - - ```python - function foo(a, b, ...rest) { - console.log('a = ' + a); - console.log('b = ' + b); - console.log(rest); - } - ``` - -7. JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但是并不会提前为变量赋值。 - - ```javascript - 'use strict'; - - function foo() { - var x = 'Hello, ' + y; - console.log(x); - var y = 'Bob'; - } - - foo(); - - // 对于 javascript 引擎,看到代码相当于 - function foo() { - var y; // 提升变量y的申明,此时y为undefined - var x = 'Hello, ' + y; - console.log(x); - y = 'Bob'; - } - ``` - - - -8. This有个巨坑,需要新手注意。 - - 如下,当没有 `var that = this;` 这句时,函数内部的函数里的this指向的是 window 对象。 - - ```javascript - 'use strict'; - - var xiaoming = { - name: '小明', - birth: 1990, - age: function () { - var that = this; // 在方法内部一开始就捕获this - function getAgeFromBirth() { - var y = new Date().getFullYear(); - return y - that.birth; // 用that而不是this - } - return getAgeFromBirth(); - } - }; - - xiaoming.age(); // 25 - ``` - - 或者,可以用 apply 的方法来解决这个问题,apply 的第一个参数是人该函数要绑定的对象,第二个参数是一个列表,装的是要传递给这个函数据参数。 - - ```javascript - function getAge() { - var y = new Date().getFullYear(); - return y - this.birth; - } - - var xiaoming = { - name: '小明', - birth: 1990, - age: getAge - }; - - xiaoming.age(); // 29 - getAge.apply(xiaoming, []); // 29, this指向xiaoming, 参数为空 - ``` - - 假如,不想绑定给任何对象,第一个参数可以用 null。如 - - ```javascript - Math.max.apply(null, [3, 5, 4]); // 5 - Math.max.call(null, 3, 5, 4); // 5 - ``` - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index 6b03b45..7876de7 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -1,127 +1,151 @@ -1.23 Pythonista 学习 Js -======================= +1.48 Python 炫技操作:模块重载的五种方法 +======================================== -|image0| +环境准备 +-------- -1. JavaScript的设计者希望用\ ``null``\ 表示一个空的值,而\ ``undefined``\ 表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用\ ``null``\ 。\ ``undefined``\ 仅仅在判断函数参数是否传递的情况下有用。 +新建一个 foo 文件夹,其下包含一个 bar.py 文件 -2. 在JavaScript中,使用等号\ ``=``\ 对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用\ ``var``\ 申明一次。 +:: -3. var 申明的变量不是全局变量,如果不使用 var - 申明变量,就会变成全局变量,多个js文件会共享这个变量,互相影响。这个语言设计弊端,在 - ECMA 推出 strict 模式后得到解决,不使用 var 申明变量的会运行出错。 + $ tree foo + foo + └── bar.py -4. 直接操作数组的长度,或者对超出数组长度的索引赋值,可以扩容和压缩数组的大小。 + 0 directories, 1 file -5. 对象之间的比较和Python差异巨大,\ ``=`` 表示值的比较,\ ``===`` - 表示对象类型的比较,主要分为下面三种情况: +bar.py 的内容非常简单,只写了个 print 语句 - .. code:: javascript +:: - // 1. 对于string,number等【基础类型】,==和===是有区别的。 - // 如果是不同类型,== 会将两边转化成同一类型再看值是否相等 - // 如果是相同类型,直接对值进行比较。 - alert('1'==1);//结果是true - alert('1'===1);//结果是false + print("successful to be imported") +只要 bar.py 被导入一次,就被执行一次 print - // 2. 如果是 Array,Object等高级类型,==和===是没有区别的。 - // 直接比较指针地址。,跟 Python 里的 is 等同。 +禁止重复导入 +------------ +‘由于有 sys.modules +的存在,当你导入一个已导入的模块时,实际上是没有效果的。’ - // 3. 基础类型与高级类型,==和===是有区别的。 - // 对于==,将高级转化为基础类型,进行“值”比较。 - // 因为类型不同,===结果为false。 - var a = new String('1');//定义一个string的高级类型 - var b = '1';//定一个基础类型字符串 - alert(b==a);//为true - alert(b===a);//为false +:: -6. 定义一个函数,其参数个数可以少于传入的参数个数,基于可以不定义参数,还可以传入参数。那传入的多余的参数如何获取呢?可以使用 - arguments 这个变量取得所有的参数,但这个变量仅在参数内起作用。 + >>> from foo import bar + successful to be imported + >>> from foo import bar + >>> - .. code:: python +重复导入方法一 +-------------- - function foo(a, b, ...rest) { - console.log('a = ' + a); - console.log('b = ' + b); - console.log(rest); - } +如果你使用的 python2(记得前面在 foo 文件夹下加一个 +``__init__.py``\ ),有一个 reload 的方法可以直接使用 -7. JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但是并不会提前为变量赋值。 +:: - .. code:: javascript + >>> from foo import bar + successful to be imported + >>> from foo import bar + >>> + >>> reload(bar) + successful to be imported + - 'use strict'; +如果你使用的 python3 那方法就多了,详细请看下面 - function foo() { - var x = 'Hello, ' + y; - console.log(x); - var y = 'Bob'; - } +重复导入方法二 +-------------- - foo(); +如果你使用 Python3.0 -> 3.3,那么可以使用 imp.reload 方法 - // 对于 javascript 引擎,看到代码相当于 - function foo() { - var y; // 提升变量y的申明,此时y为undefined - var x = 'Hello, ' + y; - console.log(x); - y = 'Bob'; - } +:: -8. This有个巨坑,需要新手注意。 + >>> from foo import bar + successful to be imported + >>> from foo import bar + >>> + >>> import imp + >>> imp.reload(bar) + successful to be imported + - 如下,当没有 ``var that = this;`` - 这句时,函数内部的函数里的this指向的是 window 对象。 +但是这个方法在 Python 3.4+,就不推荐使用了 - .. code:: javascript +:: - 'use strict'; + :1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses - var xiaoming = { - name: '小明', - birth: 1990, - age: function () { - var that = this; // 在方法内部一开始就捕获this - function getAgeFromBirth() { - var y = new Date().getFullYear(); - return y - that.birth; // 用that而不是this - } - return getAgeFromBirth(); - } - }; +重复导入方法三 +-------------- - xiaoming.age(); // 25 +如果你使用的 Python 3.4+,请使用 importlib.reload 方法 - 或者,可以用 apply 的方法来解决这个问题,apply - 的第一个参数是人该函数要绑定的对象,第二个参数是一个列表,装的是要传递给这个函数据参数。 +:: - .. code:: javascript + >>> from foo import bar + successful to be imported + >>> from foo import bar + >>> + >>> import importlib + >>> importlib.reload(bar) + successful to be imported + - function getAge() { - var y = new Date().getFullYear(); - return y - this.birth; - } +重复导入方法四 +-------------- - var xiaoming = { - name: '小明', - birth: 1990, - age: getAge - }; +如果你对包的加载器有所了解(详细可以翻阅我以前写的文章:https://iswbm.com/84.html) - xiaoming.age(); // 29 - getAge.apply(xiaoming, []); // 29, this指向xiaoming, 参数为空 +还可以使用下面的方法 - 假如,不想绑定给任何对象,第一个参数可以用 null。如 +:: - .. code:: javascript + >>> from foo import bar + successful to be imported + >>> from foo import bar + >>> + >>> bar.__spec__.loader.load_module() + successful to be imported + - Math.max.apply(null, [3, 5, 4]); // 5 - Math.max.call(null, 3, 5, 4); // 5 +重复导入方法五 +-------------- -|image1| +既然影响我们重复导入的是 +sys.modules,那我们只要将已导入的包从其中移除是不是就好了呢? -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png +:: + >>> import foo.bar + successful to be imported + >>> + >>> import foo.bar + >>> + >>> import sys + >>> sys.modules['foo.bar'] + + >>> del sys.modules['foo.bar'] + >>> + >>> import foo.bar + successful to be imported + +有没有发现在前面的例子里我使用的都是 +``from foo import bar``\ ,在这个例子里,却使用 +``import foo.bar``\ ,这是为什么呢? + +这是因为如果你使用 ``from foo import bar`` 这种方式,想使用移除 +sys.modules 来重载模块这种方法是失效的。 + +这应该算是一个小坑,不知道的人,会掉入坑中爬不出来。 + +:: + + >>> import foo.bar + successful to be imported + >>> + >>> import foo.bar + >>> + >>> import sys + >>> del sys.modules['foo.bar'] + >>> from foo import bar + >>> diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md index 8030d2a..4100dba 100644 --- a/source/c01/c01_24.md +++ b/source/c01/c01_24.md @@ -1,725 +1,148 @@ -# 1.24 深入探讨 Python 的 import 机制:实现远程导入模块 +# 1.37 Python 炫技操作:条件语句的七种写法 ![](http://image.iswbm.com/20200602135014.png) -所谓的模块导入( `import` ),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。 - -在 Python 中使用 import 关键字来实现这个操作,但不是唯一的方法,还有 `importlib.import_module()` 和 `__import__()` 等。 - -也许你看到这个标题,会说我怎么会发这么基础的文章? - -与此相反。恰恰我觉得这篇文章的内容可以算是 Python 的进阶技能,会深入地探讨并以真实案例讲解 Python import Hook 的知识点。 - -当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。 - -![](http://image.iswbm.com/20191027192949.png) - -## 1. 导入系统的基础 - -### 1.1 导入单元构成 - -导入单元有多种,可以是模块、包及变量等。 - -对于这些基础的概念,对于新手还是有必要介绍一下它们的区别。 - -**模块**:类似 \*.py,\*.pyc, \*.pyd ,\*.so,\*.dll 这样的文件,是 Python 代码载体的最小单元。 - -**包** 还可以细分为两种: - -- Regular packages:是一个带有 `__init__.py` 文件的文件夹,此文件夹下可包含其他子包,或者模块 -- Namespace packages - -关于 Namespace packages,有的人会比较陌生,我这里摘抄官方文档的一段说明来解释一下。 - -Namespace packages 是由多个 部分 构成的,每个部分为父包增加一个子包。 各个部分可能处于文件系统的不同位置。 部分也可能处于 zip 文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。 命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。 - -命名空间包的 `__path__ ` 属性不使用普通的列表。 而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 sys.path) 发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。 - -命名空间包没有 `parent/__init__.py` 文件。 实际上,在导入搜索期间可能找到多个 parent 目录,每个都由不同的部分所提供。 因此 parent/one 的物理位置不一定与 parent/two 相邻。 在这种情况下,Python 将为顶级的 parent 包创建一个命名空间包,无论是它本身还是它的某个子包被导入。 - - - -### 1.2 相对/绝对导入 - -当我们 import 导入模块或包时,Python 提供两种导入方式: - -- 相对导入(relative import ):from . import B 或 from ..A import B,其中.表示当前模块,..表示上层模块 -- 绝对导入(absolute import):import foo.bar 或者 from foo import bar - -你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6 之前),Python 默认使用的相对导入。而后来的版本中( Python2.6 之后),都以绝对导入为默认使用的导入方式。 - -使用绝对路径和相对路径各有利弊: - -- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。 -- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。 - -### 1.3 导入的标准写法 - -在 PEP8 中对模块的导入提出了要求,遵守 PEP8规范能让你的代码更具有可读性,我这边也列一下: - -- import 语句应当分行书写 - -```python -# bad -import os,sys - -# good -import os -import sys -``` - -- import语句应当使用absolute import - -```python -# bad -from ..bar import Bar - -# good -from foo.bar import test -``` - -- import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前 - -- import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列 - -```python -# 内置模块 -import os -import sys - -# 第三方模块 -import flask - -# 本地模块 -from foo import bar -``` - -### 1.4 几个有用的 sys 变量 - -`sys.path` 可以列出 Python 模块查找的目录列表 - -```python ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.path) -['', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', - '/Users/MING/Library/Python/3.6/lib/python/site-packages', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'] ->>> -``` - -`sys.meta_path` 存放的是所有的查找器。 - -```python ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.meta_path) -[, - , - ] -``` - - - -`sys.path_importer_cache` 比 `sys.path` 会更大点, 因为它会为所有被加载代码的目录记录它们的查找器。 这包括包的子目录,这些通常在 `sys.path` 中是不存在的。 - -```python ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.path_importer_cache) -{'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip': None, - '/Users/MING': FileFinder('/Users/MING'), - '/Users/MING/Library/Python/3.6/lib/python/site-packages': FileFinder('/Users/MING/Library/Python/3.6/lib/python/site-packages')} -``` - - - -## 2. \__import__ 的妙用 - -import 关键字的使用,可以说是基础中的基础。 - -但这不是模块唯一的方法,还有 `importlib.import_module()` 和 `__import__()` 等。 - -和 import 不同的是,`__import__` 是一个函数,也正是因为这个原因,使得 `__import__` 的使用会更加灵活,常常用于框架中,对于插件的动态加载。 - -实际上,当我们调用 import 导入模块时,其内部也是调用了 `__import__` ,请看如下两种导入方法,他们是等价的。 - -```python -# 使用 import -import os - -# 使用 __import__ -os = __import__('os') -``` - -通过举一反三,下面两种方法同样也是等价的。 +## 原代码 +这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。 ```python -# 使用 import .. as .. -import pandas as pd - -# 使用 __import__ -pd = __import__('pandas') +if age > 18: + return "已成年" +else: + return "未成年" ``` -上面我说 `__import__` 常常用于插件的动态,事实上也只有它能做到(相对于 import 来说)。 - -`插件`通常会位于某一特定的文件夹下,在使用过程中,可能你并不会用到全部的插件,也可能你会新增插件。 +下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外) -如果使用 import 关键字这种硬编码的方式,显然太不优雅了,当你要新增/修改插件的时候,都需要你修改代码。更合适的做法是,将这些插件以配置的方式,写在配置文件中,然后由代码去读取你的配置,动态导入你要使用的插件,即灵活又方便,也不容易出错。 +## 第一种 -假如我的一个项目中,有 `plugin01` 、`plugin02`、`plugin03 ` 、`plugin04` 四个插件,这些插件下都会实现一个核心方法 `run()` 。但有时候我不想使用全部的插件,只想使用 `plugin02`、`plugin04 ` ,那我就在配置文件中写我要使用的两个插件。 - -```shell -# my.conf -custom_plugins=['plugin02', 'plugin04'] -``` - -那我如何使用动态加载,并运行他们呢? +语法: ```python -# main.py - -for plugin in conf.custom_plugins: - __import__(plugin) - sys.modules[plugin].run() -``` - - - -## 3. 理解模块的缓存 - -在一个模块内部重复引用另一个相同模块,实际并不会导入两次,原因是在使用关键字 `import` 导入模块时,它会先检索 `sys.modules` 里是否已经载入这个模块了,如果已经载入,则不会再次导入,如果不存在,才会去检索导入这个模块。 - -来实验一下,在 `my_mod02` 这个模块里,我 import 两次 `my_mod01` 这个模块,按逻辑每一次 import 会一次 `my_mod01` 里的代码(即打印 `in mod01`),但是验证结果是,只打印了一次。 - -```shell -$ cat my_mod01.py -print('in mod01') - -$ cat my_mod02.py -import my_mod01 -import my_mod01 - -$ python my_mod02.py -in mod01 + if else ``` -该现象的解释是:因为有 `sys.modules` 的存在。 - -`sys.modules` 是一个字典(key:模块名,value:模块对象),它存放着在当前 namespace 所有已经导入的模块对象。 +例子 ```python -# test_module.py - -import sys -print(sys.modules.get('json', 'NotFound')) - -import json -print(sys.modules.get('json', 'NotFound')) -``` - -运行结果如下,可见在 导入后 json 模块后,`sys.modules` 才有了 json 模块的对象。 - -```shell -$ python test_module.py -NotFound - +>>> age1 = 20 +>>> age2 = 17 +>>> +>>> +>>> msg1 = "已成年" if age1 > 18 else "未成年" +>>> print msg1 +已成年 +>>> +>>> msg2 = "已成年" if age2 > 18 else "未成年" +>>> print msg2 +未成年 +>>> ``` - 由于有缓存的存在,使得我们无法重新载入一个模块。 +## 第二种 -但若你想反其道行之,可以借助 importlib 这个神奇的库来实现。事实也确实有此场景,比如在代码调试中,在发现代码有异常并修改后,我们通常要重启服务再次载入程序。这时候,若有了模块重载,就无比方便了,修改完代码后也无需服务的重启,就能继续调试。 - -还是以上面的例子来理解,`my_mod02.py` 改写成如下 +语法 ```python -# my_mod02.py - -import importlib -import my_mod01 -importlib.reload(my_mod01) -``` - -使用 python3 来执行这个模块,与上面不同的是,这边执行了两次 `my_mod01.py` - -```shell -$ python3 my_mod02.py -in mod01 -in mod01 + and or ``` - - -## 4. 查找器与加载器 - -如果指定名称的模块在 `sys.modules` 找不到,则将发起调用 Python 的导入协议以查找和加载该模块。 - -此协议由两个概念性模块构成,即 `查找器` 和 `加载器`。 - -一个 Python 的模块的导入,其实可以再细分为两个过程: - -1. 由查找器实现的模块查找 -2. 由加载器实现的模块加载 - -### 4.1 查找器是什么? - -查找器(finder),简单点说,查找器定义了一个模块查找机制,让程序知道该如何找到对应的模块。 - -其实 Python 内置了多个默认查找器,其存在于 sys.meta_path 中。 - -但这些查找器对应使用者来说,并不是那么重要,因此在 Python 3.3 之前, Python 解释将其隐藏了,我们称之为隐式查找器。 +例子 ```python -# Python 2.7 ->>> import sys ->>> sys.meta_path -[] +>>> msg1 = age1 > 18 and "已成年" or "未成年" +>>> msg2 = "已成年" if age2 > 18 else "未成年" >>> +>>> print(msg1) +已成年 +>>> +>>> print(msg2) +未成年 ``` -由于这点不利于开发者深入理解 import 机制,在 Python 3.3 后,所有的模块导入机制都会通过 sys.meta_path 暴露,不会在有任何隐式导入机制。 - -```python -# Python 3.6 ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.meta_path) -[, - , - ] -``` - -观察一下 Python 默认的这几种查找器 (finder),可以分为三种: - -- 一种知道如何导入内置模块 -- 一种知道如何导入冻结模块 -- 一种知道如何导入来自 [import path](https://docs.python.org/zh-cn/3/glossary.html#term-import-path) 的模块 (即 [path based finder](https://docs.python.org/zh-cn/3/glossary.html#term-path-based-finder))。 - -那我们能不能自已定义一个查找器呢?当然可以,你只要 - -- 定义一个实现了 find_module 方法的类(py2和py3均可),或者实现 find_loader 类方法(仅 py3 有效),如果找到模块需要返回一个 loader 对象或者 ModuleSpec 对象(后面会讲),没找到需要返回 None -- 定义完后,要使用这个查找器,必须注册它,将其插入在 sys.meta_path 的首位,这样就能优先使用。 - -```python -import sys - -class MyFinder(object): - @classmethod - def find_module(cls, name, path, target=None): - print("Importing", name, path, target) - # 将在后面定义 - return MyLoader() - -# 由于 finder 是按顺序读取的,所以必须插入在首位 -sys.meta_path.insert(0, MyFinder) -``` - -查找器可以分为两种: - -```shell -object - +-- Finder (deprecated) - +-- MetaPathFinder - +-- PathEntryFinder -``` - -这里需要注意的是,在 3.4 版前,查找器会直接返回 加载器(Loader)对象,而在 3.4 版后,查找器则会返回模块规格说明(ModuleSpec),其中 包含加载器。 - -而关于什么是 加载器 和 模块规格说明, 请继续往后看。 - -### 4.2 加载器是什么? - -查找器只负责查找定位找模,而真正负责加载模块的,是加载器(loader)。 - -一般的 loader 必须定义名为 ` load_module() ` 的方法。 - -为什么这里说一般,因为 loader 还分多种: - -```shell -object - +-- Finder (deprecated) - | +-- MetaPathFinder - | +-- PathEntryFinder - +-- Loader - +-- ResourceLoader --------+ - +-- InspectLoader | - +-- ExecutionLoader --+ - +-- FileLoader - +-- SourceLoader -``` - -通过查看源码可知,不同的加载器的抽象方法各有不同。 - - - -加载器通常由一个 finder 返回。详情参见 PEP 302,对于 abstract base class 可参见 importlib.abc.Loader。 - -那如何自定义我们自己的加载器呢? - -你只要 - -- 定义一个实现了 load_module 方法的类 -- 对与导入有关的属性([点击查看详情](https://docs.python.org/zh-cn/3/reference/import.html#import-related-module-attributes))进行校验 -- 创建模块对象并绑定所有与导入相关的属性变量到该模块上 -- 将此模块保存到 sys.modules 中(顺序很重要,避免递归导入) -- 然后加载模块(这是核心) -- 若加载出错,需要能够处理抛出异常( ImportError) -- 若加载成功,则返回 module 对象 - -若你想看具体的例子,可以接着往后看。 - -### 4.3 模块规格说明 - -导入机制在导入期间会使用有关每个模块的多种信息,特别是加载之前。 大多数信息都是所有模块通用的。 模块规格说明的目的是基于每个模块来封装这些导入相关信息。 - -模块的规格说明会作为模块对象的 `__spec__` 属性对外公开。 有关模块规格的详细内容请参阅 [`ModuleSpec`](https://docs.python.org/zh-cn/3/library/importlib.html#importlib.machinery.ModuleSpec)。 - -在 Python 3.4 后,查找器不再返回加载器,而是返回 ModuleSpec 对象,它储存着更多的信息 - -- 模块名 -- 加载器 -- 模块绝对路径 - -那如何查看一个模块的 ModuleSpec ? - -这边举个例子 - -```shell -$ cat my_mod02.py -import my_mod01 -print(my_mod01.__spec__) - -$ python3 my_mod02.py -in mod01 -ModuleSpec(name='my_mod01', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000000000392DBE0>, origin='/home/MING/my_mod01.py') -``` - -从 ModuleSpec 中可以看到,加载器是包含在内的,那我们如果要重新加载一个模块,是不是又有了另一种思路了? - -来一起验证一下。 +## 第三种 -现在有两个文件: - -一个是 my_info.py +语法 ```python -# my_info.py -name='wangbm' +(, )[condition] ``` - 另一个是:main.py +例子 ```python -# main.py -import my_info - -print(my_info.name) - -# 加一个断点 -import pdb;pdb.set_trace() - -# 再加载一次 -my_info.__spec__.loader.load_module() - -print(my_info.name) -``` - -在 `main.py` 处,我加了一个断点,目的是当运行到断点处时,我修改 my_info.py 里的 name 为 `ming` ,以便验证重载是否有效? - -```shell -$ python3 main.py -wangbm -> /home/MING/main.py(9)() --> my_info.__spec__.loader.load_module() -(Pdb) c -ming +>>> msg1 = ("未成年", "已成年")[age1 > 18] +>>> print(msg1) +已成年 +>>> +>>> +>>> msg2 = ("未成年", "已成年")[age2 > 18] +>>> print(msg2) +未成年 ``` -从结果来看,重载是有效的。 - - - -### 4.4 导入器是什么? - -导入器(importer),也许你在其他文章里会见到它,但其实它并不是个新鲜的东西。 +## 第四种 -它只是同时实现了查找器和加载器两种接口的对象,所以你可以说导入器(importer)是查找器(finder),也可以说它是加载器(loader)。 - - - -## 5. 远程导入模块 - -由于 Python 默认的 查找器和加载器 仅支持本地的模块的导入,并不支持实现远程模块的导入。 - -为了让你更好的理解 Python Import Hook 机制,我下面会通过实例演示,如何自己实现远程导入模块的导入器。 - -### 5.1 动手实现导入器 - -当导入一个包的时候,Python 解释器首先会从 sys.meta_path 中拿到查找器列表。 - -默认顺序是:内建模块查找器 -> 冻结模块查找器 -> 第三方模块路径(本地的 sys.path)查找器 - -若经过这三个查找器,仍然无法查找到所需的模块,则会抛出ImportError异常。 - - - -因此要实现远程导入模块,有两种思路。 - -- 一种是实现自己的元路径导入器; -- 另一种是编写一个钩子,添加到sys.path_hooks里,识别特定的目录命名模式。 - - - -我这里选择第一种方法来做为示例。 - -实现导入器,我们需要分别查找器和加载器。 - -**首先是查找器** - -由源码得知,路径查找器分为两种 - -- MetaPathFinder -- PathEntryFinder - -这里使用 MetaPathFinder 来进行查找器的编写。 - -在 Python 3.4 版本之前,查找器必须实现 `find_module()` 方法,而 Python 3.4+ 版,则推荐使用 `find_spec()` 方法,但这并不意味着你不能使用 `find_module()`,但是在没有 `find_spec()` 方法时,导入协议还是会尝试 `find_module()` 方法。 - -我先举例下使用 `find_module()` 该如何写。 +语法 ```python -from importlib import abc - -class UrlMetaFinder(abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - - def find_module(self, fullname, path=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - loader.load_module(fullname) - return loader - except Exception: - return None +(lambda: , lambda:)[]() ``` -若使用 `find_spec()` ,要注意此方法的调用需要带有两到三个参数。 - -第一个是被导入模块的完整限定名称,例如 `foo.bar.baz`。 第二个参数是供模块搜索使用的路径条目。 对于最高层级模块,第二个参数为 `None`,但对于子模块或子包,第二个参数为父包 `__path__` 属性的值。 如果相应的 `__path__` 属性无法访问,将引发 [`ModuleNotFoundError`](https://docs.python.org/zh-cn/3/library/exceptions.html#ModuleNotFoundError)。 第三个参数是一个将被作为稍后加载目标的现有模块对象。 导入系统仅会在重加载期间传入一个目标模块。 +例子 ```python -from importlib import abc -from importlib.machinery import ModuleSpec - -class UrlMetaFinder(abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - def find_spec(self, fullname, path=None, target=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - return ModuleSpec(fullname, loader, is_package=loader.is_package(fullname)) - except Exception: - return None +>>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() +>>> print(msg1) +已成年 +>>> +>>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() +>>> print(msg2) +未成年 ``` +## 第五种 - -**接下来是加载器** - -由源码得知,路径查找器分为三种 - -- FileLoader -- SourceLoader - -按理说,两种加载器都可以实现我们想要的功能,我这里选用 SourceLoader 来示范。 - -在 SourceLoader 这个抽象类里,有几个很重要的方法,在你写实现加载器的时候需要注意 - -- get_code:获取源代码,可以根据自己场景实现实现。 -- exec_module:执行源代码,并将变量赋值给 `module.__dict__` -- get_data:抽象方法,必须实现,返回指定路径的字节码。 -- get_filename:抽象方法,必须实现,返回文件名 - -在一些老的博客文章中,你会经常看到 加载器 要实现 `load_module()` ,而这个方法早已在 Python 3.4 的时候就被废弃了,当然为了兼容考虑,你若使用 `load_module()` 也是可以的。 +语法: ```python -from importlib import abc - -class UrlMetaLoader(abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def load_module(self, fullname): - code = self.get_code(fullname) - mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) - mod.__file__ = self.get_filename(fullname) - mod.__loader__ = self - mod.__package__ = fullname - exec(code, mod.__dict__) - return None - - def get_data(self): - pass - - def execute_module(self, module): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' +{True: , False: }[] ``` -当你使用这种旧模式实现自己的加载时,你需要注意两点,很重要: - -- execute_module 必须重载,而且不应该有任何逻辑,即使它并不是抽象方法。 -- load_module,需要你在查找器里手动执行,才能实现模块的加载。。 - -做为替换,你应该使用 `execute_module()` 和 `create_module()` 。由于基类里已经实现了 `execute_module` 和 `create_module()`,并且满足我们的使用场景。我这边可以不用重复实现。和旧模式相比,这里也不需要在设查找器里手动执行 `execute_module()`。 +例子: ```python -import urllib.request as urllib2 - -class UrlMetaLoader(importlib.abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def get_data(self): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' +>>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] +>>> print(msg1) +已成年 +>>> +>>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] +>>> print(msg2) +未成年 ``` -查找器和加载器都有了,别忘了往sys.meta_path 注册我们自定义的查找器(UrlMetaFinder)。 +## 第六种 -```python -def install_meta(address): - finder = UrlMetaFinder(address) - sys.meta_path.append(finder) -``` - -所有的代码都解析完毕后,我们将其整理在一个模块(my_importer.py)中 +语法 ```python -# my_importer.py -import sys -import importlib -import urllib.request as urllib2 - -class UrlMetaFinder(importlib.abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - - - def find_module(self, fullname, path=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - return loader - except Exception: - return None - -class UrlMetaLoader(importlib.abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def get_data(self): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' - -def install_meta(address): - finder = UrlMetaFinder(address) - sys.meta_path.append(finder) -``` - -### 5.2 搭建远程服务端 - -最开始我说了,要实现一个远程导入模块的方法。 - -我还缺一个在远端的服务器,来存放我的模块,为了方便,我使用python自带的 `http.server` 模块用一条命令即可实现。 - -```shell -$ mkdir httpserver && cd httpserver -$ cat>my_info.py) and (,) or (,))[0] ``` -一切准备好,我们就可以验证了。 +例子 ```python ->>> from my_importer import install_meta ->>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder ->>> import my_info # 打印ok,说明导入成功 -ok ->>> my_info.name # 验证可以取得到变量 -'wangbm' +>>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] +>>> print(msg1) +已成年 +>>> +>>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] +>>> print(msg2) +未成年 ``` -至此,我实现了一个简易的可以导入远程服务器上的模块的导入器。 - - - -## 参考文档 - -- https://docs.python.org/zh-cn/3/reference/import.html -- https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc -- https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html - +以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 +看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 -![](http://image.iswbm.com/20200607174235.png) +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index fae70f7..b8c6ab6 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -1,803 +1,165 @@ -1.24 深入探讨 Python 的 import 机制:实现远程导入模块 -===================================================== +1.37 Python 炫技操作:条件语句的七种写法 +======================================== |image0| -所谓的模块导入( ``import`` -),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。 +原代码 +------ -在 Python 中使用 import 关键字来实现这个操作,但不是唯一的方法,还有 -``importlib.import_module()`` 和 ``__import__()`` 等。 - -也许你看到这个标题,会说我怎么会发这么基础的文章? - -与此相反。恰恰我觉得这篇文章的内容可以算是 Python -的进阶技能,会深入地探讨并以真实案例讲解 Python import Hook 的知识点。 - -当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。 - -|image1| - -1. 导入系统的基础 ------------------ - -1.1 导入单元构成 -~~~~~~~~~~~~~~~~ - -导入单元有多种,可以是模块、包及变量等。 - -对于这些基础的概念,对于新手还是有必要介绍一下它们的区别。 - -**模块**\ :类似 \*.py,*.pyc, \*.pyd ,*.so,*.dll 这样的文件,是 -Python 代码载体的最小单元。 - -**包** 还可以细分为两种: - -- Regular packages:是一个带有 ``__init__.py`` - 文件的文件夹,此文件夹下可包含其他子包,或者模块 -- Namespace packages - -关于 Namespace -packages,有的人会比较陌生,我这里摘抄官方文档的一段说明来解释一下。 - -Namespace packages 是由多个 部分 构成的,每个部分为父包增加一个子包。 -各个部分可能处于文件系统的不同位置。 部分也可能处于 zip -文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。 -命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。 - -命名空间包的 ``__path__`` 属性不使用普通的列表。 -而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 sys.path) -发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。 - -命名空间包没有 ``parent/__init__.py`` 文件。 -实际上,在导入搜索期间可能找到多个 parent -目录,每个都由不同的部分所提供。 因此 parent/one 的物理位置不一定与 -parent/two 相邻。 在这种情况下,Python 将为顶级的 parent -包创建一个命名空间包,无论是它本身还是它的某个子包被导入。 - -1.2 相对/绝对导入 -~~~~~~~~~~~~~~~~~ - -当我们 import 导入模块或包时,Python 提供两种导入方式: - -- 相对导入(relative import ):from . import B 或 from ..A import - B,其中.表示当前模块,..表示上层模块 -- 绝对导入(absolute import):import foo.bar 或者 from foo import bar - -你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6 -之前),Python 默认使用的相对导入。而后来的版本中( Python2.6 -之后),都以绝对导入为默认使用的导入方式。 - -使用绝对路径和相对路径各有利弊: - -- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。 -- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。 - -1.3 导入的标准写法 -~~~~~~~~~~~~~~~~~~ - -在 PEP8 中对模块的导入提出了要求,遵守 -PEP8规范能让你的代码更具有可读性,我这边也列一下: - -- import 语句应当分行书写 +这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 +Python 功力。 .. code:: python - # bad - import os,sys - - # good - import os - import sys - -- import语句应当使用absolute import - -.. code:: python - - # bad - from ..bar import Bar - - # good - from foo.bar import test - -- import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前 - -- import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列 - -.. code:: python - - # 内置模块 - import os - import sys - - # 第三方模块 - import flask - - # 本地模块 - from foo import bar - -1.4 几个有用的 sys 变量 -~~~~~~~~~~~~~~~~~~~~~~~ - -``sys.path`` 可以列出 Python 模块查找的目录列表 - -.. code:: python - - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.path) - ['', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', - '/Users/MING/Library/Python/3.6/lib/python/site-packages', - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'] - >>> - -``sys.meta_path`` 存放的是所有的查找器。 - -.. code:: python - - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.meta_path) - [, - , - ] - -``sys.path_importer_cache`` 比 ``sys.path`` 会更大点, -因为它会为所有被加载代码的目录记录它们的查找器。 -这包括包的子目录,这些通常在 ``sys.path`` 中是不存在的。 - -.. code:: python - - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.path_importer_cache) - {'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'), - '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip': None, - '/Users/MING': FileFinder('/Users/MING'), - '/Users/MING/Library/Python/3.6/lib/python/site-packages': FileFinder('/Users/MING/Library/Python/3.6/lib/python/site-packages')} - -2. \__import_\_ 的妙用 ----------------------- - -import 关键字的使用,可以说是基础中的基础。 + if age > 18: + return "已成年" + else: + return "未成年" -但这不是模块唯一的方法,还有 ``importlib.import_module()`` 和 -``__import__()`` 等。 +下面我列举了六种这段代码的变异写法,一个比一个还 6 +,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?** +(除了第一种之外) -和 import 不同的是,\ ``__import__`` -是一个函数,也正是因为这个原因,使得 ``__import__`` -的使用会更加灵活,常常用于框架中,对于插件的动态加载。 +第一种 +------ -实际上,当我们调用 import 导入模块时,其内部也是调用了 ``__import__`` -,请看如下两种导入方法,他们是等价的。 +语法: .. code:: python - # 使用 import - import os + if else - # 使用 __import__ - os = __import__('os') - -通过举一反三,下面两种方法同样也是等价的。 +例子 .. code:: python - # 使用 import .. as .. - import pandas as pd - - # 使用 __import__ - pd = __import__('pandas') - -上面我说 ``__import__`` 常常用于插件的动态,事实上也只有它能做到(相对于 -import 来说)。 - -``插件``\ 通常会位于某一特定的文件夹下,在使用过程中,可能你并不会用到全部的插件,也可能你会新增插件。 - -如果使用 import -关键字这种硬编码的方式,显然太不优雅了,当你要新增/修改插件的时候,都需要你修改代码。更合适的做法是,将这些插件以配置的方式,写在配置文件中,然后由代码去读取你的配置,动态导入你要使用的插件,即灵活又方便,也不容易出错。 - -假如我的一个项目中,有 ``plugin01`` 、\ ``plugin02``\ 、\ ``plugin03`` -、\ ``plugin04`` 四个插件,这些插件下都会实现一个核心方法 ``run()`` -。但有时候我不想使用全部的插件,只想使用 ``plugin02``\ 、\ ``plugin04`` -,那我就在配置文件中写我要使用的两个插件。 - -.. code:: shell + >>> age1 = 20 + >>> age2 = 17 + >>> + >>> + >>> msg1 = "已成年" if age1 > 18 else "未成年" + >>> print msg1 + 已成年 + >>> + >>> msg2 = "已成年" if age2 > 18 else "未成年" + >>> print msg2 + 未成年 + >>> - # my.conf - custom_plugins=['plugin02', 'plugin04'] +第二种 +------ -那我如何使用动态加载,并运行他们呢? +语法 .. code:: python - # main.py - - for plugin in conf.custom_plugins: - __import__(plugin) - sys.modules[plugin].run() - -3. 理解模块的缓存 ------------------ - -在一个模块内部重复引用另一个相同模块,实际并不会导入两次,原因是在使用关键字 -``import`` 导入模块时,它会先检索 ``sys.modules`` -里是否已经载入这个模块了,如果已经载入,则不会再次导入,如果不存在,才会去检索导入这个模块。 + and or -来实验一下,在 ``my_mod02`` 这个模块里,我 import 两次 ``my_mod01`` -这个模块,按逻辑每一次 import 会一次 ``my_mod01`` 里的代码(即打印 -``in mod01``\ ),但是验证结果是,只打印了一次。 - -.. code:: shell - - $ cat my_mod01.py - print('in mod01') - - $ cat my_mod02.py - import my_mod01 - import my_mod01 - - $ python my_mod02.py - in mod01 - -该现象的解释是:因为有 ``sys.modules`` 的存在。 - -``sys.modules`` -是一个字典(key:模块名,value:模块对象),它存放着在当前 namespace -所有已经导入的模块对象。 +例子 .. code:: python - # test_module.py - - import sys - print(sys.modules.get('json', 'NotFound')) - - import json - print(sys.modules.get('json', 'NotFound')) - -运行结果如下,可见在 导入后 json 模块后,\ ``sys.modules`` 才有了 json -模块的对象。 - -.. code:: shell - - $ python test_module.py - NotFound - - -由于有缓存的存在,使得我们无法重新载入一个模块。 + >>> msg1 = age1 > 18 and "已成年" or "未成年" + >>> msg2 = "已成年" if age2 > 18 else "未成年" + >>> + >>> print(msg1) + 已成年 + >>> + >>> print(msg2) + 未成年 -但若你想反其道行之,可以借助 importlib -这个神奇的库来实现。事实也确实有此场景,比如在代码调试中,在发现代码有异常并修改后,我们通常要重启服务再次载入程序。这时候,若有了模块重载,就无比方便了,修改完代码后也无需服务的重启,就能继续调试。 +第三种 +------ -还是以上面的例子来理解,\ ``my_mod02.py`` 改写成如下 +语法 .. code:: python - # my_mod02.py - - import importlib - import my_mod01 - importlib.reload(my_mod01) - -使用 python3 来执行这个模块,与上面不同的是,这边执行了两次 -``my_mod01.py`` - -.. code:: shell - - $ python3 my_mod02.py - in mod01 - in mod01 - -4. 查找器与加载器 ------------------ - -如果指定名称的模块在 ``sys.modules`` 找不到,则将发起调用 Python -的导入协议以查找和加载该模块。 - -此协议由两个概念性模块构成,即 ``查找器`` 和 ``加载器``\ 。 - -一个 Python 的模块的导入,其实可以再细分为两个过程: - -1. 由查找器实现的模块查找 -2. 由加载器实现的模块加载 - -4.1 查找器是什么? -~~~~~~~~~~~~~~~~~~ - -查找器(finder),简单点说,查找器定义了一个模块查找机制,让程序知道该如何找到对应的模块。 - -其实 Python 内置了多个默认查找器,其存在于 sys.meta_path 中。 + (, )[condition] -但这些查找器对应使用者来说,并不是那么重要,因此在 Python 3.3 之前, -Python 解释将其隐藏了,我们称之为隐式查找器。 +例子 .. code:: python - # Python 2.7 - >>> import sys - >>> sys.meta_path - [] + >>> msg1 = ("未成年", "已成年")[age1 > 18] + >>> print(msg1) + 已成年 >>> + >>> + >>> msg2 = ("未成年", "已成年")[age2 > 18] + >>> print(msg2) + 未成年 -由于这点不利于开发者深入理解 import 机制,在 Python 3.3 -后,所有的模块导入机制都会通过 sys.meta_path -暴露,不会在有任何隐式导入机制。 - -.. code:: python - - # Python 3.6 - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.meta_path) - [, - , - ] - -观察一下 Python 默认的这几种查找器 (finder),可以分为三种: - -- 一种知道如何导入内置模块 -- 一种知道如何导入冻结模块 -- 一种知道如何导入来自 `import - path `__ - 的模块 (即 `path based - finder `__)。 - -那我们能不能自已定义一个查找器呢?当然可以,你只要 - -- 定义一个实现了 find_module 方法的类(py2和py3均可),或者实现 - find_loader 类方法(仅 py3 有效),如果找到模块需要返回一个 loader - 对象或者 ModuleSpec 对象(后面会讲),没找到需要返回 None -- 定义完后,要使用这个查找器,必须注册它,将其插入在 sys.meta_path - 的首位,这样就能优先使用。 - -.. code:: python - - import sys - - class MyFinder(object): - @classmethod - def find_module(cls, name, path, target=None): - print("Importing", name, path, target) - # 将在后面定义 - return MyLoader() - - # 由于 finder 是按顺序读取的,所以必须插入在首位 - sys.meta_path.insert(0, MyFinder) - -查找器可以分为两种: - -.. code:: shell - - object - +-- Finder (deprecated) - +-- MetaPathFinder - +-- PathEntryFinder - -这里需要注意的是,在 3.4 版前,查找器会直接返回 -加载器(Loader)对象,而在 3.4 -版后,查找器则会返回模块规格说明(ModuleSpec),其中 包含加载器。 - -而关于什么是 加载器 和 模块规格说明, 请继续往后看。 - -4.2 加载器是什么? -~~~~~~~~~~~~~~~~~~ - -查找器只负责查找定位找模,而真正负责加载模块的,是加载器(loader)。 - -一般的 loader 必须定义名为 ``load_module()`` 的方法。 - -为什么这里说一般,因为 loader 还分多种: - -.. code:: shell - - object - +-- Finder (deprecated) - | +-- MetaPathFinder - | +-- PathEntryFinder - +-- Loader - +-- ResourceLoader --------+ - +-- InspectLoader | - +-- ExecutionLoader --+ - +-- FileLoader - +-- SourceLoader - -通过查看源码可知,不同的加载器的抽象方法各有不同。 - -加载器通常由一个 finder 返回。详情参见 PEP 302,对于 abstract base class -可参见 importlib.abc.Loader。 - -那如何自定义我们自己的加载器呢? - -你只要 - -- 定义一个实现了 load_module 方法的类 -- 对与导入有关的属性(\ `点击查看详情 `__\ )进行校验 -- 创建模块对象并绑定所有与导入相关的属性变量到该模块上 -- 将此模块保存到 sys.modules 中(顺序很重要,避免递归导入) -- 然后加载模块(这是核心) -- 若加载出错,需要能够处理抛出异常( ImportError) -- 若加载成功,则返回 module 对象 - -若你想看具体的例子,可以接着往后看。 - -4.3 模块规格说明 -~~~~~~~~~~~~~~~~ - -导入机制在导入期间会使用有关每个模块的多种信息,特别是加载之前。 -大多数信息都是所有模块通用的。 -模块规格说明的目的是基于每个模块来封装这些导入相关信息。 - -模块的规格说明会作为模块对象的 ``__spec__`` 属性对外公开。 -有关模块规格的详细内容请参阅 -```ModuleSpec`` `__\ 。 - -在 Python 3.4 后,查找器不再返回加载器,而是返回 ModuleSpec -对象,它储存着更多的信息 - -- 模块名 -- 加载器 -- 模块绝对路径 - -那如何查看一个模块的 ModuleSpec ? - -这边举个例子 - -.. code:: shell - - $ cat my_mod02.py - import my_mod01 - print(my_mod01.__spec__) - - $ python3 my_mod02.py - in mod01 - ModuleSpec(name='my_mod01', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000000000392DBE0>, origin='/home/MING/my_mod01.py') - -从 ModuleSpec -中可以看到,加载器是包含在内的,那我们如果要重新加载一个模块,是不是又有了另一种思路了? - -来一起验证一下。 - -现在有两个文件: +第四种 +------ -一个是 my_info.py +语法 .. code:: python - # my_info.py - name='wangbm' + (lambda: , lambda:)[]() -另一个是:main.py +例子 .. code:: python - # main.py - import my_info - - print(my_info.name) - - # 加一个断点 - import pdb;pdb.set_trace() - - # 再加载一次 - my_info.__spec__.loader.load_module() - - print(my_info.name) - -在 ``main.py`` 处,我加了一个断点,目的是当运行到断点处时,我修改 -my_info.py 里的 name 为 ``ming`` ,以便验证重载是否有效? - -.. code:: shell - - $ python3 main.py - wangbm - > /home/MING/main.py(9)() - -> my_info.__spec__.loader.load_module() - (Pdb) c - ming - -从结果来看,重载是有效的。 - -4.4 导入器是什么? -~~~~~~~~~~~~~~~~~~ - -导入器(importer),也许你在其他文章里会见到它,但其实它并不是个新鲜的东西。 - -它只是同时实现了查找器和加载器两种接口的对象,所以你可以说导入器(importer)是查找器(finder),也可以说它是加载器(loader)。 - -5. 远程导入模块 ---------------- - -由于 Python 默认的 查找器和加载器 -仅支持本地的模块的导入,并不支持实现远程模块的导入。 - -为了让你更好的理解 Python Import Hook -机制,我下面会通过实例演示,如何自己实现远程导入模块的导入器。 - -5.1 动手实现导入器 -~~~~~~~~~~~~~~~~~~ - -当导入一个包的时候,Python 解释器首先会从 sys.meta_path -中拿到查找器列表。 - -默认顺序是:内建模块查找器 -> 冻结模块查找器 -> 第三方模块路径(本地的 -sys.path)查找器 - -若经过这三个查找器,仍然无法查找到所需的模块,则会抛出ImportError异常。 - -因此要实现远程导入模块,有两种思路。 - -- 一种是实现自己的元路径导入器; -- 另一种是编写一个钩子,添加到sys.path_hooks里,识别特定的目录命名模式。 - -我这里选择第一种方法来做为示例。 - -实现导入器,我们需要分别查找器和加载器。 - -**首先是查找器** - -由源码得知,路径查找器分为两种 - -- MetaPathFinder -- PathEntryFinder - -这里使用 MetaPathFinder 来进行查找器的编写。 - -在 Python 3.4 版本之前,查找器必须实现 ``find_module()`` 方法,而 Python -3.4+ 版,则推荐使用 ``find_spec()`` 方法,但这并不意味着你不能使用 -``find_module()``\ ,但是在没有 ``find_spec()`` -方法时,导入协议还是会尝试 ``find_module()`` 方法。 - -我先举例下使用 ``find_module()`` 该如何写。 - -.. code:: python - - from importlib import abc - - class UrlMetaFinder(abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - - def find_module(self, fullname, path=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - loader.load_module(fullname) - return loader - except Exception: - return None - -若使用 ``find_spec()`` ,要注意此方法的调用需要带有两到三个参数。 - -第一个是被导入模块的完整限定名称,例如 ``foo.bar.baz``\ 。 -第二个参数是供模块搜索使用的路径条目。 对于最高层级模块,第二个参数为 -``None``\ ,但对于子模块或子包,第二个参数为父包 ``__path__`` 属性的值。 -如果相应的 ``__path__`` 属性无法访问,将引发 -```ModuleNotFoundError`` `__\ 。 -第三个参数是一个将被作为稍后加载目标的现有模块对象。 -导入系统仅会在重加载期间传入一个目标模块。 - -.. code:: python - - from importlib import abc - from importlib.machinery import ModuleSpec - - class UrlMetaFinder(abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - def find_spec(self, fullname, path=None, target=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - return ModuleSpec(fullname, loader, is_package=loader.is_package(fullname)) - except Exception: - return None - -**接下来是加载器** - -由源码得知,路径查找器分为三种 - -- FileLoader -- SourceLoader - -按理说,两种加载器都可以实现我们想要的功能,我这里选用 SourceLoader -来示范。 - -在 SourceLoader -这个抽象类里,有几个很重要的方法,在你写实现加载器的时候需要注意 + >>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() + >>> print(msg1) + 已成年 + >>> + >>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() + >>> print(msg2) + 未成年 -- get_code:获取源代码,可以根据自己场景实现实现。 -- exec_module:执行源代码,并将变量赋值给 ``module.__dict__`` -- get_data:抽象方法,必须实现,返回指定路径的字节码。 -- get_filename:抽象方法,必须实现,返回文件名 +第五种 +------ -在一些老的博客文章中,你会经常看到 加载器 要实现 ``load_module()`` -,而这个方法早已在 Python 3.4 -的时候就被废弃了,当然为了兼容考虑,你若使用 ``load_module()`` -也是可以的。 +语法: .. code:: python - from importlib import abc - - class UrlMetaLoader(abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def load_module(self, fullname): - code = self.get_code(fullname) - mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) - mod.__file__ = self.get_filename(fullname) - mod.__loader__ = self - mod.__package__ = fullname - exec(code, mod.__dict__) - return None - - def get_data(self): - pass - - def execute_module(self, module): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' - -当你使用这种旧模式实现自己的加载时,你需要注意两点,很重要: + {True: , False: }[] -- execute_module 必须重载,而且不应该有任何逻辑,即使它并不是抽象方法。 -- load_module,需要你在查找器里手动执行,才能实现模块的加载。。 - -做为替换,你应该使用 ``execute_module()`` 和 ``create_module()`` -。由于基类里已经实现了 ``execute_module`` 和 -``create_module()``\ ,并且满足我们的使用场景。我这边可以不用重复实现。和旧模式相比,这里也不需要在设查找器里手动执行 -``execute_module()``\ 。 +例子: .. code:: python - import urllib.request as urllib2 - - class UrlMetaLoader(importlib.abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def get_data(self): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' - -查找器和加载器都有了,别忘了往sys.meta_path -注册我们自定义的查找器(UrlMetaFinder)。 - -.. code:: python + >>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] + >>> print(msg1) + 已成年 + >>> + >>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] + >>> print(msg2) + 未成年 - def install_meta(address): - finder = UrlMetaFinder(address) - sys.meta_path.append(finder) +第六种 +------ -所有的代码都解析完毕后,我们将其整理在一个模块(my_importer.py)中 +语法 .. code:: python - # my_importer.py - import sys - import importlib - import urllib.request as urllib2 - - class UrlMetaFinder(importlib.abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - - - def find_module(self, fullname, path=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - return loader - except Exception: - return None + (() and (,) or (,))[0] - class UrlMetaLoader(importlib.abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def get_data(self): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' - - def install_meta(address): - finder = UrlMetaFinder(address) - sys.meta_path.append(finder) - -5.2 搭建远程服务端 -~~~~~~~~~~~~~~~~~~ - -最开始我说了,要实现一个远程导入模块的方法。 - -我还缺一个在远端的服务器,来存放我的模块,为了方便,我使用python自带的 -``http.server`` 模块用一条命令即可实现。 - -.. code:: shell - - $ mkdir httpserver && cd httpserver - $ cat>my_info.py>> from my_importer import install_meta - >>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder - >>> import my_info # 打印ok,说明导入成功 - ok - >>> my_info.name # 验证可以取得到变量 - 'wangbm' - -至此,我实现了一个简易的可以导入远程服务器上的模块的导入器。 + >>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] + >>> print(msg1) + 已成年 + >>> + >>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] + >>> print(msg2) + 未成年 -参考文档 --------- +以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 -- https://docs.python.org/zh-cn/3/reference/import.html -- https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc -- https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html +看到这里,有没有涨姿势了,学了这么久的 Python +,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 -|image2| +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20191027192949.png -.. |image2| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_25.md b/source/c01/c01_25.md index ea6cf52..caa4153 100644 --- a/source/c01/c01_25.md +++ b/source/c01/c01_25.md @@ -1,110 +1,289 @@ -# 1.25 50% 的人不知道的Python 包与模块的知识盲区 +# 1.45 Python炫技操作:花式导包的八种方法 ![](http://image.iswbm.com/20200602135014.png) -## 1. 使用 \__all__ 控制可被导入的变量 -使用 `from module import *` 默认情况下会导入 module 里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用 `__all__` 来控制想要被其他模块导入的变量。 -```python -# profile.py -name='wangbm' -age=27 -gender='male' +## 1. 直接 import + +人尽皆知的方法,直接导入即可 -__all__=['name'] +```python +>>> import os +>>> os.getcwd() +'/home/wangbm' ``` -打开 python console 验证一下 +与此类似的还有,不再细讲 ```python ->>> from profile import * ->>> print(name) -wangbm ->>> print(age) -Traceback (most recent call last): - File "", line 1, in -NameError: name 'age' is not defined ->>> print(gender) -Traceback (most recent call last): - File "", line 1, in -NameError: name 'gender' is not defined +import ... +import ... as ... +from ... import ... +from ... import ... as ... ``` -`__all__` 仅对于使用`from module import *` 这种情况适用。 +一般情况下,使用 `import` 语句导入模块已经够用的。 -它经常在一个包的 `__init__.py` 中出现。 +但是在一些特殊场景中,可能还需要其他的导入方式。 +下面我会一一地给你介绍。 +## 2. 使用 \__import__ -## 2. 命名空间包的神奇之处 +`__import__` 函数可用于导入模块,import 语句也会调用函数。其定义为: -命名空间包,一个陌生的名字。 +``` +__import__(name[, globals[, locals[, fromlist[, level]]]]) +``` -与我们熟悉的常规包不同的是,它没有 `__init__.py` 文件。 +参数介绍: -更为特殊的是,它可以跨空间地将两个不相邻的子包,合并成一个虚拟机的包,我们将其称之为 `命名空间包`。 +- name (required): 被加载 module 的名称 +- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 global() +- locals (optional): 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local() +- fromlist (Optional): 被导入的 submodule 名称 +- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 类似于 '.',2 类似于 '..'。 -例如,一个项目的部分代码布局如下 +使用示例如下: +```python +>>> os = __import__('os') +>>> os.getcwd() +'/home/wangbm' ``` -foo-package/ - spam/ - blah.py -bar-package/ - spam/ - grok.py +如果要实现 `import xx as yy` 的效果,只要修改左值即可 + +如下示例,等价于 `import os as myos`: + +```python +>>> myos = __import__('os') +>>> myos.getcwd() +'/home/wangbm' ``` -在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。 -让我们看看,如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么? + +上面说过的 `__import__` 是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于 `__buildins__` 中,因此我们还可以这样导入 os 的模块: ```python ->>> import sys ->>> sys.path.extend(['foo-package', 'bar-package']) ->>> import spam.blah ->>> import spam.grok ->>> +>>> __builtins__.__dict__['__import__']('os').getcwd() +'/home/wangbm' ``` -当一个包为命名空间包时,他就不再和常规包一样具有 `__file_` 属性,取而代之的是 `__path__` +## 3. 使用 importlib 模块 + +importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。 + +它的简单示例: ```python ->>> import sys ->>> sys.path.extend(['foo-package', 'bar-package']) ->>> import spam.blah ->>> import spam.grok ->>> spam.__path__ -_NamespacePath(['foo-package/spam', 'bar-package/spam']) ->>> spam.__file__ -Traceback (most recent call last): - File "", line 1, in -AttributeError: 'module' object has no attribute '__file__' +>>> import importlib +>>> myos=importlib.import_module("os") +>>> myos.getcwd() +'/home/wangbm' ``` +如果要实现 `import xx as yy`效果,可以这样 + +```python +>>> import importlib +>>> +>>> myos = importlib.import_module("os") +>>> myos.getcwd() +'/home/wangbm' +``` -## 3. 包重载就是一个坑 -第一种方法 +## 4. 使用 imp 模块 + +`imp` 模块提供了一些 import 语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的 `__import__` 函数功能: ```python ->>> import spam >>> import imp ->>> imp.reload(spam) - +>>> file, pathname, desc = imp.find_module('os') +>>> myos = imp.load_module('sep', file, pathname, desc) +>>> myos + +>>> myos.getcwd() +'/home/wangbm' +``` + +从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib 模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib 模块的合集。 + + + +## 5. 使用 execfile + +在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。 + +语法如下: + +``` +execfile(filename[, globals[, locals]]) +``` + +参数有这么几个: + +- filename:文件名。 +- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。 +- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。 + +```python +>>> execfile("/usr/lib64/python2.7/os.py") +>>> +>>> getcwd() +'/home/wangbm' +``` + + + +## 6. 使用 exec 执行 + +`execfile` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。 + +但是原理值得借鉴,你可以使用 open ... read 读取文件内容,然后再用 exec 去执行模块。 + +示例如下: + +```python +>>> with open("/usr/lib64/python2.7/os.py", "r") as f: +... exec(f.read()) +... +>>> getcwd() +'/home/wangbm' +``` + + + +## 7. import_from_github_com + +有一个包叫做 **import_from_github_com**,从名字上很容易得知,它是一个可以从 github 下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip 先安装它。 + +```shell +$ python3 -m pip install import_from_github_com +``` + +这个包使用了PEP 302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。 + +pip 要保证是较新版本,如果不是请执行如下命令进行升级。 + +```shell +$ python3 -m pip install --upgrade pip +``` + +确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com + +示例如下 + +```python +>>> from github_com.zzzeek import sqlalchemy +Collecting git+https://github.com/zzzeek/sqlalchemy +Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build +Installing collected packages: SQLAlchemy +Running setup.py install for SQLAlchemy ... done +Successfully installed SQLAlchemy-1.1.0b1.dev0 +>>> locals() +{'__builtins__': , '__spec__': None, +'__package__': None, '__doc__': None, '__name__': '__main__', +'sqlalchemy': , +'__loader__': } >>> ``` -由于这种重载方法,只对 `import module` 有效,而使用 `from module import arg` 导入的 arg 并不会刷新。 +看了 import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 pip 来安装那些没有安装的包,然后使用Python的`__import__()`函数来引入新安装的模块。 + + + +## 8. 远程导入模块 + +我在这篇文章里([深入探讨 Python 的 import 机制:实现远程导入模块](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。 + +具体内容非常的多,你可以点击这个[链接](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)进行深入学习。 + +示例代码如下: + +```python +# 新建一个 py 文件(my_importer.py),内容如下 +import sys +import importlib +import urllib.request as urllib2 + +class UrlMetaFinder(importlib.abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return loader + except Exception: + return None + +class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + +def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) +``` + +并且在远程服务器上开启 http 服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的 python 文件,如果后面导入成功会打印 `ok`。 + +```shell +$ mkdir httpserver && cd httpserver +$ cat>my_info.py>> from my_importer import install_meta +>>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder +>>> import my_info # 打印ok,说明导入成功 +ok +>>> my_info.name # 验证可以取得到变量 +'wangbm' +``` -因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 +好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import 这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习` __import__ `以及 importlib 是非常有必要的。 diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index f7fda8f..052224e 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -1,111 +1,306 @@ -1.25 50% 的人不知道的Python 包与模块的知识盲区 -============================================== +1.45 Python炫技操作:花式导包的八种方法 +======================================= |image0| -1. 使用 \__all_\_ 控制可被导入的变量 ------------------------------------- +1. 直接 import +-------------- -使用 ``from module import *`` 默认情况下会导入 module -里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用 -``__all__`` 来控制想要被其他模块导入的变量。 +人尽皆知的方法,直接导入即可 .. code:: python - # profile.py - name='wangbm' - age=27 - gender='male' - - __all__=['name'] + >>> import os + >>> os.getcwd() + '/home/wangbm' -打开 python console 验证一下 +与此类似的还有,不再细讲 .. code:: python - >>> from profile import * - >>> print(name) - wangbm - >>> print(age) - Traceback (most recent call last): - File "", line 1, in - NameError: name 'age' is not defined - >>> print(gender) - Traceback (most recent call last): - File "", line 1, in - NameError: name 'gender' is not defined + import ... + import ... as ... + from ... import ... + from ... import ... as ... -``__all__`` 仅对于使用\ ``from module import *`` 这种情况适用。 +一般情况下,使用 ``import`` 语句导入模块已经够用的。 -它经常在一个包的 ``__init__.py`` 中出现。 +但是在一些特殊场景中,可能还需要其他的导入方式。 -2. 命名空间包的神奇之处 ------------------------ +下面我会一一地给你介绍。 -命名空间包,一个陌生的名字。 +2. 使用 \__import_\_ +-------------------- -与我们熟悉的常规包不同的是,它没有 ``__init__.py`` 文件。 +``__import__`` 函数可用于导入模块,import 语句也会调用函数。其定义为: -更为特殊的是,它可以跨空间地将两个不相邻的子包,合并成一个虚拟机的包,我们将其称之为 -``命名空间包``\ 。 +:: -例如,一个项目的部分代码布局如下 + __import__(name[, globals[, locals[, fromlist[, level]]]]) -:: +参数介绍: + +- name (required): 被加载 module 的名称 +- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 + global() +- locals (optional): + 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local() +- fromlist (Optional): 被导入的 submodule 名称 +- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 + absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 + absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 + 类似于 ‘.’,2 类似于 ‘..’。 - foo-package/ - spam/ - blah.py +使用示例如下: - bar-package/ - spam/ - grok.py +.. code:: python + + >>> os = __import__('os') + >>> os.getcwd() + '/home/wangbm' -在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。 +如果要实现 ``import xx as yy`` 的效果,只要修改左值即可 -让我们看看,如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么? +如下示例,等价于 ``import os as myos``\ : .. code:: python - >>> import sys - >>> sys.path.extend(['foo-package', 'bar-package']) - >>> import spam.blah - >>> import spam.grok - >>> + >>> myos = __import__('os') + >>> myos.getcwd() + '/home/wangbm' -当一个包为命名空间包时,他就不再和常规包一样具有 ``__file_`` -属性,取而代之的是 ``__path__`` +上面说过的 ``__import__`` +是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于 +``__buildins__`` 中,因此我们还可以这样导入 os 的模块: .. code:: python - >>> import sys - >>> sys.path.extend(['foo-package', 'bar-package']) - >>> import spam.blah - >>> import spam.grok - >>> spam.__path__ - _NamespacePath(['foo-package/spam', 'bar-package/spam']) - >>> spam.__file__ - Traceback (most recent call last): - File "", line 1, in - AttributeError: 'module' object has no attribute '__file__' + >>> __builtins__.__dict__['__import__']('os').getcwd() + '/home/wangbm' -3. 包重载就是一个坑 -------------------- +3. 使用 importlib 模块 +---------------------- -第一种方法 +importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。 + +它的简单示例: + +.. code:: python + + >>> import importlib + >>> myos=importlib.import_module("os") + >>> myos.getcwd() + '/home/wangbm' + +如果要实现 ``import xx as yy``\ 效果,可以这样 + +.. code:: python + + >>> import importlib + >>> + >>> myos = importlib.import_module("os") + >>> myos.getcwd() + '/home/wangbm' + +4. 使用 imp 模块 +---------------- + +``imp`` 模块提供了一些 import +语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的 +``__import__`` 函数功能: .. code:: python - >>> import spam >>> import imp - >>> imp.reload(spam) - + >>> file, pathname, desc = imp.find_module('os') + >>> myos = imp.load_module('sep', file, pathname, desc) + >>> myos + + >>> myos.getcwd() + '/home/wangbm' + +从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 +开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib +模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib +模块的合集。 + +5. 使用 execfile +---------------- + +在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。 + +语法如下: + +:: + + execfile(filename[, globals[, locals]]) + +参数有这么几个: + +- filename:文件名。 +- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。 +- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。 + +.. code:: python + + >>> execfile("/usr/lib64/python2.7/os.py") + >>> + >>> getcwd() + '/home/wangbm' + +6. 使用 exec 执行 +----------------- + +``execfile`` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。 + +但是原理值得借鉴,你可以使用 open … read 读取文件内容,然后再用 exec +去执行模块。 + +示例如下: + +.. code:: python + + >>> with open("/usr/lib64/python2.7/os.py", "r") as f: + ... exec(f.read()) + ... + >>> getcwd() + '/home/wangbm' + +7. import_from_github_com +------------------------- + +有一个包叫做 +**import_from_github_com**\ ,从名字上很容易得知,它是一个可以从 github +下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip +先安装它。 + +.. code:: shell + + $ python3 -m pip install import_from_github_com + +这个包使用了PEP +302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 +Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。 + +pip 要保证是较新版本,如果不是请执行如下命令进行升级。 + +.. code:: shell + + $ python3 -m pip install --upgrade pip + +确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com + +示例如下 + +.. code:: python + + >>> from github_com.zzzeek import sqlalchemy + Collecting git+https://github.com/zzzeek/sqlalchemy + Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build + Installing collected packages: SQLAlchemy + Running setup.py install for SQLAlchemy ... done + Successfully installed SQLAlchemy-1.1.0b1.dev0 + >>> locals() + {'__builtins__': , '__spec__': None, + '__package__': None, '__doc__': None, '__name__': '__main__', + 'sqlalchemy': , + '__loader__': } >>> -由于这种重载方法,只对 ``import module`` 有效,而使用 -``from module import arg`` 导入的 arg 并不会刷新。 +看了 +import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 +pip +来安装那些没有安装的包,然后使用Python的\ ``__import__()``\ 函数来引入新安装的模块。 + +8. 远程导入模块 +--------------- + +我在这篇文章里(\ `深入探讨 Python 的 import +机制:实现远程导入模块 `__\ ),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。 + +具体内容非常的多,你可以点击这个\ `链接 `__\ 进行深入学习。 + +示例代码如下: + +.. code:: python + + # 新建一个 py 文件(my_importer.py),内容如下 + import sys + import importlib + import urllib.request as urllib2 + + class UrlMetaFinder(importlib.abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return loader + except Exception: + return None + + class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + + def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) + +并且在远程服务器上开启 http +服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的 +python 文件,如果后面导入成功会打印 ``ok``\ 。 + +.. code:: shell + + $ mkdir httpserver && cd httpserver + $ cat>my_info.py>> from my_importer import install_meta + >>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder + >>> import my_info # 打印ok,说明导入成功 + ok + >>> my_info.name # 验证可以取得到变量 + 'wangbm' -因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 +好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import +这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习\ ``__import__``\ 以及 +importlib 是非常有必要的。 |image1| diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md index c4dafae..3f37049 100644 --- a/source/c01/c01_26.md +++ b/source/c01/c01_26.md @@ -1,244 +1,219 @@ -# 1.26 C语言基础的学习 +# 1.39 Python 炫技操作:合并字典的七种方法 ![](http://image.iswbm.com/20200602135014.png) -## 1. 安装编译器 +Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 -C 语言编译器用于把源代码编译成最终的可执行程序。这里假设您已经对编程语言编译器有基本的了解了。 +但你要知道,在团队合作里,炫技是大忌。 -最常用的免费可用的编译器是 GNU 的 C/C++ 编译器。 +为什么这么说呢?我说下自己的看法: -### 1.1 windows +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) -MinGw 是 Minimal GNU on Windows 的缩写,允许在 GNU/Linux 和 Windows 平台生成本地的 Windows 程序而不需要第三方运行时库。本文主要介绍 MinGw 的安装和使用。 +该篇是「**炫技系列**」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 -**安装** +## 1. 最简单的原地更新 -1.下载 [min-gw](https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/) 安装程序,下载 mingw-get-setup.exe (86.5 kB) +字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 -2.运行 mingw-get-setup.exe (86.5 kB) ,点击“运行”,continue等,注意记住安装的目录,如 **C:\MinGw**,下面修改环境变量时还会用到。 -3.修改环境变量: 选择计算机—属性---高级系统设置---环境变量,在系统变量中找到 Path 变量,在后面加入 min-gw的安装目录,如 **C:\MinGw\bin** -4.在开始菜单中,点击“运行”,输入 **cmd**,打开命令行:输入 **mingw-get.exe**,如果弹出 MinGw installation manager 窗口,说明安装正常。此时,关闭 MinGw installation manager 窗口,否则接下来的步骤会报错 -5.在cmd中输入命令 **mingw-get install gcc**,等待一会,gcc 就安装成功了。 +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> profile.update(ext_info) +>>> print(profile) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` -如果想安装 g++,gdb,只要输入命令 **mingw-get install g++** 和 **mingw-get install gdb** +如果想使用 update 这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 + +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> from copy import deepcopy +>>> +>>> full_profile = deepcopy(profile) +>>> full_profile.update(ext_info) +>>> +>>> print(full_profile) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> print(profile) +{"name": "xiaoming", "age": 27} +``` -**使用** -在 cmd 的当前工作目录写 C 程序 hello.c: -``` -# include -int main() -{ - printf("%s\n","hello world"); - return 0; -} -``` +## 2. 先解包再合并字典 -在 cmd 中输入命令 +使用 `**` 可以解包字典,解包完后再使用 dict 或者 `{}` 就可以合并。 -```shell -$ gcc hello.c +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> full_profile01 = {**profile, **ext_info} +>>> print(full_profile01) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> +>>> full_profile02 = dict(**profile, **ext_info) +>>> print(full_profile02) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ``` -在当前目录下会生成 a.exe 的可执行文件,在 cmd 中输入 a.exe 就可以执行程序了。 +若你不知道 `dict(**profile, **ext_info)` 做了啥,你可以将它等价于 -如果想调试程序,可以输入 gdb a.exe +```python +>>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` -进入 gdb 的功能,使用 gdb 常用的命令就可以调试程序了。 -### Mac OSX 及 Linux -从苹果的网站上下载 [Xcode 开发环境](https://developer.apple.com/xcode/),并按照安装说明进行安装。一旦安装上 Xcode,您就能使用 GNU 编译器。 +## 3. 借助 itertools -**使用** +在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 -打开终端,使用 vim 编辑文件 hello.c +正好我们字典也是可迭代对象,自然就可以想到,可以使用 `itertools.chain()` 函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 dict 转成字典。 -``` -# include -int main() -{ - printf("%s\n","hello world"); - return 0; -} +```python +>>> import itertools +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> +>>> dict(itertools.chain(profile.items(), ext_info.items())) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ``` -使用 gcc 工具进行编译完后,就会在当前目录下生成一个名为 a.out 的可执行文件,手动运行它即可 -```shell -$ gcc hello.c -$ ./a.out -``` +## 4. 借助 ChainMap +如果可以引入一个辅助包,那我就再提一个, `ChainMap` 也可以达到和 `itertools` 同样的效果。 -### 使用 VS code - -在 vscode 中引入标准库的头文件时,会出现波浪线,提示找不到头文件。 - -解决方法是在当前项目下的 .vscode/c_cpp_properties.json 中的 includePath 列表中添加包含标准头文件的路径,这个路径哪里来呢?你可以使用 everything 搜索一下,哪个路径下有此 stdio.h 文件。 - -比如我的是这样,注意路径不要使用 `\` 和 `\\` ,一定要使用 `\` - -```json -{ - "configurations": [ - { - "name": "Win32", - "includePath": [ - "${workspaceFolder}/**", - "E:/MinGW/lib/gcc/mingw32/8.2.0/include", - "E:/MinGW/include", - "E:/MinGW/lib/gcc/mingw32/8.2.0/include/c++/tr1", - "E:/MinGW/lib/gcc/mingw32/8.2.0/include/ssp", - "C:/Users/wangbm/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/include" - ], - "defines": [ - "_DEBUG", - "UNICODE", - "_UNICODE" - ], - "intelliSenseMode": "msvc-x64" - } - ], - "version": 4 -} +```python +>>> from collections import ChainMap +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(ChainMap(profile, ext_info)) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ``` +使用 ChainMap 有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 itertools 就不会有这个问题)。 +```python +>>> from collections import ChainMap +>>> +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info={"age": 30} +>>> dict(ChainMap(profile, ext_info)) +{'name': 'xiaoming', 'age': 27} +``` -## - -### 字符串声明定义 -字符串声明使用 `char` -```c -#include +## 5. 使用dict.items() 合并 -// 定义 name ,不设置大小 -char name[] = "wangbm"; +在 Python 3.9 之前,其实就已经有 `|` 操作符了,只不过它通常用于对集合(set)取并集。 -// 定义 gender ,设置大小为7个字节 -char gender[7] = "female"; +利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 -int main() -{ - printf("name is %s\n", name); - printf("size of name: %lu\n", sizeof(name)); - printf("len of name: %lu\n", strlen(name)); +你得先利用 `items` 方法将 dict 转成 dict_items,再对这两个 dict_items 取并集,最后利用 dict 函数,转成字典。 - printf("gender is %s\n", gender); - printf("size of gender: %lu\n", sizeof(gender)); - printf("len of gender: %lu\n", strlen(gender)); - return 0; -} +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> full_profile = dict(profile.items() | ext_info.items()) +>>> full_profile +{'gender': 'male', 'age': 27, 'name': 'xiaoming'} ``` -### 字符串操作 -- strcat(s1,s2) :string catenate,连接s2到s1末尾 -- strcpy(s1,s2) :string copy,复制字符串s2到s1 -- strlen(s1) :(string length),返回s1字符串的长度 -- strlwr(s1) :string lowercase,将s1的字符串的字母全部大写返回 -- strupr(s1) :string upercase,将s1的字符串的字母全部小写返回 -- strcmp(s1,s2) :string compare,如果 s1 和 s2 是相同的,则返回 0;如果 s1s2 则返回大于 0。 -### 字符的输入 +当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list 函数再合并(示例为 Python 3.x ) -使用 `printf` 和 `scanf` 函数 +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(list(profile.items()) + list(ext_info.items())) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` -```c -# include +若你在 Python 2.x 下,可以直接省去 list 函数。 -int main(int argc, char const *argv[]) -{ - char name[10]; - printf("Enter your name: "); - scanf("%s", name); - printf("Your name is: %s \n", name); - return 0; -} +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> dict(profile.items() + ext_info.items()) +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ``` -使用 fgets 和 fputs 函数 -```c -# include -int main(int argc, char const *argv[]) -{ - char name[10]; - printf("Enter your name: "); - fgets(name, 10, stdin); - printf("Your name is: "); - fputs(name, stdout); - return 0; -} -``` +## 6. 最酷炫的字典解析式 + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 -getchar() & putchar() +那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? +当然可以,具体示例代码如下: +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> {k:v for d in [profile, ext_info] for k,v in d.items()} +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +``` -### 指针相关的两个符号 -记住 `*` 有3个用途: -1. 乘号(Multiply): 2*3 就是6 -2. 声明指针(Pointer Statement): int a =5; int* ptr=&a;就是声明变量a是5,把a的地址附到指针ptr上 -3. 解引用 (Dereference): *ptr 单独拿出来就是找出 ptr指针指向的值,按照第二点的说法就是5. +## 7. Python 3.9 新特性 -`&`叫做取地址符号,一般指针只能接受一个内存地址而不能接受一个值 +在 2 月份发布的 Python 3.9.04a 版本中,新增了一个抓眼球的新操作符操作符: `|`, PEP584 将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 -```c -// 如下是错误的,指针不能接受一个值 -int a =5; int* ptr=a; +```python +>>> profile = {"name": "xiaoming", "age": 27} +>>> ext_info = {"gender": "male"} +>>> +>>> profile | ext_info +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} +>>> +>>> ext_info | profile +{'gender': 'male', 'name': 'xiaoming', 'age': 27} +>>> +>>> +``` -// 如下是正确的,a的地址给指针ptr -int a =5; int* ptr=&a; +除了 `|` 操作符之外,还有另外一个操作符 `|=`,类似于原地更新。 + +```python +>>> ext_info |= profile +>>> ext_info +{'gender': 'male', 'name': 'xiaoming', 'age': 27} +>>> +>>> +>>> profile |= ext_info +>>> profile +{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ``` -### 可变参数的获取 - -以下写了一个函数 `get_sum` 来求得输入的所有参数的和(除了第一个参数外,第一个参数表示,对后面几个可变参数求和,在示例中是作为结束条件存在)。 - -```c -#include -#include - -int get_sum(int n, ...) -{ - va_list arglist; // 定义一个va_list类型的字符指针,用来指向当前参数,后面取参必须通过这个指针进行 - va_start(arglist, n); // 初始化这个指针,让其指向可变参数里的第一个参数,这里的参数应该填写 ... 的那个参数 - double sum; - - for (int i = 1; i <= n; i++) - { - sum += va_arg(arglist, int); - if (i >= 3) - { - break; - } - } - - va_end(arglist); // 养成好习惯,将这个指针关闭 - return sum; -} - -int main(int argc, char const *argv[]) -{ - /* code */ - printf("The sum is: %d", get_sum(3,1,1,1)); - return 0; -} -``` +看到这里,有没有涨姿势了,学了这么久的 Python ,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 7 种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 + +--- diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index b576f57..89a0d3d 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -1,265 +1,228 @@ -1.26 C语言基础的学习 -==================== +1.39 Python 炫技操作:合并字典的七种方法 +======================================== |image0| -1. 安装编译器 -------------- +Python 语言里有许多(而且是越来越多)的高级特性,是 Python +发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 -C -语言编译器用于把源代码编译成最终的可执行程序。这里假设您已经对编程语言编译器有基本的了解了。 +但你要知道,在团队合作里,炫技是大忌。 -最常用的免费可用的编译器是 GNU 的 C/C++ 编译器。 +为什么这么说呢?我说下自己的看法: -1.1 windows -~~~~~~~~~~~ +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) -MinGw 是 Minimal GNU on Windows 的缩写,允许在 GNU/Linux 和 Windows -平台生成本地的 Windows 程序而不需要第三方运行时库。本文主要介绍 MinGw -的安装和使用。 +该篇是「\ **炫技系列**\ 」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 +Python +发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 -**安装** +1. 最简单的原地更新 +------------------- -1.下载 -`min-gw `__ -安装程序,下载 mingw-get-setup.exe (86.5 kB) +字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 -2.运行 mingw-get-setup.exe (86.5 kB) -,点击“运行”,continue等,注意记住安装的目录,如 -\**C::raw-latex:`\MinGw*`\*,下面修改环境变量时还会用到。 3.修改环境变量: -选择计算机—属性—高级系统设置—环境变量,在系统变量中找到 Path -变量,在后面加入 min-gw的安装目录,如 -\**C::raw-latex:`\MinGw`:raw-latex:`\bin*`\* -4.在开始菜单中,点击“运行”,输入 **cmd**,打开命令行:输入 -**mingw-get.exe**,如果弹出 MinGw installation manager -窗口,说明安装正常。此时,关闭 MinGw installation manager -窗口,否则接下来的步骤会报错 5.在cmd中输入命令 **mingw-get install -gcc**,等待一会,gcc 就安装成功了。 +.. code:: python -如果想安装 g++,gdb,只要输入命令 **mingw-get install g++** 和 **mingw-get -install gdb** + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> profile.update(ext_info) + >>> print(profile) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} -**使用** +如果想使用 update +这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 -在 cmd 的当前工作目录写 C 程序 hello.c: +.. code:: python -:: + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> from copy import deepcopy + >>> + >>> full_profile = deepcopy(profile) + >>> full_profile.update(ext_info) + >>> + >>> print(full_profile) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> print(profile) + {"name": "xiaoming", "age": 27} - # include - int main() - { - printf("%s\n","hello world"); - return 0; - } +2. 先解包再合并字典 +------------------- -在 cmd 中输入命令 +使用 ``**`` 可以解包字典,解包完后再使用 dict 或者 ``{}`` 就可以合并。 -.. code:: shell +.. code:: python - $ gcc hello.c + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> full_profile01 = {**profile, **ext_info} + >>> print(full_profile01) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> + >>> full_profile02 = dict(**profile, **ext_info) + >>> print(full_profile02) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} -在当前目录下会生成 a.exe 的可执行文件,在 cmd 中输入 a.exe -就可以执行程序了。 +若你不知道 ``dict(**profile, **ext_info)`` 做了啥,你可以将它等价于 -如果想调试程序,可以输入 gdb a.exe +.. code:: python -进入 gdb 的功能,使用 gdb 常用的命令就可以调试程序了。 + >>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} -Mac OSX 及 Linux -~~~~~~~~~~~~~~~~ +3. 借助 itertools +----------------- -从苹果的网站上下载 `Xcode -开发环境 `__\ ,并按照安装说明进行安装。一旦安装上 -Xcode,您就能使用 GNU 编译器。 +在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 -**使用** +正好我们字典也是可迭代对象,自然就可以想到,可以使用 +``itertools.chain()`` +函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 +dict 转成字典。 -打开终端,使用 vim 编辑文件 hello.c +.. code:: python -:: + >>> import itertools + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> + >>> dict(itertools.chain(profile.items(), ext_info.items())) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - # include - int main() - { - printf("%s\n","hello world"); - return 0; - } +4. 借助 ChainMap +---------------- -使用 gcc 工具进行编译完后,就会在当前目录下生成一个名为 a.out -的可执行文件,手动运行它即可 +如果可以引入一个辅助包,那我就再提一个, ``ChainMap`` 也可以达到和 +``itertools`` 同样的效果。 -.. code:: shell +.. code:: python - $ gcc hello.c - $ ./a.out + >>> from collections import ChainMap + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(ChainMap(profile, ext_info)) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} -使用 VS code -~~~~~~~~~~~~ +使用 ChainMap +有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 +itertools 就不会有这个问题)。 -在 vscode 中引入标准库的头文件时,会出现波浪线,提示找不到头文件。 +.. code:: python -解决方法是在当前项目下的 .vscode/c_cpp_properties.json 中的 includePath -列表中添加包含标准头文件的路径,这个路径哪里来呢?你可以使用 everything -搜索一下,哪个路径下有此 stdio.h 文件。 + >>> from collections import ChainMap + >>> + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info={"age": 30} + >>> dict(ChainMap(profile, ext_info)) + {'name': 'xiaoming', 'age': 27} -比如我的是这样,注意路径不要使用 ``\`` 和 ``\\`` ,一定要使用 ``\`` +5. 使用dict.items() 合并 +------------------------ -.. code:: json +在 Python 3.9 之前,其实就已经有 ``|`` +操作符了,只不过它通常用于对集合(set)取并集。 - { - "configurations": [ - { - "name": "Win32", - "includePath": [ - "${workspaceFolder}/**", - "E:/MinGW/lib/gcc/mingw32/8.2.0/include", - "E:/MinGW/include", - "E:/MinGW/lib/gcc/mingw32/8.2.0/include/c++/tr1", - "E:/MinGW/lib/gcc/mingw32/8.2.0/include/ssp", - "C:/Users/wangbm/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/9.0/VC/include" - ], - "defines": [ - "_DEBUG", - "UNICODE", - "_UNICODE" - ], - "intelliSenseMode": "msvc-x64" - } - ], - "version": 4 - } +利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 -字符串声明定义 -~~~~~~~~~~~~~~ +你得先利用 ``items`` 方法将 dict 转成 dict_items,再对这两个 dict_items +取并集,最后利用 dict 函数,转成字典。 -字符串声明使用 ``char`` +.. code:: python -.. code:: c + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> full_profile = dict(profile.items() | ext_info.items()) + >>> full_profile + {'gender': 'male', 'age': 27, 'name': 'xiaoming'} - #include +当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list +函数再合并(示例为 Python 3.x ) - // 定义 name ,不设置大小 - char name[] = "wangbm"; +.. code:: python - // 定义 gender ,设置大小为7个字节 - char gender[7] = "female"; + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(list(profile.items()) + list(ext_info.items())) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - int main() - { - printf("name is %s\n", name); - printf("size of name: %lu\n", sizeof(name)); - printf("len of name: %lu\n", strlen(name)); +若你在 Python 2.x 下,可以直接省去 list 函数。 - printf("gender is %s\n", gender); - printf("size of gender: %lu\n", sizeof(gender)); - printf("len of gender: %lu\n", strlen(gender)); - return 0; - } +.. code:: python -字符串操作 -~~~~~~~~~~ + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> dict(profile.items() + ext_info.items()) + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} -- strcat(s1,s2) :string catenate,连接s2到s1末尾 -- strcpy(s1,s2) :string copy,复制字符串s2到s1 -- strlen(s1) :(string length),返回s1字符串的长度 -- strlwr(s1) :string lowercase,将s1的字符串的字母全部大写返回 -- strupr(s1) :string upercase,将s1的字符串的字母全部小写返回 -- strcmp(s1,s2) :string compare,如果 s1 和 s2 是相同的,则返回 - 0;如果 s1s2 则返回大于 0。 +6. 最酷炫的字典解析式 +--------------------- -字符的输入 -~~~~~~~~~~ +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 -使用 ``printf`` 和 ``scanf`` 函数 +那就是列表解析式,集合解析式和字典解析式,通常是 Python +发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? -.. code:: c +当然可以,具体示例代码如下: - # include - - int main(int argc, char const *argv[]) - { - char name[10]; - printf("Enter your name: "); - scanf("%s", name); - printf("Your name is: %s \n", name); - return 0; - } - -使用 fgets 和 fputs 函数 - -.. code:: c - - # include - - int main(int argc, char const *argv[]) - { - char name[10]; - printf("Enter your name: "); - fgets(name, 10, stdin); - printf("Your name is: "); - fputs(name, stdout); - return 0; - } - -getchar() & putchar() - -指针相关的两个符号 -~~~~~~~~~~~~~~~~~~ - -记住 ``*`` 有3个用途: - -1. 乘号(Multiply): 2*3 就是6 -2. 声明指针(Pointer Statement): int a =5; int\* - ptr=&a;就是声明变量a是5,把a的地址附到指针ptr上 -3. 解引用 (Dereference): \*ptr 单独拿出来就是找出 - ptr指针指向的值,按照第二点的说法就是5. - -``&``\ 叫做取地址符号,一般指针只能接受一个内存地址而不能接受一个值 - -.. code:: c - - // 如下是错误的,指针不能接受一个值 - int a =5; int* ptr=a; - - // 如下是正确的,a的地址给指针ptr - int a =5; int* ptr=&a; +.. code:: python -可变参数的获取 -~~~~~~~~~~~~~~ + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> {k:v for d in [profile, ext_info] for k,v in d.items()} + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} -以下写了一个函数 ``get_sum`` -来求得输入的所有参数的和(除了第一个参数外,第一个参数表示,对后面几个可变参数求和,在示例中是作为结束条件存在)。 +7. Python 3.9 新特性 +-------------------- -.. code:: c +在 2 月份发布的 Python 3.9.04a +版本中,新增了一个抓眼球的新操作符操作符: ``|``\ , PEP584 +将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 - #include - #include +.. code:: python - int get_sum(int n, ...) - { - va_list arglist; // 定义一个va_list类型的字符指针,用来指向当前参数,后面取参必须通过这个指针进行 - va_start(arglist, n); // 初始化这个指针,让其指向可变参数里的第一个参数,这里的参数应该填写 ... 的那个参数 - double sum; - - for (int i = 1; i <= n; i++) - { - sum += va_arg(arglist, int); - if (i >= 3) - { - break; - } - } - - va_end(arglist); // 养成好习惯,将这个指针关闭 - return sum; - } + >>> profile = {"name": "xiaoming", "age": 27} + >>> ext_info = {"gender": "male"} + >>> + >>> profile | ext_info + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + >>> + >>> ext_info | profile + {'gender': 'male', 'name': 'xiaoming', 'age': 27} + >>> + >>> - int main(int argc, char const *argv[]) - { - /* code */ - printf("The sum is: %d", get_sum(3,1,1,1)); - return 0; - } +除了 ``|`` 操作符之外,还有另外一个操作符 ``|=``\ ,类似于原地更新。 + +.. code:: python + + >>> ext_info |= profile + >>> ext_info + {'gender': 'male', 'name': 'xiaoming', 'age': 27} + >>> + >>> + >>> profile |= ext_info + >>> profile + {'name': 'xiaoming', 'age': 27, 'gender': 'male'} + +看到这里,有没有涨姿势了,学了这么久的 Python +,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 +7 +种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 + +-------------- |image1| diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index e962010..2d70011 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -1,654 +1,142 @@ -# 1.27 全面学习 Python 包:包的构建与分发 +# 1.40 Python 炫技操作:判断是否包含子串的七种方法 ![](http://image.iswbm.com/20200602135014.png) -> 首发于公众号:Python编程时光 +Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 +但你要知道,在团队合作里,炫技是大忌。 +为什么这么说呢?我说下自己的看法: -## 1. 为什么需要对项目分发打包? +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) -平常我们习惯了使用 pip 来安装一些第三方模块,这个安装过程之所以简单,是因为模块开发者为我们默默地为我们做了所有繁杂的工作,而这个过程就是 `打包`。 +该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 -打包,就是将你的源代码进一步封装,并且将所有的项目部署工作都事先安排好,这样使用者拿到后即装即用,不用再操心如何部署的问题(如果你不想对照着一堆部署文档手工操作的话)。 +## 1. 使用 in 和 not in -不管你是在工作中,还是业余准备自己写一个可以上传到 PyPI 的项目,你都要学会如何打包你的项目。 +`in` 和 `not in` 在 Python 中是很常用的关键字,我们将它们归类为 `成员运算符`。 -Python 发展了这么些年了,项目打包工具也已经很成熟了。他们都有哪些呢? - -你可能听过 `disutils`、 `distutils` 、`distutils2`、`setuptools`等等,好像很熟悉,却又很陌生,他们都是什么关系呢? - -## 2. 包分发的始祖:distutils - -`distutils` 是 Python 的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是 Python 官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的。 - -`distutils` 的精髓在于编写 setup.py,它是模块分发与安装的指导文件。 - -那么如何编写 setup.py 呢?这里面的内容非常多,我会在后面进行详细的解析,请你耐心往下看。 - -你有可能没写过 setup.py ,但你绝对使用过 setup.py 来做一些事情,比如下面这条命令,我们经常用它来进行模块的安装。 - -```shell -$ python setup.py install -``` - -这样的安装方法是通过源码安装,与之对应的是通过二进制软件包的安装,同样我也会在后面进行介绍。 - -## 3. 分发工具升级:setuptools - -`setuptools` 是 distutils 增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。 - - **distribute**,或许你在其他地方也见过它,这里也提一下。 - -distribute 是 setuptools 有一个分支版本,分支的原因可能是有一部分开发者认为 setuptools 开发太慢了。但现在,distribute 又合并回了 setuptools 中。因此,我们可以认为它们是同一个东西。 - -还有一个大包分发工具是 **distutils2**,其试图尝试充分利用distutils,detuptools 和 distribute 并成为 Python 标准库中的标准工具。但该计划并没有达到预期的目的,且已经是一个废弃的项目。 - -因此,setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。 - -那么如何在一个干净的环境中安装 setuptools 呢? - -主要有两种方法: - -- 源码安装:在 https://pypi.org/project/setuptools/#files 中下载 zip 包 解压执行 `python setup.py install` 安装 -- 通过引导程序安装:下载引导程序,它可以用来下载或者更新最新版本的 setuptools - -```shell -$ wget http://peak.telecommunity.com/dist/ez_setup.py - -# 安装 -$ python ez_setup.py - -# 更新,以下两种任选 -$ python ez_setup.py –U setuptools -$ pip install -U setuptools -``` - - -## 4. easy_install 使用指南 - -当你安装完 setuptools 后,就拥有了一个叫做 `easy_install` 的第三方管理工具,这也是它区分于 distutils 的一大改进。 - -这里简单介绍一下它的用法,虽然它已经用得非常少了。 - -先是包的安装 - -```shell -# 通过包名,从PyPI寻找最新版本,自动下载、编译、安装 -$ easy_install pkg_name - -# 通过包名从指定下载页寻找链接来安装或升级包 -$ easy_install -f http://pythonpaste.org/package_index.html - -# 指定线上的包地址安装 -$ easy_install http://example.com/path/to/MyPackage-1.2.3.tgz - -# 从本地的 .egg 文件安装 -$ easy_install xxx.egg - -# 在安装时你可以添加额外的参数 -指定安装目录:--install-dir=DIR, -d DIR -指定用户安装:--user -``` - -再者是包的升级 - -```shell -# 从 pypi 中搜索并升级包 -$ easy_install --upgrade pkg_name - -# 指定版本进行升级 -$ easy_install "SomePackage==2.0" -``` - -最后是包的删除 - -```shell -$ easy_install -m pkg_name -``` - -需要注意的是,这样的删除,仅是在 easy-install.pth 文件中删除,使其不能在 python 中使用 这个模块,但实际的包还在你的电脑中,若要删除彻底,需要你手动删除相关的 .egg 及 其他文件。 - - - -默认情况下,easy_install 只会从 pypi 上下载相关软件包,由于这个源在国外,下载包的速度并不理想,使用过pip的朋友自然会想,easy_install 是否能指定源进行安装呢? - -答案是,可以的。 - -编辑配置文件 `/root/.pydistutils.cfg` - -```ini -[easy_install] -index-url=http://mirrors.aliyun.com/pypi/simple/ -find-links=http://mirrors.aliyun.com/pypi/simple/ -``` - -以上仅介绍了 easy_install 的一些常用的方法,想要了解更多,你可以点击官方文档:https://setuptools.readthedocs.io/en/latest/easy_install.html - - - -总结一句:setuptools 是官方提供的一个专业用于包分发的工具,若只从安装的角度来看,它的功能确实简单。它更大的意义是对包的分发很有用,定制化程序非常高,我们现在也还在用它进行版本包的发布。 - - - -## 5. 源码包与二进制包什么区别? - -Python 包的分发可以分为两种: - -1. 以源码包的方式发布 - -源码包安装的过程,是先解压,再编译,最后才安装,所以它是跨平台的,由于每次安装都要进行编译,相对二进包安装方式来说安装速度较慢。 - -源码包的本质是一个压缩包,其常见的格式有: - -![](http://image.iswbm.com/20191218202833.png) - -2. 以二进制包形式发布 - -二进制包的安装过程省去了编译的过程,直接进行解压安装,所以安装速度较源码包来说更快。 - -由于不同平台的编译出来的包无法通用,所以在发布时,需事先编译好多个平台的包。 - -二进制包的常见格式有: - -![](http://image.iswbm.com/20191218203005.png) - -## 6. eggs 与 wheels 有什么区别? - -Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012 年定义。Wheel 的出现是为了替代 Egg,它的本质是一个zip包,其现在被认为是 Python 的二进制包的标准格式。 - -以下是 Wheel 和 Egg 的主要区别: - -- Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义 -- Wheel 是一种分发格式,即打包格式。而 Egg 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import -- Wheel 文件不会包含 .pyc 文件 -- Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info 目录 -- Wheel 有着更丰富的命名规则。 -- Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现 -- Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易 - -wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块,然后再使用 pip 的命令。 - -```shell -$ pip install wheel -$ pip wheel --wheel-dir=/local/wheels pkg -``` - - - -## 7. 超详细讲解 setup.py 的编写? - -打包分发最关键的一步是编写 `setup.py` 文件。 - -以下是一个 setup.py 简单的使用示例 +使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下: ```python -from setuptools import setup, find_packages - -setup( - name="mytest", - version="1.0", - author="wangbm", - author_email="wongbingming@163.com", - description="Learn to Pack Python Module -->公众号:Python编程时光", - - # 项目主页 - url="http://iswbm.com/", - - # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包 - packages=find_packages() -) +>>> "llo" in "hello, python" +True +>>> +>>> "lol" in "hello, python" +False ``` -接下来,我将慢慢扩充这个setup函数,增加更多的参数,以便你能理解setup函数能做哪些事情。 - -**程序分类信息** - -`classifiers` 参数说明包的分类信息。所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers -示例: - -```python -from setuptools import setup, find_packages - -setup( - classifiers = [ - # 发展时期,常见的如下 - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable - 'Development Status :: 3 - Alpha', - - # 开发的目标用户 - 'Intended Audience :: Developers', - - # 属于什么类型 - 'Topic :: Software Development :: Build Tools', - - # 许可证信息 - 'License :: OSI Approved :: MIT License', - - # 目标 Python 版本 - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - ] -) -``` +## 2. 使用 find 方法 - -**关于文件的分发** +使用 字符串 对象的 find 方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回 `-1` ```python -from setuptools import setup, find_packages - - -setup( - name="mytest", - version="1.0", - author="wangbm", - author_email="wongbingming@163.com", - description="Learn to Pack Python Module", - url="http://iswbm.com/", - packages=find_packages(), - - # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等 - data_files=[ - ('', ['conf/*.conf']), - ('/usr/lib/systemd/system/', ['bin/*.service']), - ], - - # 希望被打包的文件 - package_data={ - '':['*.txt'], - 'bandwidth_reporter':['*.txt'] - }, - # 不打包某些文件 - exclude_package_data={ - 'bandwidth_reporter':['*.txt'] - } -) -``` - -除了以上的参数配置之外,还可以使用一个叫做 `MANIFEST.in` 的文件,来控制文件的分发。 - -如下这是一个 `MANIFEST.in` 的样例: - -``` -include *.txt -recursive-include examples *.txt *.py -prune examples/sample?/build +>>> "hello, python".find("llo") != -1 +True +>>> "hello, python".find("lol") != -1 +False +>> ``` -这些配置,规定了如下几点 -- 所有根目录下的以 txt 为后缀名的文件,都会分发 -- 根目录下的 examples 目录 和 txt、py文件都会分发 -- 路径匹配上 examples/sample?/build 不会分发 -`MANIFEST.in` 需要放在和 setup.py 同级的顶级目录下,setuptools 会自动读取该文件。 +## 3. 使用 index 方法 - - -**关于依赖包下载安装** +字符串对象有一个 index 方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。 ```python -from setuptools import setup, find_packages - - -setup( - ... - - # 表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装 - install_requires=['docutils>=0.3'], - - # setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置 - # 这里列出的包,不会自动安装。 - setup_requires=['pbr'], - - # 仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。 - # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。 - tests_require=[ - 'pytest>=3.3.1', - 'pytest-cov>=2.5.1', - ], - - # 用于安装setup_requires或tests_require里的软件包 - # 这些信息会写入egg的 metadata 信息中 - dependency_links=[ - "http://example2.com/p/foobar-1.0.tar.gz", - ], - - # install_requires 在安装模块时会自动安装依赖包 - # 而 extras_require 不会,这里仅表示该模块会依赖这些包 - # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装 - extras_require={ - 'PDF': ["ReportLab>=1.2", "RXP"], - 'reST': ["docutils>=0.3"], - } -) +def is_in(full_str, sub_str): + try: + full_str.index(sub_str) + return True + except ValueError: + return False +print(is_in("hello, python", "llo")) # True +print(is_in("hello, python", "lol")) # False ``` -关于 `install_requires`, 有以下五种常用的表示方法: - -1. `'argparse'`,只包含包名。 这种形式只检查包的存在性,不检查版本。 方便,但不利于控制风险。 -2. `'setuptools==38.2.4'`,指定版本。 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。 缺点是不利于更新,每次更新都需要改动代码。 -3. `'docutils >= 0.3'`,这是比较常用的形式。 当对某个库比较信任时,这种形式可以自动保持版本为最新。 -4. `'Django >= 1.11, != 1.11.1, <= 2'`,这是比较复杂的形式。 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。 对于一些大型、复杂的库,这种形式是最合适的。 -5. `'requests[security, socks] >= 2.18.4'`,这是包含了额外的可选依赖的形式。 正常安装requests会自动安装它的`install_requires`中指定的依赖,而不会安装`security`和`socks`这两组依赖。 这两组依赖是定义在它的`extras_require`中。 这种形式,用在深度使用某些库时。 +## 4. 使用 count 方法 -**关于安装环境的限制** +利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。 -有些库并不是在所以的 Python 版本中都适用的,若一个库安装在一个未兼容的 Python 环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。 - -这样的功能,可以使用 `python_requires` 来实现。 +只要判断结果大于 0 就说明子串存在于字符串中。 ```python -setup( - ... - python_requires='>=2.7, <=3', -) -``` - +def is_in(full_str, sub_str): + return full_str.count(sub_str) > 0 - -**生成可执行文件的分发** - -```python -from setuptools import setup, find_packages - - -setup( - name="mytest", - version="1.0", - author="wangbm", - author_email="wongbingming@163.com", - description="Learn to Pack Python Module", - url="http://iswbm.com/", - packages=find_packages(), - - # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件 - # 该文件入口指向 foo/main.py 的main 函数 - entry_points={ - 'console_scripts': [ - 'foo = foo.main:main' - ] - }, - - # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中 - # 执行 python setup.py install 后 - # 会生成 如 /usr/bin/foo.sh 和 如 /usr/bin/bar.py - scripts=['bin/foo.sh', 'bar.py'] -) +print(is_in("hello, python", "llo")) # True +print(is_in("hello, python", "lol")) # False ``` -上面的 scripts 里有的脚本中有 `sh` 和 `py` 后缀,那么安装后,setuptools 会原封不动的移动到 /usr/bin 中,并添加可执行权限。 -若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做 - -```python -from setuptools.command.install_scripts import install_scripts - -class InstallScripts(install_scripts): - - def run(self): - setuptools.command.install_scripts.install_scripts.run(self) - - # Rename some script files - for script in self.get_outputs(): - if basename.endswith(".py") or basename.endswith(".sh"): - dest = script[:-3] - else: - continue - print("moving %s to %s" % (script, dest)) - shutil.move(script, dest) - -setup( - ... - scripts=['bin/foo.sh', 'bar.py'], - - cmdclass={ - "install_scripts": InstallScripts - } -) -``` +## 5. 通过魔法方法 +在第一种方法中,我们使用 in 和 not in 判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in 时,Python 解释器会先去检查该对象是否有 `__contains__` 魔法方法。 -**ext_modules** +若有就执行它,若没有,Python 就自动会迭代整个序列,只要找到了需要的一项就返回 True 。 -`ext_modules` 参数用于构建 C 和 C++ 扩展扩展包。其是 Extension 实例的列表,每一个 Extension 实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如: +示例如下; ```python -setup( - # other arguments here... - ext_modules=[ - Extension('foo', - glob(path.join(here, 'src', '*.c')), - libraries = [ 'rt' ], - include_dirs=[numpy.get_include()]) - ] -) -``` - -详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options - - - -**指定release** - -setup.py 里只能指定 version,而不能指定 release,如果你需要变更版本号,可以使用 `--release` 参数进行指定 - -```shell -python setup.py bdist_rpm --release=20200617 -``` - - - -setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数: - -![](http://image.iswbm.com/20191218203255.png) - -更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html - -## 8. 打包辅助神器PBR 是什么? - -`pbr` 是 setuptools 的辅助工具,最初是为 OpenStack 开发(https://launchpad.net/pbr),基于`d2to1`。 - - - -`pbr` 会读取和过滤setup.cfg中的数据,然后将解析后的数据提供给 `setup.py` 作为参数。包含如下功能: - -1. 从git中获取Version、AUTHORS and ChangeLog信息 -2. Sphinx Autodoc。pbr 会扫描project,找到所有模块,生成stub files -3. Requirements。pbr会读取requirements.txt,生成setup函数需要的`install_requires/tests_require/dependency_links` - -这里需要注意,在 `requirements.txt` 文件的头部可以使用:`--index https://pypi.python.org/simple/`,这一行把一个抽象的依赖声明如 requests==1.2.0 转变为一个具体的依赖声明 requests 1.2.0 from pypi.python.org/simple/ - -4. long_description。从README.rst, README.txt or README file中生成`long_description`参数 - - - -使用pbr很简单: - -``` -from setuptools import setup - -setup( - setup_requires=['pbr'], - pbr=True, -) - -``` - -使用pbr时,setup.cfg中有一些配置。在[files]中,有三个key: -`packages`:指定需要包含的包,行为类似于setuptools.find_packages -`namespace_packages`:指定namespace packages -`data_files`: 指定目的目录和源文件路径,一个示例: - -``` -[files] -data_files = - etc/pbr = etc/pbr/* - etc/neutron = - etc/api-paste.ini - etc/dhcp-agent.ini - etc/init.d = neutron.init - -``` - -`[entry_points]` 段跟 setuptools 的方式相同。 - - - -到此,我讲了三种编写使用 setup.py 的方法 - -- 使用命令行参数指定,一个一个将参数传递进去(极不推荐) -- 在 setup.py 中的setup函数中指定(推荐使用) -- 使用 pbr ,在 setup.cfg 中指定(易于管理,更推荐) - -## 9. 如何使用 setup.py 构建包 - -1、构建源码发布包。 - -用于发布一个 Python 模块或项目,将源码打包成 tar.gz (用于 Linux 环境中)或者 zip 压缩包(用于 Windows 环境中) - -```shell -$ python setup.py sdist -``` - -那这种包如何安装呢? - -答案是,使用下一节即将介绍的 `setuptools` 中提供的 `easy_install` 工具。 - -```shell -$ easy_install xxx.tar.gz -``` - -使用 sdist 将根据当前平台创建默认格式的存档。在类 Unix 平台上,将创建后缀后为 `.tar.gz` 的 gzip 压缩的tar文件分发包,而在Windows上为 ZIP 文件。 - -当然,你也可以通过指定你要的发布包格式来打破这个默认行为 - -```shell -$ python setup.py sdist --formats=gztar,zip -``` - -你可以指定的格式有哪些呢? - -创建一个压缩的tarball和一个zip文件。可用格式为: - -![](http://image.iswbm.com/20191218203517.png) - -对以上的格式,有几点需要注意一下: - -- 在版本3.5中才添加了对 `xztar` 格式的支持 -- zip 格式需要你事先已安装相应的模块:zip程序或zipfile模块(已成为Python的标准库) -- ztar 格式正在弃用,请尽量不要使用 - -另外,如果您希望归档文件的所有文件归root拥有,可以这样指定 - -``` -python setup.py sdist --owner=root --group=root -``` - - - -2、构建二进制分发包。 - -在windows中我们习惯了双击 exe 进行软件的安装,Python 模块的安装也同样支持 打包成 exe 这样的二进制软件包。 - -```shell -$ python setup.py bdist_wininst -``` - -而在 Linux 中,大家也习惯了使用 rpm 来安装包,对此你可以使用这条命令实现 rpm 包的构建 - -```shell -$ python setup.py bdist_rpm +>>> "hello, python".__contains__("llo") +True +>>> +>>> "hello, python".__contains__("lol") +False +>>> ``` -若你喜欢使用 easy_install 或者 pip 来安装离线包。你可以将其打包成 egg 包 +这个用法与使用 in 和 not in 没有区别,但不排除有人会特意写成这样来增加代码的理解难度。 -```shell -$ python setup.py bdist_egg -``` - -若你的项目,需要安装多个平台下,既有 Windows 也有 Linux,按照上面的方法,多种格式我们要执行多次命令,为了方便,你可以一步到位,执行如下这条命令,即可生成多个格式的进制包 - -```shell -$ python setup.py bdist -``` - - - -## 10. 如何使用 setup.py 安装包 - -正常情况下,我们都是通过以上构建的源码包或者二进制包进行模块的安装。 - -但在编写 setup.py 的过程中,可能不能一步到位,需要多次调试,这时候如何测试自己写的 setup.py 文件是可用的呢? - -这时候你可以使用这条命令,它会将你的模块安装至系统全局环境中 - -```shell -$ python setup.py install -``` +## 6. 借助 operator -如若你的项目还处于开发阶段,频繁的安装模块,也是一个麻烦事。 +operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 python 代码快。 -这时候你可以使用这条命令安装,该方法不会真正的安装包,而是在系统环境中创建一个软链接指向包实际所在目录。这边在修改包之后不用再安装就能生效,便于调试。 +在 operator 中有一个方法 `contains` 可以很方便地判断子串是否在字符串中。 -```shell -$ python setup.py develop +```python +>>> import operator +>>> +>>> operator.contains("hello, python", "llo") +True +>>> operator.contains("hello, python", "lol") +False +>>> ``` -## 11. 如何发布包到 PyPi? - -通过上面的学习,你一定已经学会了如何打包自己的项目,若你觉得自己开发的模块非常不错,想要 share 给其他人使用,你可以将其上传到 PyPi (Python Package Index)上,它是 Python 官方维护的第三方包仓库,用于统一存储和管理开发者发布的 Python 包。 - - - -如果要发布自己的包,需要先到 pypi 上注册账号。然后创建 `~/.pypirc` 文件,此文件中配置 PyPI 访问地址和账号。如的.pypirc文件内容请根据自己的账号来修改。 - -典型的 .pypirc 文件 +## 7. 使用正则匹配 -```ini -[distutils] -index-servers = pypi +说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。 -[pypi] -username:xxx -password:xxx -``` - -然后使用这条命令进行信息注册,完成后,你可以在 PyPi 上看到项目信息。 +对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。 -```shell -$ python setup.py register -``` +```python +import re -注册完了后,你还要上传源码包,别人才使用下载安装 +def is_in(full_str, sub_str): + if re.findall(sub_str, full_str): + return True + else: + return False -```shell -$ python setup.py upload +print(is_in("hello, python", "llo")) # True +print(is_in("hello, python", "lol")) # False ``` -或者也可以使用 `twine` 工具注册上传,它是一个专门用于与 pypi 进行交互的工具,详情可以参考官网:https://www.ctolib.com/twine.html,这里不详细讲了。 - - -## 参考文章 -- http://blog.konghy.cn/2018/04/29/setup-dot-py/ -- https://note.qidong.name/2018/01/python-setup-requires/ +--- diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index c5a08e5..7bd620a 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -1,705 +1,154 @@ -1.27 全面学习 Python 包:包的构建与分发 -======================================= +1.40 Python 炫技操作:判断是否包含子串的七种方法 +================================================ |image0| - 首发于公众号:Python编程时光 +Python 语言里有许多(而且是越来越多)的高级特性,是 Python +发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 -1. 为什么需要对项目分发打包? ------------------------------ +但你要知道,在团队合作里,炫技是大忌。 -平常我们习惯了使用 pip -来安装一些第三方模块,这个安装过程之所以简单,是因为模块开发者为我们默默地为我们做了所有繁杂的工作,而这个过程就是 -``打包``\ 。 +为什么这么说呢?我说下自己的看法: -打包,就是将你的源代码进一步封装,并且将所有的项目部署工作都事先安排好,这样使用者拿到后即装即用,不用再操心如何部署的问题(如果你不想对照着一堆部署文档手工操作的话)。 +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) -不管你是在工作中,还是业余准备自己写一个可以上传到 PyPI -的项目,你都要学会如何打包你的项目。 - -Python 发展了这么些年了,项目打包工具也已经很成熟了。他们都有哪些呢? - -你可能听过 ``disutils``\ 、 ``distutils`` -、\ ``distutils2``\ 、\ ``setuptools``\ 等等,好像很熟悉,却又很陌生,他们都是什么关系呢? - -2. 包分发的始祖:distutils --------------------------- - -``distutils`` 是 Python -的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是 +该篇是「\ **炫技系列**\ 」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python -官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的。 - -``distutils`` 的精髓在于编写 setup.py,它是模块分发与安装的指导文件。 - -那么如何编写 setup.py -呢?这里面的内容非常多,我会在后面进行详细的解析,请你耐心往下看。 - -你有可能没写过 setup.py ,但你绝对使用过 setup.py -来做一些事情,比如下面这条命令,我们经常用它来进行模块的安装。 - -.. code:: shell - - $ python setup.py install - -这样的安装方法是通过源码安装,与之对应的是通过二进制软件包的安装,同样我也会在后面进行介绍。 - -3. 分发工具升级:setuptools ---------------------------- - -``setuptools`` 是 distutils -增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 -Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。 - -**distribute**\ ,或许你在其他地方也见过它,这里也提一下。 - -distribute 是 setuptools -有一个分支版本,分支的原因可能是有一部分开发者认为 setuptools -开发太慢了。但现在,distribute 又合并回了 setuptools -中。因此,我们可以认为它们是同一个东西。 - -还有一个大包分发工具是 -**distutils2**\ ,其试图尝试充分利用distutils,detuptools 和 distribute -并成为 Python -标准库中的标准工具。但该计划并没有达到预期的目的,且已经是一个废弃的项目。 - -因此,setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。 - -那么如何在一个干净的环境中安装 setuptools 呢? - -主要有两种方法: - -- 源码安装:在 https://pypi.org/project/setuptools/#files 中下载 zip 包 - 解压执行 ``python setup.py install`` 安装 -- 通过引导程序安装:下载引导程序,它可以用来下载或者更新最新版本的 - setuptools - -.. code:: shell - - $ wget http://peak.telecommunity.com/dist/ez_setup.py - - # 安装 - $ python ez_setup.py - - # 更新,以下两种任选 - $ python ez_setup.py –U setuptools - $ pip install -U setuptools - -4. easy_install 使用指南 ------------------------- - -当你安装完 setuptools 后,就拥有了一个叫做 ``easy_install`` -的第三方管理工具,这也是它区分于 distutils 的一大改进。 - -这里简单介绍一下它的用法,虽然它已经用得非常少了。 - -先是包的安装 - -.. code:: shell - - # 通过包名,从PyPI寻找最新版本,自动下载、编译、安装 - $ easy_install pkg_name - - # 通过包名从指定下载页寻找链接来安装或升级包 - $ easy_install -f http://pythonpaste.org/package_index.html - - # 指定线上的包地址安装 - $ easy_install http://example.com/path/to/MyPackage-1.2.3.tgz - - # 从本地的 .egg 文件安装 - $ easy_install xxx.egg - - # 在安装时你可以添加额外的参数 - 指定安装目录:--install-dir=DIR, -d DIR - 指定用户安装:--user - -再者是包的升级 - -.. code:: shell - - # 从 pypi 中搜索并升级包 - $ easy_install --upgrade pkg_name - - # 指定版本进行升级 - $ easy_install "SomePackage==2.0" - -最后是包的删除 - -.. code:: shell +发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - $ easy_install -m pkg_name +1. 使用 in 和 not in +-------------------- -需要注意的是,这样的删除,仅是在 easy-install.pth 文件中删除,使其不能在 -python 中使用 -这个模块,但实际的包还在你的电脑中,若要删除彻底,需要你手动删除相关的 -.egg 及 其他文件。 +``in`` 和 ``not in`` 在 Python 中是很常用的关键字,我们将它们归类为 +``成员运算符``\ 。 -默认情况下,easy_install 只会从 pypi -上下载相关软件包,由于这个源在国外,下载包的速度并不理想,使用过pip的朋友自然会想,easy_install -是否能指定源进行安装呢? - -答案是,可以的。 - -编辑配置文件 ``/root/.pydistutils.cfg`` - -.. code:: ini - - [easy_install] - index-url=http://mirrors.aliyun.com/pypi/simple/ - find-links=http://mirrors.aliyun.com/pypi/simple/ - -以上仅介绍了 easy_install -的一些常用的方法,想要了解更多,你可以点击官方文档:https://setuptools.readthedocs.io/en/latest/easy_install.html - -总结一句:setuptools -是官方提供的一个专业用于包分发的工具,若只从安装的角度来看,它的功能确实简单。它更大的意义是对包的分发很有用,定制化程序非常高,我们现在也还在用它进行版本包的发布。 - -5. 源码包与二进制包什么区别? ------------------------------ - -Python 包的分发可以分为两种: - -1. 以源码包的方式发布 - -源码包安装的过程,是先解压,再编译,最后才安装,所以它是跨平台的,由于每次安装都要进行编译,相对二进包安装方式来说安装速度较慢。 - -源码包的本质是一个压缩包,其常见的格式有: - -|image1| - -2. 以二进制包形式发布 - -二进制包的安装过程省去了编译的过程,直接进行解压安装,所以安装速度较源码包来说更快。 - -由于不同平台的编译出来的包无法通用,所以在发布时,需事先编译好多个平台的包。 - -二进制包的常见格式有: - -|image2| - -6. eggs 与 wheels 有什么区别? ------------------------------- - -Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012 -年定义。Wheel 的出现是为了替代 Egg,它的本质是一个zip包,其现在被认为是 -Python 的二进制包的标准格式。 - -以下是 Wheel 和 Egg 的主要区别: - -- Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义 -- Wheel 是一种分发格式,即打包格式。而 Egg - 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import -- Wheel 文件不会包含 .pyc 文件 -- Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info - 目录 -- Wheel 有着更丰富的命名规则。 -- Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现 -- Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易 - -wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块,然后再使用 pip -的命令。 - -.. code:: shell - - $ pip install wheel - $ pip wheel --wheel-dir=/local/wheels pkg - -7. 超详细讲解 setup.py 的编写? -------------------------------- - -打包分发最关键的一步是编写 ``setup.py`` 文件。 - -以下是一个 setup.py 简单的使用示例 +使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下: .. code:: python - from setuptools import setup, find_packages - - setup( - name="mytest", - version="1.0", - author="wangbm", - author_email="wongbingming@163.com", - description="Learn to Pack Python Module -->公众号:Python编程时光", - - # 项目主页 - url="http://iswbm.com/", - - # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包 - packages=find_packages() - ) - -接下来,我将慢慢扩充这个setup函数,增加更多的参数,以便你能理解setup函数能做哪些事情。 + >>> "llo" in "hello, python" + True + >>> + >>> "lol" in "hello, python" + False -**程序分类信息** +2. 使用 find 方法 +----------------- -``classifiers`` -参数说明包的分类信息。所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers - -示例: +使用 字符串 对象的 find +方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回 +``-1`` .. code:: python - from setuptools import setup, find_packages - - setup( - classifiers = [ - # 发展时期,常见的如下 - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable - 'Development Status :: 3 - Alpha', - - # 开发的目标用户 - 'Intended Audience :: Developers', - - # 属于什么类型 - 'Topic :: Software Development :: Build Tools', - - # 许可证信息 - 'License :: OSI Approved :: MIT License', + >>> "hello, python".find("llo") != -1 + True + >>> "hello, python".find("lol") != -1 + False + >> - # 目标 Python 版本 - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - ] - ) +3. 使用 index 方法 +------------------ -**关于文件的分发** +字符串对象有一个 index +方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。 .. code:: python - from setuptools import setup, find_packages - - - setup( - name="mytest", - version="1.0", - author="wangbm", - author_email="wongbingming@163.com", - description="Learn to Pack Python Module", - url="http://iswbm.com/", - packages=find_packages(), - - # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等 - data_files=[ - ('', ['conf/*.conf']), - ('/usr/lib/systemd/system/', ['bin/*.service']), - ], - - # 希望被打包的文件 - package_data={ - '':['*.txt'], - 'bandwidth_reporter':['*.txt'] - }, - # 不打包某些文件 - exclude_package_data={ - 'bandwidth_reporter':['*.txt'] - } - ) - -除了以上的参数配置之外,还可以使用一个叫做 ``MANIFEST.in`` -的文件,来控制文件的分发。 - -如下这是一个 ``MANIFEST.in`` 的样例: - -:: - - include *.txt - recursive-include examples *.txt *.py - prune examples/sample?/build - -这些配置,规定了如下几点 - -- 所有根目录下的以 txt 为后缀名的文件,都会分发 -- 根目录下的 examples 目录 和 txt、py文件都会分发 -- 路径匹配上 examples/sample?/build 不会分发 - -``MANIFEST.in`` 需要放在和 setup.py 同级的顶级目录下,setuptools -会自动读取该文件。 - -**关于依赖包下载安装** - -.. code:: python - - from setuptools import setup, find_packages - - - setup( - ... - - # 表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装 - install_requires=['docutils>=0.3'], - - # setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置 - # 这里列出的包,不会自动安装。 - setup_requires=['pbr'], - - # 仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。 - # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。 - tests_require=[ - 'pytest>=3.3.1', - 'pytest-cov>=2.5.1', - ], - - # 用于安装setup_requires或tests_require里的软件包 - # 这些信息会写入egg的 metadata 信息中 - dependency_links=[ - "http://example2.com/p/foobar-1.0.tar.gz", - ], - - # install_requires 在安装模块时会自动安装依赖包 - # 而 extras_require 不会,这里仅表示该模块会依赖这些包 - # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装 - extras_require={ - 'PDF': ["ReportLab>=1.2", "RXP"], - 'reST': ["docutils>=0.3"], - } - ) - -关于 ``install_requires``\ , 有以下五种常用的表示方法: - -1. ``'argparse'``\ ,只包含包名。 这种形式只检查包的存在性,不检查版本。 - 方便,但不利于控制风险。 -2. ``'setuptools==38.2.4'``\ ,指定版本。 - 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。 - 缺点是不利于更新,每次更新都需要改动代码。 -3. ``'docutils >= 0.3'``\ ,这是比较常用的形式。 - 当对某个库比较信任时,这种形式可以自动保持版本为最新。 -4. ``'Django >= 1.11, != 1.11.1, <= 2'``\ ,这是比较复杂的形式。 - 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。 - 对于一些大型、复杂的库,这种形式是最合适的。 -5. ``'requests[security, socks] >= 2.18.4'``\ ,这是包含了额外的可选依赖的形式。 - 正常安装requests会自动安装它的\ ``install_requires``\ 中指定的依赖,而不会安装\ ``security``\ 和\ ``socks``\ 这两组依赖。 - 这两组依赖是定义在它的\ ``extras_require``\ 中。 - 这种形式,用在深度使用某些库时。 - -**关于安装环境的限制** - -有些库并不是在所以的 Python 版本中都适用的,若一个库安装在一个未兼容的 -Python -环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。 - -这样的功能,可以使用 ``python_requires`` 来实现。 + def is_in(full_str, sub_str): + try: + full_str.index(sub_str) + return True + except ValueError: + return False -.. code:: python + print(is_in("hello, python", "llo")) # True + print(is_in("hello, python", "lol")) # False - setup( - ... - python_requires='>=2.7, <=3', - ) +4. 使用 count 方法 +------------------ -**生成可执行文件的分发** +利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。 -.. code:: python - - from setuptools import setup, find_packages - - - setup( - name="mytest", - version="1.0", - author="wangbm", - author_email="wongbingming@163.com", - description="Learn to Pack Python Module", - url="http://iswbm.com/", - packages=find_packages(), - - # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件 - # 该文件入口指向 foo/main.py 的main 函数 - entry_points={ - 'console_scripts': [ - 'foo = foo.main:main' - ] - }, - - # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中 - # 执行 python setup.py install 后 - # 会生成 如 /usr/bin/foo.sh 和 如 /usr/bin/bar.py - scripts=['bin/foo.sh', 'bar.py'] - ) - -上面的 scripts 里有的脚本中有 ``sh`` 和 ``py`` -后缀,那么安装后,setuptools 会原封不动的移动到 /usr/bin -中,并添加可执行权限。 - -若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做 +只要判断结果大于 0 就说明子串存在于字符串中。 .. code:: python - from setuptools.command.install_scripts import install_scripts - - class InstallScripts(install_scripts): + def is_in(full_str, sub_str): + return full_str.count(sub_str) > 0 - def run(self): - setuptools.command.install_scripts.install_scripts.run(self) + print(is_in("hello, python", "llo")) # True + print(is_in("hello, python", "lol")) # False - # Rename some script files - for script in self.get_outputs(): - if basename.endswith(".py") or basename.endswith(".sh"): - dest = script[:-3] - else: - continue - print("moving %s to %s" % (script, dest)) - shutil.move(script, dest) +5. 通过魔法方法 +--------------- - setup( - ... - scripts=['bin/foo.sh', 'bar.py'], - - cmdclass={ - "install_scripts": InstallScripts - } - ) +在第一种方法中,我们使用 in 和 not in +判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in +时,Python 解释器会先去检查该对象是否有 ``__contains__`` 魔法方法。 -**ext_modules** +若有就执行它,若没有,Python +就自动会迭代整个序列,只要找到了需要的一项就返回 True 。 -``ext_modules`` 参数用于构建 C 和 C++ 扩展扩展包。其是 Extension -实例的列表,每一个 Extension -实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如: +示例如下; .. code:: python - setup( - # other arguments here... - ext_modules=[ - Extension('foo', - glob(path.join(here, 'src', '*.c')), - libraries = [ 'rt' ], - include_dirs=[numpy.get_include()]) - ] - ) - -详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options - -**指定release** - -setup.py 里只能指定 version,而不能指定 -release,如果你需要变更版本号,可以使用 ``--release`` 参数进行指定 - -.. code:: shell - - python setup.py bdist_rpm --release=20200617 - -setup.py -的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 -setup 函数常用的一些参数: - -|image3| - -更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html - -8. 打包辅助神器PBR 是什么? ---------------------------- - -``pbr`` 是 setuptools 的辅助工具,最初是为 OpenStack -开发(https://launchpad.net/pbr),基于\ ``d2to1``\ 。 - -``pbr`` 会读取和过滤setup.cfg中的数据,然后将解析后的数据提供给 -``setup.py`` 作为参数。包含如下功能: - -1. 从git中获取Version、AUTHORS and ChangeLog信息 -2. Sphinx Autodoc。pbr 会扫描project,找到所有模块,生成stub files -3. Requirements。pbr会读取requirements.txt,生成setup函数需要的\ ``install_requires/tests_require/dependency_links`` - -这里需要注意,在 ``requirements.txt`` -文件的头部可以使用:\ ``--index https://pypi.python.org/simple/``\ ,这一行把一个抽象的依赖声明如 -requests==1.2.0 转变为一个具体的依赖声明 requests 1.2.0 from -pypi.python.org/simple/ - -4. long_description。从README.rst, README.txt or README - file中生成\ ``long_description``\ 参数 - -使用pbr很简单: - -:: - - from setuptools import setup - - setup( - setup_requires=['pbr'], - pbr=True, - ) - -使用pbr时,setup.cfg中有一些配置。在[files]中,有三个key: -``packages``:指定需要包含的包,行为类似于setuptools.find_packages -``namespace_packages``:指定namespace packages ``data_files``: -指定目的目录和源文件路径,一个示例: - -:: - - [files] - data_files = - etc/pbr = etc/pbr/* - etc/neutron = - etc/api-paste.ini - etc/dhcp-agent.ini - etc/init.d = neutron.init - -``[entry_points]`` 段跟 setuptools 的方式相同。 - -到此,我讲了三种编写使用 setup.py 的方法 - -- 使用命令行参数指定,一个一个将参数传递进去(极不推荐) -- 在 setup.py 中的setup函数中指定(推荐使用) -- 使用 pbr ,在 setup.cfg 中指定(易于管理,更推荐) - -9. 如何使用 setup.py 构建包 ---------------------------- - -1、构建源码发布包。 - -用于发布一个 Python 模块或项目,将源码打包成 tar.gz (用于 Linux -环境中)或者 zip 压缩包(用于 Windows 环境中) - -.. code:: shell - - $ python setup.py sdist - -那这种包如何安装呢? - -答案是,使用下一节即将介绍的 ``setuptools`` 中提供的 ``easy_install`` -工具。 - -.. code:: shell - - $ easy_install xxx.tar.gz - -使用 sdist 将根据当前平台创建默认格式的存档。在类 Unix -平台上,将创建后缀后为 ``.tar.gz`` 的 gzip -压缩的tar文件分发包,而在Windows上为 ZIP 文件。 + >>> "hello, python".__contains__("llo") + True + >>> + >>> "hello, python".__contains__("lol") + False + >>> -当然,你也可以通过指定你要的发布包格式来打破这个默认行为 +这个用法与使用 in 和 not in +没有区别,但不排除有人会特意写成这样来增加代码的理解难度。 -.. code:: shell +6. 借助 operator +---------------- - $ python setup.py sdist --formats=gztar,zip +operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 +python 代码快。 -你可以指定的格式有哪些呢? +在 operator 中有一个方法 ``contains`` +可以很方便地判断子串是否在字符串中。 -创建一个压缩的tarball和一个zip文件。可用格式为: - -|image4| - -对以上的格式,有几点需要注意一下: - -- 在版本3.5中才添加了对 ``xztar`` 格式的支持 -- zip - 格式需要你事先已安装相应的模块:zip程序或zipfile模块(已成为Python的标准库) -- ztar 格式正在弃用,请尽量不要使用 - -另外,如果您希望归档文件的所有文件归root拥有,可以这样指定 - -:: - - python setup.py sdist --owner=root --group=root - -2、构建二进制分发包。 - -在windows中我们习惯了双击 exe 进行软件的安装,Python -模块的安装也同样支持 打包成 exe 这样的二进制软件包。 - -.. code:: shell - - $ python setup.py bdist_wininst - -而在 Linux 中,大家也习惯了使用 rpm 来安装包,对此你可以使用这条命令实现 -rpm 包的构建 - -.. code:: shell - - $ python setup.py bdist_rpm - -若你喜欢使用 easy_install 或者 pip 来安装离线包。你可以将其打包成 egg 包 - -.. code:: shell - - $ python setup.py bdist_egg - -若你的项目,需要安装多个平台下,既有 Windows 也有 -Linux,按照上面的方法,多种格式我们要执行多次命令,为了方便,你可以一步到位,执行如下这条命令,即可生成多个格式的进制包 - -.. code:: shell - - $ python setup.py bdist - -10. 如何使用 setup.py 安装包 ----------------------------- - -正常情况下,我们都是通过以上构建的源码包或者二进制包进行模块的安装。 - -但在编写 setup.py -的过程中,可能不能一步到位,需要多次调试,这时候如何测试自己写的 -setup.py 文件是可用的呢? - -这时候你可以使用这条命令,它会将你的模块安装至系统全局环境中 - -.. code:: shell - - $ python setup.py install - -如若你的项目还处于开发阶段,频繁的安装模块,也是一个麻烦事。 - -这时候你可以使用这条命令安装,该方法不会真正的安装包,而是在系统环境中创建一个软链接指向包实际所在目录。这边在修改包之后不用再安装就能生效,便于调试。 - -.. code:: shell - - $ python setup.py develop - -11. 如何发布包到 PyPi? ------------------------ - -通过上面的学习,你一定已经学会了如何打包自己的项目,若你觉得自己开发的模块非常不错,想要 -share 给其他人使用,你可以将其上传到 PyPi (Python Package -Index)上,它是 Python -官方维护的第三方包仓库,用于统一存储和管理开发者发布的 Python 包。 - -如果要发布自己的包,需要先到 pypi 上注册账号。然后创建 ``~/.pypirc`` -文件,此文件中配置 PyPI -访问地址和账号。如的.pypirc文件内容请根据自己的账号来修改。 - -典型的 .pypirc 文件 - -.. code:: ini - - [distutils] - index-servers = pypi - - [pypi] - username:xxx - password:xxx +.. code:: python -然后使用这条命令进行信息注册,完成后,你可以在 PyPi 上看到项目信息。 + >>> import operator + >>> + >>> operator.contains("hello, python", "llo") + True + >>> operator.contains("hello, python", "lol") + False + >>> -.. code:: shell +7. 使用正则匹配 +--------------- - $ python setup.py register +说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。 -注册完了后,你还要上传源码包,别人才使用下载安装 +对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。 -.. code:: shell +.. code:: python - $ python setup.py upload + import re -或者也可以使用 ``twine`` 工具注册上传,它是一个专门用于与 pypi -进行交互的工具,详情可以参考官网:https://www.ctolib.com/twine.html,这里不详细讲了。 + def is_in(full_str, sub_str): + if re.findall(sub_str, full_str): + return True + else: + return False -参考文章 --------- + print(is_in("hello, python", "llo")) # True + print(is_in("hello, python", "lol")) # False -- http://blog.konghy.cn/2018/04/29/setup-dot-py/ -- https://note.qidong.name/2018/01/python-setup-requires/ +-------------- -|image5| +|image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20191218202833.png -.. |image2| image:: http://image.iswbm.com/20191218203005.png -.. |image3| image:: http://image.iswbm.com/20191218203255.png -.. |image4| image:: http://image.iswbm.com/20191218203517.png -.. |image5| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_41.md b/source/c01/c01_28.md similarity index 100% rename from source/c01/c01_41.md rename to source/c01/c01_28.md diff --git a/source/c01/c01_41.rst b/source/c01/c01_28.rst similarity index 100% rename from source/c01/c01_41.rst rename to source/c01/c01_28.rst diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md index 16db089..5c92a8e 100644 --- a/source/c01/c01_29.md +++ b/source/c01/c01_29.md @@ -1,12 +1,174 @@ -# 1.27 如何阅读 CPython源码? +# 1.42 Python 炫技操作:海象运算符的三种用法 ![](http://image.iswbm.com/20200602135014.png) +Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 +很多 Python 3.8 的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 -参考学习地址:https://realpython.com/cpython-source-code-guide/ +海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 -基于 Python 3.6 的源码分析:https://he11olx.com/ +它的英文原名叫 `Assignment Expressions`,翻译过来也就是 `赋值表达式`,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 + +![](http://image.iswbm.com/image-20200418122739417.png) + +## 1. 第一个用法:if/else + +可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? + +在 Golang 中的条件语句可以直接在 if 中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 + +```go +import "fmt" + +func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } +} +``` + +若在 Python 3.8 之前,Python 必须得这样子写 + +```python +age = 20 +if age > 18: + print("已经成年了") +``` + +但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 Golang,那这里要注意,Golang 中的 `:=` 叫短变量声明,意思是声明并初始化,它和 Python 中的 `:=` 不是一个概念) + +```python +if (age:= 20) > 18: + print("已经成年了") +``` + + + +## 2. 第二个用法:while + +在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 + +```python +file = open("demo.txt", "r") +while True: + line = file.readline() + if not line: + break + print(line.strip()) +``` + +但有了海象运算符之后,你可以这样 + +```python +file = open("demo.txt", "r") +while (line := file.readline()): + print(line.strip()) +``` + +使用它替换以往的无限 while 循环写法更为惊艳 + +比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 + +```python +while True: + p = input("Enter the password: ") + if p == "youpassword": + break +``` + +有了海象运算符之后,这样子写更为舒服 + +```python +while (p := input("Enter the password: ")) != "youpassword": + continue +``` + + + +## 3. 第三个用法:推导式 + +这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 + +在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 + +如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 + +```python +members = [ + {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, + {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, + {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, +] + +count = 0 + +def get_bmi(info): + global count + count += 1 + + print(f"执行了 {count} 次") + + height = info["height"] + weight = info["weight"] + + return weight / (height**2) + +# 查出所有会员中过于肥胖的人的 bmi 指数 +fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] + +print(fat_bmis) +``` + +输出如下 + +``` +执行了 1 次 +执行了 2 次 +执行了 3 次 +执行了 4 次 +[25.88057063502083] +``` + +可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 + +如果所有会员都是过于肥胖的,那最终将执行 6 次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for 循环 + if 判断。 + +```python +fat_bmis = [] + +# 查出所有会员中过于肥胖的人的 bmi 指数 +for m in members: + bmi = get_bmi(m) + if bmi > 24: + fat_bmis.append(bmi) +``` + +在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 + +```python +# 查出所有会员中过于肥胖的人的 bmi 指数 +fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] +``` + +最终从输出结果可以看出,只执行了 3 次 + +``` +执行了 1 次 +执行了 2 次 +执行了 3 次 +[25.88057063502083] +``` + +这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 + + + +海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 + + + +--- diff --git a/source/c01/c01_29.rst b/source/c01/c01_29.rst index f801300..9df2a34 100644 --- a/source/c01/c01_29.rst +++ b/source/c01/c01_29.rst @@ -1,14 +1,183 @@ -1.27 如何阅读 CPython源码? -=========================== +1.42 Python 炫技操作:海象运算符的三种用法 +========================================== |image0| -参考学习地址:https://realpython.com/cpython-source-code-guide/ +Python 版本发展非常快,如今最新的版本已经是 Pyhton +3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 -基于 Python 3.6 的源码分析:https://he11olx.com/ +很多 Python 3.8 +的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 + +海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 + +它的英文原名叫 ``Assignment Expressions``\ ,翻译过来也就是 +``赋值表达式``\ ,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 |image1| +1. 第一个用法:if/else +---------------------- + +可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? + +在 Golang 中的条件语句可以直接在 if +中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 + +.. code:: go + + import "fmt" + + func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } + } + +若在 Python 3.8 之前,Python 必须得这样子写 + +.. code:: python + + age = 20 + if age > 18: + print("已经成年了") + +但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 +Golang,那这里要注意,Golang 中的 ``:=`` +叫短变量声明,意思是声明并初始化,它和 Python 中的 ``:=`` 不是一个概念) + +.. code:: python + + if (age:= 20) > 18: + print("已经成年了") + +2. 第二个用法:while +-------------------- + +在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 + +.. code:: python + + file = open("demo.txt", "r") + while True: + line = file.readline() + if not line: + break + print(line.strip()) + +但有了海象运算符之后,你可以这样 + +.. code:: python + + file = open("demo.txt", "r") + while (line := file.readline()): + print(line.strip()) + +使用它替换以往的无限 while 循环写法更为惊艳 + +比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 + +.. code:: python + + while True: + p = input("Enter the password: ") + if p == "youpassword": + break + +有了海象运算符之后,这样子写更为舒服 + +.. code:: python + + while (p := input("Enter the password: ")) != "youpassword": + continue + +3. 第三个用法:推导式 +--------------------- + +这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 + +在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 + +如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 + +.. code:: python + + members = [ + {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, + {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, + {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, + ] + + count = 0 + + def get_bmi(info): + global count + count += 1 + + print(f"执行了 {count} 次") + + height = info["height"] + weight = info["weight"] + + return weight / (height**2) + + # 查出所有会员中过于肥胖的人的 bmi 指数 + fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] + + print(fat_bmis) + +输出如下 + +:: + + 执行了 1 次 + 执行了 2 次 + 执行了 3 次 + 执行了 4 次 + [25.88057063502083] + +可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 +次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 + +如果所有会员都是过于肥胖的,那最终将执行 6 +次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for +循环 + if 判断。 + +.. code:: python + + fat_bmis = [] + + # 查出所有会员中过于肥胖的人的 bmi 指数 + for m in members: + bmi = get_bmi(m) + if bmi > 24: + fat_bmis.append(bmi) + +在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 + +.. code:: python + + # 查出所有会员中过于肥胖的人的 bmi 指数 + fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] + +最终从输出结果可以看出,只执行了 3 次 + +:: + + 执行了 1 次 + 执行了 2 次 + 执行了 3 次 + [25.88057063502083] + +这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 + +海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 + +-------------- + +|image2| + .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/image-20200418122739417.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_31.md b/source/c01/c01_31.md deleted file mode 100644 index 870abb5..0000000 --- a/source/c01/c01_31.md +++ /dev/null @@ -1,25 +0,0 @@ -# 1.31 学习 Pillow 笔记 - -![](http://image.iswbm.com/20200602135014.png) - - - -## 1. 安装 pillow - -``` -pip install pillow -``` - - - -## 2. 使用 - -ARGB 是一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。 - -RGB 色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 - - - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_31.rst b/source/c01/c01_31.rst deleted file mode 100644 index 536d407..0000000 --- a/source/c01/c01_31.rst +++ /dev/null @@ -1,26 +0,0 @@ -1.31 学习 Pillow 笔记 -===================== - -|image0| - -1. 安装 pillow --------------- - -:: - - pip install pillow - -2. 使用 -------- - -ARGB -是一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。 - -RGB -色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。 - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_32.md b/source/c01/c01_32.md deleted file mode 100644 index 9d8e2f4..0000000 --- a/source/c01/c01_32.md +++ /dev/null @@ -1,39 +0,0 @@ -# 1.32 在 CentOS 7.2 上安装 Python3.7 - -![](http://image.iswbm.com/20200602135014.png) - -首先下载 python3.7的源码包,然后解压 - -```shell -$ cd ~ -$ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz -$ tar xf Python-3.7.1.tgz && cd Python-3.7.1 -``` - -安装 一些依赖包 - -```shell -$ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y -``` - -编译安装 - -```shell -$ ./configure -$ make -$ sudo make install -``` - -至此,你已经成功安装 了 Python3, pip3,setuptools - -requests.get("https://www.baidu.com") - -``` -python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple -``` - - - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_32.rst b/source/c01/c01_32.rst deleted file mode 100644 index 4317fad..0000000 --- a/source/c01/c01_32.rst +++ /dev/null @@ -1,40 +0,0 @@ -1.32 在 CentOS 7.2 上安装 Python3.7 -=================================== - -|image0| - -首先下载 python3.7的源码包,然后解压 - -.. code:: shell - - $ cd ~ - $ wget -c https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz - $ tar xf Python-3.7.1.tgz && cd Python-3.7.1 - -安装 一些依赖包 - -.. code:: shell - - $ yum install gcc zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel libffi-devel python3-devel -y - -编译安装 - -.. code:: shell - - $ ./configure - $ make - $ sudo make install - -至此,你已经成功安装 了 Python3, pip3,setuptools - -requests.get(“https://www.baidu.com”) - -:: - - python3 -m pip install --user requests aiohttp cryptography pymysql prettytable sh Fabric paramiko apscheduler bashplotlib httpie PathPicker -i https://pypi.douban.com/simple - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_34.md b/source/c01/c01_34.md deleted file mode 100644 index b0df514..0000000 --- a/source/c01/c01_34.md +++ /dev/null @@ -1,92 +0,0 @@ -# 1.34 每日一库:sh,最优雅的命令调用方式 - -![](http://image.iswbm.com/20200602135014.png) - -在编写 Python 脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 os.popen,os.system,commands,还有 subprocess。 - -今天明哥要介绍一种更加优雅的方法,就是 `sh` 这个第三方库,它能让你像调用方法那样去调用系统中的命令。 - -使用前,当然是安装它,`sh` 支持 Python 2 也支持 Python3,这里以 Python 3 为例。 - -```shell -$ python3 -m pip install sh -``` - -这里要注意一点,虽然在 Windows 上也可以安装成功,但是并不能使用,如果你尝试在 Windows 导入 它,会友好地提示你,该库只适用于 Linux 及 OSX 系统,假如你也想要在 Windows 使用,它推荐你使用它的 兄弟库 - `pbs` (https://pypi.org/project/pbs/)。 - -![](http://image.iswbm.com/20200227201644.png) - - - -安装完成后,就可以直接使用它了,以下几个示例,非常简单,简单到我感觉只要 demo ,而不需要任何的中文解释就可以让你知道他是如何使用的。 - - - -### 1. 列出目录文件 - -使用 `ls` - -```python ->>> import sh ->>> sh.ls("/home", "-l", color="never") -total 4 -drwx------ 3 centos centos 4096 Mar 8 2019 centos -``` - -使用 `glob` - -```python ->>> sh.glob("/etc/*.conf") -['/etc/mke2fs.conf', '/etc/dnsmasq.conf', '/etc/asound.conf'] -``` - - - -### 调用程序 - -```python ->>> import sh ->>> r=sh.Command('/root/test.py') ->>> r() -hello,world -``` - -上面我们的 ls ,也可以通过这种方式执行,只是不能再加参数了。 - -```python -sh.Command("ls")() -``` - - - -### 管道 - -```python ->>> print(sh.sort(sh.du(sh.glob('*'),'-shc'),'-rn')) -712K distribute-0.6.49.tar.gz -672K setuptools-1.1.5.tar.gz -548K get-pip.py -``` - -管道是有序的,默认由内而外,但如果需要并行呢?加个_piped=True - -```python ->>> for line in sh.tr(sh.tail("-f", "/home/mysql/mysql/log/alert.log", _piped=True), "[:upper:]", "[:lower:]", _iter=True): -... print line -... -innodb: doublewrite buffer not found: creating new - -innodb: doublewrite buffer created - -innodb: 127 rollback segment(s) active. - -innodb: creating foreign key constraint system tables - -innodb: foreign key constraint system tables created -``` - - - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_34.rst b/source/c01/c01_34.rst deleted file mode 100644 index 4ebfe1e..0000000 --- a/source/c01/c01_34.rst +++ /dev/null @@ -1,98 +0,0 @@ -1.34 每日一库:sh,最优雅的命令调用方式 -======================================= - -|image0| - -在编写 Python -脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 -os.popen,os.system,commands,还有 subprocess。 - -今天明哥要介绍一种更加优雅的方法,就是 ``sh`` -这个第三方库,它能让你像调用方法那样去调用系统中的命令。 - -使用前,当然是安装它,\ ``sh`` 支持 Python 2 也支持 Python3,这里以 -Python 3 为例。 - -.. code:: shell - - $ python3 -m pip install sh - -这里要注意一点,虽然在 Windows -上也可以安装成功,但是并不能使用,如果你尝试在 Windows 导入 -它,会友好地提示你,该库只适用于 Linux 及 OSX 系统,假如你也想要在 -Windows 使用,它推荐你使用它的 兄弟库 - ``pbs`` -(https://pypi.org/project/pbs/)。 - -|image1| - -安装完成后,就可以直接使用它了,以下几个示例,非常简单,简单到我感觉只要 -demo ,而不需要任何的中文解释就可以让你知道他是如何使用的。 - -1. 列出目录文件 -~~~~~~~~~~~~~~~ - -使用 ``ls`` - -.. code:: python - - >>> import sh - >>> sh.ls("/home", "-l", color="never") - total 4 - drwx------ 3 centos centos 4096 Mar 8 2019 centos - -使用 ``glob`` - -.. code:: python - - >>> sh.glob("/etc/*.conf") - ['/etc/mke2fs.conf', '/etc/dnsmasq.conf', '/etc/asound.conf'] - -调用程序 -~~~~~~~~ - -.. code:: python - - >>> import sh - >>> r=sh.Command('/root/test.py') - >>> r() - hello,world - -上面我们的 ls ,也可以通过这种方式执行,只是不能再加参数了。 - -.. code:: python - - sh.Command("ls")() - -管道 -~~~~ - -.. code:: python - - >>> print(sh.sort(sh.du(sh.glob('*'),'-shc'),'-rn')) - 712K distribute-0.6.49.tar.gz - 672K setuptools-1.1.5.tar.gz - 548K get-pip.py - -管道是有序的,默认由内而外,但如果需要并行呢?加个_piped=True - -.. code:: python - - >>> for line in sh.tr(sh.tail("-f", "/home/mysql/mysql/log/alert.log", _piped=True), "[:upper:]", "[:lower:]", _iter=True): - ... print line - ... - innodb: doublewrite buffer not found: creating new - - innodb: doublewrite buffer created - - innodb: 127 rollback segment(s) active. - - innodb: creating foreign key constraint system tables - - innodb: foreign key constraint system tables created - -|image2| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200227201644.png -.. |image2| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_35.md b/source/c01/c01_35.md deleted file mode 100644 index 1ee1341..0000000 --- a/source/c01/c01_35.md +++ /dev/null @@ -1,384 +0,0 @@ -# 1.35 使用 Python 远程登陆服务器的利器 - -![](http://image.iswbm.com/20200602135014.png) - -在使用 Python 写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 - -在 shell 环境中,我们是这样子做的。 - -```shell -$ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l" -``` - -然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样 - -```shell -host: xx.xx.xx.xx, port: xx -Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts. -Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server. -total 4 --rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc -``` - -对于直接使用 shell 命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果 - -## 1. 使用 subprocess - -若是使用 Python 来做这件事,通常我们会第一时间,想到使用 os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。 - -但是据我所知,这些库获取的 output 不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息) - -所以每次都要对 output 进行的数据清洗,然后整理格式化,才能得到我们想要的数据。 - -用 subprocess 举个例子,就像这样子 - -```python -import subprocess -ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'" -status, output = subprocess.getstatusoutput(ssh_cmd) - -# 数据清理,格式化的就不展示了 - -``` - - - -通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点 - -- **痛点一**:需要额外安装 sshpass(如果不免密的话) -- **痛点二**:干扰信息太多,数据清理、格式化相当麻烦 -- **痛点三**:代码实现不够优雅(有点土),可读性太差 -- **痛点四**:ssh 连接不能复用,一次连接仅能执行一次 -- **痛点五**:代码无法全平台,仅能在 Linux 和 OSX 上使用 - - - -为了解决这几个问题,我搜索了全网关于 Python ssh 的文章,没有看到有完整介绍这方面的技巧的。 - - - -为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn (https://github.com/BingmingWong/awesome-python-cn)。 - -期望在这里,找到有一些关于 远程连接 的一些好用的库。 - -还真的被我找到了两个 - -- sh.ssh -- Paramiko - - - -## 2. 使用 sh.ssh - -首先来介绍第一个,`sh.ssh` - -`sh` 是一个可以让你通过函数的调用来完成 Linxu/OSX 系统命令的一个库,非常好用,关于它有机会也写篇介绍。 - -```shell -$ python3 -m pip install sh -``` - - - -今天只介绍它其中的一个函数:`ssh` - -通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。 - -这段代码可以实现免密登陆,并执行我们的命令 `ls -l` - -```python -from sh import ssh -output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l") -print(output) -``` - -但有可能 ,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。 - -问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python 中要如何实现呢? - -原来 ssh 方法接收一个 `_out` 参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。 - -这就好办了呀。 - -我只要识别到有 `password:` 字样,就往标准输入写入我的密码就好了呀。 - - - -完整代码如下: - -```python -import sys -from sh import ssh - -aggregated = "" -def ssh_interact(char, stdin): - global aggregated - sys.stdout.write(char.encode()) - sys.stdout.flush() - aggregated += char - if aggregated.endswith("password: "): - stdin.put("you_password\n") - -output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact) -print(output) -``` - -这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。 - -尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。 - -通过调试查看源代码,仍然查不到问题所在,于是去 [Github](https://github.com/amoffat/sh/issues/393) 上搜了下,原来在 2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 `sh.ssh` 的人并不多,于是我又“追问”了下,期望能得到回复。 - -![](http://image.iswbm.com/20200228085749.png) - -以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 - -为了感受 `sh.ssh` 的使用效果,我设置了机器互信免密,然后使用如下这段代码。 - -```python -from sh import ssh - -my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22") - -# 相当于执行登陆一次执行一次命令,执行完就退出登陆 -print(my_server.ls()) - -# 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接 -time.sleep(5) - -# 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1 -print(my_server.ifconfig()) -``` - -惊奇地发现使用 `bake` 这种方式,`my_server.ls()` 和 `my_server.ifconfig()` 这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行 top 命令看到已连接的终端的变化,会先 `+1` 再 `-1`,说明两次命令的执行是通过两次连接实现的。 - -如此看来,使用 `sh.ssh` 可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。 - -但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。 - -最重要的一点是, `sh` 这个模块,仅支持 Linxu/OSX ,在 Windows 你得使用它的兄弟库 - `pbs` ,然后我又去 pypi 看了一眼 [pbs](https://pypi.org/project/pbs/),已经 “年久失修”,没人维护了。 - -![](http://image.iswbm.com/20200228093627.png) - -至此,我离 “卒”,就差最后一根稻草了。 - - - -## 3. 使用 paramiko - -带着最后一丝希望,我尝试使用了 `paramiko` 这个库,终于在 `paramiko` 这里,找回了本应属于 Python 的那种优雅。 - -你可以通过如下命令去安装它 - -``` -$ python3 -m pip install paramiko -``` - - - -然后接下来,就介绍几种常用的 ssh 登陆的方法 - -### 方法1:基于用户名和密码的 sshclient 方式登录 - -然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接 - -```python -import paramiko - -ssh = paramiko.SSHClient() -# 允许连接不在know_hosts文件中的主机 -ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - -# 建立连接 -ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password") - -# 使用这个连接执行命令 -ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") - -# 获取输出 -print(ssh_stdout.read()) - -# 关闭连接 -ssh.close() -``` - - - -### 方法2:基于用户名和密码的 transport 方式登录 - -方法1 是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。 - -有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1 则无法实现,那就可以使用 transport 的方法。 - -```python -import paramiko - -# 建立连接 -trans = paramiko.Transport(("xx.xx.xx.xx", 22)) -trans.connect(username="root", password="you_passwd") - -# 将sshclient的对象的transport指定为以上的trans -ssh = paramiko.SSHClient() -ssh._transport = trans - -# 剩下的就和上面一样了 -ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) -ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") -print(ssh_stdout.read()) - -# 关闭连接 -trans.close() -``` - - - -### 方法3:基于公钥密钥的 SSHClient 方式登录 - -```python -import paramiko - -# 指定本地的RSA私钥文件 -# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 -pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') - -# 建立连接 -ssh = paramiko.SSHClient() -ssh.connect(hostname='xx.xx.xx.xx', - port=22, - username='you_username', - pkey=pkey) - -# 执行命令 -stdin, stdout, stderr = ssh.exec_command('ls -l') - -# 结果放到stdout中,如果有错误将放到stderr中 -print(stdout.read()) - -# 关闭连接 -ssh.close() -``` - - - -### 方法4:基于密钥的 Transport 方式登录 - -```python -import paramiko - -# 指定本地的RSA私钥文件 -# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 -pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') - -# 建立连接 -trans = paramiko.Transport(('xx.xx.xx.xx', 22)) -trans.connect(username='you_username', pkey=pkey) - -# 将sshclient的对象的transport指定为以上的trans -ssh = paramiko.SSHClient() -ssh._transport = trans - -# 执行命令,和传统方法一样 -stdin, stdout, stderr = ssh.exec_command('df -hl') -print(stdout.read().decode()) - -# 关闭连接 -trans.close() -``` - - - -以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用 **方法二** 和 **方法四** - -用完后,记得关闭连接。 - -### 实现 sftp 文件传输 - -同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现 sftp 文件传输。 - -```python -import paramiko - -# 实例化一个trans对象# 实例化一个transport对象 -trans = paramiko.Transport(('xx.xx.xx.xx', 22)) - -# 建立连接 -trans.connect(username='you_username', password='you_passwd') - -# 实例化一个 sftp对象,指定连接的通道 -sftp = paramiko.SFTPClient.from_transport(trans) - -# 发送文件 -sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt') - -# 下载文件 -sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt') -trans.close() -``` - - - -到这里,Paramiko 已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是 Windows,这里就有一件好事,一件坏事了,。 - -好事就是:paramiko 支持 windows - -坏事就是:你需要做很多复杂的准备,你可 google 解决,但是我建议你直接放弃,坑太深了。 - -![](http://image.iswbm.com/20200228111654.png) - -### 注意事项 - -使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 "踩坑" 后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点 - -就是在执行 `ssh.exec_command(cmd)` 时,这个命令并不是同步阻塞的。 - -比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s 后,再 执行 ssh.close() - -```python -import paramiko - -trans = paramiko.Transport(("172.20.42.1", 57891)) -trans.connect(username="root", password="youpassword") -ssh = paramiko.SSHClient() -ssh._transport = trans -stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") -ssh.close() -``` - - - -但是如果改成这样,加上一行 stdout.read(), paramiko 就知道,你需要这个执行的结果,就会在 read() 进行阻塞。 - -```python -import paramiko - -trans = paramiko.Transport(("172.20.42.1", 57891)) -trans.connect(username="root", password="youpassword") -ssh = paramiko.SSHClient() -ssh._transport = trans -stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") - -# 加上一行 read() -print(stdout.read()) -ssh.close() -``` - - - -## 4. 写在最后 - -经过了一番对比,和一些实例的展示,可以看出 Paramiko 是一个专业、让人省心的 ssh 利器,个人认为 Paramiko 模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh 到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。 - -最后,希望这篇文章,能给你带来帮助。 - - - -## 5. 参考链接 - -- https://github.com/paramiko/paramiko -- http://docs.paramiko.org -- https://www.liujiangblog.com/blog/15/ -- http://docs.paramiko.org/en/stable/ - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_35.rst b/source/c01/c01_35.rst deleted file mode 100644 index d56a1c8..0000000 --- a/source/c01/c01_35.rst +++ /dev/null @@ -1,405 +0,0 @@ -1.35 使用 Python 远程登陆服务器的利器 -===================================== - -|image0| - -在使用 Python -写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 - -在 shell 环境中,我们是这样子做的。 - -.. code:: shell - - $ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l" - -然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样 - -.. code:: shell - - host: xx.xx.xx.xx, port: xx - Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts. - Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server. - total 4 - -rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc - -对于直接使用 shell -命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果 - -1. 使用 subprocess ------------------- - -若是使用 Python 来做这件事,通常我们会第一时间,想到使用 -os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。 - -但是据我所知,这些库获取的 output -不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息) - -所以每次都要对 output -进行的数据清洗,然后整理格式化,才能得到我们想要的数据。 - -用 subprocess 举个例子,就像这样子 - -.. code:: python - - import subprocess - ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'" - status, output = subprocess.getstatusoutput(ssh_cmd) - - # 数据清理,格式化的就不展示了 - - -通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点 - -- **痛点一**\ :需要额外安装 sshpass(如果不免密的话) -- **痛点二**\ :干扰信息太多,数据清理、格式化相当麻烦 -- **痛点三**\ :代码实现不够优雅(有点土),可读性太差 -- **痛点四**\ :ssh 连接不能复用,一次连接仅能执行一次 -- **痛点五**\ :代码无法全平台,仅能在 Linux 和 OSX 上使用 - -为了解决这几个问题,我搜索了全网关于 Python ssh -的文章,没有看到有完整介绍这方面的技巧的。 - -为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn -(https://github.com/BingmingWong/awesome-python-cn)。 - -期望在这里,找到有一些关于 远程连接 的一些好用的库。 - -还真的被我找到了两个 - -- sh.ssh -- Paramiko - -2. 使用 sh.ssh --------------- - -首先来介绍第一个,\ ``sh.ssh`` - -``sh`` 是一个可以让你通过函数的调用来完成 Linxu/OSX -系统命令的一个库,非常好用,关于它有机会也写篇介绍。 - -.. code:: shell - - $ python3 -m pip install sh - -今天只介绍它其中的一个函数:\ ``ssh`` - -通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。 - -这段代码可以实现免密登陆,并执行我们的命令 ``ls -l`` - -.. code:: python - - from sh import ssh - output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l") - print(output) - -但有可能 -,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。 - -问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python -中要如何实现呢? - -原来 ssh 方法接收一个 ``_out`` -参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。 - -这就好办了呀。 - -我只要识别到有 ``password:`` 字样,就往标准输入写入我的密码就好了呀。 - -完整代码如下: - -.. code:: python - - import sys - from sh import ssh - - aggregated = "" - def ssh_interact(char, stdin): - global aggregated - sys.stdout.write(char.encode()) - sys.stdout.flush() - aggregated += char - if aggregated.endswith("password: "): - stdin.put("you_password\n") - - output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact) - print(output) - -这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。 - -尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。 - -通过调试查看源代码,仍然查不到问题所在,于是去 -`Github `__ 上搜了下,原来在 -2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 -``sh.ssh`` 的人并不多,于是我又“追问”了下,期望能得到回复。 - -|image1| - -以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 - -为了感受 ``sh.ssh`` -的使用效果,我设置了机器互信免密,然后使用如下这段代码。 - -.. code:: python - - from sh import ssh - - my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22") - - # 相当于执行登陆一次执行一次命令,执行完就退出登陆 - print(my_server.ls()) - - # 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接 - time.sleep(5) - - # 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1 - print(my_server.ifconfig()) - -惊奇地发现使用 ``bake`` 这种方式,\ ``my_server.ls()`` 和 -``my_server.ifconfig()`` -这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行 -top 命令看到已连接的终端的变化,会先 ``+1`` 再 -``-1``\ ,说明两次命令的执行是通过两次连接实现的。 - -如此看来,使用 ``sh.ssh`` -可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。 - -但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。 - -最重要的一点是, ``sh`` 这个模块,仅支持 Linxu/OSX ,在 Windows -你得使用它的兄弟库 - ``pbs`` ,然后我又去 pypi 看了一眼 -`pbs `__\ ,已经 “年久失修”,没人维护了。 - -|image2| - -至此,我离 “卒”,就差最后一根稻草了。 - -3. 使用 paramiko ----------------- - -带着最后一丝希望,我尝试使用了 ``paramiko`` 这个库,终于在 ``paramiko`` -这里,找回了本应属于 Python 的那种优雅。 - -你可以通过如下命令去安装它 - -:: - - $ python3 -m pip install paramiko - -然后接下来,就介绍几种常用的 ssh 登陆的方法 - -方法1:基于用户名和密码的 sshclient 方式登录 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接 - -.. code:: python - - import paramiko - - ssh = paramiko.SSHClient() - # 允许连接不在know_hosts文件中的主机 - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # 建立连接 - ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password") - - # 使用这个连接执行命令 - ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") - - # 获取输出 - print(ssh_stdout.read()) - - # 关闭连接 - ssh.close() - -方法2:基于用户名和密码的 transport 方式登录 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -方法1 -是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。 - -有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1 -则无法实现,那就可以使用 transport 的方法。 - -.. code:: python - - import paramiko - - # 建立连接 - trans = paramiko.Transport(("xx.xx.xx.xx", 22)) - trans.connect(username="root", password="you_passwd") - - # 将sshclient的对象的transport指定为以上的trans - ssh = paramiko.SSHClient() - ssh._transport = trans - - # 剩下的就和上面一样了 - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") - print(ssh_stdout.read()) - - # 关闭连接 - trans.close() - -方法3:基于公钥密钥的 SSHClient 方式登录 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - import paramiko - - # 指定本地的RSA私钥文件 - # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 - pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') - - # 建立连接 - ssh = paramiko.SSHClient() - ssh.connect(hostname='xx.xx.xx.xx', - port=22, - username='you_username', - pkey=pkey) - - # 执行命令 - stdin, stdout, stderr = ssh.exec_command('ls -l') - - # 结果放到stdout中,如果有错误将放到stderr中 - print(stdout.read()) - - # 关闭连接 - ssh.close() - -方法4:基于密钥的 Transport 方式登录 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - import paramiko - - # 指定本地的RSA私钥文件 - # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 - pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') - - # 建立连接 - trans = paramiko.Transport(('xx.xx.xx.xx', 22)) - trans.connect(username='you_username', pkey=pkey) - - # 将sshclient的对象的transport指定为以上的trans - ssh = paramiko.SSHClient() - ssh._transport = trans - - # 执行命令,和传统方法一样 - stdin, stdout, stderr = ssh.exec_command('df -hl') - print(stdout.read().decode()) - - # 关闭连接 - trans.close() - -以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用 -**方法二** 和 **方法四** - -用完后,记得关闭连接。 - -实现 sftp 文件传输 -~~~~~~~~~~~~~~~~~~ - -同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现 -sftp 文件传输。 - -.. code:: python - - import paramiko - - # 实例化一个trans对象# 实例化一个transport对象 - trans = paramiko.Transport(('xx.xx.xx.xx', 22)) - - # 建立连接 - trans.connect(username='you_username', password='you_passwd') - - # 实例化一个 sftp对象,指定连接的通道 - sftp = paramiko.SFTPClient.from_transport(trans) - - # 发送文件 - sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt') - - # 下载文件 - sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt') - trans.close() - -到这里,Paramiko -已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是 -Windows,这里就有一件好事,一件坏事了,。 - -好事就是:paramiko 支持 windows - -坏事就是:你需要做很多复杂的准备,你可 google -解决,但是我建议你直接放弃,坑太深了。 - -|image3| - -注意事项 -~~~~~~~~ - -使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 “踩坑” -后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点 - -就是在执行 ``ssh.exec_command(cmd)`` 时,这个命令并不是同步阻塞的。 - -比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s -后,再 执行 ssh.close() - -.. code:: python - - import paramiko - - trans = paramiko.Transport(("172.20.42.1", 57891)) - trans.connect(username="root", password="youpassword") - ssh = paramiko.SSHClient() - ssh._transport = trans - stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") - ssh.close() - -但是如果改成这样,加上一行 stdout.read(), paramiko -就知道,你需要这个执行的结果,就会在 read() 进行阻塞。 - -.. code:: python - - import paramiko - - trans = paramiko.Transport(("172.20.42.1", 57891)) - trans.connect(username="root", password="youpassword") - ssh = paramiko.SSHClient() - ssh._transport = trans - stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") - - # 加上一行 read() - print(stdout.read()) - ssh.close() - -4. 写在最后 ------------ - -经过了一番对比,和一些实例的展示,可以看出 Paramiko -是一个专业、让人省心的 ssh 利器,个人认为 Paramiko -模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh -到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。 - -最后,希望这篇文章,能给你带来帮助。 - -5. 参考链接 ------------ - -- https://github.com/paramiko/paramiko -- http://docs.paramiko.org -- https://www.liujiangblog.com/blog/15/ -- http://docs.paramiko.org/en/stable/ - -|image4| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200228085749.png -.. |image2| image:: http://image.iswbm.com/20200228093627.png -.. |image3| image:: http://image.iswbm.com/20200228111654.png -.. |image4| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_36.md b/source/c01/c01_36.md deleted file mode 100644 index bb15125..0000000 --- a/source/c01/c01_36.md +++ /dev/null @@ -1,253 +0,0 @@ -# 1.36 每日一库:pretty_errors 解决bug 洁癖 - -![](http://image.iswbm.com/20200602135014.png) - -当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 - -就像这样子,天呐,密集恐惧症要犯了都 - -![](http://image.iswbm.com/image-20200307210853246.png) - -上面这段 traceback - -- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了 -- 无法直接显示报错的代码,排查问题慢人一步,效率太低 - -那有没有一种办法,可以解决这些问题呢? - -当然有了,在 Python 中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。 - -今天要介绍的这个库呢,叫做 `pretty-errors` ,从名字上就可以知道它的用途,是用来美化错误信息的。 - -通过这条命令你可以安装它 - -```shell -$ python3 -m pip install pretty-errors -``` - - - -## 1. 环境要求 - -由于使用了 `pretty-errors` 后,你的 traceback 信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 `pretty-error` 时,请确保你使用的终端可以输出带有颜色的字体。 - -在 windows 上你可以使用 Powershell,cmder 等 - -在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2 - -## 2. 效果对比 - ------- - -随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 - -![](http://image.iswbm.com/image-20200307212823345.png) - -而使用了 pretty_errors 后,报错信息被美化成这样了。 - -![](http://image.iswbm.com/image-20200307213534278.png) - -是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? - -当然这段代码少,你可能还没感受到,那就来看下 该项目在 Github上的一张效果对比图吧 - -![](https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67) - - - -## 3. 配置全局可用 - -可以看到使用了 pretty_errors 后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。 - -既然既然这样,那 pretty_errors 应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧? - -答案是显而易见的。 - -pretty_errors 和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。 - -使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 traceback 输出都自动美化。 - -```shell -$ python3 -m pretty_errors -``` - -![](http://image.iswbm.com/image-20200307214742135.png) - -配置完成后,你再运行任何脚本,traceback 都会自动美化了。 - -不仅是在我的 iTerm 终端下 - -![](http://image.iswbm.com/image-20200307213534278.png) - -在 PyCharm 中也会 - -![](http://image.iswbm.com/image-20200307215530270.png) - -唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 `文件路径` 直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 下面自定义配置的方案解决这个问题(下面会讲到,参数是:`display_link`)。 - -![](http://image.iswbm.com/image-20200307215834623.png) - -因此,有些情况下,你并不想设置 `pretty_errors` 全局可用。 - -那怎么取消之前的配置呢? - -只需要再次输出 `python -m pretty_errors`,输出入 `C` 即可清除。 - -![](http://image.iswbm.com/image-20200307214600749.png) - - - -## 4. 单文件中使用 - -取消全局可用后,你可以根据自己需要,在你需要使用 `pretty-errors` 的脚本文件中导入` pretty_errors `,即可使用 - -```python -import pretty_errors -``` - -就像这样 - -```python -import pretty_errors - -def foo(): - 1/0 - -if __name__ == "__main__": - foo() -``` - -值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。 - -因此,为了让美化更彻底,官方推荐你使用 `python -m pretty_errors` - -## 5. 自定义设置 - -上面的例子里,我们使用的都是 `pretty_errors` 的默认美化格式,展示的信息并没有那么全。 - -比如 - -- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。 -- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。 - -如果使用了 `pretty_errors` 导致异常信息有丢失,那还不如不使用 `pretty_errors` 呢。 - -不过,可以告诉你的是,`pretty_errors` 并没有你想象的那么简单。 - -它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示? - -这里举一个例子 - -```python -import pretty_errors - -# 【重点】进行配置 -pretty_errors.configure( - separator_character = '*', - filename_display = pretty_errors.FILENAME_EXTENDED, - line_number_first = True, - display_link = True, - lines_before = 5, - lines_after = 2, - line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, - code_color = ' ' + pretty_errors.default_config.line_color, -) - -# 原来的代码 -def foo(): - 1/0 - -if __name__ == "__main__": - foo() -``` - -在你像上面这样使用 `pretty_errrs.configure` 进行配置时,抛出的的异常信息就变成这样了。 - -![](http://image.iswbm.com/image-20200308121949011.png) - - - -当然了,`pretty_errors.configure()` 还可以接收很多的参数,你可以根据你自己的需要进行配置。 - -### 5.1 设置颜色 - -- `header_color`:设置标题行的颜色。 -- `timestamp_color`:设置时间戳颜色 -- `default_color`:设置默认的颜色 -- `filename_color`:设置文件名颜色 -- `line_number_color`:设置行号颜色。 -- `function_color`:设置函数颜色。 -- `link_color`:设置链接的颜色。 - -在设置颜色的时候,`pretty_errors` 提供了一些常用的 颜色常量供你直接调取。 - -- `BLACK`:黑色 -- `GREY`:灰色 -- `RED`:红色 -- `GREEN`:绿色 -- `YELLOW`:黄色 -- `BLUE`:蓝色 -- `MAGENTA`:品红色 -- `CYAN`:蓝绿色 -- `WHITE`:白色 - -而每一种颜色,都相应的匹配的 `BRIGHT_` 变体 和 `_BACKGROUND` 变体, - -其中,`_BACKGROUND` 用于设置背景色,举个例子如下。 - -![](http://image.iswbm.com/image-20200308125431779.png) - -### 5.2 设置显示内容 - -- `line_number_first` - 启用后,将首先显示行号,而不是文件名。 -- `lines_before` : 显示发生异常处的前几行代码 -- `lines_after`: 显示发生异常处的后几行代码 -- `display_link`:启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 -- `separator_character`:用于创建标题行的字符。默认情况下使用连字符。如果设置为 `''` 或者 `None` ,标题将被禁用。 -- `display_timestamp`:启用时,时间戳将写入回溯头中。 -- `display_locals` - 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。 - -- `display_trace_locals` - 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。 - -### 5.3 设置怎么显示 - -- `line_length`:设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用`full_line_newline`,以防止出现明显的双换行符。 - -- `full_line_newline`:当输出的字符满行时,是否要插入换行符。 - -- `timestamp_function` - 调用该函数以生成时间戳。默认值为`time.perf_counter`。 - -- `top_first` - 启用后,堆栈跟踪将反转,首先显示堆栈顶部。 - -- `display_arrow` - 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。 - -- `truncate_code` - 启用后,每行代码将被截断以适合行长。 - -- `stack_depth` - 要显示的堆栈跟踪的最大条目数。什么时候`0`将显示整个堆栈,这是默认值。 - -- `exception_above` - 启用后,异常将显示在堆栈跟踪上方。 - -- `exception_below`: - 启用后,异常显示在堆栈跟踪下方。 - -- `reset_stdout` - 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。 - -- `filename_display` - - 设置文件名的展示方式,有三个选项: `pretty_errors.FILENAME_COMPACT` 、`pretty_errors.FILENAME_EXTENDED`,或者`pretty_errors.FILENAME_FULL` - - - -以上,就是我对 `pretty_errors` 的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,`pretty_errors` 会是一个不错的解决方案,明哥把它推荐给你。 - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_36.rst b/source/c01/c01_36.rst deleted file mode 100644 index 8222d98..0000000 --- a/source/c01/c01_36.rst +++ /dev/null @@ -1,286 +0,0 @@ -1.36 每日一库:pretty_errors 解决bug 洁癖 -========================================= - -|image0| - -当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 -**密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 - -就像这样子,天呐,密集恐惧症要犯了都 - -|image1| - -上面这段 traceback - -- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了 -- 无法直接显示报错的代码,排查问题慢人一步,效率太低 - -那有没有一种办法,可以解决这些问题呢? - -当然有了,在 Python -中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。 - -今天要介绍的这个库呢,叫做 ``pretty-errors`` -,从名字上就可以知道它的用途,是用来美化错误信息的。 - -通过这条命令你可以安装它 - -.. code:: shell - - $ python3 -m pip install pretty-errors - -1. 环境要求 ------------ - -由于使用了 ``pretty-errors`` 后,你的 traceback -信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 -``pretty-error`` 时,请确保你使用的终端可以输出带有颜色的字体。 - -在 windows 上你可以使用 Powershell,cmder 等 - -在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2 - -2. 效果对比 ------------ - --------------- - -随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 - -|image2| - -而使用了 pretty_errors 后,报错信息被美化成这样了。 - -|image3| - -是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? - -当然这段代码少,你可能还没感受到,那就来看下 该项目在 -Github上的一张效果对比图吧 - -|image4| - -3. 配置全局可用 ---------------- - -可以看到使用了 pretty_errors -后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。 - -既然既然这样,那 pretty_errors -应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧? - -答案是显而易见的。 - -pretty_errors -和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。 - -使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 -traceback 输出都自动美化。 - -.. code:: shell - - $ python3 -m pretty_errors - -|image5| - -配置完成后,你再运行任何脚本,traceback 都会自动美化了。 - -不仅是在我的 iTerm 终端下 - -|image6| - -在 PyCharm 中也会 - -|image7| - -唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 ``文件路径`` -直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 -下面自定义配置的方案解决这个问题(下面会讲到,参数是:\ ``display_link``\ )。 - -|image8| - -因此,有些情况下,你并不想设置 ``pretty_errors`` 全局可用。 - -那怎么取消之前的配置呢? - -只需要再次输出 ``python -m pretty_errors``\ ,输出入 ``C`` 即可清除。 - -|image9| - -4. 单文件中使用 ---------------- - -取消全局可用后,你可以根据自己需要,在你需要使用 ``pretty-errors`` -的脚本文件中导入\ ``pretty_errors``\ ,即可使用 - -.. code:: python - - import pretty_errors - -就像这样 - -.. code:: python - - import pretty_errors - - def foo(): - 1/0 - - if __name__ == "__main__": - foo() - -值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。 - -因此,为了让美化更彻底,官方推荐你使用 ``python -m pretty_errors`` - -5. 自定义设置 -------------- - -上面的例子里,我们使用的都是 ``pretty_errors`` -的默认美化格式,展示的信息并没有那么全。 - -比如 - -- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。 -- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。 - -如果使用了 ``pretty_errors`` 导致异常信息有丢失,那还不如不使用 -``pretty_errors`` 呢。 - -不过,可以告诉你的是,\ ``pretty_errors`` 并没有你想象的那么简单。 - -它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示? - -这里举一个例子 - -.. code:: python - - import pretty_errors - - # 【重点】进行配置 - pretty_errors.configure( - separator_character = '*', - filename_display = pretty_errors.FILENAME_EXTENDED, - line_number_first = True, - display_link = True, - lines_before = 5, - lines_after = 2, - line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, - code_color = ' ' + pretty_errors.default_config.line_color, - ) - - # 原来的代码 - def foo(): - 1/0 - - if __name__ == "__main__": - foo() - -在你像上面这样使用 ``pretty_errrs.configure`` -进行配置时,抛出的的异常信息就变成这样了。 - -|image10| - -当然了,\ ``pretty_errors.configure()`` -还可以接收很多的参数,你可以根据你自己的需要进行配置。 - -5.1 设置颜色 -~~~~~~~~~~~~ - -- ``header_color``\ :设置标题行的颜色。 -- ``timestamp_color``\ :设置时间戳颜色 -- ``default_color``\ :设置默认的颜色 -- ``filename_color``\ :设置文件名颜色 -- ``line_number_color``\ :设置行号颜色。 -- ``function_color``\ :设置函数颜色。 -- ``link_color``\ :设置链接的颜色。 - -在设置颜色的时候,\ ``pretty_errors`` 提供了一些常用的 -颜色常量供你直接调取。 - -- ``BLACK``\ :黑色 -- ``GREY``\ :灰色 -- ``RED``\ :红色 -- ``GREEN``\ :绿色 -- ``YELLOW``\ :黄色 -- ``BLUE``\ :蓝色 -- ``MAGENTA``\ :品红色 -- ``CYAN``\ :蓝绿色 -- ``WHITE``\ :白色 - -而每一种颜色,都相应的匹配的 ``BRIGHT_`` 变体 和 ``_BACKGROUND`` 变体, - -其中,\ ``_BACKGROUND`` 用于设置背景色,举个例子如下。 - -|image11| - -5.2 设置显示内容 -~~~~~~~~~~~~~~~~ - -- ``line_number_first`` 启用后,将首先显示行号,而不是文件名。 -- ``lines_before`` : 显示发生异常处的前几行代码 -- ``lines_after``\ : 显示发生异常处的后几行代码 -- ``display_link``\ :启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 -- ``separator_character``\ :用于创建标题行的字符。默认情况下使用连字符。如果设置为 - ``''`` 或者 ``None`` ,标题将被禁用。 -- ``display_timestamp``\ :启用时,时间戳将写入回溯头中。 -- ``display_locals`` - 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。 - -- ``display_trace_locals`` - 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。 - -5.3 设置怎么显示 -~~~~~~~~~~~~~~~~ - -- ``line_length``\ :设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用\ ``full_line_newline``\ ,以防止出现明显的双换行符。 - -- ``full_line_newline``\ :当输出的字符满行时,是否要插入换行符。 - -- ``timestamp_function`` - 调用该函数以生成时间戳。默认值为\ ``time.perf_counter``\ 。 - -- ``top_first`` 启用后,堆栈跟踪将反转,首先显示堆栈顶部。 - -- ``display_arrow`` - 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。 - -- ``truncate_code`` 启用后,每行代码将被截断以适合行长。 - -- ``stack_depth`` - 要显示的堆栈跟踪的最大条目数。什么时候\ ``0``\ 将显示整个堆栈,这是默认值。 - -- ``exception_above`` 启用后,异常将显示在堆栈跟踪上方。 - -- ``exception_below``\ : 启用后,异常显示在堆栈跟踪下方。 - -- ``reset_stdout`` - 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。 - -- ``filename_display`` - - 设置文件名的展示方式,有三个选项: ``pretty_errors.FILENAME_COMPACT`` - 、\ ``pretty_errors.FILENAME_EXTENDED``\ ,或者\ ``pretty_errors.FILENAME_FULL`` - -以上,就是我对 ``pretty_errors`` -的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 -PEP8 -规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,\ ``pretty_errors`` -会是一个不错的解决方案,明哥把它推荐给你。 - -|image12| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/image-20200307210853246.png -.. |image2| image:: http://image.iswbm.com/image-20200307212823345.png -.. |image3| image:: http://image.iswbm.com/image-20200307213534278.png -.. |image4| image:: https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67 -.. |image5| image:: http://image.iswbm.com/image-20200307214742135.png -.. |image6| image:: http://image.iswbm.com/image-20200307213534278.png -.. |image7| image:: http://image.iswbm.com/image-20200307215530270.png -.. |image8| image:: http://image.iswbm.com/image-20200307215834623.png -.. |image9| image:: http://image.iswbm.com/image-20200307214600749.png -.. |image10| image:: http://image.iswbm.com/image-20200308121949011.png -.. |image11| image:: http://image.iswbm.com/image-20200308125431779.png -.. |image12| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_37.md b/source/c01/c01_37.md deleted file mode 100644 index b206844..0000000 --- a/source/c01/c01_37.md +++ /dev/null @@ -1,161 +0,0 @@ -# 1.37 Python 炫技操作:条件语句的七种写法 - -![](http://image.iswbm.com/20200602135014.png) - -有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。 - -Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - -但你要知道,在团队合作里,炫技是大忌。 - -为什么这么说呢?我说下自己的看法: -1. 越简洁的代码,越清晰的逻辑,就越不容易出错; -2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 -3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - -在这个系列里,我将总结列举一下,我所见过的那些炫技操作,今天先来个热身,写一写很简单的条件判断语句里有哪些让人想骂街的炫技操作,在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧,但学习归学习,希望你区分场景使用。 - -## 原代码 - -这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 Python 功力。 -```python -if age > 18: - return "已成年" -else: - return "未成年" -``` - -下面我列举了六种这段代码的变异写法,一个比一个还 6 ,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:**卧槽,还可以这样写?**,而后就要开始骂街了:**这是给人看的代码?** (除了第一种之外) - -## 第一种 - -语法: - -```python - if else -``` - -例子 - -```python ->>> age1 = 20 ->>> age2 = 17 ->>> ->>> ->>> msg1 = "已成年" if age1 > 18 else "未成年" ->>> print msg1 -已成年 ->>> ->>> msg2 = "已成年" if age2 > 18 else "未成年" ->>> print msg2 -未成年 ->>> -``` - -## 第二种 - -语法 - -```python - and or -``` - -例子 - -```python ->>> msg1 = age1 > 18 and "已成年" or "未成年" ->>> msg2 = "已成年" if age2 > 18 else "未成年" ->>> ->>> print(msg1) -已成年 ->>> ->>> print(msg2) -未成年 -``` - -## 第三种 - -语法 - -```python -(, )[condition] -``` - -例子 - -```python ->>> msg1 = ("未成年", "已成年")[age1 > 18] ->>> print(msg1) -已成年 ->>> ->>> ->>> msg2 = ("未成年", "已成年")[age2 > 18] ->>> print(msg2) -未成年 -``` - -## 第四种 - -语法 - -```python -(lambda: , lambda:)[]() -``` - -例子 - -```python ->>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() ->>> print(msg1) -已成年 ->>> ->>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() ->>> print(msg2) -未成年 -``` - -## 第五种 - -语法: - -```python -{True: , False: }[] -``` - -例子: - -```python ->>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] ->>> print(msg1) -已成年 ->>> ->>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] ->>> print(msg2) -未成年 -``` - -## 第六种 - -语法 - -```python -(() and (,) or (,))[0] -``` - -例子 - -```python ->>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] ->>> print(msg1) -已成年 ->>> ->>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] ->>> print(msg2) -未成年 -``` - -以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 - -看到这里,有没有涨姿势了,学了这么久的 Python ,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_37.rst b/source/c01/c01_37.rst deleted file mode 100644 index 15c3b09..0000000 --- a/source/c01/c01_37.rst +++ /dev/null @@ -1,182 +0,0 @@ -1.37 Python 炫技操作:条件语句的七种写法 -======================================== - -|image0| - -有的人说 Python 是一门 入门容易,但是精通难的语言,这一点我非常赞同。 - -Python 语言里有许多(而且是越来越多)的高级特性,是 Python -发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - -但你要知道,在团队合作里,炫技是大忌。 - -为什么这么说呢?我说下自己的看法: 1. -越简洁的代码,越清晰的逻辑,就越不容易出错; 2. -在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 -3. -简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - -在这个系列里,我将总结列举一下,我所见过的那些炫技操作,今天先来个热身,写一写很简单的条件判断语句里有哪些让人想骂街的炫技操作,在这里,如果你是 -Python -发烧友,你可以学到一些写出超酷的代码书写技巧,但学习归学习,希望你区分场景使用。 - -原代码 ------- - -这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 -Python 功力。 - -.. code:: python - - if age > 18: - return "已成年" - else: - return "未成年" - -下面我列举了六种这段代码的变异写法,一个比一个还 6 -,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?** -(除了第一种之外) - -第一种 ------- - -语法: - -.. code:: python - - if else - -例子 - -.. code:: python - - >>> age1 = 20 - >>> age2 = 17 - >>> - >>> - >>> msg1 = "已成年" if age1 > 18 else "未成年" - >>> print msg1 - 已成年 - >>> - >>> msg2 = "已成年" if age2 > 18 else "未成年" - >>> print msg2 - 未成年 - >>> - -第二种 ------- - -语法 - -.. code:: python - - and or - -例子 - -.. code:: python - - >>> msg1 = age1 > 18 and "已成年" or "未成年" - >>> msg2 = "已成年" if age2 > 18 else "未成年" - >>> - >>> print(msg1) - 已成年 - >>> - >>> print(msg2) - 未成年 - -第三种 ------- - -语法 - -.. code:: python - - (, )[condition] - -例子 - -.. code:: python - - >>> msg1 = ("未成年", "已成年")[age1 > 18] - >>> print(msg1) - 已成年 - >>> - >>> - >>> msg2 = ("未成年", "已成年")[age2 > 18] - >>> print(msg2) - 未成年 - -第四种 ------- - -语法 - -.. code:: python - - (lambda: , lambda:)[]() - -例子 - -.. code:: python - - >>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() - >>> print(msg1) - 已成年 - >>> - >>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() - >>> print(msg2) - 未成年 - -第五种 ------- - -语法: - -.. code:: python - - {True: , False: }[] - -例子: - -.. code:: python - - >>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] - >>> print(msg1) - 已成年 - >>> - >>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] - >>> print(msg2) - 未成年 - -第六种 ------- - -语法 - -.. code:: python - - (() and (,) or (,))[0] - -例子 - -.. code:: python - - >>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] - >>> print(msg1) - 已成年 - >>> - >>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] - >>> print(msg2) - 未成年 - -以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 - -看到这里,有没有涨姿势了,学了这么久的 Python -,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_38.rst b/source/c01/c01_38.rst deleted file mode 100644 index 1b2c08d..0000000 --- a/source/c01/c01_38.rst +++ /dev/null @@ -1,76 +0,0 @@ -1.38 /usr/bin/env python 有什么用? -=================================== - -|image0| - -我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 - -.. code:: json - - #!/usr/bin/python - -或者这样 - -.. code:: json - - #!/usr/bin/env python - -这两者有什么区别呢? - -稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python`` -进入console 模式里的 ``python`` - -|image1| - -而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python`` -,意思就是说你得用哪个软件 (python)来执行这个文件。 - -那么加和不加有什么区别呢? - -不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` , - -|image2| - -有没有一种方式?可以省去每次都加 ``python`` 呢? - -当然有,你可以文件头里加上\ ``#!/usr/bin/python`` -,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 - -|image3| - -明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? - -当我执行 ``env python`` 时,自动进入了 python console 的模式。 - -|image4| - -这是为什么?和 直接执行 python 好像没什么区别呀 - -当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是 -/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin -)这几个路径里去依次查找名为python的可执行文件。 - -找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python`` -里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin`` -下创建一个名字也为 python 的可执行文件时,就会执行 ``/usr/bin/python`` -了。 - -具体演示过程,你可以看下面。 - -|image5| - -那么对于这两者,我们应该使用哪个呢? - -个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 -python 解释器都是 ``/usr/bin/python`` 。 - -|image6| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200331184021.png -.. |image2| image:: http://image.iswbm.com/20200331185034.png -.. |image3| image:: http://image.iswbm.com/20200331184755.png -.. |image4| image:: http://image.iswbm.com/20200331185741.png -.. |image5| image:: http://image.iswbm.com/20200331190224.png -.. |image6| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_39.md b/source/c01/c01_39.md deleted file mode 100644 index 3f37049..0000000 --- a/source/c01/c01_39.md +++ /dev/null @@ -1,220 +0,0 @@ -# 1.39 Python 炫技操作:合并字典的七种方法 - -![](http://image.iswbm.com/20200602135014.png) - -Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - -但你要知道,在团队合作里,炫技是大忌。 - -为什么这么说呢?我说下自己的看法: - -1. 越简洁的代码,越清晰的逻辑,就越不容易出错; -2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 -3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - -该篇是「**炫技系列**」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - -## 1. 最简单的原地更新 - -字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> profile.update(ext_info) ->>> print(profile) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -如果想使用 update 这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> from copy import deepcopy ->>> ->>> full_profile = deepcopy(profile) ->>> full_profile.update(ext_info) ->>> ->>> print(full_profile) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ->>> print(profile) -{"name": "xiaoming", "age": 27} -``` - - - -## 2. 先解包再合并字典 - -使用 `**` 可以解包字典,解包完后再使用 dict 或者 `{}` 就可以合并。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> full_profile01 = {**profile, **ext_info} ->>> print(full_profile01) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ->>> ->>> full_profile02 = dict(**profile, **ext_info) ->>> print(full_profile02) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -若你不知道 `dict(**profile, **ext_info)` 做了啥,你可以将它等价于 - -```python ->>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -## 3. 借助 itertools - -在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 - -正好我们字典也是可迭代对象,自然就可以想到,可以使用 `itertools.chain()` 函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 dict 转成字典。 - -```python ->>> import itertools ->>> ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> ->>> dict(itertools.chain(profile.items(), ext_info.items())) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -## 4. 借助 ChainMap - -如果可以引入一个辅助包,那我就再提一个, `ChainMap` 也可以达到和 `itertools` 同样的效果。 - -```python ->>> from collections import ChainMap ->>> ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> dict(ChainMap(profile, ext_info)) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -使用 ChainMap 有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 itertools 就不会有这个问题)。 - -```python ->>> from collections import ChainMap ->>> ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info={"age": 30} ->>> dict(ChainMap(profile, ext_info)) -{'name': 'xiaoming', 'age': 27} -``` - - - -## 5. 使用dict.items() 合并 - -在 Python 3.9 之前,其实就已经有 `|` 操作符了,只不过它通常用于对集合(set)取并集。 - -利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 - -你得先利用 `items` 方法将 dict 转成 dict_items,再对这两个 dict_items 取并集,最后利用 dict 函数,转成字典。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> full_profile = dict(profile.items() | ext_info.items()) ->>> full_profile -{'gender': 'male', 'age': 27, 'name': 'xiaoming'} -``` - - - -当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list 函数再合并(示例为 Python 3.x ) - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> dict(list(profile.items()) + list(ext_info.items())) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - -若你在 Python 2.x 下,可以直接省去 list 函数。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> dict(profile.items() + ext_info.items()) -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -## 6. 最酷炫的字典解析式 - -Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 - -那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? - -当然可以,具体示例代码如下: - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> {k:v for d in [profile, ext_info] for k,v in d.items()} -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -## 7. Python 3.9 新特性 - -在 2 月份发布的 Python 3.9.04a 版本中,新增了一个抓眼球的新操作符操作符: `|`, PEP584 将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 - -```python ->>> profile = {"name": "xiaoming", "age": 27} ->>> ext_info = {"gender": "male"} ->>> ->>> profile | ext_info -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} ->>> ->>> ext_info | profile -{'gender': 'male', 'name': 'xiaoming', 'age': 27} ->>> ->>> -``` - -除了 `|` 操作符之外,还有另外一个操作符 `|=`,类似于原地更新。 - -```python ->>> ext_info |= profile ->>> ext_info -{'gender': 'male', 'name': 'xiaoming', 'age': 27} ->>> ->>> ->>> profile |= ext_info ->>> profile -{'name': 'xiaoming', 'age': 27, 'gender': 'male'} -``` - - - -看到这里,有没有涨姿势了,学了这么久的 Python ,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 7 种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 - - - ---- - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_39.rst b/source/c01/c01_39.rst deleted file mode 100644 index 89a0d3d..0000000 --- a/source/c01/c01_39.rst +++ /dev/null @@ -1,231 +0,0 @@ -1.39 Python 炫技操作:合并字典的七种方法 -======================================== - -|image0| - -Python 语言里有许多(而且是越来越多)的高级特性,是 Python -发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - -但你要知道,在团队合作里,炫技是大忌。 - -为什么这么说呢?我说下自己的看法: - -1. 越简洁的代码,越清晰的逻辑,就越不容易出错; -2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 -3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - -该篇是「\ **炫技系列**\ 」的第二篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 -Python -发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - -1. 最简单的原地更新 -------------------- - -字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> profile.update(ext_info) - >>> print(profile) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -如果想使用 update -这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> from copy import deepcopy - >>> - >>> full_profile = deepcopy(profile) - >>> full_profile.update(ext_info) - >>> - >>> print(full_profile) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - >>> print(profile) - {"name": "xiaoming", "age": 27} - -2. 先解包再合并字典 -------------------- - -使用 ``**`` 可以解包字典,解包完后再使用 dict 或者 ``{}`` 就可以合并。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> full_profile01 = {**profile, **ext_info} - >>> print(full_profile01) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - >>> - >>> full_profile02 = dict(**profile, **ext_info) - >>> print(full_profile02) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -若你不知道 ``dict(**profile, **ext_info)`` 做了啥,你可以将它等价于 - -.. code:: python - - >>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -3. 借助 itertools ------------------ - -在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 - -正好我们字典也是可迭代对象,自然就可以想到,可以使用 -``itertools.chain()`` -函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 -dict 转成字典。 - -.. code:: python - - >>> import itertools - >>> - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> - >>> dict(itertools.chain(profile.items(), ext_info.items())) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -4. 借助 ChainMap ----------------- - -如果可以引入一个辅助包,那我就再提一个, ``ChainMap`` 也可以达到和 -``itertools`` 同样的效果。 - -.. code:: python - - >>> from collections import ChainMap - >>> - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> dict(ChainMap(profile, ext_info)) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -使用 ChainMap -有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 -itertools 就不会有这个问题)。 - -.. code:: python - - >>> from collections import ChainMap - >>> - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info={"age": 30} - >>> dict(ChainMap(profile, ext_info)) - {'name': 'xiaoming', 'age': 27} - -5. 使用dict.items() 合并 ------------------------- - -在 Python 3.9 之前,其实就已经有 ``|`` -操作符了,只不过它通常用于对集合(set)取并集。 - -利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 - -你得先利用 ``items`` 方法将 dict 转成 dict_items,再对这两个 dict_items -取并集,最后利用 dict 函数,转成字典。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> full_profile = dict(profile.items() | ext_info.items()) - >>> full_profile - {'gender': 'male', 'age': 27, 'name': 'xiaoming'} - -当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list -函数再合并(示例为 Python 3.x ) - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> dict(list(profile.items()) + list(ext_info.items())) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -若你在 Python 2.x 下,可以直接省去 list 函数。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> dict(profile.items() + ext_info.items()) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -6. 最酷炫的字典解析式 ---------------------- - -Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 - -那就是列表解析式,集合解析式和字典解析式,通常是 Python -发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? - -当然可以,具体示例代码如下: - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> {k:v for d in [profile, ext_info] for k,v in d.items()} - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -7. Python 3.9 新特性 --------------------- - -在 2 月份发布的 Python 3.9.04a -版本中,新增了一个抓眼球的新操作符操作符: ``|``\ , PEP584 -将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> profile | ext_info - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - >>> - >>> ext_info | profile - {'gender': 'male', 'name': 'xiaoming', 'age': 27} - >>> - >>> - -除了 ``|`` 操作符之外,还有另外一个操作符 ``|=``\ ,类似于原地更新。 - -.. code:: python - - >>> ext_info |= profile - >>> ext_info - {'gender': 'male', 'name': 'xiaoming', 'age': 27} - >>> - >>> - >>> profile |= ext_info - >>> profile - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -看到这里,有没有涨姿势了,学了这么久的 Python -,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 -7 -种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 - --------------- - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_40.md b/source/c01/c01_40.md deleted file mode 100644 index 2d70011..0000000 --- a/source/c01/c01_40.md +++ /dev/null @@ -1,143 +0,0 @@ -# 1.40 Python 炫技操作:判断是否包含子串的七种方法 - -![](http://image.iswbm.com/20200602135014.png) - -Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - -但你要知道,在团队合作里,炫技是大忌。 - -为什么这么说呢?我说下自己的看法: - -1. 越简洁的代码,越清晰的逻辑,就越不容易出错; -2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 -3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - -该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - -## 1. 使用 in 和 not in - -`in` 和 `not in` 在 Python 中是很常用的关键字,我们将它们归类为 `成员运算符`。 - -使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下: - -```python ->>> "llo" in "hello, python" -True ->>> ->>> "lol" in "hello, python" -False -``` - - - -## 2. 使用 find 方法 - -使用 字符串 对象的 find 方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回 `-1` - -```python ->>> "hello, python".find("llo") != -1 -True ->>> "hello, python".find("lol") != -1 -False ->> -``` - - - -## 3. 使用 index 方法 - -字符串对象有一个 index 方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。 - -```python -def is_in(full_str, sub_str): - try: - full_str.index(sub_str) - return True - except ValueError: - return False - -print(is_in("hello, python", "llo")) # True -print(is_in("hello, python", "lol")) # False -``` - - - -## 4. 使用 count 方法 - -利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。 - -只要判断结果大于 0 就说明子串存在于字符串中。 - -```python -def is_in(full_str, sub_str): - return full_str.count(sub_str) > 0 - -print(is_in("hello, python", "llo")) # True -print(is_in("hello, python", "lol")) # False -``` - - - -## 5. 通过魔法方法 - -在第一种方法中,我们使用 in 和 not in 判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in 时,Python 解释器会先去检查该对象是否有 `__contains__` 魔法方法。 - -若有就执行它,若没有,Python 就自动会迭代整个序列,只要找到了需要的一项就返回 True 。 - -示例如下; - -```python ->>> "hello, python".__contains__("llo") -True ->>> ->>> "hello, python".__contains__("lol") -False ->>> -``` - -这个用法与使用 in 和 not in 没有区别,但不排除有人会特意写成这样来增加代码的理解难度。 - -## 6. 借助 operator - -operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 python 代码快。 - -在 operator 中有一个方法 `contains` 可以很方便地判断子串是否在字符串中。 - -```python ->>> import operator ->>> ->>> operator.contains("hello, python", "llo") -True ->>> operator.contains("hello, python", "lol") -False ->>> -``` - - - -## 7. 使用正则匹配 - -说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。 - -对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。 - -```python -import re - -def is_in(full_str, sub_str): - if re.findall(sub_str, full_str): - return True - else: - return False - -print(is_in("hello, python", "llo")) # True -print(is_in("hello, python", "lol")) # False -``` - - - ---- - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_40.rst b/source/c01/c01_40.rst deleted file mode 100644 index 7bd620a..0000000 --- a/source/c01/c01_40.rst +++ /dev/null @@ -1,154 +0,0 @@ -1.40 Python 炫技操作:判断是否包含子串的七种方法 -================================================ - -|image0| - -Python 语言里有许多(而且是越来越多)的高级特性,是 Python -发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - -但你要知道,在团队合作里,炫技是大忌。 - -为什么这么说呢?我说下自己的看法: - -1. 越简洁的代码,越清晰的逻辑,就越不容易出错; -2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 -3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - -该篇是「\ **炫技系列**\ 」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 -Python -发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - -1. 使用 in 和 not in --------------------- - -``in`` 和 ``not in`` 在 Python 中是很常用的关键字,我们将它们归类为 -``成员运算符``\ 。 - -使用这两个成员运算符,可以很让我们很直观清晰的判断一个对象是否在另一个对象中,示例如下: - -.. code:: python - - >>> "llo" in "hello, python" - True - >>> - >>> "lol" in "hello, python" - False - -2. 使用 find 方法 ------------------ - -使用 字符串 对象的 find -方法,如果有找到子串,就可以返回指定子串在字符串中的出现位置,如果没有找到,就返回 -``-1`` - -.. code:: python - - >>> "hello, python".find("llo") != -1 - True - >>> "hello, python".find("lol") != -1 - False - >> - -3. 使用 index 方法 ------------------- - -字符串对象有一个 index -方法,可以返回指定子串在该字符串中第一次出现的索引,如果没有找到会抛出异常,因此使用时需要注意捕获。 - -.. code:: python - - def is_in(full_str, sub_str): - try: - full_str.index(sub_str) - return True - except ValueError: - return False - - print(is_in("hello, python", "llo")) # True - print(is_in("hello, python", "lol")) # False - -4. 使用 count 方法 ------------------- - -利用和 index 这种曲线救国的思路,同样我们可以使用 count 的方法来判断。 - -只要判断结果大于 0 就说明子串存在于字符串中。 - -.. code:: python - - def is_in(full_str, sub_str): - return full_str.count(sub_str) > 0 - - print(is_in("hello, python", "llo")) # True - print(is_in("hello, python", "lol")) # False - -5. 通过魔法方法 ---------------- - -在第一种方法中,我们使用 in 和 not in -判断一个子串是否存在于另一个字符中,实际上当你使用 in 和 not in -时,Python 解释器会先去检查该对象是否有 ``__contains__`` 魔法方法。 - -若有就执行它,若没有,Python -就自动会迭代整个序列,只要找到了需要的一项就返回 True 。 - -示例如下; - -.. code:: python - - >>> "hello, python".__contains__("llo") - True - >>> - >>> "hello, python".__contains__("lol") - False - >>> - -这个用法与使用 in 和 not in -没有区别,但不排除有人会特意写成这样来增加代码的理解难度。 - -6. 借助 operator ----------------- - -operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比 -python 代码快。 - -在 operator 中有一个方法 ``contains`` -可以很方便地判断子串是否在字符串中。 - -.. code:: python - - >>> import operator - >>> - >>> operator.contains("hello, python", "llo") - True - >>> operator.contains("hello, python", "lol") - False - >>> - -7. 使用正则匹配 ---------------- - -说到查找功能,那正则绝对可以说是专业的工具,多复杂的查找规则,都能满足你。 - -对于判断字符串是否存在于另一个字符串中的这个需求,使用正则简直就是大材小用。 - -.. code:: python - - import re - - def is_in(full_str, sub_str): - if re.findall(sub_str, full_str): - return True - else: - return False - - print(is_in("hello, python", "llo")) # True - print(is_in("hello, python", "lol")) # False - --------------- - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_42.md b/source/c01/c01_42.md deleted file mode 100644 index 5c92a8e..0000000 --- a/source/c01/c01_42.md +++ /dev/null @@ -1,175 +0,0 @@ -# 1.42 Python 炫技操作:海象运算符的三种用法 - -![](http://image.iswbm.com/20200602135014.png) - -Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 - -很多 Python 3.8 的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 - -海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 - -它的英文原名叫 `Assignment Expressions`,翻译过来也就是 `赋值表达式`,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 - -![](http://image.iswbm.com/image-20200418122739417.png) - -## 1. 第一个用法:if/else - -可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? - -在 Golang 中的条件语句可以直接在 if 中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 - -```go -import "fmt" - -func main() { - if age := 20;age > 18 { - fmt.Println("已经成年了") - } -} -``` - -若在 Python 3.8 之前,Python 必须得这样子写 - -```python -age = 20 -if age > 18: - print("已经成年了") -``` - -但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 Golang,那这里要注意,Golang 中的 `:=` 叫短变量声明,意思是声明并初始化,它和 Python 中的 `:=` 不是一个概念) - -```python -if (age:= 20) > 18: - print("已经成年了") -``` - - - -## 2. 第二个用法:while - -在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 - -```python -file = open("demo.txt", "r") -while True: - line = file.readline() - if not line: - break - print(line.strip()) -``` - -但有了海象运算符之后,你可以这样 - -```python -file = open("demo.txt", "r") -while (line := file.readline()): - print(line.strip()) -``` - -使用它替换以往的无限 while 循环写法更为惊艳 - -比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 - -```python -while True: - p = input("Enter the password: ") - if p == "youpassword": - break -``` - -有了海象运算符之后,这样子写更为舒服 - -```python -while (p := input("Enter the password: ")) != "youpassword": - continue -``` - - - -## 3. 第三个用法:推导式 - -这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 - -在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 - -如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 - -```python -members = [ - {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, - {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, - {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, -] - -count = 0 - -def get_bmi(info): - global count - count += 1 - - print(f"执行了 {count} 次") - - height = info["height"] - weight = info["weight"] - - return weight / (height**2) - -# 查出所有会员中过于肥胖的人的 bmi 指数 -fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] - -print(fat_bmis) -``` - -输出如下 - -``` -执行了 1 次 -执行了 2 次 -执行了 3 次 -执行了 4 次 -[25.88057063502083] -``` - -可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 - -如果所有会员都是过于肥胖的,那最终将执行 6 次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for 循环 + if 判断。 - -```python -fat_bmis = [] - -# 查出所有会员中过于肥胖的人的 bmi 指数 -for m in members: - bmi = get_bmi(m) - if bmi > 24: - fat_bmis.append(bmi) -``` - -在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 - -```python -# 查出所有会员中过于肥胖的人的 bmi 指数 -fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] -``` - -最终从输出结果可以看出,只执行了 3 次 - -``` -执行了 1 次 -执行了 2 次 -执行了 3 次 -[25.88057063502083] -``` - -这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 - - - -海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 - - - ---- - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_42.rst b/source/c01/c01_42.rst deleted file mode 100644 index 9df2a34..0000000 --- a/source/c01/c01_42.rst +++ /dev/null @@ -1,183 +0,0 @@ -1.42 Python 炫技操作:海象运算符的三种用法 -========================================== - -|image0| - -Python 版本发展非常快,如今最新的版本已经是 Pyhton -3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 - -很多 Python 3.8 -的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 - -海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 - -它的英文原名叫 ``Assignment Expressions``\ ,翻译过来也就是 -``赋值表达式``\ ,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 - -|image1| - -1. 第一个用法:if/else ----------------------- - -可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? - -在 Golang 中的条件语句可以直接在 if -中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 - -.. code:: go - - import "fmt" - - func main() { - if age := 20;age > 18 { - fmt.Println("已经成年了") - } - } - -若在 Python 3.8 之前,Python 必须得这样子写 - -.. code:: python - - age = 20 - if age > 18: - print("已经成年了") - -但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 -Golang,那这里要注意,Golang 中的 ``:=`` -叫短变量声明,意思是声明并初始化,它和 Python 中的 ``:=`` 不是一个概念) - -.. code:: python - - if (age:= 20) > 18: - print("已经成年了") - -2. 第二个用法:while --------------------- - -在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 - -.. code:: python - - file = open("demo.txt", "r") - while True: - line = file.readline() - if not line: - break - print(line.strip()) - -但有了海象运算符之后,你可以这样 - -.. code:: python - - file = open("demo.txt", "r") - while (line := file.readline()): - print(line.strip()) - -使用它替换以往的无限 while 循环写法更为惊艳 - -比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 - -.. code:: python - - while True: - p = input("Enter the password: ") - if p == "youpassword": - break - -有了海象运算符之后,这样子写更为舒服 - -.. code:: python - - while (p := input("Enter the password: ")) != "youpassword": - continue - -3. 第三个用法:推导式 ---------------------- - -这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 - -在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 - -如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 - -.. code:: python - - members = [ - {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, - {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, - {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, - ] - - count = 0 - - def get_bmi(info): - global count - count += 1 - - print(f"执行了 {count} 次") - - height = info["height"] - weight = info["weight"] - - return weight / (height**2) - - # 查出所有会员中过于肥胖的人的 bmi 指数 - fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] - - print(fat_bmis) - -输出如下 - -:: - - 执行了 1 次 - 执行了 2 次 - 执行了 3 次 - 执行了 4 次 - [25.88057063502083] - -可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 -次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 - -如果所有会员都是过于肥胖的,那最终将执行 6 -次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for -循环 + if 判断。 - -.. code:: python - - fat_bmis = [] - - # 查出所有会员中过于肥胖的人的 bmi 指数 - for m in members: - bmi = get_bmi(m) - if bmi > 24: - fat_bmis.append(bmi) - -在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 - -.. code:: python - - # 查出所有会员中过于肥胖的人的 bmi 指数 - fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] - -最终从输出结果可以看出,只执行了 3 次 - -:: - - 执行了 1 次 - 执行了 2 次 - 执行了 3 次 - [25.88057063502083] - -这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 - -海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 - --------------- - -|image2| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/image-20200418122739417.png -.. |image2| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_43.md b/source/c01/c01_43.md deleted file mode 100644 index f5c9bc6..0000000 --- a/source/c01/c01_43.md +++ /dev/null @@ -1,353 +0,0 @@ -# 1.43 求你了,别再使用 pprint 打印字典了 - -![](http://image.iswbm.com/20200602135014.png) - -## 1. 吐槽问题 - -pprint 你应该很熟悉了吧? - -随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货 。 - -比如这下面这个 json 字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。 - -```json -[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] -``` - -如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint 看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。 - -```python ->>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] ->>> ->>> from pprint import pprint ->>> pprint(info) -[{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92', - 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', - 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', - 'id': 1580615, - 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b', - 'packageName': 'com.renren.mobile.android', - 'size': 21803987, - 'stars': 2}, - {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf', - 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', - 'iconUrl': 'app/com.ct.client/icon.jpg', - 'id': 1540629, - 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84', - 'packageName': 'com.ct.client', - 'size': 4794202, - 'stars': 2}] -``` - -好像有点效果,真的是 “神器”呀。 - -但是你告诉我, **\xe4\xbd\xa0\xe7\x9a** 这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。 - -好在我懂点 Python 2 的编码,知道 Python 2 中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte 存储的。 - -行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。 - -```python ->>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] ->>> ->>> from pprint import pprint ->>> pprint(info) -[{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752', - 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', - 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', - 'id': 1580615, - 'name': u'\u76ae\u7684\u561b', - 'packageName': 'com.renren.mobile.android', - 'size': 21803987, - 'stars': 2}, - {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f', - 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', - 'iconUrl': 'app/com.ct.client/icon.jpg', - 'id': 1540629, - 'name': u'\u4e0d\u5b58\u5728\u7684', - 'packageName': 'com.ct.client', - 'size': 4794202, - 'stars': 2}] -``` - -确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀? - -``` -u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f' -``` - -除此之外,我们知道 json 的严格要求必须使用 **双引号**,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是 **单引号**?我也太难了吧,我连自己的代码都无法控制了吗? - -到这里,我们知道了 pprint 带来的两个问题: - -1. 没法在 Python 2 下正常打印中文 -2. 没法输出 JSON 标准格式的格式化内容(双引号) - -## 2. 解决问题 - -### 打印中文 - -如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。 - -```python -# Python3.7 ->>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] ->>> ->>> from pprint import pprint ->>> pprint(info) -[{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青', - 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', - 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', - 'id': 1580615, - 'name': '皮的嘛', - 'packageName': 'com.renren.mobile.android', - 'size': 21803987, - 'stars': 2}, - {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿', - 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', - 'iconUrl': 'app/com.ct.client/icon.jpg', - 'id': 1540629, - 'name': '不存在的', - 'packageName': 'com.ct.client', - 'size': 4794202, - 'stars': 2}] ->>> - -``` - -但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的 Python,本来我可以选择不用的,因为有更好的替代方案(**这个后面会讲**)。 - -但是我出于猎奇,正好前两天不是写过一篇关于 编码 的文章吗,我自认为自己对于 编码还是掌握比较熟练的,就想着来解决一下这个问题。 - -索性就来看下 pprint 的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。 - -**以下是我的解决方案,供你参考**: - -写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer) - -并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str 类型,而是 unicode 类型,就用 uft8 编码成 str 类型。 - -```python -# coding: utf-8 -from pprint import PrettyPrinter - -# 继承 PrettyPrinter,复写 format 方法 -class MyPrettyPrinter(PrettyPrinter): - def format(self, object, context, maxlevels, level): - if isinstance(object, unicode): - return (object.encode('utf8'), True, False) - return PrettyPrinter.format(self, object, context, maxlevels, level) - -info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - -MyPrettyPrinter().pprint(info) -``` - -输出如下,已经解决了中文的显示问题: - -![](http://image.iswbm.com/20200507171451.png) - -### 打印双引号 - -解决了中文问题后,再来看看如何让 pprint 打印双引号。 - -在实例化 PrettyPrinter 对象的时候,可以接收一个 stream 对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个 stream,也就是标准输出。 - -现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。 - -那我们完全可以自己定义一个 stream 类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。 - -有了思路,就可以开始写代码了,如下: - -```python -# coding: utf-8 -from pprint import PrettyPrinter - -class MyPrettyPrinter(PrettyPrinter): - def format(self, object, context, maxlevels, level): - if isinstance(object, unicode): - return (object.encode('utf8'), True, False) - return PrettyPrinter.format(self, object, context, maxlevels, level) - -class MyStream(): - def write(self, text): - print text.replace('\'', '"') - -info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] -MyPrettyPrinter(stream=MyStream()).pprint(info) -``` - -尝试执行了下,我的天,怎么是这样子的。 - -```json -[ -{ -"des" -: -2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青 -, - "downloadUrl": -"app/com.renren.mobile.android/com.renren.mobile.android.apk" -, - "iconUrl": -"app/com.renren.mobile.android/icon.jpg" -, - "id": -1580615 -, - "name": -皮的嘛 -, - "packageName": -"com.renren.mobile.android" -, - "size": -21803987 -, - "stars": -2 -} -, - -{ -"des" -: -斗鱼271934走过路过不要错过,这里有最好的鸡儿 -, - "downloadUrl": -"app/com.ct.client/com.ct.client.apk" -, - "iconUrl": -"app/com.ct.client/icon.jpg" -, - "id": -1540629 -, - "name": -不存在的 -, - "packageName": -"com.ct.client" -, - "size": -4794202 -, - "stars": -2 -} -] -``` - -经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个 **换行符**。 - -那如何将使 print 函数打印的内容,不进行换行呢? - -方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个 **逗号** 就行。 - -就像下面这样。 - -![](http://image.iswbm.com/20200507174459.png) - -知道了问题所在,再修改下代码 - -```python -# coding: utf-8 -from pprint import PrettyPrinter - -class MyPrettyPrinter(PrettyPrinter): - def format(self, object, context, maxlevels, level): - if isinstance(object, unicode): - return (object.encode('utf8'), True, False) - return PrettyPrinter.format(self, object, context, maxlevels, level) - -class MyStream(): - def write(self, text): - print text.replace('\'', '"'), - -info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - -MyPrettyPrinter(stream=MyStream()).pprint(info) -``` - -终于成功了,太不容易了吧。 - -![](http://image.iswbm.com/20200507174802.png) - -## 3. 何必折腾 - -通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。 - -代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。 - -**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**。 - -为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python ,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗? - -如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用 pprint 了。 - -### 打印中文 - -其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。 - -但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint 简单得不要太多。 - -具体的代码示例如下: - -```python ->>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] ->>> ->>> import json ->>> ->>> ->>> print json.dumps(info, indent=4, ensure_ascii=False) -[ - { - "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk", - "iconUrl": "app/com.renren.mobile.android/icon.jpg", - "name": "皮的嘛", - "stars": 2, - "packageName": "com.renren.mobile.android", - "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青", - "id": 1580615, - "size": 21803987 - }, - { - "downloadUrl": "app/com.ct.client/com.ct.client.apk", - "iconUrl": "app/com.ct.client/icon.jpg", - "name": "不存在的", - "stars": 2, - "packageName": "com.ct.client", - "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿", - "id": 1540629, - "size": 4794202 - } -] ->>> -``` - -json.dumps 的关键参数有两个: - -- **indent=4**:以 4 个空格缩进单位 -- **ensure_ascii=False**:接收非 ASCII 编码的字符,这样才能使用中文 - -与 pprint 相比 json.dumps 可以说完胜: - -1. 两个参数就能实现所有我的需求(打印中文与双引号) -2. 就算在 Python 2 下,使用中文也不需要用 `u'中文'` 这种写法 -3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题 - -## 4. 总结一下 - -本来很简单的一个观点,我为了证明 pprint 实现那两个需求有多么困难,花了很多的时间去研究了 pprint 的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。 - -本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点 - -1. 核心观点:Python2 下不要再使用 pprint -2. 若真要使用,且有和一样的改造需求,可以参考我的实现 -3. Python 2 中的 print 语句后居然可以加 逗号 - -以上。希望此文能对你有帮助。 - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_43.rst b/source/c01/c01_43.rst deleted file mode 100644 index cb4ba3b..0000000 --- a/source/c01/c01_43.rst +++ /dev/null @@ -1,391 +0,0 @@ -1.43 求你了,别再使用 pprint 打印字典了 -======================================= - -|image0| - -1. 吐槽问题 ------------ - -pprint 你应该很熟悉了吧? - -随便在搜索引擎上搜索如何打印漂亮的字典或者格式化字符串时,大部分人都会推荐你使用这货 -。 - -比如这下面这个 json -字符串或者说字典(我随便在网上找的),如果不格式化美化一下,根本无法阅读。 - -.. code:: json - - [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] - -如果你不想看到一堆密密麻麻的字,那就使用大伙都极力推荐的 pprint -看下什么效果(以下在 Python 2 中演示,Python 3 中是不一样的效果)。 - -.. code:: python - - >>> info=[{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017 你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934 走过路过不要错过,这里有最好的鸡儿"}] - >>> - >>> from pprint import pprint - >>> pprint(info) - [{'des': '2011-2017 \xe4\xbd\xa0\xe7\x9a\x84\xe9\x93\x81\xe5\xa4\xb4\xe5\xa8\x83\xe4\xb8\x80\xe7\x9b\xb4\xe5\x9c\xa8\xe8\xbf\x99\xe5\x84\xbf\xe3\x80\x82\xe4\xb8\xad\xe5\x9b\xbd\xe6\x9c\x80\xe5\xa4\xa7\xe7\x9a\x84\xe5\xae\x9e\xe5\x90\x8d\xe5\x88\xb6SNS\xe7\xbd\x91\xe7\xbb\x9c\xe5\xb9\xb3\xe5\x8f\xb0\xef\xbc\x8c\xe5\xab\xa9\xe5\xa4\xb4\xe9\x9d\x92', - 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', - 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', - 'id': 1580615, - 'name': '\xe7\x9a\xae\xe7\x9a\x84\xe5\x98\x9b', - 'packageName': 'com.renren.mobile.android', - 'size': 21803987, - 'stars': 2}, - {'des': '\xe6\x96\x97\xe9\xb1\xbc271934 \xe8\xb5\xb0\xe8\xbf\x87\xe8\xb7\xaf\xe8\xbf\x87\xe4\xb8\x8d\xe8\xa6\x81\xe9\x94\x99\xe8\xbf\x87\xef\xbc\x8c\xe8\xbf\x99\xe9\x87\x8c\xe6\x9c\x89\xe6\x9c\x80\xe5\xa5\xbd\xe7\x9a\x84\xe9\xb8\xa1\xe5\x84\xbf', - 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', - 'iconUrl': 'app/com.ct.client/icon.jpg', - 'id': 1540629, - 'name': '\xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8\xe7\x9a\x84', - 'packageName': 'com.ct.client', - 'size': 4794202, - 'stars': 2}] - -好像有点效果,真的是 “神器”呀。 - -但是你告诉我, -**:raw-latex:`\xe`4:raw-latex:`\xbd`:raw-latex:`\xa`0:raw-latex:`\xe`7:raw-latex:`\x`9a** -这些是什么玩意?本来想提高可读性的,现在变成完全不可读了。 - -好在我懂点 Python 2 的编码,知道 Python 2 -中默认(不带u)的字符串格式都是 str 类型,也是 bytes 类型,它是以 byte -存储的。 - -行吧,好像是我错了,我改了下,使用 unicode 类型来定义中文字符串吧。 - -.. code:: python - - >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - >>> - >>> from pprint import pprint - >>> pprint(info) - [{'des': u'2011-2017\u4f60\u7684\u94c1\u5934\u5a03\u4e00\u76f4\u5728\u8fd9\u513f\u3002\u4e2d\u56fd\u6700\u5927\u7684\u5b9e\u540d\u5236SNS\u7f51\u7edc\u5e73\u53f0\uff0c\u5ae9\u5934\u9752', - 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', - 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', - 'id': 1580615, - 'name': u'\u76ae\u7684\u561b', - 'packageName': 'com.renren.mobile.android', - 'size': 21803987, - 'stars': 2}, - {'des': u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f', - 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', - 'iconUrl': 'app/com.ct.client/icon.jpg', - 'id': 1540629, - 'name': u'\u4e0d\u5b58\u5728\u7684', - 'packageName': 'com.ct.client', - 'size': 4794202, - 'stars': 2}] - -确实是有好点了,但是看到下面这些,我崩溃了,我哪里知道这是什么鬼,难道是我太菜了吗?当我是计算机呀? - -:: - - u'\u6597\u9c7c271934\u8d70\u8fc7\u8def\u8fc7\u4e0d\u8981\u9519\u8fc7\uff0c\u8fd9\u91cc\u6709\u6700\u597d\u7684\u9e21\u513f' - -除此之外,我们知道 json 的严格要求必须使用 -**双引号**\ ,而我定义字典时,也使用了双引号了,为什么打印出来的为什么是 -**单引号**\ ?我也太难了吧,我连自己的代码都无法控制了吗? - -到这里,我们知道了 pprint 带来的两个问题: - -1. 没法在 Python 2 下正常打印中文 -2. 没法输出 JSON 标准格式的格式化内容(双引号) - -2. 解决问题 ------------ - -打印中文 -~~~~~~~~ - -如果你是在 Python 3 下使用,你会发现中文是可以正常显示的。 - -.. code:: python - - # Python3.7 - >>> info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - >>> - >>> from pprint import pprint - >>> pprint(info) - [{'des': '2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青', - 'downloadUrl': 'app/com.renren.mobile.android/com.renren.mobile.android.apk', - 'iconUrl': 'app/com.renren.mobile.android/icon.jpg', - 'id': 1580615, - 'name': '皮的嘛', - 'packageName': 'com.renren.mobile.android', - 'size': 21803987, - 'stars': 2}, - {'des': '斗鱼271934走过路过不要错过,这里有最好的鸡儿', - 'downloadUrl': 'app/com.ct.client/com.ct.client.apk', - 'iconUrl': 'app/com.ct.client/icon.jpg', - 'id': 1540629, - 'name': '不存在的', - 'packageName': 'com.ct.client', - 'size': 4794202, - 'stars': 2}] - >>> - -但是很多时候(在公司的一些服务器)你无法选择自己使用哪个版本的 -Python,本来我可以选择不用的,因为有更好的替代方案(\ **这个后面会讲**\ )。 - -但是我出于猎奇,正好前两天不是写过一篇关于 编码 -的文章吗,我自认为自己对于 -编码还是掌握比较熟练的,就想着来解决一下这个问题。 - -索性就来看下 pprint -的源代码,还真被我找到了解决方法,如果你也想挑战一下,不防在这里停住,自己研究一下如何实现,我相信对你阅读源码会有帮助。 - -**以下是我的解决方案,供你参考**\ : - -写一个自己的 printer 对象,继承自 PrettyPrinter (pprint 使用的printer) - -并且复写 format 方法,判断传进来的字符串对象是否 str 类型,如果不是 str -类型,而是 unicode 类型,就用 uft8 编码成 str 类型。 - -.. code:: python - - # coding: utf-8 - from pprint import PrettyPrinter - - # 继承 PrettyPrinter,复写 format 方法 - class MyPrettyPrinter(PrettyPrinter): - def format(self, object, context, maxlevels, level): - if isinstance(object, unicode): - return (object.encode('utf8'), True, False) - return PrettyPrinter.format(self, object, context, maxlevels, level) - - info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - - MyPrettyPrinter().pprint(info) - -输出如下,已经解决了中文的显示问题: - -|image1| - -打印双引号 -~~~~~~~~~~ - -解决了中文问题后,再来看看如何让 pprint 打印双引号。 - -在实例化 PrettyPrinter 对象的时候,可以接收一个 stream -对象,它表示你要将内容输出到哪里,默认是使用 sys.stdout 这个 -stream,也就是标准输出。 - -现在我们要修改输出的内容,也就是将输出的单引号替换成双引号。 - -那我们完全可以自己定义一个 stream -类型的对象,该对象不需要继承任何父类,只要你实现 write 方法就可以。 - -有了思路,就可以开始写代码了,如下: - -.. code:: python - - # coding: utf-8 - from pprint import PrettyPrinter - - class MyPrettyPrinter(PrettyPrinter): - def format(self, object, context, maxlevels, level): - if isinstance(object, unicode): - return (object.encode('utf8'), True, False) - return PrettyPrinter.format(self, object, context, maxlevels, level) - - class MyStream(): - def write(self, text): - print text.replace('\'', '"') - - info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - MyPrettyPrinter(stream=MyStream()).pprint(info) - -尝试执行了下,我的天,怎么是这样子的。 - -.. code:: json - - [ - { - "des" - : - 2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青 - , - "downloadUrl": - "app/com.renren.mobile.android/com.renren.mobile.android.apk" - , - "iconUrl": - "app/com.renren.mobile.android/icon.jpg" - , - "id": - 1580615 - , - "name": - 皮的嘛 - , - "packageName": - "com.renren.mobile.android" - , - "size": - 21803987 - , - "stars": - 2 - } - , - - { - "des" - : - 斗鱼271934走过路过不要错过,这里有最好的鸡儿 - , - "downloadUrl": - "app/com.ct.client/com.ct.client.apk" - , - "iconUrl": - "app/com.ct.client/icon.jpg" - , - "id": - 1540629 - , - "name": - 不存在的 - , - "packageName": - "com.ct.client" - , - "size": - 4794202 - , - "stars": - 2 - } - ] - -经过一番研究,才知道是因为 print 函数默认会将打印的内容后面加个 -**换行符**\ 。 - -那如何将使 print 函数打印的内容,不进行换行呢? - -方法很简单,但是我相信很多人都不知道,只要在 print 的内容后加一个 -**逗号** 就行。 - -就像下面这样。 - -|image2| - -知道了问题所在,再修改下代码 - -.. code:: python - - # coding: utf-8 - from pprint import PrettyPrinter - - class MyPrettyPrinter(PrettyPrinter): - def format(self, object, context, maxlevels, level): - if isinstance(object, unicode): - return (object.encode('utf8'), True, False) - return PrettyPrinter.format(self, object, context, maxlevels, level) - - class MyStream(): - def write(self, text): - print text.replace('\'', '"'), - - info = [{"id":1580615,"name":u"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":u"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":u"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":u"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - - MyPrettyPrinter(stream=MyStream()).pprint(info) - -终于成功了,太不容易了吧。 - -|image3| - -3. 何必折腾 ------------ - -通过上面的一番折腾,我终于实现了我 **梦寐以求** 的需求。 - -代价就是我整整花费了两个小时,才得以实现,而对于小白来说,可能没有信心,也没有耐心去做这样的事情。 - -**所以我想说的是,Python 2 下的 pprint ,真的不要再用了**\ 。 - -为什么我要用这么 说,因为明明有更好的替代品,人生苦短,既然用了 Python -,当然是怎么简单怎么来咯,何必为难自己呢,一行代码可以解决的事情,偏偏要去写两个类,那不是自讨苦吃吗?(我这是在骂自己吗? - -如果你愿意抛弃 pprint ,那我推荐你用 json.dumps ,我保证你再也不想用 -pprint 了。 - -.. _打印中文-1: - -打印中文 -~~~~~~~~ - -其实无法打印中文,是 Python 2 引来的大坑,并不能全怪 pprint 。 - -但是同样的问题,在 json.dumps 这里,却只要加个参数就好了,可比 pprint -简单得不要太多。 - -具体的代码示例如下: - -.. code:: python - - >>> info = [{"id":1580615,"name":"皮的嘛","packageName":"com.renren.mobile.android","iconUrl":"app/com.renren.mobile.android/icon.jpg","stars":2,"size":21803987,"downloadUrl":"app/com.renren.mobile.android/com.renren.mobile.android.apk","des":"2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青"},{"id":1540629,"name":"不存在的","packageName":"com.ct.client","iconUrl":"app/com.ct.client/icon.jpg","stars":2,"size":4794202,"downloadUrl":"app/com.ct.client/com.ct.client.apk","des":"斗鱼271934走过路过不要错过,这里有最好的鸡儿"}] - >>> - >>> import json - >>> - >>> - >>> print json.dumps(info, indent=4, ensure_ascii=False) - [ - { - "downloadUrl": "app/com.renren.mobile.android/com.renren.mobile.android.apk", - "iconUrl": "app/com.renren.mobile.android/icon.jpg", - "name": "皮的嘛", - "stars": 2, - "packageName": "com.renren.mobile.android", - "des": "2011-2017你的铁头娃一直在这儿。中国最大的实名制SNS网络平台,嫩头青", - "id": 1580615, - "size": 21803987 - }, - { - "downloadUrl": "app/com.ct.client/com.ct.client.apk", - "iconUrl": "app/com.ct.client/icon.jpg", - "name": "不存在的", - "stars": 2, - "packageName": "com.ct.client", - "des": "斗鱼271934走过路过不要错过,这里有最好的鸡儿", - "id": 1540629, - "size": 4794202 - } - ] - >>> - -json.dumps 的关键参数有两个: - -- **indent=4**\ :以 4 个空格缩进单位 -- **ensure_ascii=False**\ :接收非 ASCII 编码的字符,这样才能使用中文 - -与 pprint 相比 json.dumps 可以说完胜: - -1. 两个参数就能实现所有我的需求(打印中文与双引号) -2. 就算在 Python 2 下,使用中文也不需要用 ``u'中文'`` 这种写法 -3. Python2 和 Python3 的写法完全一致,对于这一点不需要考虑兼容问题 - -4. 总结一下 ------------ - -本来很简单的一个观点,我为了证明 pprint -实现那两个需求有多么困难,花了很多的时间去研究了 pprint -的源码(各种处理其实还是挺复杂的),不过好在最后也能有所收获。 - -本文的分享就到这里,阅读本文,我认为你可以获取到三个知识点 - -1. 核心观点:Python2 下不要再使用 pprint -2. 若真要使用,且有和一样的改造需求,可以参考我的实现 -3. Python 2 中的 print 语句后居然可以加 逗号 - -以上。希望此文能对你有帮助。 - -|image4| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200507171451.png -.. |image2| image:: http://image.iswbm.com/20200507174459.png -.. |image3| image:: http://image.iswbm.com/20200507174802.png -.. |image4| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_44.md b/source/c01/c01_44.md deleted file mode 100644 index 5f818e4..0000000 --- a/source/c01/c01_44.md +++ /dev/null @@ -1,274 +0,0 @@ -# 1.44 详解 Python 中的编码问题 - -![](http://image.iswbm.com/20200602135014.png) - -Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 Python 开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 - -一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式? - -反反复复,这个过程真是太痛苦了。 - -今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再 Google,收藏这篇文章就行。 - - - -## 1. Python 3 中 str 与 bytes - -在 Python3中,字符串有两种类型 ,str 和 bytes。 - -今天就来说一说这二者的区别: - -- `unicode string(str 类型)`:以 Unicode code points 形式存储,**人类认识的形式** -- `byte string(bytes 类型)`:以 byte 形式存储,**机器认识的形式** - -在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 `type` 和 `isinstance` 可以判别 - -```python -# python3 - ->>> str_obj = "你好" ->>> ->>> type(str_obj) - ->>> ->>> isinstance("你好", str) -True ->>> ->>> isinstance("你好", bytes) -False ->>> -``` - -而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个 `b`,就表示你要定义一个 bytes 类型的字符串对象。 - -```python -# python3 ->>> byte_obj = b"Hello World!" ->>> type(byte_obj) - ->>> ->>> isinstance(byte_obj, str) -False ->>> ->>> isinstance(byte_obj, bytes) -True ->>> -``` - -但是在定义中文字符串时,你就不能直接在前面加 `b` 了,而应该使用 `encode` 转一下。 - -```python ->>> byte_obj=b"你好" - File "", line 1 -SyntaxError: bytes can only contain ASCII literal characters. ->>> ->>> str_obj="你好" ->>> ->>> str_obj.encode("utf-8") -b'\xe4\xbd\xa0\xe5\xa5\xbd' ->>> -``` - -## 2. Python 2 中 str 与 unicode - -而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。 - -在 Python2 里,字符串也只有两种类型,unicode 和 str 。 - -只有 unicode object 和 非unicode object(其实应该叫 str object) 的区别: - -- `unicode string(unicode类型)`:以 Unicode code points 形式存储,**人类认识的形式** -- `byte string(str 类型)`:以 byte 形式存储,**机器认识的形式** - -当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str 字符串对象,比如这样 - -```python -# python2 - ->>> str_obj="你好" ->>> ->>> type(str_obj) - ->>> ->>> str_obj -'\xe4\xbd\xa0\xe5\xa5\xbd' ->>> ->>> isinstance(str_obj, bytes) -True ->>> isinstance(str_obj, str) -True ->>> isinstance(str_obj, unicode) -False ->>> ->>> str is bytes -True -``` - -而当我们在双引号或单引号前面加个 `u`,就表明我们定义的是 unicode 字符串对象,比如这样 - -```python -# python2 - ->>> unicode_obj = u"你好" ->>> ->>> unicode_obj -u'\u4f60\u597d' ->>> ->>> type(unicode_obj) - ->>> ->>> isinstance(unicode_obj, bytes) -False ->>> isinstance(unicode_obj, str) -False ->>> ->>> isinstance(unicode_obj, unicode) -True -``` - - - -## 3. 如何检测对象的编码 - -所有的字符,在 unicode 字符集中都有对应的编码值(英文叫做:`code point`) - -而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312 等。 - -也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。 - -那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码? - -这时候就要介绍到一个 python 的库 -- `chardet` ,使用它之前 需要先安装 - -``` -python3 -m pip install chardet -``` - -chardet 有一个 detect 方法,可以 `预测`其其编码格式 - -```python ->>> import chardet ->>> chardet.detect('微信公众号:Python编程时光'.encode('gbk')) -{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} -``` - -为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence 字段,其表示预测的可信度,或者说成功率。 - -但是使用它时,若你的字符数较少,就有可能 “`误诊`”),比如只有 `中文` 两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成 KOI8-R 编码。 - -```python ->>> str_obj = "中文" ->>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes ->>> ->>> chardet.detect(byte_obj) -{'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'} ->>> ->>> str_obj2 = str(byte_obj, encoding='KOI8-R') ->>> str_obj2 -'жпнд' -``` - -所以为了编码诊断的准确,要尽量使用足够多的字符。 - -chardet 支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html) - -![](http://image.iswbm.com/20200423185819.png) - - - -## 4. 编码与解码的区别 - -编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2 已经远去,这里以及后面都只用 Python 3 举例) - -- **编码**:encode 方法,把字符串对象转化为二进制字节序列 - -- **解码**:decode 方法,把二进制字节序列转化为字符串对象 - -![](http://image.iswbm.com/20200423190331.png) - - - -那么假如我们真知道了其编码格式,如何来转成 unicode 呢? - -**有两种方法** - -**第一种**是,直接使用 decode 方法 - -```python ->>> byte_obj.decode('gbk') -'中文' ->>> -``` - -**第二种**是,使用 str 类来转 - -```python ->>> str_obj = str(byte_obj, encoding='gbk') ->>> str_obj -'中文' ->>> -``` - - - -## 5. 如何设置文件编码 - -在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python 2 的时候,如果你的 python 文件里有中文,运行是会报错的。 - -``` -SyntaxError: Non-ASCII character '\xe4' in file demo.py -``` - -原因就是 ASCII 编码表太小,无法解释中文。 - -而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。 - -对于这个问题,通常解决方法有两种: - -**第一种方法** - -在 python2 中,可以使用在头部指定 - -可以这样写,虽然很好看 - -``` -# -*- coding: utf-8 -*- -``` - -但这样写太麻烦了,我通常使用下面两种写法 - -``` -# coding:utf-8 -# coding=utf-8 -``` - - - -**第二种方法** - -``` -import sys - -reload(sys) -sys.setdefaultencoding('utf-8') -``` - -这里在调用sys.setdefaultencoding(‘utf-8’) 设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除 sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用 sys.setdefaultencoding 这个方法。 - - - -## 6. 参考文章 - - - -- [阮一峰老师文章的常识性错误之 Unicode 与 UTF-8](https://foofish.net/unicode_utf-8.html) -- [Strings, Bytes, and Unicode in Python 2 and 3](https://timothybramlett.com/Strings_Bytes_and_Unicode_in_Python_2_and_3.html) -- [字符编码笔记:ASCII,Unicode 和 UTF-8](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) - - - ---- - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_44.rst b/source/c01/c01_44.rst deleted file mode 100644 index 70b3276..0000000 --- a/source/c01/c01_44.rst +++ /dev/null @@ -1,295 +0,0 @@ -1.44 详解 Python 中的编码问题 -============================= - -|image0| - -Python 中编码问题,一直是很多 Python 开发者的噩梦,尽管你是工作多年的 -Python -开发者,也肯定会经常遇到令人神烦的编码问题,好不容易花了半天搞明白了。 - -一段时间后,又全都忘光光了,一脸懵逼的你又开始你找各种博客、帖子,从头搞清楚什么是编码?什么是 -unicode?它和 ASCII 有什么区别?为什么 decode encode 老是报错?python2 -里和 python3 的字符串类型怎么都不一样,怎么对应起来?如何检测编码格式? - -反反复复,这个过程真是太痛苦了。 - -今天我把大家在 Python 上会遇到的一些编码问题都讲清楚了,以后你可以不用再 -Google,收藏这篇文章就行。 - -1. Python 3 中 str 与 bytes ---------------------------- - -在 Python3中,字符串有两种类型 ,str 和 bytes。 - -今天就来说一说这二者的区别: - -- ``unicode string(str 类型)``\ :以 Unicode code points - 形式存储,\ **人类认识的形式** -- ``byte string(bytes 类型)``\ :以 byte - 形式存储,\ **机器认识的形式** - -在 Python 3 中你定义的所有字符串,都是 unicode string类型,使用 ``type`` -和 ``isinstance`` 可以判别 - -.. code:: python - - # python3 - - >>> str_obj = "你好" - >>> - >>> type(str_obj) - - >>> - >>> isinstance("你好", str) - True - >>> - >>> isinstance("你好", bytes) - False - >>> - -而 bytes 是一个二进制序列对象,你只要你在定义字符串时前面加一个 -``b``\ ,就表示你要定义一个 bytes 类型的字符串对象。 - -.. code:: python - - # python3 - >>> byte_obj = b"Hello World!" - >>> type(byte_obj) - - >>> - >>> isinstance(byte_obj, str) - False - >>> - >>> isinstance(byte_obj, bytes) - True - >>> - -但是在定义中文字符串时,你就不能直接在前面加 ``b`` 了,而应该使用 -``encode`` 转一下。 - -.. code:: python - - >>> byte_obj=b"你好" - File "", line 1 - SyntaxError: bytes can only contain ASCII literal characters. - >>> - >>> str_obj="你好" - >>> - >>> str_obj.encode("utf-8") - b'\xe4\xbd\xa0\xe5\xa5\xbd' - >>> - -2. Python 2 中 str 与 unicode ------------------------------ - -而在 Python2 中,字符串的类型又与 Python3 不一样,需要仔细区分。 - -在 Python2 里,字符串也只有两种类型,unicode 和 str 。 - -只有 unicode object 和 非unicode object(其实应该叫 str object) -的区别: - -- ``unicode string(unicode类型)``\ :以 Unicode code points - 形式存储,\ **人类认识的形式** -- ``byte string(str 类型)``\ :以 byte 形式存储,\ **机器认识的形式** - -当我们直接使用双引号或单引号包含字符的方式来定义字符串时,就是 str -字符串对象,比如这样 - -.. code:: python - - # python2 - - >>> str_obj="你好" - >>> - >>> type(str_obj) - - >>> - >>> str_obj - '\xe4\xbd\xa0\xe5\xa5\xbd' - >>> - >>> isinstance(str_obj, bytes) - True - >>> isinstance(str_obj, str) - True - >>> isinstance(str_obj, unicode) - False - >>> - >>> str is bytes - True - -而当我们在双引号或单引号前面加个 ``u``\ ,就表明我们定义的是 unicode -字符串对象,比如这样 - -.. code:: python - - # python2 - - >>> unicode_obj = u"你好" - >>> - >>> unicode_obj - u'\u4f60\u597d' - >>> - >>> type(unicode_obj) - - >>> - >>> isinstance(unicode_obj, bytes) - False - >>> isinstance(unicode_obj, str) - False - >>> - >>> isinstance(unicode_obj, unicode) - True - -3. 如何检测对象的编码 ---------------------- - -所有的字符,在 unicode -字符集中都有对应的编码值(英文叫做:\ ``code point``\ ) - -而把这些编码值按照一定的规则保存成二进制字节码,就是我们说的编码方式,常见的有:UTF-8,GB2312 -等。 - -也就是说,当我们要将内存中的字符串持久化到硬盘中的时候,都要指定编码方法,而反过来,读取的时候,也要指定正确的编码方法(这个过程叫解码),不然会出现乱码。 - -那问题就来了,当我们知道了其对应的编码方法,我们就可以正常解码,但并不是所有时候我们都能知道应该用什么编码方式去解码? - -这时候就要介绍到一个 python 的库 – ``chardet`` ,使用它之前 需要先安装 - -:: - - python3 -m pip install chardet - -chardet 有一个 detect 方法,可以 ``预测``\ 其其编码格式 - -.. code:: python - - >>> import chardet - >>> chardet.detect('微信公众号:Python编程时光'.encode('gbk')) - {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'} - -为什么说是预测呢,通过上面的输出来看,你会看到有一个 confidence -字段,其表示预测的可信度,或者说成功率。 - -但是使用它时,若你的字符数较少,就有可能 “``误诊``”),比如只有 ``中文`` -两个字,就像下面这样,我们是 使用 gbk 编码的,使用 chardet 却识别成 -KOI8-R 编码。 - -.. code:: python - - >>> str_obj = "中文" - >>> byte_obj = bytes(a, encoding='gbk') # 先得到一个 gbk 编码的 bytes - >>> - >>> chardet.detect(byte_obj) - {'encoding': 'KOI8-R', 'confidence': 0.682639754276994, 'language': 'Russian'} - >>> - >>> str_obj2 = str(byte_obj, encoding='KOI8-R') - >>> str_obj2 - 'жпнд' - -所以为了编码诊断的准确,要尽量使用足够多的字符。 - -chardet -支持多国的语言,从官方文档中可以看到支持如下这些语言(https://chardet.readthedocs.io/en/latest/supported-encodings.html) - -|image1| - -4. 编码与解码的区别 -------------------- - -编码和解码,其实就是 str 与 bytes 的相互转化的过程(Python 2 -已经远去,这里以及后面都只用 Python 3 举例) - -- **编码**\ :encode 方法,把字符串对象转化为二进制字节序列 - -- **解码**\ :decode 方法,把二进制字节序列转化为字符串对象 - -|image2| - -那么假如我们真知道了其编码格式,如何来转成 unicode 呢? - -**有两种方法** - -**第一种**\ 是,直接使用 decode 方法 - -.. code:: python - - >>> byte_obj.decode('gbk') - '中文' - >>> - -**第二种**\ 是,使用 str 类来转 - -.. code:: python - - >>> str_obj = str(byte_obj, encoding='gbk') - >>> str_obj - '中文' - >>> - -5. 如何设置文件编码 -------------------- - -在 Python 2 中,默认使用的是 ASCII 编码来读取的,因此,我们在使用 Python -2 的时候,如果你的 python 文件里有中文,运行是会报错的。 - -:: - - SyntaxError: Non-ASCII character '\xe4' in file demo.py - -原因就是 ASCII 编码表太小,无法解释中文。 - -而在 Python 3 中,默认使用的是 uft-8 来读取,所以省了不少的事。 - -对于这个问题,通常解决方法有两种: - -**第一种方法** - -在 python2 中,可以使用在头部指定 - -可以这样写,虽然很好看 - -:: - - # -*- coding: utf-8 -*- - -但这样写太麻烦了,我通常使用下面两种写法 - -:: - - # coding:utf-8 - # coding=utf-8 - -**第二种方法** - -:: - - import sys - - reload(sys) - sys.setdefaultencoding('utf-8') - -这里在调用sys.setdefaultencoding(‘utf-8’) -设置默认的解码方式之前,执行了reload(sys),这是必须的,因为python在加载完sys之后,会删除 -sys.setdefaultencoding 这个方法,我们需要重新载入sys,才能调用 -sys.setdefaultencoding 这个方法。 - -6. 参考文章 ------------ - -- `阮一峰老师文章的常识性错误之 Unicode 与 - UTF-8 `__ -- `Strings, Bytes, and Unicode in Python 2 and - 3 `__ -- `字符编码笔记:ASCII,Unicode 和 - UTF-8 `__ - --------------- - -|image3| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200423185819.png -.. |image2| image:: http://image.iswbm.com/20200423190331.png -.. |image3| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_45.md b/source/c01/c01_45.md deleted file mode 100644 index caa4153..0000000 --- a/source/c01/c01_45.md +++ /dev/null @@ -1,290 +0,0 @@ -# 1.45 Python炫技操作:花式导包的八种方法 - -![](http://image.iswbm.com/20200602135014.png) - - - -## 1. 直接 import - -人尽皆知的方法,直接导入即可 - -```python ->>> import os ->>> os.getcwd() -'/home/wangbm' -``` - -与此类似的还有,不再细讲 - -```python -import ... -import ... as ... -from ... import ... -from ... import ... as ... -``` - -一般情况下,使用 `import` 语句导入模块已经够用的。 - -但是在一些特殊场景中,可能还需要其他的导入方式。 - -下面我会一一地给你介绍。 - -## 2. 使用 \__import__ - -`__import__` 函数可用于导入模块,import 语句也会调用函数。其定义为: - -``` -__import__(name[, globals[, locals[, fromlist[, level]]]]) -``` - -参数介绍: - -- name (required): 被加载 module 的名称 -- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 global() -- locals (optional): 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local() -- fromlist (Optional): 被导入的 submodule 名称 -- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 类似于 '.',2 类似于 '..'。 - -使用示例如下: - -```python ->>> os = __import__('os') ->>> os.getcwd() -'/home/wangbm' -``` - -如果要实现 `import xx as yy` 的效果,只要修改左值即可 - -如下示例,等价于 `import os as myos`: - -```python ->>> myos = __import__('os') ->>> myos.getcwd() -'/home/wangbm' -``` - - - -上面说过的 `__import__` 是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于 `__buildins__` 中,因此我们还可以这样导入 os 的模块: - -```python ->>> __builtins__.__dict__['__import__']('os').getcwd() -'/home/wangbm' -``` - - - -## 3. 使用 importlib 模块 - -importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。 - -它的简单示例: - -```python ->>> import importlib ->>> myos=importlib.import_module("os") ->>> myos.getcwd() -'/home/wangbm' -``` - -如果要实现 `import xx as yy`效果,可以这样 - -```python ->>> import importlib ->>> ->>> myos = importlib.import_module("os") ->>> myos.getcwd() -'/home/wangbm' -``` - - - -## 4. 使用 imp 模块 - -`imp` 模块提供了一些 import 语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的 `__import__` 函数功能: - -```python ->>> import imp ->>> file, pathname, desc = imp.find_module('os') ->>> myos = imp.load_module('sep', file, pathname, desc) ->>> myos - ->>> myos.getcwd() -'/home/wangbm' -``` - -从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib 模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib 模块的合集。 - - - -## 5. 使用 execfile - -在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。 - -语法如下: - -``` -execfile(filename[, globals[, locals]]) -``` - -参数有这么几个: - -- filename:文件名。 -- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。 -- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。 - -```python ->>> execfile("/usr/lib64/python2.7/os.py") ->>> ->>> getcwd() -'/home/wangbm' -``` - - - -## 6. 使用 exec 执行 - -`execfile` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。 - -但是原理值得借鉴,你可以使用 open ... read 读取文件内容,然后再用 exec 去执行模块。 - -示例如下: - -```python ->>> with open("/usr/lib64/python2.7/os.py", "r") as f: -... exec(f.read()) -... ->>> getcwd() -'/home/wangbm' -``` - - - -## 7. import_from_github_com - -有一个包叫做 **import_from_github_com**,从名字上很容易得知,它是一个可以从 github 下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip 先安装它。 - -```shell -$ python3 -m pip install import_from_github_com -``` - -这个包使用了PEP 302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。 - -pip 要保证是较新版本,如果不是请执行如下命令进行升级。 - -```shell -$ python3 -m pip install --upgrade pip -``` - -确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com - -示例如下 - -```python ->>> from github_com.zzzeek import sqlalchemy -Collecting git+https://github.com/zzzeek/sqlalchemy -Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build -Installing collected packages: SQLAlchemy -Running setup.py install for SQLAlchemy ... done -Successfully installed SQLAlchemy-1.1.0b1.dev0 ->>> locals() -{'__builtins__': , '__spec__': None, -'__package__': None, '__doc__': None, '__name__': '__main__', -'sqlalchemy': , -'__loader__': } ->>> -``` - -看了 import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 pip 来安装那些没有安装的包,然后使用Python的`__import__()`函数来引入新安装的模块。 - - - -## 8. 远程导入模块 - -我在这篇文章里([深入探讨 Python 的 import 机制:实现远程导入模块](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。 - -具体内容非常的多,你可以点击这个[链接](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&chksm=e8866544dff1ec52e01b6c9a982dfa150b8e34ad472acca35201373dc51dadb5a8630870982a&scene=21#wechat_redirect)进行深入学习。 - -示例代码如下: - -```python -# 新建一个 py 文件(my_importer.py),内容如下 -import sys -import importlib -import urllib.request as urllib2 - -class UrlMetaFinder(importlib.abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - - - def find_module(self, fullname, path=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - return loader - except Exception: - return None - -class UrlMetaLoader(importlib.abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def get_data(self): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' - -def install_meta(address): - finder = UrlMetaFinder(address) - sys.meta_path.append(finder) -``` - -并且在远程服务器上开启 http 服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的 python 文件,如果后面导入成功会打印 `ok`。 - -```shell -$ mkdir httpserver && cd httpserver -$ cat>my_info.py>> from my_importer import install_meta ->>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder ->>> import my_info # 打印ok,说明导入成功 -ok ->>> my_info.name # 验证可以取得到变量 -'wangbm' -``` - - - -好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import 这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习` __import__ `以及 importlib 是非常有必要的。 - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_45.rst b/source/c01/c01_45.rst deleted file mode 100644 index 052224e..0000000 --- a/source/c01/c01_45.rst +++ /dev/null @@ -1,309 +0,0 @@ -1.45 Python炫技操作:花式导包的八种方法 -======================================= - -|image0| - -1. 直接 import --------------- - -人尽皆知的方法,直接导入即可 - -.. code:: python - - >>> import os - >>> os.getcwd() - '/home/wangbm' - -与此类似的还有,不再细讲 - -.. code:: python - - import ... - import ... as ... - from ... import ... - from ... import ... as ... - -一般情况下,使用 ``import`` 语句导入模块已经够用的。 - -但是在一些特殊场景中,可能还需要其他的导入方式。 - -下面我会一一地给你介绍。 - -2. 使用 \__import_\_ --------------------- - -``__import__`` 函数可用于导入模块,import 语句也会调用函数。其定义为: - -:: - - __import__(name[, globals[, locals[, fromlist[, level]]]]) - -参数介绍: - -- name (required): 被加载 module 的名称 -- globals (optional): 包含全局变量的字典,该选项很少使用,采用默认值 - global() -- locals (optional): - 包含局部变量的字典,内部标准实现未用到该变量,采用默认值 - local() -- fromlist (Optional): 被导入的 submodule 名称 -- level (Optional): 导入路径选项,Python 2 中默认为 -1,表示同时支持 - absolute import 和 relative import。Python 3 中默认为 0,表示仅支持 - absolute import。如果大于 0,则表示相对导入的父目录的级数,即 1 - 类似于 ‘.’,2 类似于 ‘..’。 - -使用示例如下: - -.. code:: python - - >>> os = __import__('os') - >>> os.getcwd() - '/home/wangbm' - -如果要实现 ``import xx as yy`` 的效果,只要修改左值即可 - -如下示例,等价于 ``import os as myos``\ : - -.. code:: python - - >>> myos = __import__('os') - >>> myos.getcwd() - '/home/wangbm' - -上面说过的 ``__import__`` -是一个内建函数,既然是内建函数的话,那么这个内建函数必将存在于 -``__buildins__`` 中,因此我们还可以这样导入 os 的模块: - -.. code:: python - - >>> __builtins__.__dict__['__import__']('os').getcwd() - '/home/wangbm' - -3. 使用 importlib 模块 ----------------------- - -importlib 是 Python 中的一个标准库,importlib 能提供的功能非常全面。 - -它的简单示例: - -.. code:: python - - >>> import importlib - >>> myos=importlib.import_module("os") - >>> myos.getcwd() - '/home/wangbm' - -如果要实现 ``import xx as yy``\ 效果,可以这样 - -.. code:: python - - >>> import importlib - >>> - >>> myos = importlib.import_module("os") - >>> myos.getcwd() - '/home/wangbm' - -4. 使用 imp 模块 ----------------- - -``imp`` 模块提供了一些 import -语句内部实现的接口。例如模块查找(find_module)、模块加载(load_module)等等(模块的导入过程会包含模块查找、加载、缓存等步骤)。可以用该模块来简单实现内建的 -``__import__`` 函数功能: - -.. code:: python - - >>> import imp - >>> file, pathname, desc = imp.find_module('os') - >>> myos = imp.load_module('sep', file, pathname, desc) - >>> myos - - >>> myos.getcwd() - '/home/wangbm' - -从 python 3 开始,内建的 reload 函数被移到了 imp 模块中。而从 Python 3.4 -开始,imp 模块被否决,不再建议使用,其包含的功能被移到了 importlib -模块下。即从 Python 3.4 开始,importlib 模块是之前 imp 模块和 importlib -模块的合集。 - -5. 使用 execfile ----------------- - -在 Python 2 中有一个 execfile 函数,利用它可以用来执行一个文件。 - -语法如下: - -:: - - execfile(filename[, globals[, locals]]) - -参数有这么几个: - -- filename:文件名。 -- globals:变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。 -- locals:变量作用域,局部命名空间,如果被提供,可以是任何映射对象。 - -.. code:: python - - >>> execfile("/usr/lib64/python2.7/os.py") - >>> - >>> getcwd() - '/home/wangbm' - -6. 使用 exec 执行 ------------------ - -``execfile`` 只能在 Python2 中使用,Python 3.x 里已经删除了这个函数。 - -但是原理值得借鉴,你可以使用 open … read 读取文件内容,然后再用 exec -去执行模块。 - -示例如下: - -.. code:: python - - >>> with open("/usr/lib64/python2.7/os.py", "r") as f: - ... exec(f.read()) - ... - >>> getcwd() - '/home/wangbm' - -7. import_from_github_com -------------------------- - -有一个包叫做 -**import_from_github_com**\ ,从名字上很容易得知,它是一个可以从 github -下载安装并导入的包。为了使用它,你需要做的就是按照如下命令使用pip -先安装它。 - -.. code:: shell - - $ python3 -m pip install import_from_github_com - -这个包使用了PEP -302中新的引入钩子,允许你可以从github上引入包。这个包实际做的就是安装这个包并将它添加到本地。你需要 -Python 3.2 或者更高的版本,并且 git 和 pip 都已经安装才能使用这个包。 - -pip 要保证是较新版本,如果不是请执行如下命令进行升级。 - -.. code:: shell - - $ python3 -m pip install --upgrade pip - -确保环境 ok 后,你就可以在 Python shell 中使用 import_from_github_com - -示例如下 - -.. code:: python - - >>> from github_com.zzzeek import sqlalchemy - Collecting git+https://github.com/zzzeek/sqlalchemy - Cloning https://github.com/zzzeek/sqlalchemy to /tmp/pip-acfv7t06-build - Installing collected packages: SQLAlchemy - Running setup.py install for SQLAlchemy ... done - Successfully installed SQLAlchemy-1.1.0b1.dev0 - >>> locals() - {'__builtins__': , '__spec__': None, - '__package__': None, '__doc__': None, '__name__': '__main__', - 'sqlalchemy': , - '__loader__': } - >>> - -看了 -import_from_github_com的源码后,你会注意到它并没有使用importlib。实际上,它的原理就是使用 -pip -来安装那些没有安装的包,然后使用Python的\ ``__import__()``\ 函数来引入新安装的模块。 - -8. 远程导入模块 ---------------- - -我在这篇文章里(\ `深入探讨 Python 的 import -机制:实现远程导入模块 `__\ ),深入剖析了导入模块的内部原理,并在最后手动实现了从远程服务器上读取模块内容,并在本地成功将模块导入的导入器。 - -具体内容非常的多,你可以点击这个\ `链接 `__\ 进行深入学习。 - -示例代码如下: - -.. code:: python - - # 新建一个 py 文件(my_importer.py),内容如下 - import sys - import importlib - import urllib.request as urllib2 - - class UrlMetaFinder(importlib.abc.MetaPathFinder): - def __init__(self, baseurl): - self._baseurl = baseurl - - - def find_module(self, fullname, path=None): - if path is None: - baseurl = self._baseurl - else: - # 不是原定义的url就直接返回不存在 - if not path.startswith(self._baseurl): - return None - baseurl = path - - try: - loader = UrlMetaLoader(baseurl) - return loader - except Exception: - return None - - class UrlMetaLoader(importlib.abc.SourceLoader): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_code(self, fullname): - f = urllib2.urlopen(self.get_filename(fullname)) - return f.read() - - def get_data(self): - pass - - def get_filename(self, fullname): - return self.baseurl + fullname + '.py' - - def install_meta(address): - finder = UrlMetaFinder(address) - sys.meta_path.append(finder) - -并且在远程服务器上开启 http -服务(为了方便,我仅在本地进行演示),并且手动编辑一个名为 my_info 的 -python 文件,如果后面导入成功会打印 ``ok``\ 。 - -.. code:: shell - - $ mkdir httpserver && cd httpserver - $ cat>my_info.py>> from my_importer import install_meta - >>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder - >>> import my_info # 打印ok,说明导入成功 - ok - >>> my_info.name # 验证可以取得到变量 - 'wangbm' - -好了,8 种方法都给大家介绍完毕,对于普通开发者来说,其实只要掌握 import -这种方法足够了,而对于那些想要自己开发框架的人来说,深入学习\ ``__import__``\ 以及 -importlib 是非常有必要的。 - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c01/c01_47.md b/source/c01/c01_47.md deleted file mode 100644 index 3b8caf0..0000000 --- a/source/c01/c01_47.md +++ /dev/null @@ -1,185 +0,0 @@ -# 1.47 少有人知的 Python "重试机制" - -为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 - -这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 - -这里要给大家介绍的是一个第三方库 - `Tenacity` ,它实现了几乎我们可以使用到的所有重试场景,比如: - -1. 在什么情况下才进行重试? -2. 重试几次呢? -3. 重试多久后结束? -4. 每次重试的间隔多长呢? -5. 重试失败后的回调? - -在使用它之前 ,先要安装它 - -```shell -$ pip install tenacity -``` - - - -### **最基本的重试** - -无条件重试,重试之间无间隔 - -```python -from tenacity import retry - -@retry -def test_retry(): - print("等待重试,重试无间隔执行...") - raise Exception - -test_retry() -``` - -无条件重试,但是在重试之前要等待 2 秒 - -```python -from tenacity import retry, wait_fixed - -@retry(wait=wait_fixed(2)) -def test_retry(): - print("等待重试...") - raise Exception - -test_retry() -``` - - - -### 设置停止基本条件 - -只重试7 次 - -```python -from tenacity import retry, stop_after_attempt - -@retry(stop=stop_after_attempt(7)) -def test_retry(): - print("等待重试...") - raise Exception - -test_retry() -``` - -重试 10 秒后不再重试 - -```python -from tenacity import retry, stop_after_delay - -@retry(stop=stop_after_delay(10)) -def test_retry(): - print("等待重试...") - raise Exception - -test_retry() -``` - -或者上面两个条件满足一个就结束重试 - -```python -from tenacity import retry, stop_after_delay, stop_after_attempt - -@retry(stop=(stop_after_delay(10) | stop_after_attempt(7))) -def test_retry(): - print("等待重试...") - raise Exception - -test_retry() -``` - -### 设置何时进行重试 - -在出现特定错误/异常(比如请求超时)的情况下,再进行重试 - -```python -from requests import exceptions -from tenacity import retry, retry_if_exception_type - -@retry(retry=retry_if_exception_type(exceptions.Timeout)) -def test_retry(): - print("等待重试...") - raise exceptions.Timeout - -test_retry() -``` - -在满足自定义条件时,再进行重试。 - -如下示例,当 `test_retry` 函数返回值为 False 时,再进行重试 - -```python -from tenacity import retry, stop_after_attempt, retry_if_result - -def is_false(value): - return value is False - -@retry(stop=stop_after_attempt(3), - retry=retry_if_result(is_false)) -def test_retry(): - return False - -test_retry() -``` - - - -### 重试后错误重新抛出 - -当出现异常后,tenacity 会进行重试,若重试后还是失败,默认情况下,往上抛出的异常会变成 RetryError,而不是最根本的原因。 - -因此可以加一个参数(`reraise=True`),使得当重试失败后,往外抛出的异常还是原来的那个。 - -```python -from tenacity import retry, stop_after_attempt - -@retry(stop=stop_after_attempt(7), reraise=True) -def test_retry(): - print("等待重试...") - raise Exception - -test_retry() -``` - - - -### 设置回调函数 - -当最后一次重试失败后,可以执行一个回调函数 - -```python -from tenacity import * - -def return_last_value(retry_state): - print("执行回调函数") - return retry_state.outcome.result() # 表示返回原函数的返回值 - -def is_false(value): - return value is False - -@retry(stop=stop_after_attempt(3), - retry_error_callback=return_last_value, - retry=retry_if_result(is_false)) -def test_retry(): - print("等待重试中...") - return False - -print(test_retry()) -``` - -输出如下 - -```shell -等待重试中... -等待重试中... -等待重试中... -执行回调函数 -False -``` - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_47.rst b/source/c01/c01_47.rst deleted file mode 100644 index 234bde4..0000000 --- a/source/c01/c01_47.rst +++ /dev/null @@ -1,187 +0,0 @@ -1.47 少有人知的 Python “重试机制” -================================= - -为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 - -这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 - -这里要给大家介绍的是一个第三方库 - ``Tenacity`` -,它实现了几乎我们可以使用到的所有重试场景,比如: - -1. 在什么情况下才进行重试? -2. 重试几次呢? -3. 重试多久后结束? -4. 每次重试的间隔多长呢? -5. 重试失败后的回调? - -在使用它之前 ,先要安装它 - -.. code:: shell - - $ pip install tenacity - -**最基本的重试** -~~~~~~~~~~~~~~~~ - -无条件重试,重试之间无间隔 - -.. code:: python - - from tenacity import retry - - @retry - def test_retry(): - print("等待重试,重试无间隔执行...") - raise Exception - - test_retry() - -无条件重试,但是在重试之前要等待 2 秒 - -.. code:: python - - from tenacity import retry, wait_fixed - - @retry(wait=wait_fixed(2)) - def test_retry(): - print("等待重试...") - raise Exception - - test_retry() - -设置停止基本条件 -~~~~~~~~~~~~~~~~ - -只重试7 次 - -.. code:: python - - from tenacity import retry, stop_after_attempt - - @retry(stop=stop_after_attempt(7)) - def test_retry(): - print("等待重试...") - raise Exception - - test_retry() - -重试 10 秒后不再重试 - -.. code:: python - - from tenacity import retry, stop_after_delay - - @retry(stop=stop_after_delay(10)) - def test_retry(): - print("等待重试...") - raise Exception - - test_retry() - -或者上面两个条件满足一个就结束重试 - -.. code:: python - - from tenacity import retry, stop_after_delay, stop_after_attempt - - @retry(stop=(stop_after_delay(10) | stop_after_attempt(7))) - def test_retry(): - print("等待重试...") - raise Exception - - test_retry() - -设置何时进行重试 -~~~~~~~~~~~~~~~~ - -在出现特定错误/异常(比如请求超时)的情况下,再进行重试 - -.. code:: python - - from requests import exceptions - from tenacity import retry, retry_if_exception_type - - @retry(retry=retry_if_exception_type(exceptions.Timeout)) - def test_retry(): - print("等待重试...") - raise exceptions.Timeout - - test_retry() - -在满足自定义条件时,再进行重试。 - -如下示例,当 ``test_retry`` 函数返回值为 False 时,再进行重试 - -.. code:: python - - from tenacity import retry, stop_after_attempt, retry_if_result - - def is_false(value): - return value is False - - @retry(stop=stop_after_attempt(3), - retry=retry_if_result(is_false)) - def test_retry(): - return False - - test_retry() - -重试后错误重新抛出 -~~~~~~~~~~~~~~~~~~ - -当出现异常后,tenacity -会进行重试,若重试后还是失败,默认情况下,往上抛出的异常会变成 -RetryError,而不是最根本的原因。 - -因此可以加一个参数(\ ``reraise=True``\ ),使得当重试失败后,往外抛出的异常还是原来的那个。 - -.. code:: python - - from tenacity import retry, stop_after_attempt - - @retry(stop=stop_after_attempt(7), reraise=True) - def test_retry(): - print("等待重试...") - raise Exception - - test_retry() - -设置回调函数 -~~~~~~~~~~~~ - -当最后一次重试失败后,可以执行一个回调函数 - -.. code:: python - - from tenacity import * - - def return_last_value(retry_state): - print("执行回调函数") - return retry_state.outcome.result() # 表示返回原函数的返回值 - - def is_false(value): - return value is False - - @retry(stop=stop_after_attempt(3), - retry_error_callback=return_last_value, - retry=retry_if_result(is_false)) - def test_retry(): - print("等待重试中...") - return False - - print(test_retry()) - -输出如下 - -.. code:: shell - - 等待重试中... - 等待重试中... - 等待重试中... - 执行回调函数 - False - -|image0| - -.. |image0| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c03/c03_08.md b/source/c03/c03_08.md new file mode 100644 index 0000000..29b2687 --- /dev/null +++ b/source/c03/c03_08.md @@ -0,0 +1,597 @@ +# 3.8 深入理解 Python 中的描述符 + +![](http://image.iswbm.com/20200602135014.png) + +学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, Descriptor(描述符)特性可以排得上号。 + +描述符 是Python 语言独有的特性,它不仅在应用层使用,在语言语法糖的实现上也有使用到(在下面的文章会一一介绍)。 + +当你点进这篇文章时 + +- 你也许没学过描述符,甚至没听过描述符。 +- 或者你对描述符只是一知半解 + +无论你是哪种,本篇都将带你全面的学习描述符,一起来感受 Python 语言的优雅。 + +### 1. 为什么要使用描述符? + +假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。 + +```python +class Student: + def __init__(self, name, math, chinese, english): + self.name = name + self.math = math + self.chinese = chinese + self.english = english + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) +``` + +看起来一切都很合理 + +```python +>>> std1 = Student('小明', 76, 87, 68) +>>> std1 + +``` + +但是程序并不像人那么智能,不会自动根据使用场景判断数据的合法性,如果老师在录入成绩的时候,不小心录入了将成绩录成了负数,或者超过100,程序是无法感知的。 + +聪明的你,马上在代码中加入了判断逻辑。 + +```python +class Student: + def __init__(self, name, math, chinese, english): + self.name = name + if 0 <= math <= 100: + self.math = math + else: + raise ValueError("Valid value must be in [0, 100]") + + if 0 <= chinese <= 100: + self.chinese = chinese + else: + raise ValueError("Valid value must be in [0, 100]") + + if 0 <= chinese <= 100: + self.english = english + else: + raise ValueError("Valid value must be in [0, 100]") + + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) +``` + +这下程序稍微有点人工智能了,能够自己明辨是非了。 + +![](http://image.iswbm.com/20190425221322.png) + +程序是智能了,但在`__init__`里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过 Property 特性,可以很好的应用在这里。于是你将代码修改成如下,代码的可读性瞬间提升了不少 + +```python +class Student: + def __init__(self, name, math, chinese, english): + self.name = name + self.math = math + self.chinese = chinese + self.english = english + + @property + def math(self): + return self._math + + @math.setter + def math(self, value): + if 0 <= value <= 100: + self._math = value + else: + raise ValueError("Valid value must be in [0, 100]") + + @property + def chinese(self): + return self._chinese + + @chinese.setter + def chinese(self, value): + if 0 <= value <= 100: + self._chinese = value + else: + raise ValueError("Valid value must be in [0, 100]") + + @property + def english(self): + return self._english + + @english.setter + def english(self, value): + if 0 <= value <= 100: + self._english = value + else: + raise ValueError("Valid value must be in [0, 100]") + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) +``` + +程序还是一样的人工智能,非常好。 + +![](http://image.iswbm.com/20190425221322.png) + +你以为你写的代码,已经非常优秀,无懈可击了。 + + + +没想到,人外有天,你的主管看了你的代码后,深深地叹了口气:类里的三个属性,math、chinese、english,都使用了 Property 对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100 就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢,这代码简直没法忍。去了解一下 Python 的描述符吧。 + +经过主管的指点,你知道了「描述符」这个东西。怀着一颗敬畏之心,你去搜索了下关于 描述符的用法。 + +其实也很简单,一个实现了 `描述符协议` 的类就是一个描述符。 + +什么描述符协议:在类里实现了 `__get__()`、`__set__()`、`__delete__()` 其中至少一个方法。 + +- `__get__`: 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。 +- `__set__ `:将在属性分配操作中调用。不会返回任何内容。 +- `__delete__ `:控制删除操作。不会返回内容。 + +对描述符有了大概的了解后,你开始重写上面的方法。 + +如前所述,Score 类是一个描述符,当从 Student 的实例访问 math、chinese、english这三个属性的时候,都会经过 Score 类里的三个特殊的方法。这里的 Score 避免了 使用Property 出现大量的代码无法复用的尴尬。 + +```python +class Score: + def __init__(self, default=0): + self._score = default + + def __set__(self, instance, value): + if not isinstance(value, int): + raise TypeError('Score must be integer') + if not 0 <= value <= 100: + raise ValueError('Valid value must be in [0, 100]') + + self._score = value + + def __get__(self, instance, owner): + return self._score + + def __delete__(self): + del self._score + +class Student: + math = Score(0) + chinese = Score(0) + english = Score(0) + + def __init__(self, name, math, chinese, english): + self.name = name + self.math = math + self.chinese = chinese + self.english = english + + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) +``` + +实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等) + +![](http://image.iswbm.com/20190425221233.png) + +以上,我举了下具体的实例,从最原始的编码风格到 Property ,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 + +到这里,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 `保护属性不受修改`、`属性类型检查` 的基本功能,同时有大大提高代码的复用率。 + + + +### 2. 描述符的访问规则 + +描述符分两种: + +- 数据描述符:实现了`__get__` 和 `__set__` 两种方法的描述符 +- 非数据描述符:只实现了`__get__` 一种方法的描述符 + +你一定会问,他们有什么区别呢?网上的讲解,我看过几个,很多都把一个简单的东西讲得复杂了。 + +其实就一句话,**数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**。 + +如果实例字典中有与描述符同名的属性,如果描述符是数据描述符,优先使用数据描述符,如果是非数据描述符,优先使用字典中的属性。 + +这边还是以上节的成绩管理的例子来说明,方便你理解。 + +```python +# 数据描述符 +class DataDes: + def __init__(self, default=0): + self._score = default + + def __set__(self, instance, value): + self._score = value + + def __get__(self, instance, owner): + print("访问数据描述符里的 __get__") + return self._score + +# 非数据描述符 +class NoDataDes: + def __init__(self, default=0): + self._score = default + + def __get__(self, instance, owner): + print("访问非数据描述符里的 __get__") + return self._score + + +class Student: + math = DataDes(0) + chinese = NoDataDes(0) + + def __init__(self, name, math, chinese): + self.name = name + self.math = math + self.chinese = chinese + + def __getattribute__(self, item): + print("调用 __getattribute__") + return super(Student, self).__getattribute__(item) + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese) +``` + +需要注意的是,math 是数据描述符,而 chinese 是非数据描述符。从下面的验证中,可以看出,当实例属性和数据描述符同名时,会优先访问数据描述符(如下面的math),而当实例属性和非数据描述符同名时,会优先访问实例属性(`__getattribute__`) + +```python +>>> std = Student('xm', 88, 99) +>>> +>>> std.math +调用 __getattribute__ +访问数据描述符里的 __get__ +88 +>>> std.chinese +调用 __getattribute__ +99 +``` + +讲完了数据描述符和非数据描述符,我们还需要了解的对象属性的查找规律。 + +当我们对一个实例属性进行访问时,Python 会按 `obj.__dict__` → `type(obj).__dict__` → `type(obj)的父类.__dict__` 顺序进行查找,如果查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。 + +### 3. 基于描述符如何实现property + +经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。 + +正常人所见过的描述符的用法就是上面提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Python 的特性的底层实现机制都是基于 `描述符协议` 的,比如我们熟悉的`@property` 、`@classmethod` 、`@staticmethod` 和 `super` 等。 + +先来说说 `property` 吧。 + +有了前面的基础,我们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。 + +```python +class Student: + def __init__(self, name): + self.name = name + + @property + def math(self): + return self._math + + @math.setter + def math(self, value): + if 0 <= value <= 100: + self._math = value + else: + raise ValueError("Valid value must be in [0, 100]") +``` + +不防再简单回顾一下它的用法,通过property装饰的函数,如例子中的 math 会变成 Student 实例的属性。而对 math 属性赋值会进入 使用 `math.setter` 装饰函数的逻辑代码块。 + +为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入 property 的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。 + +不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。 + +这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类 `property` 特性。 + +代码如下: + +```python +class TestProperty(object): + + def __init__(self, fget=None, fset=None, fdel=None, doc=None): + self.fget = fget + self.fset = fset + self.fdel = fdel + self.__doc__ = doc + + def __get__(self, obj, objtype=None): + print("in __get__") + if obj is None: + return self + if self.fget is None: + raise AttributeError + return self.fget(obj) + + def __set__(self, obj, value): + print("in __set__") + if self.fset is None: + raise AttributeError + self.fset(obj, value) + + def __delete__(self, obj): + print("in __delete__") + if self.fdel is None: + raise AttributeError + self.fdel(obj) + + + def getter(self, fget): + print("in getter") + return type(self)(fget, self.fset, self.fdel, self.__doc__) + + def setter(self, fset): + print("in setter") + return type(self)(self.fget, fset, self.fdel, self.__doc__) + + def deleter(self, fdel): + print("in deleter") + return type(self)(self.fget, self.fset, fdel, self.__doc__) +``` + +然后 Student 类,我们也相应改成如下 + +```python +class Student: + def __init__(self, name): + self.name = name + + # 其实只有这里改变 + @TestProperty + def math(self): + return self._math + + @math.setter + def math(self, value): + if 0 <= value <= 100: + self._math = value + else: + raise ValueError("Valid value must be in [0, 100]") +``` + + + +为了尽量让你少产生一点疑惑,我这里做两点说明: + +1. 使用`TestProperty`装饰后,`math` 不再是一个函数,而是`TestProperty` 类的一个实例。所以第二个math函数可以使用 `math.setter` 来装饰,本质是调用`TestProperty.setter` 来产生一个新的 `TestProperty` 实例赋值给第二个`math`。 + +2. 第一个 `math` 和第二个 `math` 是两个不同 `TestProperty` 实例。但他们都属于同一个描述符类(TestProperty),当对 math 对于赋值时,就会进入 `TestProperty.__set__`,当对math 进行取值里,就会进入 `TestProperty.__get__`。仔细一看,其实最终访问的还是Student实例的 `_math` 属性。 + +说了这么多,还是运行一下,更加直观一点。 + +```python +# 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math +in setter +>>> +>>> s1.math = 90 +in __set__ +>>> s1.math +in __get__ +90 +``` + +对于以上理解 `property` 的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。 + +### 4. 基于描述符如何实现staticmethod + +说完了 `property` ,这里再来讲讲 `@classmethod` 和 `@staticmethod` 的实现原理。 + +我这里定义了一个类,用了两种方式来实现静态方法。 + +```python +class Test: + @staticmethod + def myfunc(): + print("hello") + +# 上下两种写法等价 + +class Test: + def myfunc(): + print("hello") + # 重点:这就是描述符的体现 + myfunc = staticmethod(myfunc) +``` + +这两种写法是等价的,就好像在 `property` 一样,其实以下两种写法也是等价的。 + +```python +@TestProperty +def math(self): + return self._math + +math = TestProperty(fget=math) +``` + +话题还是转回到 `staticmethod` 这边来吧。 + +由上面的注释,可以看出 `staticmethod` 其实就相当于一个描述符类,而`myfunc` 在此刻变成了一个描述符。关于 `staticmethod` 的实现,你可以参照下面这段我自己写的代码,加以理解。 + +![](http://image.iswbm.com/20190519001930.png) + +调用这个方法可以知道,每调用一次,它都会经过描述符类的 `__get__` 。 + +```python +>>> Test.myfunc() +in staticmethod __get__ +hello +>>> Test().myfunc() +in staticmethod __get__ +hello +``` + +### 5. 基于描述符如何实现classmethod + +同样的 ` classmethod` 也是一样。 + +```python +class classmethod(object): + def __init__(self, f): + self.f = f + + def __get__(self, instance, owner=None): + print("in classmethod __get__") + + def newfunc(*args): + return self.f(owner, *args) + return newfunc + +class Test: + def myfunc(cls): + print("hello") + + # 重点:这就是描述符的体现 + myfunc = classmethod(myfunc) +``` + +验证结果如下 + +```python +>>> Test.myfunc() +in classmethod __get__ +hello +>>> Test().myfunc() +in classmethod __get__ +hello +``` + +讲完了 `property`、`staticmethod`和`classmethod` 与 描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 super 的实现原理,就交由你来自己完成。 + +### 6. 所有实例共享描述符 + +通过以上内容的学习,你是不是觉得自己已经对描述符足够了解了呢? + +可在这里,我想说以上的描述符代码都有问题。 + +问题在哪里呢?请看下面这个例子。 + +```python +class Score: + def __init__(self, default=0): + self._value = default + + def __get__(self, instance, owner): + return self._value + + def __set__(self, instance, value): + if 0 <= value <= 100: + self._value = value + else: + raise ValueError + + +class Student: + math = Score(0) + chinese = Score(0) + english = Score(0) + + def __repr__(self): + return "".format(self.math, self.chinese, self.english) +``` + +Student 里没有像前面那样写了构造函数,但是关键不在这儿,没写只是因为没必要写。 + +然后来看一下会出现什么样的问题呢 + +```python +>>> std1 = Student() +>>> std1 + +>>> std1.math = 85 +>>> std1 + +>>> std2 = Student() +>>> std2 # std2 居然共享了std1 的属性值 + +>>> std2.math = 100 +>>> std1 # std2 也会改变std1 的属性值 + +``` + +从结果上来看,std2 居然共享了 std1 的属性值,只要其中一个实例的变量发生改变,另一个实例的变量也会跟着改变。 + +探其根因,是由于此时 math,chinese,english 三个全部是类变量,导致 std2 和 std1 在访问 math,chinese,english 这三个变量时,其实都是访问类变量。 + +问题是不是来了?小明和小强的分数怎么可能是绑定的呢?这很明显与实际业务不符。 + +使用描述符给我们制造了便利,却无形中给我们带来了麻烦,难道这也是描述符的特性吗? + +描述符是个很好用的特性,会出现这个问题,是由于我们之前写的描述符代码都是错误的。 + +描述符的机制,在我看来,只是抢占了访问顺序,而具体的逻辑却要因地制宜,视情况而定。 + +如果要把 math,chinese,english 这三个变量变成实例之间相互隔离的属性,应该这么写。 + +```python +class Score: + def __init__(self, subject): + self.name = subject + + def __get__(self, instance, owner): + return instance.__dict__[self.name] + + def __set__(self, instance, value): + if 0 <= value <= 100: + instance.__dict__[self.name] = value + else: + raise ValueError + + +class Student: + math = Score("math") + chinese = Score("chinese") + english = Score("english") + + def __init__(self, math, chinese, english): + self.math = math + self.chinese = chinese + self.english = english + + def __repr__(self): + return "".format(self.math, self.chinese, self.english) +``` + +引导程序逻辑进入描述符之后,不管你是获取属性,还是设置属性,都是直接作用于 instance 的。 + +![](http://image.iswbm.com/20200812085823.png) + +这段代码,你可以仔细和前面的对比一下。 + +不难看出: + +- 之前的错误代码,更像是把描述符当做了存储节点。 +- 之后的正确代码,则是把描述符直接当做代理,本身不存储值。 + +以上便是我对描述符的全部分享,希望能对你有所帮助。 + + + +## 参考文档 + +- [Python描述器引导(翻译)](https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html#python) + + + + + +--- + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c03/c03_08.rst b/source/c03/c03_08.rst new file mode 100644 index 0000000..2441ad2 --- /dev/null +++ b/source/c03/c03_08.rst @@ -0,0 +1,655 @@ +3.8 深入理解 Python 中的描述符 +============================== + +|image0| + +学习 Python 这么久了,说起 Python 的优雅之处,能让我脱口而出的, +Descriptor(描述符)特性可以排得上号。 + +描述符 是Python +语言独有的特性,它不仅在应用层使用,在语言语法糖的实现上也有使用到(在下面的文章会一一介绍)。 + +当你点进这篇文章时 + +- 你也许没学过描述符,甚至没听过描述符。 +- 或者你对描述符只是一知半解 + +无论你是哪种,本篇都将带你全面的学习描述符,一起来感受 Python +语言的优雅。 + +1. 为什么要使用描述符? +~~~~~~~~~~~~~~~~~~~~~~~ + +假想你正在给学校写一个成绩管理系统,并没有太多编码经验的你,可能会这样子写。 + +.. code:: python + + class Student: + def __init__(self, name, math, chinese, english): + self.name = name + self.math = math + self.chinese = chinese + self.english = english + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) + +看起来一切都很合理 + +.. code:: python + + >>> std1 = Student('小明', 76, 87, 68) + >>> std1 + + +但是程序并不像人那么智能,不会自动根据使用场景判断数据的合法性,如果老师在录入成绩的时候,不小心录入了将成绩录成了负数,或者超过100,程序是无法感知的。 + +聪明的你,马上在代码中加入了判断逻辑。 + +.. code:: python + + class Student: + def __init__(self, name, math, chinese, english): + self.name = name + if 0 <= math <= 100: + self.math = math + else: + raise ValueError("Valid value must be in [0, 100]") + + if 0 <= chinese <= 100: + self.chinese = chinese + else: + raise ValueError("Valid value must be in [0, 100]") + + if 0 <= chinese <= 100: + self.english = english + else: + raise ValueError("Valid value must be in [0, 100]") + + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) + +这下程序稍微有点人工智能了,能够自己明辨是非了。 + +|image1| + +程序是智能了,但在\ ``__init__``\ 里有太多的判断逻辑,很影响代码的可读性。巧的是,你刚好学过 +Property +特性,可以很好的应用在这里。于是你将代码修改成如下,代码的可读性瞬间提升了不少 + +.. code:: python + + class Student: + def __init__(self, name, math, chinese, english): + self.name = name + self.math = math + self.chinese = chinese + self.english = english + + @property + def math(self): + return self._math + + @math.setter + def math(self, value): + if 0 <= value <= 100: + self._math = value + else: + raise ValueError("Valid value must be in [0, 100]") + + @property + def chinese(self): + return self._chinese + + @chinese.setter + def chinese(self, value): + if 0 <= value <= 100: + self._chinese = value + else: + raise ValueError("Valid value must be in [0, 100]") + + @property + def english(self): + return self._english + + @english.setter + def english(self, value): + if 0 <= value <= 100: + self._english = value + else: + raise ValueError("Valid value must be in [0, 100]") + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) + +程序还是一样的人工智能,非常好。 + +|image2| + +你以为你写的代码,已经非常优秀,无懈可击了。 + +没想到,人外有天,你的主管看了你的代码后,深深地叹了口气:类里的三个属性,math、chinese、english,都使用了 +Property +对属性的合法性进行了有效控制。功能上,没有问题,但就是太啰嗦了,三个变量的合法性逻辑都是一样的,只要大于0,小于100 +就可以,代码重复率太高了,这里三个成绩还好,但假设还有地理、生物、历史、化学等十几门的成绩呢,这代码简直没法忍。去了解一下 +Python 的描述符吧。 + +经过主管的指点,你知道了「描述符」这个东西。怀着一颗敬畏之心,你去搜索了下关于 +描述符的用法。 + +其实也很简单,一个实现了 ``描述符协议`` 的类就是一个描述符。 + +什么描述符协议:在类里实现了 +``__get__()``\ 、\ ``__set__()``\ 、\ ``__delete__()`` +其中至少一个方法。 + +- ``__get__``\ : + 用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。 +- ``__set__``\ :将在属性分配操作中调用。不会返回任何内容。 +- ``__delete__``\ :控制删除操作。不会返回内容。 + +对描述符有了大概的了解后,你开始重写上面的方法。 + +如前所述,Score 类是一个描述符,当从 Student 的实例访问 +math、chinese、english这三个属性的时候,都会经过 Score +类里的三个特殊的方法。这里的 Score 避免了 使用Property +出现大量的代码无法复用的尴尬。 + +.. code:: python + + class Score: + def __init__(self, default=0): + self._score = default + + def __set__(self, instance, value): + if not isinstance(value, int): + raise TypeError('Score must be integer') + if not 0 <= value <= 100: + raise ValueError('Valid value must be in [0, 100]') + + self._score = value + + def __get__(self, instance, owner): + return self._score + + def __delete__(self): + del self._score + + class Student: + math = Score(0) + chinese = Score(0) + english = Score(0) + + def __init__(self, name, math, chinese, english): + self.name = name + self.math = math + self.chinese = chinese + self.english = english + + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese, self.english + ) + +实现的效果和前面的一样,可以对数据的合法性进行有效控制(字段类型、数值区间等) + +|image3| + +以上,我举了下具体的实例,从最原始的编码风格到 Property +,最后引出描述符。由浅入深,一步一步带你感受到描述符的优雅之处。 + +到这里,你需要记住的只有一点,就是描述符给我们带来的编码上的便利,它在实现 +``保护属性不受修改``\ 、\ ``属性类型检查`` +的基本功能,同时有大大提高代码的复用率。 + +2. 描述符的访问规则 +~~~~~~~~~~~~~~~~~~~ + +描述符分两种: + +- 数据描述符:实现了\ ``__get__`` 和 ``__set__`` 两种方法的描述符 +- 非数据描述符:只实现了\ ``__get__`` 一种方法的描述符 + +你一定会问,他们有什么区别呢?网上的讲解,我看过几个,很多都把一个简单的东西讲得复杂了。 + +其实就一句话,\ **数据描述器和非数据描述器的区别在于:它们相对于实例的字典的优先级不同**\ 。 + +如果实例字典中有与描述符同名的属性,如果描述符是数据描述符,优先使用数据描述符,如果是非数据描述符,优先使用字典中的属性。 + +这边还是以上节的成绩管理的例子来说明,方便你理解。 + +.. code:: python + + # 数据描述符 + class DataDes: + def __init__(self, default=0): + self._score = default + + def __set__(self, instance, value): + self._score = value + + def __get__(self, instance, owner): + print("访问数据描述符里的 __get__") + return self._score + + # 非数据描述符 + class NoDataDes: + def __init__(self, default=0): + self._score = default + + def __get__(self, instance, owner): + print("访问非数据描述符里的 __get__") + return self._score + + + class Student: + math = DataDes(0) + chinese = NoDataDes(0) + + def __init__(self, name, math, chinese): + self.name = name + self.math = math + self.chinese = chinese + + def __getattribute__(self, item): + print("调用 __getattribute__") + return super(Student, self).__getattribute__(item) + + def __repr__(self): + return "".format( + self.name, self.math, self.chinese) + +需要注意的是,math 是数据描述符,而 chinese +是非数据描述符。从下面的验证中,可以看出,当实例属性和数据描述符同名时,会优先访问数据描述符(如下面的math),而当实例属性和非数据描述符同名时,会优先访问实例属性(\ ``__getattribute__``\ ) + +.. code:: python + + >>> std = Student('xm', 88, 99) + >>> + >>> std.math + 调用 __getattribute__ + 访问数据描述符里的 __get__ + 88 + >>> std.chinese + 调用 __getattribute__ + 99 + +讲完了数据描述符和非数据描述符,我们还需要了解的对象属性的查找规律。 + +当我们对一个实例属性进行访问时,Python 会按 ``obj.__dict__`` → +``type(obj).__dict__`` → ``type(obj)的父类.__dict__`` +顺序进行查找,如果查找到目标属性并发现是一个描述符,Python +会调用描述符协议来改变默认的控制行为。 + +3. 基于描述符如何实现property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。 + +正常人所见过的描述符的用法就是上面提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 +Python 的特性的底层实现机制都是基于 ``描述符协议`` +的,比如我们熟悉的\ ``@property`` 、\ ``@classmethod`` +、\ ``@staticmethod`` 和 ``super`` 等。 + +先来说说 ``property`` 吧。 + +有了前面的基础,我们知道了 property +的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。 + +.. code:: python + + class Student: + def __init__(self, name): + self.name = name + + @property + def math(self): + return self._math + + @math.setter + def math(self, value): + if 0 <= value <= 100: + self._math = value + else: + raise ValueError("Valid value must be in [0, 100]") + +不防再简单回顾一下它的用法,通过property装饰的函数,如例子中的 math +会变成 Student 实例的属性。而对 math 属性赋值会进入 使用 ``math.setter`` +装饰函数的逻辑代码块。 + +为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入 +property +的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。 + +不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。 + +这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类 +``property`` 特性。 + +代码如下: + +.. code:: python + + class TestProperty(object): + + def __init__(self, fget=None, fset=None, fdel=None, doc=None): + self.fget = fget + self.fset = fset + self.fdel = fdel + self.__doc__ = doc + + def __get__(self, obj, objtype=None): + print("in __get__") + if obj is None: + return self + if self.fget is None: + raise AttributeError + return self.fget(obj) + + def __set__(self, obj, value): + print("in __set__") + if self.fset is None: + raise AttributeError + self.fset(obj, value) + + def __delete__(self, obj): + print("in __delete__") + if self.fdel is None: + raise AttributeError + self.fdel(obj) + + + def getter(self, fget): + print("in getter") + return type(self)(fget, self.fset, self.fdel, self.__doc__) + + def setter(self, fset): + print("in setter") + return type(self)(self.fget, fset, self.fdel, self.__doc__) + + def deleter(self, fdel): + print("in deleter") + return type(self)(self.fget, self.fset, fdel, self.__doc__) + +然后 Student 类,我们也相应改成如下 + +.. code:: python + + class Student: + def __init__(self, name): + self.name = name + + # 其实只有这里改变 + @TestProperty + def math(self): + return self._math + + @math.setter + def math(self, value): + if 0 <= value <= 100: + self._math = value + else: + raise ValueError("Valid value must be in [0, 100]") + +为了尽量让你少产生一点疑惑,我这里做两点说明: + +1. 使用\ ``TestProperty``\ 装饰后,\ ``math`` + 不再是一个函数,而是\ ``TestProperty`` + 类的一个实例。所以第二个math函数可以使用 ``math.setter`` + 来装饰,本质是调用\ ``TestProperty.setter`` 来产生一个新的 + ``TestProperty`` 实例赋值给第二个\ ``math``\ 。 + +2. 第一个 ``math`` 和第二个 ``math`` 是两个不同 ``TestProperty`` + 实例。但他们都属于同一个描述符类(TestProperty),当对 math + 对于赋值时,就会进入 ``TestProperty.__set__``\ ,当对math + 进行取值里,就会进入 + ``TestProperty.__get__``\ 。仔细一看,其实最终访问的还是Student实例的 + ``_math`` 属性。 + +说了这么多,还是运行一下,更加直观一点。 + +.. code:: python + + # 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math + in setter + >>> + >>> s1.math = 90 + in __set__ + >>> s1.math + in __get__ + 90 + +对于以上理解 ``property`` +的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。 + +4. 基于描述符如何实现staticmethod +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +说完了 ``property`` ,这里再来讲讲 ``@classmethod`` 和 ``@staticmethod`` +的实现原理。 + +我这里定义了一个类,用了两种方式来实现静态方法。 + +.. code:: python + + class Test: + @staticmethod + def myfunc(): + print("hello") + + # 上下两种写法等价 + + class Test: + def myfunc(): + print("hello") + # 重点:这就是描述符的体现 + myfunc = staticmethod(myfunc) + +这两种写法是等价的,就好像在 ``property`` +一样,其实以下两种写法也是等价的。 + +.. code:: python + + @TestProperty + def math(self): + return self._math + + math = TestProperty(fget=math) + +话题还是转回到 ``staticmethod`` 这边来吧。 + +由上面的注释,可以看出 ``staticmethod`` +其实就相当于一个描述符类,而\ ``myfunc`` 在此刻变成了一个描述符。关于 +``staticmethod`` 的实现,你可以参照下面这段我自己写的代码,加以理解。 + +|image4| + +调用这个方法可以知道,每调用一次,它都会经过描述符类的 ``__get__`` 。 + +.. code:: python + + >>> Test.myfunc() + in staticmethod __get__ + hello + >>> Test().myfunc() + in staticmethod __get__ + hello + +5. 基于描述符如何实现classmethod +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +同样的 ``classmethod`` 也是一样。 + +.. code:: python + + class classmethod(object): + def __init__(self, f): + self.f = f + + def __get__(self, instance, owner=None): + print("in classmethod __get__") + + def newfunc(*args): + return self.f(owner, *args) + return newfunc + + class Test: + def myfunc(cls): + print("hello") + + # 重点:这就是描述符的体现 + myfunc = classmethod(myfunc) + +验证结果如下 + +.. code:: python + + >>> Test.myfunc() + in classmethod __get__ + hello + >>> Test().myfunc() + in classmethod __get__ + hello + +讲完了 ``property``\ 、\ ``staticmethod``\ 和\ ``classmethod`` 与 +描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 +super 的实现原理,就交由你来自己完成。 + +6. 所有实例共享描述符 +~~~~~~~~~~~~~~~~~~~~~ + +通过以上内容的学习,你是不是觉得自己已经对描述符足够了解了呢? + +可在这里,我想说以上的描述符代码都有问题。 + +问题在哪里呢?请看下面这个例子。 + +.. code:: python + + class Score: + def __init__(self, default=0): + self._value = default + + def __get__(self, instance, owner): + return self._value + + def __set__(self, instance, value): + if 0 <= value <= 100: + self._value = value + else: + raise ValueError + + + class Student: + math = Score(0) + chinese = Score(0) + english = Score(0) + + def __repr__(self): + return "".format(self.math, self.chinese, self.english) + +Student +里没有像前面那样写了构造函数,但是关键不在这儿,没写只是因为没必要写。 + +然后来看一下会出现什么样的问题呢 + +.. code:: python + + >>> std1 = Student() + >>> std1 + + >>> std1.math = 85 + >>> std1 + + >>> std2 = Student() + >>> std2 # std2 居然共享了std1 的属性值 + + >>> std2.math = 100 + >>> std1 # std2 也会改变std1 的属性值 + + +从结果上来看,std2 居然共享了 std1 +的属性值,只要其中一个实例的变量发生改变,另一个实例的变量也会跟着改变。 + +探其根因,是由于此时 math,chinese,english 三个全部是类变量,导致 std2 +和 std1 在访问 math,chinese,english 这三个变量时,其实都是访问类变量。 + +问题是不是来了?小明和小强的分数怎么可能是绑定的呢?这很明显与实际业务不符。 + +使用描述符给我们制造了便利,却无形中给我们带来了麻烦,难道这也是描述符的特性吗? + +描述符是个很好用的特性,会出现这个问题,是由于我们之前写的描述符代码都是错误的。 + +描述符的机制,在我看来,只是抢占了访问顺序,而具体的逻辑却要因地制宜,视情况而定。 + +如果要把 math,chinese,english +这三个变量变成实例之间相互隔离的属性,应该这么写。 + +.. code:: python + + class Score: + def __init__(self, subject): + self.name = subject + + def __get__(self, instance, owner): + return instance.__dict__[self.name] + + def __set__(self, instance, value): + if 0 <= value <= 100: + instance.__dict__[self.name] = value + else: + raise ValueError + + + class Student: + math = Score("math") + chinese = Score("chinese") + english = Score("english") + + def __init__(self, math, chinese, english): + self.math = math + self.chinese = chinese + self.english = english + + def __repr__(self): + return "".format(self.math, self.chinese, self.english) + +引导程序逻辑进入描述符之后,不管你是获取属性,还是设置属性,都是直接作用于 +instance 的。 + +|image5| + +这段代码,你可以仔细和前面的对比一下。 + +不难看出: + +- 之前的错误代码,更像是把描述符当做了存储节点。 +- 之后的正确代码,则是把描述符直接当做代理,本身不存储值。 + +以上便是我对描述符的全部分享,希望能对你有所帮助。 + +参考文档 +-------- + +- `Python描述器引导(翻译) `__ + +-------------- + +|image6| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20190425221322.png +.. |image2| image:: http://image.iswbm.com/20190425221322.png +.. |image3| image:: http://image.iswbm.com/20190425221233.png +.. |image4| image:: http://image.iswbm.com/20190519001930.png +.. |image5| image:: http://image.iswbm.com/20200812085823.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c03/c03_09.md b/source/c03/c03_09.md new file mode 100644 index 0000000..defb819 --- /dev/null +++ b/source/c03/c03_09.md @@ -0,0 +1,725 @@ +# 1.24 深入探讨 Python 的 import 机制:实现远程导入模块 + +![](http://image.iswbm.com/20200602135014.png) + +所谓的模块导入( `import` ),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。 + +在 Python 中使用 import 关键字来实现这个操作,但不是唯一的方法,还有 `importlib.import_module()` 和 `__import__()` 等。 + +也许你看到这个标题,会说我怎么会发这么基础的文章? + +与此相反。恰恰我觉得这篇文章的内容可以算是 Python 的进阶技能,会深入地探讨并以真实案例讲解 Python import Hook 的知识点。 + +当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。 + +![](http://image.iswbm.com/20191027192949.png) + +## 1. 导入系统的基础 + +### 1.1 导入单元构成 + +导入单元有多种,可以是模块、包及变量等。 + +对于这些基础的概念,对于新手还是有必要介绍一下它们的区别。 + +**模块**:类似 \*.py,\*.pyc, \*.pyd ,\*.so,\*.dll 这样的文件,是 Python 代码载体的最小单元。 + +**包** 还可以细分为两种: + +- Regular packages:是一个带有 `__init__.py` 文件的文件夹,此文件夹下可包含其他子包,或者模块 +- Namespace packages + +关于 Namespace packages,有的人会比较陌生,我这里摘抄官方文档的一段说明来解释一下。 + +Namespace packages 是由多个 部分 构成的,每个部分为父包增加一个子包。 各个部分可能处于文件系统的不同位置。 部分也可能处于 zip 文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。 命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。 + +命名空间包的 `__path__ ` 属性不使用普通的列表。 而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 sys.path) 发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。 + +命名空间包没有 `parent/__init__.py` 文件。 实际上,在导入搜索期间可能找到多个 parent 目录,每个都由不同的部分所提供。 因此 parent/one 的物理位置不一定与 parent/two 相邻。 在这种情况下,Python 将为顶级的 parent 包创建一个命名空间包,无论是它本身还是它的某个子包被导入。 + + + +### 1.2 相对/绝对导入 + +当我们 import 导入模块或包时,Python 提供两种导入方式: + +- 相对导入(relative import ):from . import B 或 from ..A import B,其中.表示当前模块,..表示上层模块 +- 绝对导入(absolute import):import foo.bar 或者 from foo import bar + +你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6 之前),Python 默认使用的相对导入。而后来的版本中( Python2.6 之后),都以绝对导入为默认使用的导入方式。 + +使用绝对路径和相对路径各有利弊: + +- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。 +- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。 + +### 1.3 导入的标准写法 + +在 PEP8 中对模块的导入提出了要求,遵守 PEP8规范能让你的代码更具有可读性,我这边也列一下: + +- import 语句应当分行书写 + +```python +# bad +import os,sys + +# good +import os +import sys +``` + +- import语句应当使用absolute import + +```python +# bad +from ..bar import Bar + +# good +from foo.bar import test +``` + +- import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前 + +- import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列 + +```python +# 内置模块 +import os +import sys + +# 第三方模块 +import flask + +# 本地模块 +from foo import bar +``` + +### 1.4 几个有用的 sys 变量 + +`sys.path` 可以列出 Python 模块查找的目录列表 + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path) +['', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', + '/Users/MING/Library/Python/3.6/lib/python/site-packages', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'] +>>> +``` + +`sys.meta_path` 存放的是所有的查找器。 + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.meta_path) +[, + , + ] +``` + + + +`sys.path_importer_cache` 比 `sys.path` 会更大点, 因为它会为所有被加载代码的目录记录它们的查找器。 这包括包的子目录,这些通常在 `sys.path` 中是不存在的。 + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path_importer_cache) +{'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip': None, + '/Users/MING': FileFinder('/Users/MING'), + '/Users/MING/Library/Python/3.6/lib/python/site-packages': FileFinder('/Users/MING/Library/Python/3.6/lib/python/site-packages')} +``` + + + +## 2. \__import__ 的妙用 + +import 关键字的使用,可以说是基础中的基础。 + +但这不是模块唯一的方法,还有 `importlib.import_module()` 和 `__import__()` 等。 + +和 import 不同的是,`__import__` 是一个函数,也正是因为这个原因,使得 `__import__` 的使用会更加灵活,常常用于框架中,对于插件的动态加载。 + +实际上,当我们调用 import 导入模块时,其内部也是调用了 `__import__` ,请看如下两种导入方法,他们是等价的。 + +```python +# 使用 import +import os + +# 使用 __import__ +os = __import__('os') +``` + +通过举一反三,下面两种方法同样也是等价的。 + +```python +# 使用 import .. as .. +import pandas as pd + +# 使用 __import__ +pd = __import__('pandas') +``` + +上面我说 `__import__` 常常用于插件的动态,事实上也只有它能做到(相对于 import 来说)。 + +`插件`通常会位于某一特定的文件夹下,在使用过程中,可能你并不会用到全部的插件,也可能你会新增插件。 + +如果使用 import 关键字这种硬编码的方式,显然太不优雅了,当你要新增/修改插件的时候,都需要你修改代码。更合适的做法是,将这些插件以配置的方式,写在配置文件中,然后由代码去读取你的配置,动态导入你要使用的插件,即灵活又方便,也不容易出错。 + +假如我的一个项目中,有 `plugin01` 、`plugin02`、`plugin03 ` 、`plugin04` 四个插件,这些插件下都会实现一个核心方法 `run()` 。但有时候我不想使用全部的插件,只想使用 `plugin02`、`plugin04 ` ,那我就在配置文件中写我要使用的两个插件。 + +```shell +# my.conf +custom_plugins=['plugin02', 'plugin04'] +``` + +那我如何使用动态加载,并运行他们呢? + +```python +# main.py + +for plugin in conf.custom_plugins: + __import__(plugin) + sys.modules[plugin].run() +``` + + + +## 3. 理解模块的缓存 + +在一个模块内部重复引用另一个相同模块,实际并不会导入两次,原因是在使用关键字 `import` 导入模块时,它会先检索 `sys.modules` 里是否已经载入这个模块了,如果已经载入,则不会再次导入,如果不存在,才会去检索导入这个模块。 + +来实验一下,在 `my_mod02` 这个模块里,我 import 两次 `my_mod01` 这个模块,按逻辑每一次 import 会一次 `my_mod01` 里的代码(即打印 `in mod01`),但是验证结果是,只打印了一次。 + +```shell +$ cat my_mod01.py +print('in mod01') + +$ cat my_mod02.py +import my_mod01 +import my_mod01 + +$ python my_mod02.py +in mod01 +``` + +该现象的解释是:因为有 `sys.modules` 的存在。 + +`sys.modules` 是一个字典(key:模块名,value:模块对象),它存放着在当前 namespace 所有已经导入的模块对象。 + +```python +# test_module.py + +import sys +print(sys.modules.get('json', 'NotFound')) + +import json +print(sys.modules.get('json', 'NotFound')) +``` + +运行结果如下,可见在 导入后 json 模块后,`sys.modules` 才有了 json 模块的对象。 + +```shell +$ python test_module.py +NotFound + +``` + + 由于有缓存的存在,使得我们无法重新载入一个模块。 + +但若你想反其道行之,可以借助 importlib 这个神奇的库来实现。事实也确实有此场景,比如在代码调试中,在发现代码有异常并修改后,我们通常要重启服务再次载入程序。这时候,若有了模块重载,就无比方便了,修改完代码后也无需服务的重启,就能继续调试。 + +还是以上面的例子来理解,`my_mod02.py` 改写成如下 + +```python +# my_mod02.py + +import importlib +import my_mod01 +importlib.reload(my_mod01) +``` + +使用 python3 来执行这个模块,与上面不同的是,这边执行了两次 `my_mod01.py` + +```shell +$ python3 my_mod02.py +in mod01 +in mod01 +``` + + + +## 4. 查找器与加载器 + +如果指定名称的模块在 `sys.modules` 找不到,则将发起调用 Python 的导入协议以查找和加载该模块。 + +此协议由两个概念性模块构成,即 `查找器` 和 `加载器`。 + +一个 Python 的模块的导入,其实可以再细分为两个过程: + +1. 由查找器实现的模块查找 +2. 由加载器实现的模块加载 + +### 4.1 查找器是什么? + +查找器(finder),简单点说,查找器定义了一个模块查找机制,让程序知道该如何找到对应的模块。 + +其实 Python 内置了多个默认查找器,其存在于 sys.meta_path 中。 + +但这些查找器对应使用者来说,并不是那么重要,因此在 Python 3.3 之前, Python 解释将其隐藏了,我们称之为隐式查找器。 + +```python +# Python 2.7 +>>> import sys +>>> sys.meta_path +[] +>>> +``` + +由于这点不利于开发者深入理解 import 机制,在 Python 3.3 后,所有的模块导入机制都会通过 sys.meta_path 暴露,不会在有任何隐式导入机制。 + +```python +# Python 3.6 +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.meta_path) +[, + , + ] +``` + +观察一下 Python 默认的这几种查找器 (finder),可以分为三种: + +- 一种知道如何导入内置模块 +- 一种知道如何导入冻结模块 +- 一种知道如何导入来自 [import path](https://docs.python.org/zh-cn/3/glossary.html#term-import-path) 的模块 (即 [path based finder](https://docs.python.org/zh-cn/3/glossary.html#term-path-based-finder))。 + +那我们能不能自已定义一个查找器呢?当然可以,你只要 + +- 定义一个实现了 find_module 方法的类(py2和py3均可),或者实现 find_loader 类方法(仅 py3 有效),如果找到模块需要返回一个 loader 对象或者 ModuleSpec 对象(后面会讲),没找到需要返回 None +- 定义完后,要使用这个查找器,必须注册它,将其插入在 sys.meta_path 的首位,这样就能优先使用。 + +```python +import sys + +class MyFinder(object): + @classmethod + def find_module(cls, name, path, target=None): + print("Importing", name, path, target) + # 将在后面定义 + return MyLoader() + +# 由于 finder 是按顺序读取的,所以必须插入在首位 +sys.meta_path.insert(0, MyFinder) +``` + +查找器可以分为两种: + +```shell +object + +-- Finder (deprecated) + +-- MetaPathFinder + +-- PathEntryFinder +``` + +这里需要注意的是,在 3.4 版前,查找器会直接返回 加载器(Loader)对象,而在 3.4 版后,查找器则会返回模块规格说明(ModuleSpec),其中 包含加载器。 + +而关于什么是 加载器 和 模块规格说明, 请继续往后看。 + +### 4.2 加载器是什么? + +查找器只负责查找定位找模,而真正负责加载模块的,是加载器(loader)。 + +一般的 loader 必须定义名为 ` load_module() ` 的方法。 + +为什么这里说一般,因为 loader 还分多种: + +```shell +object + +-- Finder (deprecated) + | +-- MetaPathFinder + | +-- PathEntryFinder + +-- Loader + +-- ResourceLoader --------+ + +-- InspectLoader | + +-- ExecutionLoader --+ + +-- FileLoader + +-- SourceLoader +``` + +通过查看源码可知,不同的加载器的抽象方法各有不同。 + + + +加载器通常由一个 finder 返回。详情参见 PEP 302,对于 abstract base class 可参见 importlib.abc.Loader。 + +那如何自定义我们自己的加载器呢? + +你只要 + +- 定义一个实现了 load_module 方法的类 +- 对与导入有关的属性([点击查看详情](https://docs.python.org/zh-cn/3/reference/import.html#import-related-module-attributes))进行校验 +- 创建模块对象并绑定所有与导入相关的属性变量到该模块上 +- 将此模块保存到 sys.modules 中(顺序很重要,避免递归导入) +- 然后加载模块(这是核心) +- 若加载出错,需要能够处理抛出异常( ImportError) +- 若加载成功,则返回 module 对象 + +若你想看具体的例子,可以接着往后看。 + +### 4.3 模块规格说明 + +导入机制在导入期间会使用有关每个模块的多种信息,特别是加载之前。 大多数信息都是所有模块通用的。 模块规格说明的目的是基于每个模块来封装这些导入相关信息。 + +模块的规格说明会作为模块对象的 `__spec__` 属性对外公开。 有关模块规格的详细内容请参阅 [`ModuleSpec`](https://docs.python.org/zh-cn/3/library/importlib.html#importlib.machinery.ModuleSpec)。 + +在 Python 3.4 后,查找器不再返回加载器,而是返回 ModuleSpec 对象,它储存着更多的信息 + +- 模块名 +- 加载器 +- 模块绝对路径 + +那如何查看一个模块的 ModuleSpec ? + +这边举个例子 + +```shell +$ cat my_mod02.py +import my_mod01 +print(my_mod01.__spec__) + +$ python3 my_mod02.py +in mod01 +ModuleSpec(name='my_mod01', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000000000392DBE0>, origin='/home/MING/my_mod01.py') +``` + +从 ModuleSpec 中可以看到,加载器是包含在内的,那我们如果要重新加载一个模块,是不是又有了另一种思路了? + +来一起验证一下。 + +现在有两个文件: + +一个是 my_info.py + +```python +# my_info.py +name='wangbm' +``` + + 另一个是:main.py + +```python +# main.py +import my_info + +print(my_info.name) + +# 加一个断点 +import pdb;pdb.set_trace() + +# 再加载一次 +my_info.__spec__.loader.load_module() + +print(my_info.name) +``` + +在 `main.py` 处,我加了一个断点,目的是当运行到断点处时,我修改 my_info.py 里的 name 为 `ming` ,以便验证重载是否有效? + +```shell +$ python3 main.py +wangbm +> /home/MING/main.py(9)() +-> my_info.__spec__.loader.load_module() +(Pdb) c +ming +``` + +从结果来看,重载是有效的。 + + + +### 4.4 导入器是什么? + +导入器(importer),也许你在其他文章里会见到它,但其实它并不是个新鲜的东西。 + +它只是同时实现了查找器和加载器两种接口的对象,所以你可以说导入器(importer)是查找器(finder),也可以说它是加载器(loader)。 + + + +## 5. 远程导入模块 + +由于 Python 默认的 查找器和加载器 仅支持本地的模块的导入,并不支持实现远程模块的导入。 + +为了让你更好的理解 Python Import Hook 机制,我下面会通过实例演示,如何自己实现远程导入模块的导入器。 + +### 5.1 动手实现导入器 + +当导入一个包的时候,Python 解释器首先会从 sys.meta_path 中拿到查找器列表。 + +默认顺序是:内建模块查找器 -> 冻结模块查找器 -> 第三方模块路径(本地的 sys.path)查找器 + +若经过这三个查找器,仍然无法查找到所需的模块,则会抛出ImportError异常。 + + + +因此要实现远程导入模块,有两种思路。 + +- 一种是实现自己的元路径导入器; +- 另一种是编写一个钩子,添加到sys.path_hooks里,识别特定的目录命名模式。 + + + +我这里选择第一种方法来做为示例。 + +实现导入器,我们需要分别查找器和加载器。 + +**首先是查找器** + +由源码得知,路径查找器分为两种 + +- MetaPathFinder +- PathEntryFinder + +这里使用 MetaPathFinder 来进行查找器的编写。 + +在 Python 3.4 版本之前,查找器必须实现 `find_module()` 方法,而 Python 3.4+ 版,则推荐使用 `find_spec()` 方法,但这并不意味着你不能使用 `find_module()`,但是在没有 `find_spec()` 方法时,导入协议还是会尝试 `find_module()` 方法。 + +我先举例下使用 `find_module()` 该如何写。 + +```python +from importlib import abc + +class UrlMetaFinder(abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + loader.load_module(fullname) + return loader + except Exception: + return None +``` + +若使用 `find_spec()` ,要注意此方法的调用需要带有两到三个参数。 + +第一个是被导入模块的完整限定名称,例如 `foo.bar.baz`。 第二个参数是供模块搜索使用的路径条目。 对于最高层级模块,第二个参数为 `None`,但对于子模块或子包,第二个参数为父包 `__path__` 属性的值。 如果相应的 `__path__` 属性无法访问,将引发 [`ModuleNotFoundError`](https://docs.python.org/zh-cn/3/library/exceptions.html#ModuleNotFoundError)。 第三个参数是一个将被作为稍后加载目标的现有模块对象。 导入系统仅会在重加载期间传入一个目标模块。 + +```python +from importlib import abc +from importlib.machinery import ModuleSpec + +class UrlMetaFinder(abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + def find_spec(self, fullname, path=None, target=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return ModuleSpec(fullname, loader, is_package=loader.is_package(fullname)) + except Exception: + return None +``` + + + +**接下来是加载器** + +由源码得知,路径查找器分为三种 + +- FileLoader +- SourceLoader + +按理说,两种加载器都可以实现我们想要的功能,我这里选用 SourceLoader 来示范。 + +在 SourceLoader 这个抽象类里,有几个很重要的方法,在你写实现加载器的时候需要注意 + +- get_code:获取源代码,可以根据自己场景实现实现。 +- exec_module:执行源代码,并将变量赋值给 `module.__dict__` +- get_data:抽象方法,必须实现,返回指定路径的字节码。 +- get_filename:抽象方法,必须实现,返回文件名 + +在一些老的博客文章中,你会经常看到 加载器 要实现 `load_module()` ,而这个方法早已在 Python 3.4 的时候就被废弃了,当然为了兼容考虑,你若使用 `load_module()` 也是可以的。 + +```python +from importlib import abc + +class UrlMetaLoader(abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def load_module(self, fullname): + code = self.get_code(fullname) + mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) + mod.__file__ = self.get_filename(fullname) + mod.__loader__ = self + mod.__package__ = fullname + exec(code, mod.__dict__) + return None + + def get_data(self): + pass + + def execute_module(self, module): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' +``` + +当你使用这种旧模式实现自己的加载时,你需要注意两点,很重要: + +- execute_module 必须重载,而且不应该有任何逻辑,即使它并不是抽象方法。 +- load_module,需要你在查找器里手动执行,才能实现模块的加载。。 + +做为替换,你应该使用 `execute_module()` 和 `create_module()` 。由于基类里已经实现了 `execute_module` 和 `create_module()`,并且满足我们的使用场景。我这边可以不用重复实现。和旧模式相比,这里也不需要在设查找器里手动执行 `execute_module()`。 + +```python +import urllib.request as urllib2 + +class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' +``` + +查找器和加载器都有了,别忘了往sys.meta_path 注册我们自定义的查找器(UrlMetaFinder)。 + +```python +def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) +``` + +所有的代码都解析完毕后,我们将其整理在一个模块(my_importer.py)中 + +```python +# my_importer.py +import sys +import importlib +import urllib.request as urllib2 + +class UrlMetaFinder(importlib.abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return loader + except Exception: + return None + +class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + +def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) +``` + +### 5.2 搭建远程服务端 + +最开始我说了,要实现一个远程导入模块的方法。 + +我还缺一个在远端的服务器,来存放我的模块,为了方便,我使用python自带的 `http.server` 模块用一条命令即可实现。 + +```shell +$ mkdir httpserver && cd httpserver +$ cat>my_info.py>> from my_importer import install_meta +>>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder +>>> import my_info # 打印ok,说明导入成功 +ok +>>> my_info.name # 验证可以取得到变量 +'wangbm' +``` + +至此,我实现了一个简易的可以导入远程服务器上的模块的导入器。 + + + +## 参考文档 + +- https://docs.python.org/zh-cn/3/reference/import.html +- https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc +- https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c03/c03_09.rst b/source/c03/c03_09.rst new file mode 100644 index 0000000..fae70f7 --- /dev/null +++ b/source/c03/c03_09.rst @@ -0,0 +1,803 @@ +1.24 深入探讨 Python 的 import 机制:实现远程导入模块 +===================================================== + +|image0| + +所谓的模块导入( ``import`` +),是指在一个模块中使用另一个模块的代码的操作,它有利于代码的复用。 + +在 Python 中使用 import 关键字来实现这个操作,但不是唯一的方法,还有 +``importlib.import_module()`` 和 ``__import__()`` 等。 + +也许你看到这个标题,会说我怎么会发这么基础的文章? + +与此相反。恰恰我觉得这篇文章的内容可以算是 Python +的进阶技能,会深入地探讨并以真实案例讲解 Python import Hook 的知识点。 + +当然为了使文章更系统、全面,前面会有小篇幅讲解基础知识点,但请你有耐心的往后读下去,因为后面才是本篇文章的精华所在,希望你不要错过。 + +|image1| + +1. 导入系统的基础 +----------------- + +1.1 导入单元构成 +~~~~~~~~~~~~~~~~ + +导入单元有多种,可以是模块、包及变量等。 + +对于这些基础的概念,对于新手还是有必要介绍一下它们的区别。 + +**模块**\ :类似 \*.py,*.pyc, \*.pyd ,*.so,*.dll 这样的文件,是 +Python 代码载体的最小单元。 + +**包** 还可以细分为两种: + +- Regular packages:是一个带有 ``__init__.py`` + 文件的文件夹,此文件夹下可包含其他子包,或者模块 +- Namespace packages + +关于 Namespace +packages,有的人会比较陌生,我这里摘抄官方文档的一段说明来解释一下。 + +Namespace packages 是由多个 部分 构成的,每个部分为父包增加一个子包。 +各个部分可能处于文件系统的不同位置。 部分也可能处于 zip +文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。 +命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。 + +命名空间包的 ``__path__`` 属性不使用普通的列表。 +而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 sys.path) +发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。 + +命名空间包没有 ``parent/__init__.py`` 文件。 +实际上,在导入搜索期间可能找到多个 parent +目录,每个都由不同的部分所提供。 因此 parent/one 的物理位置不一定与 +parent/two 相邻。 在这种情况下,Python 将为顶级的 parent +包创建一个命名空间包,无论是它本身还是它的某个子包被导入。 + +1.2 相对/绝对导入 +~~~~~~~~~~~~~~~~~ + +当我们 import 导入模块或包时,Python 提供两种导入方式: + +- 相对导入(relative import ):from . import B 或 from ..A import + B,其中.表示当前模块,..表示上层模块 +- 绝对导入(absolute import):import foo.bar 或者 from foo import bar + +你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6 +之前),Python 默认使用的相对导入。而后来的版本中( Python2.6 +之后),都以绝对导入为默认使用的导入方式。 + +使用绝对路径和相对路径各有利弊: + +- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。 +- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。 + +1.3 导入的标准写法 +~~~~~~~~~~~~~~~~~~ + +在 PEP8 中对模块的导入提出了要求,遵守 +PEP8规范能让你的代码更具有可读性,我这边也列一下: + +- import 语句应当分行书写 + +.. code:: python + + # bad + import os,sys + + # good + import os + import sys + +- import语句应当使用absolute import + +.. code:: python + + # bad + from ..bar import Bar + + # good + from foo.bar import test + +- import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前 + +- import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列 + +.. code:: python + + # 内置模块 + import os + import sys + + # 第三方模块 + import flask + + # 本地模块 + from foo import bar + +1.4 几个有用的 sys 变量 +~~~~~~~~~~~~~~~~~~~~~~~ + +``sys.path`` 可以列出 Python 模块查找的目录列表 + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path) + ['', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', + '/Users/MING/Library/Python/3.6/lib/python/site-packages', + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'] + >>> + +``sys.meta_path`` 存放的是所有的查找器。 + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.meta_path) + [, + , + ] + +``sys.path_importer_cache`` 比 ``sys.path`` 会更大点, +因为它会为所有被加载代码的目录记录它们的查找器。 +这包括包的子目录,这些通常在 ``sys.path`` 中是不存在的。 + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path_importer_cache) + {'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/encodings'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages': FileFinder('/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'), + '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip': None, + '/Users/MING': FileFinder('/Users/MING'), + '/Users/MING/Library/Python/3.6/lib/python/site-packages': FileFinder('/Users/MING/Library/Python/3.6/lib/python/site-packages')} + +2. \__import_\_ 的妙用 +---------------------- + +import 关键字的使用,可以说是基础中的基础。 + +但这不是模块唯一的方法,还有 ``importlib.import_module()`` 和 +``__import__()`` 等。 + +和 import 不同的是,\ ``__import__`` +是一个函数,也正是因为这个原因,使得 ``__import__`` +的使用会更加灵活,常常用于框架中,对于插件的动态加载。 + +实际上,当我们调用 import 导入模块时,其内部也是调用了 ``__import__`` +,请看如下两种导入方法,他们是等价的。 + +.. code:: python + + # 使用 import + import os + + # 使用 __import__ + os = __import__('os') + +通过举一反三,下面两种方法同样也是等价的。 + +.. code:: python + + # 使用 import .. as .. + import pandas as pd + + # 使用 __import__ + pd = __import__('pandas') + +上面我说 ``__import__`` 常常用于插件的动态,事实上也只有它能做到(相对于 +import 来说)。 + +``插件``\ 通常会位于某一特定的文件夹下,在使用过程中,可能你并不会用到全部的插件,也可能你会新增插件。 + +如果使用 import +关键字这种硬编码的方式,显然太不优雅了,当你要新增/修改插件的时候,都需要你修改代码。更合适的做法是,将这些插件以配置的方式,写在配置文件中,然后由代码去读取你的配置,动态导入你要使用的插件,即灵活又方便,也不容易出错。 + +假如我的一个项目中,有 ``plugin01`` 、\ ``plugin02``\ 、\ ``plugin03`` +、\ ``plugin04`` 四个插件,这些插件下都会实现一个核心方法 ``run()`` +。但有时候我不想使用全部的插件,只想使用 ``plugin02``\ 、\ ``plugin04`` +,那我就在配置文件中写我要使用的两个插件。 + +.. code:: shell + + # my.conf + custom_plugins=['plugin02', 'plugin04'] + +那我如何使用动态加载,并运行他们呢? + +.. code:: python + + # main.py + + for plugin in conf.custom_plugins: + __import__(plugin) + sys.modules[plugin].run() + +3. 理解模块的缓存 +----------------- + +在一个模块内部重复引用另一个相同模块,实际并不会导入两次,原因是在使用关键字 +``import`` 导入模块时,它会先检索 ``sys.modules`` +里是否已经载入这个模块了,如果已经载入,则不会再次导入,如果不存在,才会去检索导入这个模块。 + +来实验一下,在 ``my_mod02`` 这个模块里,我 import 两次 ``my_mod01`` +这个模块,按逻辑每一次 import 会一次 ``my_mod01`` 里的代码(即打印 +``in mod01``\ ),但是验证结果是,只打印了一次。 + +.. code:: shell + + $ cat my_mod01.py + print('in mod01') + + $ cat my_mod02.py + import my_mod01 + import my_mod01 + + $ python my_mod02.py + in mod01 + +该现象的解释是:因为有 ``sys.modules`` 的存在。 + +``sys.modules`` +是一个字典(key:模块名,value:模块对象),它存放着在当前 namespace +所有已经导入的模块对象。 + +.. code:: python + + # test_module.py + + import sys + print(sys.modules.get('json', 'NotFound')) + + import json + print(sys.modules.get('json', 'NotFound')) + +运行结果如下,可见在 导入后 json 模块后,\ ``sys.modules`` 才有了 json +模块的对象。 + +.. code:: shell + + $ python test_module.py + NotFound + + +由于有缓存的存在,使得我们无法重新载入一个模块。 + +但若你想反其道行之,可以借助 importlib +这个神奇的库来实现。事实也确实有此场景,比如在代码调试中,在发现代码有异常并修改后,我们通常要重启服务再次载入程序。这时候,若有了模块重载,就无比方便了,修改完代码后也无需服务的重启,就能继续调试。 + +还是以上面的例子来理解,\ ``my_mod02.py`` 改写成如下 + +.. code:: python + + # my_mod02.py + + import importlib + import my_mod01 + importlib.reload(my_mod01) + +使用 python3 来执行这个模块,与上面不同的是,这边执行了两次 +``my_mod01.py`` + +.. code:: shell + + $ python3 my_mod02.py + in mod01 + in mod01 + +4. 查找器与加载器 +----------------- + +如果指定名称的模块在 ``sys.modules`` 找不到,则将发起调用 Python +的导入协议以查找和加载该模块。 + +此协议由两个概念性模块构成,即 ``查找器`` 和 ``加载器``\ 。 + +一个 Python 的模块的导入,其实可以再细分为两个过程: + +1. 由查找器实现的模块查找 +2. 由加载器实现的模块加载 + +4.1 查找器是什么? +~~~~~~~~~~~~~~~~~~ + +查找器(finder),简单点说,查找器定义了一个模块查找机制,让程序知道该如何找到对应的模块。 + +其实 Python 内置了多个默认查找器,其存在于 sys.meta_path 中。 + +但这些查找器对应使用者来说,并不是那么重要,因此在 Python 3.3 之前, +Python 解释将其隐藏了,我们称之为隐式查找器。 + +.. code:: python + + # Python 2.7 + >>> import sys + >>> sys.meta_path + [] + >>> + +由于这点不利于开发者深入理解 import 机制,在 Python 3.3 +后,所有的模块导入机制都会通过 sys.meta_path +暴露,不会在有任何隐式导入机制。 + +.. code:: python + + # Python 3.6 + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.meta_path) + [, + , + ] + +观察一下 Python 默认的这几种查找器 (finder),可以分为三种: + +- 一种知道如何导入内置模块 +- 一种知道如何导入冻结模块 +- 一种知道如何导入来自 `import + path `__ + 的模块 (即 `path based + finder `__)。 + +那我们能不能自已定义一个查找器呢?当然可以,你只要 + +- 定义一个实现了 find_module 方法的类(py2和py3均可),或者实现 + find_loader 类方法(仅 py3 有效),如果找到模块需要返回一个 loader + 对象或者 ModuleSpec 对象(后面会讲),没找到需要返回 None +- 定义完后,要使用这个查找器,必须注册它,将其插入在 sys.meta_path + 的首位,这样就能优先使用。 + +.. code:: python + + import sys + + class MyFinder(object): + @classmethod + def find_module(cls, name, path, target=None): + print("Importing", name, path, target) + # 将在后面定义 + return MyLoader() + + # 由于 finder 是按顺序读取的,所以必须插入在首位 + sys.meta_path.insert(0, MyFinder) + +查找器可以分为两种: + +.. code:: shell + + object + +-- Finder (deprecated) + +-- MetaPathFinder + +-- PathEntryFinder + +这里需要注意的是,在 3.4 版前,查找器会直接返回 +加载器(Loader)对象,而在 3.4 +版后,查找器则会返回模块规格说明(ModuleSpec),其中 包含加载器。 + +而关于什么是 加载器 和 模块规格说明, 请继续往后看。 + +4.2 加载器是什么? +~~~~~~~~~~~~~~~~~~ + +查找器只负责查找定位找模,而真正负责加载模块的,是加载器(loader)。 + +一般的 loader 必须定义名为 ``load_module()`` 的方法。 + +为什么这里说一般,因为 loader 还分多种: + +.. code:: shell + + object + +-- Finder (deprecated) + | +-- MetaPathFinder + | +-- PathEntryFinder + +-- Loader + +-- ResourceLoader --------+ + +-- InspectLoader | + +-- ExecutionLoader --+ + +-- FileLoader + +-- SourceLoader + +通过查看源码可知,不同的加载器的抽象方法各有不同。 + +加载器通常由一个 finder 返回。详情参见 PEP 302,对于 abstract base class +可参见 importlib.abc.Loader。 + +那如何自定义我们自己的加载器呢? + +你只要 + +- 定义一个实现了 load_module 方法的类 +- 对与导入有关的属性(\ `点击查看详情 `__\ )进行校验 +- 创建模块对象并绑定所有与导入相关的属性变量到该模块上 +- 将此模块保存到 sys.modules 中(顺序很重要,避免递归导入) +- 然后加载模块(这是核心) +- 若加载出错,需要能够处理抛出异常( ImportError) +- 若加载成功,则返回 module 对象 + +若你想看具体的例子,可以接着往后看。 + +4.3 模块规格说明 +~~~~~~~~~~~~~~~~ + +导入机制在导入期间会使用有关每个模块的多种信息,特别是加载之前。 +大多数信息都是所有模块通用的。 +模块规格说明的目的是基于每个模块来封装这些导入相关信息。 + +模块的规格说明会作为模块对象的 ``__spec__`` 属性对外公开。 +有关模块规格的详细内容请参阅 +```ModuleSpec`` `__\ 。 + +在 Python 3.4 后,查找器不再返回加载器,而是返回 ModuleSpec +对象,它储存着更多的信息 + +- 模块名 +- 加载器 +- 模块绝对路径 + +那如何查看一个模块的 ModuleSpec ? + +这边举个例子 + +.. code:: shell + + $ cat my_mod02.py + import my_mod01 + print(my_mod01.__spec__) + + $ python3 my_mod02.py + in mod01 + ModuleSpec(name='my_mod01', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000000000392DBE0>, origin='/home/MING/my_mod01.py') + +从 ModuleSpec +中可以看到,加载器是包含在内的,那我们如果要重新加载一个模块,是不是又有了另一种思路了? + +来一起验证一下。 + +现在有两个文件: + +一个是 my_info.py + +.. code:: python + + # my_info.py + name='wangbm' + +另一个是:main.py + +.. code:: python + + # main.py + import my_info + + print(my_info.name) + + # 加一个断点 + import pdb;pdb.set_trace() + + # 再加载一次 + my_info.__spec__.loader.load_module() + + print(my_info.name) + +在 ``main.py`` 处,我加了一个断点,目的是当运行到断点处时,我修改 +my_info.py 里的 name 为 ``ming`` ,以便验证重载是否有效? + +.. code:: shell + + $ python3 main.py + wangbm + > /home/MING/main.py(9)() + -> my_info.__spec__.loader.load_module() + (Pdb) c + ming + +从结果来看,重载是有效的。 + +4.4 导入器是什么? +~~~~~~~~~~~~~~~~~~ + +导入器(importer),也许你在其他文章里会见到它,但其实它并不是个新鲜的东西。 + +它只是同时实现了查找器和加载器两种接口的对象,所以你可以说导入器(importer)是查找器(finder),也可以说它是加载器(loader)。 + +5. 远程导入模块 +--------------- + +由于 Python 默认的 查找器和加载器 +仅支持本地的模块的导入,并不支持实现远程模块的导入。 + +为了让你更好的理解 Python Import Hook +机制,我下面会通过实例演示,如何自己实现远程导入模块的导入器。 + +5.1 动手实现导入器 +~~~~~~~~~~~~~~~~~~ + +当导入一个包的时候,Python 解释器首先会从 sys.meta_path +中拿到查找器列表。 + +默认顺序是:内建模块查找器 -> 冻结模块查找器 -> 第三方模块路径(本地的 +sys.path)查找器 + +若经过这三个查找器,仍然无法查找到所需的模块,则会抛出ImportError异常。 + +因此要实现远程导入模块,有两种思路。 + +- 一种是实现自己的元路径导入器; +- 另一种是编写一个钩子,添加到sys.path_hooks里,识别特定的目录命名模式。 + +我这里选择第一种方法来做为示例。 + +实现导入器,我们需要分别查找器和加载器。 + +**首先是查找器** + +由源码得知,路径查找器分为两种 + +- MetaPathFinder +- PathEntryFinder + +这里使用 MetaPathFinder 来进行查找器的编写。 + +在 Python 3.4 版本之前,查找器必须实现 ``find_module()`` 方法,而 Python +3.4+ 版,则推荐使用 ``find_spec()`` 方法,但这并不意味着你不能使用 +``find_module()``\ ,但是在没有 ``find_spec()`` +方法时,导入协议还是会尝试 ``find_module()`` 方法。 + +我先举例下使用 ``find_module()`` 该如何写。 + +.. code:: python + + from importlib import abc + + class UrlMetaFinder(abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + loader.load_module(fullname) + return loader + except Exception: + return None + +若使用 ``find_spec()`` ,要注意此方法的调用需要带有两到三个参数。 + +第一个是被导入模块的完整限定名称,例如 ``foo.bar.baz``\ 。 +第二个参数是供模块搜索使用的路径条目。 对于最高层级模块,第二个参数为 +``None``\ ,但对于子模块或子包,第二个参数为父包 ``__path__`` 属性的值。 +如果相应的 ``__path__`` 属性无法访问,将引发 +```ModuleNotFoundError`` `__\ 。 +第三个参数是一个将被作为稍后加载目标的现有模块对象。 +导入系统仅会在重加载期间传入一个目标模块。 + +.. code:: python + + from importlib import abc + from importlib.machinery import ModuleSpec + + class UrlMetaFinder(abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + def find_spec(self, fullname, path=None, target=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return ModuleSpec(fullname, loader, is_package=loader.is_package(fullname)) + except Exception: + return None + +**接下来是加载器** + +由源码得知,路径查找器分为三种 + +- FileLoader +- SourceLoader + +按理说,两种加载器都可以实现我们想要的功能,我这里选用 SourceLoader +来示范。 + +在 SourceLoader +这个抽象类里,有几个很重要的方法,在你写实现加载器的时候需要注意 + +- get_code:获取源代码,可以根据自己场景实现实现。 +- exec_module:执行源代码,并将变量赋值给 ``module.__dict__`` +- get_data:抽象方法,必须实现,返回指定路径的字节码。 +- get_filename:抽象方法,必须实现,返回文件名 + +在一些老的博客文章中,你会经常看到 加载器 要实现 ``load_module()`` +,而这个方法早已在 Python 3.4 +的时候就被废弃了,当然为了兼容考虑,你若使用 ``load_module()`` +也是可以的。 + +.. code:: python + + from importlib import abc + + class UrlMetaLoader(abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def load_module(self, fullname): + code = self.get_code(fullname) + mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) + mod.__file__ = self.get_filename(fullname) + mod.__loader__ = self + mod.__package__ = fullname + exec(code, mod.__dict__) + return None + + def get_data(self): + pass + + def execute_module(self, module): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + +当你使用这种旧模式实现自己的加载时,你需要注意两点,很重要: + +- execute_module 必须重载,而且不应该有任何逻辑,即使它并不是抽象方法。 +- load_module,需要你在查找器里手动执行,才能实现模块的加载。。 + +做为替换,你应该使用 ``execute_module()`` 和 ``create_module()`` +。由于基类里已经实现了 ``execute_module`` 和 +``create_module()``\ ,并且满足我们的使用场景。我这边可以不用重复实现。和旧模式相比,这里也不需要在设查找器里手动执行 +``execute_module()``\ 。 + +.. code:: python + + import urllib.request as urllib2 + + class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + +查找器和加载器都有了,别忘了往sys.meta_path +注册我们自定义的查找器(UrlMetaFinder)。 + +.. code:: python + + def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) + +所有的代码都解析完毕后,我们将其整理在一个模块(my_importer.py)中 + +.. code:: python + + # my_importer.py + import sys + import importlib + import urllib.request as urllib2 + + class UrlMetaFinder(importlib.abc.MetaPathFinder): + def __init__(self, baseurl): + self._baseurl = baseurl + + + def find_module(self, fullname, path=None): + if path is None: + baseurl = self._baseurl + else: + # 不是原定义的url就直接返回不存在 + if not path.startswith(self._baseurl): + return None + baseurl = path + + try: + loader = UrlMetaLoader(baseurl) + return loader + except Exception: + return None + + class UrlMetaLoader(importlib.abc.SourceLoader): + def __init__(self, baseurl): + self.baseurl = baseurl + + def get_code(self, fullname): + f = urllib2.urlopen(self.get_filename(fullname)) + return f.read() + + def get_data(self): + pass + + def get_filename(self, fullname): + return self.baseurl + fullname + '.py' + + def install_meta(address): + finder = UrlMetaFinder(address) + sys.meta_path.append(finder) + +5.2 搭建远程服务端 +~~~~~~~~~~~~~~~~~~ + +最开始我说了,要实现一个远程导入模块的方法。 + +我还缺一个在远端的服务器,来存放我的模块,为了方便,我使用python自带的 +``http.server`` 模块用一条命令即可实现。 + +.. code:: shell + + $ mkdir httpserver && cd httpserver + $ cat>my_info.py>> from my_importer import install_meta + >>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder + >>> import my_info # 打印ok,说明导入成功 + ok + >>> my_info.name # 验证可以取得到变量 + 'wangbm' + +至此,我实现了一个简易的可以导入远程服务器上的模块的导入器。 + +参考文档 +-------- + +- https://docs.python.org/zh-cn/3/reference/import.html +- https://docs.python.org/zh-cn/3/library/importlib.html#module-importlib.abc +- https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p11_load_modules_from_remote_machine_by_hooks.html + +|image2| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20191027192949.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c03/c03_10.md b/source/c03/c03_10.md new file mode 100644 index 0000000..1551bb4 --- /dev/null +++ b/source/c03/c03_10.md @@ -0,0 +1,148 @@ +# 3.10 Python几个高阶函数 + +![](http://image.iswbm.com/20200602135014.png) + +--- + +## 1. lambda 表达式 + +匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。 + +正常情况下,我们定义一个函数,使用的是 `def` 关键字,而当你学会使用匿名函数后,替代 `def` 的是 `lambda`。 + +这边使用`def` 和 `lambda` 分别举个例子,你很快就能理解。 + +``` +def mySum(x, y): + return x+y +mySum(2, 3) +# 5 + +(lambda x, y: x+y)(2, 4) +# 6 +``` + +从上面的示例,我们可以看到匿名函数直接运行,省下了很多行的代码,有没有? + +接下来,我们的仔细看一下它的用法 + +带 if/else + +``` +>>>( lambda x, y: x if x < y else y )( 1, 2 ) +1 +``` + +嵌套函数 + +``` +>>>( lambda x: ( lambda y: ( lambda z: x + y + z )( 1 ) )( 2 ) )( 3 ) +6 +``` + +递归函数 + +``` +>>> func = lambda n:1 if n == 0 else n * func(n-1) +>>> func(5) +120 +``` + +或者 + +``` +>>> f = lambda func, n: 1 if n == 0 else n * func( func, n - 1 ) +>>> f(f,4) +24 +``` + +从以上示例来看,lambda 表达式和常规的函数相比,写法比较怪异,可读性相对较差。除了可以直接运行之外,好像并没有其他较为突出的功能,为什么在今天我们要介绍它呢? + +首先我们要知道 lambda 是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。 + +## 2. map 函数 + +map 函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。 + +它可以实现怎样的功能呢,我举个例子你就明白了。 + +``` +>>> map(lambda x: x*2, [1,2,3,4,5]) +[2, 4, 6, 8, 10] +``` + +可以很清楚地看到,它可以将后面序列中的每一个元素做为参数传入lambda中。 + +当我们不使用 map 函数时,你也许会这样子写。 + +``` +mylist=[] +for i in [1,2,3,4,5]: + mylist.append(i*2) +``` + +## 3. filter 函数 + +filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda 表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回 True,则元素会被保留下来,当表达式返回 False ,则元素会被丢弃。 + +下面这个例子,将过滤出一个列表中小于0的元素。 + +``` +>>>filter(lambda x: x < 0, range(-5, 5)) +[-5, -4, -3, -2, -1] +``` + +## 4. reduce 函数 + +reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 lambda 函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。 + +![](http://image.iswbm.com/20200930175131.png) + +这边举个例子你也就明白了。 + +``` +>>>reduce(lambda x,y: x+y, [1,2,3,4,5]) +15 +``` + +它的运算过程分解一下是这样的。 + +``` +1+2=3 +3+3=6 +6+4+10 +10+5=15 +``` + +## 5. 注意点 + +以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的 Pythonic ,在某一程度上代码看起来更加的简洁。 + +如果你是新手呢,你需要注意的是,以上示例是在 Python2.x 环境下演示的。而在 Python3.x 中,却有所不同,你可以自己尝试一下。 + +这里总结一下: + +第一点,map 和 filter 函数返回的都不再是一个列表,而是一个迭代器对象。这里以map为例 + +``` +>>> map_obj = map(lambda x: x*2, [1,2,3,4,5]) +>>> from collections.abc import Iterator +>>> isinstance(map_obj, Iterator) +True +>>> next(map_obj) +2 +>>> list(map_obj) +[4, 6, 8, 10] +``` + +第二点,reduce 不可以直接调用,而是要先导入才能使用, + +``` +from functools import reduce +``` + + + +--- + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c03/c03_10.rst b/source/c03/c03_10.rst new file mode 100644 index 0000000..b52655c --- /dev/null +++ b/source/c03/c03_10.rst @@ -0,0 +1,169 @@ +3.10 Python几个高阶函数 +======================= + +|image0| + +-------------- + +1. lambda 表达式 +---------------- + +匿名函数(英语:anonymous +function)是指一类无需定义标识符(函数名)的函数。通俗来说呢,就是它可以让我们的函数,可以不需要函数名。 + +正常情况下,我们定义一个函数,使用的是 ``def`` +关键字,而当你学会使用匿名函数后,替代 ``def`` 的是 ``lambda``\ 。 + +这边使用\ ``def`` 和 ``lambda`` 分别举个例子,你很快就能理解。 + +:: + + def mySum(x, y): + return x+y + mySum(2, 3) + # 5 + + (lambda x, y: x+y)(2, 4) + # 6 + +从上面的示例,我们可以看到匿名函数直接运行,省下了很多行的代码,有没有? + +接下来,我们的仔细看一下它的用法 + +带 if/else + +:: + + >>>( lambda x, y: x if x < y else y )( 1, 2 ) + 1 + +嵌套函数 + +:: + + >>>( lambda x: ( lambda y: ( lambda z: x + y + z )( 1 ) )( 2 ) )( 3 ) + 6 + +递归函数 + +:: + + >>> func = lambda n:1 if n == 0 else n * func(n-1) + >>> func(5) + 120 + +或者 + +:: + + >>> f = lambda func, n: 1 if n == 0 else n * func( func, n - 1 ) + >>> f(f,4) + 24 + +从以上示例来看,lambda +表达式和常规的函数相比,写法比较怪异,可读性相对较差。除了可以直接运行之外,好像并没有其他较为突出的功能,为什么在今天我们要介绍它呢? + +首先我们要知道 lambda +是一个表达式,而不是一个语句。正因为这个特点,我们可以在一些特殊的场景中去使用它。具体是什么场景呢?接下来我们会介绍到几个非常好用的内置函数。 + +2. map 函数 +----------- + +map +函数,它接收两个参数,第一个参数是一个函数对象(当然也可以是一个lambda表达式),第二个参数是一个序列。 + +它可以实现怎样的功能呢,我举个例子你就明白了。 + +:: + + >>> map(lambda x: x*2, [1,2,3,4,5]) + [2, 4, 6, 8, 10] + +可以很清楚地看到,它可以将后面序列中的每一个元素做为参数传入lambda中。 + +当我们不使用 map 函数时,你也许会这样子写。 + +:: + + mylist=[] + for i in [1,2,3,4,5]: + mylist.append(i*2) + +3. filter 函数 +-------------- + +filter 函数,和 map 函数相似。同样也是接收两个参数,一个lambda +表达式,一个序列。它会遍历后面序列中每一个元素,并将其做为参数传入lambda表达式中,当表达式返回 +True,则元素会被保留下来,当表达式返回 False ,则元素会被丢弃。 + +下面这个例子,将过滤出一个列表中小于0的元素。 + +:: + + >>>filter(lambda x: x < 0, range(-5, 5)) + [-5, -4, -3, -2, -1] + +4. reduce 函数 +-------------- + +reduce 函数,也是类似的。它的作用是先对序列中的第 1、2 +个元素进行操作,得到的结果再与第三个数据用 lambda +函数运算,将其得到的结果再与第四个元素进行运算,以此类推下去直到后面没有元素了。 + +|image1| + +这边举个例子你也就明白了。 + +:: + + >>>reduce(lambda x,y: x+y, [1,2,3,4,5]) + 15 + +它的运算过程分解一下是这样的。 + +:: + + 1+2=3 + 3+3=6 + 6+4+10 + 10+5=15 + +5. 注意点 +--------- + +以上几个函数,熟练的掌握它们的写法,可以让我们的代码看起来更加的 +Pythonic ,在某一程度上代码看起来更加的简洁。 + +如果你是新手呢,你需要注意的是,以上示例是在 Python2.x +环境下演示的。而在 Python3.x 中,却有所不同,你可以自己尝试一下。 + +这里总结一下: + +第一点,map 和 filter +函数返回的都不再是一个列表,而是一个迭代器对象。这里以map为例 + +:: + + >>> map_obj = map(lambda x: x*2, [1,2,3,4,5]) + >>> from collections.abc import Iterator + >>> isinstance(map_obj, Iterator) + True + >>> next(map_obj) + 2 + >>> list(map_obj) + [4, 6, 8, 10] + +第二点,reduce 不可以直接调用,而是要先导入才能使用, + +:: + + from functools import reduce + +-------------- + +|image2| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200930175131.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c03/c03_11.md b/source/c03/c03_11.md new file mode 100644 index 0000000..aa4a66c --- /dev/null +++ b/source/c03/c03_11.md @@ -0,0 +1,198 @@ +# 3.11 with 与 上下文管理器 + +![](http://image.iswbm.com/20200602135014.png) + +`with` 这个关键字,对于每一学习Python的人,都不会陌生。 + +操作文本对象的时候,几乎所有的人都会让我们要用 `with open` ,这就是一个上下文管理的例子。你一定已经相当熟悉了,我就不再废话了。 + +```python +with open('test.txt') as f: + print f.readlines() +``` + +## 1. what context manager? + +**基本语法** + +```python +with EXPR as VAR: + BLOCK +``` + +先理清几个概念 + +``` +1. 上下文表达式:with open('test.txt') as f: +2. 上下文管理器:open('test.txt') +3. f 不是上下文管理器,应该是资源对象。 +``` + +## 2. how context manager? + +要自己实现这样一个上下文管理,要先知道上下文管理协议。 + +简单点说,就是在一个类里,实现了`__enter__`和`__exit__`的方法,这个类的实例就是一个上下文管理器。 + +例如这个示例: + +```python +class Resource(): + def __enter__(self): + print('===connect to resource===') + return self + def __exit__(self, exc_type, exc_val, exc_tb): + print('===close resource connection===') + + def operate(self): + print('===in operation===') + +with Resource() as res: + res.operate() +``` + +我们执行一下,通过日志的打印顺序。可以知道其执行过程。 + +``` +===connect to resource=== +===in operation=== +===close resource connection=== +``` + +从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在`__enter__`中,而将资源的关闭写在`__exit__` 中。 + +## 3. why context manager? + +学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。 + +为什么要使用上下文管理器? + +在我看来,这和 Python 崇尚的优雅风格有关。 + +1. 可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接; +2. 可以以一种更加优雅的方式,处理异常; + +第一种,我们上面已经以资源的连接为例讲过了。 + +而第二种,会被大多数人所忽略。这里会重点讲一下。 + +大家都知道,处理异常,通常都是使用 `try...execept..` 来捕获处理的。这样做一个不好的地方是,在代码的主逻辑里,会有大量的异常处理代理,这会很大的影响我们的可读性。 + +好一点的做法呢,可以使用 `with` 将异常的处理隐藏起来。 + +仍然是以上面的代码为例,我们将`1/0` 这个`一定会抛出异常的代码`写在 `operate` 里 + +```python +class Resource(): + def __enter__(self): + print('===connect to resource===') + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + print('===close resource connection===') + return True + + def operate(self): + 1/0 + +with Resource() as res: + res.operate() +``` + +运行一下,惊奇地发现,居然不会报错。 + +这就是上下文管理协议的一个强大之处,异常可以在`__exit__` 进行捕获并由你自己决定如何处理,是抛出呢还是在这里就解决了。在`__exit__` 里返回 `True`(没有return 就默认为 return False),就相当于告诉 Python解释器,这个异常我们已经捕获了,不需要再往外抛了。 + +在 写`__exit__` 函数时,需要注意的事,它必须要有这三个参数: + +- exc_type:异常类型 +- exc_val:异常值 +- exc_tb:异常的错误栈信息 + +当主逻辑代码没有报异常时,这三个参数将都为None。 + +## 4. how contextlib? + +在上面的例子中,我们只是为了构建一个上下文管理器,却写了一个类。如果只是要实现一个简单的功能,写一个类未免有点过于繁杂。这时候,我们就想,如果只写一个函数就可以实现上下文管理器就好了。 + +这个点Python早就想到了。它给我们提供了一个装饰器,你只要按照它的代码协议来实现函数内容,就可以将这个函数对象变成一个上下文管理器。 + +我们按照 contextlib 的协议来自己实现一个打开文件(with open)的上下文管理器。 + +```python +import contextlib + +@contextlib.contextmanager +def open_func(file_name): + # __enter__方法 + print('open file:', file_name, 'in __enter__') + file_handler = open(file_name, 'r') + + # 【重点】:yield + yield file_handler + + # __exit__方法 + print('close file:', file_name, 'in __exit__') + file_handler.close() + return + +with open_func('/Users/MING/mytest.txt') as file_in: + for line in file_in: + print(line) +``` + +在被装饰函数里,必须是一个生成器(带有yield),而yield之前的代码,就相当于`__enter__`里的内容。yield 之后的代码,就相当于`__exit__` 里的内容。 + +上面这段代码只能实现上下文管理器的第一个目的(管理资源),并不能实现第二个目的(处理异常)。 + +如果要处理异常,可以改成下面这个样子。 + +```python +import contextlib + +@contextlib.contextmanager +def open_func(file_name): + # __enter__方法 + print('open file:', file_name, 'in __enter__') + file_handler = open(file_name, 'r') + + try: + yield file_handler + except Exception as exc: + # deal with exception + print('the exception was thrown') + finally: + print('close file:', file_name, 'in __exit__') + file_handler.close() + + return + +with open_func('/Users/MING/mytest.txt') as file_in: + for line in file_in: + 1/0 + print(line) +``` + +好像只要讲到上下文管理器,大多数人都会谈到打开文件这个经典的例子。 + +但是在实际开发中,可以使用到上下文管理器的例子也不少。我这边举个我自己的例子。 + +在OpenStack中,给一个虚拟机创建快照时,需要先创建一个临时文件夹,来存放这个本地快照镜像,等到本地快照镜像创建完成后,再将这个镜像上传到Glance。然后删除这个临时目录。 + +这段代码的主逻辑是`创建快照`,而`创建临时目录 `,属于前置条件,`删除临时目录`,是收尾工作。 + +虽然代码量很少,逻辑也不复杂,但是“`创建临时目录,使用完后再删除临时目录`”这个功能,在一个项目中很多地方都需要用到,如果可以将这段逻辑处理写成一个工具函数作为一个上下文管理器,那代码的复用率也大大提高。 + +代码是这样的 + +![](http://image.iswbm.com/20190310172800.png) + +总结起来,使用上下文管理器有三个好处: + +1. 提高代码的复用率; +2. 提高代码的优雅度; +3. 提高代码的可读性; + +------ + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c03/c03_11.rst b/source/c03/c03_11.rst new file mode 100644 index 0000000..179f1e0 --- /dev/null +++ b/source/c03/c03_11.rst @@ -0,0 +1,217 @@ +3.11 with 与 上下文管理器 +========================= + +|image0| + +``with`` 这个关键字,对于每一学习Python的人,都不会陌生。 + +操作文本对象的时候,几乎所有的人都会让我们要用 ``with open`` +,这就是一个上下文管理的例子。你一定已经相当熟悉了,我就不再废话了。 + +.. code:: python + + with open('test.txt') as f: + print f.readlines() + +1. what context manager? +------------------------- + +**基本语法** + +.. code:: python + + with EXPR as VAR: + BLOCK + +先理清几个概念 + +:: + + 1. 上下文表达式:with open('test.txt') as f: + 2. 上下文管理器:open('test.txt') + 3. f 不是上下文管理器,应该是资源对象。 + +2. how context manager? +------------------------ + +要自己实现这样一个上下文管理,要先知道上下文管理协议。 + +简单点说,就是在一个类里,实现了\ ``__enter__``\ 和\ ``__exit__``\ 的方法,这个类的实例就是一个上下文管理器。 + +例如这个示例: + +.. code:: python + + class Resource(): + def __enter__(self): + print('===connect to resource===') + return self + def __exit__(self, exc_type, exc_val, exc_tb): + print('===close resource connection===') + + def operate(self): + print('===in operation===') + + with Resource() as res: + res.operate() + +我们执行一下,通过日志的打印顺序。可以知道其执行过程。 + +:: + + ===connect to resource=== + ===in operation=== + ===close resource connection=== + +从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在\ ``__enter__``\ 中,而将资源的关闭写在\ ``__exit__`` +中。 + +3. why context manager? +------------------------ + +学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。 + +为什么要使用上下文管理器? + +在我看来,这和 Python 崇尚的优雅风格有关。 + +1. 可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接; +2. 可以以一种更加优雅的方式,处理异常; + +第一种,我们上面已经以资源的连接为例讲过了。 + +而第二种,会被大多数人所忽略。这里会重点讲一下。 + +大家都知道,处理异常,通常都是使用 ``try...execept..`` +来捕获处理的。这样做一个不好的地方是,在代码的主逻辑里,会有大量的异常处理代理,这会很大的影响我们的可读性。 + +好一点的做法呢,可以使用 ``with`` 将异常的处理隐藏起来。 + +仍然是以上面的代码为例,我们将\ ``1/0`` +这个\ ``一定会抛出异常的代码``\ 写在 ``operate`` 里 + +.. code:: python + + class Resource(): + def __enter__(self): + print('===connect to resource===') + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + print('===close resource connection===') + return True + + def operate(self): + 1/0 + + with Resource() as res: + res.operate() + +运行一下,惊奇地发现,居然不会报错。 + +这就是上下文管理协议的一个强大之处,异常可以在\ ``__exit__`` +进行捕获并由你自己决定如何处理,是抛出呢还是在这里就解决了。在\ ``__exit__`` +里返回 ``True``\ (没有return 就默认为 return False),就相当于告诉 +Python解释器,这个异常我们已经捕获了,不需要再往外抛了。 + +在 写\ ``__exit__`` 函数时,需要注意的事,它必须要有这三个参数: + +- exc_type:异常类型 +- exc_val:异常值 +- exc_tb:异常的错误栈信息 + +当主逻辑代码没有报异常时,这三个参数将都为None。 + +4. how contextlib? +------------------ + +在上面的例子中,我们只是为了构建一个上下文管理器,却写了一个类。如果只是要实现一个简单的功能,写一个类未免有点过于繁杂。这时候,我们就想,如果只写一个函数就可以实现上下文管理器就好了。 + +这个点Python早就想到了。它给我们提供了一个装饰器,你只要按照它的代码协议来实现函数内容,就可以将这个函数对象变成一个上下文管理器。 + +我们按照 contextlib 的协议来自己实现一个打开文件(with +open)的上下文管理器。 + +.. code:: python + + import contextlib + + @contextlib.contextmanager + def open_func(file_name): + # __enter__方法 + print('open file:', file_name, 'in __enter__') + file_handler = open(file_name, 'r') + + # 【重点】:yield + yield file_handler + + # __exit__方法 + print('close file:', file_name, 'in __exit__') + file_handler.close() + return + + with open_func('/Users/MING/mytest.txt') as file_in: + for line in file_in: + print(line) + +在被装饰函数里,必须是一个生成器(带有yield),而yield之前的代码,就相当于\ ``__enter__``\ 里的内容。yield +之后的代码,就相当于\ ``__exit__`` 里的内容。 + +上面这段代码只能实现上下文管理器的第一个目的(管理资源),并不能实现第二个目的(处理异常)。 + +如果要处理异常,可以改成下面这个样子。 + +.. code:: python + + import contextlib + + @contextlib.contextmanager + def open_func(file_name): + # __enter__方法 + print('open file:', file_name, 'in __enter__') + file_handler = open(file_name, 'r') + + try: + yield file_handler + except Exception as exc: + # deal with exception + print('the exception was thrown') + finally: + print('close file:', file_name, 'in __exit__') + file_handler.close() + + return + + with open_func('/Users/MING/mytest.txt') as file_in: + for line in file_in: + 1/0 + print(line) + +好像只要讲到上下文管理器,大多数人都会谈到打开文件这个经典的例子。 + +但是在实际开发中,可以使用到上下文管理器的例子也不少。我这边举个我自己的例子。 + +在OpenStack中,给一个虚拟机创建快照时,需要先创建一个临时文件夹,来存放这个本地快照镜像,等到本地快照镜像创建完成后,再将这个镜像上传到Glance。然后删除这个临时目录。 + +这段代码的主逻辑是\ ``创建快照``\ ,而\ ``创建临时目录``\ ,属于前置条件,\ ``删除临时目录``\ ,是收尾工作。 + +虽然代码量很少,逻辑也不复杂,但是“``创建临时目录,使用完后再删除临时目录``”这个功能,在一个项目中很多地方都需要用到,如果可以将这段逻辑处理写成一个工具函数作为一个上下文管理器,那代码的复用率也大大提高。 + +代码是这样的 + +|image1| + +总结起来,使用上下文管理器有三个好处: + +1. 提高代码的复用率; +2. 提高代码的优雅度; +3. 提高代码的可读性; + +-------------- + +|image2| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20190310172800.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c03/c03_12.md b/source/c03/c03_12.md new file mode 100644 index 0000000..4829596 --- /dev/null +++ b/source/c03/c03_12.md @@ -0,0 +1,106 @@ +# 3.12 静态方法其实暗藏玄机 + +![](http://image.iswbm.com/20200602135014.png) + +静态方法,应该没人不知道吧? + +它可以算是一个很基础、简单的知识点。 + +但是就是这样一个知识点,也有不少可以探究的东西。 + +不信我问你三个问题,你看能否答上来。 + +1. Python2.x和3.x中,函数和方法的区分有什么不同? +2. 有了类/实例方法和普通函数,为什么还会有静态方法? +3. Python3.x 中,静态方法有几种写法? + +如果你没能答上来,那你可以尝试在下文中寻找答案。 + +--- + +在 Python 2 中的函数和方法的区别,十分清晰,很好分辨。但在 Python3中,我却发现完全又是另一套准则。 + +首先先来 Python2 的(以下在 Python2.7中测试通过) + +![](http://image.iswbm.com/20190630111243.png) + +可以得出结论: + +1、普通函数(未定位在类里),都是函数。 + +2、静态方法(@staticmethod),都是函数。 + +3、类方法(@classmethod),都是方法。 + +4、实例方法(首参数为self且非静态、非类方法的),都是方法。 + +你一定想说,类方法和实例方法,是方法没错呀,毕竟名字本身就有方法,普通函数是函数,我也理解呀。那静态方法,为什么不是方法而是函数呢? + +名字只是一个外在的表面称呼,你能说「赵铁男」就一定是汉子吗? + +我的理解是:方法是一种和对象(实例或者类)绑定后的函数。 + +类方法的首参是`cls`,调用时,无需显示传入。实例方法首参是self,调用时,也无需显示传入。 + +而静态方法,其实和普通函数没啥区别,唯一的区别就是,他定义的位置被放在了类里,做为类或者实例的一个函数。 + +那你肯定又要问了,既然静态方法和普通函数都是一样的,为什么要刻意将它放在类里呢? + +我上面说了,放在类里定义,就可以让它成为类的一个工具函数,这就像你身上随身携带了一把刀,这把刀与你本人并没有什么直接关系,唯一的联系就是这把刀是你的,而你把它带在身上,无论你去到哪里,只要需要,你就可以直接拿出来用上。对比将函数放在类外面,缺点是什么呢?就是当你出门在外(在别的模块里)发现你要用刀的时候,还要特地跑一趟去商店买一把刀(import 这个函数)。 + +另外,我觉得静态方法在业务和设计上的意义,会更多一些。 + +一般静态方法是做为类或者实例的一个工具函数,比如对变量的做一个合法性的检验,对数据进行序列化反序列化操作等等。 + +说完了 Python2 ,再来说说Python3. + +以前我觉得 Python2 对于方法和函数的界线更加清晰,但接触了 Python3,我反而觉得Python3里方法和函数的区分似乎更加合理。 + +还是刚刚那段代码,我更改了解释器为Python3.6(以下在 Python3.6中测试通过) + +![](http://image.iswbm.com/20190630104956.png) + +和Python2的唯一区别是,`People.jump` 在Python3 中变成了函数。 + +这一下颠覆了你刚刚才建立起来的知识体系有木有? + +先别急,我再做个试验,也许你就知道了。 + +**在 Python2中** + +执行People.jump('hello'),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。 + +![](http://image.iswbm.com/20190630105735.png) + +**在 Python3中** + +你可以发现,这里的jump的首参不再要求是 People 的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。 + +![](http://image.iswbm.com/20190630105600.png) + +也就是说,当你往jump中传入的首参为People的实例时,jump 就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。 + +你看,多么灵活呀。 + +再总结一下,在Python3中: + +1、普通函数(未定位在类里),都是函数。 + +2、静态方法(@staticmethod),都是函数。 + +3、类方法(@classmethod),都是方法。 + +4、方法和函数区分没有那么明确,而是更加灵活了,一个函数有可能是方法也有可能是函数。 + +你肯定又要问了,那这是不是就意味着,Python3 中静态方法,可以不用再使用@staticmethod 装饰了呢,反正Python3都可以识别。 + +这是个好问题,是的,可以不用指定,但是最好指定,如果你不指定,你调用这个方法只能通过People.jump,而不能通过 self.jump了,因为首参不是 self,而如果使用@staticmethod 就可以使用self.jump。 + +所以说这是一个规范,就像类的私有方法,规范要求外部最好不要调用,但这不是强制要求,不是说外部就不能调用。 + +写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 + + + +![](http://image.iswbm.com/20200607174235.png) + diff --git a/source/c03/c03_12.rst b/source/c03/c03_12.rst new file mode 100644 index 0000000..99e9dae --- /dev/null +++ b/source/c03/c03_12.rst @@ -0,0 +1,122 @@ +3.12 静态方法其实暗藏玄机 +========================= + +|image0| + +静态方法,应该没人不知道吧? + +它可以算是一个很基础、简单的知识点。 + +但是就是这样一个知识点,也有不少可以探究的东西。 + +不信我问你三个问题,你看能否答上来。 + +1. Python2.x和3.x中,函数和方法的区分有什么不同? +2. 有了类/实例方法和普通函数,为什么还会有静态方法? +3. Python3.x 中,静态方法有几种写法? + +如果你没能答上来,那你可以尝试在下文中寻找答案。 + +-------------- + +在 Python 2 中的函数和方法的区别,十分清晰,很好分辨。但在 +Python3中,我却发现完全又是另一套准则。 + +首先先来 Python2 的(以下在 Python2.7中测试通过) + +|image1| + +可以得出结论: + +1、普通函数(未定位在类里),都是函数。 + +2、静态方法(@staticmethod),都是函数。 + +3、类方法(@classmethod),都是方法。 + +4、实例方法(首参数为self且非静态、非类方法的),都是方法。 + +你一定想说,类方法和实例方法,是方法没错呀,毕竟名字本身就有方法,普通函数是函数,我也理解呀。那静态方法,为什么不是方法而是函数呢? + +名字只是一个外在的表面称呼,你能说「赵铁男」就一定是汉子吗? + +我的理解是:方法是一种和对象(实例或者类)绑定后的函数。 + +类方法的首参是\ ``cls``\ ,调用时,无需显示传入。实例方法首参是self,调用时,也无需显示传入。 + +而静态方法,其实和普通函数没啥区别,唯一的区别就是,他定义的位置被放在了类里,做为类或者实例的一个函数。 + +那你肯定又要问了,既然静态方法和普通函数都是一样的,为什么要刻意将它放在类里呢? + +我上面说了,放在类里定义,就可以让它成为类的一个工具函数,这就像你身上随身携带了一把刀,这把刀与你本人并没有什么直接关系,唯一的联系就是这把刀是你的,而你把它带在身上,无论你去到哪里,只要需要,你就可以直接拿出来用上。对比将函数放在类外面,缺点是什么呢?就是当你出门在外(在别的模块里)发现你要用刀的时候,还要特地跑一趟去商店买一把刀(import +这个函数)。 + +另外,我觉得静态方法在业务和设计上的意义,会更多一些。 + +一般静态方法是做为类或者实例的一个工具函数,比如对变量的做一个合法性的检验,对数据进行序列化反序列化操作等等。 + +说完了 Python2 ,再来说说Python3. + +以前我觉得 Python2 对于方法和函数的界线更加清晰,但接触了 +Python3,我反而觉得Python3里方法和函数的区分似乎更加合理。 + +还是刚刚那段代码,我更改了解释器为Python3.6(以下在 +Python3.6中测试通过) + +|image2| + +和Python2的唯一区别是,\ ``People.jump`` 在Python3 中变成了函数。 + +这一下颠覆了你刚刚才建立起来的知识体系有木有? + +先别急,我再做个试验,也许你就知道了。 + +**在 Python2中** + +执行People.jump(‘hello’),会报错说,jump的首参必须为People的实例对象,这可以理解,毕竟jump定义时,第一个参数为self。 + +|image3| + +**在 Python3中** + +你可以发现,这里的jump的首参不再要求是 People +的一个实例,而可以是任意的对象,比如我使用字符串对象,也没有报错。 + +|image4| + +也就是说,当你往jump中传入的首参为People的实例时,jump +就是方法,而当你传入的首参不是People的实例对象时,jump就是函数。 + +你看,多么灵活呀。 + +再总结一下,在Python3中: + +1、普通函数(未定位在类里),都是函数。 + +2、静态方法(@staticmethod),都是函数。 + +3、类方法(@classmethod),都是方法。 + +4、方法和函数区分没有那么明确,而是更加灵活了,一个函数有可能是方法也有可能是函数。 + +你肯定又要问了,那这是不是就意味着,Python3 +中静态方法,可以不用再使用@staticmethod +装饰了呢,反正Python3都可以识别。 + +这是个好问题,是的,可以不用指定,但是最好指定,如果你不指定,你调用这个方法只能通过People.jump,而不能通过 +self.jump了,因为首参不是 self,而如果使用@staticmethod +就可以使用self.jump。 + +所以说这是一个规范,就像类的私有方法,规范要求外部最好不要调用,但这不是强制要求,不是说外部就不能调用。 + +写这篇文章的起源,是前两天有位读者在交流里问到了相关的问题,正好没什么主题可以写,就拿过来做为素材整理一下,也正好没有写过静态方法、类方法的内容,没想到简单的东西,也能写出这么多的内容出来。 + +|image5| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20190630111243.png +.. |image2| image:: http://image.iswbm.com/20190630104956.png +.. |image3| image:: http://image.iswbm.com/20190630105735.png +.. |image4| image:: http://image.iswbm.com/20190630105600.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c03/c03_13.md b/source/c03/c03_13.md new file mode 100644 index 0000000..32e911b --- /dev/null +++ b/source/c03/c03_13.md @@ -0,0 +1,138 @@ +# 3.13 包导入的三个冷门知识点 + +![](http://image.iswbm.com/20200602135014.png) + +## 1. 使用 \__all__ 控制可被导入的变量 + +使用 `from module import *` 默认情况下会导入 module 里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用 `__all__` 来控制想要被其他模块导入的变量。 + +```python +# profile.py +name='wangbm' +age=27 +gender='male' + +__all__=['name'] +``` + +打开 python console 验证一下 + +```python +>>> from profile import * +>>> print(name) +wangbm +>>> print(age) +Traceback (most recent call last): + File "", line 1, in +NameError: name 'age' is not defined +>>> print(gender) +Traceback (most recent call last): + File "", line 1, in +NameError: name 'gender' is not defined +``` + +`__all__` 仅对于使用`from module import *` 这种情况适用。 + +它经常在一个包的 `__init__.py` 中出现。 + + + +## 2. 命名空间包的神奇之处 + +命名空间包,一个陌生的名字。 + +与我们熟悉的常规包不同的是,它没有 `__init__.py` 文件。 + +更为特殊的是,它可以跨空间地将两个不相邻的子包,合并成一个虚拟机的包,我们将其称之为 `命名空间包`。 + +例如,一个项目的部分代码布局如下 + +``` +foo-package/ + spam/ + blah.py + +bar-package/ + spam/ + grok.py +``` + +在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有\__init__.py文件。 + +让我们看看,如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么? + +```python +>>> import sys +>>> sys.path.extend(['foo-package', 'bar-package']) +>>> import spam.blah +>>> import spam.grok +>>> +``` + + + +当一个包为命名空间包时,他就不再和常规包一样具有 `__file_` 属性,取而代之的是 `__path__` + +```python +>>> import sys +>>> sys.path.extend(['foo-package', 'bar-package']) +>>> import spam.blah +>>> import spam.grok +>>> spam.__path__ +_NamespacePath(['foo-package/spam', 'bar-package/spam']) +>>> spam.__file__ +Traceback (most recent call last): + File "", line 1, in +AttributeError: 'module' object has no attribute '__file__' +``` + + + +## 3. 模块重载中的一个坑 + +由于有 sys.modules 的存在,当你导入一个已导入的模块时,实际上是没有效果的。 + +为了达到模块的重载,有的人会将已导入的包从 sys.modules 中移除后再导入 + +就像下面这样子 + +``` +>>> import foo.bar +successful to be imported +>>> +>>> import foo.bar +>>> +>>> import sys +>>> sys.modules['foo.bar'] + +>>> del sys.modules['foo.bar'] +>>> +>>> import foo.bar +successful to be imported +``` + +上面的例子里我使用的是`import foo.bar` ,如果你使用的是 `from foo import bar` 这种导入形式,会发现重载是同样是无效的。 + +这应该算是一个小坑,不知道的人,会掉入坑中爬不出来。 + +``` +>>> import foo.bar +successful to be imported +>>> +>>> import foo.bar +>>> +>>> import sys +>>> del sys.modules['foo.bar'] +>>> from foo import bar +>>> +``` + + + +因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 + + + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c03/c03_13.rst b/source/c03/c03_13.rst new file mode 100644 index 0000000..d68db2f --- /dev/null +++ b/source/c03/c03_13.rst @@ -0,0 +1,140 @@ +3.13 包导入的三个冷门知识点 +=========================== + +|image0| + +1. 使用 \__all_\_ 控制可被导入的变量 +------------------------------------ + +使用 ``from module import *`` 默认情况下会导入 module +里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用 +``__all__`` 来控制想要被其他模块导入的变量。 + +.. code:: python + + # profile.py + name='wangbm' + age=27 + gender='male' + + __all__=['name'] + +打开 python console 验证一下 + +.. code:: python + + >>> from profile import * + >>> print(name) + wangbm + >>> print(age) + Traceback (most recent call last): + File "", line 1, in + NameError: name 'age' is not defined + >>> print(gender) + Traceback (most recent call last): + File "", line 1, in + NameError: name 'gender' is not defined + +``__all__`` 仅对于使用\ ``from module import *`` 这种情况适用。 + +它经常在一个包的 ``__init__.py`` 中出现。 + +2. 命名空间包的神奇之处 +----------------------- + +命名空间包,一个陌生的名字。 + +与我们熟悉的常规包不同的是,它没有 ``__init__.py`` 文件。 + +更为特殊的是,它可以跨空间地将两个不相邻的子包,合并成一个虚拟机的包,我们将其称之为 +``命名空间包``\ 。 + +例如,一个项目的部分代码布局如下 + +:: + + foo-package/ + spam/ + blah.py + + bar-package/ + spam/ + grok.py + +在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。 + +让我们看看,如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么? + +.. code:: python + + >>> import sys + >>> sys.path.extend(['foo-package', 'bar-package']) + >>> import spam.blah + >>> import spam.grok + >>> + +当一个包为命名空间包时,他就不再和常规包一样具有 ``__file_`` +属性,取而代之的是 ``__path__`` + +.. code:: python + + >>> import sys + >>> sys.path.extend(['foo-package', 'bar-package']) + >>> import spam.blah + >>> import spam.grok + >>> spam.__path__ + _NamespacePath(['foo-package/spam', 'bar-package/spam']) + >>> spam.__file__ + Traceback (most recent call last): + File "", line 1, in + AttributeError: 'module' object has no attribute '__file__' + +3. 模块重载中的一个坑 +--------------------- + +由于有 sys.modules +的存在,当你导入一个已导入的模块时,实际上是没有效果的。 + +为了达到模块的重载,有的人会将已导入的包从 sys.modules 中移除后再导入 + +就像下面这样子 + +:: + + >>> import foo.bar + successful to be imported + >>> + >>> import foo.bar + >>> + >>> import sys + >>> sys.modules['foo.bar'] + + >>> del sys.modules['foo.bar'] + >>> + >>> import foo.bar + successful to be imported + +上面的例子里我使用的是\ ``import foo.bar`` ,如果你使用的是 +``from foo import bar`` 这种导入形式,会发现重载是同样是无效的。 + +这应该算是一个小坑,不知道的人,会掉入坑中爬不出来。 + +:: + + >>> import foo.bar + successful to be imported + >>> + >>> import foo.bar + >>> + >>> import sys + >>> del sys.modules['foo.bar'] + >>> from foo import bar + >>> + +因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。 + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c03/c03_14.md b/source/c03/c03_14.md new file mode 100644 index 0000000..33029d6 --- /dev/null +++ b/source/c03/c03_14.md @@ -0,0 +1,651 @@ +# 3.14 全面学习 Python 包:包的构建与分发 + +![](http://image.iswbm.com/20200602135014.png) + +## 1. 为什么需要对项目分发打包? + +平常我们习惯了使用 pip 来安装一些第三方模块,这个安装过程之所以简单,是因为模块开发者为我们默默地为我们做了所有繁杂的工作,而这个过程就是 `打包`。 + +打包,就是将你的源代码进一步封装,并且将所有的项目部署工作都事先安排好,这样使用者拿到后即装即用,不用再操心如何部署的问题(如果你不想对照着一堆部署文档手工操作的话)。 + +不管你是在工作中,还是业余准备自己写一个可以上传到 PyPI 的项目,你都要学会如何打包你的项目。 + +Python 发展了这么些年了,项目打包工具也已经很成熟了。他们都有哪些呢? + +你可能听过 `disutils`、 `distutils` 、`distutils2`、`setuptools`等等,好像很熟悉,却又很陌生,他们都是什么关系呢? + +## 2. 包分发的始祖:distutils + +`distutils` 是 Python 的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是 Python 官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的。 + +`distutils` 的精髓在于编写 setup.py,它是模块分发与安装的指导文件。 + +那么如何编写 setup.py 呢?这里面的内容非常多,我会在后面进行详细的解析,请你耐心往下看。 + +你有可能没写过 setup.py ,但你绝对使用过 setup.py 来做一些事情,比如下面这条命令,我们经常用它来进行模块的安装。 + +```shell +$ python setup.py install +``` + +这样的安装方法是通过源码安装,与之对应的是通过二进制软件包的安装,同样我也会在后面进行介绍。 + +## 3. 分发工具升级:setuptools + +`setuptools` 是 distutils 增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。 + + **distribute**,或许你在其他地方也见过它,这里也提一下。 + +distribute 是 setuptools 有一个分支版本,分支的原因可能是有一部分开发者认为 setuptools 开发太慢了。但现在,distribute 又合并回了 setuptools 中。因此,我们可以认为它们是同一个东西。 + +还有一个大包分发工具是 **distutils2**,其试图尝试充分利用distutils,detuptools 和 distribute 并成为 Python 标准库中的标准工具。但该计划并没有达到预期的目的,且已经是一个废弃的项目。 + +因此,setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。 + +那么如何在一个干净的环境中安装 setuptools 呢? + +主要有两种方法: + +- 源码安装:在 https://pypi.org/project/setuptools/#files 中下载 zip 包 解压执行 `python setup.py install` 安装 +- 通过引导程序安装:下载引导程序,它可以用来下载或者更新最新版本的 setuptools + +```shell +$ wget http://peak.telecommunity.com/dist/ez_setup.py + +# 安装 +$ python ez_setup.py + +# 更新,以下两种任选 +$ python ez_setup.py –U setuptools +$ pip install -U setuptools +``` + + +## 4. easy_install 使用指南 + +当你安装完 setuptools 后,就拥有了一个叫做 `easy_install` 的第三方管理工具,这也是它区分于 distutils 的一大改进。 + +这里简单介绍一下它的用法,虽然它已经用得非常少了。 + +先是包的安装 + +```shell +# 通过包名,从PyPI寻找最新版本,自动下载、编译、安装 +$ easy_install pkg_name + +# 通过包名从指定下载页寻找链接来安装或升级包 +$ easy_install -f http://pythonpaste.org/package_index.html + +# 指定线上的包地址安装 +$ easy_install http://example.com/path/to/MyPackage-1.2.3.tgz + +# 从本地的 .egg 文件安装 +$ easy_install xxx.egg + +# 在安装时你可以添加额外的参数 +指定安装目录:--install-dir=DIR, -d DIR +指定用户安装:--user +``` + +再者是包的升级 + +```shell +# 从 pypi 中搜索并升级包 +$ easy_install --upgrade pkg_name + +# 指定版本进行升级 +$ easy_install "SomePackage==2.0" +``` + +最后是包的删除 + +```shell +$ easy_install -m pkg_name +``` + +需要注意的是,这样的删除,仅是在 easy-install.pth 文件中删除,使其不能在 python 中使用 这个模块,但实际的包还在你的电脑中,若要删除彻底,需要你手动删除相关的 .egg 及 其他文件。 + + + +默认情况下,easy_install 只会从 pypi 上下载相关软件包,由于这个源在国外,下载包的速度并不理想,使用过pip的朋友自然会想,easy_install 是否能指定源进行安装呢? + +答案是,可以的。 + +编辑配置文件 `/root/.pydistutils.cfg` + +```ini +[easy_install] +index-url=http://mirrors.aliyun.com/pypi/simple/ +find-links=http://mirrors.aliyun.com/pypi/simple/ +``` + +以上仅介绍了 easy_install 的一些常用的方法,想要了解更多,你可以点击官方文档:https://setuptools.readthedocs.io/en/latest/easy_install.html + + + +总结一句:setuptools 是官方提供的一个专业用于包分发的工具,若只从安装的角度来看,它的功能确实简单。它更大的意义是对包的分发很有用,定制化程序非常高,我们现在也还在用它进行版本包的发布。 + + + +## 5. 源码包与二进制包什么区别? + +Python 包的分发可以分为两种: + +1. 以源码包的方式发布 + +源码包安装的过程,是先解压,再编译,最后才安装,所以它是跨平台的,由于每次安装都要进行编译,相对二进包安装方式来说安装速度较慢。 + +源码包的本质是一个压缩包,其常见的格式有: + +![](http://image.iswbm.com/20191218202833.png) + +2. 以二进制包形式发布 + +二进制包的安装过程省去了编译的过程,直接进行解压安装,所以安装速度较源码包来说更快。 + +由于不同平台的编译出来的包无法通用,所以在发布时,需事先编译好多个平台的包。 + +二进制包的常见格式有: + +![](http://image.iswbm.com/20191218203005.png) + +## 6. eggs 与 wheels 有什么区别? + +Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012 年定义。Wheel 的出现是为了替代 Egg,它的本质是一个zip包,其现在被认为是 Python 的二进制包的标准格式。 + +以下是 Wheel 和 Egg 的主要区别: + +- Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义 +- Wheel 是一种分发格式,即打包格式。而 Egg 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import +- Wheel 文件不会包含 .pyc 文件 +- Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info 目录 +- Wheel 有着更丰富的命名规则。 +- Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现 +- Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易 + +wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块,然后再使用 pip 的命令。 + +```shell +$ pip install wheel +$ pip wheel --wheel-dir=/local/wheels pkg +``` + + + +## 7. 超详细讲解 setup.py 的编写? + +打包分发最关键的一步是编写 `setup.py` 文件。 + +以下是一个 setup.py 简单的使用示例 + +```python +from setuptools import setup, find_packages + +setup( + name="mytest", + version="1.0", + author="wangbm", + author_email="wongbingming@163.com", + description="Learn to Pack Python Module -->公众号:Python编程时光", + + # 项目主页 + url="http://iswbm.com/", + + # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包 + packages=find_packages() +) +``` + +接下来,我将慢慢扩充这个setup函数,增加更多的参数,以便你能理解setup函数能做哪些事情。 + +**程序分类信息** + +`classifiers` 参数说明包的分类信息。所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers + +示例: + +```python +from setuptools import setup, find_packages + +setup( + classifiers = [ + # 发展时期,常见的如下 + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 3 - Alpha', + + # 开发的目标用户 + 'Intended Audience :: Developers', + + # 属于什么类型 + 'Topic :: Software Development :: Build Tools', + + # 许可证信息 + 'License :: OSI Approved :: MIT License', + + # 目标 Python 版本 + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ] +) +``` + + + +**关于文件的分发** + +```python +from setuptools import setup, find_packages + + +setup( + name="mytest", + version="1.0", + author="wangbm", + author_email="wongbingming@163.com", + description="Learn to Pack Python Module", + url="http://iswbm.com/", + packages=find_packages(), + + # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等 + data_files=[ + ('', ['conf/*.conf']), + ('/usr/lib/systemd/system/', ['bin/*.service']), + ], + + # 希望被打包的文件 + package_data={ + '':['*.txt'], + 'bandwidth_reporter':['*.txt'] + }, + # 不打包某些文件 + exclude_package_data={ + 'bandwidth_reporter':['*.txt'] + } +) +``` + +除了以上的参数配置之外,还可以使用一个叫做 `MANIFEST.in` 的文件,来控制文件的分发。 + +如下这是一个 `MANIFEST.in` 的样例: + +``` +include *.txt +recursive-include examples *.txt *.py +prune examples/sample?/build +``` + +这些配置,规定了如下几点 + +- 所有根目录下的以 txt 为后缀名的文件,都会分发 +- 根目录下的 examples 目录 和 txt、py文件都会分发 +- 路径匹配上 examples/sample?/build 不会分发 + +`MANIFEST.in` 需要放在和 setup.py 同级的顶级目录下,setuptools 会自动读取该文件。 + + + +**关于依赖包下载安装** + +```python +from setuptools import setup, find_packages + + +setup( + ... + + # 表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装 + install_requires=['docutils>=0.3'], + + # setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置 + # 这里列出的包,不会自动安装。 + setup_requires=['pbr'], + + # 仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。 + # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。 + tests_require=[ + 'pytest>=3.3.1', + 'pytest-cov>=2.5.1', + ], + + # 用于安装setup_requires或tests_require里的软件包 + # 这些信息会写入egg的 metadata 信息中 + dependency_links=[ + "http://example2.com/p/foobar-1.0.tar.gz", + ], + + # install_requires 在安装模块时会自动安装依赖包 + # 而 extras_require 不会,这里仅表示该模块会依赖这些包 + # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装 + extras_require={ + 'PDF': ["ReportLab>=1.2", "RXP"], + 'reST': ["docutils>=0.3"], + } +) + +``` + +关于 `install_requires`, 有以下五种常用的表示方法: + +1. `'argparse'`,只包含包名。 这种形式只检查包的存在性,不检查版本。 方便,但不利于控制风险。 +2. `'setuptools==38.2.4'`,指定版本。 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。 缺点是不利于更新,每次更新都需要改动代码。 +3. `'docutils >= 0.3'`,这是比较常用的形式。 当对某个库比较信任时,这种形式可以自动保持版本为最新。 +4. `'Django >= 1.11, != 1.11.1, <= 2'`,这是比较复杂的形式。 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。 对于一些大型、复杂的库,这种形式是最合适的。 +5. `'requests[security, socks] >= 2.18.4'`,这是包含了额外的可选依赖的形式。 正常安装requests会自动安装它的`install_requires`中指定的依赖,而不会安装`security`和`socks`这两组依赖。 这两组依赖是定义在它的`extras_require`中。 这种形式,用在深度使用某些库时。 + + + +**关于安装环境的限制** + +有些库并不是在所以的 Python 版本中都适用的,若一个库安装在一个未兼容的 Python 环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。 + +这样的功能,可以使用 `python_requires` 来实现。 + +```python +setup( + ... + python_requires='>=2.7, <=3', +) +``` + + + +**生成可执行文件的分发** + +```python +from setuptools import setup, find_packages + + +setup( + name="mytest", + version="1.0", + author="wangbm", + author_email="wongbingming@163.com", + description="Learn to Pack Python Module", + url="http://iswbm.com/", + packages=find_packages(), + + # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件 + # 该文件入口指向 foo/main.py 的main 函数 + entry_points={ + 'console_scripts': [ + 'foo = foo.main:main' + ] + }, + + # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中 + # 执行 python setup.py install 后 + # 会生成 如 /usr/bin/foo.sh 和 如 /usr/bin/bar.py + scripts=['bin/foo.sh', 'bar.py'] +) +``` + +上面的 scripts 里有的脚本中有 `sh` 和 `py` 后缀,那么安装后,setuptools 会原封不动的移动到 /usr/bin 中,并添加可执行权限。 + +若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做 + +```python +from setuptools.command.install_scripts import install_scripts + +class InstallScripts(install_scripts): + + def run(self): + setuptools.command.install_scripts.install_scripts.run(self) + + # Rename some script files + for script in self.get_outputs(): + if basename.endswith(".py") or basename.endswith(".sh"): + dest = script[:-3] + else: + continue + print("moving %s to %s" % (script, dest)) + shutil.move(script, dest) + +setup( + ... + scripts=['bin/foo.sh', 'bar.py'], + + cmdclass={ + "install_scripts": InstallScripts + } +) +``` + + + +**ext_modules** + +`ext_modules` 参数用于构建 C 和 C++ 扩展扩展包。其是 Extension 实例的列表,每一个 Extension 实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如: + +```python +setup( + # other arguments here... + ext_modules=[ + Extension('foo', + glob(path.join(here, 'src', '*.c')), + libraries = [ 'rt' ], + include_dirs=[numpy.get_include()]) + ] +) +``` + +详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options + + + +**指定release** + +setup.py 里只能指定 version,而不能指定 release,如果你需要变更版本号,可以使用 `--release` 参数进行指定 + +```shell +python setup.py bdist_rpm --release=20200617 +``` + + + +setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数: + +![](http://image.iswbm.com/20191218203255.png) + +更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html + +## 8. 打包辅助神器PBR 是什么? + +`pbr` 是 setuptools 的辅助工具,最初是为 OpenStack 开发(https://launchpad.net/pbr),基于`d2to1`。 + + + +`pbr` 会读取和过滤setup.cfg中的数据,然后将解析后的数据提供给 `setup.py` 作为参数。包含如下功能: + +1. 从git中获取Version、AUTHORS and ChangeLog信息 +2. Sphinx Autodoc。pbr 会扫描project,找到所有模块,生成stub files +3. Requirements。pbr会读取requirements.txt,生成setup函数需要的`install_requires/tests_require/dependency_links` + +这里需要注意,在 `requirements.txt` 文件的头部可以使用:`--index https://pypi.python.org/simple/`,这一行把一个抽象的依赖声明如 requests==1.2.0 转变为一个具体的依赖声明 requests 1.2.0 from pypi.python.org/simple/ + +4. long_description。从README.rst, README.txt or README file中生成`long_description`参数 + + + +使用pbr很简单: + +``` +from setuptools import setup + +setup( + setup_requires=['pbr'], + pbr=True, +) + +``` + +使用pbr时,setup.cfg中有一些配置。在[files]中,有三个key: +`packages`:指定需要包含的包,行为类似于setuptools.find_packages +`namespace_packages`:指定namespace packages +`data_files`: 指定目的目录和源文件路径,一个示例: + +``` +[files] +data_files = + etc/pbr = etc/pbr/* + etc/neutron = + etc/api-paste.ini + etc/dhcp-agent.ini + etc/init.d = neutron.init + +``` + +`[entry_points]` 段跟 setuptools 的方式相同。 + + + +到此,我讲了三种编写使用 setup.py 的方法 + +- 使用命令行参数指定,一个一个将参数传递进去(极不推荐) +- 在 setup.py 中的setup函数中指定(推荐使用) +- 使用 pbr ,在 setup.cfg 中指定(易于管理,更推荐) + +## 9. 如何使用 setup.py 构建包 + +1、构建源码发布包。 + +用于发布一个 Python 模块或项目,将源码打包成 tar.gz (用于 Linux 环境中)或者 zip 压缩包(用于 Windows 环境中) + +```shell +$ python setup.py sdist +``` + +那这种包如何安装呢? + +答案是,使用下一节即将介绍的 `setuptools` 中提供的 `easy_install` 工具。 + +```shell +$ easy_install xxx.tar.gz +``` + +使用 sdist 将根据当前平台创建默认格式的存档。在类 Unix 平台上,将创建后缀后为 `.tar.gz` 的 gzip 压缩的tar文件分发包,而在Windows上为 ZIP 文件。 + +当然,你也可以通过指定你要的发布包格式来打破这个默认行为 + +```shell +$ python setup.py sdist --formats=gztar,zip +``` + +你可以指定的格式有哪些呢? + +创建一个压缩的tarball和一个zip文件。可用格式为: + +![](http://image.iswbm.com/20191218203517.png) + +对以上的格式,有几点需要注意一下: + +- 在版本3.5中才添加了对 `xztar` 格式的支持 +- zip 格式需要你事先已安装相应的模块:zip程序或zipfile模块(已成为Python的标准库) +- ztar 格式正在弃用,请尽量不要使用 + +另外,如果您希望归档文件的所有文件归root拥有,可以这样指定 + +``` +python setup.py sdist --owner=root --group=root +``` + + + +2、构建二进制分发包。 + +在windows中我们习惯了双击 exe 进行软件的安装,Python 模块的安装也同样支持 打包成 exe 这样的二进制软件包。 + +```shell +$ python setup.py bdist_wininst +``` + +而在 Linux 中,大家也习惯了使用 rpm 来安装包,对此你可以使用这条命令实现 rpm 包的构建 + +```shell +$ python setup.py bdist_rpm +``` + +若你喜欢使用 easy_install 或者 pip 来安装离线包。你可以将其打包成 egg 包 + +```shell +$ python setup.py bdist_egg +``` + +若你的项目,需要安装多个平台下,既有 Windows 也有 Linux,按照上面的方法,多种格式我们要执行多次命令,为了方便,你可以一步到位,执行如下这条命令,即可生成多个格式的进制包 + +```shell +$ python setup.py bdist +``` + + + +## 10. 如何使用 setup.py 安装包 + +正常情况下,我们都是通过以上构建的源码包或者二进制包进行模块的安装。 + +但在编写 setup.py 的过程中,可能不能一步到位,需要多次调试,这时候如何测试自己写的 setup.py 文件是可用的呢? + +这时候你可以使用这条命令,它会将你的模块安装至系统全局环境中 + +```shell +$ python setup.py install +``` + +如若你的项目还处于开发阶段,频繁的安装模块,也是一个麻烦事。 + +这时候你可以使用这条命令安装,该方法不会真正的安装包,而是在系统环境中创建一个软链接指向包实际所在目录。这边在修改包之后不用再安装就能生效,便于调试。 + +```shell +$ python setup.py develop +``` + + + +## 11. 如何发布包到 PyPi? + +通过上面的学习,你一定已经学会了如何打包自己的项目,若你觉得自己开发的模块非常不错,想要 share 给其他人使用,你可以将其上传到 PyPi (Python Package Index)上,它是 Python 官方维护的第三方包仓库,用于统一存储和管理开发者发布的 Python 包。 + + + +如果要发布自己的包,需要先到 pypi 上注册账号。然后创建 `~/.pypirc` 文件,此文件中配置 PyPI 访问地址和账号。如的.pypirc文件内容请根据自己的账号来修改。 + +典型的 .pypirc 文件 + +```ini +[distutils] +index-servers = pypi + +[pypi] +username:xxx +password:xxx +``` + +然后使用这条命令进行信息注册,完成后,你可以在 PyPi 上看到项目信息。 + +```shell +$ python setup.py register +``` + +注册完了后,你还要上传源码包,别人才使用下载安装 + +```shell +$ python setup.py upload +``` + +或者也可以使用 `twine` 工具注册上传,它是一个专门用于与 pypi 进行交互的工具,详情可以参考官网:https://www.ctolib.com/twine.html,这里不详细讲了。 + + + +## 参考文章 + +- http://blog.konghy.cn/2018/04/29/setup-dot-py/ +- https://note.qidong.name/2018/01/python-setup-requires/ + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c03/c03_14.rst b/source/c03/c03_14.rst new file mode 100644 index 0000000..e1aec71 --- /dev/null +++ b/source/c03/c03_14.rst @@ -0,0 +1,703 @@ +3.14 全面学习 Python 包:包的构建与分发 +======================================= + +|image0| + +1. 为什么需要对项目分发打包? +----------------------------- + +平常我们习惯了使用 pip +来安装一些第三方模块,这个安装过程之所以简单,是因为模块开发者为我们默默地为我们做了所有繁杂的工作,而这个过程就是 +``打包``\ 。 + +打包,就是将你的源代码进一步封装,并且将所有的项目部署工作都事先安排好,这样使用者拿到后即装即用,不用再操心如何部署的问题(如果你不想对照着一堆部署文档手工操作的话)。 + +不管你是在工作中,还是业余准备自己写一个可以上传到 PyPI +的项目,你都要学会如何打包你的项目。 + +Python 发展了这么些年了,项目打包工具也已经很成熟了。他们都有哪些呢? + +你可能听过 ``disutils``\ 、 ``distutils`` +、\ ``distutils2``\ 、\ ``setuptools``\ 等等,好像很熟悉,却又很陌生,他们都是什么关系呢? + +2. 包分发的始祖:distutils +-------------------------- + +``distutils`` 是 Python +的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是 +Python +官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的。 + +``distutils`` 的精髓在于编写 setup.py,它是模块分发与安装的指导文件。 + +那么如何编写 setup.py +呢?这里面的内容非常多,我会在后面进行详细的解析,请你耐心往下看。 + +你有可能没写过 setup.py ,但你绝对使用过 setup.py +来做一些事情,比如下面这条命令,我们经常用它来进行模块的安装。 + +.. code:: shell + + $ python setup.py install + +这样的安装方法是通过源码安装,与之对应的是通过二进制软件包的安装,同样我也会在后面进行介绍。 + +3. 分发工具升级:setuptools +--------------------------- + +``setuptools`` 是 distutils +增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 +Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。 + +**distribute**\ ,或许你在其他地方也见过它,这里也提一下。 + +distribute 是 setuptools +有一个分支版本,分支的原因可能是有一部分开发者认为 setuptools +开发太慢了。但现在,distribute 又合并回了 setuptools +中。因此,我们可以认为它们是同一个东西。 + +还有一个大包分发工具是 +**distutils2**\ ,其试图尝试充分利用distutils,detuptools 和 distribute +并成为 Python +标准库中的标准工具。但该计划并没有达到预期的目的,且已经是一个废弃的项目。 + +因此,setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。 + +那么如何在一个干净的环境中安装 setuptools 呢? + +主要有两种方法: + +- 源码安装:在 https://pypi.org/project/setuptools/#files 中下载 zip 包 + 解压执行 ``python setup.py install`` 安装 +- 通过引导程序安装:下载引导程序,它可以用来下载或者更新最新版本的 + setuptools + +.. code:: shell + + $ wget http://peak.telecommunity.com/dist/ez_setup.py + + # 安装 + $ python ez_setup.py + + # 更新,以下两种任选 + $ python ez_setup.py –U setuptools + $ pip install -U setuptools + +4. easy_install 使用指南 +------------------------ + +当你安装完 setuptools 后,就拥有了一个叫做 ``easy_install`` +的第三方管理工具,这也是它区分于 distutils 的一大改进。 + +这里简单介绍一下它的用法,虽然它已经用得非常少了。 + +先是包的安装 + +.. code:: shell + + # 通过包名,从PyPI寻找最新版本,自动下载、编译、安装 + $ easy_install pkg_name + + # 通过包名从指定下载页寻找链接来安装或升级包 + $ easy_install -f http://pythonpaste.org/package_index.html + + # 指定线上的包地址安装 + $ easy_install http://example.com/path/to/MyPackage-1.2.3.tgz + + # 从本地的 .egg 文件安装 + $ easy_install xxx.egg + + # 在安装时你可以添加额外的参数 + 指定安装目录:--install-dir=DIR, -d DIR + 指定用户安装:--user + +再者是包的升级 + +.. code:: shell + + # 从 pypi 中搜索并升级包 + $ easy_install --upgrade pkg_name + + # 指定版本进行升级 + $ easy_install "SomePackage==2.0" + +最后是包的删除 + +.. code:: shell + + $ easy_install -m pkg_name + +需要注意的是,这样的删除,仅是在 easy-install.pth 文件中删除,使其不能在 +python 中使用 +这个模块,但实际的包还在你的电脑中,若要删除彻底,需要你手动删除相关的 +.egg 及 其他文件。 + +默认情况下,easy_install 只会从 pypi +上下载相关软件包,由于这个源在国外,下载包的速度并不理想,使用过pip的朋友自然会想,easy_install +是否能指定源进行安装呢? + +答案是,可以的。 + +编辑配置文件 ``/root/.pydistutils.cfg`` + +.. code:: ini + + [easy_install] + index-url=http://mirrors.aliyun.com/pypi/simple/ + find-links=http://mirrors.aliyun.com/pypi/simple/ + +以上仅介绍了 easy_install +的一些常用的方法,想要了解更多,你可以点击官方文档:https://setuptools.readthedocs.io/en/latest/easy_install.html + +总结一句:setuptools +是官方提供的一个专业用于包分发的工具,若只从安装的角度来看,它的功能确实简单。它更大的意义是对包的分发很有用,定制化程序非常高,我们现在也还在用它进行版本包的发布。 + +5. 源码包与二进制包什么区别? +----------------------------- + +Python 包的分发可以分为两种: + +1. 以源码包的方式发布 + +源码包安装的过程,是先解压,再编译,最后才安装,所以它是跨平台的,由于每次安装都要进行编译,相对二进包安装方式来说安装速度较慢。 + +源码包的本质是一个压缩包,其常见的格式有: + +|image1| + +2. 以二进制包形式发布 + +二进制包的安装过程省去了编译的过程,直接进行解压安装,所以安装速度较源码包来说更快。 + +由于不同平台的编译出来的包无法通用,所以在发布时,需事先编译好多个平台的包。 + +二进制包的常见格式有: + +|image2| + +6. eggs 与 wheels 有什么区别? +------------------------------ + +Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012 +年定义。Wheel 的出现是为了替代 Egg,它的本质是一个zip包,其现在被认为是 +Python 的二进制包的标准格式。 + +以下是 Wheel 和 Egg 的主要区别: + +- Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义 +- Wheel 是一种分发格式,即打包格式。而 Egg + 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import +- Wheel 文件不会包含 .pyc 文件 +- Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info + 目录 +- Wheel 有着更丰富的命名规则。 +- Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现 +- Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易 + +wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块,然后再使用 pip +的命令。 + +.. code:: shell + + $ pip install wheel + $ pip wheel --wheel-dir=/local/wheels pkg + +7. 超详细讲解 setup.py 的编写? +------------------------------- + +打包分发最关键的一步是编写 ``setup.py`` 文件。 + +以下是一个 setup.py 简单的使用示例 + +.. code:: python + + from setuptools import setup, find_packages + + setup( + name="mytest", + version="1.0", + author="wangbm", + author_email="wongbingming@163.com", + description="Learn to Pack Python Module -->公众号:Python编程时光", + + # 项目主页 + url="http://iswbm.com/", + + # 你要安装的包,通过 setuptools.find_packages 找到当前目录下有哪些包 + packages=find_packages() + ) + +接下来,我将慢慢扩充这个setup函数,增加更多的参数,以便你能理解setup函数能做哪些事情。 + +**程序分类信息** + +``classifiers`` +参数说明包的分类信息。所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers + +示例: + +.. code:: python + + from setuptools import setup, find_packages + + setup( + classifiers = [ + # 发展时期,常见的如下 + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 3 - Alpha', + + # 开发的目标用户 + 'Intended Audience :: Developers', + + # 属于什么类型 + 'Topic :: Software Development :: Build Tools', + + # 许可证信息 + 'License :: OSI Approved :: MIT License', + + # 目标 Python 版本 + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ] + ) + +**关于文件的分发** + +.. code:: python + + from setuptools import setup, find_packages + + + setup( + name="mytest", + version="1.0", + author="wangbm", + author_email="wongbingming@163.com", + description="Learn to Pack Python Module", + url="http://iswbm.com/", + packages=find_packages(), + + # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等 + data_files=[ + ('', ['conf/*.conf']), + ('/usr/lib/systemd/system/', ['bin/*.service']), + ], + + # 希望被打包的文件 + package_data={ + '':['*.txt'], + 'bandwidth_reporter':['*.txt'] + }, + # 不打包某些文件 + exclude_package_data={ + 'bandwidth_reporter':['*.txt'] + } + ) + +除了以上的参数配置之外,还可以使用一个叫做 ``MANIFEST.in`` +的文件,来控制文件的分发。 + +如下这是一个 ``MANIFEST.in`` 的样例: + +:: + + include *.txt + recursive-include examples *.txt *.py + prune examples/sample?/build + +这些配置,规定了如下几点 + +- 所有根目录下的以 txt 为后缀名的文件,都会分发 +- 根目录下的 examples 目录 和 txt、py文件都会分发 +- 路径匹配上 examples/sample?/build 不会分发 + +``MANIFEST.in`` 需要放在和 setup.py 同级的顶级目录下,setuptools +会自动读取该文件。 + +**关于依赖包下载安装** + +.. code:: python + + from setuptools import setup, find_packages + + + setup( + ... + + # 表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装 + install_requires=['docutils>=0.3'], + + # setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置 + # 这里列出的包,不会自动安装。 + setup_requires=['pbr'], + + # 仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。 + # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。 + tests_require=[ + 'pytest>=3.3.1', + 'pytest-cov>=2.5.1', + ], + + # 用于安装setup_requires或tests_require里的软件包 + # 这些信息会写入egg的 metadata 信息中 + dependency_links=[ + "http://example2.com/p/foobar-1.0.tar.gz", + ], + + # install_requires 在安装模块时会自动安装依赖包 + # 而 extras_require 不会,这里仅表示该模块会依赖这些包 + # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装 + extras_require={ + 'PDF': ["ReportLab>=1.2", "RXP"], + 'reST': ["docutils>=0.3"], + } + ) + +关于 ``install_requires``\ , 有以下五种常用的表示方法: + +1. ``'argparse'``\ ,只包含包名。 这种形式只检查包的存在性,不检查版本。 + 方便,但不利于控制风险。 +2. ``'setuptools==38.2.4'``\ ,指定版本。 + 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。 + 缺点是不利于更新,每次更新都需要改动代码。 +3. ``'docutils >= 0.3'``\ ,这是比较常用的形式。 + 当对某个库比较信任时,这种形式可以自动保持版本为最新。 +4. ``'Django >= 1.11, != 1.11.1, <= 2'``\ ,这是比较复杂的形式。 + 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。 + 对于一些大型、复杂的库,这种形式是最合适的。 +5. ``'requests[security, socks] >= 2.18.4'``\ ,这是包含了额外的可选依赖的形式。 + 正常安装requests会自动安装它的\ ``install_requires``\ 中指定的依赖,而不会安装\ ``security``\ 和\ ``socks``\ 这两组依赖。 + 这两组依赖是定义在它的\ ``extras_require``\ 中。 + 这种形式,用在深度使用某些库时。 + +**关于安装环境的限制** + +有些库并不是在所以的 Python 版本中都适用的,若一个库安装在一个未兼容的 +Python +环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。 + +这样的功能,可以使用 ``python_requires`` 来实现。 + +.. code:: python + + setup( + ... + python_requires='>=2.7, <=3', + ) + +**生成可执行文件的分发** + +.. code:: python + + from setuptools import setup, find_packages + + + setup( + name="mytest", + version="1.0", + author="wangbm", + author_email="wongbingming@163.com", + description="Learn to Pack Python Module", + url="http://iswbm.com/", + packages=find_packages(), + + # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件 + # 该文件入口指向 foo/main.py 的main 函数 + entry_points={ + 'console_scripts': [ + 'foo = foo.main:main' + ] + }, + + # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中 + # 执行 python setup.py install 后 + # 会生成 如 /usr/bin/foo.sh 和 如 /usr/bin/bar.py + scripts=['bin/foo.sh', 'bar.py'] + ) + +上面的 scripts 里有的脚本中有 ``sh`` 和 ``py`` +后缀,那么安装后,setuptools 会原封不动的移动到 /usr/bin +中,并添加可执行权限。 + +若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做 + +.. code:: python + + from setuptools.command.install_scripts import install_scripts + + class InstallScripts(install_scripts): + + def run(self): + setuptools.command.install_scripts.install_scripts.run(self) + + # Rename some script files + for script in self.get_outputs(): + if basename.endswith(".py") or basename.endswith(".sh"): + dest = script[:-3] + else: + continue + print("moving %s to %s" % (script, dest)) + shutil.move(script, dest) + + setup( + ... + scripts=['bin/foo.sh', 'bar.py'], + + cmdclass={ + "install_scripts": InstallScripts + } + ) + +**ext_modules** + +``ext_modules`` 参数用于构建 C 和 C++ 扩展扩展包。其是 Extension +实例的列表,每一个 Extension +实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如: + +.. code:: python + + setup( + # other arguments here... + ext_modules=[ + Extension('foo', + glob(path.join(here, 'src', '*.c')), + libraries = [ 'rt' ], + include_dirs=[numpy.get_include()]) + ] + ) + +详细了解可参考:https://docs.python.org/3.6/distutils/setupscript.html#preprocessor-options + +**指定release** + +setup.py 里只能指定 version,而不能指定 +release,如果你需要变更版本号,可以使用 ``--release`` 参数进行指定 + +.. code:: shell + + python setup.py bdist_rpm --release=20200617 + +setup.py +的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 +setup 函数常用的一些参数: + +|image3| + +更多参数可见:https://setuptools.readthedocs.io/en/latest/setuptools.html + +8. 打包辅助神器PBR 是什么? +--------------------------- + +``pbr`` 是 setuptools 的辅助工具,最初是为 OpenStack +开发(https://launchpad.net/pbr),基于\ ``d2to1``\ 。 + +``pbr`` 会读取和过滤setup.cfg中的数据,然后将解析后的数据提供给 +``setup.py`` 作为参数。包含如下功能: + +1. 从git中获取Version、AUTHORS and ChangeLog信息 +2. Sphinx Autodoc。pbr 会扫描project,找到所有模块,生成stub files +3. Requirements。pbr会读取requirements.txt,生成setup函数需要的\ ``install_requires/tests_require/dependency_links`` + +这里需要注意,在 ``requirements.txt`` +文件的头部可以使用:\ ``--index https://pypi.python.org/simple/``\ ,这一行把一个抽象的依赖声明如 +requests==1.2.0 转变为一个具体的依赖声明 requests 1.2.0 from +pypi.python.org/simple/ + +4. long_description。从README.rst, README.txt or README + file中生成\ ``long_description``\ 参数 + +使用pbr很简单: + +:: + + from setuptools import setup + + setup( + setup_requires=['pbr'], + pbr=True, + ) + +使用pbr时,setup.cfg中有一些配置。在[files]中,有三个key: +``packages``:指定需要包含的包,行为类似于setuptools.find_packages +``namespace_packages``:指定namespace packages ``data_files``: +指定目的目录和源文件路径,一个示例: + +:: + + [files] + data_files = + etc/pbr = etc/pbr/* + etc/neutron = + etc/api-paste.ini + etc/dhcp-agent.ini + etc/init.d = neutron.init + +``[entry_points]`` 段跟 setuptools 的方式相同。 + +到此,我讲了三种编写使用 setup.py 的方法 + +- 使用命令行参数指定,一个一个将参数传递进去(极不推荐) +- 在 setup.py 中的setup函数中指定(推荐使用) +- 使用 pbr ,在 setup.cfg 中指定(易于管理,更推荐) + +9. 如何使用 setup.py 构建包 +--------------------------- + +1、构建源码发布包。 + +用于发布一个 Python 模块或项目,将源码打包成 tar.gz (用于 Linux +环境中)或者 zip 压缩包(用于 Windows 环境中) + +.. code:: shell + + $ python setup.py sdist + +那这种包如何安装呢? + +答案是,使用下一节即将介绍的 ``setuptools`` 中提供的 ``easy_install`` +工具。 + +.. code:: shell + + $ easy_install xxx.tar.gz + +使用 sdist 将根据当前平台创建默认格式的存档。在类 Unix +平台上,将创建后缀后为 ``.tar.gz`` 的 gzip +压缩的tar文件分发包,而在Windows上为 ZIP 文件。 + +当然,你也可以通过指定你要的发布包格式来打破这个默认行为 + +.. code:: shell + + $ python setup.py sdist --formats=gztar,zip + +你可以指定的格式有哪些呢? + +创建一个压缩的tarball和一个zip文件。可用格式为: + +|image4| + +对以上的格式,有几点需要注意一下: + +- 在版本3.5中才添加了对 ``xztar`` 格式的支持 +- zip + 格式需要你事先已安装相应的模块:zip程序或zipfile模块(已成为Python的标准库) +- ztar 格式正在弃用,请尽量不要使用 + +另外,如果您希望归档文件的所有文件归root拥有,可以这样指定 + +:: + + python setup.py sdist --owner=root --group=root + +2、构建二进制分发包。 + +在windows中我们习惯了双击 exe 进行软件的安装,Python +模块的安装也同样支持 打包成 exe 这样的二进制软件包。 + +.. code:: shell + + $ python setup.py bdist_wininst + +而在 Linux 中,大家也习惯了使用 rpm 来安装包,对此你可以使用这条命令实现 +rpm 包的构建 + +.. code:: shell + + $ python setup.py bdist_rpm + +若你喜欢使用 easy_install 或者 pip 来安装离线包。你可以将其打包成 egg 包 + +.. code:: shell + + $ python setup.py bdist_egg + +若你的项目,需要安装多个平台下,既有 Windows 也有 +Linux,按照上面的方法,多种格式我们要执行多次命令,为了方便,你可以一步到位,执行如下这条命令,即可生成多个格式的进制包 + +.. code:: shell + + $ python setup.py bdist + +10. 如何使用 setup.py 安装包 +---------------------------- + +正常情况下,我们都是通过以上构建的源码包或者二进制包进行模块的安装。 + +但在编写 setup.py +的过程中,可能不能一步到位,需要多次调试,这时候如何测试自己写的 +setup.py 文件是可用的呢? + +这时候你可以使用这条命令,它会将你的模块安装至系统全局环境中 + +.. code:: shell + + $ python setup.py install + +如若你的项目还处于开发阶段,频繁的安装模块,也是一个麻烦事。 + +这时候你可以使用这条命令安装,该方法不会真正的安装包,而是在系统环境中创建一个软链接指向包实际所在目录。这边在修改包之后不用再安装就能生效,便于调试。 + +.. code:: shell + + $ python setup.py develop + +11. 如何发布包到 PyPi? +----------------------- + +通过上面的学习,你一定已经学会了如何打包自己的项目,若你觉得自己开发的模块非常不错,想要 +share 给其他人使用,你可以将其上传到 PyPi (Python Package +Index)上,它是 Python +官方维护的第三方包仓库,用于统一存储和管理开发者发布的 Python 包。 + +如果要发布自己的包,需要先到 pypi 上注册账号。然后创建 ``~/.pypirc`` +文件,此文件中配置 PyPI +访问地址和账号。如的.pypirc文件内容请根据自己的账号来修改。 + +典型的 .pypirc 文件 + +.. code:: ini + + [distutils] + index-servers = pypi + + [pypi] + username:xxx + password:xxx + +然后使用这条命令进行信息注册,完成后,你可以在 PyPi 上看到项目信息。 + +.. code:: shell + + $ python setup.py register + +注册完了后,你还要上传源码包,别人才使用下载安装 + +.. code:: shell + + $ python setup.py upload + +或者也可以使用 ``twine`` 工具注册上传,它是一个专门用于与 pypi +进行交互的工具,详情可以参考官网:https://www.ctolib.com/twine.html,这里不详细讲了。 + +参考文章 +-------- + +- http://blog.konghy.cn/2018/04/29/setup-dot-py/ +- https://note.qidong.name/2018/01/python-setup-requires/ + +|image5| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20191218202833.png +.. |image2| image:: http://image.iswbm.com/20191218203005.png +.. |image3| image:: http://image.iswbm.com/20191218203255.png +.. |image4| image:: http://image.iswbm.com/20191218203517.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png + diff --git a/source/c04/c04_02.md b/source/c04/c04_02.md index b5ec958..45f4f70 100644 --- a/source/c04/c04_02.md +++ b/source/c04/c04_02.md @@ -1,160 +1,161 @@ -# 4.2 Xshell的高效使用手册 +# 4.2 虚拟环境:Pipenv ![](http://image.iswbm.com/20200602135014.png) ---- +以前一直使用pip+virtualenv+virtualwrapper管理模块和环境, 但是virtualwrapper在windows上使用不太方便,而且包和环境分开管理确实经常不记得哪个是哪个了。 -![](http://image.iswbm.com/20190511162815.png) -做为一名开发人员,我们难免都会与服务器打交道。 +为什么 会推荐 pipenv 呢? -有时候是公司的线上生产环境,你需要上去部署公司的项目。 -有时候是在阿里上买的云主机,想自己搭个博客来写写文章。 +- 它是 `virtualenv` 和 `pip` 的合体,可以合起来使用; +- 使用`Pipfile` 和 `Pipfile.lock`替代`requirements.txt` +- 可以使用 `pipenv graph`很方便的看出包的依赖关系。 +- 通过加载`.env`文件简化开发工作流程 -就像我,是从事云计算相关的,每天远程登陆的服务器都有几十台。 +## 4.14.1 安装pipenv -这时候,掌握一些远程登陆工具的使用技巧是相当有必要的,会大大增加你使用的便利性和愉悦性。 +如果你的电脑上没有安装 pipenv,可以使用如下方法安装 -优秀的远程工具有很多,Xshell,SecureCRT,PuTTY等等。 +```shell +# mac +$ brew install pipenv -由于小明是个很注重工具顔值的人,所以一开始就选择了Xshell。本篇也只会介绍Xshell的使用技巧,但是快捷键在其他工具上部分是通用的。 +# windows +pip install [--user] pipenv +``` -## 快捷入口 +如果你的电脑是 windows 的。 -**快速命令** +![](http://image.iswbm.com/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn) -查看 -> 快速命令 +需要将如标示路径,加入到 环境变量 PATH 中。 -双击底部自定义快速命令。 -![](http://image.iswbm.com/20190511162524.png) +![](http://image.iswbm.com/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX) -**使用收藏栏** +然后需要重启一下,CMD 终端才能够刷新环境变量。 -点击最左侧按按钮添加收藏。 -![](http://image.iswbm.com/20190511162607.png) +## 4.14.2 创建虚拟环境 -**快捷设置** +DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 -工具 -> 选项 -> 键盘和鼠标 +```shell +$ mkdir DjangoWebBlog && cd DjangoWebBlog -① 右键粘贴 -② 双击分隔符 -③ 选中即复制 +# 在当前目录下创建一个虚拟环境(默认的Python版本) +$ pipenv install +``` -![](http://image.iswbm.com/20190511162716.png) +你也可以指定版本创建 -**设置Meta键** +```shell +$ pipenv --two # 相当于 pipenv --python /usr/bin/python2 +$ pipenv --three # 相当于 pipenv --python /usr/bin/python3 -文件 -> 属性 -> 键盘 +$ pipenv --python 3.7 # 也可以指定具体的版本 +pipenv install --python 2 +``` -一定要打钩,这是后面诸多快捷使用的前提。 -![](http://image.iswbm.com/20190511162730.png) +这边以安装 python2 版本的虚拟环境为例说明。 -## 移动光标 -``` -Ctrl+f 向后移动一个字符 -Ctrl+b 向前移动一个字符 +![](http://image.iswbm.com/20190612211330.png) -Ctrl+a 将光标移至输入行头,相当于Home键 -Ctrl+e 将光标移至输入行末,相当于End键 +如果你原项目使用的是 requirements.txt 这个管理包的方式,这时候执行 `pipenv --tow` 创建一个虚拟环境后,会找到 requirements.txt ,并根据这里面的依赖包生成 Pipfile文件。 -Alt+f 以单词为单位,向前移动 -Alt+b 以单词为单位,向前移动 +![](http://image.iswbm.com/20190612213015.png) -Shift+PgUp 将终端显示向上滚动 -Shift+PgDn 将终端显示向下滚动 -``` +## 4.14.3 查询虚拟环境 -## 删除元素 -``` -Ctrl+u 删除到行首 -Ctrl+k 删除到行末 +```shell +# 返回项目的路径 +$ pipenv --where -Ctrl+y 粘贴上次Ctrl+u/k的字符 -Ctrl+d 删除当前字符,等同于Del +# 返回虚拟环境路径 +$ pipenv --venv -Alt+Backspace 向前删除一个单词,和 Ctrl+w 一样 +# 返回该虚拟环境的解释器 +$ pipenv --py ``` -## 切换标签 -``` -Alt+n n为1-9数字,快速切换标签页 -Ctrl+Tab 向右切换标签 +演示如下: -Ctrl+Shift+Tab 向右切换标签 -Shift+Tab 两个窗口来回切换 -``` +![](http://image.iswbm.com/20190612213950.png) -## 屏幕模式 -``` -Alt+s 切换到简单版模式 -Alt+Enter 切换至全屏 -``` +## 4.14.4 操作虚拟环境 -## 快速操作 ```shell -Alt+. 取得上次命令的参数,并粘贴 -Ctrl+r 在历史命令中查找,回车执行 -Ctrl+o 放在命令后执行,可重复执行 +# 进入这个虚拟环境 +$ pipenv shell +# 退出这个虚拟环境 +$ exit +$ deactivate -Alt+u 以单词为单位,将光标处到单词结尾的字符转化为大写 -Alt+l 以单词为单位,将光标处到单词结尾的字符转化为小写 - -Alt+Shift+# 注释当前命令,相当于ctrl-a,#,enter +# 移除当前目录的虚拟环境 +$ pipenv --rm ``` -## 辅助命令 +执行 `pipenv shell` 就可以进入这个虚拟环境,在头部会有虚拟环境的标识名称。有这个标识,说明已经进入虚拟环境。 + +![](http://image.iswbm.com/20190612211925.png) + +```python +# 在当前虚拟环境中运行 +$ pipenv run python # 进入交互式,跟直接执行 python 一样 +$ pipenv run python 文件名 # 运行文件 +$ pipenv run pip ... # 运行pip ``` -Ctrl+s 锁住终端,可用来停留在当前屏 -Ctrl+q 解锁终端,恢复刷屏 -Ctrl+d 键盘输入结束或退出终端 +## 4.14.5 虚拟环境包管理 -Ctrl+s 暂停当前程序,暂停后按下任意键恢复运行 -Ctrl+z 将当前程序放到后台运行,恢复到前台为命令fg +```shell +# 安装一个本地包(setup.py)到虚拟环境(Pipfile) +$ pipenv install -e . -Ctrl+Shift+r 重新连接 +# 安装、卸载模块 +$ pipenv install requests +$ pipenv uninstall requests +$ pipenv uninstall --all # 卸载全部包 +$ pipenv install -r path/to/requirements.txt -Ctrl+Insert 复制 -Shift+Insert 粘贴 -``` -以我日常使用到的,暂时就这么些了,以后有用更多的使用技巧和快捷键再来补充。 +# 安装所有依赖 +$ pipenv install --dev -## 配色方案 +# 更新包 +$ pipenv update # 更新所有包 +$ pipenv update --outdated # 打印所有要更新的包 +$ pipenv update <包名> # 更新指定的包 -新建一个文件`ubuntu.xcs` +# 将Pipfile和Pipfile.lock文件里面的包导出为requirements.txt文件 +$ pipenv run pip freeze # 相当于pipenv run pip freeze >requirements.txt +$ pipenv lock -r > requirements.txt +$ pipenv lock -r --dev # 若只想导出开发用的包 ``` -[ubuntu] -text(bold)=ffffff -magenta(bold)=ad7fa8 -text=ffffff -white(bold)=eeeeec -green=4e9a06 -red(bold)=ef2929 -green(bold)=8ae234 -black(bold)=555753 -red=cc0000 -blue=3465a4 -black=000000 -blue(bold)=729fcf -yellow(bold)=fce94f -cyan(bold)=34e2e2 -yellow=c4a000 -magenta=75507b -background=300a24 -white=d3d7cf -cyan=06989a -[Names] -count=1 -name0=ubuntu + +## 4.14.5 其他命令 + +```shell + +# 创建一个包含预发布的锁文件: +$ pipenv lock --pre + +# 打印所有包的依赖关系图 +$ pipenv graph + +# 检查安全漏洞 +$ pipenv check ``` -然后在xshell配色方案中导入即可 +打印该虚拟环境下所有包的依赖关系图 + +![](http://image.iswbm.com/20190614000336.png) + +有的python第三方包旧版本会有安全漏洞,使用 pipenv check 可以检查安全漏洞。 + +![](http://image.iswbm.com/20190612215924.png) -附上一个更全的帖子:[Xshell快捷键汇总](https://www.cnblogs.com/zhoushihui/p/5404392.html) +.env`文件,用来存放一些环境变量。 --- diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index 1dbdb5e..7d897a7 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -1,180 +1,185 @@ -4.2 Xshell的高效使用手册 -======================== +4.2 虚拟环境:Pipenv +==================== |image0| --------------- +以前一直使用pip+virtualenv+virtualwrapper管理模块和环境, +但是virtualwrapper在windows上使用不太方便,而且包和环境分开管理确实经常不记得哪个是哪个了。 -|image1| +为什么 会推荐 pipenv 呢? + +- 它是 ``virtualenv`` 和 ``pip`` 的合体,可以合起来使用; +- 使用\ ``Pipfile`` 和 ``Pipfile.lock``\ 替代\ ``requirements.txt`` +- 可以使用 ``pipenv graph``\ 很方便的看出包的依赖关系。 +- 通过加载\ ``.env``\ 文件简化开发工作流程 + +4.14.1 安装pipenv +----------------- -做为一名开发人员,我们难免都会与服务器打交道。 +如果你的电脑上没有安装 pipenv,可以使用如下方法安装 -有时候是公司的线上生产环境,你需要上去部署公司的项目。 -有时候是在阿里上买的云主机,想自己搭个博客来写写文章。 +.. code:: shell -就像我,是从事云计算相关的,每天远程登陆的服务器都有几十台。 + # mac + $ brew install pipenv -这时候,掌握一些远程登陆工具的使用技巧是相当有必要的,会大大增加你使用的便利性和愉悦性。 + # windows + pip install [--user] pipenv -优秀的远程工具有很多,Xshell,SecureCRT,PuTTY等等。 +如果你的电脑是 windows 的。 -由于小明是个很注重工具顔值的人,所以一开始就选择了Xshell。本篇也只会介绍Xshell的使用技巧,但是快捷键在其他工具上部分是通用的。 +|image1| -快捷入口 --------- +需要将如标示路径,加入到 环境变量 PATH 中。 -**快速命令** +|image2| -查看 -> 快速命令 +然后需要重启一下,CMD 终端才能够刷新环境变量。 -双击底部自定义快速命令。 |image2| +4.14.2 创建虚拟环境 +------------------- -**使用收藏栏** +DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 -点击最左侧按按钮添加收藏。 |image3| +.. code:: shell -**快捷设置** + $ mkdir DjangoWebBlog && cd DjangoWebBlog -工具 -> 选项 -> 键盘和鼠标 + # 在当前目录下创建一个虚拟环境(默认的Python版本) + $ pipenv install -① 右键粘贴 ② 双击分隔符 ③ 选中即复制 +你也可以指定版本创建 -|image4| +.. code:: shell + + $ pipenv --two # 相当于 pipenv --python /usr/bin/python2 + $ pipenv --three # 相当于 pipenv --python /usr/bin/python3 -**设置Meta键** + $ pipenv --python 3.7 # 也可以指定具体的版本 + pipenv install --python 2 -文件 -> 属性 -> 键盘 +这边以安装 python2 版本的虚拟环境为例说明。 -一定要打钩,这是后面诸多快捷使用的前提。 |image5| +|image3| -移动光标 --------- +如果你原项目使用的是 requirements.txt 这个管理包的方式,这时候执行 +``pipenv --tow`` 创建一个虚拟环境后,会找到 requirements.txt +,并根据这里面的依赖包生成 Pipfile文件。 -:: +|image4| - Ctrl+f 向后移动一个字符 - Ctrl+b 向前移动一个字符 +4.14.3 查询虚拟环境 +------------------- - Ctrl+a 将光标移至输入行头,相当于Home键 - Ctrl+e 将光标移至输入行末,相当于End键 +.. code:: shell - Alt+f 以单词为单位,向前移动 - Alt+b 以单词为单位,向前移动 + # 返回项目的路径 + $ pipenv --where - Shift+PgUp 将终端显示向上滚动 - Shift+PgDn 将终端显示向下滚动 + # 返回虚拟环境路径 + $ pipenv --venv -删除元素 --------- + # 返回该虚拟环境的解释器 + $ pipenv --py -:: +演示如下: - Ctrl+u 删除到行首 - Ctrl+k 删除到行末 +|image5| - Ctrl+y 粘贴上次Ctrl+u/k的字符 - Ctrl+d 删除当前字符,等同于Del +4.14.4 操作虚拟环境 +------------------- - Alt+Backspace 向前删除一个单词,和 Ctrl+w 一样 +.. code:: shell -切换标签 --------- + # 进入这个虚拟环境 + $ pipenv shell -:: + # 退出这个虚拟环境 + $ exit + $ deactivate - Alt+n n为1-9数字,快速切换标签页 - Ctrl+Tab 向右切换标签 + # 移除当前目录的虚拟环境 + $ pipenv --rm - Ctrl+Shift+Tab 向右切换标签 - Shift+Tab 两个窗口来回切换 +执行 ``pipenv shell`` +就可以进入这个虚拟环境,在头部会有虚拟环境的标识名称。有这个标识,说明已经进入虚拟环境。 -屏幕模式 --------- +|image6| -:: +.. code:: python - Alt+s 切换到简单版模式 - Alt+Enter 切换至全屏 + # 在当前虚拟环境中运行 + $ pipenv run python # 进入交互式,跟直接执行 python 一样 + $ pipenv run python 文件名 # 运行文件 + $ pipenv run pip ... # 运行pip -快速操作 --------- +4.14.5 虚拟环境包管理 +--------------------- .. code:: shell - Alt+. 取得上次命令的参数,并粘贴 - Ctrl+r 在历史命令中查找,回车执行 - Ctrl+o 放在命令后执行,可重复执行 + # 安装一个本地包(setup.py)到虚拟环境(Pipfile) + $ pipenv install -e . + # 安装、卸载模块 + $ pipenv install requests + $ pipenv uninstall requests + $ pipenv uninstall --all # 卸载全部包 + $ pipenv install -r path/to/requirements.txt - Alt+u 以单词为单位,将光标处到单词结尾的字符转化为大写 - Alt+l 以单词为单位,将光标处到单词结尾的字符转化为小写 - Alt+Shift+# 注释当前命令,相当于ctrl-a,#,enter + # 安装所有依赖 + $ pipenv install --dev -辅助命令 --------- + # 更新包 + $ pipenv update # 更新所有包 + $ pipenv update --outdated # 打印所有要更新的包 + $ pipenv update <包名> # 更新指定的包 -:: + # 将Pipfile和Pipfile.lock文件里面的包导出为requirements.txt文件 + $ pipenv run pip freeze # 相当于pipenv run pip freeze >requirements.txt - Ctrl+s 锁住终端,可用来停留在当前屏 - Ctrl+q 解锁终端,恢复刷屏 - Ctrl+d 键盘输入结束或退出终端 + $ pipenv lock -r > requirements.txt + $ pipenv lock -r --dev # 若只想导出开发用的包 +4.14.5 其他命令 +--------------- - Ctrl+s 暂停当前程序,暂停后按下任意键恢复运行 - Ctrl+z 将当前程序放到后台运行,恢复到前台为命令fg +.. code:: shell - Ctrl+Shift+r 重新连接 - Ctrl+Insert 复制 - Shift+Insert 粘贴 + # 创建一个包含预发布的锁文件: + $ pipenv lock --pre -以我日常使用到的,暂时就这么些了,以后有用更多的使用技巧和快捷键再来补充。 + # 打印所有包的依赖关系图 + $ pipenv graph -配色方案 --------- + # 检查安全漏洞 + $ pipenv check -新建一个文件\ ``ubuntu.xcs`` +打印该虚拟环境下所有包的依赖关系图 -:: +|image7| - [ubuntu] - text(bold)=ffffff - magenta(bold)=ad7fa8 - text=ffffff - white(bold)=eeeeec - green=4e9a06 - red(bold)=ef2929 - green(bold)=8ae234 - black(bold)=555753 - red=cc0000 - blue=3465a4 - black=000000 - blue(bold)=729fcf - yellow(bold)=fce94f - cyan(bold)=34e2e2 - yellow=c4a000 - magenta=75507b - background=300a24 - white=d3d7cf - cyan=06989a - [Names] - count=1 - name0=ubuntu +有的python第三方包旧版本会有安全漏洞,使用 pipenv check +可以检查安全漏洞。 -然后在xshell配色方案中导入即可 +|image8| -附上一个更全的帖子:\ `Xshell快捷键汇总 `__ +.env`文件,用来存放一些环境变量。 -------------- -|image6| +|image9| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20190511162815.png -.. |image2| image:: http://image.iswbm.com/20190511162524.png -.. |image3| image:: http://image.iswbm.com/20190511162607.png -.. |image4| image:: http://image.iswbm.com/20190511162716.png -.. |image5| image:: http://image.iswbm.com/20190511162730.png -.. |image6| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn +.. |image2| image:: http://image.iswbm.com/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX +.. |image3| image:: http://image.iswbm.com/20190612211330.png +.. |image4| image:: http://image.iswbm.com/20190612213015.png +.. |image5| image:: http://image.iswbm.com/20190612213950.png +.. |image6| image:: http://image.iswbm.com/20190612211925.png +.. |image7| image:: http://image.iswbm.com/20190614000336.png +.. |image8| image:: http://image.iswbm.com/20190612215924.png +.. |image9| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_14.md b/source/c04/c04_14.md index f595119..b5ec958 100644 --- a/source/c04/c04_14.md +++ b/source/c04/c04_14.md @@ -1,161 +1,160 @@ -# 4.14 虚拟环境:Pipenv +# 4.2 Xshell的高效使用手册 ![](http://image.iswbm.com/20200602135014.png) -以前一直使用pip+virtualenv+virtualwrapper管理模块和环境, 但是virtualwrapper在windows上使用不太方便,而且包和环境分开管理确实经常不记得哪个是哪个了。 - +--- -为什么 会推荐 pipenv 呢? +![](http://image.iswbm.com/20190511162815.png) -- 它是 `virtualenv` 和 `pip` 的合体,可以合起来使用; -- 使用`Pipfile` 和 `Pipfile.lock`替代`requirements.txt` -- 可以使用 `pipenv graph`很方便的看出包的依赖关系。 -- 通过加载`.env`文件简化开发工作流程 +做为一名开发人员,我们难免都会与服务器打交道。 -## 4.14.1 安装pipenv +有时候是公司的线上生产环境,你需要上去部署公司的项目。 +有时候是在阿里上买的云主机,想自己搭个博客来写写文章。 -如果你的电脑上没有安装 pipenv,可以使用如下方法安装 +就像我,是从事云计算相关的,每天远程登陆的服务器都有几十台。 -```shell -# mac -$ brew install pipenv +这时候,掌握一些远程登陆工具的使用技巧是相当有必要的,会大大增加你使用的便利性和愉悦性。 -# windows -pip install [--user] pipenv -``` +优秀的远程工具有很多,Xshell,SecureCRT,PuTTY等等。 -如果你的电脑是 windows 的。 +由于小明是个很注重工具顔值的人,所以一开始就选择了Xshell。本篇也只会介绍Xshell的使用技巧,但是快捷键在其他工具上部分是通用的。 -![](http://image.iswbm.com/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn) +## 快捷入口 -需要将如标示路径,加入到 环境变量 PATH 中。 +**快速命令** -![](http://image.iswbm.com/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX) +查看 -> 快速命令 -然后需要重启一下,CMD 终端才能够刷新环境变量。 +双击底部自定义快速命令。 +![](http://image.iswbm.com/20190511162524.png) -## 4.14.2 创建虚拟环境 +**使用收藏栏** -DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 +点击最左侧按按钮添加收藏。 +![](http://image.iswbm.com/20190511162607.png) -```shell -$ mkdir DjangoWebBlog && cd DjangoWebBlog +**快捷设置** -# 在当前目录下创建一个虚拟环境(默认的Python版本) -$ pipenv install -``` +工具 -> 选项 -> 键盘和鼠标 -你也可以指定版本创建 +① 右键粘贴 +② 双击分隔符 +③ 选中即复制 -```shell -$ pipenv --two # 相当于 pipenv --python /usr/bin/python2 -$ pipenv --three # 相当于 pipenv --python /usr/bin/python3 +![](http://image.iswbm.com/20190511162716.png) -$ pipenv --python 3.7 # 也可以指定具体的版本 -pipenv install --python 2 -``` +**设置Meta键** -这边以安装 python2 版本的虚拟环境为例说明。 +文件 -> 属性 -> 键盘 -![](http://image.iswbm.com/20190612211330.png) +一定要打钩,这是后面诸多快捷使用的前提。 +![](http://image.iswbm.com/20190511162730.png) -如果你原项目使用的是 requirements.txt 这个管理包的方式,这时候执行 `pipenv --tow` 创建一个虚拟环境后,会找到 requirements.txt ,并根据这里面的依赖包生成 Pipfile文件。 - -![](http://image.iswbm.com/20190612213015.png) - -## 4.14.3 查询虚拟环境 - -```shell -# 返回项目的路径 -$ pipenv --where - -# 返回虚拟环境路径 -$ pipenv --venv - -# 返回该虚拟环境的解释器 -$ pipenv --py +## 移动光标 ``` +Ctrl+f 向后移动一个字符 +Ctrl+b 向前移动一个字符 -演示如下: +Ctrl+a 将光标移至输入行头,相当于Home键 +Ctrl+e 将光标移至输入行末,相当于End键 -![](http://image.iswbm.com/20190612213950.png) +Alt+f 以单词为单位,向前移动 +Alt+b 以单词为单位,向前移动 -## 4.14.4 操作虚拟环境 +Shift+PgUp 将终端显示向上滚动 +Shift+PgDn 将终端显示向下滚动 +``` -```shell -# 进入这个虚拟环境 -$ pipenv shell +## 删除元素 +``` +Ctrl+u 删除到行首 +Ctrl+k 删除到行末 -# 退出这个虚拟环境 -$ exit -$ deactivate +Ctrl+y 粘贴上次Ctrl+u/k的字符 +Ctrl+d 删除当前字符,等同于Del -# 移除当前目录的虚拟环境 -$ pipenv --rm +Alt+Backspace 向前删除一个单词,和 Ctrl+w 一样 ``` -执行 `pipenv shell` 就可以进入这个虚拟环境,在头部会有虚拟环境的标识名称。有这个标识,说明已经进入虚拟环境。 - -![](http://image.iswbm.com/20190612211925.png) +## 切换标签 +``` +Alt+n n为1-9数字,快速切换标签页 +Ctrl+Tab 向右切换标签 -```python -# 在当前虚拟环境中运行 -$ pipenv run python # 进入交互式,跟直接执行 python 一样 -$ pipenv run python 文件名 # 运行文件 -$ pipenv run pip ... # 运行pip +Ctrl+Shift+Tab 向右切换标签 +Shift+Tab 两个窗口来回切换 ``` -## 4.14.5 虚拟环境包管理 +## 屏幕模式 +``` +Alt+s 切换到简单版模式 +Alt+Enter 切换至全屏 +``` +## 快速操作 ```shell -# 安装一个本地包(setup.py)到虚拟环境(Pipfile) -$ pipenv install -e . - -# 安装、卸载模块 -$ pipenv install requests -$ pipenv uninstall requests -$ pipenv uninstall --all # 卸载全部包 -$ pipenv install -r path/to/requirements.txt +Alt+. 取得上次命令的参数,并粘贴 +Ctrl+r 在历史命令中查找,回车执行 +Ctrl+o 放在命令后执行,可重复执行 -# 安装所有依赖 -$ pipenv install --dev +Alt+u 以单词为单位,将光标处到单词结尾的字符转化为大写 +Alt+l 以单词为单位,将光标处到单词结尾的字符转化为小写 -# 更新包 -$ pipenv update # 更新所有包 -$ pipenv update --outdated # 打印所有要更新的包 -$ pipenv update <包名> # 更新指定的包 - -# 将Pipfile和Pipfile.lock文件里面的包导出为requirements.txt文件 -$ pipenv run pip freeze # 相当于pipenv run pip freeze >requirements.txt - -$ pipenv lock -r > requirements.txt -$ pipenv lock -r --dev # 若只想导出开发用的包 +Alt+Shift+# 注释当前命令,相当于ctrl-a,#,enter ``` -## 4.14.5 其他命令 +## 辅助命令 +``` +Ctrl+s 锁住终端,可用来停留在当前屏 +Ctrl+q 解锁终端,恢复刷屏 +Ctrl+d 键盘输入结束或退出终端 -```shell -# 创建一个包含预发布的锁文件: -$ pipenv lock --pre +Ctrl+s 暂停当前程序,暂停后按下任意键恢复运行 +Ctrl+z 将当前程序放到后台运行,恢复到前台为命令fg -# 打印所有包的依赖关系图 -$ pipenv graph +Ctrl+Shift+r 重新连接 -# 检查安全漏洞 -$ pipenv check +Ctrl+Insert 复制 +Shift+Insert 粘贴 ``` -打印该虚拟环境下所有包的依赖关系图 +以我日常使用到的,暂时就这么些了,以后有用更多的使用技巧和快捷键再来补充。 -![](http://image.iswbm.com/20190614000336.png) +## 配色方案 -有的python第三方包旧版本会有安全漏洞,使用 pipenv check 可以检查安全漏洞。 +新建一个文件`ubuntu.xcs` + +``` +[ubuntu] +text(bold)=ffffff +magenta(bold)=ad7fa8 +text=ffffff +white(bold)=eeeeec +green=4e9a06 +red(bold)=ef2929 +green(bold)=8ae234 +black(bold)=555753 +red=cc0000 +blue=3465a4 +black=000000 +blue(bold)=729fcf +yellow(bold)=fce94f +cyan(bold)=34e2e2 +yellow=c4a000 +magenta=75507b +background=300a24 +white=d3d7cf +cyan=06989a +[Names] +count=1 +name0=ubuntu +``` -![](http://image.iswbm.com/20190612215924.png) +然后在xshell配色方案中导入即可 -.env`文件,用来存放一些环境变量。 +附上一个更全的帖子:[Xshell快捷键汇总](https://www.cnblogs.com/zhoushihui/p/5404392.html) --- diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index cff6e02..1dbdb5e 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -1,185 +1,180 @@ -4.14 虚拟环境:Pipenv -===================== +4.2 Xshell的高效使用手册 +======================== |image0| -以前一直使用pip+virtualenv+virtualwrapper管理模块和环境, -但是virtualwrapper在windows上使用不太方便,而且包和环境分开管理确实经常不记得哪个是哪个了。 - -为什么 会推荐 pipenv 呢? - -- 它是 ``virtualenv`` 和 ``pip`` 的合体,可以合起来使用; -- 使用\ ``Pipfile`` 和 ``Pipfile.lock``\ 替代\ ``requirements.txt`` -- 可以使用 ``pipenv graph``\ 很方便的看出包的依赖关系。 -- 通过加载\ ``.env``\ 文件简化开发工作流程 - -4.14.1 安装pipenv ------------------ +-------------- -如果你的电脑上没有安装 pipenv,可以使用如下方法安装 +|image1| -.. code:: shell +做为一名开发人员,我们难免都会与服务器打交道。 - # mac - $ brew install pipenv +有时候是公司的线上生产环境,你需要上去部署公司的项目。 +有时候是在阿里上买的云主机,想自己搭个博客来写写文章。 - # windows - pip install [--user] pipenv +就像我,是从事云计算相关的,每天远程登陆的服务器都有几十台。 -如果你的电脑是 windows 的。 +这时候,掌握一些远程登陆工具的使用技巧是相当有必要的,会大大增加你使用的便利性和愉悦性。 -|image1| +优秀的远程工具有很多,Xshell,SecureCRT,PuTTY等等。 -需要将如标示路径,加入到 环境变量 PATH 中。 +由于小明是个很注重工具顔值的人,所以一开始就选择了Xshell。本篇也只会介绍Xshell的使用技巧,但是快捷键在其他工具上部分是通用的。 -|image2| +快捷入口 +-------- -然后需要重启一下,CMD 终端才能够刷新环境变量。 +**快速命令** -4.14.2 创建虚拟环境 -------------------- +查看 -> 快速命令 -DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 +双击底部自定义快速命令。 |image2| -.. code:: shell +**使用收藏栏** - $ mkdir DjangoWebBlog && cd DjangoWebBlog +点击最左侧按按钮添加收藏。 |image3| - # 在当前目录下创建一个虚拟环境(默认的Python版本) - $ pipenv install +**快捷设置** -你也可以指定版本创建 +工具 -> 选项 -> 键盘和鼠标 -.. code:: shell +① 右键粘贴 ② 双击分隔符 ③ 选中即复制 - $ pipenv --two # 相当于 pipenv --python /usr/bin/python2 - $ pipenv --three # 相当于 pipenv --python /usr/bin/python3 +|image4| - $ pipenv --python 3.7 # 也可以指定具体的版本 - pipenv install --python 2 +**设置Meta键** -这边以安装 python2 版本的虚拟环境为例说明。 +文件 -> 属性 -> 键盘 -|image3| +一定要打钩,这是后面诸多快捷使用的前提。 |image5| -如果你原项目使用的是 requirements.txt 这个管理包的方式,这时候执行 -``pipenv --tow`` 创建一个虚拟环境后,会找到 requirements.txt -,并根据这里面的依赖包生成 Pipfile文件。 +移动光标 +-------- -|image4| +:: -4.14.3 查询虚拟环境 -------------------- + Ctrl+f 向后移动一个字符 + Ctrl+b 向前移动一个字符 -.. code:: shell + Ctrl+a 将光标移至输入行头,相当于Home键 + Ctrl+e 将光标移至输入行末,相当于End键 - # 返回项目的路径 - $ pipenv --where + Alt+f 以单词为单位,向前移动 + Alt+b 以单词为单位,向前移动 - # 返回虚拟环境路径 - $ pipenv --venv + Shift+PgUp 将终端显示向上滚动 + Shift+PgDn 将终端显示向下滚动 - # 返回该虚拟环境的解释器 - $ pipenv --py +删除元素 +-------- -演示如下: +:: -|image5| + Ctrl+u 删除到行首 + Ctrl+k 删除到行末 -4.14.4 操作虚拟环境 -------------------- + Ctrl+y 粘贴上次Ctrl+u/k的字符 + Ctrl+d 删除当前字符,等同于Del -.. code:: shell + Alt+Backspace 向前删除一个单词,和 Ctrl+w 一样 - # 进入这个虚拟环境 - $ pipenv shell +切换标签 +-------- - # 退出这个虚拟环境 - $ exit - $ deactivate +:: - # 移除当前目录的虚拟环境 - $ pipenv --rm + Alt+n n为1-9数字,快速切换标签页 + Ctrl+Tab 向右切换标签 -执行 ``pipenv shell`` -就可以进入这个虚拟环境,在头部会有虚拟环境的标识名称。有这个标识,说明已经进入虚拟环境。 + Ctrl+Shift+Tab 向右切换标签 + Shift+Tab 两个窗口来回切换 -|image6| +屏幕模式 +-------- -.. code:: python +:: - # 在当前虚拟环境中运行 - $ pipenv run python # 进入交互式,跟直接执行 python 一样 - $ pipenv run python 文件名 # 运行文件 - $ pipenv run pip ... # 运行pip + Alt+s 切换到简单版模式 + Alt+Enter 切换至全屏 -4.14.5 虚拟环境包管理 ---------------------- +快速操作 +-------- .. code:: shell - # 安装一个本地包(setup.py)到虚拟环境(Pipfile) - $ pipenv install -e . + Alt+. 取得上次命令的参数,并粘贴 + Ctrl+r 在历史命令中查找,回车执行 + Ctrl+o 放在命令后执行,可重复执行 - # 安装、卸载模块 - $ pipenv install requests - $ pipenv uninstall requests - $ pipenv uninstall --all # 卸载全部包 - $ pipenv install -r path/to/requirements.txt + Alt+u 以单词为单位,将光标处到单词结尾的字符转化为大写 + Alt+l 以单词为单位,将光标处到单词结尾的字符转化为小写 - # 安装所有依赖 - $ pipenv install --dev + Alt+Shift+# 注释当前命令,相当于ctrl-a,#,enter - # 更新包 - $ pipenv update # 更新所有包 - $ pipenv update --outdated # 打印所有要更新的包 - $ pipenv update <包名> # 更新指定的包 +辅助命令 +-------- - # 将Pipfile和Pipfile.lock文件里面的包导出为requirements.txt文件 - $ pipenv run pip freeze # 相当于pipenv run pip freeze >requirements.txt +:: - $ pipenv lock -r > requirements.txt - $ pipenv lock -r --dev # 若只想导出开发用的包 + Ctrl+s 锁住终端,可用来停留在当前屏 + Ctrl+q 解锁终端,恢复刷屏 + Ctrl+d 键盘输入结束或退出终端 -4.14.5 其他命令 ---------------- -.. code:: shell + Ctrl+s 暂停当前程序,暂停后按下任意键恢复运行 + Ctrl+z 将当前程序放到后台运行,恢复到前台为命令fg + Ctrl+Shift+r 重新连接 - # 创建一个包含预发布的锁文件: - $ pipenv lock --pre + Ctrl+Insert 复制 + Shift+Insert 粘贴 - # 打印所有包的依赖关系图 - $ pipenv graph +以我日常使用到的,暂时就这么些了,以后有用更多的使用技巧和快捷键再来补充。 - # 检查安全漏洞 - $ pipenv check +配色方案 +-------- -打印该虚拟环境下所有包的依赖关系图 +新建一个文件\ ``ubuntu.xcs`` -|image7| +:: -有的python第三方包旧版本会有安全漏洞,使用 pipenv check -可以检查安全漏洞。 + [ubuntu] + text(bold)=ffffff + magenta(bold)=ad7fa8 + text=ffffff + white(bold)=eeeeec + green=4e9a06 + red(bold)=ef2929 + green(bold)=8ae234 + black(bold)=555753 + red=cc0000 + blue=3465a4 + black=000000 + blue(bold)=729fcf + yellow(bold)=fce94f + cyan(bold)=34e2e2 + yellow=c4a000 + magenta=75507b + background=300a24 + white=d3d7cf + cyan=06989a + [Names] + count=1 + name0=ubuntu -|image8| +然后在xshell配色方案中导入即可 -.env`文件,用来存放一些环境变量。 +附上一个更全的帖子:\ `Xshell快捷键汇总 `__ -------------- -|image9| +|image6| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/Fk6WZ2xbqg2DM3AvnYCpsiKQ4xOn -.. |image2| image:: http://image.iswbm.com/FjuJ8yZsgjkzVuBRZHxK1ZnnzaEX -.. |image3| image:: http://image.iswbm.com/20190612211330.png -.. |image4| image:: http://image.iswbm.com/20190612213015.png -.. |image5| image:: http://image.iswbm.com/20190612213950.png -.. |image6| image:: http://image.iswbm.com/20190612211925.png -.. |image7| image:: http://image.iswbm.com/20190614000336.png -.. |image8| image:: http://image.iswbm.com/20190612215924.png -.. |image9| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20190511162815.png +.. |image2| image:: http://image.iswbm.com/20190511162524.png +.. |image3| image:: http://image.iswbm.com/20190511162607.png +.. |image4| image:: http://image.iswbm.com/20190511162716.png +.. |image5| image:: http://image.iswbm.com/20190511162730.png +.. |image6| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_18.md b/source/c04/c04_18.md index 8480088..aedad82 100644 --- a/source/c04/c04_18.md +++ b/source/c04/c04_18.md @@ -1,354 +1,171 @@ -# 4.18 详细的 Mac 使用指南 +# 4.18 如何用好 Python的用户环境? ![](http://image.iswbm.com/20200602135014.png) -## 4.18.1 iTerm2 +在之前写过一篇关于虚拟环境使用的文章 :[Python 虚拟环境使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&chksm=e886669bdff1ef8d82aae3a231ef0651f82d5e97cf1e64aceda00e686119900518c202dc9b1b&scene=21#wechat_redirect). -介绍一下快捷键 +但是还没有好好的介绍一下 Python 的用户环境,原因是自己一直没遇到要使用 `用户环境` 的使用场景,所以就一直懒得写。 -``` -# 分窗口操作 -shift + command + d(横切)command + d(竖切) - -2、历史信息查找和粘贴:command + f,呼出查找功能,找到后 tab 键可以选中找到的文本,通过option + 回车粘贴。 - -3、自动完成:command + ; ,呼出自动完成窗口,根据上下文提供内容选择项 -# 粘贴历史 -shift + command + h - -# 回放功能 -option + command + b - -# 光标去哪了? -command + / - -# 在所有的窗口中搜索 -option + command + e -``` - -## 4.18.2 截图工具 - -1. Xnip:取色+丰富的标注功能+滚动截屏; -2. [Snip](https://snip.qq.com/):小巧轻量+滚动截屏(不能从App Store下载,亲测已经失效); -3. QQ:截屏+录屏+OCR文字识别; -4. Snipaste:贴屏功能; -5. ScreenShot PSD:psd 文件,每种元素都有单独的图层。 - -## 4.18.3 快捷操作 - -``` -# 查看桌面,并不是返回桌面,再按一下就还原原来的窗口了 -fn + f11 # 通常情况下 -f11 # 在外接键盘下,请先设置 F1,F2 这些键为标准功能键,偏好设置->键盘 - -# 最大化窗口与取消 -command + ctrl + f - -# 最小化/隐藏窗口 -command + h - -# 最小化除了当前active窗口之外的所有窗口 -comand + option + h - -# 隐藏除当前窗口外的其他所有窗口 -command + opthon + h - -# 关闭除访达外的其他程序 -command + q - -# 在不同的程序间切换 -command + tab -command + shift + tab - -# 在一个程序的不同窗口切换 -command + ~ - -# 强制退出程序 -command + option + esc - -# 删除光标处到行首 -command + backspace(删除键) - -# 新建窗口打开页面 -command + 鼠标左键 -``` - -查询汇率 - -``` -command + space -输入 1港币或者1美元 -``` +恰巧这两天,自己遇到了一个使用用户环境的体验可以完爆虚拟环境的案例,就拿出来分享一下。 -将网页图片保存到本地 +## 1. 我的使用背景 -``` -直接拖动图片 -如果不仍想让访达窗口保持在最前面,就按住 command 键 -``` +公司有数以万计的服务器,为了对实现对访问记录进行集中管理以及出于安全考虑,每台服务器都有访问限制,必须使用公司的跳板机才能登陆。 -显示控制台 +每个公司的员工在跳板机上都有自己的用户、 家目录,对于很多需要 root 权限的操作,是高度受限制的。 -``` -原生:ctrl+shift+L -由于与 MWeb 的插入链接冲突,而Mweb 又没有修改快捷键的入口,所以我将启动台的快捷键改成:ctrl+option+L - -要卸载 app 的话 -1. ctrl+option+L 显示启动台 -2. 再按 ctrl+option(默认,不需要设置) 就会出现抖动效果,点击 x 就可以卸载了。 -``` +比如我现在我要在跳板机上实现远程登陆大批量的机器进行一些维护工作,当然我这里使用的还是 Python 来实现,这个 Python 脚本里有一些依赖库(比如 之前介绍过的 paramiko 这个神器),在跳板机上中并没有安装。 +![](http://image.iswbm.com/20200427180207.png) +做为普通用户的你,是没有权限安装第三方包的。 -设置我自定义的系统偏好设置 +![](http://image.iswbm.com/20200427180042.png) -![](http://image.iswbm.com/image-20200704192441091.png) +问题就来了,我如何才能在跳板机中使用 paramiko 这个包呢? -设置打开访达快捷键 +## 2. 为何不使用虚拟环境? -1. 打开 『自动操作』 -2. 新建文稿 -3. 选择『服务』 或者 『自动操作』(因为不同版本的 macOS名字不同) -4. 进行如下设置 +既然不能对全局的 Python 环境进行更改,那我完全可以自己再创建一个环境,只要这个环境里事先装好 paramiko 这个包不就好了。 -![](http://image.iswbm.com/image-20200704194215498.png) +因此,使用虚拟环境是一种解决方案,但它并不是一个完美的解决方案。 -5. 取消勾选:因为我要设置的快捷键与它冲突 +**原因有以下几点**: -![、](http://image.iswbm.com/image-20200704195011274.png) +1、 创建虚拟环境的过程,步骤较多,比较复杂。这里的复杂是相对于我后面要使用的用户环境而言。 -6. 设置快捷键 +2、 虚拟环境是包含一整个 Python 解释器,存在大量与系统重复的包,size比较大,并不轻便。 -![](http://image.iswbm.com/image-20200704195122336.png) +3、 使用 console 模式调试的话,进入很不方便 -## 4.18.4 系统设置 +![](http://image.iswbm.com/20200427182334.png) -关闭仪表盘 +就算你不使用 console 模式,你调用脚本的方式,也会很奇怪,你得这样 +```python +$ zabbix_env/bin/python demo.py ``` -点击系统偏好设置 -> 调度中心 -> 仪表盘 -> 关闭 -``` - -finder的显示 - -![](http://image.iswbm.com/20190810161513.png) - - - -**[防止电脑温度过高](https://mp.weixin.qq.com/s/qKQO616vxADFp1cVtA62Cw)** -1. 不使用 Adobe Flash 播放器(改用 HTML5播放器),因为其效能极低,会耗费大量的系统资源,导致电脑温度快速上升。 - -2. 不将电脑放在软的地方,如沙发,枕头等,可以买个散热支架。 - - ![来自Mac派](http://image.iswbm.com/20190810162000.png) - - 3. 打开「活动监视器」(Alfred就可以打开),杀掉暂没用且cpu使用率最高的程序 - - ![](http://image.iswbm.com/20190810162315.png) - - 4. MacBook Pro CPU 温度在 5、60℃ 的时候,风扇会转到两三千转每分钟,只有 CPU 温度达到 70 多度或更高时,才会高速运转降温。但这时 Mac 已经很热了。 - - 我们可以借用软件,手动让散热风扇全速运转,这样就能更快的散热。常用的软件有 Macs Fan Control、TG Pro、smcFanControl,三个用下来我比较推荐 Macs Fan Control。 - - Macs Fan Control 可以查看各 CPU 核心的温度、主板、电池、内存温度等。可以分别调节两个风扇的转速,也可以设定条件自动调整转速。安装后就可以在系统状态栏看到电脑目前的温度和转速。 - - 风扇转得快了,散热自然也快了,但是风扇声音也更大了。建议只在非常烫(超过60℃?)的时候开启。 - -## 4.18.5 brew 的使用 - -设置国内源 +如果你不想使用这样,可以给这个脚本加个可执行权限,并在脚本的第一行指定你的解释器,省去了一点点麻烦,可即便如此,我仍然感觉很别扭。 ```shell -git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git - -git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git - -git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git +[wangbm@35ha02 ~]$ cat demo.py +#!/home/wangbm/zabbix_env/bin/python -brew update +import zabbix_api +[wangbm@35ha02 ~]$ +[wangbm@35ha02 ~]$ +[wangbm@35ha02 ~]$ chmod +x demo.py +[wangbm@35ha02 ~]$ +[wangbm@35ha02 ~]$ ./demo.py # 可以执行,没有报错 +[wangbm@35ha02 ~]$ ``` -如果要还原 -```shell -git -C "$(brew --repo)" remote set-url origin https://github.com/Homebrew/brew.git -git -C "$(brew --repo homebrew/core)" remote set-url origin https://github.com/Homebrew/homebrew-core.git +你可能会问我:为什么不使用 virtualenv + virtualenvwrapper ,这样可以使用 workon 进入虚拟环境。 -git -C "$(brew --repo homebrew/cask)" remote set-url origin https://github.com/Homebrew/homebrew-cask.git +原因是跳板机里的都是很古老的包,你看上面的 Python 还是 2.7.5 呢,所以你所说的那些工具通通没有。 -brew update -``` -安装docker -```shell -brew cask install docker -``` +## 3. 用户环境原理 -## 4.18.6 访达使用技巧 +这里要介绍的这种方案(**用户环境**),可能很多人都没有使用过,甚至没有听过,它算是一个冷门但是非常好用的功能。 -详细请看这篇文章([MacOS实用技巧之Finder(访达)的使用](https://www.jianshu.com/p/3666e6954e8a)),非常好的教程。 - -**跳转技巧** - -```shell -# 快速打开访达:先打开搜索,再打开个人家目录 -打开搜索:command + option(alt) + space -关闭标签页:command + shift + h +操作之前 ,先简单介绍一下它。 -# 返回父级文件夹 -command + ↑ +先提一个问题,Python 在查找导入包时,如果我们多个路径都有这个包,那 Python 如何确定应该从哪个路径进行导入呢? -# 进入文件夹 -command + ↓ +答案是, 搜索导入路径是有优先级的,你可以通过 sys.path 进行查看。 -# 前进 后退 -command + [ -comand + ] - -# 快速跳转至第一个文件或最后一个文件 -option + ↑ -option + ↓ - - -# 打开指定路径(前提访达得是激活状态的窗口) -# 注意在这里,可以使用 tab 补全 -shift + command + g +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path) +['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] +>>> ``` +可以看到路径 `/home/wangbm/.local/lib/python2.7/site-packages` 是优先于 `/usr/lib64/python2.7/site-packages` 路径的。 +这就是 **用户环境** 的原理,只要我们将包装在自己家目录下,就可以优先于全局环境中进行查找。 -**操作文件与文件夹** - -```shell -# enter -重命名文件夹 - -# 选中所有文件,并将这些文件归档入一个新的文件夹 -右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 +使用起来,可以做到用户无感知,跟使用原生的全局环境并没有区别。 +## 4. 具体操作方法 -# 选择 -点击 -> 拖拽 -如果想要取消选中,就 command + 点击 +创建一个用户环境,并安装上你所需要的包,一条命令就能搞定,这可比虚拟环境简单方便多了。 +那么怎么操作呢? -# 打开最近使用过的文件夹 -comand + shift + f - -# 显示/隐藏文件 -command + shift + . - -# 查看文件/夹 详情 -command + i - -# mac 中拷贝和复制不一样 -command + c 拷贝 -command + d 复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option -command + v 粘贴 -command + option + v 称动 ,或者使用鼠标拖动 - -# 可以设置搜索的范围 -command + f - -# 新建文件夹 -command + shift + n - -# 关闭访达标签页,如果是最后一个标签页,则关闭访达 -command + w -``` - -**定制服务(复制文件路径)** +只要你在使用 pip 安装包时,加上 `--user` 参数,pip 就会将其安装在当前用户的 `~/.local/lib/python2.x/site-packages` 下,而其他用户的 python 则不会受影响。 ```shell -# 复制文件路径,有两种方法 -# 【第一种】:快捷键 -command + option + c -# 若你使用 alfred ,快捷键会冲突,解决方法:先右键,再 option,选择将 xx 拷贝为路径名称 - -# 第二种:使用服务 -参考 https://sspai.com/post/33422 +$ pip install --user pkg ``` -**在 iTerm2中打开访达** +这里要注意的是,不能使用这种方式,亲测它会将包装到全局环境下,具体原因我还没有深究。 ```shell -# 在当前目录打开 -open . - -# 在指定目录打开 -open ~/Code +$ python -m pip install --user pkg ``` -搜索时,优先搜索当前文件夹:访达的偏好设置 - -![](http://image.iswbm.com/image-20200704192031119.png) - -此时如果你想要搜索电脑全局,那么有两种方法 - -1. Command + option + 空格 -2. command + 空格 - -## 4.18.7 使用小鹤双拼 - -2018 款的 MBP 系统是 10.13.6 ,这个系统支持的双拼是自然码,若想使用小鹤双拼,可以使用如下命令 - -```shell -defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 -``` - -同样的,还有更多的方案,都可以使用命令来修改 +为了让你理解这个过程,我这里来举个例子,并且验证其是否可以做到用户隔离。 ```shell -全拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 0 - -智能 ABC:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 1 - -微软双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 2 - -紫光双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 3 - -小鹤双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 - -自然码:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 5 - -拼音加加:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 6 +# 在全局环境中未安装 requests +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ su - wangbm + +# 由于用户环境继承自全局环境,这里也未安装 +[wangbm@localhost ~]$ pip list | grep requests +[wangbm@localhost ~]$ pip install --user requests +[wangbm@localhost ~]$ pip list | grep requests +requests (2.22.0) +[wangbm@localhost ~]$ + +# 从 Location 属性可发现 requests 只安装在当前用户环境中 +[wangbm@localhost ~]$ pip show requests +--- +Metadata-Version: 2.1 +Name: requests +Version: 2.22.0 +Summary: Python HTTP for Humans. +Home-page: http://python-requests.org +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +Installer: pip +License: Apache 2.0 +Location: /home/wangbm/.local/lib/python2.7/site-packages +[wangbm@localhost ~]$ exit +logout -搜狗双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 7 +# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ ``` -练习的话,可以使用这两个网站: - -练习单字:https://api.ihint.me/shuang/ - -练习文章:https://api.ihint.me/zi/ - -对应的 github:https://github.com/BlueSky-07/Shuang - -## 4.18.8 输入法切换 BUG +有了这个思路,我就可以先在其他机器(前提自己必须拥有管理员权限 )上,创建一个用户环境,并且安装上 paramiko 这个包。 -问题是由 `TISwitcher` 引起的。 +然后将这个用户环境,压缩拷贝至跳板机自己的家目录下的 `.local/lib` 目录下并解压。 -当你按住 `control` 键,不停的敲 `空格` 就可了看到这个进程的面貌了。 +然后直接使用 python 进入 console 模式,现在已经可以直接使用 paramiko 这个包了。 -对于使用 `command + space` 切换输入法的, `control` 换成 `command` 即可。 `TISwitcher` 干掉没有任何影响。顶多就是切换输入法时,看不到切换状态而已。 +![](http://image.iswbm.com/20200427185854.png) -解决方法如下: -1. 打开 `Activity Monitor` -2. 找到 `TISwitcher` 这个进程,干掉就 OK 了 -3. 为了防止重启后,这个进程再次启动, 直接删掉 `/System/Library/CoreServices/Menu Extras/TextInput.menu/Contents/SharedSupport/TISwitcher.app` - - - -## 参考文章 - -1. [Mac 上值得推荐的录屏软件](https://mp.weixin.qq.com/s/cvS6BLI53JFQY2P3rvg9Xw) -2. [Mac 连显示器或电视需要买什么线?](https://mp.weixin.qq.com/s/V8A_1GBxtlN2WZrcTsi-YQ) -3. [新手如何快速入门 Mac 的使用?](https://mp.weixin.qq.com/s/55_R1xJ5fv8F8P9Nin93Ww) - ---- ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c04/c04_18.rst b/source/c04/c04_18.rst index af90b56..cef8a2c 100644 --- a/source/c04/c04_18.rst +++ b/source/c04/c04_18.rst @@ -1,386 +1,197 @@ -4.18 详细的 Mac 使用指南 -======================== +4.18 如何用好 Python的用户环境? +================================ |image0| -4.18.1 iTerm2 -------------- +在之前写过一篇关于虚拟环境使用的文章 :\ `Python +虚拟环境使用指南 `__. -介绍一下快捷键 +但是还没有好好的介绍一下 Python 的用户环境,原因是自己一直没遇到要使用 +``用户环境`` 的使用场景,所以就一直懒得写。 -:: +恰巧这两天,自己遇到了一个使用用户环境的体验可以完爆虚拟环境的案例,就拿出来分享一下。 - # 分窗口操作 - shift + command + d(横切)command + d(竖切) - - 2、历史信息查找和粘贴:command + f,呼出查找功能,找到后 tab 键可以选中找到的文本,通过option + 回车粘贴。 - - 3、自动完成:command + ; ,呼出自动完成窗口,根据上下文提供内容选择项 - # 粘贴历史 - shift + command + h - - # 回放功能 - option + command + b - - # 光标去哪了? - command + / - - # 在所有的窗口中搜索 - option + command + e - -4.18.2 截图工具 +1. 我的使用背景 --------------- -1. Xnip:取色+丰富的标注功能+滚动截屏; -2. `Snip `__\ :小巧轻量+滚动截屏(不能从App - Store下载,亲测已经失效); -3. QQ:截屏+录屏+OCR文字识别; -4. Snipaste:贴屏功能; -5. ScreenShot PSD:psd 文件,每种元素都有单独的图层。 - -4.18.3 快捷操作 ---------------- - -:: - - # 查看桌面,并不是返回桌面,再按一下就还原原来的窗口了 - fn + f11 # 通常情况下 - f11 # 在外接键盘下,请先设置 F1,F2 这些键为标准功能键,偏好设置->键盘 - - # 最大化窗口与取消 - command + ctrl + f - - # 最小化/隐藏窗口 - command + h - - # 最小化除了当前active窗口之外的所有窗口 - comand + option + h - - # 隐藏除当前窗口外的其他所有窗口 - command + opthon + h - - # 关闭除访达外的其他程序 - command + q - - # 在不同的程序间切换 - command + tab - command + shift + tab - - # 在一个程序的不同窗口切换 - command + ~ - - # 强制退出程序 - command + option + esc - - # 删除光标处到行首 - command + backspace(删除键) - - # 新建窗口打开页面 - command + 鼠标左键 - -查询汇率 - -:: - - command + space - 输入 1港币或者1美元 - -将网页图片保存到本地 - -:: - - 直接拖动图片 - 如果不仍想让访达窗口保持在最前面,就按住 command 键 +公司有数以万计的服务器,为了对实现对访问记录进行集中管理以及出于安全考虑,每台服务器都有访问限制,必须使用公司的跳板机才能登陆。 -显示控制台 +每个公司的员工在跳板机上都有自己的用户、 家目录,对于很多需要 root +权限的操作,是高度受限制的。 -:: - - 原生:ctrl+shift+L - 由于与 MWeb 的插入链接冲突,而Mweb 又没有修改快捷键的入口,所以我将启动台的快捷键改成:ctrl+option+L - - 要卸载 app 的话 - 1. ctrl+option+L 显示启动台 - 2. 再按 ctrl+option(默认,不需要设置) 就会出现抖动效果,点击 x 就可以卸载了。 - -设置我自定义的系统偏好设置 +比如我现在我要在跳板机上实现远程登陆大批量的机器进行一些维护工作,当然我这里使用的还是 +Python 来实现,这个 Python 脚本里有一些依赖库(比如 之前介绍过的 +paramiko 这个神器),在跳板机上中并没有安装。 |image1| -设置打开访达快捷键 - -1. 打开 『自动操作』 -2. 新建文稿 -3. 选择『服务』 或者 『自动操作』(因为不同版本的 macOS名字不同) -4. 进行如下设置 +做为普通用户的你,是没有权限安装第三方包的。 |image2| -5. 取消勾选:因为我要设置的快捷键与它冲突 - -.. figure:: http://image.iswbm.com/image-20200704195011274.png - :alt: 、 - - 、 - -6. 设置快捷键 - -|image3| - -4.18.4 系统设置 ---------------- - -关闭仪表盘 - -:: +问题就来了,我如何才能在跳板机中使用 paramiko 这个包呢? - 点击系统偏好设置 -> 调度中心 -> 仪表盘 -> 关闭 +2. 为何不使用虚拟环境? +----------------------- -finder的显示 +既然不能对全局的 Python +环境进行更改,那我完全可以自己再创建一个环境,只要这个环境里事先装好 +paramiko 这个包不就好了。 -|image4| - -`防止电脑温度过高 `__ - -1. 不使用 Adobe Flash 播放器(改用 - HTML5播放器),因为其效能极低,会耗费大量的系统资源,导致电脑温度快速上升。 - -2. 不将电脑放在软的地方,如沙发,枕头等,可以买个散热支架。 - - .. figure:: http://image.iswbm.com/20190810162000.png - :alt: 来自Mac派 - - 来自Mac派 - - 3. 打开「活动监视器」(Alfred就可以打开),杀掉暂没用且cpu使用率最高的程序 +因此,使用虚拟环境是一种解决方案,但它并不是一个完美的解决方案。 - |image5| +**原因有以下几点**\ : - 4. MacBook Pro CPU 温度在 5、60℃ - 的时候,风扇会转到两三千转每分钟,只有 CPU 温度达到 70 - 多度或更高时,才会高速运转降温。但这时 Mac 已经很热了。 +1、 +创建虚拟环境的过程,步骤较多,比较复杂。这里的复杂是相对于我后面要使用的用户环境而言。 - 我们可以借用软件,手动让散热风扇全速运转,这样就能更快的散热。常用的软件有 - Macs Fan Control、TG Pro、smcFanControl,三个用下来我比较推荐 Macs - Fan Control。 - - Macs Fan Control 可以查看各 CPU - 核心的温度、主板、电池、内存温度等。可以分别调节两个风扇的转速,也可以设定条件自动调整转速。安装后就可以在系统状态栏看到电脑目前的温度和转速。 - - 风扇转得快了,散热自然也快了,但是风扇声音也更大了。建议只在非常烫(超过60℃?)的时候开启。 - -4.18.5 brew 的使用 ------------------- - -设置国内源 - -.. code:: shell +2、 虚拟环境是包含一整个 Python +解释器,存在大量与系统重复的包,size比较大,并不轻便。 - git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git +3、 使用 console 模式调试的话,进入很不方便 - git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git - - git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git - - brew update - -如果要还原 - -.. code:: shell - - git -C "$(brew --repo)" remote set-url origin https://github.com/Homebrew/brew.git - - git -C "$(brew --repo homebrew/core)" remote set-url origin https://github.com/Homebrew/homebrew-core.git - - git -C "$(brew --repo homebrew/cask)" remote set-url origin https://github.com/Homebrew/homebrew-cask.git - - brew update - -安装docker - -.. code:: shell +|image3| - brew cask install docker +就算你不使用 console 模式,你调用脚本的方式,也会很奇怪,你得这样 -4.18.6 访达使用技巧 -------------------- +.. code:: python -详细请看这篇文章(\ `MacOS实用技巧之Finder(访达)的使用 `__\ ),非常好的教程。 + $ zabbix_env/bin/python demo.py -**跳转技巧** +如果你不想使用这样,可以给这个脚本加个可执行权限,并在脚本的第一行指定你的解释器,省去了一点点麻烦,可即便如此,我仍然感觉很别扭。 .. code:: shell - # 快速打开访达:先打开搜索,再打开个人家目录 - 打开搜索:command + option(alt) + space - 关闭标签页:command + shift + h + [wangbm@35ha02 ~]$ cat demo.py + #!/home/wangbm/zabbix_env/bin/python - # 返回父级文件夹 - command + ↑ + import zabbix_api + [wangbm@35ha02 ~]$ + [wangbm@35ha02 ~]$ + [wangbm@35ha02 ~]$ chmod +x demo.py + [wangbm@35ha02 ~]$ + [wangbm@35ha02 ~]$ ./demo.py # 可以执行,没有报错 + [wangbm@35ha02 ~]$ - # 进入文件夹 - command + ↓ +你可能会问我:为什么不使用 virtualenv + virtualenvwrapper ,这样可以使用 +workon 进入虚拟环境。 - # 前进 后退 - command + [ - comand + ] +原因是跳板机里的都是很古老的包,你看上面的 Python 还是 2.7.5 +呢,所以你所说的那些工具通通没有。 - # 快速跳转至第一个文件或最后一个文件 - option + ↑ - option + ↓ - - - # 打开指定路径(前提访达得是激活状态的窗口) - # 注意在这里,可以使用 tab 补全 - shift + command + g - -**操作文件与文件夹** - -.. code:: shell +3. 用户环境原理 +--------------- - # enter - 重命名文件夹 +这里要介绍的这种方案(\ **用户环境**\ ),可能很多人都没有使用过,甚至没有听过,它算是一个冷门但是非常好用的功能。 - # 选中所有文件,并将这些文件归档入一个新的文件夹 - 右键 -> 用所选项目新建的文件夹(Ctrl+Command+n) -> 回车,重命名 +操作之前 ,先简单介绍一下它。 +先提一个问题,Python 在查找导入包时,如果我们多个路径都有这个包,那 +Python 如何确定应该从哪个路径进行导入呢? - # 选择 - 点击 -> 拖拽 - 如果想要取消选中,就 command + 点击 +答案是, 搜索导入路径是有优先级的,你可以通过 sys.path 进行查看。 +.. code:: python - # 打开最近使用过的文件夹 - comand + shift + f + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path) + ['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] + >>> - # 显示/隐藏文件 - command + shift + . +可以看到路径 ``/home/wangbm/.local/lib/python2.7/site-packages`` +是优先于 ``/usr/lib64/python2.7/site-packages`` 路径的。 - # 查看文件/夹 详情 - command + i +这就是 **用户环境** +的原理,只要我们将包装在自己家目录下,就可以优先于全局环境中进行查找。 - # mac 中拷贝和复制不一样 - command + c 拷贝 - command + d 复制(会多出一个副本),或者使用鼠标拖动,但是记住要按option - command + v 粘贴 - command + option + v 称动 ,或者使用鼠标拖动 +使用起来,可以做到用户无感知,跟使用原生的全局环境并没有区别。 - # 可以设置搜索的范围 - command + f +4. 具体操作方法 +--------------- - # 新建文件夹 - command + shift + n +创建一个用户环境,并安装上你所需要的包,一条命令就能搞定,这可比虚拟环境简单方便多了。 - # 关闭访达标签页,如果是最后一个标签页,则关闭访达 - command + w +那么怎么操作呢? -**定制服务(复制文件路径)** +只要你在使用 pip 安装包时,加上 ``--user`` 参数,pip +就会将其安装在当前用户的 ``~/.local/lib/python2.x/site-packages`` +下,而其他用户的 python 则不会受影响。 .. code:: shell - # 复制文件路径,有两种方法 - # 【第一种】:快捷键 - command + option + c - # 若你使用 alfred ,快捷键会冲突,解决方法:先右键,再 option,选择将 xx 拷贝为路径名称 + $ pip install --user pkg - # 第二种:使用服务 - 参考 https://sspai.com/post/33422 - -**在 iTerm2中打开访达** +这里要注意的是,不能使用这种方式,亲测它会将包装到全局环境下,具体原因我还没有深究。 .. code:: shell - # 在当前目录打开 - open . - - # 在指定目录打开 - open ~/Code - -搜索时,优先搜索当前文件夹:访达的偏好设置 - -|image6| - -此时如果你想要搜索电脑全局,那么有两种方法 - -1. Command + option + 空格 -2. command + 空格 + $ python -m pip install --user pkg -4.18.7 使用小鹤双拼 -------------------- - -2018 款的 MBP 系统是 10.13.6 -,这个系统支持的双拼是自然码,若想使用小鹤双拼,可以使用如下命令 +为了让你理解这个过程,我这里来举个例子,并且验证其是否可以做到用户隔离。 .. code:: shell - defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 - -同样的,还有更多的方案,都可以使用命令来修改 - -.. code:: shell - - 全拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 0 - - 智能 ABC:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 1 - - 微软双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 2 - - 紫光双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 3 - - 小鹤双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 4 - - 自然码:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 5 - - 拼音加加:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 6 - - 搜狗双拼:defaults write com.apple.inputmethod.CoreChineseEngineFramework shuangpinLayout 7 - -练习的话,可以使用这两个网站: + # 在全局环境中未安装 requests + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ su - wangbm + + # 由于用户环境继承自全局环境,这里也未安装 + [wangbm@localhost ~]$ pip list | grep requests + [wangbm@localhost ~]$ pip install --user requests + [wangbm@localhost ~]$ pip list | grep requests + requests (2.22.0) + [wangbm@localhost ~]$ + + # 从 Location 属性可发现 requests 只安装在当前用户环境中 + [wangbm@localhost ~]$ pip show requests + --- + Metadata-Version: 2.1 + Name: requests + Version: 2.22.0 + Summary: Python HTTP for Humans. + Home-page: http://python-requests.org + Author: Kenneth Reitz + Author-email: me@kennethreitz.org + Installer: pip + License: Apache 2.0 + Location: /home/wangbm/.local/lib/python2.7/site-packages + [wangbm@localhost ~]$ exit + logout + + # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ + +有了这个思路,我就可以先在其他机器(前提自己必须拥有管理员权限 +)上,创建一个用户环境,并且安装上 paramiko 这个包。 + +然后将这个用户环境,压缩拷贝至跳板机自己的家目录下的 ``.local/lib`` +目录下并解压。 + +然后直接使用 python 进入 console 模式,现在已经可以直接使用 paramiko +这个包了。 -练习单字:https://api.ihint.me/shuang/ - -练习文章:https://api.ihint.me/zi/ - -对应的 github:https://github.com/BlueSky-07/Shuang - -4.18.8 输入法切换 BUG ---------------------- - -问题是由 ``TISwitcher`` 引起的。 - -当你按住 ``control`` 键,不停的敲 ``空格`` 就可了看到这个进程的面貌了。 - -对于使用 ``command + space`` 切换输入法的, ``control`` 换成 ``command`` -即可。 ``TISwitcher`` -干掉没有任何影响。顶多就是切换输入法时,看不到切换状态而已。 - -解决方法如下: - -1. 打开 ``Activity Monitor`` -2. 找到 ``TISwitcher`` 这个进程,干掉就 OK 了 -3. 为了防止重启后,这个进程再次启动, 直接删掉 - ``/System/Library/CoreServices/Menu Extras/TextInput.menu/Contents/SharedSupport/TISwitcher.app`` - -参考文章 --------- - -1. `Mac - 上值得推荐的录屏软件 `__ -2. `Mac - 连显示器或电视需要买什么线? `__ -3. `新手如何快速入门 Mac - 的使用? `__ - --------------- +|image4| -|image7| +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/image-20200704192441091.png -.. |image2| image:: http://image.iswbm.com/image-20200704194215498.png -.. |image3| image:: http://image.iswbm.com/image-20200704195122336.png -.. |image4| image:: http://image.iswbm.com/20190810161513.png -.. |image5| image:: http://image.iswbm.com/20190810162315.png -.. |image6| image:: http://image.iswbm.com/image-20200704192031119.png -.. |image7| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200427180207.png +.. |image2| image:: http://image.iswbm.com/20200427180042.png +.. |image3| image:: http://image.iswbm.com/20200427182334.png +.. |image4| image:: http://image.iswbm.com/20200427185854.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_20.md b/source/c04/c04_20.md index 7ec2fbc..3e477cb 100644 --- a/source/c04/c04_20.md +++ b/source/c04/c04_20.md @@ -1,48 +1,378 @@ -# 4.20 学会使用谷歌搜索引擎 +# 4.20 最全的 pip 使用指南 ![](http://image.iswbm.com/20200602135014.png) -1、按文件类型搜索 加filetype +所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python 为基础封装出各种有利于开发的第三方工具包。 +这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 + +Python 从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 + +当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 + +pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python 的标配。 + +当然也有其他的包管理工具 + +- **distutils**:仅用于打包和安装,严格来讲不算是包管理工具 + +- **setuptools**:distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 egg 的文件格式。 + +- **Pipenv**:一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 + +- 还有其他的,这里不一一列出。 + + + +今天的主角是 pip ,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 + + + +## 1. 查询软件包 + +查询当前环境安装的所有软件包 + +```shell +$ pip list +``` + +查询 pypi 上含有某名字的包 + +```shell +$ pip search pkg +``` + +查询当前环境中可升级的包 + +```shell +$ pip list --outdated +``` + +查询一个包的详细内容 + +```shell +$ pip show pkg +``` + +## 2. 下载软件包 + +在不安装软件包的情况下下载软件包到本地 + +```shell +$ pip download --destination-directory /local/wheels -r requirements.txt +``` + +下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi 上安装。 + +```shell +$ pip install --no-index --find-links=/local/wheels -r requirements.txt +``` + +当然你也从你下载的包中,自己构建生成 wheel 文件 + +```shell +$ pip install wheel +$ pip wheel --wheel-dir=/local/wheels -r requirements.txt +``` + + + +## 3. 安装软件包 + +使用 `pip install ` 可以很方便地从 pypi 上搜索下载并安装 python 包。 + +如下所示 + +```shell +$ pip install requests +``` + +这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 + +**3.1 只从本地安装,而不从 pypi 安装** + +```shell +# 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 +$ pip install --no-index --find-links=/local/wheels pkg +``` + +**3.2 限定版本进行软件包安装** + +以下三种,对单个 python 包的版本进行了约束 + +```shell +# 所安装的包的版本为 2.1.2 +$ pip install pkg==2.1.2 + +# 所安装的包必须大于等于 2.1.2 +$ pip install pkg>=2.1.2 + +# 所安装的包必须小于等于 2.1.2 +$ pip install pkg<=2.1.2 +``` + +以下命令用于管理/控制整个 python 环境的包版本 + +```shell +# 导出依赖包列表 +pip freeze >requirements.txt + +# 从依赖包列表中安装 +pip install -r requirements.txt + +# 确保当前环境软件包的版本(并不确保安装) +pip install -c constraints.txt +``` + + + +**3.3 限制不使用二进制包安装** + +由于默认情况下,wheel 包的平台是运行 pip download 命令 的平台,所以可能出现平台不适配的情况。 + +比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl 就不能在 linux_x86_64 安装。 + +使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 + +比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 + +```shell +# 下载非二进制的包 +$ pip download --no-binary=:all: pkg + +# 安装非二进制的包 +$ pip install pkg --no-binary ``` -流畅的python filetype:pdf + +**3.4 指定代理服务器安装** + +当你身处在一个内网环境中时,无法直接连接公网。这时候你使用`pip install` 安装包,就会失败。 + +面对这种情况,可以有两种方法: + +1. 下载离线包拷贝到内网机器中安装 +2. 使用代理服务器转发请求 + +第一种方法,虽说可行,但有相当多的弊端 + +- 步骤繁杂,耗时耗力 +- 无法处理包的依赖问题 + +这里重点来介绍,第二种方法: + +```shell +$ pip install --proxy [user:passwd@]http_server_ip:port pkg ``` -2、过滤关键字 用减号 +每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:`$HOME/.config/pip/pip.conf` -谷歌搜索 `python parse 库` ,出现的都是 `urllib.parse`,可我实际想要找是 parse 库的相关资料。 +对于这个路径,说明几点 -![](http://image.iswbm.com/20200826102731.png) +- 不同的操作系统,路径各不相同 -那我可以使用 `-` ,过滤掉 urllib 的搜索结果 +```shell +# Linux/Unix: +/etc/pip.conf +~/.pip/pip.conf +~/.config/pip/pip.conf + +# Mac OSX: +~/Library/Application Support/pip/pip.conf +~/.pip/pip.conf +/Library/Application Support/pip/pip.conf + +# Windows: +%APPDATA%\pip\pip.ini +%HOME%\pip\pip.ini +C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) +C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) +``` + +- 若在你的机子上没有此文件,则自行创建即可 + +如何配置,这边给个样例: + +```ini +[global] +index-url = http://mirrors.aliyun.com/pypi/simple/ +# 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port +proxy=http://xxx.xxx.xxx.xxx:8080 + +[install] +# 信任阿里云的镜像源,否则会有警告 +trusted-host=mirrors.aliyun.com ``` -python parse -urllib + +**3.5 安装用户私有软件包** + +很多人可能还不清楚,python 的安装包是可以用户隔离的。 + +如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 + +如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 + +面对这种情况,我们就想能否安装单独为我所用的包呢? + +庆幸的是,还真有。 + +我能想到的有两种方法: + +1. 使用虚拟环境 +2. 将包安装在用户的环境中 + +虚拟环境,之前写过几篇文章,这里不再展开讲。 + +今天的重点是第二种方法,教你如何安装用户私有的包? + +命令也很简单,只要加上 `--user` 参数,pip 就会将其安装在当前用户的 `~/.local/lib/python3.x/site-packages` 下,而其他用户的 python 则不会受影响。 + +```shell +pip install --user pkg ``` -![](http://image.iswbm.com/20200826102136.png) +来举个例子 + +```shell +# 在全局环境中未安装 requests +[root@localhost ~]# pip list | grep requests +[root@localhost ~]# su - wangbm +[root@localhost ~]# -3、必须包含某关键字 用加号 +# 由于用户环境继承自全局环境,这里也未安装 +[wangbm@localhost ~]# pip list | grep requests +[wangbm@localhost ~]# pip install --user requests +[wangbm@localhost ~]# pip list | grep requests +requests (2.22.0) +[wangbm@localhost ~]# +# 从 Location 属性可发现 requests 只安装在当前用户环境中 +[wangbm@ws_compute01 ~]$ pip show requests +--- +Metadata-Version: 2.1 +Name: requests +Version: 2.22.0 +Summary: Python HTTP for Humans. +Home-page: http://python-requests.org +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +Installer: pip +License: Apache 2.0 +Location: /home/wangbm/.local/lib/python2.7/site-packages +[wangbm@localhost ~]$ exit +logout + +# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ ``` -linux常用命令 +centos + +当你身处个人用户环境中,python 导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 + +验证如下: + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path) +['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] +>>> + ``` -4、搜索指定网站 加site +**3.6 延长超时时间** + +若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 +对于这种情况,一般重试几次就好了。 + +但是这样难免有些麻烦,有没有更好的解决方法呢? + +有的,可以通过延长超时时间。 + +```shell +$ pip install --default-timeout=100 ``` -Python冷知识 site:iswbm.com + + + +## 4. 卸载软件包 + +就一条命令,不再赘述 + +```shell +$ pip uninstall pkg ``` -5、链接中包含字符串 加inurl + + +## 5. 升级软件包 + +想要对现有的 python 进行升级,其本质上也是先从 pypi 上下载最新版本的包,再对其进行安装。所以升级也是使用 `pip install`,只不过要加一个参数 `--upgrade`。 ``` -Python冷知识 inurl:python-online +$ pip install --upgrade pkg ``` -6、完全匹配搜索结果 加双绰号包裹 +在升级的时候,其实还有一个不怎么用到的选项 `--upgrade-strategy`,它是用来指定升级策略。 + +它的可选项只有两个: + +- `eager` :升级全部依赖包 +- `only-if-need`:只有当旧版本不能适配新的父依赖包时,才会升级。 +在 pip 10.0 版本之后,这个选项的默认值是 `only-if-need`,因此如下两种写法是一互致的。 + +```shell +pip install --upgrade pkg1 +pip install --upgrade pkg1 --upgrade-strategy only-if-need ``` -"装饰器进阶用法详解" + +## 6. 配置文件 + +由于在使用 pip 安装一些包时,默认会使用 pip 的官方源,所以经常会报网络超时失败。 + +常用的解决办法是,在安装包时,使用 `-i` 参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 + +这时候,其实可以将这个源写进 pip 的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 + +那怎么配置呢?文件文件在哪? + +使用` win+r` 输入 `%APPDATA%` 进入用户资料文件夹,查看有没有一个 pip 的文件夹,若没有则创建之。 + +然后进入这个 文件夹,新建一个 `pip.ini` 的文件,内容如下 + +```ini +[global] +time-out=60 +index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ +[install] +trusted-host=tsinghua.edu.cn ``` + + + + +以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 + +![](http://image.iswbm.com/20191105200041.png) + + + +![](http://image.iswbm.com/20200607174235.png) + + + diff --git a/source/c04/c04_20.rst b/source/c04/c04_20.rst index e393898..c7a4ad6 100644 --- a/source/c04/c04_20.rst +++ b/source/c04/c04_20.rst @@ -1,54 +1,392 @@ -4.20 学会使用谷歌搜索引擎 -========================= +4.20 最全的 pip 使用指南 +======================== |image0| -1、按文件类型搜索 加filetype +所有的 Python 开发者都清楚,Python +之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 +Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python +为基础封装出各种有利于开发的第三方工具包。 -:: +这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 - 流畅的python filetype:pdf +Python +从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 -2、过滤关键字 用减号 +当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 -谷歌搜索 ``python parse 库`` ,出现的都是 -``urllib.parse``\ ,可我实际想要找是 parse 库的相关资料。 +pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python +的标配。 -|image1| +当然也有其他的包管理工具 -那我可以使用 ``-`` ,过滤掉 urllib 的搜索结果 +- **distutils**\ :仅用于打包和安装,严格来讲不算是包管理工具 -:: +- **setuptools**\ :distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 + egg 的文件格式。 - python parse -urllib +- **Pipenv**\ :一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 -|image2| +- 还有其他的,这里不一一列出。 -3、必须包含某关键字 用加号 +今天的主角是 pip +,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 -:: +1. 查询软件包 +------------- - linux常用命令 +centos +查询当前环境安装的所有软件包 -4、搜索指定网站 加site +.. code:: shell -:: + $ pip list - Python冷知识 site:iswbm.com +查询 pypi 上含有某名字的包 -5、链接中包含字符串 加inurl +.. code:: shell -:: + $ pip search pkg + +查询当前环境中可升级的包 + +.. code:: shell + + $ pip list --outdated + +查询一个包的详细内容 + +.. code:: shell + + $ pip show pkg + +2. 下载软件包 +------------- + +在不安装软件包的情况下下载软件包到本地 + +.. code:: shell + + $ pip download --destination-directory /local/wheels -r requirements.txt + +下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi +上安装。 + +.. code:: shell + + $ pip install --no-index --find-links=/local/wheels -r requirements.txt + +当然你也从你下载的包中,自己构建生成 wheel 文件 + +.. code:: shell + + $ pip install wheel + $ pip wheel --wheel-dir=/local/wheels -r requirements.txt + +3. 安装软件包 +------------- + +使用 ``pip install `` 可以很方便地从 pypi 上搜索下载并安装 python +包。 + +如下所示 + +.. code:: shell + + $ pip install requests + +这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 + +**3.1 只从本地安装,而不从 pypi 安装** + +.. code:: shell + + # 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 + $ pip install --no-index --find-links=/local/wheels pkg + +**3.2 限定版本进行软件包安装** + +以下三种,对单个 python 包的版本进行了约束 + +.. code:: shell + + # 所安装的包的版本为 2.1.2 + $ pip install pkg==2.1.2 + + # 所安装的包必须大于等于 2.1.2 + $ pip install pkg>=2.1.2 + + # 所安装的包必须小于等于 2.1.2 + $ pip install pkg<=2.1.2 + +以下命令用于管理/控制整个 python 环境的包版本 + +.. code:: shell + + # 导出依赖包列表 + pip freeze >requirements.txt + + # 从依赖包列表中安装 + pip install -r requirements.txt + + # 确保当前环境软件包的版本(并不确保安装) + pip install -c constraints.txt + +**3.3 限制不使用二进制包安装** + +由于默认情况下,wheel 包的平台是运行 pip download 命令 +的平台,所以可能出现平台不适配的情况。 + +比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl +就不能在 linux_x86_64 安装。 + +使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 + +比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 + +.. code:: shell + + # 下载非二进制的包 + $ pip download --no-binary=:all: pkg + + # 安装非二进制的包 + $ pip install pkg --no-binary + +**3.4 指定代理服务器安装** + +当你身处在一个内网环境中时,无法直接连接公网。这时候你使用\ ``pip install`` +安装包,就会失败。 + +面对这种情况,可以有两种方法: + +1. 下载离线包拷贝到内网机器中安装 +2. 使用代理服务器转发请求 + +第一种方法,虽说可行,但有相当多的弊端 + +- 步骤繁杂,耗时耗力 +- 无法处理包的依赖问题 + +这里重点来介绍,第二种方法: + +.. code:: shell + + $ pip install --proxy [user:passwd@]http_server_ip:port pkg + +每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:\ ``$HOME/.config/pip/pip.conf`` + +对于这个路径,说明几点 + +- 不同的操作系统,路径各不相同 - Python冷知识 inurl:python-online +.. code:: shell -6、完全匹配搜索结果 加双绰号包裹 + # Linux/Unix: + /etc/pip.conf + ~/.pip/pip.conf + ~/.config/pip/pip.conf + + # Mac OSX: + ~/Library/Application Support/pip/pip.conf + ~/.pip/pip.conf + /Library/Application Support/pip/pip.conf + + # Windows: + %APPDATA%\pip\pip.ini + %HOME%\pip\pip.ini + C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) + C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) + +- 若在你的机子上没有此文件,则自行创建即可 + +如何配置,这边给个样例: + +.. code:: ini + + [global] + index-url = http://mirrors.aliyun.com/pypi/simple/ + + # 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port + proxy=http://xxx.xxx.xxx.xxx:8080 + + [install] + # 信任阿里云的镜像源,否则会有警告 + trusted-host=mirrors.aliyun.com + +**3.5 安装用户私有软件包** + +很多人可能还不清楚,python 的安装包是可以用户隔离的。 + +如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 + +如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 + +面对这种情况,我们就想能否安装单独为我所用的包呢? + +庆幸的是,还真有。 + +我能想到的有两种方法: + +1. 使用虚拟环境 +2. 将包安装在用户的环境中 + +虚拟环境,之前写过几篇文章,这里不再展开讲。 + +今天的重点是第二种方法,教你如何安装用户私有的包? + +命令也很简单,只要加上 ``--user`` 参数,pip 就会将其安装在当前用户的 +``~/.local/lib/python3.x/site-packages`` 下,而其他用户的 python +则不会受影响。 + +.. code:: shell + + pip install --user pkg + +来举个例子 + +.. code:: shell + + # 在全局环境中未安装 requests + [root@localhost ~]# pip list | grep requests + [root@localhost ~]# su - wangbm + [root@localhost ~]# + + # 由于用户环境继承自全局环境,这里也未安装 + [wangbm@localhost ~]# pip list | grep requests + [wangbm@localhost ~]# pip install --user requests + [wangbm@localhost ~]# pip list | grep requests + requests (2.22.0) + [wangbm@localhost ~]# + + # 从 Location 属性可发现 requests 只安装在当前用户环境中 + [wangbm@ws_compute01 ~]$ pip show requests + --- + Metadata-Version: 2.1 + Name: requests + Version: 2.22.0 + Summary: Python HTTP for Humans. + Home-page: http://python-requests.org + Author: Kenneth Reitz + Author-email: me@kennethreitz.org + Installer: pip + License: Apache 2.0 + Location: /home/wangbm/.local/lib/python2.7/site-packages + [wangbm@localhost ~]$ exit + logout + + # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ + +当你身处个人用户环境中,python +导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 + +验证如下: + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path) + ['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] + >>> + +**3.6 延长超时时间** + +若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 + +对于这种情况,一般重试几次就好了。 + +但是这样难免有些麻烦,有没有更好的解决方法呢? + +有的,可以通过延长超时时间。 + +.. code:: shell + + $ pip install --default-timeout=100 + +4. 卸载软件包 +------------- + +就一条命令,不再赘述 + +.. code:: shell + + $ pip uninstall pkg + +5. 升级软件包 +------------- + +想要对现有的 python 进行升级,其本质上也是先从 pypi +上下载最新版本的包,再对其进行安装。所以升级也是使用 +``pip install``\ ,只不过要加一个参数 ``--upgrade``\ 。 :: - "装饰器进阶用法详解" + $ pip install --upgrade pkg + +在升级的时候,其实还有一个不怎么用到的选项 +``--upgrade-strategy``\ ,它是用来指定升级策略。 + +它的可选项只有两个: + +- ``eager`` :升级全部依赖包 +- ``only-if-need``\ :只有当旧版本不能适配新的父依赖包时,才会升级。 + +在 pip 10.0 版本之后,这个选项的默认值是 +``only-if-need``\ ,因此如下两种写法是一互致的。 + +.. code:: shell + + pip install --upgrade pkg1 + pip install --upgrade pkg1 --upgrade-strategy only-if-need + +6. 配置文件 +----------- + +由于在使用 pip 安装一些包时,默认会使用 pip +的官方源,所以经常会报网络超时失败。 + +常用的解决办法是,在安装包时,使用 ``-i`` +参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 + +这时候,其实可以将这个源写进 pip +的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 + +那怎么配置呢?文件文件在哪? + +使用\ ``win+r`` 输入 ``%APPDATA%`` 进入用户资料文件夹,查看有没有一个 +pip 的文件夹,若没有则创建之。 + +然后进入这个 文件夹,新建一个 ``pip.ini`` 的文件,内容如下 + +.. code:: ini + + [global] + time-out=60 + index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ + [install] + trusted-host=tsinghua.edu.cn + +以上几乎包含了 pip +的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 + +|image1| + +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200826102731.png -.. |image2| image:: http://image.iswbm.com/20200826102136.png +.. |image1| image:: http://image.iswbm.com/20191105200041.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_21.md b/source/c04/c04_21.md deleted file mode 100644 index 7b76c17..0000000 --- a/source/c04/c04_21.md +++ /dev/null @@ -1,378 +0,0 @@ -# 4.21 最全的 pip 使用指南,50% 你可能没用过 - -![](http://image.iswbm.com/20200602135014.png) - -所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python 为基础封装出各种有利于开发的第三方工具包。 - -这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 - -Python 从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 - -当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 - -pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python 的标配。 - -当然也有其他的包管理工具 - -- **distutils**:仅用于打包和安装,严格来讲不算是包管理工具 - -- **setuptools**:distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 egg 的文件格式。 - -- **Pipenv**:一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 - -- 还有其他的,这里不一一列出。 - - - -今天的主角是 pip ,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 - - - -## 1. 查询软件包 - -查询当前环境安装的所有软件包 - -```shell -$ pip list -``` - -查询 pypi 上含有某名字的包 - -```shell -$ pip search pkg -``` - -查询当前环境中可升级的包 - -```shell -$ pip list --outdated -``` - -查询一个包的详细内容 - -```shell -$ pip show pkg -``` - -## 2. 下载软件包 - -在不安装软件包的情况下下载软件包到本地 - -```shell -$ pip download --destination-directory /local/wheels -r requirements.txt -``` - -下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi 上安装。 - -```shell -$ pip install --no-index --find-links=/local/wheels -r requirements.txt -``` - -当然你也从你下载的包中,自己构建生成 wheel 文件 - -```shell -$ pip install wheel -$ pip wheel --wheel-dir=/local/wheels -r requirements.txt -``` - - - -## 3. 安装软件包 - -使用 `pip install ` 可以很方便地从 pypi 上搜索下载并安装 python 包。 - -如下所示 - -```shell -$ pip install requests -``` - -这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 - -**3.1 只从本地安装,而不从 pypi 安装** - -```shell -# 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 -$ pip install --no-index --find-links=/local/wheels pkg -``` - -**3.2 限定版本进行软件包安装** - -以下三种,对单个 python 包的版本进行了约束 - -```shell -# 所安装的包的版本为 2.1.2 -$ pip install pkg==2.1.2 - -# 所安装的包必须大于等于 2.1.2 -$ pip install pkg>=2.1.2 - -# 所安装的包必须小于等于 2.1.2 -$ pip install pkg<=2.1.2 -``` - -以下命令用于管理/控制整个 python 环境的包版本 - -```shell -# 导出依赖包列表 -pip freeze >requirements.txt - -# 从依赖包列表中安装 -pip install -r requirements.txt - -# 确保当前环境软件包的版本(并不确保安装) -pip install -c constraints.txt -``` - - - -**3.3 限制不使用二进制包安装** - -由于默认情况下,wheel 包的平台是运行 pip download 命令 的平台,所以可能出现平台不适配的情况。 - -比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl 就不能在 linux_x86_64 安装。 - -使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 - -比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 - -```shell -# 下载非二进制的包 -$ pip download --no-binary=:all: pkg - -# 安装非二进制的包 -$ pip install pkg --no-binary -``` - -**3.4 指定代理服务器安装** - -当你身处在一个内网环境中时,无法直接连接公网。这时候你使用`pip install` 安装包,就会失败。 - -面对这种情况,可以有两种方法: - -1. 下载离线包拷贝到内网机器中安装 -2. 使用代理服务器转发请求 - -第一种方法,虽说可行,但有相当多的弊端 - -- 步骤繁杂,耗时耗力 -- 无法处理包的依赖问题 - -这里重点来介绍,第二种方法: - -```shell -$ pip install --proxy [user:passwd@]http_server_ip:port pkg -``` - -每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:`$HOME/.config/pip/pip.conf` - -对于这个路径,说明几点 - -- 不同的操作系统,路径各不相同 - -```shell -# Linux/Unix: -/etc/pip.conf -~/.pip/pip.conf -~/.config/pip/pip.conf - -# Mac OSX: -~/Library/Application Support/pip/pip.conf -~/.pip/pip.conf -/Library/Application Support/pip/pip.conf - -# Windows: -%APPDATA%\pip\pip.ini -%HOME%\pip\pip.ini -C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) -C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) -``` - -- 若在你的机子上没有此文件,则自行创建即可 - -如何配置,这边给个样例: - -```ini -[global] -index-url = http://mirrors.aliyun.com/pypi/simple/ - -# 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port -proxy=http://xxx.xxx.xxx.xxx:8080 - -[install] -# 信任阿里云的镜像源,否则会有警告 -trusted-host=mirrors.aliyun.com -``` - -**3.5 安装用户私有软件包** - -很多人可能还不清楚,python 的安装包是可以用户隔离的。 - -如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 - -如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 - -面对这种情况,我们就想能否安装单独为我所用的包呢? - -庆幸的是,还真有。 - -我能想到的有两种方法: - -1. 使用虚拟环境 -2. 将包安装在用户的环境中 - -虚拟环境,之前写过几篇文章,这里不再展开讲。 - -今天的重点是第二种方法,教你如何安装用户私有的包? - -命令也很简单,只要加上 `--user` 参数,pip 就会将其安装在当前用户的 `~/.local/lib/python3.x/site-packages` 下,而其他用户的 python 则不会受影响。 - -```shell -pip install --user pkg -``` - -来举个例子 - -```shell -# 在全局环境中未安装 requests -[root@localhost ~]# pip list | grep requests -[root@localhost ~]# su - wangbm -[root@localhost ~]# - -# 由于用户环境继承自全局环境,这里也未安装 -[wangbm@localhost ~]# pip list | grep requests -[wangbm@localhost ~]# pip install --user requests -[wangbm@localhost ~]# pip list | grep requests -requests (2.22.0) -[wangbm@localhost ~]# - -# 从 Location 属性可发现 requests 只安装在当前用户环境中 -[wangbm@ws_compute01 ~]$ pip show requests ---- -Metadata-Version: 2.1 -Name: requests -Version: 2.22.0 -Summary: Python HTTP for Humans. -Home-page: http://python-requests.org -Author: Kenneth Reitz -Author-email: me@kennethreitz.org -Installer: pip -License: Apache 2.0 -Location: /home/wangbm/.local/lib/python2.7/site-packages -[wangbm@localhost ~]$ exit -logout - -# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 -[root@localhost ~]$ pip list | grep requests -[root@localhost ~]$ -``` - -当你身处个人用户环境中,python 导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 - -验证如下: - -```python ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.path) -['', - '/usr/lib64/python27.zip', - '/usr/lib64/python2.7', - '/usr/lib64/python2.7/plat-linux2', - '/usr/lib64/python2.7/lib-tk', - '/usr/lib64/python2.7/lib-old', - '/usr/lib64/python2.7/lib-dynload', - '/home/wangbm/.local/lib/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages/gtk-2.0', - '/usr/lib/python2.7/site-packages', - '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', - '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] ->>> - -``` - -**3.6 延长超时时间** - -若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 - -对于这种情况,一般重试几次就好了。 - -但是这样难免有些麻烦,有没有更好的解决方法呢? - -有的,可以通过延长超时时间。 - -```shell -$ pip install --default-timeout=100 -``` - - - -## 4. 卸载软件包 - -就一条命令,不再赘述 - -```shell -$ pip uninstall pkg -``` - - - -## 5. 升级软件包 - -想要对现有的 python 进行升级,其本质上也是先从 pypi 上下载最新版本的包,再对其进行安装。所以升级也是使用 `pip install`,只不过要加一个参数 `--upgrade`。 - -``` -$ pip install --upgrade pkg -``` - -在升级的时候,其实还有一个不怎么用到的选项 `--upgrade-strategy`,它是用来指定升级策略。 - -它的可选项只有两个: - -- `eager` :升级全部依赖包 -- `only-if-need`:只有当旧版本不能适配新的父依赖包时,才会升级。 - -在 pip 10.0 版本之后,这个选项的默认值是 `only-if-need`,因此如下两种写法是一互致的。 - -```shell -pip install --upgrade pkg1 -pip install --upgrade pkg1 --upgrade-strategy only-if-need -``` - -## 6. 配置文件 - -由于在使用 pip 安装一些包时,默认会使用 pip 的官方源,所以经常会报网络超时失败。 - -常用的解决办法是,在安装包时,使用 `-i` 参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 - -这时候,其实可以将这个源写进 pip 的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 - -那怎么配置呢?文件文件在哪? - -使用` win+r` 输入 `%APPDATA%` 进入用户资料文件夹,查看有没有一个 pip 的文件夹,若没有则创建之。 - -然后进入这个 文件夹,新建一个 `pip.ini` 的文件,内容如下 - -```ini -[global] -time-out=60 -index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ -[install] -trusted-host=tsinghua.edu.cn -``` - - - - - -以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 - -![](http://image.iswbm.com/20191105200041.png) - - - -![](http://image.iswbm.com/20200607174235.png) - - - diff --git a/source/c04/c04_22.md b/source/c04/c04_22.md index 02a0c79..c3043b3 100644 --- a/source/c04/c04_22.md +++ b/source/c04/c04_22.md @@ -2,13 +2,5 @@ ![](http://image.iswbm.com/20200602135014.png) -## 开启阅读模式 - -开启阅读模式:chrome://flags/#enable-reader-mode - -![](http://image.iswbm.com/20191201103653.png) - -开启完成后,需要重启浏览器,你可以随便打开一篇博客,然后在地址栏右边会有一个阅读模式的按钮。 - diff --git a/source/c04/c04_22.rst b/source/c04/c04_22.rst index 088ec9d..d7da5a8 100644 --- a/source/c04/c04_22.rst +++ b/source/c04/c04_22.rst @@ -3,15 +3,5 @@ |image0| -开启阅读模式 ------------- - -开启阅读模式:chrome://flags/#enable-reader-mode - -|image1| - -开启完成后,需要重启浏览器,你可以随便打开一篇博客,然后在地址栏右边会有一个阅读模式的按钮。 - .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20191201103653.png diff --git a/source/c04/c04_23.md b/source/c04/c04_23.md deleted file mode 100644 index 0bfd154..0000000 --- a/source/c04/c04_23.md +++ /dev/null @@ -1,56 +0,0 @@ -# 4.23 电脑使用技巧 - -![](http://image.iswbm.com/20200602135014.png) - -## 1. 添加右键菜单 - -参考 Sublime Text3的添加方法 - -在sublime的安装目录下新增一个文件:`sublime_addright.inf`,内容如下,然后右键选择安装。 - -``` -[Version] -Signature="$Windows NT$" - -[DefaultInstall] -AddReg=SublimeText3 - -[SublimeText3] -hkcr,"*\\shell\\SublimeText3",,,"Edit with SublimeText3" -hkcr,"*\\shell\\SublimeText3\\command",,,"""%1%\sublime_text.exe"" ""%%1"" %%*" -hkcr,"Directory\shell\SublimeText3",,,"Edit with SublimeText3" -hkcr,"*\\shell\\SublimeText3","Icon",0x20000,"%1%\sublime_text.exe, 0" -hkcr,"Directory\shell\SublimeText3\command",,,"""%1%\sublime_text.exe"" ""%%1""" -``` - -## 2. Rime五筆安裝方法 - -下載地址:https://rime.im/download/ - -五筆配置:https://github.com/rime/rime-wubi - -下載下來是一個zip包,裏面都是五筆的配置文件。 - -``` -wubi_pinyin.schema.yaml -wubi_trad.schema.yaml -wubi86.dict.yaml -wubi86.schema.yaml -``` - -將這些文件複製到你的rime安裝目錄下的data文件夾下 - -然後在 data 目錄下編輯 default.yaml 添加,兩種五筆輸入方案,你任選一種 - -``` -schema_list: - - schema: wubi_pinyin - - schema: wubi6 -``` - -臨時修改的配置,無法立即生效,需要重新部署。 - -在你的安裝目錄下,找到`WeaselDeployer.exe` 點擊,選擇你想要添加的輸入方案,比如這裏選擇 五筆-拼音,再點中,然後選擇皮膚,最後就會觸發重新部署,配置生效。 - -![](http://image.iswbm.com/20200119143952.png) - diff --git a/source/c04/c04_23.rst b/source/c04/c04_23.rst deleted file mode 100644 index 61f4373..0000000 --- a/source/c04/c04_23.rst +++ /dev/null @@ -1,64 +0,0 @@ -4.23 电脑使用技巧 -================= - -|image0| - -1. 添加右键菜单 ---------------- - -参考 Sublime Text3的添加方法 - -在sublime的安装目录下新增一个文件:\ ``sublime_addright.inf``\ ,内容如下,然后右键选择安装。 - -:: - - [Version] - Signature="$Windows NT$" - - [DefaultInstall] - AddReg=SublimeText3 - - [SublimeText3] - hkcr,"*\\shell\\SublimeText3",,,"Edit with SublimeText3" - hkcr,"*\\shell\\SublimeText3\\command",,,"""%1%\sublime_text.exe"" ""%%1"" %%*" - hkcr,"Directory\shell\SublimeText3",,,"Edit with SublimeText3" - hkcr,"*\\shell\\SublimeText3","Icon",0x20000,"%1%\sublime_text.exe, 0" - hkcr,"Directory\shell\SublimeText3\command",,,"""%1%\sublime_text.exe"" ""%%1""" - -2. Rime五筆安裝方法 -------------------- - -下載地址:https://rime.im/download/ - -五筆配置:https://github.com/rime/rime-wubi - -下載下來是一個zip包,裏面都是五筆的配置文件。 - -:: - - wubi_pinyin.schema.yaml - wubi_trad.schema.yaml - wubi86.dict.yaml - wubi86.schema.yaml - -將這些文件複製到你的rime安裝目錄下的data文件夾下 - -然後在 data 目錄下編輯 default.yaml 添加,兩種五筆輸入方案,你任選一種 - -:: - - schema_list: - - schema: wubi_pinyin - - schema: wubi6 - -臨時修改的配置,無法立即生效,需要重新部署。 - -在你的安裝目錄下,找到\ ``WeaselDeployer.exe`` -點擊,選擇你想要添加的輸入方案,比如這裏選擇 -五筆-拼音,再點中,然後選擇皮膚,最後就會觸發重新部署,配置生效。 - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200119143952.png - diff --git a/source/c04/c04_24.md b/source/c04/c04_24.md deleted file mode 100644 index c85c759..0000000 --- a/source/c04/c04_24.md +++ /dev/null @@ -1,171 +0,0 @@ -# 4.24 Python “用户环境”的一次完美应用 - -![](http://image.iswbm.com/20200602135014.png) - -在之前写过一篇关于虚拟环境使用的文章 :[Python 虚拟环境使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&chksm=e886669bdff1ef8d82aae3a231ef0651f82d5e97cf1e64aceda00e686119900518c202dc9b1b&scene=21#wechat_redirect). - -但是还没有好好的介绍一下 Python 的用户环境,原因是自己一直没遇到要使用 `用户环境` 的使用场景,所以就一直懒得写。 - -恰巧这两天,自己遇到了一个使用用户环境的体验可以完爆虚拟环境的案例,就拿出来分享一下。 - -## 1. 我的使用背景 - -公司有数以万计的服务器,为了对实现对访问记录进行集中管理以及出于安全考虑,每台服务器都有访问限制,必须使用公司的跳板机才能登陆。 - -每个公司的员工在跳板机上都有自己的用户、 家目录,对于很多需要 root 权限的操作,是高度受限制的。 - -比如我现在我要在跳板机上实现远程登陆大批量的机器进行一些维护工作,当然我这里使用的还是 Python 来实现,这个 Python 脚本里有一些依赖库(比如 之前介绍过的 paramiko 这个神器),在跳板机上中并没有安装。 - -![](http://image.iswbm.com/20200427180207.png) - -做为普通用户的你,是没有权限安装第三方包的。 - -![](http://image.iswbm.com/20200427180042.png) - -问题就来了,我如何才能在跳板机中使用 paramiko 这个包呢? - -## 2. 为何不使用虚拟环境? - -既然不能对全局的 Python 环境进行更改,那我完全可以自己再创建一个环境,只要这个环境里事先装好 paramiko 这个包不就好了。 - -因此,使用虚拟环境是一种解决方案,但它并不是一个完美的解决方案。 - -**原因有以下几点**: - -1、 创建虚拟环境的过程,步骤较多,比较复杂。这里的复杂是相对于我后面要使用的用户环境而言。 - -2、 虚拟环境是包含一整个 Python 解释器,存在大量与系统重复的包,size比较大,并不轻便。 - -3、 使用 console 模式调试的话,进入很不方便 - -![](http://image.iswbm.com/20200427182334.png) - -就算你不使用 console 模式,你调用脚本的方式,也会很奇怪,你得这样 - -```python -$ zabbix_env/bin/python demo.py -``` - -如果你不想使用这样,可以给这个脚本加个可执行权限,并在脚本的第一行指定你的解释器,省去了一点点麻烦,可即便如此,我仍然感觉很别扭。 - -```shell -[wangbm@35ha02 ~]$ cat demo.py -#!/home/wangbm/zabbix_env/bin/python - -import zabbix_api -[wangbm@35ha02 ~]$ -[wangbm@35ha02 ~]$ -[wangbm@35ha02 ~]$ chmod +x demo.py -[wangbm@35ha02 ~]$ -[wangbm@35ha02 ~]$ ./demo.py # 可以执行,没有报错 -[wangbm@35ha02 ~]$ -``` - - - -你可能会问我:为什么不使用 virtualenv + virtualenvwrapper ,这样可以使用 workon 进入虚拟环境。 - -原因是跳板机里的都是很古老的包,你看上面的 Python 还是 2.7.5 呢,所以你所说的那些工具通通没有。 - - - -## 3. 用户环境原理 - -这里要介绍的这种方案(**用户环境**),可能很多人都没有使用过,甚至没有听过,它算是一个冷门但是非常好用的功能。 - -操作之前 ,先简单介绍一下它。 - -先提一个问题,Python 在查找导入包时,如果我们多个路径都有这个包,那 Python 如何确定应该从哪个路径进行导入呢? - -答案是, 搜索导入路径是有优先级的,你可以通过 sys.path 进行查看。 - -```python ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.path) -['', - '/usr/lib64/python27.zip', - '/usr/lib64/python2.7', - '/usr/lib64/python2.7/plat-linux2', - '/usr/lib64/python2.7/lib-tk', - '/usr/lib64/python2.7/lib-old', - '/usr/lib64/python2.7/lib-dynload', - '/home/wangbm/.local/lib/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages/gtk-2.0', - '/usr/lib/python2.7/site-packages', - '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', - '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] ->>> -``` - -可以看到路径 `/home/wangbm/.local/lib/python2.7/site-packages` 是优先于 `/usr/lib64/python2.7/site-packages` 路径的。 - -这就是 **用户环境** 的原理,只要我们将包装在自己家目录下,就可以优先于全局环境中进行查找。 - -使用起来,可以做到用户无感知,跟使用原生的全局环境并没有区别。 - -## 4. 具体操作方法 - -创建一个用户环境,并安装上你所需要的包,一条命令就能搞定,这可比虚拟环境简单方便多了。 - -那么怎么操作呢? - -只要你在使用 pip 安装包时,加上 `--user` 参数,pip 就会将其安装在当前用户的 `~/.local/lib/python2.x/site-packages` 下,而其他用户的 python 则不会受影响。 - -```shell -$ pip install --user pkg -``` - -这里要注意的是,不能使用这种方式,亲测它会将包装到全局环境下,具体原因我还没有深究。 - -```shell -$ python -m pip install --user pkg -``` - -为了让你理解这个过程,我这里来举个例子,并且验证其是否可以做到用户隔离。 - -```shell -# 在全局环境中未安装 requests -[root@localhost ~]$ pip list | grep requests -[root@localhost ~]$ su - wangbm - -# 由于用户环境继承自全局环境,这里也未安装 -[wangbm@localhost ~]$ pip list | grep requests -[wangbm@localhost ~]$ pip install --user requests -[wangbm@localhost ~]$ pip list | grep requests -requests (2.22.0) -[wangbm@localhost ~]$ - -# 从 Location 属性可发现 requests 只安装在当前用户环境中 -[wangbm@localhost ~]$ pip show requests ---- -Metadata-Version: 2.1 -Name: requests -Version: 2.22.0 -Summary: Python HTTP for Humans. -Home-page: http://python-requests.org -Author: Kenneth Reitz -Author-email: me@kennethreitz.org -Installer: pip -License: Apache 2.0 -Location: /home/wangbm/.local/lib/python2.7/site-packages -[wangbm@localhost ~]$ exit -logout - -# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 -[root@localhost ~]$ pip list | grep requests -[root@localhost ~]$ -``` - -有了这个思路,我就可以先在其他机器(前提自己必须拥有管理员权限 )上,创建一个用户环境,并且安装上 paramiko 这个包。 - -然后将这个用户环境,压缩拷贝至跳板机自己的家目录下的 `.local/lib` 目录下并解压。 - -然后直接使用 python 进入 console 模式,现在已经可以直接使用 paramiko 这个包了。 - -![](http://image.iswbm.com/20200427185854.png) - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c04/c04_24.rst b/source/c04/c04_24.rst deleted file mode 100644 index 9b8949a..0000000 --- a/source/c04/c04_24.rst +++ /dev/null @@ -1,197 +0,0 @@ -4.24 Python “用户环境”的一次完美应用 -==================================== - -|image0| - -在之前写过一篇关于虚拟环境使用的文章 :\ `Python -虚拟环境使用指南 `__. - -但是还没有好好的介绍一下 Python 的用户环境,原因是自己一直没遇到要使用 -``用户环境`` 的使用场景,所以就一直懒得写。 - -恰巧这两天,自己遇到了一个使用用户环境的体验可以完爆虚拟环境的案例,就拿出来分享一下。 - -1. 我的使用背景 ---------------- - -公司有数以万计的服务器,为了对实现对访问记录进行集中管理以及出于安全考虑,每台服务器都有访问限制,必须使用公司的跳板机才能登陆。 - -每个公司的员工在跳板机上都有自己的用户、 家目录,对于很多需要 root -权限的操作,是高度受限制的。 - -比如我现在我要在跳板机上实现远程登陆大批量的机器进行一些维护工作,当然我这里使用的还是 -Python 来实现,这个 Python 脚本里有一些依赖库(比如 之前介绍过的 -paramiko 这个神器),在跳板机上中并没有安装。 - -|image1| - -做为普通用户的你,是没有权限安装第三方包的。 - -|image2| - -问题就来了,我如何才能在跳板机中使用 paramiko 这个包呢? - -2. 为何不使用虚拟环境? ------------------------ - -既然不能对全局的 Python -环境进行更改,那我完全可以自己再创建一个环境,只要这个环境里事先装好 -paramiko 这个包不就好了。 - -因此,使用虚拟环境是一种解决方案,但它并不是一个完美的解决方案。 - -**原因有以下几点**\ : - -1、 -创建虚拟环境的过程,步骤较多,比较复杂。这里的复杂是相对于我后面要使用的用户环境而言。 - -2、 虚拟环境是包含一整个 Python -解释器,存在大量与系统重复的包,size比较大,并不轻便。 - -3、 使用 console 模式调试的话,进入很不方便 - -|image3| - -就算你不使用 console 模式,你调用脚本的方式,也会很奇怪,你得这样 - -.. code:: python - - $ zabbix_env/bin/python demo.py - -如果你不想使用这样,可以给这个脚本加个可执行权限,并在脚本的第一行指定你的解释器,省去了一点点麻烦,可即便如此,我仍然感觉很别扭。 - -.. code:: shell - - [wangbm@35ha02 ~]$ cat demo.py - #!/home/wangbm/zabbix_env/bin/python - - import zabbix_api - [wangbm@35ha02 ~]$ - [wangbm@35ha02 ~]$ - [wangbm@35ha02 ~]$ chmod +x demo.py - [wangbm@35ha02 ~]$ - [wangbm@35ha02 ~]$ ./demo.py # 可以执行,没有报错 - [wangbm@35ha02 ~]$ - -你可能会问我:为什么不使用 virtualenv + virtualenvwrapper ,这样可以使用 -workon 进入虚拟环境。 - -原因是跳板机里的都是很古老的包,你看上面的 Python 还是 2.7.5 -呢,所以你所说的那些工具通通没有。 - -3. 用户环境原理 ---------------- - -这里要介绍的这种方案(\ **用户环境**\ ),可能很多人都没有使用过,甚至没有听过,它算是一个冷门但是非常好用的功能。 - -操作之前 ,先简单介绍一下它。 - -先提一个问题,Python 在查找导入包时,如果我们多个路径都有这个包,那 -Python 如何确定应该从哪个路径进行导入呢? - -答案是, 搜索导入路径是有优先级的,你可以通过 sys.path 进行查看。 - -.. code:: python - - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.path) - ['', - '/usr/lib64/python27.zip', - '/usr/lib64/python2.7', - '/usr/lib64/python2.7/plat-linux2', - '/usr/lib64/python2.7/lib-tk', - '/usr/lib64/python2.7/lib-old', - '/usr/lib64/python2.7/lib-dynload', - '/home/wangbm/.local/lib/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages/gtk-2.0', - '/usr/lib/python2.7/site-packages', - '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', - '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] - >>> - -可以看到路径 ``/home/wangbm/.local/lib/python2.7/site-packages`` -是优先于 ``/usr/lib64/python2.7/site-packages`` 路径的。 - -这就是 **用户环境** -的原理,只要我们将包装在自己家目录下,就可以优先于全局环境中进行查找。 - -使用起来,可以做到用户无感知,跟使用原生的全局环境并没有区别。 - -4. 具体操作方法 ---------------- - -创建一个用户环境,并安装上你所需要的包,一条命令就能搞定,这可比虚拟环境简单方便多了。 - -那么怎么操作呢? - -只要你在使用 pip 安装包时,加上 ``--user`` 参数,pip -就会将其安装在当前用户的 ``~/.local/lib/python2.x/site-packages`` -下,而其他用户的 python 则不会受影响。 - -.. code:: shell - - $ pip install --user pkg - -这里要注意的是,不能使用这种方式,亲测它会将包装到全局环境下,具体原因我还没有深究。 - -.. code:: shell - - $ python -m pip install --user pkg - -为了让你理解这个过程,我这里来举个例子,并且验证其是否可以做到用户隔离。 - -.. code:: shell - - # 在全局环境中未安装 requests - [root@localhost ~]$ pip list | grep requests - [root@localhost ~]$ su - wangbm - - # 由于用户环境继承自全局环境,这里也未安装 - [wangbm@localhost ~]$ pip list | grep requests - [wangbm@localhost ~]$ pip install --user requests - [wangbm@localhost ~]$ pip list | grep requests - requests (2.22.0) - [wangbm@localhost ~]$ - - # 从 Location 属性可发现 requests 只安装在当前用户环境中 - [wangbm@localhost ~]$ pip show requests - --- - Metadata-Version: 2.1 - Name: requests - Version: 2.22.0 - Summary: Python HTTP for Humans. - Home-page: http://python-requests.org - Author: Kenneth Reitz - Author-email: me@kennethreitz.org - Installer: pip - License: Apache 2.0 - Location: /home/wangbm/.local/lib/python2.7/site-packages - [wangbm@localhost ~]$ exit - logout - - # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 - [root@localhost ~]$ pip list | grep requests - [root@localhost ~]$ - -有了这个思路,我就可以先在其他机器(前提自己必须拥有管理员权限 -)上,创建一个用户环境,并且安装上 paramiko 这个包。 - -然后将这个用户环境,压缩拷贝至跳板机自己的家目录下的 ``.local/lib`` -目录下并解压。 - -然后直接使用 python 进入 console 模式,现在已经可以直接使用 paramiko -这个包了。 - -|image4| - -|image5| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200427180207.png -.. |image2| image:: http://image.iswbm.com/20200427180042.png -.. |image3| image:: http://image.iswbm.com/20200427182334.png -.. |image4| image:: http://image.iswbm.com/20200427185854.png -.. |image5| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c04/c04_25.md b/source/c04/c04_25.md deleted file mode 100644 index 65fceee..0000000 --- a/source/c04/c04_25.md +++ /dev/null @@ -1,17 +0,0 @@ -# 4.25 如何写好 readme? - -## 贡献者名单 - -一个如下的 HTML 代码,代表一个头像。[点击参考](https://raw.githubusercontent.com/Snailclimb/JavaGuide/master/README.md) - -```html - - - -``` - -## 添加表情 - -参考这个github:https://gist.github.com/rxaviers/7360908 - -或者看这个网站:https://www.webfx.com/tools/emoji-cheat-sheet/ \ No newline at end of file diff --git a/source/c04/c04_25.rst b/source/c04/c04_25.rst deleted file mode 100644 index 066f97f..0000000 --- a/source/c04/c04_25.rst +++ /dev/null @@ -1,21 +0,0 @@ -4.25 如何写好 readme? -===================== - -贡献者名单 ----------- - -一个如下的 HTML -代码,代表一个头像。\ `点击参考 `__ - -.. code:: html - - - - - -添加表情 --------- - -参考这个github:https://gist.github.com/rxaviers/7360908 - -或者看这个网站:https://www.webfx.com/tools/emoji-cheat-sheet/ diff --git a/source/c09/c09_06.md b/source/c09/c09_06.md index 90382c2..75b5138 100644 --- a/source/c09/c09_06.md +++ b/source/c09/c09_06.md @@ -1,4 +1,4 @@ -# 9.6 学习编程的好网站 +# 9.6 赶紧收藏!学习 Python 的好网站 ![](http://image.iswbm.com/20200602135014.png) @@ -78,6 +78,12 @@ ![](http://image.iswbm.com/image-20200701123010074.png) +参考学习地址:https://realpython.com/cpython-source-code-guide/ + +基于 Python 3.6 的源码分析:https://he11olx.com/ + +![](http://image.iswbm.com/20201004150826.png) + ### 书栈网:Python 网站链接:https://www.bookstack.cn/explore?cid=13&tab=popular diff --git a/source/c09/c09_06.rst b/source/c09/c09_06.rst index 5819b52..40e1d7b 100644 --- a/source/c09/c09_06.rst +++ b/source/c09/c09_06.rst @@ -1,5 +1,5 @@ -9.6 学习编程的好网站 -==================== +9.6 赶紧收藏!学习 Python 的好网站 +================================== |image0| @@ -77,33 +77,39 @@ Python3 源码剖析 |image10| +参考学习地址:https://realpython.com/cpython-source-code-guide/ + +基于 Python 3.6 的源码分析:https://he11olx.com/ + +|image11| + 书栈网:Python ~~~~~~~~~~~~~~ 网站链接:https://www.bookstack.cn/explore?cid=13&tab=popular -|image11| +|image12| 追梦人物:DRF 实战教程 ~~~~~~~~~~~~~~~~~~~~~~ 专栏链接:https://zhuanlan.zhihu.com/djstudyteam -|image12| +|image13| Python Tips 刷题挑战 ~~~~~~~~~~~~~~~~~~~~ 网站链接:http://www.pythontip.com/coding/code_oj -|image13| +|image14| Python Tips 设计模式 ~~~~~~~~~~~~~~~~~~~~ 网站链接:http://www.pythontip.com/python-patterns/detail/abstract_factory -|image14| +|image15| 综合性网站 ---------- @@ -115,14 +121,14 @@ Python Tips 设计模式 **网站链接**\ :https://www.bookstack.cn/rank?tab=popular -|image15| +|image16| 魔法学院 ~~~~~~~~ **网站链接**\ :http://www.nowamagic.net/academy/ -|image16| +|image17| 学习 Linux ---------- @@ -132,7 +138,7 @@ Linux 手册 网站链接:https://man.linuxde.net/ -|image17| +|image18| 每天一个linux命令 ~~~~~~~~~~~~~~~~~ @@ -144,14 +150,14 @@ Linux 手册 网站链接:https://www.shiyanlou.com/courses/1 -|image18| +|image19| 网站链接:https://www.shiyanlou.com/courses/68 -|image19| - |image20| +|image21| + .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200802110436.png .. |image2| image:: http://image.iswbm.com/20200802111158.png @@ -163,14 +169,15 @@ Linux 手册 .. |image8| image:: http://image.iswbm.com/20200525080531.png .. |image9| image:: http://image.iswbm.com/20200525080715.png .. |image10| image:: http://image.iswbm.com/image-20200701123010074.png -.. |image11| image:: http://image.iswbm.com/20200802114734.png -.. |image12| image:: http://image.iswbm.com/20200802120804.png -.. |image13| image:: http://image.iswbm.com/20200802121125.png -.. |image14| image:: http://image.iswbm.com/20200802121331.png -.. |image15| image:: http://image.iswbm.com/20200104144109.png -.. |image16| image:: http://image.iswbm.com/20200112210558.png -.. |image17| image:: http://image.iswbm.com/image-20200704204307530.png -.. |image18| image:: http://image.iswbm.com/20200704204506.png -.. |image19| image:: http://image.iswbm.com/20200704204558.png -.. |image20| image:: http://image.iswbm.com/20200607174235.png +.. |image11| image:: http://image.iswbm.com/20201004150826.png +.. |image12| image:: http://image.iswbm.com/20200802114734.png +.. |image13| image:: http://image.iswbm.com/20200802120804.png +.. |image14| image:: http://image.iswbm.com/20200802121125.png +.. |image15| image:: http://image.iswbm.com/20200802121331.png +.. |image16| image:: http://image.iswbm.com/20200104144109.png +.. |image17| image:: http://image.iswbm.com/20200112210558.png +.. |image18| image:: http://image.iswbm.com/image-20200704204307530.png +.. |image19| image:: http://image.iswbm.com/20200704204506.png +.. |image20| image:: http://image.iswbm.com/20200704204558.png +.. |image21| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c09/c09_07.md b/source/c09/c09_07.md new file mode 100644 index 0000000..1d82779 --- /dev/null +++ b/source/c09/c09_07.md @@ -0,0 +1,19 @@ +# 9.7 解决网页上不能复制的几个小技巧 + +![](http://image.iswbm.com/20200602135014.png) + +## 1. 解决网页鼠标限制 + +``` +解决网页不能选中,在console中输入:document.onselectstart=true +解决网页不能复制,在console中输入:document.oncopy=true +解决网页不能右键,在console中输入:document.oncontextmenu=true +``` + +## 2. 在网页上为所欲为 + +```javascript +document.body.contentEditable='true' +``` + +在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 Ctrl+Z 后退 。 \ No newline at end of file diff --git a/source/c09/c09_07.rst b/source/c09/c09_07.rst new file mode 100644 index 0000000..2ffb815 --- /dev/null +++ b/source/c09/c09_07.rst @@ -0,0 +1,26 @@ +9.7 解决网页上不能复制的几个小技巧 +================================== + +|image0| + +1. 解决网页鼠标限制 +------------------- + +:: + + 解决网页不能选中,在console中输入:document.onselectstart=true + 解决网页不能复制,在console中输入:document.oncopy=true + 解决网页不能右键,在console中输入:document.oncontextmenu=true + +2. 在网页上为所欲为 +------------------- + +.. code:: javascript + + document.body.contentEditable='true' + +在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 +Ctrl+Z 后退 。 + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c10/c10_01.md b/source/c10/c10_01.md index 8b9bb69..598e01b 100644 --- a/source/c10/c10_01.md +++ b/source/c10/c10_01.md @@ -1,277 +1,384 @@ -# 10.1 网络知识扫盲:一篇文章搞懂 DNS +# 10.1 使用 Python 远程登陆服务器的利器 ![](http://image.iswbm.com/20200602135014.png) -## 1. DNS 是什么? +在使用 Python 写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 -DNS (Domain Name System 的缩写)的作用非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。 +在 shell 环境中,我们是这样子做的。 -举例来说,如果你要访问域名`math.stackexchange.com`,首先要通过DNS查出它的IP地址是`151.101.129.69`。 +```shell +$ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l" +``` -## 2. 域名的层级 +然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样 -由于后面我会讲到 DNS 的解析过程,因此需要你对域名的层级有一些了解 +```shell +host: xx.xx.xx.xx, port: xx +Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts. +Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server. +total 4 +-rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc +``` -- 根域名 :`.root` 或者 `.` ,通常是省略的 -- 顶级域名,如 `.com`,`.cn` 等 -- 次级域名,如 `baidu.com` 里的 `baidu`,这个用户是可以注册购买的 -- 主机域名,比如 `baike.baidu.com` 里的`baike`,这个用户是可分配的 +对于直接使用 shell 命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果 -``` -主机名.次级域名.顶级域名.根域名 +## 1. 使用 subprocess -baike.baidu.com.root -``` +若是使用 Python 来做这件事,通常我们会第一时间,想到使用 os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。 +但是据我所知,这些库获取的 output 不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息) +所以每次都要对 output 进行的数据清洗,然后整理格式化,才能得到我们想要的数据。 -## 3. DNS 解析过程 +用 subprocess 举个例子,就像这样子 -咱们以访问 `www.163.com` 这个域名为例,来看一看当你访问 www.163.com 时,会发生哪些事: +```python +import subprocess +ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'" +status, output = subprocess.getstatusoutput(ssh_cmd) -1. 先查找本地 DNS 缓存(自己的电脑上),有则返回,没有则进入下一步 -2. 查看本地 hosts 文件有没有相应的映射记录,有则返回,没有则进入下一步 -3. 向本地 DNS 服务器(一般都是你的网络接入服务器商提供,比如中国电信,中国移动)发送请求进行查询,本地DNS服务器收到请求后,会先查下自己的缓存记录,如果查到了直接返回就结束了,如果没有查到,本地DNS服务器就会向DNS的根域名服务器发起查询请求:请问老大, `www.163.com` 的ip是啥? -4. 根域名服务器收到请求后,看到这是个 `.com` 的域名,就回信说:这个域名是由 `.com` 老弟管理的,你去问他好了,这是`.com`老弟的联系方式(ip1)。 -5. 本地 DNS 服务器接收到回信后,照着老大哥给的联系方式(ip1),马上给 `.com` 这个顶级域名服务器发起请求:请问 `.com` 大大,`www.163.com` 的ip 是啥? -6. `.com` 顶级域名服务器接收到请求后,看到这是 `163.com` 的域名,就回信说:这个域名是 `.163.com` 老弟管理的,你就去问他就行了,这是他的联系方式(ip2) -7. 本地 DNS 服务器接收到回信后,按照前辈的指引(ip2),又向 `.163.com` 这个权威域名服务器发起请求:请问 `163.com` 大大,请问 `www.163.com` 的ip是啥? -8. `163.com` 权威域名服务器接收到请求后,确认了是自己管理的域名,马上查了下自己的小本本,把 `www.163.com` 的ip告诉了 本地DNS服务器。 -9. 本地DNS服务器接收到回信后,非常地开心,这下总算拿到了`www.163.com`的ip了,马上把这个消息告诉了要求查询的客户(就是你的电脑)。由于这个过程比较漫长,本地DNS服务器为了节省时间,也为了尽量不去打扰各位老大哥,就把这个查询结果偷偷地记在了自己的小本本上,方便下次有人来查询时,可以快速回应。 +# 数据清理,格式化的就不展示了 + +``` -总结起来就是三句话 -1. 从"根域名服务器"查到"顶级域名服务器"的NS记录和A记录(IP地址) -2. 从"顶级域名服务器"查到"次级域名服务器"的NS记录和A记录(IP地址) -3. 从"次级域名服务器"查出"主机名"的IP地址 -![](http://image.iswbm.com/464291-20170703113844956-354755333.jpg) +通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点 -## 4. DNS的缓存时间 +- **痛点一**:需要额外安装 sshpass(如果不免密的话) +- **痛点二**:干扰信息太多,数据清理、格式化相当麻烦 +- **痛点三**:代码实现不够优雅(有点土),可读性太差 +- **痛点四**:ssh 连接不能复用,一次连接仅能执行一次 +- **痛点五**:代码无法全平台,仅能在 Linux 和 OSX 上使用 -上面的几个步骤里,可以看到有两个地方会缓存 DNS 的查询记录,有了缓存,在一定程度上会提高查询效率,但同时在准确率上会有所损失。 -因此我们在配置 DNS 解析的时候,会有一个 TTL 参数(Time To Live),意思就是这个缓存可以存活多长时间,过了这个时间,本地 DNS 就会删除这条记录,删除了缓存后,你再访问,就要重新走一遍上面的流程,获取最新的地址。 -![](http://image.iswbm.com/image-20200531141521689.png) +为了解决这几个问题,我搜索了全网关于 Python ssh 的文章,没有看到有完整介绍这方面的技巧的。 -## 5. DNS 的记录类型 +为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn (https://github.com/BingmingWong/awesome-python-cn)。 -当我们在阿里云买了一个域名后,可以配置我们主机域名解析规则,也就是 **记录**。 +期望在这里,找到有一些关于 远程连接 的一些好用的库。 -![阿里云 域名云解析](http://image.iswbm.com/image-20200531170212224.png) +还真的被我找到了两个 -常见的 DNS 记录类型如下 +- sh.ssh +- Paramiko -- `A`:地址记录(Address),返回域名指向的IP地址。 -- `NS`:域名服务器记录(Name Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址。 -- `MX`:邮件记录(Mail eXchange),返回接收电子邮件的服务器地址。 -- `CNAME`:规范名称记录(Canonical Name),返回另一个域名,即当前查询的域名是另一个域名的跳转,详见下文。 -- `PTR`:逆向查询记录(Pointer Record),只用于从IP地址查询域名,详见下文。 +## 2. 使用 sh.ssh +首先来介绍第一个,`sh.ssh` -## 6. DNS 报文结构 +`sh` 是一个可以让你通过函数的调用来完成 Linxu/OSX 系统命令的一个库,非常好用,关于它有机会也写篇介绍。 -后面我将使用 wireshark 抓取 DNS 的数据包,但是在开始之前 ,得先了解一下 DNS 的报文结构 +```shell +$ python3 -m pip install sh +``` -![](http://image.iswbm.com/image-20200531152824672.png) -- 事务 ID:DNS 报文的 ID 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。通过它可以区分 DNS 应答报文是对哪个请求进行响应的。 -- 标志:DNS 报文中的标志字段。 -- 问题计数:DNS 查询请求的数目。 -- 回答资源记录数:DNS 响应的数目。 -- 权威名称服务器计数:权威名称服务器的数目。 -- 附加资源记录数:额外的记录数目(权威名称服务器对应 IP 地址的数目)。 -## 7. Wireshark抓包实战 +今天只介绍它其中的一个函数:`ssh` -打开 Wireshark 后,使用 `ping 163.com` 来发起 DNS 解析请求,使用 `DNS` 关键字在Wireshark 过滤。 +通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。 -从抓取的报文整体来看,我们可以粗略获取几个信息 +这段代码可以实现免密登陆,并执行我们的命令 `ls -l` -1. DNS 是应用层协议,传输层协议使用的是 UDP -2. DNS 默认端口是 53 +```python +from sh import ssh +output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l") +print(output) +``` -![](http://image.iswbm.com/20200531175736.png) +但有可能 ,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。 -请求和应答的报文的截图我放在了下面,接下来我将逐个分析。 +问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python 中要如何实现呢? -**请求** +原来 ssh 方法接收一个 `_out` 参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。 -![](http://image.iswbm.com/20200531175811.png) +这就好办了呀。 -**应答** +我只要识别到有 `password:` 字样,就往标准输入写入我的密码就好了呀。 -![](http://image.iswbm.com/image-20200531153110621.png) +完整代码如下: -### Transaction ID +```python +import sys +from sh import ssh -请求和应答的事务ID应当是一个:0xd0d7 +aggregated = "" +def ssh_interact(char, stdin): + global aggregated + sys.stdout.write(char.encode()) + sys.stdout.flush() + aggregated += char + if aggregated.endswith("password: "): + stdin.put("you_password\n") -### Flags +output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact) +print(output) +``` -标志字段里的内容比较多,每个字段的含义如下 +这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。 -- QR(Response):查询请求/响应的标志信息。查询请求时,值为 0;响应时,值为 1。 -- Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 表示服务器状态请求。 -- AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。 -- TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。 -- RD(Recursion Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。 -- RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 1 时,表示服务器支持递归查询。 -- Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。 -- rcode(Reply code):返回码字段,表示响应的差错状态。当值为 0 时,表示没有错误;当值为 1 时,表示报文格式错误(Format error),服务器不能理解请求的报文;当值为 2 时,表示域名服务器失败(Server failure),因为服务器的原因导致没办法处理这个请求;当值为 3 时,表示名字错误(Name Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为 4 时,表示查询类型不支持(Not Implemented),即域名服务器不支持查询类型;当值为 5 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。 +尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。 -### Answer RRs +通过调试查看源代码,仍然查不到问题所在,于是去 [Github](https://github.com/amoffat/sh/issues/393) 上搜了下,原来在 2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 `sh.ssh` 的人并不多,于是我又“追问”了下,期望能得到回复。 -回答资源记录数,在应答包里为 2,说明返回了两条查询结果,你可以在 Answer 字段里看到。 +![](http://image.iswbm.com/20200228085749.png) -### Authority RRs +以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 -权威名称服务器计数 +为了感受 `sh.ssh` 的使用效果,我设置了机器互信免密,然后使用如下这段代码。 -### Additionnal RRs +```python +from sh import ssh -附加资源记录数 +my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22") -### Answers +# 相当于执行登陆一次执行一次命令,执行完就退出登陆 +print(my_server.ls()) -应答的主要内容,这里返回两条结果,每条结果里的字段有 +# 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接 +time.sleep(5) -```shell -Name: 查询的域名 -Type: A表示IPv4,AAAA 表示IPv6 -Class: 表示Internet,几乎总是它 -Time to live: 生存时间 -Data length: 数据长度 -Address: 查询到的 IP 地址 +# 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1 +print(my_server.ifconfig()) ``` +惊奇地发现使用 `bake` 这种方式,`my_server.ls()` 和 `my_server.ifconfig()` 这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行 top 命令看到已连接的终端的变化,会先 `+1` 再 `-1`,说明两次命令的执行是通过两次连接实现的。 +如此看来,使用 `sh.ssh` 可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。 -## 8. DNS 劫持 与 HTTP 劫持 +但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。 -通过上面的讲解,我们都知道了,DNS 完成了一次域名到 IP 的映射查询,当你在访问 www.baidu.com 时,能正确返回给你 百度首页的 ip。 +最重要的一点是, `sh` 这个模块,仅支持 Linxu/OSX ,在 Windows 你得使用它的兄弟库 - `pbs` ,然后我又去 pypi 看了一眼 [pbs](https://pypi.org/project/pbs/),已经 “年久失修”,没人维护了。 -但如果此时 DNS 解析出现了一些问题,当你想要访问 www.baidu.com 时,却返回给你 www.google.com 的ip,这就是我们常说的 DNS 劫持。 +![](http://image.iswbm.com/20200228093627.png) -与之容易混淆的有 HTTP 劫持。 +至此,我离 “卒”,就差最后一根稻草了。 -那什么是 HTTP 劫持呢? -你一定见过当你在访问 某个网站时,右下角也突然弹出了一个扎眼的广告弹窗。这就是 HTTP 劫持。 -借助别人文章里的例子,它们俩的区别就好比是 +## 3. 使用 paramiko -- DNS劫持是你想去机场的时候,把你给丢到火车站。 +带着最后一丝希望,我尝试使用了 `paramiko` 这个库,终于在 `paramiko` 这里,找回了本应属于 Python 的那种优雅。 -- HTTP劫持是你去机场途中,有人给你塞小广告。 +你可以通过如下命令去安装它 -**那么 DNS劫持 是如何产生的呢?** +``` +$ python3 -m pip install paramiko +``` -下面大概说几种DNS劫持方法: -**1.本机DNS劫持** -攻击者通过某些手段使用户的计算机感染上木马病毒,或者恶意软件之后,恶意修改本地DNS配置,比如修改本地hosts文件,缓存等 +然后接下来,就介绍几种常用的 ssh 登陆的方法 -**2. 路由DNS劫持** +### 方法1:基于用户名和密码的 sshclient 方式登录 -很多用户默认路由器的默认密码,攻击者可以侵入到路由管理员账号中,修改路由器的默认配置 +然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接 -**3.攻击DNS服务器** +```python +import paramiko -直接攻击DNS服务器,例如对DNS服务器进行DDOS攻击,可以是DNS服务器宕机,出现异常请求,还可以利用某些手段感染dns服务器的缓存,使给用户返回来的是恶意的ip地址 +ssh = paramiko.SSHClient() +# 允许连接不在know_hosts文件中的主机 +ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) -## 9. 工具的使用 +# 建立连接 +ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password") -### dig 命令 +# 使用这个连接执行命令 +ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") -dig是一个在类Unix命令行模式下查询DNS包括NS记录,A记录,MX记录等相关信息的工具。 +# 获取输出 +print(ssh_stdout.read()) -通过 dig (参数:`+trace`)命令,我们可以看到上面描述的 DNS 解析的详细过程 +# 关闭连接 +ssh.close() +``` -![](http://image.iswbm.com/image-20200531162810531.png) -从返回的结果,我们可以看得出几点信息 -1. 我们的本地 DNS 服务器 ip 为 192.168.1.1,端口为53,你可以在 /etc/resolv.conf 里看到这个配置 -2. 根域名服务器目前全球一共只有十三台,从a.root-servers.net. `到`m.root-servers.net. ,它们对应的ip地址,已经内置在本地DNS服务器中。 +### 方法2:基于用户名和密码的 transport 方式登录 -如果你只想看到结果,可以使用 `+short` 参数,可以直接返回 www.163.com 对应着哪几个ip +方法1 是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。 -![](http://image.iswbm.com/image-20200531164525384.png) +有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1 则无法实现,那就可以使用 transport 的方法。 -你也可以加个 `@` 参数 ,指定从某个 DNS 服务器进行查询 +```python +import paramiko -![](http://image.iswbm.com/image-20200531170427834.png) +# 建立连接 +trans = paramiko.Transport(("xx.xx.xx.xx", 22)) +trans.connect(username="root", password="you_passwd") -如果你只想查看指定的记录类型 +# 将sshclient的对象的transport指定为以上的trans +ssh = paramiko.SSHClient() +ssh._transport = trans -![](http://image.iswbm.com/image-20200531170543250.png) +# 剩下的就和上面一样了 +ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") +print(ssh_stdout.read()) +# 关闭连接 +trans.close() +``` -### host 命令 -`host` 命令 可以看作`dig`命令的简化版本,返回当前请求域名的各种记录。 +### 方法3:基于公钥密钥的 SSHClient 方式登录 -![](http://image.iswbm.com/image-20200531171610902.png) +```python +import paramiko +# 指定本地的RSA私钥文件 +# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 +pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') +# 建立连接 +ssh = paramiko.SSHClient() +ssh.connect(hostname='xx.xx.xx.xx', + port=22, + username='you_username', + pkey=pkey) -### whois命令 +# 执行命令 +stdin, stdout, stderr = ssh.exec_command('ls -l') -`whois`命令用来查看域名的注册情况。 +# 结果放到stdout中,如果有错误将放到stderr中 +print(stdout.read()) -![](http://image.iswbm.com/image-20200531171905345.png) +# 关闭连接 +ssh.close() +``` -### nslookup命令 -nslookup也是常用的一个查询 DNS 解析结果的工具 -```shell -$ nslookup [查询的域名] [指定DNS服务器] +### 方法4:基于密钥的 Transport 方式登录 + +```python +import paramiko + +# 指定本地的RSA私钥文件 +# 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 +pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') + +# 建立连接 +trans = paramiko.Transport(('xx.xx.xx.xx', 22)) +trans.connect(username='you_username', pkey=pkey) + +# 将sshclient的对象的transport指定为以上的trans +ssh = paramiko.SSHClient() +ssh._transport = trans + +# 执行命令,和传统方法一样 +stdin, stdout, stderr = ssh.exec_command('df -hl') +print(stdout.read().decode()) + +# 关闭连接 +trans.close() ``` -![](http://image.iswbm.com/image-20200531145109182.png) -你也可以指定公网的域名服务器进行查询,比如常见的 114.114.114.114 -![](http://image.iswbm.com/image-20200531145449577.png) +以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用 **方法二** 和 **方法四** +用完后,记得关闭连接。 +### 实现 sftp 文件传输 -## 10. 手动清理本地缓存 +同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现 sftp 文件传输。 -MacOS +```python +import paramiko -```shell -$ sudo dscacheutil -flushcache -$ sudo killall -HUP mDNSResponder +# 实例化一个trans对象# 实例化一个transport对象 +trans = paramiko.Transport(('xx.xx.xx.xx', 22)) + +# 建立连接 +trans.connect(username='you_username', password='you_passwd') + +# 实例化一个 sftp对象,指定连接的通道 +sftp = paramiko.SFTPClient.from_transport(trans) + +# 发送文件 +sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt') + +# 下载文件 +sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt') +trans.close() ``` -Windows -```shell -$ ipconfig /flushdns + +到这里,Paramiko 已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是 Windows,这里就有一件好事,一件坏事了,。 + +好事就是:paramiko 支持 windows + +坏事就是:你需要做很多复杂的准备,你可 google 解决,但是我建议你直接放弃,坑太深了。 + +![](http://image.iswbm.com/20200228111654.png) + +### 注意事项 + +使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 "踩坑" 后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点 + +就是在执行 `ssh.exec_command(cmd)` 时,这个命令并不是同步阻塞的。 + +比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s 后,再 执行 ssh.close() + +```python +import paramiko + +trans = paramiko.Transport(("172.20.42.1", 57891)) +trans.connect(username="root", password="youpassword") +ssh = paramiko.SSHClient() +ssh._transport = trans +stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") +ssh.close() ``` -Linux -```shell -# 使用NSCD的DNS缓存 -$ sudo /etc/init.d/nscd restart -# 服务器或者路由器使用DNSMASQ -$ sudo dnsmasq restart +但是如果改成这样,加上一行 stdout.read(), paramiko 就知道,你需要这个执行的结果,就会在 read() 进行阻塞。 + +```python +import paramiko + +trans = paramiko.Transport(("172.20.42.1", 57891)) +trans.connect(username="root", password="youpassword") +ssh = paramiko.SSHClient() +ssh._transport = trans +stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") + +# 加上一行 read() +print(stdout.read()) +ssh.close() ``` -![](http://image.iswbm.com/20200607174235.png) +## 4. 写在最后 + +经过了一番对比,和一些实例的展示,可以看出 Paramiko 是一个专业、让人省心的 ssh 利器,个人认为 Paramiko 模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh 到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。 + +最后,希望这篇文章,能给你带来帮助。 + + + +## 5. 参考链接 + +- https://github.com/paramiko/paramiko +- http://docs.paramiko.org +- https://www.liujiangblog.com/blog/15/ +- http://docs.paramiko.org/en/stable/ + + + +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_01.rst b/source/c10/c10_01.rst index bdabd65..70ac45e 100644 --- a/source/c10/c10_01.rst +++ b/source/c10/c10_01.rst @@ -1,353 +1,405 @@ -10.1 网络知识扫盲:一篇文章搞懂 DNS -=================================== +10.1 使用 Python 远程登陆服务器的利器 +===================================== |image0| -1. DNS 是什么? ---------------- +在使用 Python +写一些脚本的时候,在某些情况下,我们需要频繁登陆远程服务去执行一次命令,并返回一些结果。 -DNS (Domain Name System -的缩写)的作用非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。 +在 shell 环境中,我们是这样子做的。 -举例来说,如果你要访问域名\ ``math.stackexchange.com``\ ,首先要通过DNS查出它的IP地址是\ ``151.101.129.69``\ 。 +.. code:: shell -2. 域名的层级 -------------- + $ sshpass -p ${passwd} ssh -p ${port} -l ${user} -o StrictHostKeyChecking=no xx.xx.xx.xx "ls -l" -由于后面我会讲到 DNS 的解析过程,因此需要你对域名的层级有一些了解 +然后你会发现,你的输出有很多你并不需要,但是又不去不掉的一些信息(也许有方法,请留言交流),类似这样 -- 根域名 :\ ``.root`` 或者 ``.`` ,通常是省略的 -- 顶级域名,如 ``.com``\ ,\ ``.cn`` 等 -- 次级域名,如 ``baidu.com`` 里的 ``baidu``\ ,这个用户是可以注册购买的 -- 主机域名,比如 ``baike.baidu.com`` - 里的\ ``baike``\ ,这个用户是可分配的 +.. code:: shell -:: + host: xx.xx.xx.xx, port: xx + Warning: Permanently added '[xx.xx.xx.xx]:xx' (RSA) to the list of known hosts. + Login failure: [Errno 1] This server is not registered to rmp platform, please confirm whether cdn server. + total 4 + -rw-r--r-- 1 root root 239 Mar 30 2018 admin-openrc - 主机名.次级域名.顶级域名.根域名 - - baike.baidu.com.root - -3. DNS 解析过程 ---------------- - -咱们以访问 ``www.163.com`` 这个域名为例,来看一看当你访问 www.163.com -时,会发生哪些事: - -1. 先查找本地 DNS 缓存(自己的电脑上),有则返回,没有则进入下一步 -2. 查看本地 hosts 文件有没有相应的映射记录,有则返回,没有则进入下一步 -3. 向本地 DNS - 服务器(一般都是你的网络接入服务器商提供,比如中国电信,中国移动)发送请求进行查询,本地DNS服务器收到请求后,会先查下自己的缓存记录,如果查到了直接返回就结束了,如果没有查到,本地DNS服务器就会向DNS的根域名服务器发起查询请求:请问老大, - ``www.163.com`` 的ip是啥? -4. 根域名服务器收到请求后,看到这是个 ``.com`` - 的域名,就回信说:这个域名是由 ``.com`` - 老弟管理的,你去问他好了,这是\ ``.com``\ 老弟的联系方式(ip1)。 -5. 本地 DNS 服务器接收到回信后,照着老大哥给的联系方式(ip1),马上给 - ``.com`` 这个顶级域名服务器发起请求:请问 ``.com`` - 大大,\ ``www.163.com`` 的ip 是啥? -6. ``.com`` 顶级域名服务器接收到请求后,看到这是 ``163.com`` - 的域名,就回信说:这个域名是 ``.163.com`` - 老弟管理的,你就去问他就行了,这是他的联系方式(ip2) -7. 本地 DNS 服务器接收到回信后,按照前辈的指引(ip2),又向 ``.163.com`` - 这个权威域名服务器发起请求:请问 ``163.com`` 大大,请问 - ``www.163.com`` 的ip是啥? -8. ``163.com`` - 权威域名服务器接收到请求后,确认了是自己管理的域名,马上查了下自己的小本本,把 - ``www.163.com`` 的ip告诉了 本地DNS服务器。 -9. 本地DNS服务器接收到回信后,非常地开心,这下总算拿到了\ ``www.163.com``\ 的ip了,马上把这个消息告诉了要求查询的客户(就是你的电脑)。由于这个过程比较漫长,本地DNS服务器为了节省时间,也为了尽量不去打扰各位老大哥,就把这个查询结果偷偷地记在了自己的小本本上,方便下次有人来查询时,可以快速回应。 - -总结起来就是三句话 - -1. 从“根域名服务器”查到“顶级域名服务器”的NS记录和A记录(IP地址) -2. 从“顶级域名服务器”查到“次级域名服务器”的NS记录和A记录(IP地址) -3. 从“次级域名服务器”查出“主机名”的IP地址 +对于直接使用 shell +命令,来执行命令的,可以直接使用管道,或者将标准输出重定向到文件的方法取得执行命令返回的结果 -|image1| +1. 使用 subprocess +------------------ -4. DNS的缓存时间 ----------------- +若是使用 Python 来做这件事,通常我们会第一时间,想到使用 +os.popen,os.system,commands,subprocess 等一些命令执行库来间接获取 。 -上面的几个步骤里,可以看到有两个地方会缓存 DNS -的查询记录,有了缓存,在一定程度上会提高查询效率,但同时在准确率上会有所损失。 +但是据我所知,这些库获取的 output +不仅只有标准输出,还包含标准错误(也就是上面那些多余的信息) -因此我们在配置 DNS 解析的时候,会有一个 TTL 参数(Time To -Live),意思就是这个缓存可以存活多长时间,过了这个时间,本地 DNS -就会删除这条记录,删除了缓存后,你再访问,就要重新走一遍上面的流程,获取最新的地址。 +所以每次都要对 output +进行的数据清洗,然后整理格式化,才能得到我们想要的数据。 -|image2| +用 subprocess 举个例子,就像这样子 -5. DNS 的记录类型 ------------------ +.. code:: python -当我们在阿里云买了一个域名后,可以配置我们主机域名解析规则,也就是 -**记录**\ 。 + import subprocess + ssh_cmd = "sshpass -p ${passwd} ssh -p 22 -l root -o StrictHostKeyChecking=no xx.xx.xx.xx 'ls -l'" + status, output = subprocess.getstatusoutput(ssh_cmd) -.. figure:: http://image.iswbm.com/image-20200531170212224.png - :alt: 阿里云 域名云解析 + # 数据清理,格式化的就不展示了 + - 阿里云 域名云解析 +通过以上的文字 + 代码的展示 ,可以感觉到 ssh 登陆的几大痛点 -常见的 DNS 记录类型如下 +- **痛点一**\ :需要额外安装 sshpass(如果不免密的话) +- **痛点二**\ :干扰信息太多,数据清理、格式化相当麻烦 +- **痛点三**\ :代码实现不够优雅(有点土),可读性太差 +- **痛点四**\ :ssh 连接不能复用,一次连接仅能执行一次 +- **痛点五**\ :代码无法全平台,仅能在 Linux 和 OSX 上使用 -- ``A``\ :地址记录(Address),返回域名指向的IP地址。 +为了解决这几个问题,我搜索了全网关于 Python ssh +的文章,没有看到有完整介绍这方面的技巧的。 -- ``NS``\ :域名服务器记录(Name - Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址。 -- ``MX``\ :邮件记录(Mail eXchange),返回接收电子邮件的服务器地址。 -- ``CNAME``\ :规范名称记录(Canonical - Name),返回另一个域名,即当前查询的域名是另一个域名的跳转,详见下文。 -- ``PTR``\ :逆向查询记录(Pointer - Record),只用于从IP地址查询域名,详见下文。 +为此,我就翻阅了一个很火的 Github 项目: awesome-python-cn +(https://github.com/BingmingWong/awesome-python-cn)。 -6. DNS 报文结构 ---------------- +期望在这里,找到有一些关于 远程连接 的一些好用的库。 -后面我将使用 wireshark 抓取 DNS 的数据包,但是在开始之前 ,得先了解一下 -DNS 的报文结构 +还真的被我找到了两个 -|image3| +- sh.ssh +- Paramiko -- 事务 ID:DNS 报文的 ID - 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。通过它可以区分 - DNS 应答报文是对哪个请求进行响应的。 -- 标志:DNS 报文中的标志字段。 -- 问题计数:DNS 查询请求的数目。 -- 回答资源记录数:DNS 响应的数目。 -- 权威名称服务器计数:权威名称服务器的数目。 -- 附加资源记录数:额外的记录数目(权威名称服务器对应 IP 地址的数目)。 +2. 使用 sh.ssh +-------------- -7. Wireshark抓包实战 --------------------- +首先来介绍第一个,\ ``sh.ssh`` -打开 Wireshark 后,使用 ``ping 163.com`` 来发起 DNS 解析请求,使用 -``DNS`` 关键字在Wireshark 过滤。 +``sh`` 是一个可以让你通过函数的调用来完成 Linxu/OSX +系统命令的一个库,非常好用,关于它有机会也写篇介绍。 -从抓取的报文整体来看,我们可以粗略获取几个信息 +.. code:: shell -1. DNS 是应用层协议,传输层协议使用的是 UDP -2. DNS 默认端口是 53 + $ python3 -m pip install sh -|image4| +今天只介绍它其中的一个函数:\ ``ssh`` + +通常两台机器互访,为了方便,可设置免密登陆,这样就不需要输入密码。 -请求和应答的报文的截图我放在了下面,接下来我将逐个分析。 +这段代码可以实现免密登陆,并执行我们的命令 ``ls -l`` -**请求** +.. code:: python -|image5| + from sh import ssh + output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l") + print(output) -**应答** +但有可能 +,我们并不想设置互信免密,为了使这段代码更通用,我假定我们没有设置免密,只能使用密码进行登陆。 -|image6| +问题就来了,要输入密码,必须得使用交互式的方法来输入呀,在 Python +中要如何实现呢? -Transaction ID -~~~~~~~~~~~~~~ +原来 ssh 方法接收一个 ``_out`` +参数,这个参数可以为一个字符串,表示文件路径,也可以是一个文件对象(或者类文件对象),还可以是一个回调函数,意思是当有标准输出时,就会调用将输出内容传给这个函数。 -请求和应答的事务ID应当是一个:0xd0d7 +这就好办了呀。 -Flags -~~~~~ +我只要识别到有 ``password:`` 字样,就往标准输入写入我的密码就好了呀。 -标志字段里的内容比较多,每个字段的含义如下 +完整代码如下: -- QR(Response):查询请求/响应的标志信息。查询请求时,值为 - 0;响应时,值为 1。 -- Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 - 表示服务器状态请求。 -- AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 - 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。 -- TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 - 字节并已被截断,只返回前 512 个字节。 -- RD(Recursion - Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 - 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。 -- RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 - 1 时,表示服务器支持递归查询。 -- Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。 -- rcode(Reply code):返回码字段,表示响应的差错状态。当值为 0 - 时,表示没有错误;当值为 1 时,表示报文格式错误(Format - error),服务器不能理解请求的报文;当值为 2 - 时,表示域名服务器失败(Server - failure),因为服务器的原因导致没办法处理这个请求;当值为 3 - 时,表示名字错误(Name - Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为 - 4 时,表示查询类型不支持(Not - Implemented),即域名服务器不支持查询类型;当值为 5 - 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。 +.. code:: python -Answer RRs -~~~~~~~~~~ + import sys + from sh import ssh -回答资源记录数,在应答包里为 2,说明返回了两条查询结果,你可以在 Answer -字段里看到。 + aggregated = "" + def ssh_interact(char, stdin): + global aggregated + sys.stdout.write(char.encode()) + sys.stdout.flush() + aggregated += char + if aggregated.endswith("password: "): + stdin.put("you_password\n") -Authority RRs -~~~~~~~~~~~~~ + output=ssh("root@xx.xx.xx.xx", "-p 22", "ls -l",_tty_in=True, _out_bufsize=0, _out=ssh_interact) + print(output) -权威名称服务器计数 +这是官方文档(http://amoffat.github.io/sh/tutorials/interacting_with_processes.html?highlight=ssh)给的一些信息,写的一个demo。 -Additionnal RRs -~~~~~~~~~~~~~~~ +尝试运行后,发现程序会一直在运行中,永远不会返回,不会退出,回调函数也永远不会进入。 -附加资源记录数 +通过调试查看源代码,仍然查不到问题所在,于是去 +`Github `__ 上搜了下,原来在 +2017 年就已经存在这个问题了,到现在 2020 年了还没有修复,看来使用 +``sh.ssh`` 的人并不多,于是我又“追问”了下,期望能得到回复。 -Answers -~~~~~~~ +|image1| -应答的主要内容,这里返回两条结果,每条结果里的字段有 +以上这个问题,只有在需要输入密码才会出现,如果设置了机器互信是没有问题的。 -.. code:: shell +为了感受 ``sh.ssh`` +的使用效果,我设置了机器互信免密,然后使用如下这段代码。 + +.. code:: python - Name: 查询的域名 - Type: A表示IPv4,AAAA 表示IPv6 - Class: 表示Internet,几乎总是它 - Time to live: 生存时间 - Data length: 数据长度 - Address: 查询到的 IP 地址 + from sh import ssh -8. DNS 劫持 与 HTTP 劫持 ------------------------- + my_server=ssh.bake("root@xx.xx.xx.xx", "-p 22") -通过上面的讲解,我们都知道了,DNS 完成了一次域名到 IP -的映射查询,当你在访问 www.baidu.com 时,能正确返回给你 百度首页的 ip。 + # 相当于执行登陆一次执行一次命令,执行完就退出登陆 + print(my_server.ls()) -但如果此时 DNS 解析出现了一些问题,当你想要访问 www.baidu.com -时,却返回给你 www.google.com 的ip,这就是我们常说的 DNS 劫持。 + # 可在 sleep 期间,手动登陆服务器,使用 top ,查看当前有多少终端在连接 + time.sleep(5) -与之容易混淆的有 HTTP 劫持。 + # 再次执行这条命令时,登陆终端数将 +1,执行完后,又将 -1 + print(my_server.ifconfig()) -那什么是 HTTP 劫持呢? +惊奇地发现使用 ``bake`` 这种方式,\ ``my_server.ls()`` 和 +``my_server.ifconfig()`` +这种看似是通过同一个ssh连接,执行两次命令,可实际上,你可以在远程机器上,执行 +top 命令看到已连接的终端的变化,会先 ``+1`` 再 +``-1``\ ,说明两次命令的执行是通过两次连接实现的。 -你一定见过当你在访问 -某个网站时,右下角也突然弹出了一个扎眼的广告弹窗。这就是 HTTP 劫持。 +如此看来,使用 ``sh.ssh`` +可以解决痛点一(如果上述问题能得到解决)、痛点二、痛点三。 -借助别人文章里的例子,它们俩的区别就好比是 +但是它仍然无法复用 ssh 连接,还是不太方便,不是我理想中的最佳方案。 -- DNS劫持是你想去机场的时候,把你给丢到火车站。 +最重要的一点是, ``sh`` 这个模块,仅支持 Linxu/OSX ,在 Windows +你得使用它的兄弟库 - ``pbs`` ,然后我又去 pypi 看了一眼 +`pbs `__\ ,已经 “年久失修”,没人维护了。 -- HTTP劫持是你去机场途中,有人给你塞小广告。 +|image2| -**那么 DNS劫持 是如何产生的呢?** +至此,我离 “卒”,就差最后一根稻草了。 -下面大概说几种DNS劫持方法: +3. 使用 paramiko +---------------- -**1.本机DNS劫持** +带着最后一丝希望,我尝试使用了 ``paramiko`` 这个库,终于在 ``paramiko`` +这里,找回了本应属于 Python 的那种优雅。 -攻击者通过某些手段使用户的计算机感染上木马病毒,或者恶意软件之后,恶意修改本地DNS配置,比如修改本地hosts文件,缓存等 +你可以通过如下命令去安装它 -**2. 路由DNS劫持** +:: -很多用户默认路由器的默认密码,攻击者可以侵入到路由管理员账号中,修改路由器的默认配置 + $ python3 -m pip install paramiko -**3.攻击DNS服务器** +然后接下来,就介绍几种常用的 ssh 登陆的方法 -直接攻击DNS服务器,例如对DNS服务器进行DDOS攻击,可以是DNS服务器宕机,出现异常请求,还可以利用某些手段感染dns服务器的缓存,使给用户返回来的是恶意的ip地址 +方法1:基于用户名和密码的 sshclient 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -9. 工具的使用 -------------- +然后你可以参考如下这段代码,在 Linux/OSX 系统下进行远程连接 -dig 命令 -~~~~~~~~ +.. code:: python -dig是一个在类Unix命令行模式下查询DNS包括NS记录,A记录,MX记录等相关信息的工具。 + import paramiko -通过 dig (参数:\ ``+trace``\ )命令,我们可以看到上面描述的 DNS -解析的详细过程 + ssh = paramiko.SSHClient() + # 允许连接不在know_hosts文件中的主机 + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) -|image7| + # 建立连接 + ssh.connect("xx.xx.xx.xx", username="root", port=22, password="you_password") -从返回的结果,我们可以看得出几点信息 + # 使用这个连接执行命令 + ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") -1. 我们的本地 DNS 服务器 ip 为 192.168.1.1,端口为53,你可以在 - /etc/resolv.conf 里看到这个配置 -2. 根域名服务器目前全球一共只有十三台,从a.root-servers.net. - ``到``\ m.root-servers.net. - ,它们对应的ip地址,已经内置在本地DNS服务器中。 + # 获取输出 + print(ssh_stdout.read()) -如果你只想看到结果,可以使用 ``+short`` 参数,可以直接返回 www.163.com -对应着哪几个ip + # 关闭连接 + ssh.close() -|image8| +方法2:基于用户名和密码的 transport 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -你也可以加个 ``@`` 参数 ,指定从某个 DNS 服务器进行查询 +方法1 +是传统的连接服务器、执行命令、关闭的一个操作,多个操作需要连接多次,无法复用连接[**痛点四**]。 -|image9| +有时候需要登录上服务器执行多个操作,比如执行命令、上传/下载文件,方法1 +则无法实现,那就可以使用 transport 的方法。 -如果你只想查看指定的记录类型 +.. code:: python -|image10| + import paramiko -host 命令 -~~~~~~~~~ + # 建立连接 + trans = paramiko.Transport(("xx.xx.xx.xx", 22)) + trans.connect(username="root", password="you_passwd") -``host`` 命令 -可以看作\ ``dig``\ 命令的简化版本,返回当前请求域名的各种记录。 + # 将sshclient的对象的transport指定为以上的trans + ssh = paramiko.SSHClient() + ssh._transport = trans -|image11| + # 剩下的就和上面一样了 + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("ls -l") + print(ssh_stdout.read()) -whois命令 -~~~~~~~~~ + # 关闭连接 + trans.close() -``whois``\ 命令用来查看域名的注册情况。 +方法3:基于公钥密钥的 SSHClient 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -|image12| +.. code:: python -nslookup命令 -~~~~~~~~~~~~ + import paramiko -nslookup也是常用的一个查询 DNS 解析结果的工具 + # 指定本地的RSA私钥文件 + # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 + pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') -.. code:: shell + # 建立连接 + ssh = paramiko.SSHClient() + ssh.connect(hostname='xx.xx.xx.xx', + port=22, + username='you_username', + pkey=pkey) - $ nslookup [查询的域名] [指定DNS服务器] + # 执行命令 + stdin, stdout, stderr = ssh.exec_command('ls -l') -|image13| + # 结果放到stdout中,如果有错误将放到stderr中 + print(stdout.read()) -你也可以指定公网的域名服务器进行查询,比如常见的 114.114.114.114 + # 关闭连接 + ssh.close() -|image14| +方法4:基于密钥的 Transport 方式登录 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -10. 手动清理本地缓存 --------------------- +.. code:: python -MacOS + import paramiko -.. code:: shell + # 指定本地的RSA私钥文件 + # 如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 + pkey = paramiko.RSAKey.from_private_key_file('/home/you_username/.ssh/id_rsa', password='12345') - $ sudo dscacheutil -flushcache - $ sudo killall -HUP mDNSResponder + # 建立连接 + trans = paramiko.Transport(('xx.xx.xx.xx', 22)) + trans.connect(username='you_username', pkey=pkey) -Windows + # 将sshclient的对象的transport指定为以上的trans + ssh = paramiko.SSHClient() + ssh._transport = trans -.. code:: shell + # 执行命令,和传统方法一样 + stdin, stdout, stderr = ssh.exec_command('df -hl') + print(stdout.read().decode()) - $ ipconfig /flushdns + # 关闭连接 + trans.close() -Linux +以上四种方法,可以帮助你实现远程登陆服务器执行命令,如果需要复用连接:一次连接执行多次命令,可以使用 +**方法二** 和 **方法四** -.. code:: shell +用完后,记得关闭连接。 + +实现 sftp 文件传输 +~~~~~~~~~~~~~~~~~~ + +同时,paramiko 做为 ssh 的完美解决方案,它非常专业,利用它还可以实现 +sftp 文件传输。 + +.. code:: python + + import paramiko + + # 实例化一个trans对象# 实例化一个transport对象 + trans = paramiko.Transport(('xx.xx.xx.xx', 22)) - # 使用NSCD的DNS缓存 - $ sudo /etc/init.d/nscd restart + # 建立连接 + trans.connect(username='you_username', password='you_passwd') - # 服务器或者路由器使用DNSMASQ - $ sudo dnsmasq restart + # 实例化一个 sftp对象,指定连接的通道 + sftp = paramiko.SFTPClient.from_transport(trans) -|image15| + # 发送文件 + sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt') + + # 下载文件 + sftp.get(remotepath='/tmp/22.txt', localpath='/tmp/33.txt') + trans.close() + +到这里,Paramiko +已经完胜了,但是仍然有一个痛点我们没有提及,就是多平台,说的就是 +Windows,这里就有一件好事,一件坏事了,。 + +好事就是:paramiko 支持 windows + +坏事就是:你需要做很多复杂的准备,你可 google +解决,但是我建议你直接放弃,坑太深了。 + +|image3| + +注意事项 +~~~~~~~~ + +使用 paramiko 的时候,有一点需要注意一下,这个也是我自己 “踩坑” +后才发现的,其实我觉得这个设计挺好的,如果你不需要等待它返回数据,可以直接实现异步效果,只不过对于不知道这个设计的人,确实是个容易掉坑的点 + +就是在执行 ``ssh.exec_command(cmd)`` 时,这个命令并不是同步阻塞的。 + +比如下面这段代码,执行时,你会发现 脚本立马就结束退出了,并不会等待 5 s +后,再 执行 ssh.close() + +.. code:: python + + import paramiko + + trans = paramiko.Transport(("172.20.42.1", 57891)) + trans.connect(username="root", password="youpassword") + ssh = paramiko.SSHClient() + ssh._transport = trans + stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") + ssh.close() + +但是如果改成这样,加上一行 stdout.read(), paramiko +就知道,你需要这个执行的结果,就会在 read() 进行阻塞。 + +.. code:: python + + import paramiko + + trans = paramiko.Transport(("172.20.42.1", 57891)) + trans.connect(username="root", password="youpassword") + ssh = paramiko.SSHClient() + ssh._transport = trans + stdin, stdout, stderr = ssh.exec_command("sleep 5;echo ok") + + # 加上一行 read() + print(stdout.read()) + ssh.close() + +4. 写在最后 +----------- + +经过了一番对比,和一些实例的展示,可以看出 Paramiko +是一个专业、让人省心的 ssh 利器,个人认为 Paramiko +模块是运维人员必学模块之一,如果你恰好需要在 Python 代码中实现 ssh +到远程服务器去获取一些信息,那么我把 Paramiko 推荐给你。 + +最后,希望这篇文章,能给你带来帮助。 + +5. 参考链接 +----------- + +- https://github.com/paramiko/paramiko +- http://docs.paramiko.org +- https://www.liujiangblog.com/blog/15/ +- http://docs.paramiko.org/en/stable/ + +|image4| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/464291-20170703113844956-354755333.jpg -.. |image2| image:: http://image.iswbm.com/image-20200531141521689.png -.. |image3| image:: http://image.iswbm.com/image-20200531152824672.png -.. |image4| image:: http://image.iswbm.com/20200531175736.png -.. |image5| image:: http://image.iswbm.com/20200531175811.png -.. |image6| image:: http://image.iswbm.com/image-20200531153110621.png -.. |image7| image:: http://image.iswbm.com/image-20200531162810531.png -.. |image8| image:: http://image.iswbm.com/image-20200531164525384.png -.. |image9| image:: http://image.iswbm.com/image-20200531170427834.png -.. |image10| image:: http://image.iswbm.com/image-20200531170543250.png -.. |image11| image:: http://image.iswbm.com/image-20200531171610902.png -.. |image12| image:: http://image.iswbm.com/image-20200531171905345.png -.. |image13| image:: http://image.iswbm.com/image-20200531145109182.png -.. |image14| image:: http://image.iswbm.com/image-20200531145449577.png -.. |image15| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200228085749.png +.. |image2| image:: http://image.iswbm.com/20200228093627.png +.. |image3| image:: http://image.iswbm.com/20200228111654.png +.. |image4| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_02.md b/source/c10/c10_02.md index 316caf8..496da16 100644 --- a/source/c10/c10_02.md +++ b/source/c10/c10_02.md @@ -1,149 +1,253 @@ -# 10.2 网络知识扫盲:如何理解 OSI七层模型 +# 10.2 pretty_errors 解决bug 洁癖 ![](http://image.iswbm.com/20200602135014.png) -OSI (Open System Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 +当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 **密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 -![img](https://pic4.zhimg.com/80/v2-854e3df8ea850c977c30cb1deb1f64db_1440w.jpg) +就像这样子,天呐,密集恐惧症要犯了都 -为什么要有这个模型呢? +![](http://image.iswbm.com/image-20200307210853246.png) -主要是为了实现开放系统环境中的互连性、互操作性和应用的可移植性。 +上面这段 traceback -有了这个模型,各个层的实现就可以独立开发,复用性强,增加自由度的同时,也提高了生产效率。 +- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了 +- 无法直接显示报错的代码,排查问题慢人一步,效率太低 -## 数据的发送过程 +那有没有一种办法,可以解决这些问题呢? -数据包的发起方,一定都是从高层发起,然后从上往下传输数据,下层为上层提供传输服务,下方不关心传输内容,上层不关心下层如何传输。 +当然有了,在 Python 中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。 -就如下面这张图所示 +今天要介绍的这个库呢,叫做 `pretty-errors` ,从名字上就可以知道它的用途,是用来美化错误信息的。 -![](http://image.iswbm.com/20200526233356.png) +通过这条命令你可以安装它 +```shell +$ python3 -m pip install pretty-errors +``` -## 应用层 -代表协议有:HTTP,HTTPS,FTP,SMTP,TELNET +## 1. 环境要求 -由于日常开发中,我们接触的基本都是 HTTP,这里就以 HTTP 为例。 +由于使用了 `pretty-errors` 后,你的 traceback 信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 `pretty-error` 时,请确保你使用的终端可以输出带有颜色的字体。 -这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页。 +在 windows 上你可以使用 Powershell,cmder 等 -览器会将你请求的信息,封装成一个 HTTP 数据包,这个数据包头会包含一些基本的头部信息及请求体(如果有的话)。然后交由下一层处理。 +在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2 -## 表示层 +## 2. 效果对比 -代表协议有:ASCII,SSL/TLS 等 +------ -这一层的作用是,将应用处理的信息转换为适合网络传输的格式。 +随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 -比如我只会说中文,而日本友人只会说日文,那么我们两个是无法交流的。但如果我们都会说英文,交流时我先在心里想好要说的话是什么,再用英语说出来,日本友人听到英文,在心里转换为日语,他就能弄懂我的意思,此时表示层就是各自在心里转化语言。 +![](http://image.iswbm.com/image-20200307212823345.png) -当浏览器请求回一堆数据,是解析成文本还是图片,就由表示层决定。数据的压缩、加密、打包等功能也都在这层完成。 +而使用了 pretty_errors 后,报错信息被美化成这样了。 -## 会话层 +![](http://image.iswbm.com/image-20200307213534278.png) -代表协议有: `ADSP`、`RPC` 等。 +是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? -负责建立和断开通信连接(数据流动的逻辑通路),以及数据的分割等数据传输相关的管理。 +当然这段代码少,你可能还没感受到,那就来看下 该项目在 Github上的一张效果对比图吧 -比如,何时建立连接,何时断开连接以及保持多久的连接,都是由会话层来进行管理的。 +![](https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67) -## 传输层 +## 3. 配置全局可用 -代表协议有 :TCP,UDP等 +可以看到使用了 pretty_errors 后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。 -**传输层起着可靠传输的作用。** +既然既然这样,那 pretty_errors 应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧? -对于 DNS 域名解析,传输层协议是 UDP +答案是显而易见的。 -而对于 HTTP 请求,传输层协议是 TCP +pretty_errors 和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。 -**那这两种协议有什么区别呢?** +使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 traceback 输出都自动美化。 -`TCP` 协议提供可靠的通信传输,简单说就是确认目标能通信的情况下才会传输数据(因此需要三次握手),传输过程如果丢了数据,也会重发。而 `UDP` 协议则不然,不会确认目标能否通信,只会根据协议发到对方地址的端口。至于对方收不收到,丢不丢包,一概不管。 +```shell +$ python3 -m pretty_errors +``` -无论是哪种协议,其协议头信息都会包含本地端口号,和目标端口号(还有报文长度)。 +![](http://image.iswbm.com/image-20200307214742135.png) -加上这层头信息后,封装好的数据包再交由网络层。 +配置完成后,你再运行任何脚本,traceback 都会自动美化了。 -## 网络层 +不仅是在我的 iTerm 终端下 -代表协议有:IP,ICMP 协议等,其也叫IP层,主要应用是路由器(并非我们家中的路由器,扩展阅读:https://www.zhihu.com/question/52176116)。 +![](http://image.iswbm.com/image-20200307213534278.png) -**网络层负责将数据传输到目标地址。** +在 PyCharm 中也会 -网络层将数据从发送端的主机发送到接收端的主机,两台主机间可能会存在很多数据链路,但网络层就是负责找出一条相对顺畅的通路将数据传递过去。传输的地址使用的是IP地址。 +![](http://image.iswbm.com/image-20200307215530270.png) -IP地址和我们的住址有点相似,我们的住址可以从省到市再到街逐步缩小范围,直至我们住址。IP地址也有这样的能力,通过不断转发到更近的IP地址,最终可以到达目标地址。如何选择这条路,就看网络层了。 +唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 `文件路径` 直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 下面自定义配置的方案解决这个问题(下面会讲到,参数是:`display_link`)。 -这好比是快递公司的路线规划者。快递公司有很多集散中心,根据集散中心的情况(是否拥堵),找出一条经过n个集散中心的路径将货物(数据)沿路运过去。 +![](http://image.iswbm.com/image-20200307215834623.png) -在这层里,会给数据包再加一些头信息,包括本地IP地址,目标IP地址(还有数据包的生存空间TTL),同时还会有一个Protocol字段 表示上层到底是 UDP还是TCP协议,这个是用于对端在接收到这个数据包后知道如何解析。 +因此,有些情况下,你并不想设置 `pretty_errors` 全局可用。 -加上这些头信息后,再把**数据报**(或者说**分组**、**报文**)传递给链路层。 +那怎么取消之前的配置呢? +只需要再次输出 `python -m pretty_errors`,输出入 `C` 即可清除。 +![](http://image.iswbm.com/image-20200307214600749.png) -## 链路层 -代表协议有:HDLC,PPP,SLIP 等,其也叫 MAC 层,主要应用是交换机,网桥。 -**该层负责物理层面(一个以太网相连)上互连的节点之间的通信传输。** +## 4. 单文件中使用 -数据链路层会将0、1序列划分为具有意义的数据帧传送给对端(数据帧的生成与接收)。举个例子可能会更好理解,暂且把需要传输的数据看作为不同来源的水,如果直接倒入池子中时,是无法重新分辨出不同来源的水的。但如果将不同来源的灌入瓶子中并打上记号,那就能区分出不同来源的水。这也就是为什么要划分为具有意义的数据帧传送给对端。 +取消全局可用后,你可以根据自己需要,在你需要使用 `pretty-errors` 的脚本文件中导入` pretty_errors `,即可使用 -数据链路层可以看作是快递公司的司机,他们驾驶着汽车,将打包好的货物(数据帧)从一个城市(物理节点)运输到另一个城市。 +```python +import pretty_errors +``` -在这层里,会给数据包再加一些头信息,包括本地的mac地址,目标mac地址,同时还有一个type字段表示上层协议是使用的是IP协议,还是其他什么协议,也是用于对端在接收到这个数据包后知道如何解析。 +就像这样 -需要注意的是,数据链路层只负责将数据运送给物理相连的两端,并不负责直接发送到最终地址。 +```python +import pretty_errors -加上这些头信息后,再把**数据帧**传递给物理层。 +def foo(): + 1/0 +if __name__ == "__main__": + foo() +``` +值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。 -这一层主要解决两个问题: +因此,为了让美化更彻底,官方推荐你使用 `python -m pretty_errors` -**第一个问题**:网络包是发给谁的,由谁来接收 +## 5. 自定义设置 -链路层协议头会包含目标MAC和源MAC +上面的例子里,我们使用的都是 `pretty_errors` 的默认美化格式,展示的信息并没有那么全。 -**第二个问题**:大家都在发包,谁先发,谁后发? +比如 -对于这个问题,有多种算法可以解决 +- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。 +- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。 -方法一:信道划分,就跟马路上分多个车道一样,你走你的,我走我的,互不影响 +如果使用了 `pretty_errors` 导致异常信息有丢失,那还不如不使用 `pretty_errors` 呢。 -方法二:轮流协议,还是以交通举例,今天单号出行,明天双号出行,轮着来 +不过,可以告诉你的是,`pretty_errors` 并没有你想象的那么简单。 -方法三:随机接入协议,不管三七二十一,有事儿先出门,发现特堵,就回去。错过高峰再出。 +它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示? +这里举一个例子 +```python +import pretty_errors -## 物理层 +# 【重点】进行配置 +pretty_errors.configure( + separator_character = '*', + filename_display = pretty_errors.FILENAME_EXTENDED, + line_number_first = True, + display_link = True, + lines_before = 5, + lines_after = 2, + line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, + code_color = ' ' + pretty_errors.default_config.line_color, +) -代表协议有: `RS 232C`、`RS 449/422/423`、`V.24` 和 `X.21`、`X.21bis` 等。 +# 原来的代码 +def foo(): + 1/0 -物理层负责0、1比特流(0、1序列)与电压高低、光的闪灭之间的互换。 +if __name__ == "__main__": + foo() +``` -物理层其实就是我们日常能接触的物理介质,比如光纤、电缆、还有空气(还有集线器、中继器、调制解调器),根据这些传输介质的不同,二进制流(0和1)会相应地转化成光信号,电信号,电磁波信号。 +在你像上面这样使用 `pretty_errrs.configure` 进行配置时,抛出的的异常信息就变成这样了。 -物理层是 `OSI` 七层模型的物理基础,没有它就谈不上数据传输了。 +![](http://image.iswbm.com/image-20200308121949011.png) -## 参考文章 +当然了,`pretty_errors.configure()` 还可以接收很多的参数,你可以根据你自己的需要进行配置。 -- https://juejin.im/post/59eb06b1f265da430f313c7f +### 5.1 设置颜色 +- `header_color`:设置标题行的颜色。 +- `timestamp_color`:设置时间戳颜色 +- `default_color`:设置默认的颜色 +- `filename_color`:设置文件名颜色 +- `line_number_color`:设置行号颜色。 +- `function_color`:设置函数颜色。 +- `link_color`:设置链接的颜色。 +在设置颜色的时候,`pretty_errors` 提供了一些常用的 颜色常量供你直接调取。 ---- +- `BLACK`:黑色 +- `GREY`:灰色 +- `RED`:红色 +- `GREEN`:绿色 +- `YELLOW`:黄色 +- `BLUE`:蓝色 +- `MAGENTA`:品红色 +- `CYAN`:蓝绿色 +- `WHITE`:白色 +而每一种颜色,都相应的匹配的 `BRIGHT_` 变体 和 `_BACKGROUND` 变体, +其中,`_BACKGROUND` 用于设置背景色,举个例子如下。 + +![](http://image.iswbm.com/image-20200308125431779.png) + +### 5.2 设置显示内容 + +- `line_number_first` + 启用后,将首先显示行号,而不是文件名。 +- `lines_before` : 显示发生异常处的前几行代码 +- `lines_after`: 显示发生异常处的后几行代码 +- `display_link`:启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 +- `separator_character`:用于创建标题行的字符。默认情况下使用连字符。如果设置为 `''` 或者 `None` ,标题将被禁用。 +- `display_timestamp`:启用时,时间戳将写入回溯头中。 +- `display_locals` + 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。 + +- `display_trace_locals` + 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。 + +### 5.3 设置怎么显示 + +- `line_length`:设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用`full_line_newline`,以防止出现明显的双换行符。 + +- `full_line_newline`:当输出的字符满行时,是否要插入换行符。 + +- `timestamp_function` + 调用该函数以生成时间戳。默认值为`time.perf_counter`。 + +- `top_first` + 启用后,堆栈跟踪将反转,首先显示堆栈顶部。 + +- `display_arrow` + 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。 + +- `truncate_code` + 启用后,每行代码将被截断以适合行长。 + +- `stack_depth` + 要显示的堆栈跟踪的最大条目数。什么时候`0`将显示整个堆栈,这是默认值。 + +- `exception_above` + 启用后,异常将显示在堆栈跟踪上方。 + +- `exception_below`: + 启用后,异常显示在堆栈跟踪下方。 + +- `reset_stdout` + 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。 + +- `filename_display` + + 设置文件名的展示方式,有三个选项: `pretty_errors.FILENAME_COMPACT` 、`pretty_errors.FILENAME_EXTENDED`,或者`pretty_errors.FILENAME_FULL` + + + +以上,就是我对 `pretty_errors` 的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 PEP8 规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,`pretty_errors` 会是一个不错的解决方案,明哥把它推荐给你。 ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_02.rst b/source/c10/c10_02.rst index db03718..bf2c4bf 100644 --- a/source/c10/c10_02.rst +++ b/source/c10/c10_02.rst @@ -1,160 +1,286 @@ -10.2 网络知识扫盲:如何理解 OSI七层模型 -======================================= +10.2 pretty_errors 解决bug 洁癖 +=============================== |image0| -OSI (Open System -Interconnect),即开放式系统互联。一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 +当我们写的一个脚本或程序发生各种不可预知的异常时,如果我们没有进行捕获处理的时候,通常都会致使程序崩溃退出,并且会在终端打印出一堆 +**密密麻麻** 的 traceback 堆栈信息来告诉我们,是哪个地方出了问题。 -.. figure:: https://pic4.zhimg.com/80/v2-854e3df8ea850c977c30cb1deb1f64db_1440w.jpg - :alt: img +就像这样子,天呐,密集恐惧症要犯了都 - img +|image1| + +上面这段 traceback + +- 只有黑白两个颜色,无法像代码高亮那样,对肉眼实现太不友好了 +- 无法直接显示报错的代码,排查问题慢人一步,效率太低 + +那有没有一种办法,可以解决这些问题呢? + +当然有了,在 Python +中,没有什么问题是一个库解决不了的,如果有,那就等你去开发这个库。 + +今天要介绍的这个库呢,叫做 ``pretty-errors`` +,从名字上就可以知道它的用途,是用来美化错误信息的。 + +通过这条命令你可以安装它 + +.. code:: shell + + $ python3 -m pip install pretty-errors -为什么要有这个模型呢? +1. 环境要求 +----------- -主要是为了实现开放系统环境中的互连性、互操作性和应用的可移植性。 +由于使用了 ``pretty-errors`` 后,你的 traceback +信息输出,会有代码高亮那样的效果,因此当你在使用测试使用 +``pretty-error`` 时,请确保你使用的终端可以输出带有颜色的字体。 -有了这个模型,各个层的实现就可以独立开发,复用性强,增加自由度的同时,也提高了生产效率。 +在 windows 上你可以使用 Powershell,cmder 等 + +在 Mac 上你可以使用自带的终端,或者安装一个更好用的 iTerm2 + +2. 效果对比 +----------- -数据的发送过程 -------------- -数据包的发起方,一定都是从高层发起,然后从上往下传输数据,下层为上层提供传输服务,下方不关心传输内容,上层不关心下层如何传输。 +随便写一个没有使用 pretty-errors ,并且报错了的程序,是这样子的。 -就如下面这张图所示 +|image2| -|image1| +而使用了 pretty_errors 后,报错信息被美化成这样了。 -应用层 ------- +|image3| -代表协议有:HTTP,HTTPS,FTP,SMTP,TELNET +是不是感觉清楚了不少,那种密密麻麻带来的焦虑感是不是都消失了呢? -由于日常开发中,我们接触的基本都是 HTTP,这里就以 HTTP 为例。 +当然这段代码少,你可能还没感受到,那就来看下 该项目在 +Github上的一张效果对比图吧 -这层负责的是真正业务上的内容,比如你在你的浏览器上,请求一个百度的首页。 +|image4| -览器会将你请求的信息,封装成一个 HTTP -数据包,这个数据包头会包含一些基本的头部信息及请求体(如果有的话)。然后交由下一层处理。 +3. 配置全局可用 +--------------- -表示层 ------- +可以看到使用了 pretty_errors +后,无非就是把过滤掉了一些干扰我们视线的无用信息,然后把有用的关键信息给我们高亮显示。 -代表协议有:ASCII,SSL/TLS 等 +既然既然这样,那 pretty_errors +应该也能支持我们如何自定义我们选用什么样的颜色,怎么排版吧? -这一层的作用是,将应用处理的信息转换为适合网络传输的格式。 +答案是显而易见的。 -比如我只会说中文,而日本友人只会说日文,那么我们两个是无法交流的。但如果我们都会说英文,交流时我先在心里想好要说的话是什么,再用英语说出来,日本友人听到英文,在心里转换为日语,他就能弄懂我的意思,此时表示层就是各自在心里转化语言。 +pretty_errors +和其他库不太一样,在一定程度上(如果你使用全局配置的话),它并不是开箱即用的,你在使用它之前可能需要做一下配置。 -当浏览器请求回一堆数据,是解析成文本还是图片,就由表示层决定。数据的压缩、加密、打包等功能也都在这层完成。 +使用这一条命令,会让你进行配置,可以让你在该环境中运行其他脚本时的 +traceback 输出都自动美化。 -会话层 ------- +.. code:: shell -代表协议有: ``ADSP``\ 、\ ``RPC`` 等。 + $ python3 -m pretty_errors -负责建立和断开通信连接(数据流动的逻辑通路),以及数据的分割等数据传输相关的管理。 +|image5| -比如,何时建立连接,何时断开连接以及保持多久的连接,都是由会话层来进行管理的。 +配置完成后,你再运行任何脚本,traceback 都会自动美化了。 -传输层 ------- +不仅是在我的 iTerm 终端下 -代表协议有 :TCP,UDP等 +|image6| -**传输层起着可靠传输的作用。** +在 PyCharm 中也会 -对于 DNS 域名解析,传输层协议是 UDP +|image7| -而对于 HTTP 请求,传输层协议是 TCP +唯一的缺点就是,原先在 PyCharm 中的 traceback 可以直接点击 ``文件路径`` +直接跳转到对应错误文件代码行,而你如果是在 VSCode 可以使用 +下面自定义配置的方案解决这个问题(下面会讲到,参数是:\ ``display_link``\ )。 -**那这两种协议有什么区别呢?** +|image8| -``TCP`` -协议提供可靠的通信传输,简单说就是确认目标能通信的情况下才会传输数据(因此需要三次握手),传输过程如果丢了数据,也会重发。而 -``UDP`` -协议则不然,不会确认目标能否通信,只会根据协议发到对方地址的端口。至于对方收不收到,丢不丢包,一概不管。 +因此,有些情况下,你并不想设置 ``pretty_errors`` 全局可用。 -无论是哪种协议,其协议头信息都会包含本地端口号,和目标端口号(还有报文长度)。 +那怎么取消之前的配置呢? -加上这层头信息后,封装好的数据包再交由网络层。 +只需要再次输出 ``python -m pretty_errors``\ ,输出入 ``C`` 即可清除。 -网络层 ------- +|image9| -代表协议有:IP,ICMP -协议等,其也叫IP层,主要应用是路由器(并非我们家中的路由器,扩展阅读:https://www.zhihu.com/question/52176116)。 +4. 单文件中使用 +--------------- -**网络层负责将数据传输到目标地址。** +取消全局可用后,你可以根据自己需要,在你需要使用 ``pretty-errors`` +的脚本文件中导入\ ``pretty_errors``\ ,即可使用 -网络层将数据从发送端的主机发送到接收端的主机,两台主机间可能会存在很多数据链路,但网络层就是负责找出一条相对顺畅的通路将数据传递过去。传输的地址使用的是IP地址。 +.. code:: python -IP地址和我们的住址有点相似,我们的住址可以从省到市再到街逐步缩小范围,直至我们住址。IP地址也有这样的能力,通过不断转发到更近的IP地址,最终可以到达目标地址。如何选择这条路,就看网络层了。 + import pretty_errors -这好比是快递公司的路线规划者。快递公司有很多集散中心,根据集散中心的情况(是否拥堵),找出一条经过n个集散中心的路径将货物(数据)沿路运过去。 +就像这样 -在这层里,会给数据包再加一些头信息,包括本地IP地址,目标IP地址(还有数据包的生存空间TTL),同时还会有一个Protocol字段 -表示上层到底是 -UDP还是TCP协议,这个是用于对端在接收到这个数据包后知道如何解析。 +.. code:: python -加上这些头信息后,再把\ **数据报**\ (或者说\ **分组**\ 、\ **报文**\ )传递给链路层。 + import pretty_errors -链路层 ------- + def foo(): + 1/0 -代表协议有:HDLC,PPP,SLIP 等,其也叫 MAC 层,主要应用是交换机,网桥。 + if __name__ == "__main__": + foo() -**该层负责物理层面(一个以太网相连)上互连的节点之间的通信传输。** +值得一提的是,使用这种方式,若是你的脚本中,出现语法错误,则输出的异常信息还是按照之前的方式展示,并不会被美化。 -数据链路层会将0、1序列划分为具有意义的数据帧传送给对端(数据帧的生成与接收)。举个例子可能会更好理解,暂且把需要传输的数据看作为不同来源的水,如果直接倒入池子中时,是无法重新分辨出不同来源的水的。但如果将不同来源的灌入瓶子中并打上记号,那就能区分出不同来源的水。这也就是为什么要划分为具有意义的数据帧传送给对端。 +因此,为了让美化更彻底,官方推荐你使用 ``python -m pretty_errors`` -数据链路层可以看作是快递公司的司机,他们驾驶着汽车,将打包好的货物(数据帧)从一个城市(物理节点)运输到另一个城市。 +5. 自定义设置 +------------- -在这层里,会给数据包再加一些头信息,包括本地的mac地址,目标mac地址,同时还有一个type字段表示上层协议是使用的是IP协议,还是其他什么协议,也是用于对端在接收到这个数据包后知道如何解析。 +上面的例子里,我们使用的都是 ``pretty_errors`` +的默认美化格式,展示的信息并没有那么全。 -需要注意的是,数据链路层只负责将数据运送给物理相连的两端,并不负责直接发送到最终地址。 +比如 -加上这些头信息后,再把\ **数据帧**\ 传递给物理层。 +- 它并没有展示报错文件的绝对路径,这将使我们很难定位到是哪个文件里的代码出现错误。 +- 如果能把具体报错的代码,给我们展示在终端屏幕上,就不需要我们再到源码文件中排查原因了。 -这一层主要解决两个问题: +如果使用了 ``pretty_errors`` 导致异常信息有丢失,那还不如不使用 +``pretty_errors`` 呢。 -**第一个问题**\ :网络包是发给谁的,由谁来接收 +不过,可以告诉你的是,\ ``pretty_errors`` 并没有你想象的那么简单。 -链路层协议头会包含目标MAC和源MAC +它足够开放,支持自定义配置,可以由你选择你需要展示哪些信息,怎么展示? -**第二个问题**\ :大家都在发包,谁先发,谁后发? +这里举一个例子 -对于这个问题,有多种算法可以解决 +.. code:: python -方法一:信道划分,就跟马路上分多个车道一样,你走你的,我走我的,互不影响 + import pretty_errors -方法二:轮流协议,还是以交通举例,今天单号出行,明天双号出行,轮着来 + # 【重点】进行配置 + pretty_errors.configure( + separator_character = '*', + filename_display = pretty_errors.FILENAME_EXTENDED, + line_number_first = True, + display_link = True, + lines_before = 5, + lines_after = 2, + line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, + code_color = ' ' + pretty_errors.default_config.line_color, + ) -方法三:随机接入协议,不管三七二十一,有事儿先出门,发现特堵,就回去。错过高峰再出。 + # 原来的代码 + def foo(): + 1/0 -物理层 ------- + if __name__ == "__main__": + foo() -代表协议有: ``RS 232C``\ 、\ ``RS 449/422/423``\ 、\ ``V.24`` 和 -``X.21``\ 、\ ``X.21bis`` 等。 +在你像上面这样使用 ``pretty_errrs.configure`` +进行配置时,抛出的的异常信息就变成这样了。 -物理层负责0、1比特流(0、1序列)与电压高低、光的闪灭之间的互换。 +|image10| -物理层其实就是我们日常能接触的物理介质,比如光纤、电缆、还有空气(还有集线器、中继器、调制解调器),根据这些传输介质的不同,二进制流(0和1)会相应地转化成光信号,电信号,电磁波信号。 +当然了,\ ``pretty_errors.configure()`` +还可以接收很多的参数,你可以根据你自己的需要进行配置。 -物理层是 ``OSI`` 七层模型的物理基础,没有它就谈不上数据传输了。 +5.1 设置颜色 +~~~~~~~~~~~~ -参考文章 --------- +- ``header_color``\ :设置标题行的颜色。 +- ``timestamp_color``\ :设置时间戳颜色 +- ``default_color``\ :设置默认的颜色 +- ``filename_color``\ :设置文件名颜色 +- ``line_number_color``\ :设置行号颜色。 +- ``function_color``\ :设置函数颜色。 +- ``link_color``\ :设置链接的颜色。 -- https://juejin.im/post/59eb06b1f265da430f313c7f +在设置颜色的时候,\ ``pretty_errors`` 提供了一些常用的 +颜色常量供你直接调取。 --------------- +- ``BLACK``\ :黑色 +- ``GREY``\ :灰色 +- ``RED``\ :红色 +- ``GREEN``\ :绿色 +- ``YELLOW``\ :黄色 +- ``BLUE``\ :蓝色 +- ``MAGENTA``\ :品红色 +- ``CYAN``\ :蓝绿色 +- ``WHITE``\ :白色 -|image2| +而每一种颜色,都相应的匹配的 ``BRIGHT_`` 变体 和 ``_BACKGROUND`` 变体, + +其中,\ ``_BACKGROUND`` 用于设置背景色,举个例子如下。 + +|image11| + +5.2 设置显示内容 +~~~~~~~~~~~~~~~~ + +- ``line_number_first`` 启用后,将首先显示行号,而不是文件名。 +- ``lines_before`` : 显示发生异常处的前几行代码 +- ``lines_after``\ : 显示发生异常处的后几行代码 +- ``display_link``\ :启用后,将在错误位置下方写入链接,VScode将允许您单击该链接。 +- ``separator_character``\ :用于创建标题行的字符。默认情况下使用连字符。如果设置为 + ``''`` 或者 ``None`` ,标题将被禁用。 +- ``display_timestamp``\ :启用时,时间戳将写入回溯头中。 +- ``display_locals`` + 启用后,将显示在顶部堆栈框架代码中的局部变量及其值。 + +- ``display_trace_locals`` + 启用后,其他堆栈框架代码中出现的局部变量将与它们的值一起显示。 + +5.3 设置怎么显示 +~~~~~~~~~~~~~~~~ + +- ``line_length``\ :设置每行的长度,默认为0,表示每行的输出将与控制台尺寸相匹配,如果你设置的长度将好与控制台宽度匹配,则可能需要禁用\ ``full_line_newline``\ ,以防止出现明显的双换行符。 + +- ``full_line_newline``\ :当输出的字符满行时,是否要插入换行符。 + +- ``timestamp_function`` + 调用该函数以生成时间戳。默认值为\ ``time.perf_counter``\ 。 + +- ``top_first`` 启用后,堆栈跟踪将反转,首先显示堆栈顶部。 + +- ``display_arrow`` + 启用后,将针对语法错误显示一个箭头,指向有问题的令牌。 + +- ``truncate_code`` 启用后,每行代码将被截断以适合行长。 + +- ``stack_depth`` + 要显示的堆栈跟踪的最大条目数。什么时候\ ``0``\ 将显示整个堆栈,这是默认值。 + +- ``exception_above`` 启用后,异常将显示在堆栈跟踪上方。 + +- ``exception_below``\ : 启用后,异常显示在堆栈跟踪下方。 + +- ``reset_stdout`` + 启用后,重置转义序列将写入stdout和stderr;如果您的控制台留下错误的颜色,请启用此选项。 + +- ``filename_display`` + + 设置文件名的展示方式,有三个选项: ``pretty_errors.FILENAME_COMPACT`` + 、\ ``pretty_errors.FILENAME_EXTENDED``\ ,或者\ ``pretty_errors.FILENAME_FULL`` + +以上,就是我对 ``pretty_errors`` +的使用体验,总的来说,这个库功能非常强大,使用效果也特别酷炫,它就跟 +PEP8 +规范一样,没有它是可以,但是有了它会更好一样。对于某些想自定义错误输出场景的人,\ ``pretty_errors`` +会是一个不错的解决方案,明哥把它推荐给你。 + +|image12| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200526233356.png -.. |image2| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/image-20200307210853246.png +.. |image2| image:: http://image.iswbm.com/image-20200307212823345.png +.. |image3| image:: http://image.iswbm.com/image-20200307213534278.png +.. |image4| image:: https://warehouse-camo.cmh1.psfhosted.org/31399c5a034c3989b9e99b35249e8f2f0d40e102/68747470733a2f2f692e696d6775722e636f6d2f306a7045716f622e706e67 +.. |image5| image:: http://image.iswbm.com/image-20200307214742135.png +.. |image6| image:: http://image.iswbm.com/image-20200307213534278.png +.. |image7| image:: http://image.iswbm.com/image-20200307215530270.png +.. |image8| image:: http://image.iswbm.com/image-20200307215834623.png +.. |image9| image:: http://image.iswbm.com/image-20200307214600749.png +.. |image10| image:: http://image.iswbm.com/image-20200308121949011.png +.. |image11| image:: http://image.iswbm.com/image-20200308125431779.png +.. |image12| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md index f8286a3..3b8caf0 100644 --- a/source/c10/c10_03.md +++ b/source/c10/c10_03.md @@ -1,574 +1,185 @@ -# 10.3 网络知识扫盲:详解TCP的三次握手与四次挥手 +# 1.47 少有人知的 Python "重试机制" -![](http://image.iswbm.com/20200602135014.png) +为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 +这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 +这里要给大家介绍的是一个第三方库 - `Tenacity` ,它实现了几乎我们可以使用到的所有重试场景,比如: -## 1. TCP 协议是什么? +1. 在什么情况下才进行重试? +2. 重试几次呢? +3. 重试多久后结束? +4. 每次重试的间隔多长呢? +5. 重试失败后的回调? -TCP 是 Transmission Control Protocol 的缩写,意思是传输控制协议。一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。 +在使用它之前 ,先要安装它 -从这个定义里,有很多初学就首先懵了。 - -什么是面向连接? - -什么是可靠的通信协议? - -什么是面向字节流的? - - - -为了让你对 TCP 有个初步的了解,我打算先从这个定义入手。 - -### 什么是面向连接? - -面向连接,是相对于另一个传输层协议 UDP 而言的(后面会单独介绍)。 - -TCP 是面向连接的,所以在开始传输数据前要先经历三次握手建立连接。 - -而 UDP 即刻就可以传输数据,并不需要先三次握手来建立连接。 - -一个更可靠,而一个更开放。 - -就好比,你去医院看病,如果是专家号,一般要提前预约,对只要预约(三次握手建立了连接)上了,你去了就不会看不上病。这是 TCP 。 - -而如果你没有预约,就直接跑过去,那不好意思,你只能看普通门诊,而普通门诊等的人很多,你就不一定能看得上病了。这是 UDP。 - -既然是连接,必然是一对一的,就像绳子的两端。所以 TCP 是一对一发送消息。 - -而 UDP 协议不需要连接,可以一对一,也可以一对多,也可以多对多发送消息。 - -### 什么是可靠的通信协议? - -可不可靠,也是相对于 UDP 而言的。 - -TCP 自身有三次握手和超时重传等机制,所以无论网络如何变化,主要不是主机宕机等原因都可以保证一个报文可以到达目标主机。 - -与之对比, UDP 就比较不负责任了,不管你收不收得到,反正我就无脑发,网络拥堵我也发,它的职责是发出去。 - -### 什么是面向字节流的? - -与面向字节流相对的是,UDP 的面向报文。 - -面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会使IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 - -面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。 - - -## 2. 完整解读 TCP 报文格式 - -搞懂一个通信协议,了解它的报文格式是必经之路。 - -TCP 的报文段结构,可以从下面这张图中非常清晰的看到。 - -![TCP 报文首部](http://image.iswbm.com/20200606095627.png) - -接下来,我会一个一个讲解这些字段的内容。 - -**源端口** 和 **目标端口**:各占 2 个字节。2 个字节,也就是 16个 bit,这应该也能说明为什么计算机端口的范围是 1-65535 (0 不使用,2^16=65536,最大位65536不使用)了吧?有了源端口和目标端口,加上 IP 首部里的源IP和目标IP,就可以唯一确定一个连接。 - -**序列号**:共占用 4个字节。说明序列号的范围是 [0, 2^32-1],也就是 [0, 4294967296]。当序号增加到 4294967296 后,下一个序号将回到0重新开始。在建立连接时由计算机生成的随机数作为其初始值(ISN,即Initial Sequence Number,初始序列号),通过 SYN 包传给接收端主机,每发送一次数据,就**累加**一次该「数据字节数」的大小(其中要注意的是 SYN 和 FIN 包的 seq 也要消耗一个序号)。**用来解决网络包乱序问题。** - -**确认号**:共占用 4个字节。说明确认号的范围是 [0, 2^32-1],也就是 [0, 4294967296]。它表示**期望**收到对方下一次数据的序列号(所以 ack 一般都是上次接收成功的数据字节序号加1),发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。**用来解决不丢包的问题**。TCP在接收到数据后 200ms 才会发送ACK包,这种设定是为了等待是否有数据可以一起发送的。 - -**数据偏移(图中为报文首部)**:共占 4 个bit,它表示的是TCP报文的数据起始处距离TCP报文起始处的距离有多远。实际生活中我们说距离多远,我们的单位通常是米,而这里距离有多远,单位是 4 个字节(也就是 32bit)。由于 4 个bit,能表示的最大整数是 15,也就说明 TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15)。这意味着 TCP 的首部(除数据外的都叫首部)长度是 20-60 个字节。 - -**窗口**:共占 16 个bit,因此最大的窗口大小为 2^16-1 = 65535 = 64k。这是早期的设计,对于现在的网络应用,可能会不太够,因此可以在选项里加一个 **窗口扩大选项**,来传输更多的数据。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。 - -**保留**:占 6个bit,保留为今后使用,目前应置为0。 - -**紧急指针**:占16个bit。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) 。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,**即使窗口为0时也可以发送紧急数据**。 - -**标志位:** - -- **SYN**(SYNchronization): 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。 -- **ACK**(ACKnowledgment):仅当ACK = 1时确认号字段才有效,当ACK = 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1,如果你可以看下我后面 wireshark 抓的包里除了 最初建立连接的 SYN 包之外,其他的包也都有 ACK 标志。 -- **RST**(ReSet):当 RST=1 时,表示 TCP 连接中出现异常(如主机崩溃或其他原因)必须强制断开连接,然后再重新建立连接进行传输。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。 -- **FIN**(Finish):当FIN=1 时,表示今后不会再有数据发送,希望断开连接。 -- **PSH**(Push) 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。 -- **URG**(Urgent):当URG=1时,表明开户了urgent mode,紧急指针就开始生效了。 - -**选项**:长度可变,最长可达40个字节。当没有使用“选项”时,TCP的首部长度是20字节。 - -- MSS 选项:TCP报文段中的数据字段的最大长度,后面有详解。 -- 窗口扩大选项:占用三个字节,使得接收端可接收更多的数据,由 2^16-1 扩充到 2^(16+14)-1,其中这个14是窗口移位记数器的最大值。详情请参见:TCP/IP详解 卷1 协议 P262 -- 时间戳选项:共占 10 个字节,其中最主要的字段是时间戳字段(4字节)和时间戳回送回答字段(4字节)。 - - - -## 3. 如何模拟 TCP 连接? - -只搞懂报文格式,没有实战的话,就永远只停留在字面上,无法深刻地理解它。 - -所以接下来我会使用 wireshark 进行对三次握手、数据传输、四次挥手进行一次抓包并分析这个过程。 - -但是在开始之前 ,首先要学会模拟建立一个 tcp 连接,好能让我们轻松使用过滤器来显示结果。 - -为此我使用 Python 写了两个小脚本 - -**1、服务端** - -监听 13200 端口,如果有客户端连接就发送 hello 字符串 - -```python -# tcp_server.py - -import socket # 导入 socket 模块 -import time - -s = socket.socket() # 创建 socket 对象 -host = socket.gethostname() # 获取本地主机名 -port = 13200 # 设置端口 -s.bind((host, port)) # 绑定端口 - -s.listen(5) # 等待客户端连接 -while True: - c, addr = s.accept() # 建立客户端连接 - c.send('hello'.encode("utf-8")) - c.send('world'.encode("utf-8")) - time.sleep(1) - c.close() # 关闭连接 +```shell +$ pip install tenacity ``` -运行后,可以使用 lsof 命令查看 13200 端口是否处于监听中 -![](http://image.iswbm.com/image-20200601221524846.png) -**2、客户端** +### **最基本的重试** -连接 13200 端口,并接收并打印服务端发送的内容 +无条件重试,重试之间无间隔 ```python -# tcp_client.py - -import socket # 导入 socket 模块 -import time +from tenacity import retry -s = socket.socket() # 创建 socket 对象 -host = socket.gethostname() # 获取本地主机名 -port = 13200 # 设置端口号 +@retry +def test_retry(): + print("等待重试,重试无间隔执行...") + raise Exception -s.connect((host, port)) -print(s.recv(1024)) -time.sleep(2) -s.close() +test_retry() ``` +无条件重试,但是在重试之前要等待 2 秒 +```python +from tenacity import retry, wait_fixed -## 4. Wireshark 抓包实战分析 - -一切准备就绪后,打开我们的 wireshark ,并设置捕获过滤器 port=13200 - -![](http://image.iswbm.com/image-20200601222110435.png) - -然后开启抓包,最后执行上面的 客户端代码` tcp_client.py`,就可以在 wireshark 上看到如下内容。 - -![](http://image.iswbm.com/image-20200602234904143.png) - - - -### 三次握手 - -三次握手的过程可以参考下面这张图来帮助理解 - -![](http://image.iswbm.com/20200605130951.png) - -使用 wireshark 抓到的三次握手的包如下所示 - -![wireshare 三次握手](http://image.iswbm.com/image-20200603003018160.png) - -客户端要连接上服务端,首先要发送一个 SYN 包表示请求连接。这个SYN 包的 seq 为0。这是第一次握手。 - -当服务端接收这个 SYN 包时,知道了有人要连接自己,就发了一个 ACK 包说: 你要连接这件事,我已经知道啦。但是连接是双方的事情,我也要连接客户端呀,因此 服务端实际上也会发送一个 SYN 包给客户端,请求连接。此时 ACK 和 SYN 如果分开发,服务端觉得太麻烦了,于是就把这两个包合并在一起发,所以实际上只发一个 SYN+ACK 的包。这一点说重要也不重要,说不重要也重要,因为面试的时候经常会问到,**为什么不是四次握手呢?**答案就在这里,**因为一个包可以解决的事情没必要发两个包**。**这是第二次握手。** - -当客户端接收到服务端发送的 SYN+ACK 包时,知道服务端同意了自己的请求,并且也要求连接自己,有来就有往,客户端连忙回了个 ACK 包表示同意。**这就是第三次握手。** - - - -### 数据传输 - -在上面的 Python 代码中,服务端会向客户端发送了两次数据: `hello` 和 `world` - -那么这个数据是在哪里发送的呢? - -仔细看 wireshark 抓到的包,有两个 PSH 的包,意思就是有数据传输的意思。 - -打开这两个包分析一下 - -首先是第一个包 - -![](http://image.iswbm.com/image-20200602235431620.png) - -然后是第二个包 - -这里需要你理解的有两点 - -**1、为什么这里的 seq 为6呢?** - -因为第一次的 seq 为1,len=5,一共发了5个字节,所以第二次发送,要从6开始计数啦。 - -**2、为什么第一次 ack 为1,而第二次ack还是1呢?** - -因为客户端没有向服务端发送数据,所以 ack 将始终为1,直到客户端要向服务端发送数据。 - -![](http://image.iswbm.com/image-20200602235723214.png) - -### 四次挥手 - -四次挥手的过程可以参考下面这张图来帮助理解 - -![](http://image.iswbm.com/20200605192855.png) - -使用 wireshark 抓到的四次挥手的包如下所示 - -![wireshark 四次挥手](http://image.iswbm.com/image-20200603001339731.png) - -在服务端发送完两次数据后,调用一次了 close 方法,发送了一个 FIN 包请求关闭连接,**这是第一次挥手**,这个 FIN 包里的 seq 为11,是两次发送的数据长度+1,很容易理解,ack 始终为 1,上面讲过了也好理解。 - -当客户端收到了服务端发来的 FIN 包后,知道了服务端要关闭连接了,于是就回了一个 ACK 的应答包(**这是第二次挥手**),告诉服务端:恩,我知道了。但由于客户端这边还有一些事情要做(可能是还有数据要发送之类的,在 Python 代码里我通过 time.sleep 来模拟),所以要晚点才能关闭连接。这里的 ACK 包,seq 号 是取第一次挥手的 ack 号,而 ack 号是取 第一次挥手的 seq +1. - -等客户端事情也做完了(time.sleep 结束),也会主动发送一个 FIN 包(代码里是通过调用 close 方法实现)告诉服务端:我这边也结束了,可以关闭连接啦。这是第三次挥手。这个 FIN 包里的 seq 号还是取第一次挥手的 ack 号,而 ack 号也是取 第一次挥手的 seq +1,这和第二次挥手时是一样的。 - -既然是一样的,那为什么不一起发送呢? - -这个问题很好。当服务端数据都发送完了要关闭连接,而客户端自己也没什么事情 要做了也要关闭连接,确实是可以一起发送。这时候就四次挥手就变成了三次挥手,所以挥手并不总是四次的。 - -上面解析了三次挥手,还差最后一次。 - -最后一次挥手,就是服务端接收到客户端的 FIN 包后,知道了客户端要关闭连接了,就回了一个 ACK 应答包。此时的 seq 为第三次挥手的 ack,而 ack 为 第三次挥手的 seq +1。 - -至此,四次挥手全部完成。 - -## 5. 拷问灵魂的四个问题 - -### 问题1:为什么要三次握手? - -在建立连接前要经历三次握手,几乎是人尽皆知的事情。 - -但是为什么需要三次握手,这是一个值得思考的问题。 - -在大多数的文章里面,讲到三次握手都会用形象的比喻来跟你解释,比如和女朋友打电话的场景。 +@retry(wait=wait_fixed(2)) +def test_retry(): + print("等待重试...") + raise Exception -```shell -她:“你可以听到了吗?” -我:“可以呀,你呢,你可以听到我的吗?” -她:“我也可以听到了。” # 确认对应可以听到了再对话 -我:“你吃饭了吗?“ -她:“吃啦。“ +test_retry() ``` -从这个例子里,可以提炼出一点,就是三次握手就是在确保连接的双方都能发送且接收到对方的消息。 - -这个例子是好的,但是只讲这个例子又是不够的。 - -这会让读者对三次握手停留在表层,导致无法真正去学习 TCP 的精髓之处。 - -接下来,我会说说我对 TCP 的理解。 - -关于 为什么需要握手(注意:这里还没开始讨论为什么要三次握手),我认为应该有两个理由: - -1. 同步起始序列号,为后续数据传输做准备 -2. 保证双方都可能发送数据且能接收数据 - -关于第一点,其实两次握手就可以,客户端把自己的 seq 通过 SYN 包告诉服务端,而服务端把自己的 seq 通过 SYN+ACK 包告诉客户端。 - -而第二点呢,必须要三次握手才能保证,这个大家应该能够理解,不再赘述。 - -**除此之外,在网络上,你会经常看到还有第三个理由** +### 设置停止基本条件 -他们的论据是在 RFC 793 中可以找出下面这句话 +只重试7 次 -> The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion. - -翻译一下,就是三次握手的最主要原因是为了防止旧的重复连接初始化造成混乱。 - -怎么理解这句话呢?举个例子吧 - -由于网络环境是错综复杂的,当我们发送了一个SYN包 a 后,很有可能过了很久还没有到达目标机器,此时,客户端会重新发送一个 SYN 包 b重新请求连接。 - -![](http://image.iswbm.com/20200605200027.png) - -b 包比 a 包先到达了目标机器(即使a包是先发的),当目标机器收到了 b 包,就会回复给源机器一个回包,当后面 a 包也到达了目标机器后,对于目标机器来说,虽然a 和 b 是来源于同一机器 同一端口,但是它才不管是不是重复连接,因为对于目标机器来说,只要来请求连接我都欢迎,收一个我回一个,至于哪个才是最新的连接,哪个是重复的?它不管,它把这个职责交还给了客户端,毕竟哪个包才是最新的,它最清楚了。 - -那问题就来了,源机器是如何决定 a 包过期的呢? - -源机器 收到了来自目标机器 对 a 包的 ACK 回应后,通过自身的上下文信息,知道了这是一个历史连接(序列号过期或超时),那么客户端就会发送 `RST` 报文给服务端,表示中止这一次连接。 - -由此,我们可以看到,三次握手可以解决这个重复连接的问题。 - -这里请注意,我说的是 **可以解决**,而不是说 **因此我们需要三次握手**。 - -没有第三次握手会有多个重复连接导致浪费资源,是建立在三次请求才会建立连接的基础上才会出现的问题,这不是设计三次请求的原因。只是三次握手刚好也解决了这个问题,这个逻辑要搞清楚。 - -### 问题2:为什么不是握手两次? - -这个问题可以转换成『只握手两次就建立连接会出现什么样的问题?』 +```python +from tenacity import retry, stop_after_attempt -还是用给女朋友打电话这个例子,男朋友如果没有跟女朋友确认对方是否可以听到自己的话,就自己一直在说说说,最后只能尴尬收场。这就是我们所说的不可靠的连接,只是单向,而不是双向。 +@retry(stop=stop_after_attempt(7)) +def test_retry(): + print("等待重试...") + raise Exception -```shell -她:“你可以听到了吗?” -我:“可以呀” # 没有向对方确认是否可以听到自己就开始一直说说说 -我:“你吃饭了吗?“ -我:“人呢?“ -我:“喂?“ -我:“去哪啦?“ +test_retry() ``` -在实际应用上,其实只握手两次还会出现更严重的问题,那就是资源浪费。 +重试 10 秒后不再重试 -还是上面那个例子,a 包由于网络拥堵,迟迟没有发到目标机器 ,由于超时源机器会重新发送一个 SYN 包 b,如果只进行了两次握手,目标机器就建立了连接,那么当 b 包到达后,目标机器又会创建一个连接,而这个连接是无用的、多余的。 - -![](http://image.iswbm.com/20200605201138.png) - -这里仅仅假设只超时重发一次就成功了,如果超时重发了 10 次,甚至更多呢?本来TCP 传输只需要一个连接就行了,现在服务端却创建了 n 个 连接,对于服务器资源来说无疑是非常浪费的。 - -### 问题3:为什么不是握手四次? - -看到这里,你应该很清楚 三次握手的流程了。 - -那么握手四次是什么样的呢? +```python +from tenacity import retry, stop_after_delay -还是以给女朋友打电话的例子来说明 +@retry(stop=stop_after_delay(10)) +def test_retry(): + print("等待重试...") + raise Exception -```shell -她:“你可以听到了吗?” -我:“可以呀!” -我:“你呢,你可以听到我的吗?” -她:“我也可以听到了。” +test_retry() ``` -和三次握手相对比,其实就是把原来第二次握手的内容拆分成两次发送。 +或者上面两个条件满足一个就结束重试 -![](http://image.iswbm.com/20200605202450.png) - -所以为什么不握手四次? - -因为三次握手就可以完成的事,为什么要四次握手呢?没必要。 - -### 问题4:为什么不握手五次或更多? +```python +from tenacity import retry, stop_after_delay, stop_after_attempt -这个问题有点迷,你可能还不太清楚,还是以跟女朋友打电话为例 +@retry(stop=(stop_after_delay(10) | stop_after_attempt(7))) +def test_retry(): + print("等待重试...") + raise Exception -```shell -她:“你可以听到了吗?” -我:“可以呀,你呢,你可以听到我的吗?” -她:“恩,我也可以听到了。你呢,现在还可以听到吗?” -我:“可以呀,现在你那边还听到我的吗?” -她:“是的,可以,你呢,可以听到我现在说的吗” -我:“可以听到,那你呢?” -... -... +test_retry() ``` -在每一次跟确认可以听到对方的声音时,还生怕这个消息对方收不到这个消息,所以两个人就一直在确认,跟个zz一样。 - -所以你问我,为什么不握手五次或更多? - -因为三次是基本保障,再多一个,就是多余,容易死循环。 - - - -## 6. MTU 和 MSS 是什么? - -### MTU - -Maximum Transmission Unit,最大传输单元。 - -在TCP/IP协议族中,指的是**IP数据报**能经过一个**物理网络**的**最大报文长度**,其中包括了IP首部(从20个字节到60个字节不等)。 - -由此我们知道,MTU 为多大跟链路层的介质有关,我们接触最多的以太网的 MTU 设为1500字节。 - -其他的你可以参考 下面这张图(摘自维基百科) - -![](http://image.iswbm.com/image-20200604204657243.png) - -如果上层协议(如 TCP)交给IP协议的内容实在是太多,使得 IP 报文的大小超过了 MTU ,以以太网为例,如果 IP 报文大小超过了1500 Bytes ,那么**IP报文就必须要分片传输**,到达目的主机或目的路由器之后由其进行重组分片。 - -IP分片发生在IP层,不仅源端主机会进行分片,中间的路由器也有可能分片,因为不同的网络的MTU是不一样的,如果传输路径上的某个网络的MTU比源端网络的MTU要小,路由器就可能对IP数据报再次进行分片。而分片数据的重组只会发生在目的端的IP层。 - -### MSS - -Maximum Segment Size ,它表示的是 TCP 报文段中的数据字段的最大长度。 - -数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是整个TCP报文段的最大长度,而是“TCP报文段长度减去TCP首部长度”。 - -MSS 和 MTU 的关系是: - -MSS = MTU - IP首部大小 - TCP首部大小 - -![](http://image.iswbm.com/tcp_pdus.png) - - - -**那为什么要规定一个最大报文长度MSS呢?** - -这并不是考虑接受方的接收缓存可能存放不下TCP报文段中的数据。实际上,MSS与接收窗口值没有关系。我们知道,TCP报文段的数据部分,至少要加上40字节的首部(TCP首部20字节和IP首部20字节,这里还没有考虑首部中的可选部分)才能组装成一个IP数据报。 - -若选择较小的MSS长度,网络的利用率就降低。设想在极端情况下,当TCP报文段只含有1字节的数据时,在IP层传输的数据报的开销至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,对网络的利用率就不会超过1/41。到了数据链路层还要加上一些开销。 - -但反过来,若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片组成成原来的TCP报文段,当传输出错时还要进行重传。 - -IP层是没有超时重传机制的,如果IP层对一个数据包进行了分片,只要有一个分片丢失了,只能依赖于传输层进行重传,结果是所有的分片都要重传一遍,这个代价有点大。 - -因此,MSS应尽可能大些,只要在IP层传输时不需要分片就行。由于IP数据报所经历的路径是动态变化的,因此在这条路径上确定的不需要的分片的MSS,如果改走另一条路径就可能需要进行分片。**因此最佳的MSS是很难确定的**。 - -在连接过程中,双方都把自己能够支持的MSS写入这一字段,以后就按照这个数值传输数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节长。因此,所有在互联网上的主机都应该接受的报文段长度是536+20(固定首部长度)=556字节。 - - +### 设置何时进行重试 -## 7. 网络编程的常规步骤 - -上面为了方便抓包,我使用了 Python 写了一个服务器和客户端程序进行通信。 - -这里有必要说一下,面向 TCP 进行网络编程的常规步骤 - -![](http://image.iswbm.com/20200605204727.png) - -如果是服务端: - -1. 用函数socket() 创建一个socket; - -2. 用函数setsockopt() 设置socket属性; **可选步骤** - -3. 用函数bind() 绑定IP地址、端口等信息到socket上; - -4. 用函数listen() 开启监听; - -5. 用函数accept() 接收客户端上来的连接; - -6. 用函数send()和recv() 或者 read()和write() 收发数据; - -7. 关闭网络连接; - -8. 关闭监听; - -而如果是客户端: - -1. 用函数socket() 创建一个socket; - -2. 用函数setsockopt() 设置socket属性 ;**可选步骤** - -3. 用函数bind() 绑定IP地址、端口等信息到socket上; **可选步骤** -4. 用函数connect() 对方的IP地址和端口连接服务器 ; -5. 用函数send()和recv() 或者 read()和write() 收发数据; -6. 关闭网络连接; - -其中最主要、最关键的有三个函数: - -### connect() - -它是一个阻塞函数,通过 TCP 三次握手与服务器建立连接。 - -一般的情况下 客户端的connect函数 默认是阻塞行为 直到三次握手阶段成功为止。 - -### listen() - -不是一个阻塞函数: 它会将套接字 和 套接字对应队列的长度告诉Linux内核 - - 他是被动连接的 一直监听来自不同客户端的请求 listen函数只要 作用将socketfd 变成被动的连接监听socket 其中参数backlog作用 设置内核中队列的长度 。 - -### accpet() - -是一个阻塞函数,它会从处于 established 状态的队列中取出完成的连接。 - -当队列中没有完成连接时候就会阻塞,直到取出队列中已完成连接的用户连接为止。 - -那如果服务器没有及时调用 accept 函数取走完成连接的队列怎么办呢? - -服务器的连接队列满掉后,服务器不会对再对建立新连接的 SYN 进行应答,所以客户端的 connect 就会返回 ETIMEDOUT。 - -## 8. 注意事项 - -### ack 和 ACK 有区别吗? - -上面的分析三次握手和四次挥手时,有一个细节问题,可能不是那么重要,但是需要你搞清楚。 - -就是 ack 和 ACK 是否一致?答案是否定的 - -如果是 大写的 ACK ,表示的是标志位里的 flag,除了最初建立连接时的 SYN 包之外,后续的所有包此位都会被置为 1。 - -如果是 小写的 ack,表示的是希望确认号,表示的是希望接收到对方下一次数据的序列号, ack 一般都是上次接收成功的数据字节序号加1。 - -### TCP 包最多可传输多少数据? - -对于TCP协议来说,整个包的最大长度是由最大传输大小(MSS,Maxitum Segment Size)决定,MSS就是TCP数据包每次能够传输的最大数据分段。 - -为了达到最佳的传输效能 TCP协议在建立连接的时候通常要协商双方的MSS值。 - -通讯双方会根据双方提供的 MSS值的较小值来确定为这次连接的 MSS值。 - -在以太网中,MTU 为 1500 Bytes,减去IP数据包包头的大小20Bytes 和 TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 - - - -## 9. 异常情况分析 - -### 试图与一个不存在的端口建立连接(主机正常) - -这里的不存在的端口是指在服务器端没有程序监听在该端口。我们的客户端就调用connect,试图与其建立连接。这时会发生什么呢? - -这种情况下我们在客户端通常会收到如下异常内容: +在出现特定错误/异常(比如请求超时)的情况下,再进行重试 ```python -Traceback (most recent call last): - File "/Users/MING/Code/Python/tcp_client.py", line 8, in - s.connect((host, port)) -ConnectionRefusedError: [Errno 61] Connection refused -``` +from requests import exceptions +from tenacity import retry, retry_if_exception_type -试想一下,服务端本来就没有程序监听在这个接口,因此在服务端是无法完成连接的建立过程的。我们参考三次握手的流程可以知道当客户端的SYNC包到达服务端时,TCP协议没有找到监听的套接字,就会向客户端发送一个错误的报文,告诉客户端产生了错误。而该错误报文就是一个包含RST的报文。这种异常情况也很容易模拟,我们只需要写一个小程序,连接服务器上没有监听的端口即可。如下是通过wireshark捕获的数据包,可以看到红色部分的RST报文。 +@retry(retry=retry_if_exception_type(exceptions.Timeout)) +def test_retry(): + print("等待重试...") + raise exceptions.Timeout -![](http://image.iswbm.com/image-20200604223625787.png) +test_retry() +``` +在满足自定义条件时,再进行重试。 +如下示例,当 `test_retry` 函数返回值为 False 时,再进行重试 -### 试图与一个某端口建立连接但该主机已经宕机(主机宕机) +```python +from tenacity import retry, stop_after_attempt, retry_if_result -这也是一种比较常见的情况,当某台服务器主机宕机了,而客户端并不知道,因此会重复发送SYNC数据包. +def is_false(value): + return value is False -如下图所示,可以看到客户端每隔一段时间就会向服务端发送一个SYNC数据包。这里面具体的时间是跟TCP协议相关的,具体时间不同的操作系统实现可能稍有不同。 +@retry(stop=stop_after_attempt(3), + retry=retry_if_result(is_false)) +def test_retry(): + return False -![](http://image.iswbm.com/image-20200604224127512.png) +test_retry() +``` -### 建立连接时,服务器应用被阻塞(或者僵死) +### 重试后错误重新抛出 -还有一种异常情况是,客户端建立连接的过程中服务端应用处于僵死状态,这种情况在实际中也会经常出现(我们假设仅仅应用程序僵死,而内核没有僵死)。 +当出现异常后,tenacity 会进行重试,若重试后还是失败,默认情况下,往上抛出的异常会变成 RetryError,而不是最根本的原因。 -对于TCP的服务端来说,当它收到SYN数据包时,就会创建一个套接字的数据结构并给客户端回复ACK,再次收到客户端的ACK时会将套接字数据结构的状态转换为ESTABLISHED,并将其加入就绪队列。 +因此可以加一个参数(`reraise=True`),使得当重试失败后,往外抛出的异常还是原来的那个。 -当上面的套接字处于就绪队列时,accept函数才被唤醒了,可以从套接字中读取数据。 +```python +from tenacity import retry, stop_after_attempt -在 accept 返回之前,客户端也是可以发送数据的,因为数据的发送与接收都是在内核态进行的。客户端发送数据后,服务端的网卡会先接收,然后通过中断通知IP层,再上传到TCP层。TCP层根据目的端口和地址将数据存入关联的缓冲区。 +@retry(stop=stop_after_attempt(7), reraise=True) +def test_retry(): + print("等待重试...") + raise Exception -到此,可以得出几点结论。 +test_retry() +``` -1. 在 accept 返回之前,三次握手已经完成。 -2. TCP的客户端是否可以发送数据与服务端程序是否工作没有关系。 -但是如果内核也处于僵死状态,那情况可就完全不一样了。 -此时由于机器完全卡死,TCP服务端无法接受任何消息,自然也无法给客户端发送任何应答报文,也不会有后续发送数据的环节了。 +### 设置回调函数 +当最后一次重试失败后,可以执行一个回调函数 +```python +from tenacity import * -## 10. 参考文章 +def return_last_value(retry_state): + print("执行回调函数") + return retry_state.outcome.result() # 表示返回原函数的返回值 -[TCP报文段的首部格式](https://blog.csdn.net/qq_32998153/article/details/79680704) +def is_false(value): + return value is False -[TCP 、UDP、IP包的最大长度](https://www.cnblogs.com/jiangzhaowei/p/9273854.html) +@retry(stop=stop_after_attempt(3), + retry_error_callback=return_last_value, + retry=retry_if_result(is_false)) +def test_retry(): + print("等待重试中...") + return False -[理解了这些异常现象才敢说真正懂了TCP协议](https://network.51cto.com/art/201905/596543.htm) +print(test_retry()) +``` -[近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题](https://mp.weixin.qq.com/s/tH8RFmjrveOmgLvk9hmrkw) +输出如下 +```shell +等待重试中... +等待重试中... +等待重试中... +执行回调函数 +False +``` ---- ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst index e63f91b..234bde4 100644 --- a/source/c10/c10_03.rst +++ b/source/c10/c10_03.rst @@ -1,698 +1,187 @@ -10.3 网络知识扫盲:详解TCP的三次握手与四次挥手 -============================================== +1.47 少有人知的 Python “重试机制” +================================= -|image0| - -1. TCP 协议是什么? -------------------- +为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 -TCP 是 Transmission Control Protocol -的缩写,意思是传输控制协议。一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC -793 定义。 +这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 -从这个定义里,有很多初学就首先懵了。 +这里要给大家介绍的是一个第三方库 - ``Tenacity`` +,它实现了几乎我们可以使用到的所有重试场景,比如: -什么是面向连接? +1. 在什么情况下才进行重试? +2. 重试几次呢? +3. 重试多久后结束? +4. 每次重试的间隔多长呢? +5. 重试失败后的回调? -什么是可靠的通信协议? +在使用它之前 ,先要安装它 -什么是面向字节流的? +.. code:: shell -为了让你对 TCP 有个初步的了解,我打算先从这个定义入手。 + $ pip install tenacity -什么是面向连接? +**最基本的重试** ~~~~~~~~~~~~~~~~ -面向连接,是相对于另一个传输层协议 UDP 而言的(后面会单独介绍)。 - -TCP 是面向连接的,所以在开始传输数据前要先经历三次握手建立连接。 - -而 UDP 即刻就可以传输数据,并不需要先三次握手来建立连接。 - -一个更可靠,而一个更开放。 - -就好比,你去医院看病,如果是专家号,一般要提前预约,对只要预约(三次握手建立了连接)上了,你去了就不会看不上病。这是 -TCP 。 - -而如果你没有预约,就直接跑过去,那不好意思,你只能看普通门诊,而普通门诊等的人很多,你就不一定能看得上病了。这是 -UDP。 - -既然是连接,必然是一对一的,就像绳子的两端。所以 TCP 是一对一发送消息。 - -而 UDP 协议不需要连接,可以一对一,也可以一对多,也可以多对多发送消息。 - -什么是可靠的通信协议? -~~~~~~~~~~~~~~~~~~~~~~ - -可不可靠,也是相对于 UDP 而言的。 - -TCP -自身有三次握手和超时重传等机制,所以无论网络如何变化,主要不是主机宕机等原因都可以保证一个报文可以到达目标主机。 - -与之对比, UDP -就比较不负责任了,不管你收不收得到,反正我就无脑发,网络拥堵我也发,它的职责是发出去。 - -什么是面向字节流的? -~~~~~~~~~~~~~~~~~~~~ - -与面向字节流相对的是,UDP 的面向报文。 - -面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。若报文太长,则IP层需要分片,降低效率。若太短,会使IP太小。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这也就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。 - -面向字节流的话,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。 - -2. 完整解读 TCP 报文格式 ------------------------- - -搞懂一个通信协议,了解它的报文格式是必经之路。 - -TCP 的报文段结构,可以从下面这张图中非常清晰的看到。 - -.. figure:: http://image.iswbm.com/20200606095627.png - :alt: TCP 报文首部 - - TCP 报文首部 - -接下来,我会一个一个讲解这些字段的内容。 - -**源端口** 和 **目标端口**\ :各占 2 个字节。2 个字节,也就是 16个 -bit,这应该也能说明为什么计算机端口的范围是 1-65535 (0 -不使用,2^16=65536,最大位65536不使用)了吧?有了源端口和目标端口,加上 -IP 首部里的源IP和目标IP,就可以唯一确定一个连接。 - -**序列号**\ :共占用 4个字节。说明序列号的范围是 [0, 2^32-1],也就是 [0, -4294967296]。当序号增加到 4294967296 -后,下一个序号将回到0重新开始。在建立连接时由计算机生成的随机数作为其初始值(ISN,即Initial -Sequence Number,初始序列号),通过 SYN -包传给接收端主机,每发送一次数据,就\ **累加**\ 一次该「数据字节数」的大小(其中要注意的是 -SYN 和 FIN 包的 seq 也要消耗一个序号)。\ **用来解决网络包乱序问题。** - -**确认号**\ :共占用 4个字节。说明确认号的范围是 [0, 2^32-1],也就是 [0, -4294967296]。它表示\ **期望**\ 收到对方下一次数据的序列号(所以 ack -一般都是上次接收成功的数据字节序号加1),发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。\ **用来解决不丢包的问题**\ 。TCP在接收到数据后 -200ms 才会发送ACK包,这种设定是为了等待是否有数据可以一起发送的。 - -**数据偏移(图中为报文首部)**\ :共占 4 -个bit,它表示的是TCP报文的数据起始处距离TCP报文起始处的距离有多远。实际生活中我们说距离多远,我们的单位通常是米,而这里距离有多远,单位是 -4 个字节(也就是 32bit)。由于 4 个bit,能表示的最大整数是 15,也就说明 -TCP 报文里数据开始的位置距离报文起点是 60 个字节(4*15)。这意味着 TCP -的首部(除数据外的都叫首部)长度是 20-60 个字节。 - -**窗口**\ :共占 16 个bit,因此最大的窗口大小为 2^16-1 = 65535 = -64k。这是早期的设计,对于现在的网络应用,可能会不太够,因此可以在选项里加一个 -**窗口扩大选项**\ ,来传输更多的数据。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。 - -**保留**\ :占 6个bit,保留为今后使用,目前应置为0。 - -**紧急指针**\ :占16个bit。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) -。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,\ **即使窗口为0时也可以发送紧急数据**\ 。 - -**标志位:** - -- **SYN**\ (SYNchronization): - 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。 -- **ACK**\ (ACKnowledgment):仅当ACK = 1时确认号字段才有效,当ACK = - 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1,如果你可以看下我后面 - wireshark 抓的包里除了 最初建立连接的 SYN 包之外,其他的包也都有 ACK - 标志。 -- **RST**\ (ReSet):当 RST=1 时,表示 TCP - 连接中出现异常(如主机崩溃或其他原因)必须强制断开连接,然后再重新建立连接进行传输。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。 -- **FIN**\ (Finish):当FIN=1 - 时,表示今后不会再有数据发送,希望断开连接。 -- **PSH**\ (Push) - 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。 -- **URG**\ (Urgent):当URG=1时,表明开户了urgent - mode,紧急指针就开始生效了。 - -**选项**\ :长度可变,最长可达40个字节。当没有使用“选项”时,TCP的首部长度是20字节。 - -- MSS 选项:TCP报文段中的数据字段的最大长度,后面有详解。 -- 窗口扩大选项:占用三个字节,使得接收端可接收更多的数据,由 2^16-1 - 扩充到 - 2^(16+14)-1,其中这个14是窗口移位记数器的最大值。详情请参见:TCP/IP详解 - 卷1 协议 P262 -- 时间戳选项:共占 10 - 个字节,其中最主要的字段是时间戳字段(4字节)和时间戳回送回答字段(4字节)。 - -3. 如何模拟 TCP 连接? ----------------------- - -只搞懂报文格式,没有实战的话,就永远只停留在字面上,无法深刻地理解它。 - -所以接下来我会使用 wireshark -进行对三次握手、数据传输、四次挥手进行一次抓包并分析这个过程。 - -但是在开始之前 ,首先要学会模拟建立一个 tcp -连接,好能让我们轻松使用过滤器来显示结果。 - -为此我使用 Python 写了两个小脚本 - -**1、服务端** - -监听 13200 端口,如果有客户端连接就发送 hello 字符串 +无条件重试,重试之间无间隔 .. code:: python - # tcp_server.py - - import socket # 导入 socket 模块 - import time - - s = socket.socket() # 创建 socket 对象 - host = socket.gethostname() # 获取本地主机名 - port = 13200 # 设置端口 - s.bind((host, port)) # 绑定端口 + from tenacity import retry - s.listen(5) # 等待客户端连接 - while True: - c, addr = s.accept() # 建立客户端连接 - c.send('hello'.encode("utf-8")) - c.send('world'.encode("utf-8")) - time.sleep(1) - c.close() # 关闭连接 + @retry + def test_retry(): + print("等待重试,重试无间隔执行...") + raise Exception -运行后,可以使用 lsof 命令查看 13200 端口是否处于监听中 + test_retry() -|image1| - -**2、客户端** - -连接 13200 端口,并接收并打印服务端发送的内容 +无条件重试,但是在重试之前要等待 2 秒 .. code:: python - # tcp_client.py - - import socket # 导入 socket 模块 - import time - - s = socket.socket() # 创建 socket 对象 - host = socket.gethostname() # 获取本地主机名 - port = 13200 # 设置端口号 - - s.connect((host, port)) - print(s.recv(1024)) - time.sleep(2) - s.close() - -4. Wireshark 抓包实战分析 -------------------------- - -一切准备就绪后,打开我们的 wireshark ,并设置捕获过滤器 port=13200 - -|image2| - -然后开启抓包,最后执行上面的 客户端代码\ ``tcp_client.py``\ ,就可以在 -wireshark 上看到如下内容。 - -|image3| - -三次握手 -~~~~~~~~ - -三次握手的过程可以参考下面这张图来帮助理解 - -|image4| - -使用 wireshark 抓到的三次握手的包如下所示 - -.. figure:: http://image.iswbm.com/image-20200603003018160.png - :alt: wireshare 三次握手 - - wireshare 三次握手 - -客户端要连接上服务端,首先要发送一个 SYN 包表示请求连接。这个SYN 包的 -seq 为0。这是第一次握手。 - -当服务端接收这个 SYN 包时,知道了有人要连接自己,就发了一个 ACK 包说: -你要连接这件事,我已经知道啦。但是连接是双方的事情,我也要连接客户端呀,因此 -服务端实际上也会发送一个 SYN 包给客户端,请求连接。此时 ACK 和 SYN -如果分开发,服务端觉得太麻烦了,于是就把这两个包合并在一起发,所以实际上只发一个 -SYN+ACK -的包。这一点说重要也不重要,说不重要也重要,因为面试的时候经常会问到,\ **为什么不是四次握手呢?**\ 答案就在这里,\ **因为一个包可以解决的事情没必要发两个包**\ 。\ **这是第二次握手。** - -当客户端接收到服务端发送的 SYN+ACK -包时,知道服务端同意了自己的请求,并且也要求连接自己,有来就有往,客户端连忙回了个 -ACK 包表示同意。\ **这就是第三次握手。** - -数据传输 -~~~~~~~~ - -在上面的 Python 代码中,服务端会向客户端发送了两次数据: ``hello`` 和 -``world`` - -那么这个数据是在哪里发送的呢? - -仔细看 wireshark 抓到的包,有两个 PSH 的包,意思就是有数据传输的意思。 - -打开这两个包分析一下 - -首先是第一个包 - -|image5| - -然后是第二个包 - -这里需要你理解的有两点 - -**1、为什么这里的 seq 为6呢?** - -因为第一次的 seq -为1,len=5,一共发了5个字节,所以第二次发送,要从6开始计数啦。 - -**2、为什么第一次 ack 为1,而第二次ack还是1呢?** - -因为客户端没有向服务端发送数据,所以 ack -将始终为1,直到客户端要向服务端发送数据。 - -|image6| - -四次挥手 -~~~~~~~~ - -四次挥手的过程可以参考下面这张图来帮助理解 - -|image7| - -使用 wireshark 抓到的四次挥手的包如下所示 - -.. figure:: http://image.iswbm.com/image-20200603001339731.png - :alt: wireshark 四次挥手 - - wireshark 四次挥手 - -在服务端发送完两次数据后,调用一次了 close 方法,发送了一个 FIN -包请求关闭连接,\ **这是第一次挥手**\ ,这个 FIN 包里的 seq -为11,是两次发送的数据长度+1,很容易理解,ack 始终为 -1,上面讲过了也好理解。 - -当客户端收到了服务端发来的 FIN -包后,知道了服务端要关闭连接了,于是就回了一个 ACK -的应答包(\ **这是第二次挥手**\ ),告诉服务端:恩,我知道了。但由于客户端这边还有一些事情要做(可能是还有数据要发送之类的,在 -Python 代码里我通过 time.sleep 来模拟),所以要晚点才能关闭连接。这里的 -ACK 包,seq 号 是取第一次挥手的 ack 号,而 ack 号是取 第一次挥手的 seq -+1. - -等客户端事情也做完了(time.sleep 结束),也会主动发送一个 FIN -包(代码里是通过调用 close -方法实现)告诉服务端:我这边也结束了,可以关闭连接啦。这是第三次挥手。这个 -FIN 包里的 seq 号还是取第一次挥手的 ack 号,而 ack 号也是取 第一次挥手的 -seq +1,这和第二次挥手时是一样的。 - -既然是一样的,那为什么不一起发送呢? - -这个问题很好。当服务端数据都发送完了要关闭连接,而客户端自己也没什么事情 -要做了也要关闭连接,确实是可以一起发送。这时候就四次挥手就变成了三次挥手,所以挥手并不总是四次的。 - -上面解析了三次挥手,还差最后一次。 - -最后一次挥手,就是服务端接收到客户端的 FIN -包后,知道了客户端要关闭连接了,就回了一个 ACK 应答包。此时的 seq -为第三次挥手的 ack,而 ack 为 第三次挥手的 seq +1。 - -至此,四次挥手全部完成。 - -5. 拷问灵魂的四个问题 ---------------------- - -问题1:为什么要三次握手? -~~~~~~~~~~~~~~~~~~~~~~~~~ - -在建立连接前要经历三次握手,几乎是人尽皆知的事情。 - -但是为什么需要三次握手,这是一个值得思考的问题。 - -在大多数的文章里面,讲到三次握手都会用形象的比喻来跟你解释,比如和女朋友打电话的场景。 - -.. code:: shell - - 她:“你可以听到了吗?” - 我:“可以呀,你呢,你可以听到我的吗?” - 她:“我也可以听到了。” # 确认对应可以听到了再对话 - 我:“你吃饭了吗?“ - 她:“吃啦。“ - -从这个例子里,可以提炼出一点,就是三次握手就是在确保连接的双方都能发送且接收到对方的消息。 + from tenacity import retry, wait_fixed -这个例子是好的,但是只讲这个例子又是不够的。 + @retry(wait=wait_fixed(2)) + def test_retry(): + print("等待重试...") + raise Exception -这会让读者对三次握手停留在表层,导致无法真正去学习 TCP 的精髓之处。 + test_retry() -接下来,我会说说我对 TCP 的理解。 - -关于 -为什么需要握手(注意:这里还没开始讨论为什么要三次握手),我认为应该有两个理由: - -1. 同步起始序列号,为后续数据传输做准备 -2. 保证双方都可能发送数据且能接收数据 - -关于第一点,其实两次握手就可以,客户端把自己的 seq 通过 SYN -包告诉服务端,而服务端把自己的 seq 通过 SYN+ACK 包告诉客户端。 - -而第二点呢,必须要三次握手才能保证,这个大家应该能够理解,不再赘述。 - -**除此之外,在网络上,你会经常看到还有第三个理由** - -他们的论据是在 RFC 793 中可以找出下面这句话 - - The principle reason for the three-way handshake is to prevent old - duplicate connection initiations from causing confusion. - -翻译一下,就是三次握手的最主要原因是为了防止旧的重复连接初始化造成混乱。 - -怎么理解这句话呢?举个例子吧 - -由于网络环境是错综复杂的,当我们发送了一个SYN包 a -后,很有可能过了很久还没有到达目标机器,此时,客户端会重新发送一个 SYN -包 b重新请求连接。 - -|image8| - -b 包比 a 包先到达了目标机器(即使a包是先发的),当目标机器收到了 b -包,就会回复给源机器一个回包,当后面 a -包也到达了目标机器后,对于目标机器来说,虽然a 和 b 是来源于同一机器 -同一端口,但是它才不管是不是重复连接,因为对于目标机器来说,只要来请求连接我都欢迎,收一个我回一个,至于哪个才是最新的连接,哪个是重复的?它不管,它把这个职责交还给了客户端,毕竟哪个包才是最新的,它最清楚了。 - -那问题就来了,源机器是如何决定 a 包过期的呢? - -源机器 收到了来自目标机器 对 a 包的 ACK -回应后,通过自身的上下文信息,知道了这是一个历史连接(序列号过期或超时),那么客户端就会发送 -``RST`` 报文给服务端,表示中止这一次连接。 - -由此,我们可以看到,三次握手可以解决这个重复连接的问题。 - -这里请注意,我说的是 **可以解决**\ ,而不是说 -**因此我们需要三次握手**\ 。 - -没有第三次握手会有多个重复连接导致浪费资源,是建立在三次请求才会建立连接的基础上才会出现的问题,这不是设计三次请求的原因。只是三次握手刚好也解决了这个问题,这个逻辑要搞清楚。 - -问题2:为什么不是握手两次? -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -这个问题可以转换成『只握手两次就建立连接会出现什么样的问题?』 - -还是用给女朋友打电话这个例子,男朋友如果没有跟女朋友确认对方是否可以听到自己的话,就自己一直在说说说,最后只能尴尬收场。这就是我们所说的不可靠的连接,只是单向,而不是双向。 - -.. code:: shell - - 她:“你可以听到了吗?” - 我:“可以呀” # 没有向对方确认是否可以听到自己就开始一直说说说 - 我:“你吃饭了吗?“ - 我:“人呢?“ - 我:“喂?“ - 我:“去哪啦?“ - -在实际应用上,其实只握手两次还会出现更严重的问题,那就是资源浪费。 - -还是上面那个例子,a 包由于网络拥堵,迟迟没有发到目标机器 -,由于超时源机器会重新发送一个 SYN 包 -b,如果只进行了两次握手,目标机器就建立了连接,那么当 b -包到达后,目标机器又会创建一个连接,而这个连接是无用的、多余的。 - -|image9| - -这里仅仅假设只超时重发一次就成功了,如果超时重发了 10 -次,甚至更多呢?本来TCP 传输只需要一个连接就行了,现在服务端却创建了 n -个 连接,对于服务器资源来说无疑是非常浪费的。 - -问题3:为什么不是握手四次? -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -看到这里,你应该很清楚 三次握手的流程了。 - -那么握手四次是什么样的呢? - -还是以给女朋友打电话的例子来说明 - -.. code:: shell - - 她:“你可以听到了吗?” - 我:“可以呀!” - 我:“你呢,你可以听到我的吗?” - 她:“我也可以听到了。” - -和三次握手相对比,其实就是把原来第二次握手的内容拆分成两次发送。 - -|image10| - -所以为什么不握手四次? - -因为三次握手就可以完成的事,为什么要四次握手呢?没必要。 - -问题4:为什么不握手五次或更多? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -这个问题有点迷,你可能还不太清楚,还是以跟女朋友打电话为例 - -.. code:: shell - - 她:“你可以听到了吗?” - 我:“可以呀,你呢,你可以听到我的吗?” - 她:“恩,我也可以听到了。你呢,现在还可以听到吗?” - 我:“可以呀,现在你那边还听到我的吗?” - 她:“是的,可以,你呢,可以听到我现在说的吗” - 我:“可以听到,那你呢?” - ... - ... - -在每一次跟确认可以听到对方的声音时,还生怕这个消息对方收不到这个消息,所以两个人就一直在确认,跟个zz一样。 - -所以你问我,为什么不握手五次或更多? - -因为三次是基本保障,再多一个,就是多余,容易死循环。 - -6. MTU 和 MSS 是什么? ----------------------- - -MTU -~~~ - -Maximum Transmission Unit,最大传输单元。 - -在TCP/IP协议族中,指的是\ **IP数据报**\ 能经过一个\ **物理网络**\ 的\ **最大报文长度**\ ,其中包括了IP首部(从20个字节到60个字节不等)。 - -由此我们知道,MTU 为多大跟链路层的介质有关,我们接触最多的以太网的 MTU -设为1500字节。 - -其他的你可以参考 下面这张图(摘自维基百科) - -|image11| - -如果上层协议(如 TCP)交给IP协议的内容实在是太多,使得 IP -报文的大小超过了 MTU ,以以太网为例,如果 IP 报文大小超过了1500 Bytes -,那么\ **IP报文就必须要分片传输**\ ,到达目的主机或目的路由器之后由其进行重组分片。 - -IP分片发生在IP层,不仅源端主机会进行分片,中间的路由器也有可能分片,因为不同的网络的MTU是不一样的,如果传输路径上的某个网络的MTU比源端网络的MTU要小,路由器就可能对IP数据报再次进行分片。而分片数据的重组只会发生在目的端的IP层。 - -MSS -~~~ - -Maximum Segment Size ,它表示的是 TCP 报文段中的数据字段的最大长度。 - -数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是整个TCP报文段的最大长度,而是“TCP报文段长度减去TCP首部长度”。 - -MSS 和 MTU 的关系是: - -MSS = MTU - IP首部大小 - TCP首部大小 - -|image12| - -**那为什么要规定一个最大报文长度MSS呢?** - -这并不是考虑接受方的接收缓存可能存放不下TCP报文段中的数据。实际上,MSS与接收窗口值没有关系。我们知道,TCP报文段的数据部分,至少要加上40字节的首部(TCP首部20字节和IP首部20字节,这里还没有考虑首部中的可选部分)才能组装成一个IP数据报。 - -若选择较小的MSS长度,网络的利用率就降低。设想在极端情况下,当TCP报文段只含有1字节的数据时,在IP层传输的数据报的开销至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,对网络的利用率就不会超过1/41。到了数据链路层还要加上一些开销。 - -但反过来,若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片组成成原来的TCP报文段,当传输出错时还要进行重传。 - -IP层是没有超时重传机制的,如果IP层对一个数据包进行了分片,只要有一个分片丢失了,只能依赖于传输层进行重传,结果是所有的分片都要重传一遍,这个代价有点大。 - -因此,MSS应尽可能大些,只要在IP层传输时不需要分片就行。由于IP数据报所经历的路径是动态变化的,因此在这条路径上确定的不需要的分片的MSS,如果改走另一条路径就可能需要进行分片。\ **因此最佳的MSS是很难确定的**\ 。 - -在连接过程中,双方都把自己能够支持的MSS写入这一字段,以后就按照这个数值传输数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节长。因此,所有在互联网上的主机都应该接受的报文段长度是536+20(固定首部长度)=556字节。 - -7. 网络编程的常规步骤 ---------------------- - -上面为了方便抓包,我使用了 Python 写了一个服务器和客户端程序进行通信。 - -这里有必要说一下,面向 TCP 进行网络编程的常规步骤 - -|image13| - -如果是服务端: - -1. 用函数socket() 创建一个socket; - -2. 用函数setsockopt() 设置socket属性; **可选步骤** - -3. 用函数bind() 绑定IP地址、端口等信息到socket上; - -4. 用函数listen() 开启监听; - -5. 用函数accept() 接收客户端上来的连接; - -6. 用函数send()和recv() 或者 read()和write() 收发数据; - -7. 关闭网络连接; - -8. 关闭监听; - -而如果是客户端: - -1. 用函数socket() 创建一个socket; - -2. 用函数setsockopt() 设置socket属性 ;\ **可选步骤** - -3. 用函数bind() 绑定IP地址、端口等信息到socket上; **可选步骤** -4. 用函数connect() 对方的IP地址和端口连接服务器 ; -5. 用函数send()和recv() 或者 read()和write() 收发数据; -6. 关闭网络连接; - -其中最主要、最关键的有三个函数: - -connect() -~~~~~~~~~ - -它是一个阻塞函数,通过 TCP 三次握手与服务器建立连接。 - -一般的情况下 客户端的connect函数 默认是阻塞行为 -直到三次握手阶段成功为止。 +设置停止基本条件 +~~~~~~~~~~~~~~~~ -listen() -~~~~~~~~ +只重试7 次 -不是一个阻塞函数: 它会将套接字 和 套接字对应队列的长度告诉Linux内核 +.. code:: python -他是被动连接的 一直监听来自不同客户端的请求 listen函数只要 -作用将socketfd 变成被动的连接监听socket 其中参数backlog作用 -设置内核中队列的长度 。 + from tenacity import retry, stop_after_attempt -accpet() -~~~~~~~~ + @retry(stop=stop_after_attempt(7)) + def test_retry(): + print("等待重试...") + raise Exception -是一个阻塞函数,它会从处于 established 状态的队列中取出完成的连接。 + test_retry() -当队列中没有完成连接时候就会阻塞,直到取出队列中已完成连接的用户连接为止。 +重试 10 秒后不再重试 -那如果服务器没有及时调用 accept 函数取走完成连接的队列怎么办呢? +.. code:: python -服务器的连接队列满掉后,服务器不会对再对建立新连接的 SYN -进行应答,所以客户端的 connect 就会返回 ETIMEDOUT。 + from tenacity import retry, stop_after_delay -8. 注意事项 ------------ + @retry(stop=stop_after_delay(10)) + def test_retry(): + print("等待重试...") + raise Exception -ack 和 ACK 有区别吗? -~~~~~~~~~~~~~~~~~~~~~ + test_retry() -上面的分析三次握手和四次挥手时,有一个细节问题,可能不是那么重要,但是需要你搞清楚。 +或者上面两个条件满足一个就结束重试 -就是 ack 和 ACK 是否一致?答案是否定的 +.. code:: python -如果是 大写的 ACK ,表示的是标志位里的 flag,除了最初建立连接时的 SYN -包之外,后续的所有包此位都会被置为 1。 + from tenacity import retry, stop_after_delay, stop_after_attempt -如果是 小写的 -ack,表示的是希望确认号,表示的是希望接收到对方下一次数据的序列号, ack -一般都是上次接收成功的数据字节序号加1。 + @retry(stop=(stop_after_delay(10) | stop_after_attempt(7))) + def test_retry(): + print("等待重试...") + raise Exception -TCP 包最多可传输多少数据? -~~~~~~~~~~~~~~~~~~~~~~~~~~ + test_retry() -对于TCP协议来说,整个包的最大长度是由最大传输大小(MSS,Maxitum Segment -Size)决定,MSS就是TCP数据包每次能够传输的最大数据分段。 +设置何时进行重试 +~~~~~~~~~~~~~~~~ -为了达到最佳的传输效能 TCP协议在建立连接的时候通常要协商双方的MSS值。 +在出现特定错误/异常(比如请求超时)的情况下,再进行重试 -通讯双方会根据双方提供的 MSS值的较小值来确定为这次连接的 MSS值。 +.. code:: python -在以太网中,MTU 为 1500 Bytes,减去IP数据包包头的大小20Bytes 和 -TCP数据段的包头20Bytes,TCP 层最大的 MSS 为 1460。 + from requests import exceptions + from tenacity import retry, retry_if_exception_type -9. 异常情况分析 ---------------- + @retry(retry=retry_if_exception_type(exceptions.Timeout)) + def test_retry(): + print("等待重试...") + raise exceptions.Timeout -试图与一个不存在的端口建立连接(主机正常) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + test_retry() -这里的不存在的端口是指在服务器端没有程序监听在该端口。我们的客户端就调用connect,试图与其建立连接。这时会发生什么呢? +在满足自定义条件时,再进行重试。 -这种情况下我们在客户端通常会收到如下异常内容: +如下示例,当 ``test_retry`` 函数返回值为 False 时,再进行重试 .. code:: python - Traceback (most recent call last): - File "/Users/MING/Code/Python/tcp_client.py", line 8, in - s.connect((host, port)) - ConnectionRefusedError: [Errno 61] Connection refused + from tenacity import retry, stop_after_attempt, retry_if_result -试想一下,服务端本来就没有程序监听在这个接口,因此在服务端是无法完成连接的建立过程的。我们参考三次握手的流程可以知道当客户端的SYNC包到达服务端时,TCP协议没有找到监听的套接字,就会向客户端发送一个错误的报文,告诉客户端产生了错误。而该错误报文就是一个包含RST的报文。这种异常情况也很容易模拟,我们只需要写一个小程序,连接服务器上没有监听的端口即可。如下是通过wireshark捕获的数据包,可以看到红色部分的RST报文。 + def is_false(value): + return value is False -|image14| + @retry(stop=stop_after_attempt(3), + retry=retry_if_result(is_false)) + def test_retry(): + return False -试图与一个某端口建立连接但该主机已经宕机(主机宕机) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + test_retry() -这也是一种比较常见的情况,当某台服务器主机宕机了,而客户端并不知道,因此会重复发送SYNC数据包. +重试后错误重新抛出 +~~~~~~~~~~~~~~~~~~ -如下图所示,可以看到客户端每隔一段时间就会向服务端发送一个SYNC数据包。这里面具体的时间是跟TCP协议相关的,具体时间不同的操作系统实现可能稍有不同。 +当出现异常后,tenacity +会进行重试,若重试后还是失败,默认情况下,往上抛出的异常会变成 +RetryError,而不是最根本的原因。 -|image15| +因此可以加一个参数(\ ``reraise=True``\ ),使得当重试失败后,往外抛出的异常还是原来的那个。 -建立连接时,服务器应用被阻塞(或者僵死) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. code:: python -还有一种异常情况是,客户端建立连接的过程中服务端应用处于僵死状态,这种情况在实际中也会经常出现(我们假设仅仅应用程序僵死,而内核没有僵死)。 + from tenacity import retry, stop_after_attempt -对于TCP的服务端来说,当它收到SYN数据包时,就会创建一个套接字的数据结构并给客户端回复ACK,再次收到客户端的ACK时会将套接字数据结构的状态转换为ESTABLISHED,并将其加入就绪队列。 + @retry(stop=stop_after_attempt(7), reraise=True) + def test_retry(): + print("等待重试...") + raise Exception -当上面的套接字处于就绪队列时,accept函数才被唤醒了,可以从套接字中读取数据。 + test_retry() -在 accept -返回之前,客户端也是可以发送数据的,因为数据的发送与接收都是在内核态进行的。客户端发送数据后,服务端的网卡会先接收,然后通过中断通知IP层,再上传到TCP层。TCP层根据目的端口和地址将数据存入关联的缓冲区。 +设置回调函数 +~~~~~~~~~~~~ -到此,可以得出几点结论。 +当最后一次重试失败后,可以执行一个回调函数 -1. 在 accept 返回之前,三次握手已经完成。 -2. TCP的客户端是否可以发送数据与服务端程序是否工作没有关系。 +.. code:: python -但是如果内核也处于僵死状态,那情况可就完全不一样了。 + from tenacity import * -此时由于机器完全卡死,TCP服务端无法接受任何消息,自然也无法给客户端发送任何应答报文,也不会有后续发送数据的环节了。 + def return_last_value(retry_state): + print("执行回调函数") + return retry_state.outcome.result() # 表示返回原函数的返回值 -10. 参考文章 ------------- + def is_false(value): + return value is False -`TCP报文段的首部格式 `__ + @retry(stop=stop_after_attempt(3), + retry_error_callback=return_last_value, + retry=retry_if_result(is_false)) + def test_retry(): + print("等待重试中...") + return False -`TCP -、UDP、IP包的最大长度 `__ + print(test_retry()) -`理解了这些异常现象才敢说真正懂了TCP协议 `__ +输出如下 -`近 40 张图解被问千百遍的 TCP -三次握手和四次挥手面试题 `__ +.. code:: shell --------------- + 等待重试中... + 等待重试中... + 等待重试中... + 执行回调函数 + False -|image16| +|image0| -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/image-20200601221524846.png -.. |image2| image:: http://image.iswbm.com/image-20200601222110435.png -.. |image3| image:: http://image.iswbm.com/image-20200602234904143.png -.. |image4| image:: http://image.iswbm.com/20200605130951.png -.. |image5| image:: http://image.iswbm.com/image-20200602235431620.png -.. |image6| image:: http://image.iswbm.com/image-20200602235723214.png -.. |image7| image:: http://image.iswbm.com/20200605192855.png -.. |image8| image:: http://image.iswbm.com/20200605200027.png -.. |image9| image:: http://image.iswbm.com/20200605201138.png -.. |image10| image:: http://image.iswbm.com/20200605202450.png -.. |image11| image:: http://image.iswbm.com/image-20200604204657243.png -.. |image12| image:: http://image.iswbm.com/tcp_pdus.png -.. |image13| image:: http://image.iswbm.com/20200605204727.png -.. |image14| image:: http://image.iswbm.com/image-20200604223625787.png -.. |image15| image:: http://image.iswbm.com/image-20200604224127512.png -.. |image16| image:: http://image.iswbm.com/20200607174235.png +.. |image0| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index 4b665fb..35846e7 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -1,785 +1,92 @@ -# 10.4 全网最全(中文) tcpdump 抓包指南 +# 1.34 最优雅的命令调用方式 ![](http://image.iswbm.com/20200602135014.png) -今天要给大家介绍的一个 Unix 下的一个 **网络数据采集分析工具**,也就是我们常说的抓包工具。 +在编写 Python 脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 os.popen,os.system,commands,还有 subprocess。 -与它功能类似的工具有 wireshark ,不同的是,wireshark 有图形化界面,而 tcpdump 则只有命令行。 +今天明哥要介绍一种更加优雅的方法,就是 `sh` 这个第三方库,它能让你像调用方法那样去调用系统中的命令。 -由于我本人更习惯使用命令行的方式进行抓包,因此今天先跳过 wireshark,直接给大家介绍这个 tcpdump 神器。 - -这篇文章,我肝了好几天,借助于Linux 的 man 帮助命令,我把 tcpdump 的用法全部研究了个遍,才形成了本文,不夸张的说,应该可以算是中文里把 tcpdump 讲得最清楚明白,并且还最全的文章了(至少我从百度、谷歌的情况来看),所以本文值得你收藏分享,就怕你错过了,就再也找不到像这样把 tcpdump 讲得直白而且特全的文章了。 - -![](http://image.iswbm.com/20200630095709.png) - -在讲解之前,有两点需要声明: - -1. 第三节到第六节里的 tcpdump 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第五节的逻辑逻辑运算符进行组合搭配。 -2. 不同 Linux 发行版下、不同版本的 tcpdump 可能有小许差异, 本文是基于 CentOS 7.2 的 4.5.1 版本的tcpdump 进行学习的,若在你的环境中无法使用,请参考 `man tcpdump` 进行针对性学习。 - -## 1. tcpdump 核心参数图解 - -大家都知道,网络上的流量、数据包,非常的多,因此要想抓到我们所需要的数据包,就需要我们定义一个精准的过滤器,把这些目标数据包,从巨大的数据包网络中抓取出来。 - -所以学习抓包工具,其实就是学习如何定义过滤器的过程。 - -而在 tcpdump 的世界里,过滤器的实现,都是通过一个又一个的参数组合起来,一个参数不够精准,那就再加一个,直到我们能过滤掉无用的数据包,只留下我们感兴趣的数据包。 - -tcpdump 的参数非常的多,初学者在没有掌握 tcpdump 时,会对这个命令的众多参数产生很多的疑惑。 - -就比如下面这个命令,我们要通过 `host` 参数指定 host ip 进行过滤 - -```shell -$ tcpdump host 192.168.10.100 -``` - -`主程序` + `参数名`+ `参数值` 这样的组合才是我们正常认知里面命令行该有的样子。 - -可 tcpdump 却不走寻常路,我们居然还可以在 host 前再加一个限定词,来缩小过滤的范围? - -```shell -$ tcpdump src host 192.168.10.100 -``` - -从字面上理解,确实很容易理解,但是这不符合编写命令行程序的正常逻辑,导致我们会有所疑虑: - -1. 除了 src ,dst,可还有其它可以用的限定词? - -2. src,host 应该如何理解它们,叫参数名?不合适,因为 src 明显不合适。 - - -如果你在网上看到有关 tcpdump 的博客、教程,无一不是给你一个参数组合,告诉你这是实现了怎样的一个过滤器?这样的教学方式,很容易让你依赖别人的文章来使用 tcpdump,而不能将 tcpdump 这样神器消化,达到灵活应用,灵活搭配过滤器的效果。 - -上面加了 src 本身就颠覆了我们的认知,你可知道在 src 之前还可以加更多的条件,比如 tcp, udp, icmp 等词,在你之前的基础上再过滤一层。 - -```shell -$ tcpdump tcp src host 192.168.10.100 -``` - - - -这种参数的不确定性,让大多数人对 tcpdump 的学习始终无法得其精髓。 - -因此,在学习 tcpdump 之前,我觉得有必要要先让你知道:**tcpdump 的参数是如何组成的?这非常重要。** - -为此,我画了一张图,方便你直观的理解 tcpdump 的各种参数: - -![](http://image.iswbm.com/20200628111325.png) - -1. option 可选参数:将在后边一一解释。 -2. proto 类过滤器:根据协议进行过滤,可识别的关键词有: tcp, udp, icmp, ip, ip6, arp, rarp,ether,wlan, fddi, tr, decnet -3. type 类过滤器:可识别的关键词有:host, net, port, portrange,这些词后边需要再接参数。 -4. direction 类过滤器:根据数据流向进行过滤,可识别的关键字有:src, dst,同时你可以使用逻辑运算符进行组合,比如 src or dst - -proto、type、direction 这三类过滤器的内容比较简单,也最常用,因此我将其放在最前面,也就是 **第三节:常规过滤规则**一起介绍。 - -而 option 可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 **第四节:可选参数解析** - -当你看完前面六节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% 的使用需求。 - -你一定会问了,还有 20% 呢? - -其实 tcpdump 还有一些过滤关键词,它不符合以上四种过滤规则,可能需要你单独记忆。关于这部分我会在 **第六节:特殊过滤规则** 里进行介绍。 - -## 2. 理解 tcpdump 的输出 - -### 2.1 输出内容结构 - -tcpdump 输出的内容虽然多,却很规律。 - -这里以我随便抓取的一个 tcp 包为例来看一下 - -```shell -21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48 -``` - -从上面的输出来看,可以总结出: - -1. 第一列:时分秒毫秒 21:26:49.013621 -2. 第二列:网络协议 IP -3. 第三列:发送方的ip地址+端口号,其中172.20.20.1是 ip,而15605 是端口号 -4. 第四列:箭头 >, 表示数据流向 -5. 第五列:接收方的ip地址+端口号,其中 172.20.20.2 是 ip,而5920 是端口号 -6. 第六列:冒号 -7. 第七列:数据包内容,包括Flags 标识符,seq 号,ack 号,win 窗口,数据长度 length,其中 [P.] 表示 PUSH 标志位为 1,更多标识符见下面 - - - -### 2.2 Flags 标识符 - -使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种: - -- `[S]` : SYN(开始连接) -- `[P]` : PSH(推送数据) -- `[F]` : FIN (结束连接) -- `[R]` : RST(重置连接) -- `[.]` : 没有 Flag (意思是除上面四种类型外的其他情况,有可能是 ACK 也有可能是 URG) - -## 3. 常规过滤规则 - -### 3.1 基于IP地址过滤:host - -使用 `host` 就可以指定 host ip 进行过滤 - -```shell -$ tcpdump host 192.168.10.100 -``` - -数据包的 ip 可以再细分为源ip和目标ip两种 +使用前,当然是安装它,`sh` 支持 Python 2 也支持 Python3,这里以 Python 3 为例。 ```shell -# 根据源ip进行过滤 -$ tcpdump -i eth2 src 192.168.10.100 - -# 根据目标ip进行过滤 -$ tcpdump -i eth2 dst 192.168.10.200 -``` - -### 3.2 基于网段进行过滤:net - -若你的ip范围是一个网段,可以直接这样指定 - -```shell -$ tcpdump net 192.168.10.0/24 +$ python3 -m pip install sh ``` -网段同样可以再细分为源网段和目标网段 +这里要注意一点,虽然在 Windows 上也可以安装成功,但是并不能使用,如果你尝试在 Windows 导入 它,会友好地提示你,该库只适用于 Linux 及 OSX 系统,假如你也想要在 Windows 使用,它推荐你使用它的 兄弟库 - `pbs` (https://pypi.org/project/pbs/)。 -```shell -# 根据源网段进行过滤 -$ tcpdump src net 192.168 - -# 根据目标网段进行过滤 -$ tcpdump dst net 192.168 -``` +![](http://image.iswbm.com/20200227201644.png) -### 3.3 基于端口进行过滤:port +安装完成后,就可以直接使用它了,以下几个示例,非常简单,简单到我感觉只要 demo ,而不需要任何的中文解释就可以让你知道他是如何使用的。 -使用 `port` 就可以指定特定端口进行过滤 -```shell -$ tcpdump port 8088 -``` -端口同样可以再细分为源端口,目标端口 +### 1. 列出目录文件 -```shell -# 根据源端口进行过滤 -$ tcpdump src port 8088 +使用 `ls` -# 根据目标端口进行过滤 -$ tcpdump dst port 8088 +```python +>>> import sh +>>> sh.ls("/home", "-l", color="never") +total 4 +drwx------ 3 centos centos 4096 Mar 8 2019 centos ``` -如果你想要同时指定两个端口你可以这样写 +使用 `glob` -```shell -$ tcpdump port 80 or port 8088 +```python +>>> sh.glob("/etc/*.conf") +['/etc/mke2fs.conf', '/etc/dnsmasq.conf', '/etc/asound.conf'] ``` -但也可以简写成这样 -```shell -$ tcpdump port 80 or 8088 -``` -如果你的想抓取的不再是一两个端口,而是一个范围,一个一个指定就非常麻烦了,此时你可以这样指定一个端口段。 +### 调用程序 -```shell -$ tcpdump portrange 8000-8080 -$ tcpdump src portrange 8000-8080 -$ tcpdump dst portrange 8000-8080 +```python +>>> import sh +>>> r=sh.Command('/root/test.py') +>>> r() +hello,world ``` +上面我们的 ls ,也可以通过这种方式执行,只是不能再加参数了。 - -对于一些常见协议的默认端口,我们还可以直接使用协议名,而不用具体的端口号 - -比如 http == 80,https == 443 等 - -```shell -$ tcpdump tcp port http +```python +sh.Command("ls")() ``` -### 3.4 基于协议进行过滤:proto - -常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等 +### 管道 -若你只想查看 icmp 的包,可以直接这样写 - -```shell -$ tcpdump icmp +```python +>>> print(sh.sort(sh.du(sh.glob('*'),'-shc'),'-rn')) +712K distribute-0.6.49.tar.gz +672K setuptools-1.1.5.tar.gz +548K get-pip.py ``` -protocol 可选值:ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or netbeui - +管道是有序的,默认由内而外,但如果需要并行呢?加个_piped=True - -### 3.5 基本IP协议的版本进行过滤 - -当你想查看 tcp 的包,你也许会这样子写 - -```shell -$ tcpdump tcp +```python +>>> for line in sh.tr(sh.tail("-f", "/home/mysql/mysql/log/alert.log", _piped=True), "[:upper:]", "[:lower:]", _iter=True): +... print line +... +innodb: doublewrite buffer not found: creating new + +innodb: doublewrite buffer created + +innodb: 127 rollback segment(s) active. + +innodb: creating foreign key constraint system tables + +innodb: foreign key constraint system tables created ``` -这样子写也没问题,就是不够精准,为什么这么说呢? - -ip 根据版本的不同,可以再细分为 IPv4 和 IPv6 两种,如果你只指定了 tcp,这两种其实都会包含在内。 - - - -那有什么办法,能够将 IPv4 和 IPv6 区分开来呢? - -很简单,如果是 IPv4 的 tcp 包 ,就这样写(友情提示:数字 6 表示的是 tcp 在ip报文中的编号。) - -```shell -$ tcpdump 'ip proto tcp' - -# or - -$ tcpdump ip proto 6 - -# or - -$ tcpdump 'ip protochain tcp' - -# or - -$ tcpdump ip protochain 6 -``` - -而如果是 IPv6 的 tcp 包 ,就这样写 - -```shell -$ tcpdump 'ip6 proto tcp' - -# or - -$ tcpdump ip6 proto 6 - -# or - -$ tcpdump 'ip6 protochain tcp' - -# or - -$ tcpdump ip6 protochain 6 -``` - - - -关于上面这几个命令示例,有两点需要注意: - -1. 跟在 proto 和 protochain 后面的如果是 tcp, udp, icmp ,那么过滤器需要用引号包含,这是因为 tcp,udp, icmp 是 tcpdump 的关键字。 -2. 跟在ip 和 ip6 关键字后面的 proto 和 protochain 是两个新面孔,看起来用法类似,它们是否等价,又有什么区别呢? - -关于第二点,网络上没有找到很具体的答案,我只能通过 `man tcpdump` 的提示, 给出自己的个人猜测,但不保证正确。 - -proto 后面跟的 `` 的关键词是固定的,只能是 ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or netbeui 这里面的其中一个。 - -而 protochain 后面跟的 protocol 要求就没有那么严格,它可以是任意词,只要 tcpdump 的 IP 报文头部里的 protocol 字段为 `` 就能匹配上。 - - - -理论上来讲,下面两种写法效果是一样的 - -```shell -$ tcpdump 'ip && tcp' -$ tcpdump 'ip proto tcp' -``` - -同样的,这两种写法也是一样的 - -```shell -$ tcpdump 'ip6 && tcp' -$ tcpdump 'ip6 proto tcp' -``` - - - -## 4. 可选参数解析 - -### 4.1 设置不解析域名提升速度 - -- `-n`:不把ip转化成域名,直接显示 ip,避免执行 DNS lookups 的过程,速度会快很多 -- `-nn`:不把协议和端口号转化成名字,速度也会快很多。 -- `-N`:不打印出host 的域名部分.。比如,,如果设置了此选现,tcpdump 将会打印'nic' 而不是 'nic.ddn.mil'. - -### 4.2 过滤结果输出到文件 - -使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 wireshark 。 - -而要使用wireshark ,我们得将 tcpdump 抓到的包数据生成到文件中,最后再使用 wireshark 打开它即可。 - -使用 `-w` 参数后接一个以 `.pcap` 后缀命令的文件名,就可以将 tcpdump 抓到的数据保存到文件中。 - -```shell -$ tcpdump icmp -w icmp.pcap -``` - -### 4.3 从文件中读取包数据 - -使用 `-w` 是写入数据到文件,而使用 `-r` 是从文件中读取数据。 - -读取后,我们照样可以使用上述的过滤器语法进行过滤分析。 - -```shell -$ tcpdump icmp -r all.pcap -``` - - - -### 4.4 控制详细内容的输出 - -- `-v`:产生详细的输出. 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。 -- `-vv`:产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。(摘自网络,目前我还未使用过) -- `-vvv`:产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) - -### 4.5 控制时间的显示 - -- `-t `:在每行的输出中不输出时间 -- `-tt`:在每行的输出中会输出时间戳 -- `-ttt`:输出每两行打印的时间间隔(以毫秒为单位) -- `-tttt`:在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观) - -### 4.6 显示数据包的头部 - -- `-x`:以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部) -- `-xx`:以16进制的形式打印每个包的头部数据(包括数据链路层的头部) -- `-X`:以16进制和 ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。 -- `-XX`:以16进制和 ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。 - -### 4.7 过滤指定网卡的数据包 - -- `-i`:指定要过滤的网卡接口,如果要查看所有网卡,可以 `-i any` - -### 4.8 过滤特定流向的数据包 - -- `-Q`: 选择是入方向还是出方向的数据包,可选项有:in, out, inout,也可以使用 --direction=[direction] 这种写法 - -### 4.9 其他常用的一些参数 - -- `-A`:以ASCII码方式显示每一个数据包(不显示链路层头部信息). 在抓取包含网页数据的数据包时, 可方便查看数据 - -- `-l` : 基于行的输出,便于你保存查看,或者交给其它工具分析 -- `-q` : 简洁地打印输出。即打印很少的协议相关信息, 从而输出行都比较简短. -- `-c` : 捕获 count 个包 tcpdump 就退出 -- `-s` : tcpdump 默认只会截取前 `96` 字节的内容,要想截取所有的报文内容,可以使用 `-s number`, `number` 就是你要截取的报文字节数,如果是 0 的话,表示截取报文全部内容。 -- `-S` : 使用绝对序列号,而不是相对序列号 -- `-C`:file-size,tcpdump 在把原始数据包直接保存到文件中之前, 检查此文件大小是否超过file-size. 如果超过了, 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w 选项指定的文件名一致, 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加. file-size的单位是百万字节(nt: 这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576) -- `-F`:使用file 文件作为过滤条件表达式的输入, 此时命令行上的输入将被忽略. - -### 4.10 对输出内容进行控制的参数 - -- `-D` : 显示所有可用网络接口的列表 -- `-e` : 每行的打印输出中将包括数据包的数据链路层头部信息 -- `-E` : 揭秘IPSEC数据 -- `-L` :列出指定网络接口所支持的数据链路层的类型后退出 -- `-Z`:后接用户名,在抓包时会受到权限的限制。如果以root用户启动tcpdump,tcpdump将会有超级用户权限。 -- `-d`:打印出易读的包匹配码 -- `-dd`:以C语言的形式打印出包匹配码. -- `-ddd`:以十进制数的形式打印出包匹配码 - -## 5. 过滤规则组合 - -有编程基础的同学,对于下面三个逻辑运算符应该不陌生了吧 - -- and:所有的条件都需要满足,也可以表示为 `&&` -- or:只要有一个条件满足就可以,也可以表示为 `||` -- not:取反,也可以使用 `!` - -举个例子,我想需要抓一个来自`10.5.2.3`,发往任意主机的3389端口的包 - -```shell -$ tcpdump src 10.5.2.3 and dst port 3389 -``` - -当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell 中是特殊符号,因为你需要使用引号将其包含。例子如下: - -```shell -$ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' -``` - -而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用下面两个符号 - -- `=`:判断二者相等 -- `==`:判断二者相等 -- `!=`:判断二者不相等 - -当你使用这两个符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如 - -- if:表示网卡接口名、 -- proc:表示进程名 -- pid:表示进程 id -- svc:表示 service class -- dir:表示方向,in 和 out -- eproc:表示 effective process name -- epid:表示 effective process ID - -比如我现在要过滤来自进程名为 `nc` 发出的流经 en0 网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写 - -```shell -$ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" -``` - - - -## 6. 特殊过滤规则 - -### 6.1 根据 tcpflags 进行过滤 - -通过[上一篇文章](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&token=1970357830&lang=zh_CN#rd),我们知道了 tcp 的首部有一个标志位。 - -![TCP 报文首部](http://image.iswbm.com/20200606095627.png) - -tcpdump 支持我们根据数据包的标志位进行过滤 - -``` -proto [ expr:size ] -``` - -- `proto`:可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6) - -- `expr`:可以是数值,也可以是一个表达式,表示与指定的协议头开始处的字节偏移量。 -- `size`:是可选的,表示从字节偏移量开始取的字节数量。 - -接下来,我将举几个例子,让人明白它的写法,不过在那之前,有几个点需要你明白,这在后面的例子中会用到: - -**1、**tcpflags 可以理解为是一个别名常量,相当于 13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位,所以 tcp[tcpflags] 等价于 tcp[13] ,对应下图中的报文位置。 - -![](http://image.iswbm.com/20200628222034.png) - -**2、**tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg 这些同样可以理解为别名常量,分别代表 1,2,4,8,16,32,64。这些数字是如何计算出来的呢? - -以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2 - -![](http://image.iswbm.com/20200628222010.png) - -由于数字不好记忆,所以一般使用这样的“别名常量”表示。 - -因此当下面这个表达式成立时,就代表这个包是一个 syn 包。 - -```shell -tcp[tcpflags] == tcp-syn -``` - - - -要抓取特定数据包,方法有很多种。 - -下面以最常见的 syn包为例,演示一下如何用 tcpdump 抓取到 syn 包,而其他的类型的包也是同样的道理。 - -据我总结,主要有三种写法: - -1、第一种写法:使用数字表示偏移量 - -```shell -$ tcpdump -i eth0 "tcp[13] & 2 != 0" -``` - -2、第二种写法:使用别名常量表示偏移量 - -```shell -$ tcpdump -i eth0 "tcp[tcpflags] & tcp-syn != 0" -``` - -3、第三种写法:使用混合写法 - -```shell -$ tcpdump -i eth0 "tcp[tcpflags] & 2 != 0" - -# or - -$ tcpdump -i eth0 "tcp[13] & tcp-syn != 0" -``` - - - -如果我想同时捕获多种类型的包呢,比如 syn + ack 包 - -1、第一种写法 - -```shell -$ tcpdump -i eth0 'tcp[13] == 2 or tcp[13] == 16' -``` - -2、第二种写法 - -```shell -$ tcpdump -i eth0 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack' -``` - -3、第三种写法 - -```shell -$ tcpdump -i eth0 "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0" -``` - -4、第四种写法:注意这里是 单个等号,而不是像上面一样两个等号,18(syn+ack) = 2(syn) + 16(ack) - -```shell -$ tcpdump -i eth0 'tcp[13] = 18' - -# or - -$ tcpdump -i eth0 'tcp[tcpflags] = 18' -``` - - - -tcp 中有 类似 tcp-syn 的别名常量,其他协议也是有的,比如 icmp 协议,可以使用的别名常量有 - -```shell -icmp-echoreply, icmp-unreach, icmp-sourcequench, -icmp-redirect, icmp-echo, icmp-routeradvert, -icmp-routersolicit, icmp-timx-ceed, icmp-paramprob, -icmp-tstamp, icmp-tstampreply,icmp-ireq, -icmp-ireqreply, icmp-maskreq, icmp-maskreply -``` - - - -### 6.2 基于包大小进行过滤 - -若你想查看指定大小的数据包,也是可以的 - -```shell -$ tcpdump less 32 -$ tcpdump greater 64 -$ tcpdump <= 128 -``` - - - -### 6.3 根据 mac 地址进行过滤 - -例子如下,其中 ehost 是记录在 /etc/ethers 里的 name - -```shell -$ tcpdump ether host [ehost] -$ tcpdump ether dst [ehost] -$ tcpdump ether src [ehost] -``` - - - -### 6.4 过滤通过指定网关的数据包 - -```shell -$ tcpdump gateway [host] -``` - - - -### 6.5 过滤广播/多播数据包 - -```shell -$ tcpdump ether broadcast -$ tcpdump ether multicast - -$ tcpdump ip broadcast -$ tcpdump ip multicast - -$ tcpdump ip6 multicast -``` - - - -## 7. 如何抓取到更精准的包? - -先给你抛出一个问题:如果我只想抓取 HTTP 的 POST 请求该如何写呢? - -如果只学习了上面的内容,恐怕你还是无法写法满足这个抓取需求的过滤器。 - -在学习之前,我先给出答案,然后再剖析一下,这个过滤器是如何生效的,居然能让我们对包内的内容进行判断。 - -```shell -$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' -``` - -命令里的可选参数,在前面的内容里已经详细讲过了。这里不再细讲。 - -本节的重点是引号里的内容,看起来很复杂的样子。 - -将它逐一分解,我们只要先理解了下面几种用法,就能明白 - -- `tcp[n]`:表示 tcp 报文里 第 n 个字节 - -- `tcp[n:c]`:表示 tcp 报文里从第n个字节开始取 c 个字节,tcp[12:1] 表示从报文的第12个字节(因为有第0个字节,所以这里的12其实表示的是13)开始算起取一个字节,也就是 8 个bit。查看 [tcp 的报文首部结构](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure),可以得知这 8 个bit 其实就是下图中的红框圈起来的位置,而在这里我们只要前面 4个bit,也就是实际数据在整个报文首部中的偏移量。 - - ![](http://image.iswbm.com/20200629085659.png) - -- `&`:是[位运算](https://en.wikipedia.org/wiki/Bitwise_operation)里的 and 操作符,比如 `0011 & 0010 = 0010` -- `>>`:是位运算里的右移操作,比如 `0111 >> 2 = 0001` -- `0xf0`:是 10 进制的 240 的 16 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 4bit 全部是 1,后面4个bit全部是0,往后看你就知道这个特点有什么用了。 - -分解完后,再慢慢合并起来看 - -1、`tcp[12:1] & 0xf0` 其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 个字节是这样组成的 `10110000`,那么这个表达式就可以变成 10110110 && 11110000 = 10110000,得到了 10110000 后,再进入下一步。 - -2、`tcp[12:1] & 0xf0) >> 2` :如果你不理解 tcp 报文首部里的数据偏移,请先点击这个前往我的[上一篇文章](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&token=1970357830&lang=zh_CN#rd),搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 - -`tcp[12:1] & 0xf0) >> 2` 这个表达式实际是 `(tcp[12:1] & 0xf0) >> 4 ) << 2` 的简写形式。所以要搞懂 `tcp[12:1] & 0xf0) >> 2` 只要理解了`(tcp[12:1] & 0xf0) >> 4 ) << 2` 就行了 。 - -从上一步我们算出了 `tcp[12:1] & 0xf0` 的值其实是一个字节,也就是 8 个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 4个bit,也就是说 上面得到的值 10110000,前面 4 位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 右移4位即可,也就是 `tcp[12:1] & 0xf0) >> 4`,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 Data Offset 的单位是 4个字节,因为要将 1011 乘以 4才可以,除以4在位运算中相当于左移2位,也就是 `<<2`,与前面的 `>>4` 结合起来一起算的话,最终的运算可以简化为 `>>2`。 - -至此,我们终于得出了实际数据开始的位置是 `tcp[12:1] & 0xf0) >> 2` (单位是字节)。 - -找到了数据的起点后,可别忘了我们的目的是从数据中打到 HTTP 请求的方法,是 GET 呢 还是 POST ,或者是其他的? - -有了上面的经验,我们自然懂得使用 `tcp[((tcp[12:1] & 0xf0) >> 2):4]` 从数据开始的位置再取出四个字节,然后将结果与 `GET ` (注意 GET最后还有个空格)的 16进制写法(也就是 `0x47455420`)进行比对。 - -```shell -0x47 --> 71 --> G -0x45 --> 69 --> E -0x54 --> 84 --> T -0x20 --> 32 --> 空格 -``` - -![](http://image.iswbm.com/20200629130407.png) - -如果相等,则该表达式为True,tcpdump 认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。 - - - -## 8. 抓包实战应用例子 - -### 8.1 提取 HTTP 的 User-Agent - -从 HTTP 请求头中提取 HTTP 的 User-Agent: - -```bash -$ tcpdump -nn -A -s1500 -l | grep "User-Agent:" -``` - -通过 `egrep` 可以同时提取User-Agent 和主机名(或其他头文件): - -```bash -$ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:' -``` - -### 8.2 抓取 HTTP GET 和 POST 请求 - -抓取 HTTP GET 请求包: - -```bash -$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' - -# or - -$ tcpdump -vvAls0 | grep 'GET' -``` - -可以抓取 HTTP POST 请求包: - -```bash -$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354' - -# or - -$ tcpdump -vvAls0 | grep 'POST' -``` - -注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST 请求会被分割为多个 TCP 数据包。 - -### 8.3 找出发包数最多的 IP - -找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 IP,可以使用下面的命令: - -```bash -$ tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20 -``` - -- **cut -f 1,2,3,4 -d '.'** : 以 `.` 为分隔符,打印出每行的前四列。即 IP 地址。 -- **sort | uniq -c** : 排序并计数 -- **sort -nr** : 按照数值大小逆向排序 - -### 8.4 抓取 DNS 请求和响应 - -DNS 的默认端口是 53,因此可以通过端口进行过滤 - -```shell -$ tcpdump -i any -s0 port 53 -``` - - - -### 8.5 切割 pcap 文件 - -当抓取大量数据并写入文件时,可以自动切割为多个大小相同的文件。例如,下面的命令表示每 3600 秒创建一个新文件 `capture-(hour).pcap`,每个文件大小不超过 `200*1000000` 字节: - -```bash -$ tcpdump -w /tmp/capture-%H.pcap -G 3600 -C 200 -``` - -这些文件的命名为 `capture-{1-24}.pcap`,24 小时之后,之前的文件就会被覆盖。 - -### 8.6 提取 HTTP POST 请求中的密码 - -从 HTTP POST 请求中提取密码和主机名: - -```shell -$ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:" -``` - -### 8.7 提取 HTTP 请求的 URL - -提取 HTTP 请求的主机名和路径: - -```shell -$ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:" -``` - -### 8.8 抓取 HTTP 有效数据包 - -抓取 80 端口的 HTTP 有效数据包,排除 TCP 连接建立过程的数据包(SYN / FIN / ACK): - -```shell -$ tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' -``` - -### 8.9 结合 Wireshark 进行分析 - -通常 `Wireshark`(或 tshark)比 tcpdump 更容易分析应用层协议。一般的做法是在远程服务器上先使用 `tcpdump` 抓取数据并写入文件,然后再将文件拷贝到本地工作站上用 `Wireshark` 分析。 - -还有一种更高效的方法,可以通过 ssh 连接将抓取到的数据实时发送给 Wireshark 进行分析。以 MacOS 系统为例,可以通过 `brew cask install wireshark` 来安装,然后通过下面的命令来分析: - -```shell -$ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - not port 22' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - -``` - -例如,如果想分析 DNS 协议,可以使用下面的命令: - -```shell -$ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - port 53' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - -``` - -抓取到的数据: - -![](https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/20200210170101.png) - -`-c` 选项用来限制抓取数据的大小。如果不限制大小,就只能通过 `ctrl-c` 来停止抓取,这样一来不仅关闭了 tcpdump,也关闭了 wireshark。 - - - -到这里,我已经将我所知道的 tcpdump 的用法全部说了一遍,如果你有认真地看完本文,相信会有不小的收获,掌握一个上手的抓包工具,对于以后我们学习网络、分析网络协议、以及定位网络问题,会很有帮助,而 tcpdump 是我推荐的一个抓包工具。 - -## 9. 参考文章 - -1. [FreeBSD Manual Pages About tcpdump](https://www.freebsd.org/cgi/man.cgi?query=tcpdump&apropos=0&sektion=0&manpath=FreeBSD+7.2-RELEASE&format=html) -3. [Linux tcpdump命令详解](https://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html) -4. [一份快速实用的 tcpdump 命令参考手册](http://team.jiunile.com/blog/2019/06/tcpdump.html) -4. [超详细的网络抓包神器 tcpdump 使用指南](https://fuckcloudnative.io/posts/tcpdump-examples/) -5. [[译]tcpdump 示例教程](https://colobu.com/2019/07/16/a-tcpdump-tutorial-with-examples/) -6. [[英]tcpdump 示例教程](https://danielmiessler.com/study/tcpdump/) ---- ![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_04.rst b/source/c10/c10_04.rst index e32a599..65fcc70 100644 --- a/source/c10/c10_04.rst +++ b/source/c10/c10_04.rst @@ -1,920 +1,98 @@ -10.4 全网最全(中文) tcpdump 抓包指南 -==================================== +1.34 最优雅的命令调用方式 +========================= |image0| -今天要给大家介绍的一个 Unix 下的一个 -**网络数据采集分析工具**\ ,也就是我们常说的抓包工具。 +在编写 Python +脚本的时候,很经常需要我们去调用系统的命令,方法有很多种,比如 +os.popen,os.system,commands,还有 subprocess。 -与它功能类似的工具有 wireshark ,不同的是,wireshark 有图形化界面,而 -tcpdump 则只有命令行。 +今天明哥要介绍一种更加优雅的方法,就是 ``sh`` +这个第三方库,它能让你像调用方法那样去调用系统中的命令。 -由于我本人更习惯使用命令行的方式进行抓包,因此今天先跳过 -wireshark,直接给大家介绍这个 tcpdump 神器。 - -这篇文章,我肝了好几天,借助于Linux 的 man 帮助命令,我把 tcpdump -的用法全部研究了个遍,才形成了本文,不夸张的说,应该可以算是中文里把 -tcpdump -讲得最清楚明白,并且还最全的文章了(至少我从百度、谷歌的情况来看),所以本文值得你收藏分享,就怕你错过了,就再也找不到像这样把 -tcpdump 讲得直白而且特全的文章了。 - -|image1| - -在讲解之前,有两点需要声明: - -1. 第三节到第六节里的 tcpdump - 命令示例,只为了说明参数的使用,并不一定就能抓到包,如果要精准抓到你所需要的包,需要配合第五节的逻辑逻辑运算符进行组合搭配。 -2. 不同 Linux 发行版下、不同版本的 tcpdump 可能有小许差异, 本文是基于 - CentOS 7.2 的 4.5.1 版本的tcpdump - 进行学习的,若在你的环境中无法使用,请参考 ``man tcpdump`` - 进行针对性学习。 - -1. tcpdump 核心参数图解 ------------------------ - -大家都知道,网络上的流量、数据包,非常的多,因此要想抓到我们所需要的数据包,就需要我们定义一个精准的过滤器,把这些目标数据包,从巨大的数据包网络中抓取出来。 - -所以学习抓包工具,其实就是学习如何定义过滤器的过程。 - -而在 tcpdump -的世界里,过滤器的实现,都是通过一个又一个的参数组合起来,一个参数不够精准,那就再加一个,直到我们能过滤掉无用的数据包,只留下我们感兴趣的数据包。 - -tcpdump 的参数非常的多,初学者在没有掌握 tcpdump -时,会对这个命令的众多参数产生很多的疑惑。 - -就比如下面这个命令,我们要通过 ``host`` 参数指定 host ip 进行过滤 - -.. code:: shell - - $ tcpdump host 192.168.10.100 - -``主程序`` + ``参数名``\ + ``参数值`` -这样的组合才是我们正常认知里面命令行该有的样子。 - -可 tcpdump 却不走寻常路,我们居然还可以在 host -前再加一个限定词,来缩小过滤的范围? - -.. code:: shell - - $ tcpdump src host 192.168.10.100 - -从字面上理解,确实很容易理解,但是这不符合编写命令行程序的正常逻辑,导致我们会有所疑虑: - -1. 除了 src ,dst,可还有其它可以用的限定词? - -2. src,host 应该如何理解它们,叫参数名?不合适,因为 src 明显不合适。 - -如果你在网上看到有关 tcpdump -的博客、教程,无一不是给你一个参数组合,告诉你这是实现了怎样的一个过滤器?这样的教学方式,很容易让你依赖别人的文章来使用 -tcpdump,而不能将 tcpdump -这样神器消化,达到灵活应用,灵活搭配过滤器的效果。 - -上面加了 src 本身就颠覆了我们的认知,你可知道在 src -之前还可以加更多的条件,比如 tcp, udp, icmp -等词,在你之前的基础上再过滤一层。 - -.. code:: shell - - $ tcpdump tcp src host 192.168.10.100 - -这种参数的不确定性,让大多数人对 tcpdump 的学习始终无法得其精髓。 - -因此,在学习 tcpdump 之前,我觉得有必要要先让你知道:\ **tcpdump -的参数是如何组成的?这非常重要。** - -为此,我画了一张图,方便你直观的理解 tcpdump 的各种参数: - -|image2| - -1. option 可选参数:将在后边一一解释。 -2. proto 类过滤器:根据协议进行过滤,可识别的关键词有: tcp, udp, icmp, - ip, ip6, arp, rarp,ether,wlan, fddi, tr, decnet -3. type 类过滤器:可识别的关键词有:host, net, port, - portrange,这些词后边需要再接参数。 -4. direction 类过滤器:根据数据流向进行过滤,可识别的关键字有:src, - dst,同时你可以使用逻辑运算符进行组合,比如 src or dst - -proto、type、direction -这三类过滤器的内容比较简单,也最常用,因此我将其放在最前面,也就是 -**第三节:常规过滤规则**\ 一起介绍。 - -而 option -可选的参数非常多,有的甚至也不经常用到,因此我将其放到后面一点,也就是 -**第四节:可选参数解析** - -当你看完前面六节,你对 tcpdump 的认识会上了一个台阶,至少能够满足你 80% -的使用需求。 - -你一定会问了,还有 20% 呢? - -其实 tcpdump -还有一些过滤关键词,它不符合以上四种过滤规则,可能需要你单独记忆。关于这部分我会在 -**第六节:特殊过滤规则** 里进行介绍。 - -2. 理解 tcpdump 的输出 ----------------------- - -2.1 输出内容结构 -~~~~~~~~~~~~~~~~ - -tcpdump 输出的内容虽然多,却很规律。 - -这里以我随便抓取的一个 tcp 包为例来看一下 - -.. code:: shell - - 21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48 - -从上面的输出来看,可以总结出: - -1. 第一列:时分秒毫秒 21:26:49.013621 -2. 第二列:网络协议 IP -3. 第三列:发送方的ip地址+端口号,其中172.20.20.1是 ip,而15605 是端口号 -4. 第四列:箭头 >, 表示数据流向 -5. 第五列:接收方的ip地址+端口号,其中 172.20.20.2 是 ip,而5920 - 是端口号 -6. 第六列:冒号 -7. 第七列:数据包内容,包括Flags 标识符,seq 号,ack 号,win - 窗口,数据长度 length,其中 [P.] 表示 PUSH 标志位为 - 1,更多标识符见下面 - -2.2 Flags 标识符 -~~~~~~~~~~~~~~~~ - -使用 tcpdump 抓包后,会遇到的 TCP 报文 Flags,有以下几种: - -- ``[S]`` : SYN(开始连接) -- ``[P]`` : PSH(推送数据) -- ``[F]`` : FIN (结束连接) -- ``[R]`` : RST(重置连接) -- ``[.]`` : 没有 Flag (意思是除上面四种类型外的其他情况,有可能是 ACK - 也有可能是 URG) - -3. 常规过滤规则 ---------------- - -3.1 基于IP地址过滤:host -~~~~~~~~~~~~~~~~~~~~~~~~ - -使用 ``host`` 就可以指定 host ip 进行过滤 - -.. code:: shell - - $ tcpdump host 192.168.10.100 - -数据包的 ip 可以再细分为源ip和目标ip两种 - -.. code:: shell - - # 根据源ip进行过滤 - $ tcpdump -i eth2 src 192.168.10.100 - - # 根据目标ip进行过滤 - $ tcpdump -i eth2 dst 192.168.10.200 - -3.2 基于网段进行过滤:net -~~~~~~~~~~~~~~~~~~~~~~~~~ - -若你的ip范围是一个网段,可以直接这样指定 - -.. code:: shell - - $ tcpdump net 192.168.10.0/24 - -网段同样可以再细分为源网段和目标网段 - -.. code:: shell - - # 根据源网段进行过滤 - $ tcpdump src net 192.168 - - # 根据目标网段进行过滤 - $ tcpdump dst net 192.168 - -3.3 基于端口进行过滤:port -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -使用 ``port`` 就可以指定特定端口进行过滤 - -.. code:: shell - - $ tcpdump port 8088 - -端口同样可以再细分为源端口,目标端口 - -.. code:: shell - - # 根据源端口进行过滤 - $ tcpdump src port 8088 - - # 根据目标端口进行过滤 - $ tcpdump dst port 8088 - -如果你想要同时指定两个端口你可以这样写 - -.. code:: shell - - $ tcpdump port 80 or port 8088 - -但也可以简写成这样 - -.. code:: shell - - $ tcpdump port 80 or 8088 - -如果你的想抓取的不再是一两个端口,而是一个范围,一个一个指定就非常麻烦了,此时你可以这样指定一个端口段。 - -.. code:: shell - - $ tcpdump portrange 8000-8080 - $ tcpdump src portrange 8000-8080 - $ tcpdump dst portrange 8000-8080 - -对于一些常见协议的默认端口,我们还可以直接使用协议名,而不用具体的端口号 - -比如 http == 80,https == 443 等 - -.. code:: shell - - $ tcpdump tcp port http - -3.4 基于协议进行过滤:proto -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -常见的网络协议有:tcp, udp, icmp, http, ip,ipv6 等 - -若你只想查看 icmp 的包,可以直接这样写 - -.. code:: shell - - $ tcpdump icmp - -protocol 可选值:ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, -mopdl, moprc, iso, stp, ipx, or netbeui - -3.5 基本IP协议的版本进行过滤 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -当你想查看 tcp 的包,你也许会这样子写 - -.. code:: shell - - $ tcpdump tcp - -这样子写也没问题,就是不够精准,为什么这么说呢? - -ip 根据版本的不同,可以再细分为 IPv4 和 IPv6 两种,如果你只指定了 -tcp,这两种其实都会包含在内。 - -那有什么办法,能够将 IPv4 和 IPv6 区分开来呢? - -很简单,如果是 IPv4 的 tcp 包 ,就这样写(友情提示:数字 6 表示的是 tcp -在ip报文中的编号。) - -.. code:: shell - - $ tcpdump 'ip proto tcp' - - # or - - $ tcpdump ip proto 6 - - # or - - $ tcpdump 'ip protochain tcp' - - # or - - $ tcpdump ip protochain 6 - -而如果是 IPv6 的 tcp 包 ,就这样写 - -.. code:: shell - - $ tcpdump 'ip6 proto tcp' - - # or - - $ tcpdump ip6 proto 6 - - # or - - $ tcpdump 'ip6 protochain tcp' - - # or - - $ tcpdump ip6 protochain 6 - -关于上面这几个命令示例,有两点需要注意: - -1. 跟在 proto 和 protochain 后面的如果是 tcp, udp, icmp - ,那么过滤器需要用引号包含,这是因为 tcp,udp, icmp 是 tcpdump - 的关键字。 -2. 跟在ip 和 ip6 关键字后面的 proto 和 protochain - 是两个新面孔,看起来用法类似,它们是否等价,又有什么区别呢? - -关于第二点,网络上没有找到很具体的答案,我只能通过 ``man tcpdump`` -的提示, 给出自己的个人猜测,但不保证正确。 - -proto 后面跟的 ```` 的关键词是固定的,只能是 ip, ip6, arp, -rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or -netbeui 这里面的其中一个。 - -而 protochain 后面跟的 protocol 要求就没有那么严格,它可以是任意词,只要 -tcpdump 的 IP 报文头部里的 protocol 字段为 ```` 就能匹配上。 - -理论上来讲,下面两种写法效果是一样的 - -.. code:: shell - - $ tcpdump 'ip && tcp' - $ tcpdump 'ip proto tcp' - -同样的,这两种写法也是一样的 - -.. code:: shell - - $ tcpdump 'ip6 && tcp' - $ tcpdump 'ip6 proto tcp' - -4. 可选参数解析 ---------------- - -4.1 设置不解析域名提升速度 -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- ``-n``\ :不把ip转化成域名,直接显示 ip,避免执行 DNS lookups - 的过程,速度会快很多 -- ``-nn``\ :不把协议和端口号转化成名字,速度也会快很多。 -- ``-N``\ :不打印出host 的域名部分.。比如,,如果设置了此选现,tcpdump - 将会打印’nic’ 而不是 ‘nic.ddn.mil’. - -4.2 过滤结果输出到文件 -~~~~~~~~~~~~~~~~~~~~~~ - -使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 -wireshark 。 - -而要使用wireshark ,我们得将 tcpdump -抓到的包数据生成到文件中,最后再使用 wireshark 打开它即可。 - -使用 ``-w`` 参数后接一个以 ``.pcap`` 后缀命令的文件名,就可以将 tcpdump -抓到的数据保存到文件中。 - -.. code:: shell - - $ tcpdump icmp -w icmp.pcap - -4.3 从文件中读取包数据 -~~~~~~~~~~~~~~~~~~~~~~ - -使用 ``-w`` 是写入数据到文件,而使用 ``-r`` 是从文件中读取数据。 - -读取后,我们照样可以使用上述的过滤器语法进行过滤分析。 - -.. code:: shell - - $ tcpdump icmp -r all.pcap - -4.4 控制详细内容的输出 -~~~~~~~~~~~~~~~~~~~~~~ - -- ``-v``\ :产生详细的输出. - 比如包的TTL,id标识,数据包长度,以及IP包的一些选项。同时它还会打开一些附加的包完整性检测,比如对IP或ICMP包头部的校验和。 -- ``-vv``\ :产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, - SMB数据包也会被完全解码。(摘自网络,目前我还未使用过) -- ``-vvv``\ :产生比-vv更详细的输出。比如 telent 时所使用的SB, SE - 选项将会被打印, - 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来(摘自网络,目前我还未使用过) - -4.5 控制时间的显示 -~~~~~~~~~~~~~~~~~~ - -- ``-t``\ :在每行的输出中不输出时间 -- ``-tt``\ :在每行的输出中会输出时间戳 -- ``-ttt``\ :输出每两行打印的时间间隔(以毫秒为单位) -- ``-tttt``\ :在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最直观) - -4.6 显示数据包的头部 -~~~~~~~~~~~~~~~~~~~~ - -- ``-x``\ :以16进制的形式打印每个包的头部数据(但不包括数据链路层的头部) -- ``-xx``\ :以16进制的形式打印每个包的头部数据(包括数据链路层的头部) -- ``-X``\ :以16进制和 - ASCII码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。 -- ``-XX``\ :以16进制和 - ASCII码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便。 - -4.7 过滤指定网卡的数据包 -~~~~~~~~~~~~~~~~~~~~~~~~ - -- ``-i``\ :指定要过滤的网卡接口,如果要查看所有网卡,可以 ``-i any`` - -4.8 过滤特定流向的数据包 -~~~~~~~~~~~~~~~~~~~~~~~~ - -- ``-Q``\ : 选择是入方向还是出方向的数据包,可选项有:in, out, - inout,也可以使用 –direction=[direction] 这种写法 - -4.9 其他常用的一些参数 -~~~~~~~~~~~~~~~~~~~~~~ - -- ``-A``\ :以ASCII码方式显示每一个数据包(不显示链路层头部信息). - 在抓取包含网页数据的数据包时, 可方便查看数据 - -- ``-l`` : 基于行的输出,便于你保存查看,或者交给其它工具分析 -- ``-q`` : 简洁地打印输出。即打印很少的协议相关信息, - 从而输出行都比较简短. -- ``-c`` : 捕获 count 个包 tcpdump 就退出 -- ``-s`` : tcpdump 默认只会截取前 ``96`` - 字节的内容,要想截取所有的报文内容,可以使用 ``-s number``\ , - ``number`` 就是你要截取的报文字节数,如果是 0 - 的话,表示截取报文全部内容。 -- ``-S`` : 使用绝对序列号,而不是相对序列号 -- ``-C``\ :file-size,tcpdump 在把原始数据包直接保存到文件中之前, - 检查此文件大小是否超过file-size. 如果超过了, - 将关闭此文件,另创一个文件继续用于原始数据包的记录. 新创建的文件名与-w - 选项指定的文件名一致, - 但文件名后多了一个数字.该数字会从1开始随着新创建文件的增多而增加. - file-size的单位是百万字节(nt: - 这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, - 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576) -- ``-F``\ :使用file 文件作为过滤条件表达式的输入, - 此时命令行上的输入将被忽略. - -4.10 对输出内容进行控制的参数 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- ``-D`` : 显示所有可用网络接口的列表 -- ``-e`` : 每行的打印输出中将包括数据包的数据链路层头部信息 -- ``-E`` : 揭秘IPSEC数据 -- ``-L`` :列出指定网络接口所支持的数据链路层的类型后退出 -- ``-Z``\ :后接用户名,在抓包时会受到权限的限制。如果以root用户启动tcpdump,tcpdump将会有超级用户权限。 -- ``-d``\ :打印出易读的包匹配码 -- ``-dd``\ :以C语言的形式打印出包匹配码. -- ``-ddd``\ :以十进制数的形式打印出包匹配码 - -5. 过滤规则组合 ---------------- - -有编程基础的同学,对于下面三个逻辑运算符应该不陌生了吧 - -- and:所有的条件都需要满足,也可以表示为 ``&&`` -- or:只要有一个条件满足就可以,也可以表示为 ``||`` -- not:取反,也可以使用 ``!`` - -举个例子,我想需要抓一个来自\ ``10.5.2.3``\ ,发往任意主机的3389端口的包 - -.. code:: shell - - $ tcpdump src 10.5.2.3 and dst port 3389 - -当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell -中是特殊符号,因为你需要使用引号将其包含。例子如下: - -.. code:: shell - - $ tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)' - -而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用下面两个符号 - -- ``=``\ :判断二者相等 -- ``==``\ :判断二者相等 -- ``!=``\ :判断二者不相等 - -当你使用这两个符号时,tcpdump -还提供了一些关键字的接口来方便我们进行判断,比如 - -- if:表示网卡接口名、 -- proc:表示进程名 -- pid:表示进程 id -- svc:表示 service class -- dir:表示方向,in 和 out -- eproc:表示 effective process name -- epid:表示 effective process ID - -比如我现在要过滤来自进程名为 ``nc`` 发出的流经 en0 -网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写 - -.. code:: shell - - $ tcpdump "( if=en0 and proc =nc ) || (if != en0 and dir=in)" - -6. 特殊过滤规则 ---------------- - -6.1 根据 tcpflags 进行过滤 -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -通过\ `上一篇文章 `__\ ,我们知道了 -tcp 的首部有一个标志位。 - -.. figure:: http://image.iswbm.com/20200606095627.png - :alt: TCP 报文首部 - - TCP 报文首部 - -tcpdump 支持我们根据数据包的标志位进行过滤 - -:: - - proto [ expr:size ] - -- ``proto``\ :可以是熟知的协议之一(如ip,arp,tcp,udp,icmp,ipv6) - -- ``expr``\ :可以是数值,也可以是一个表达式,表示与指定的协议头开始处的字节偏移量。 -- ``size``\ :是可选的,表示从字节偏移量开始取的字节数量。 - -接下来,我将举几个例子,让人明白它的写法,不过在那之前,有几个点需要你明白,这在后面的例子中会用到: - -**1、**\ tcpflags 可以理解为是一个别名常量,相当于 -13,它代表着与指定的协议头开头相关的字节偏移量,也就是标志位,所以 -tcp[tcpflags] 等价于 tcp[13] ,对应下图中的报文位置。 - -|image3| - -**2、**\ tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg -这些同样可以理解为别名常量,分别代表 -1,2,4,8,16,32,64。这些数字是如何计算出来的呢? - -以 tcp-syn 为例,你可以参照下面这张图,计算出来的值 是就是 2 - -|image4| - -由于数字不好记忆,所以一般使用这样的“别名常量”表示。 - -因此当下面这个表达式成立时,就代表这个包是一个 syn 包。 - -.. code:: shell - - tcp[tcpflags] == tcp-syn - -要抓取特定数据包,方法有很多种。 - -下面以最常见的 syn包为例,演示一下如何用 tcpdump 抓取到 syn -包,而其他的类型的包也是同样的道理。 - -据我总结,主要有三种写法: - -1、第一种写法:使用数字表示偏移量 - -.. code:: shell - - $ tcpdump -i eth0 "tcp[13] & 2 != 0" - -2、第二种写法:使用别名常量表示偏移量 - -.. code:: shell - - $ tcpdump -i eth0 "tcp[tcpflags] & tcp-syn != 0" - -3、第三种写法:使用混合写法 - -.. code:: shell - - $ tcpdump -i eth0 "tcp[tcpflags] & 2 != 0" - - # or - - $ tcpdump -i eth0 "tcp[13] & tcp-syn != 0" - -如果我想同时捕获多种类型的包呢,比如 syn + ack 包 - -1、第一种写法 - -.. code:: shell - - $ tcpdump -i eth0 'tcp[13] == 2 or tcp[13] == 16' - -2、第二种写法 - -.. code:: shell - - $ tcpdump -i eth0 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack' - -3、第三种写法 - -.. code:: shell - - $ tcpdump -i eth0 "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0" - -4、第四种写法:注意这里是 -单个等号,而不是像上面一样两个等号,18(syn+ack) = 2(syn) + 16(ack) - -.. code:: shell - - $ tcpdump -i eth0 'tcp[13] = 18' - - # or - - $ tcpdump -i eth0 'tcp[tcpflags] = 18' - -tcp 中有 类似 tcp-syn 的别名常量,其他协议也是有的,比如 icmp -协议,可以使用的别名常量有 - -.. code:: shell - - icmp-echoreply, icmp-unreach, icmp-sourcequench, - icmp-redirect, icmp-echo, icmp-routeradvert, - icmp-routersolicit, icmp-timx-ceed, icmp-paramprob, - icmp-tstamp, icmp-tstampreply,icmp-ireq, - icmp-ireqreply, icmp-maskreq, icmp-maskreply - -6.2 基于包大小进行过滤 -~~~~~~~~~~~~~~~~~~~~~~ - -若你想查看指定大小的数据包,也是可以的 - -.. code:: shell - - $ tcpdump less 32 - $ tcpdump greater 64 - $ tcpdump <= 128 - -6.3 根据 mac 地址进行过滤 -~~~~~~~~~~~~~~~~~~~~~~~~~ - -例子如下,其中 ehost 是记录在 /etc/ethers 里的 name - -.. code:: shell - - $ tcpdump ether host [ehost] - $ tcpdump ether dst [ehost] - $ tcpdump ether src [ehost] - -6.4 过滤通过指定网关的数据包 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: shell - - $ tcpdump gateway [host] - -6.5 过滤广播/多播数据包 -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: shell - - $ tcpdump ether broadcast - $ tcpdump ether multicast - - $ tcpdump ip broadcast - $ tcpdump ip multicast - - $ tcpdump ip6 multicast - -7. 如何抓取到更精准的包? -------------------------- - -先给你抛出一个问题:如果我只想抓取 HTTP 的 POST 请求该如何写呢? - -如果只学习了上面的内容,恐怕你还是无法写法满足这个抓取需求的过滤器。 - -在学习之前,我先给出答案,然后再剖析一下,这个过滤器是如何生效的,居然能让我们对包内的内容进行判断。 - -.. code:: shell - - $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4]' - -命令里的可选参数,在前面的内容里已经详细讲过了。这里不再细讲。 - -本节的重点是引号里的内容,看起来很复杂的样子。 - -将它逐一分解,我们只要先理解了下面几种用法,就能明白 - -- ``tcp[n]``\ :表示 tcp 报文里 第 n 个字节 - -- ``tcp[n:c]``\ :表示 tcp 报文里从第n个字节开始取 c 个字节,tcp[12:1] - 表示从报文的第12个字节(因为有第0个字节,所以这里的12其实表示的是13)开始算起取一个字节,也就是 - 8 个bit。查看 `tcp - 的报文首部结构 `__\ ,可以得知这 - 8 个bit 其实就是下图中的红框圈起来的位置,而在这里我们只要前面 - 4个bit,也就是实际数据在整个报文首部中的偏移量。 - - |image5| - -- ``&``\ :是\ `位运算 `__\ 里的 - and 操作符,比如 ``0011 & 0010 = 0010`` -- ``>>``\ :是位运算里的右移操作,比如 ``0111 >> 2 = 0001`` -- ``0xf0``\ :是 10 进制的 240 的 16 - 进制表示,但对于位操作来说,10进制和16进制都将毫无意义,我们需要的是二进制,将其转换成二进制后是:11110000,这个数有什么特点呢?前面个 - 4bit 全部是 1,后面4个bit全部是0,往后看你就知道这个特点有什么用了。 - -分解完后,再慢慢合并起来看 - -1、\ ``tcp[12:1] & 0xf0`` -其实并不直观,但是我们将它换一种写法,就好看多了,假设 tcp 报文中的 第12 -个字节是这样组成的 ``10110000``\ ,那么这个表达式就可以变成 10110110 && -11110000 = 10110000,得到了 10110000 后,再进入下一步。 - -2、\ ``tcp[12:1] & 0xf0) >> 2`` :如果你不理解 tcp -报文首部里的数据偏移,请先点击这个前往我的\ `上一篇文章 `__\ ,搞懂数据偏移的意义,否则我保证你这里会绝对会听懵了。 - -``tcp[12:1] & 0xf0) >> 2`` 这个表达式实际是 -``(tcp[12:1] & 0xf0) >> 4 ) << 2`` 的简写形式。所以要搞懂 -``tcp[12:1] & 0xf0) >> 2`` -只要理解了\ ``(tcp[12:1] & 0xf0) >> 4 ) << 2`` 就行了 。 - -从上一步我们算出了 ``tcp[12:1] & 0xf0`` 的值其实是一个字节,也就是 8 -个bit,但是你再回去看下上面的 tcp 报文首部结构图,表示数据偏移量的只有 -4个bit,也就是说 上面得到的值 10110000,前面 4 -位(1011)才是正确的偏移量,那么为了得到 1011,只需要将 10110000 -右移4位即可,也就是 -``tcp[12:1] & 0xf0) >> 4``\ ,至此我们是不是已经得出了实际数据的正确位置呢,很遗憾还没有,前一篇文章里我们讲到 -Data Offset 的单位是 4个字节,因为要将 1011 乘以 -4才可以,除以4在位运算中相当于左移2位,也就是 ``<<2``\ ,与前面的 -``>>4`` 结合起来一起算的话,最终的运算可以简化为 ``>>2``\ 。 - -至此,我们终于得出了实际数据开始的位置是 ``tcp[12:1] & 0xf0) >> 2`` -(单位是字节)。 - -找到了数据的起点后,可别忘了我们的目的是从数据中打到 HTTP 请求的方法,是 -GET 呢 还是 POST ,或者是其他的? - -有了上面的经验,我们自然懂得使用 ``tcp[((tcp[12:1] & 0xf0) >> 2):4]`` -从数据开始的位置再取出四个字节,然后将结果与 ``GET`` (注意 -GET最后还有个空格)的 16进制写法(也就是 ``0x47455420``\ )进行比对。 +使用前,当然是安装它,\ ``sh`` 支持 Python 2 也支持 Python3,这里以 +Python 3 为例。 .. code:: shell - 0x47 --> 71 --> G - 0x45 --> 69 --> E - 0x54 --> 84 --> T - 0x20 --> 32 --> 空格 - -|image6| - -如果相等,则该表达式为True,tcpdump -认为这就是我们所需要抓的数据包,将其输出到我们的终端屏幕上。 - -8. 抓包实战应用例子 -------------------- - -8.1 提取 HTTP 的 User-Agent -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -从 HTTP 请求头中提取 HTTP 的 User-Agent: - -.. code:: bash + $ python3 -m pip install sh - $ tcpdump -nn -A -s1500 -l | grep "User-Agent:" +这里要注意一点,虽然在 Windows +上也可以安装成功,但是并不能使用,如果你尝试在 Windows 导入 +它,会友好地提示你,该库只适用于 Linux 及 OSX 系统,假如你也想要在 +Windows 使用,它推荐你使用它的 兄弟库 - ``pbs`` +(https://pypi.org/project/pbs/)。 -通过 ``egrep`` 可以同时提取User-Agent 和主机名(或其他头文件): - -.. code:: bash - - $ tcpdump -nn -A -s1500 -l | egrep -i 'User-Agent:|Host:' - -8.2 抓取 HTTP GET 和 POST 请求 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -抓取 HTTP GET 请求包: - -.. code:: bash - - $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' - - # or - - $ tcpdump -vvAls0 | grep 'GET' - -可以抓取 HTTP POST 请求包: - -.. code:: bash - - $ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354' - - # or - - $ tcpdump -vvAls0 | grep 'POST' - -注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST -请求会被分割为多个 TCP 数据包。 - -8.3 找出发包数最多的 IP -~~~~~~~~~~~~~~~~~~~~~~~ - -找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 -IP,可以使用下面的命令: - -.. code:: bash - - $ tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20 - -- **cut -f 1,2,3,4 -d ‘.’** : 以 ``.`` 为分隔符,打印出每行的前四列。即 - IP 地址。 -- **sort \| uniq -c** : 排序并计数 -- **sort -nr** : 按照数值大小逆向排序 - -8.4 抓取 DNS 请求和响应 -~~~~~~~~~~~~~~~~~~~~~~~ - -DNS 的默认端口是 53,因此可以通过端口进行过滤 - -.. code:: shell - - $ tcpdump -i any -s0 port 53 - -8.5 切割 pcap 文件 -~~~~~~~~~~~~~~~~~~ - -当抓取大量数据并写入文件时,可以自动切割为多个大小相同的文件。例如,下面的命令表示每 -3600 秒创建一个新文件 ``capture-(hour).pcap``\ ,每个文件大小不超过 -``200*1000000`` 字节: - -.. code:: bash - - $ tcpdump -w /tmp/capture-%H.pcap -G 3600 -C 200 - -这些文件的命名为 ``capture-{1-24}.pcap``\ ,24 -小时之后,之前的文件就会被覆盖。 - -8.6 提取 HTTP POST 请求中的密码 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -从 HTTP POST 请求中提取密码和主机名: - -.. code:: shell - - $ tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:" - -8.7 提取 HTTP 请求的 URL -~~~~~~~~~~~~~~~~~~~~~~~~ - -提取 HTTP 请求的主机名和路径: - -.. code:: shell +|image1| - $ tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:" +安装完成后,就可以直接使用它了,以下几个示例,非常简单,简单到我感觉只要 +demo ,而不需要任何的中文解释就可以让你知道他是如何使用的。 -8.8 抓取 HTTP 有效数据包 -~~~~~~~~~~~~~~~~~~~~~~~~ +1. 列出目录文件 +~~~~~~~~~~~~~~~ -抓取 80 端口的 HTTP 有效数据包,排除 TCP 连接建立过程的数据包(SYN / FIN -/ ACK): +使用 ``ls`` -.. code:: shell +.. code:: python - $ tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' + >>> import sh + >>> sh.ls("/home", "-l", color="never") + total 4 + drwx------ 3 centos centos 4096 Mar 8 2019 centos -8.9 结合 Wireshark 进行分析 -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +使用 ``glob`` -通常 ``Wireshark``\ (或 tshark)比 tcpdump -更容易分析应用层协议。一般的做法是在远程服务器上先使用 ``tcpdump`` -抓取数据并写入文件,然后再将文件拷贝到本地工作站上用 ``Wireshark`` -分析。 +.. code:: python -还有一种更高效的方法,可以通过 ssh 连接将抓取到的数据实时发送给 -Wireshark 进行分析。以 MacOS 系统为例,可以通过 -``brew cask install wireshark`` 来安装,然后通过下面的命令来分析: + >>> sh.glob("/etc/*.conf") + ['/etc/mke2fs.conf', '/etc/dnsmasq.conf', '/etc/asound.conf'] -.. code:: shell +调用程序 +~~~~~~~~ - $ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - not port 22' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - +.. code:: python -例如,如果想分析 DNS 协议,可以使用下面的命令: + >>> import sh + >>> r=sh.Command('/root/test.py') + >>> r() + hello,world -.. code:: shell +上面我们的 ls ,也可以通过这种方式执行,只是不能再加参数了。 - $ ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - port 53' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i - +.. code:: python -抓取到的数据: + sh.Command("ls")() -|image7| +管道 +~~~~ -``-c`` 选项用来限制抓取数据的大小。如果不限制大小,就只能通过 ``ctrl-c`` -来停止抓取,这样一来不仅关闭了 tcpdump,也关闭了 wireshark。 +.. code:: python -到这里,我已经将我所知道的 tcpdump -的用法全部说了一遍,如果你有认真地看完本文,相信会有不小的收获,掌握一个上手的抓包工具,对于以后我们学习网络、分析网络协议、以及定位网络问题,会很有帮助,而 -tcpdump 是我推荐的一个抓包工具。 + >>> print(sh.sort(sh.du(sh.glob('*'),'-shc'),'-rn')) + 712K distribute-0.6.49.tar.gz + 672K setuptools-1.1.5.tar.gz + 548K get-pip.py -9. 参考文章 ------------ +管道是有序的,默认由内而外,但如果需要并行呢?加个_piped=True -1. `FreeBSD Manual Pages About - tcpdump `__ -2. `Linux - tcpdump命令详解 `__ -3. `一份快速实用的 tcpdump - 命令参考手册 `__ -4. `超详细的网络抓包神器 tcpdump - 使用指南 `__ -5. `[译]tcpdump - 示例教程 `__ -6. `[英]tcpdump 示例教程 `__ +.. code:: python --------------- + >>> for line in sh.tr(sh.tail("-f", "/home/mysql/mysql/log/alert.log", _piped=True), "[:upper:]", "[:lower:]", _iter=True): + ... print line + ... + innodb: doublewrite buffer not found: creating new + + innodb: doublewrite buffer created + + innodb: 127 rollback segment(s) active. + + innodb: creating foreign key constraint system tables + + innodb: foreign key constraint system tables created -|image8| +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200630095709.png -.. |image2| image:: http://image.iswbm.com/20200628111325.png -.. |image3| image:: http://image.iswbm.com/20200628222034.png -.. |image4| image:: http://image.iswbm.com/20200628222010.png -.. |image5| image:: http://image.iswbm.com/20200629085659.png -.. |image6| image:: http://image.iswbm.com/20200629130407.png -.. |image7| image:: https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/20200210170101.png -.. |image8| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20200227201644.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c10/c10_05.md b/source/c10/c10_05.md deleted file mode 100644 index 67386e8..0000000 --- a/source/c10/c10_05.md +++ /dev/null @@ -1,36 +0,0 @@ -# 10.5 Wireshark 抓包教程 - -![](http://image.iswbm.com/20200602135014.png) - - - -## 过滤器 - -过滤器按照过滤器的使用时间点不同,可以分为捕获过滤器和显示过滤器。 - -**1. 网络协议过滤** - -比如 TCP,只显示 TCP 协议,HTTP 只显示 HTTP 协议等。在过滤器输入框中直接输入协议名称即可,不区分大小写。 - -**2. IP 地址过滤** - -如 ip.src == 192.168.1.102 显示源地址为 `192.168.1.102`, -而 ip.dst == 192.168.1.102, 目标地址为 `192.168.1.102`。 - -**3. 端口过滤** - -tcp.port == 80, 端口为 80 的 - -tcp.srcport == 80, 只显示 TCP 协议的原端口为 80 的。 - -**4. Http 模式过滤** - -http.request.method == “GET”,只显示 HTTP GET 方法的。 - -**5. 结合逻辑运算符 AND/OR 组成复杂的表达式** - -AND/OR 也可以写成 `&&` / `||` - - - -`Wireshark` 只能查看封包,而不能修改封包的内容,或者发送封包。 \ No newline at end of file diff --git a/source/c10/c10_05.rst b/source/c10/c10_05.rst deleted file mode 100644 index a42d0f4..0000000 --- a/source/c10/c10_05.rst +++ /dev/null @@ -1,38 +0,0 @@ -10.5 Wireshark 抓包教程 -======================= - -|image0| - -过滤器 ------- - -过滤器按照过滤器的使用时间点不同,可以分为捕获过滤器和显示过滤器。 - -**1. 网络协议过滤** - -比如 TCP,只显示 TCP 协议,HTTP 只显示 HTTP -协议等。在过滤器输入框中直接输入协议名称即可,不区分大小写。 - -**2. IP 地址过滤** - -如 ip.src == 192.168.1.102 显示源地址为 ``192.168.1.102``\ , 而 ip.dst -== 192.168.1.102, 目标地址为 ``192.168.1.102``\ 。 - -**3. 端口过滤** - -tcp.port == 80, 端口为 80 的 - -tcp.srcport == 80, 只显示 TCP 协议的原端口为 80 的。 - -**4. Http 模式过滤** - -http.request.method == “GET”,只显示 HTTP GET 方法的。 - -**5. 结合逻辑运算符 AND/OR 组成复杂的表达式** - -AND/OR 也可以写成 ``&&`` / ``||`` - -``Wireshark`` 只能查看封包,而不能修改封包的内容,或者发送封包。 - -.. |image0| image:: http://image.iswbm.com/20200602135014.png - diff --git a/source/c10/c10_06.md b/source/c10/c10_06.md deleted file mode 100644 index 5859d1f..0000000 --- a/source/c10/c10_06.md +++ /dev/null @@ -1,41 +0,0 @@ -# 10.6 通过比较,学习 TCP 与 UDP - -![](http://image.iswbm.com/20200602135014.png) - -## 1. TCP 与 UDP 的区别 - -**1. 连接** - -TCP 是面向连接的,传输数据前要先经历三次握手建立连接 - -UDP 不需要连接,即刻就可以传输数据 - -**2. 可靠性** - -TCP 能够保证数据可靠地,完整地,无重复的到达对端。 - -而 UDP 并不保证,有可能会丢包 - -**3. 连接场景** - -TCP 的连接,就跟线一样,只有两端,所以只能一对一 通信。 - -UDP 则支持一对一,一对多,多对多的通信。 - -**4. 传输控制** - -TCP 有丢包重传,拥塞控制,流量控制的机制 - -UDP 则没有,就算网络非常拥堵,也不会影响 UDP 的发送速率 - -**5. 首部开销** - -TCP 首部长度较长,且是可变长的,会有一定的开销。 - -UDP 首部固定只有 8 个字节,开销较小。 - - - -[面向报文(UDP)和面向字节流(TCP)的区别](https://blog.csdn.net/ce123_zhouwei/article/details/8976006) - -[TCP缓存区与窗口的关系](https://blog.csdn.net/yxccc_914/article/details/52515558) \ No newline at end of file diff --git a/source/c10/c10_06.rst b/source/c10/c10_06.rst deleted file mode 100644 index 3c1507f..0000000 --- a/source/c10/c10_06.rst +++ /dev/null @@ -1,44 +0,0 @@ -10.6 通过比较,学习 TCP 与 UDP -============================== - -|image0| - -1. TCP 与 UDP 的区别 --------------------- - -**1. 连接** - -TCP 是面向连接的,传输数据前要先经历三次握手建立连接 - -UDP 不需要连接,即刻就可以传输数据 - -**2. 可靠性** - -TCP 能够保证数据可靠地,完整地,无重复的到达对端。 - -而 UDP 并不保证,有可能会丢包 - -**3. 连接场景** - -TCP 的连接,就跟线一样,只有两端,所以只能一对一 通信。 - -UDP 则支持一对一,一对多,多对多的通信。 - -**4. 传输控制** - -TCP 有丢包重传,拥塞控制,流量控制的机制 - -UDP 则没有,就算网络非常拥堵,也不会影响 UDP 的发送速率 - -**5. 首部开销** - -TCP 首部长度较长,且是可变长的,会有一定的开销。 - -UDP 首部固定只有 8 个字节,开销较小。 - -`面向报文(UDP)和面向字节流(TCP)的区别 `__ - -`TCP缓存区与窗口的关系 `__ - -.. |image0| image:: http://image.iswbm.com/20200602135014.png - diff --git a/source/c10/c10_07.md b/source/c10/c10_07.md deleted file mode 100644 index 0978459..0000000 --- a/source/c10/c10_07.md +++ /dev/null @@ -1,478 +0,0 @@ -# 10.7 网络知识扫盲:CSRF 跨域攻击与JWT跨域认证 - -![](http://image.iswbm.com/20200602135014.png) - - - -![](http://image.iswbm.com/20200711143644.png) - -## 1. 什么是跨域请求 - -要明白什么叫跨域请求,首先得知道什么叫域。 - -域,是指由 `协议` + `域名` + `端口号` 组成的一个虚拟概念。 - -![](http://image.iswbm.com/20200705171112.png) - -如果两个域的协议、域名、端口号都一样,就称他们为同域,但是只要有其中一个不一样,就不是同域。 - -那么 `跨域请求` 又是什么意思呢? - -简单来说,就是在一个域内请求了另一个域的资源,由于域不一致,会有安全隐患。 - - - -## 2. 跨域请求的安全隐患 - -有一个词,叫 CSRF (Cross-site request forgery)攻击,中文名是 `跨站请求伪造`。 - -简单来说呢,就是攻击者盗用了你的身份,以你的名义发送恶意请求,它能做的坏事有很多,比如以你的名义发邮件,发消息,购物,盗取帐号等。 - -CSRF 的实际工作原理是怎样的? - -比如现在有两个网站,A 网站是真实受信息的网站,而 B网站是危险网站。 - -当你登陆 A 网站后,浏览器会存储 A 网站服务器给你生成的 sessionid 存入 cookie,有了这个 cookie ,就拥有了你的帐号权限,以后请求资料,就不用再次登陆啦。 - -对于真实用户来说,是便利,可对于攻击者来说,却是可乘之机。 - -![Cookie + Session 方法](http://image.iswbm.com/20200707220426.png) - -他们可以使用各种社工学引导你点击他们的链接/网站,然后利用你的浏览器上存储的 cookie ,然后在自己的 网站B 发起对 网站A 的请求,获取一些隐私信息,做一些侵害用户权益的事情。这便是一个完整的 CSRF 攻击。 - - - -## 3. 跨域请求的安全防御 - -完成一次完整的 CSRF 攻击,只要两个步骤: - -1. 登录受信任网站A,并本地已经存储了 Cookie -2. 在不登出A的情况下,访问危险网站B,网站 B 诱导你发 A 发请求。 - -很多浏览器用户对于网络安全是无意识的,因此我们不能指望通过规范用户行为来避免CSRF攻击。 - -那如何从技术手段规避一定的 CSRF 攻击的风险呢? - -1. 利用浏览器的同源策略:最基础的安全策略 -2. 对请求的来源进行验证:Referer Check -3. 使用验证码强制使用户进行交互确认,保证请求是用户发起 -4. CSRF Token,注意不要使用 cookie 来存储token -5. JSON Web Token - -以上是我知道的历史上用来抵御 CSRF 攻击的方法 - -- 有的虽然实现简单,但是不够安全 - -- 有的虽然安全,但是用户体验不好 - -- 有的虽然安全,用户体验好,但是有缺点 - -具体应该选哪一种呢,不妨继续往下看。 - -### 3.1 同源策略 - -浏览器上有一个同源策略(SOP,全称 Same origin policy),它会在一定程度上禁止这种跨域请求的发生。 - -但同源策略是最基本的安全策略,对于低级的 CSRF 攻击 ,它是很有效果的。 - -可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。 - -同源策略在提升了 Web前端的安全性的同时,也牺牲了Web拓展上的灵活性。 - -设想若把html、js、css、flash,image等文件全部布置在一台服务器上,小网站这样凑活还行,大中网站如果这样做服务器根本受不了的,因此同源策略,就像是双刃剑。不过这些都是有解的。 - -### 3.2 Referer Check - -在 HTTP 协议中,有一个字段叫做 Referer,它记录了HTTP 请求的来源地址。 - -当发生 CSRF 攻击时,这个来源地址,会变成危险网站 B,因此只要在服务端校验这个 Referer 是不是和自己同一个域就可以判断这个请求是跨站请求。 - -![Referer Check 图解](http://image.iswbm.com/20200705193118.png) - -但这种方法,也是有局限性的,在一些非主流的浏览器,或者使用了那些非常古老的浏览器版本,这个 Referer 字段,是有可能会被篡改的。 - -退一步讲,假设你使用了最安全的最新版本的浏览器,这个值无法被篡改,依旧还是有安全隐患。 - -因为有些用户出于某些隐私考虑,会在浏览器设置关闭这个 Referer 字段,也有的网站会使用一些技术手段使用请求不携带 Referer 字段。 - -因此,当你要使用 Referer Check 来做为 防御 CSRF 攻击的主要手段时,请确保你的用户群体使用的一定是最安全的最新版本的浏览器,并且默认用户不会手动关闭 Referer 。 - -### 3.3 加验证码 - -验证码,强制用户必须与应用进行交互,才能完成最终请求。 - -其实加验证码,是能很好遏制 CSRF 攻击,但是网站总不能给所有的操作都加上验证码吧,那样的话,用户估计都跑光光了,因此为了保证用户体验,验证码只能作为一种辅助手段,不能作为主要解决方案。 - -### 3.4 CSRF Token - - CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下,直接利用用户自己的 cookie 来通过安全验证。 - -所以要抵御 CSRF,关键在于要在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中(不然黑客又能拿到了)。 - -业界普遍的防御方案是使用 CSRF Token - -使用 CSRF Token 根据token验证方式的不同,也可以分为两种: - -**第一种**:如图所示 - -![](http://image.iswbm.com/image-20200707221742925.png) - -1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 Token,然后放入HTML表单中传给浏览器,并且存入 session 中。 - -2. 当用户提交表单请求时,表单数据会带上这个 Token 发送给服务端 ; -3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 token 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 - -**第二种**: - -![](http://image.iswbm.com/image-20200707222024941.png) - -1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 Token,然后放入HTML表单中,并且会把这个 Token 放在 cookie 里发给浏览器。 -2. 当用户提交表单请求时,表单数据会带上这个 Token 发送给服务端,并且带上携带 token 的 cookie ; -3. 服务端收到表单请求后,会从表单数据里取出 Token,与 cookie 里的 token 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 - -### 3.5 新增 Header - -使用上面的 CSRF Token 已经可以避免 CSRF 攻击,但是它却有可能又引入了另一个问题。 - -若 CSRF Token 没有使用 cookie,就必须要将 Token 存储在服务端的 Session 中,这样就会面临几个问题 - -1. 服务端每生成一个 Token,都会存放入 session 中,而随着用户请求的增多,服务端的开销会明显增大。 -2. 如果网站有多个子域,分别对应不同的服务器,比如 taobao.com 后台是服务器 a,zhibo.baotao.com 后台是 服务器b, 不同子域要想使用同一个 Token,就要求所有的服务器要能共享这个 Token。一般要有一个中心节点(且应是一个集群)来存储这个Token,这样看下来,架构就变得更加复杂了。 - -想要解决这些问题,可以使用我们接下来要讲的 JWT(全称:JSON Web Token) - -使用了 JWT 后,有了哪些变化呢 - -1. 服务器只负责生成 Token和校验Token,而不再存储Token -2. 将服务器的压力分摊给了所有的客户端。 -3. 服务端的 鉴权不使用 cookie ,而是由新增的 Header 字段:Authorization 里的 JWT 。 - -JWT 是本篇文章重要知识点之一,下面我会详细说说关于 JWT 的内容。 - -## 4. JWT 的工作原理及目的 - -为了让你直观感受 JWT 的工作原理,我画了下面这张图 - -![JWT 工作图解](http://image.iswbm.com/20200705220524.png) - -1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 -2. 后端核对用户名和密码成功后,会计算生成JWT Payload 字符串(具体计算方法,后续会讲),然后返回 response 给浏览器。 -3. 浏览器收到 JWT 后,将其保存在 cookie 里或者 localStorage 或者 sessionStorage 里(具体如何选,后面会说)。 -4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization 字段。 -5. 后端收到新请求后,会使用密钥验证 JWT 签名。 -6. 验证通过后后端使用 JWT 中包含的用户信息进行其他相关操作,返回相应结果。 - -![](http://image.iswbm.com/20200711144042.png) - -JWT 的诞生并不是解决 CSRF 跨域攻击,而是解决跨域认证的难题。 - -举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,这应该如何实现呢? - -一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。 - -另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。 - -JWT 就是这种方案的一个优秀代表。 - -## 5. JWT 如何生成? - -JWT 其实就是一个字符串,比如下面这样 - -```shell -eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ -``` - -仔细观察,会发现它里面有三个 `.` ,以 `.` 为分界,可以将 JWT 分为三部分。 - -![](http://image.iswbm.com/20200705212820.png) - -1. **第一部分**:头部(Header) -2. **第二部分**:载荷(Payload) -3. **第三部分**:签名(Signature) - -![](http://image.iswbm.com/20200705215033.png) - -### 5.1 头部(Header) - -JWT 的头部承载两部分信息: - -- 声明类型:这里是 JWT -- 声明加密的算法:通常直接使用 HMAC SHA256 - -完整的头部就像下面这样的JSON: - -```bash -{ - "typ": "JWT", - "alg": "HS256" -} -``` - -然后将头部进行 Base64URL 算法编码转换,构成了第一部分 - -```shell -eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 -``` - -### 5.2 载荷(Payload) - -载荷,同样也是个 JSON 对象,它是存放有效信息的地方,但不建议存放密码等敏感信息。 - -JWT 规定了7个官方字段,供选用: - -- iss (issuer):签发人 -- exp (expiration time):过期时间 -- sub (subject):主题 -- aud (audience):受众 -- nbf (Not Before):生效时间 -- iat (Issued At):签发时间 -- jti (JWT ID):编号 - -除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。 - -注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。 - -```json -{ - "sub": "1234567890", - "name": "John Doe", - "admin": true -} -``` - -然后将其进行 Base64URL 算法转换,得到 JWT 的第二部分。 - -```shell -eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 -``` - -### 5.3 签名(Signature) - -Signature 部分是对前两部分的签名,防止数据篡改。 - -首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。 - -``` -HMACSHA256( - base64UrlEncode(header) + "." + - base64UrlEncode(payload), - secret) -``` - -算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(`.`)分隔,就可以返回给用户。 - -## 6. 如何手动生成 JWT? - -如果你想手动生成一个 JWT 用于测试,有两种方法 - -**第一种:使用 https://jwt.io/ 这个网站 。** - -我使用前面的 header 和 payload,然后使用 secret 密钥:`Python` - -最后生成的 JWT 结果如下 - -```shell -eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.3wGDum3_A8tAt1bdal5CpYbIUlpHfPQxs96Ijx883kI -``` - -![](http://image.iswbm.com/20200706005103.png) - -**第二种:使用 Python 代码生成** - -首先安装一下 pyjwt 这个库 - -```shell -$ pip install pyjwt -``` - -然后就可以在代码中使用它 - -```python -import jwt -import datetime -import uuid - -salt = 'minggezuishuai' - -# 构造header , 这里不写默认的也是 -headers = { - 'typ': 'JWT', - 'alg': 'HS256' -} - -# 构造payload -payload = { - 'user_id': str(uuid.uuid4()), # 自定义用户ID - 'username': "wangbm", # 自定义用户名 - 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间,取现在时间,五分钟后token失效 -} -token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8') - -# token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8 -``` - -如果你只是测试使用,完全不用写那么多代码,用命令行即可 - -```shell -$ pyjwt --key="minggezuishuai" encode user_id=888f20d9-07ed-41bd-b329-17c6f058a14e username=wangbm exp=+120 -eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 -``` - -## 7. Base64URL 算法 - -前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。 - -JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。 - -## 8. JWT 如何保存? - -关于浏览器应该将 JWT 保存在哪?这个问题,其实也困扰了我很久。 - -如果使用搜索引擎去查,我相信你也一定会被他们绕晕。 - -比如在这篇帖子([When and how to use it](https://blog.logrocket.com/jwt-authentication-best-practices/) )里,作者的观点是,不应该保存在 localstorage 和 session storage,因为这样,第三方的脚本就能直接获取到。 - -作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 - -![](http://image.iswbm.com/image-20200705233446534.png) - -再比如这一篇帖子([JWT(JSON Web Token) : Implementation with Node](https://medium.com/@am_pra_veen/jwt-json-web-token-implementation-with-node-d0661d4c7cbb))提到了要把 JWT 保存到 local-storage。 - -![](http://image.iswbm.com/image-20200705233925900.png) - -因此,我决定不再看网络上关于 『应将 JWT 保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 。 - -JWT 的保存位置,可以分为如下四种 - -1. 保存在 localStorage -2. 保存在 sessionStorage -3. 保存在 cookie -4. 保存在 cookie 并设置 HttpOnly - -第一种和第二种其实可以归为一类,这一类有个特点,就是该域内的 js 脚本都可以读取,这种情况下 JWT 通过 js 脚本放入 Header 里的 Authorization 字段,会存在 XSS 攻击风险。 - -第三种,与第四种相比,区别在于 cookie 有没有标记 HttpOnly,没有标记 HttpOnly 的 cookie ,客户端可以将 JWT 通过 js 脚本放入 Header 里的 Authorization 字段。这么看好像同时存在CSRF 攻击风险和 XSS 攻击风险,实则不然,我们虽然将 JWT 存储在 cookie 里,但是我们的服务端并没有利用 cookie 里的 JWT 直接去鉴权,而是通过 header 里的 Authorization 去鉴权,因此这种方法只有 XSS 攻击风险,而没有 CSRF 攻击风险。 - -而第四种,加了 HttpOnly 标记,意味着这个 cookie 无法通过js脚本进行读取和修改,杜绝了 XSS 攻击的发生。与此同时,网站自身的 js 脚本也无法利用 cookie 设置 header 的Authorization 字段,因此只能通过 cookie 里的 JWT 去鉴权,所以不可避免还是存在 CSRF 攻击风险。 - -如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 - -![](http://image.iswbm.com/image-20200706001903273.png) - -是的,事实也确实如此。 - -所以我的观点是,开发人员应当根据实际情况来选择 JWT 的存储位置。 - -- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF 攻击 -- 而如果访问量/业务量对服务器造成很大压力,或觉得服务器共享 token 对架构要求太高了,那就抛弃CSRF Token 的方式,而改用 JWT。选择了 JWT ,就面临着要将 JWT 存储在哪的问题。 -- 若选择了 JWT ,那么请不要使用 cookie HttpCookie 来存储它,因为使用它还是会有 CSRF 攻击风险。 -- 那另外三种如何选择呢?这三种无论使用哪种,都不可避免有 XSS 攻击风险。我的思路是,XSS 攻击通过其他的手段来规避,这里使用JWT 只有 防御 CSRF 攻击与服务器性能的优化,这两个目标。 -- 那我剩下的三种,我建议是使用 cookie 存储,但不使用 cookie 来鉴权。服务器鉴权还是通过请求里的 Authorization 字段(通过js写入 Header 的)。 - -当然,如果你觉得你通过 `Referer Check` 、`加验证码` 等其他手段,已经可以保证不受 CSRF 攻击的威胁,此时你使用 JWT ,就可以选择使用 JWT + cookie HttpOnly,扼杀 XSS 攻击的可能。 - - - -## 9. JWT 如何发送? - -通过上面第七节的描述,其实我也讲到了 JWT 根据不同场景可以选择两种发送方式 - -- 第一种:将 JWT 放在 Header 里的 `Authorization` 字段,并使用 `Bearer`标注 - -```shell -'Authorization': 'Bearer ' + ${token} -``` - -- 第二种:把 JWT 放入 cookie ,发送给服务端,虽然发送。但是不使用它来鉴权。 - -## 10. JWT 如何校验? - -后端收到请求后,从 Header 中取出 `Authorization` 里的 JWT ,使用之前的签名算法对 header 和 payload 再次计算生成新的签名,并与 JWT 里的签名进行对比,如果一样,说明校验通过,是个合法的 Token。 - -```shell -HMACSHA256( - base64UrlEncode(header) + "." + - base64UrlEncode(payload), - secret) -``` - -验证是个合法的 Token 后,还要检查这个 Token 是否过期,在 JWT 里的 payload 中,有 Token 的过期时间,可以通过它来检查 Token 是否可以用? - -payload 里同时还有用户的相关信息,有了这些信息后,后端就可以知道这是哪个用户的请求了,到这里一切都验证通过,就可以执行相关的业务逻辑了。 - -前面我使用了 `pyjwt` 这个来生成 JWT ,事实上,这个库也可以用来验证 token。 - -使用 jwt 的 decode 会先验签再解码取得 payload 的信息。 - -```python ->>> import jwt ->>> token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8" ->>> jwt.decode(token, 'minggezuishuai', algorithms=['HS256']) -{'user_id': '888f20d9-07ed-41bd-b329-17c6f058a14e', 'username': 'wangbm', 'exp': 1594434326} ->>> -``` - -验签同样也可以使用命令行 - -```shell -$ pyjwt --key="minggezuishuai" decode eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 -{"user_id": "888f20d9-07ed-41bd-b329-17c6f058a14e", "username": "wangbm", "exp": 1594434859} -``` - -如果不想验证签名及有效期,而只是想取下payload,只需加个`--no-verify` 参数即可 - -```shell -$ pyjwt --key="minggezuishuai" decode --no-verify {token} -``` - -更的详细使用方法,可以执行 `pyjwt --help` 学习或者前往官方文档:https://pyjwt.readthedocs.io/en/latest/index.html - -## 11. JWT 的最佳搭配 - -在真正的业务中,是有可能使用 payload 来存放一些用户的敏感信息的,由于 payload 是采用 Base64URL 转换而成,它是可逆的,因此当你在 payload 存放敏感信息时,需要保证 JWT 的安全性,不能让其暴露在 『阳光』下。 - -为此,JWT 最好与 HTTPS 配合使用,利用 HTTPS 的非对称加密来保证 JWT 的安全。 - -具体是如何保障的呢? - -HTTPS 是基于 SSL/TLS 的非对称加密算法工作的。 - -在非对称加密算法的规则下,服务器会拥有一个叫做『私钥』的东西,它是私有的,除了服务器之外,不能再有第二个人知道它。 - -而相对的,所有的客户端(浏览器)同时也会有一个叫做『公钥』的东西,它是对所有人公开的,任何人都可以拥有它。它与『私钥』合称为一个密钥对。 - -公钥和私钥的规则是: - -- **公钥加密的东西,只有私钥能解。**因此如果 JWT 的 payload 里有你的敏感信息,那也不要紧,只要把 JWT 用公钥(**前提是这个公钥得是正确的,下面会说到**)加密一下,那黑客就算拿到了这个密文,也无法解密,因为私钥只有服务器才有。 -- **私钥加密的东西,所有的公钥也都能解。**因此服务器发给客户端的 JWT 的payload 尽量不要有敏感信息。 - -![](http://image.iswbm.com/20200711141903.png) - -那么问题又来了,如果客户端拿到的公钥,是黑客伪造的,客户端拿着这个假公钥加密自己的敏感信息,然后发出去,黑客在拿到这个用自己伪造的公钥加密的数据,非常开心,因为这个公钥对应的私钥在自己手里,自己是可以解密得到里面的数据的。 - -因此如何保证服务器发给客户端(浏览器)的公钥是正确的呢? - -答案是通过**数字证书**来保证。但是由于这个不是本文的重点,因此我将这块内容放在后面的文章详细解释。 - -## 12. 总结写在最后 - -最后,我总结一下,本文的要点: - -1. CSRF 攻击的产生,需要cookie 的『助攻』,否则无法完成。 -2. CRSF 是利用 cookie,而不是盗取 cookie,这点一定要明白。 -3. 但也并不是使用了 cookie 就会有 CSRF 风险,而应该说是用 cookie 去做鉴权才会有 CSRF 风险,参考 CSRF Token (把 token 存储在 cookie 的情况)和 JWT (把 token 存储在 cookie 的情况)。 -4. CSRF Token 和 JWT 虽然都可以做到防御 CSRF 攻击,但其实无论是哪个都无法同时做到防御 CSRF 和 XSS 攻击,在阻止了 CSRF 攻击后, 需要再通过其他手段来减少 XSS 攻击的可能性。 -5. JWT 就是一个由服务端按照一定的规则生成的字符串, -6. JWT 的目的是为了做一个无状态的 session,避免去频繁查询 session,减少了对服务器产生的压力,简化后端架构模型。它的主要用途是解决跨域认证的问题,而解决 CSRF 跨域攻击只是它的附带功能。 -7. payload 是经过 base64URL 算法转换而成的字符串,是可逆的,因此尽量不要存放敏感数据,如若非要存放敏感数据,最好与 HTTPS 协议搭配使用,避免数据泄露。 -8. JWT 的保存位置与方式,没有绝对的方案,具体如何选择要视情况而定。 - -## 13. 参考文章 - -- [咱妈说别乱点链接之浅谈CSRF攻击](https://cloud.tencent.com/developer/article/1004943) -- [JSON Web Token 入门教程](http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html) -- [Where to Store your JWTs – Cookies vs HTML5 Web Storage](https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage) - -- [JWT 超详细分析](https://www.cnblogs.com/DeadBoy/p/11481146.html) - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_07.rst b/source/c10/c10_07.rst deleted file mode 100644 index 8249277..0000000 --- a/source/c10/c10_07.rst +++ /dev/null @@ -1,622 +0,0 @@ -10.7 网络知识扫盲:CSRF 跨域攻击与JWT跨域认证 -============================================= - -|image0| - -|image1| - -1. 什么是跨域请求 ------------------ - -要明白什么叫跨域请求,首先得知道什么叫域。 - -域,是指由 ``协议`` + ``域名`` + ``端口号`` 组成的一个虚拟概念。 - -|image2| - -如果两个域的协议、域名、端口号都一样,就称他们为同域,但是只要有其中一个不一样,就不是同域。 - -那么 ``跨域请求`` 又是什么意思呢? - -简单来说,就是在一个域内请求了另一个域的资源,由于域不一致,会有安全隐患。 - -2. 跨域请求的安全隐患 ---------------------- - -有一个词,叫 CSRF (Cross-site request forgery)攻击,中文名是 -``跨站请求伪造``\ 。 - -简单来说呢,就是攻击者盗用了你的身份,以你的名义发送恶意请求,它能做的坏事有很多,比如以你的名义发邮件,发消息,购物,盗取帐号等。 - -CSRF 的实际工作原理是怎样的? - -比如现在有两个网站,A 网站是真实受信息的网站,而 B网站是危险网站。 - -当你登陆 A 网站后,浏览器会存储 A 网站服务器给你生成的 sessionid 存入 -cookie,有了这个 cookie -,就拥有了你的帐号权限,以后请求资料,就不用再次登陆啦。 - -对于真实用户来说,是便利,可对于攻击者来说,却是可乘之机。 - -.. figure:: http://image.iswbm.com/20200707220426.png - :alt: Cookie + Session 方法 - - Cookie + Session 方法 - -他们可以使用各种社工学引导你点击他们的链接/网站,然后利用你的浏览器上存储的 -cookie ,然后在自己的 网站B 发起对 网站A -的请求,获取一些隐私信息,做一些侵害用户权益的事情。这便是一个完整的 -CSRF 攻击。 - -3. 跨域请求的安全防御 ---------------------- - -完成一次完整的 CSRF 攻击,只要两个步骤: - -1. 登录受信任网站A,并本地已经存储了 Cookie -2. 在不登出A的情况下,访问危险网站B,网站 B 诱导你发 A 发请求。 - -很多浏览器用户对于网络安全是无意识的,因此我们不能指望通过规范用户行为来避免CSRF攻击。 - -那如何从技术手段规避一定的 CSRF 攻击的风险呢? - -1. 利用浏览器的同源策略:最基础的安全策略 -2. 对请求的来源进行验证:Referer Check -3. 使用验证码强制使用户进行交互确认,保证请求是用户发起 -4. CSRF Token,注意不要使用 cookie 来存储token -5. JSON Web Token - -以上是我知道的历史上用来抵御 CSRF 攻击的方法 - -- 有的虽然实现简单,但是不够安全 - -- 有的虽然安全,但是用户体验不好 - -- 有的虽然安全,用户体验好,但是有缺点 - -具体应该选哪一种呢,不妨继续往下看。 - -3.1 同源策略 -~~~~~~~~~~~~ - -浏览器上有一个同源策略(SOP,全称 Same origin -policy),它会在一定程度上禁止这种跨域请求的发生。 - -但同源策略是最基本的安全策略,对于低级的 CSRF 攻击 ,它是很有效果的。 - -可以说 Web -是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。 - -同源策略在提升了 Web前端的安全性的同时,也牺牲了Web拓展上的灵活性。 - -设想若把html、js、css、flash,image等文件全部布置在一台服务器上,小网站这样凑活还行,大中网站如果这样做服务器根本受不了的,因此同源策略,就像是双刃剑。不过这些都是有解的。 - -3.2 Referer Check -~~~~~~~~~~~~~~~~~ - -在 HTTP 协议中,有一个字段叫做 Referer,它记录了HTTP 请求的来源地址。 - -当发生 CSRF 攻击时,这个来源地址,会变成危险网站 -B,因此只要在服务端校验这个 Referer -是不是和自己同一个域就可以判断这个请求是跨站请求。 - -.. figure:: http://image.iswbm.com/20200705193118.png - :alt: Referer Check 图解 - - Referer Check 图解 - -但这种方法,也是有局限性的,在一些非主流的浏览器,或者使用了那些非常古老的浏览器版本,这个 -Referer 字段,是有可能会被篡改的。 - -退一步讲,假设你使用了最安全的最新版本的浏览器,这个值无法被篡改,依旧还是有安全隐患。 - -因为有些用户出于某些隐私考虑,会在浏览器设置关闭这个 Referer -字段,也有的网站会使用一些技术手段使用请求不携带 Referer 字段。 - -因此,当你要使用 Referer Check 来做为 防御 CSRF -攻击的主要手段时,请确保你的用户群体使用的一定是最安全的最新版本的浏览器,并且默认用户不会手动关闭 -Referer 。 - -3.3 加验证码 -~~~~~~~~~~~~ - -验证码,强制用户必须与应用进行交互,才能完成最终请求。 - -其实加验证码,是能很好遏制 CSRF -攻击,但是网站总不能给所有的操作都加上验证码吧,那样的话,用户估计都跑光光了,因此为了保证用户体验,验证码只能作为一种辅助手段,不能作为主要解决方案。 - -3.4 CSRF Token -~~~~~~~~~~~~~~ - -CSRF -攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 -cookie 中,因此黑客可以在不知道这些验证信息的情况下,直接利用用户自己的 -cookie 来通过安全验证。 - -所以要抵御 -CSRF,关键在于要在请求中放入黑客所不能伪造的信息,并且该信息不存在于 -cookie 之中(不然黑客又能拿到了)。 - -业界普遍的防御方案是使用 CSRF Token - -使用 CSRF Token 根据token验证方式的不同,也可以分为两种: - -**第一种**\ :如图所示 - -|image3| - -1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 - Token,然后放入HTML表单中传给浏览器,并且存入 session 中。 - -2. 当用户提交表单请求时,表单数据会带上这个 Token 发送给服务端 ; -3. 服务端收到表单请求后,会从表单数据里取出 Token,然后和 session 里的 - token - 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 - -**第二种**\ : - -|image4| - -1. 当用户请求一个更新用户名的页面时,由服务端生成一个随机数 - Token,然后放入HTML表单中,并且会把这个 Token 放在 cookie - 里发给浏览器。 -2. 当用户提交表单请求时,表单数据会带上这个 Token - 发送给服务端,并且带上携带 token 的 cookie ; -3. 服务端收到表单请求后,会从表单数据里取出 Token,与 cookie 里的 token - 进行对比,如果是一样的,就是合法的用户请求,将新的用户名存入数据库,如果不一样,那就是非法的请求,应当拒绝。 - -3.5 新增 Header -~~~~~~~~~~~~~~~ - -使用上面的 CSRF Token 已经可以避免 CSRF -攻击,但是它却有可能又引入了另一个问题。 - -若 CSRF Token 没有使用 cookie,就必须要将 Token 存储在服务端的 Session -中,这样就会面临几个问题 - -1. 服务端每生成一个 Token,都会存放入 session - 中,而随着用户请求的增多,服务端的开销会明显增大。 -2. 如果网站有多个子域,分别对应不同的服务器,比如 taobao.com - 后台是服务器 a,zhibo.baotao.com 后台是 服务器b, - 不同子域要想使用同一个 Token,就要求所有的服务器要能共享这个 - Token。一般要有一个中心节点(且应是一个集群)来存储这个Token,这样看下来,架构就变得更加复杂了。 - -想要解决这些问题,可以使用我们接下来要讲的 JWT(全称:JSON Web Token) - -使用了 JWT 后,有了哪些变化呢 - -1. 服务器只负责生成 Token和校验Token,而不再存储Token -2. 将服务器的压力分摊给了所有的客户端。 -3. 服务端的 鉴权不使用 cookie ,而是由新增的 Header 字段:Authorization - 里的 JWT 。 - -JWT 是本篇文章重要知识点之一,下面我会详细说说关于 JWT 的内容。 - -4. JWT 的工作原理及目的 ------------------------ - -为了让你直观感受 JWT 的工作原理,我画了下面这张图 - -.. figure:: http://image.iswbm.com/20200705220524.png - :alt: JWT 工作图解 - - JWT 工作图解 - -1. 用户以 Web表单 的形式,将自己的用户名和密码 POST 到后端的接口。 -2. 后端核对用户名和密码成功后,会计算生成JWT Payload - 字符串(具体计算方法,后续会讲),然后返回 response 给浏览器。 -3. 浏览器收到 JWT 后,将其保存在 cookie 里或者 localStorage 或者 - sessionStorage 里(具体如何选,后面会说)。 -4. 后续在该域上发出的请求,都会将 JWT放入HTTP Header 中的 Authorization - 字段。 -5. 后端收到新请求后,会使用密钥验证 JWT 签名。 -6. 验证通过后后端使用 JWT - 中包含的用户信息进行其他相关操作,返回相应结果。 - -|image5| - -JWT 的诞生并不是解决 CSRF 跨域攻击,而是解决跨域认证的难题。 - -举例来说,A 网站和 B -网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,这应该如何实现呢? - -一种解决方案是 session -数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。 - -另一种方案是服务器索性不保存 session -数据了,所有数据都保存在客户端,每次请求都发回服务器。 - -JWT 就是这种方案的一个优秀代表。 - -5. JWT 如何生成? ------------------ - -JWT 其实就是一个字符串,比如下面这样 - -.. code:: shell - - eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ - -仔细观察,会发现它里面有三个 ``.`` ,以 ``.`` 为分界,可以将 JWT -分为三部分。 - -|image6| - -1. **第一部分**\ :头部(Header) -2. **第二部分**\ :载荷(Payload) -3. **第三部分**\ :签名(Signature) - -|image7| - -5.1 头部(Header) -~~~~~~~~~~~~~~~~~~ - -JWT 的头部承载两部分信息: - -- 声明类型:这里是 JWT -- 声明加密的算法:通常直接使用 HMAC SHA256 - -完整的头部就像下面这样的JSON: - -.. code:: bash - - { - "typ": "JWT", - "alg": "HS256" - } - -然后将头部进行 Base64URL 算法编码转换,构成了第一部分 - -.. code:: shell - - eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 - -5.2 载荷(Payload) -~~~~~~~~~~~~~~~~~~~ - -载荷,同样也是个 JSON -对象,它是存放有效信息的地方,但不建议存放密码等敏感信息。 - -JWT 规定了7个官方字段,供选用: - -- iss (issuer):签发人 -- exp (expiration time):过期时间 -- sub (subject):主题 -- aud (audience):受众 -- nbf (Not Before):生效时间 -- iat (Issued At):签发时间 -- jti (JWT ID):编号 - -除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。 - -注意,JWT -默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。 - -.. code:: json - - { - "sub": "1234567890", - "name": "John Doe", - "admin": true - } - -然后将其进行 Base64URL 算法转换,得到 JWT 的第二部分。 - -.. code:: shell - - eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 - -5.3 签名(Signature) -~~~~~~~~~~~~~~~~~~~~~ - -Signature 部分是对前两部分的签名,防止数据篡改。 - -首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 -Header 里面指定的签名算法(默认是 HMAC -SHA256),按照下面的公式产生签名。 - -:: - - HMACSHA256( - base64UrlEncode(header) + "." + - base64UrlEncode(payload), - secret) - -算出签名以后,把 Header、Payload、Signature -三个部分拼成一个字符串,每个部分之间用“点”(\ ``.``\ )分隔,就可以返回给用户。 - -6. 如何手动生成 JWT? ---------------------- - -如果你想手动生成一个 JWT 用于测试,有两种方法 - -**第一种:使用 https://jwt.io/ 这个网站 。** - -我使用前面的 header 和 payload,然后使用 secret 密钥:\ ``Python`` - -最后生成的 JWT 结果如下 - -.. code:: shell - - eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.3wGDum3_A8tAt1bdal5CpYbIUlpHfPQxs96Ijx883kI - -|image8| - -**第二种:使用 Python 代码生成** - -首先安装一下 pyjwt 这个库 - -.. code:: shell - - $ pip install pyjwt - -然后就可以在代码中使用它 - -.. code:: python - - import jwt - import datetime - import uuid - - salt = 'minggezuishuai' - - # 构造header , 这里不写默认的也是 - headers = { - 'typ': 'JWT', - 'alg': 'HS256' - } - - # 构造payload - payload = { - 'user_id': str(uuid.uuid4()), # 自定义用户ID - 'username': "wangbm", # 自定义用户名 - 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间,取现在时间,五分钟后token失效 - } - token = jwt.encode(payload=payload, key=salt, algorithm="HS256", headers=headers).decode('utf-8') - - # token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8 - -如果你只是测试使用,完全不用写那么多代码,用命令行即可 - -.. code:: shell - - $ pyjwt --key="minggezuishuai" encode user_id=888f20d9-07ed-41bd-b329-17c6f058a14e username=wangbm exp=+120 - eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 - -7. Base64URL 算法 ------------------ - -前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 -算法基本类似,但有一些小的不同。 - -JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 -api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL -里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成\_ 。这就是 -Base64URL 算法。 - -8. JWT 如何保存? ------------------ - -关于浏览器应该将 JWT 保存在哪?这个问题,其实也困扰了我很久。 - -如果使用搜索引擎去查,我相信你也一定会被他们绕晕。 - -比如在这篇帖子(\ `When and how to use -it `__ -)里,作者的观点是,不应该保存在 localstorage 和 session -storage,因为这样,第三方的脚本就能直接获取到。 - -作者推荐的做法是,将 JWT 保存在 cookie 里,并设置 HttpOnly。 - -|image9| - -再比如这一篇帖子(\ `JWT(JSON Web Token) : Implementation with -Node `__\ )提到了要把 -JWT 保存到 local-storage。 - -|image10| - -因此,我决定不再看网络上关于 『应将 JWT -保存的哪?』的文章。而是自己思考,以下是我个人观点,不代表一定正确,仅供参考 -。 - -JWT 的保存位置,可以分为如下四种 - -1. 保存在 localStorage -2. 保存在 sessionStorage -3. 保存在 cookie -4. 保存在 cookie 并设置 HttpOnly - -第一种和第二种其实可以归为一类,这一类有个特点,就是该域内的 js -脚本都可以读取,这种情况下 JWT 通过 js 脚本放入 Header 里的 -Authorization 字段,会存在 XSS 攻击风险。 - -第三种,与第四种相比,区别在于 cookie 有没有标记 HttpOnly,没有标记 -HttpOnly 的 cookie ,客户端可以将 JWT 通过 js 脚本放入 Header 里的 -Authorization 字段。这么看好像同时存在CSRF 攻击风险和 XSS -攻击风险,实则不然,我们虽然将 JWT 存储在 cookie -里,但是我们的服务端并没有利用 cookie 里的 JWT 直接去鉴权,而是通过 -header 里的 Authorization 去鉴权,因此这种方法只有 XSS 攻击风险,而没有 -CSRF 攻击风险。 - -而第四种,加了 HttpOnly 标记,意味着这个 cookie -无法通过js脚本进行读取和修改,杜绝了 XSS -攻击的发生。与此同时,网站自身的 js 脚本也无法利用 cookie 设置 header -的Authorization 字段,因此只能通过 cookie 里的 JWT -去鉴权,所以不可避免还是存在 CSRF 攻击风险。 - -如此看来,好像不管哪一种都有弊端,没有一种完美的解决方案。 - -|image11| - -是的,事实也确实如此。 - -所以我的观点是,开发人员应当根据实际情况来选择 JWT 的存储位置。 - -- 当访问量/业务量不是很大时,可以使用 CSRF Token 来防止 CSRF 攻击 -- 而如果访问量/业务量对服务器造成很大压力,或觉得服务器共享 token - 对架构要求太高了,那就抛弃CSRF Token 的方式,而改用 JWT。选择了 JWT - ,就面临着要将 JWT 存储在哪的问题。 -- 若选择了 JWT ,那么请不要使用 cookie HttpCookie - 来存储它,因为使用它还是会有 CSRF 攻击风险。 -- 那另外三种如何选择呢?这三种无论使用哪种,都不可避免有 XSS - 攻击风险。我的思路是,XSS 攻击通过其他的手段来规避,这里使用JWT 只有 - 防御 CSRF 攻击与服务器性能的优化,这两个目标。 -- 那我剩下的三种,我建议是使用 cookie 存储,但不使用 cookie - 来鉴权。服务器鉴权还是通过请求里的 Authorization 字段(通过js写入 - Header 的)。 - -当然,如果你觉得你通过 ``Referer Check`` 、\ ``加验证码`` -等其他手段,已经可以保证不受 CSRF 攻击的威胁,此时你使用 JWT -,就可以选择使用 JWT + cookie HttpOnly,扼杀 XSS 攻击的可能。 - -9. JWT 如何发送? ------------------ - -通过上面第七节的描述,其实我也讲到了 JWT -根据不同场景可以选择两种发送方式 - -- 第一种:将 JWT 放在 Header 里的 ``Authorization`` 字段,并使用 - ``Bearer``\ 标注 - -.. code:: shell - - 'Authorization': 'Bearer ' + ${token} - -- 第二种:把 JWT 放入 cookie - ,发送给服务端,虽然发送。但是不使用它来鉴权。 - -10. JWT 如何校验? ------------------- - -后端收到请求后,从 Header 中取出 ``Authorization`` 里的 JWT -,使用之前的签名算法对 header 和 payload 再次计算生成新的签名,并与 JWT -里的签名进行对比,如果一样,说明校验通过,是个合法的 Token。 - -.. code:: shell - - HMACSHA256( - base64UrlEncode(header) + "." + - base64UrlEncode(payload), - secret) - -验证是个合法的 Token 后,还要检查这个 Token 是否过期,在 JWT 里的 -payload 中,有 Token 的过期时间,可以通过它来检查 Token 是否可以用? - -payload -里同时还有用户的相关信息,有了这些信息后,后端就可以知道这是哪个用户的请求了,到这里一切都验证通过,就可以执行相关的业务逻辑了。 - -前面我使用了 ``pyjwt`` 这个来生成 JWT ,事实上,这个库也可以用来验证 -token。 - -使用 jwt 的 decode 会先验签再解码取得 payload 的信息。 - -.. code:: python - - >>> import jwt - >>> token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8" - >>> jwt.decode(token, 'minggezuishuai', algorithms=['HS256']) - {'user_id': '888f20d9-07ed-41bd-b329-17c6f058a14e', 'username': 'wangbm', 'exp': 1594434326} - >>> - -验签同样也可以使用命令行 - -.. code:: shell - - $ pyjwt --key="minggezuishuai" decode eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8 - {"user_id": "888f20d9-07ed-41bd-b329-17c6f058a14e", "username": "wangbm", "exp": 1594434859} - -如果不想验证签名及有效期,而只是想取下payload,只需加个\ ``--no-verify`` -参数即可 - -.. code:: shell - - $ pyjwt --key="minggezuishuai" decode --no-verify {token} - -更的详细使用方法,可以执行 ``pyjwt --help`` -学习或者前往官方文档:https://pyjwt.readthedocs.io/en/latest/index.html - -11. JWT 的最佳搭配 ------------------- - -在真正的业务中,是有可能使用 payload 来存放一些用户的敏感信息的,由于 -payload 是采用 Base64URL 转换而成,它是可逆的,因此当你在 payload -存放敏感信息时,需要保证 JWT 的安全性,不能让其暴露在 『阳光』下。 - -为此,JWT 最好与 HTTPS 配合使用,利用 HTTPS 的非对称加密来保证 JWT -的安全。 - -具体是如何保障的呢? - -HTTPS 是基于 SSL/TLS 的非对称加密算法工作的。 - -在非对称加密算法的规则下,服务器会拥有一个叫做『私钥』的东西,它是私有的,除了服务器之外,不能再有第二个人知道它。 - -而相对的,所有的客户端(浏览器)同时也会有一个叫做『公钥』的东西,它是对所有人公开的,任何人都可以拥有它。它与『私钥』合称为一个密钥对。 - -公钥和私钥的规则是: - -- **公钥加密的东西,只有私钥能解。**\ 因此如果 JWT 的 payload - 里有你的敏感信息,那也不要紧,只要把 JWT - 用公钥(\ **前提是这个公钥得是正确的,下面会说到**\ )加密一下,那黑客就算拿到了这个密文,也无法解密,因为私钥只有服务器才有。 -- **私钥加密的东西,所有的公钥也都能解。**\ 因此服务器发给客户端的 JWT - 的payload 尽量不要有敏感信息。 - -|image12| - -那么问题又来了,如果客户端拿到的公钥,是黑客伪造的,客户端拿着这个假公钥加密自己的敏感信息,然后发出去,黑客在拿到这个用自己伪造的公钥加密的数据,非常开心,因为这个公钥对应的私钥在自己手里,自己是可以解密得到里面的数据的。 - -因此如何保证服务器发给客户端(浏览器)的公钥是正确的呢? - -答案是通过\ **数字证书**\ 来保证。但是由于这个不是本文的重点,因此我将这块内容放在后面的文章详细解释。 - -12. 总结写在最后 ----------------- - -最后,我总结一下,本文的要点: - -1. CSRF 攻击的产生,需要cookie 的『助攻』,否则无法完成。 -2. CRSF 是利用 cookie,而不是盗取 cookie,这点一定要明白。 -3. 但也并不是使用了 cookie 就会有 CSRF 风险,而应该说是用 cookie - 去做鉴权才会有 CSRF 风险,参考 CSRF Token (把 token 存储在 cookie - 的情况)和 JWT (把 token 存储在 cookie 的情况)。 -4. CSRF Token 和 JWT 虽然都可以做到防御 CSRF - 攻击,但其实无论是哪个都无法同时做到防御 CSRF 和 XSS 攻击,在阻止了 - CSRF 攻击后, 需要再通过其他手段来减少 XSS 攻击的可能性。 -5. JWT 就是一个由服务端按照一定的规则生成的字符串, -6. JWT 的目的是为了做一个无状态的 session,避免去频繁查询 - session,减少了对服务器产生的压力,简化后端架构模型。它的主要用途是解决跨域认证的问题,而解决 - CSRF 跨域攻击只是它的附带功能。 -7. payload 是经过 base64URL - 算法转换而成的字符串,是可逆的,因此尽量不要存放敏感数据,如若非要存放敏感数据,最好与 - HTTPS 协议搭配使用,避免数据泄露。 -8. JWT 的保存位置与方式,没有绝对的方案,具体如何选择要视情况而定。 - -13. 参考文章 ------------- - -- `咱妈说别乱点链接之浅谈CSRF攻击 `__ -- `JSON Web Token - 入门教程 `__ -- `Where to Store your JWTs – Cookies vs HTML5 Web - Storage `__ - -- `JWT 超详细分析 `__ - -|image13| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200711143644.png -.. |image2| image:: http://image.iswbm.com/20200705171112.png -.. |image3| image:: http://image.iswbm.com/image-20200707221742925.png -.. |image4| image:: http://image.iswbm.com/image-20200707222024941.png -.. |image5| image:: http://image.iswbm.com/20200711144042.png -.. |image6| image:: http://image.iswbm.com/20200705212820.png -.. |image7| image:: http://image.iswbm.com/20200705215033.png -.. |image8| image:: http://image.iswbm.com/20200706005103.png -.. |image9| image:: http://image.iswbm.com/image-20200705233446534.png -.. |image10| image:: http://image.iswbm.com/image-20200705233925900.png -.. |image11| image:: http://image.iswbm.com/image-20200706001903273.png -.. |image12| image:: http://image.iswbm.com/20200711141903.png -.. |image13| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c10/c10_08.md b/source/c10/c10_08.md deleted file mode 100644 index 5b157be..0000000 --- a/source/c10/c10_08.md +++ /dev/null @@ -1,365 +0,0 @@ -# 10.8 数字证书、签名到底是什么? - -![](http://image.iswbm.com/20200602135014.png) - -我们都知道 HTTP 协议都是明文传输内容,为了保证数据传输的安全,HTTPS 协议就应运而生了,但它其实并不是一个全新的协议,而是HTTP 协议基本之上 再加上SSL/TLS 协议。 - -因此当你访问一个支持 https 的网站时,是需要先进行 SSL/TLS 握手建立连接的。 - -SSL/TLS 握手的目的是为了 **安全** 地协商出一份**对称加密**的密钥,有了这个密钥之后,后续的数据全部使用这个密钥进行加密。 - -这个过程其实挺有趣的,涉及到的知识点,专业名词也很多,比如对称加密,非对称加密,信息摘要,数字签名,数字证书,公钥和私钥。本篇文章,会详细地介绍这些极易混淆的专业名词。 - -在讲解之前,我先给你提出几个摸底问题,如果你已经还不能够熟练回答,那么本篇文章会给你答案: - -1、 对称加密和非对称加密,各有什么优缺点? - -2、对称加密和非对称加密,是排他关系吗?是否可以搭配使用? - -3、摘要和加密有什么区别?有了摘要算法为什么还要有加密算法? - -4、如何在通信时同时做到保密性、高效性? - -5、如何申请数字证书?证书分为哪几种?SSL 证书如何部署? - -6、完整说下证书申请、证书签发、证书下发客户端、客户端验证证书、数据加密传输的整个流程? - -## 1. 对称加密与非对称加密 - -### 对称加密 - -`对称加密`是通信双方共同拥有一把密钥。 - -这把密钥可以把明文加密(encryption)成密文,也可以把密文解密(decryption)成明文。 - -常见的对称加密算法有AES、DES、RC4,其中最常用的是AES。 - -对称加密的优点是:速度快。 - -同时也有一个缺点,就是不那么安全,一旦你的密钥被别人窃取了,所有的数据就会在网络的世界里裸奔。 - -### 非对称加密 - -与 对称加密相对的是 `非对称加密`。 - -通信双方持有不同的密钥。 - -服务端的密钥,称之为 `私钥`(private key),客户端的密钥,称之为 `公钥` (public key)。 - -他们二者的区别是: - -1、私钥应仅在服务端保存,绝不可泄露。而公钥可以存在于任何的客户端,即使黑客拿到了也没有关系。 - -2、公钥加密的密文只有相对应的私钥才能解密 - -![](http://image.iswbm.com/image-20200723233619429.png) - -3、私钥加密的内容,所有与之相对应的公钥都能解密。 - -4、公钥通常用来生成签名,私钥用来验证签名。 - -![](http://image.iswbm.com/image-20200724121333485.png) - -5、公钥和私钥是相对的,两者本身并没有规定哪一个必须是公钥或私钥。这意味着,公钥只要不对外公开,那就可以做为私钥,私钥公开后也可以做为公钥。 - -典型的非对称加密算法有 RSA 。 - -非对称加密的优点,就是安全系数特别高。缺点就是速度会慢一些。 - -### 对称与非对称加密结合 - -当客户端收到的公钥是准确的时候,通信就是安全的。 - -因为用正确公钥加密过的密文,只有服务端的私钥能解。 - -那么如何保证,客户端收到正确的公钥呢? - -答案是:通过非对称加密来协商对称加密的密钥,服务端一旦把正确的公钥安全地送达到客户端后,后续的通信,为了保证高效通信,再采用对称加密来加密数据。 - -具体的过程,后面会更加详细的阐述这一过程。 - - - -## 2. 摘要、签名、证书是啥? - -### 信息摘要 - -一段信息,经过摘要算法得到一串哈希值,就是摘要(dijest)。 - -常见的摘要算法有MD5、SHA1、SHA256、SHA512等。 - -关于摘要,有几点需要你明白的: - -1、摘要算法,是把任意长度的信息,映射成一个定长的字符串。 - -2、摘要算法,两个不同的信息,是有可能算出同一个摘要值的。 - -3、摘要算法与加密算法不同,不存在解密的过程。 - -4、摘要算法不用于数据的保密,而是用于数据的完整性校验。 - -### 数字签名 - -摘要经过私钥的加密后,便有了一个新的名字 -- `数字签名`。 - -`签名` 是在发送方,这是一个加密的过程。 - -`验签` 是在接收方,这是一个解密的过程。 - -那搞懂数字签名的意义是什么?只要回答下面两个问题即可。 - -**第一个问题,有了信息摘要,为何还要有数字签名?** - -答:信息摘要,虽然也不可逆,但却容易却被伪造。所以信息摘要只用于校验完整性,而要保证信息摘要的正确性,就要依靠数字签名啦。 - -数字签名的签名和验签是非对称加密,其他人除非拿到私钥,不然没法伪造。 - -**第二个问题,为什么不对内容直接加密,而是对摘要进行加密。** - -答:由上面我们知道了非对称加密的速度非常慢,如果传输的数据量非常大,那这个加密再解密的时间要远比网络传输的时间来得长,这样反而会得不偿失。 - -如果我们对传输的内容只有完整性要求,而安全性没有要求(意思是传输的内容被人知道了也没关系)。那就可以对摘要进行加密,到客户端这里解密后得到摘要明文,再用这个摘要明文与传输的数据二次计算的摘要进行比较,若一致,则说明传输的内容是完整的,没有被篡改。 - -### 数字证书 - -在数字签名那里,不知道你有没有发现一个问题? - -数字签名是非对称加密,服务端有一个私钥,客户端一个公钥,只有这两个对上了验签。 - -那假如说你(客户端)拿到的公钥并不是服务端给的呢,而是黑客塞给你的呢?而你却把这个假公钥当成真的,那么当你使用这个假公钥加密一些敏感信息时,黑客就可以截取你的这段信息,由于这信息是用黑客自己的公钥加密的,这样一来,黑客拿自己的私钥就能解密得到你的敏感信息。 - -这就是问题所在。 - -要解决这个问题,其实只要保证『公钥』是可信的。只有服务端发给你的公钥你才能拿,而坏人给你的公钥,你要懂得识别并丢弃它。 - -数字证书就应运而生了。 - -要理解数字证书,同样只要搞懂两个问题即可。 - -1. 数字证书是什么东西?其实它就是一个 `.crt` 文件 -2. 数字证书是谁颁发的?由权威证书认证机构颁发,一般我们简称为 CA 机构 - -2. 数字证书如何申请的?或者说如何颁发的? - -为了让你理解这一过程,我画了下面这张图: - -![](http://image.iswbm.com/20200724124502.png) - -1、在自己的服务器上生成一对公钥和私钥。然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等其他信息整合在一起,生成`.csr` 文件。 - -2、将这个 `.csr` 文件发给 CA 机构,CA 机构收到申请后,会通过各种手段验证申请者的组织信息和个人信息,如无异常(组织存在,企业合法,确实是域名的拥有者),CA 就会使用散列算法对`.csr`里的明文信息先做一个HASH,得到一个信息摘要,再用 CA 自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的 **签名**。签名 + `.csr` 明文信息,即是 **证书**。CA 把这个证书返回给申请人 - - - -## 3. 数字证书(Certificate) - -在HTTPS的传输过程中,有一个非常关键的角色--`数字证书`,那什么是数字证书?又有什么作用呢? - -所谓数字证书,是一种用于电脑的身份识别机制。由数字证书颁发机构(CA)对使用私钥创建的签名请求文件做的签名(盖章),表示CA结构对证书持有者的认可。 - -### 3.1 数字证书拥有以下几个优点 - -- 使用数字证书能够提高用户的可信度; -- 数字证书中的公钥,能够与服务端的私钥配对使用,实现数据传输过程中的加密和解密; -- 在证认使用者身份期间,使用者的敏感个人数据并不会被传输至证书持有者的网络系统上; - -### 2.2. 证书类型 - -x509的证书编码格式有两种: - -1. PEM(Privacy-enhanced Electronic Mail)是明文格式的,以 -----BEGIN CERTIFICATE-----开头,已-----END CERTIFICATE-----结尾。中间是经过base64编码的内容,apache需要的证书就是这类编码的证书.查看这类证书的信息的命令为: `openssl x509 -noout -text -in server.pem`。其实PEM就是把DER的内容进行了一次base64编码 -2. DER是二进制格式的证书,查看这类证书的信息的命令为: `openssl x509 -noout -text -inform der -in server.der` - -### 2.3. 扩展名 - -- .crt证书文件,可以是DER(二进制)编码的,也可以是PEM(ASCII (Base64))编码的),在类unix系统中比较常见; -- .cer也是证书,常见于Windows系统。编码类型同样可以是DER或者PEM的,windows下有工具可以转换crt到cer; -- .csr证书签名请求文件,一般是生成请求以后发送给CA,然后CA会给您签名并发回证书 -- .key一般公钥或者密钥都会用这种扩展名,可以是DER编码的或者是PEM编码的。查看DER编码的(公钥或者密钥)的文件的命令为: `openssl rsa -inform DER -noout -text -in xxx.key`。查看PEM编码的(公钥或者密钥)的文件的命令为: `openssl rsa -inform PEM -noout -text -in xxx.key`; -- .p12证书文件,包含一个X509证书和一个被密码保护的私钥 - -### 2.4 证书的种类 - -安全证书主要分为DV、OV和EV三个种类,对应的安全等级为一般、较好和最高三个等级。三者的审核过程、审核标准和对应的域名数量也不同,所以价格在一两百元到几万元不等。 - -#### DV SSL - -DV SSL证书是只验证网站域名所有权的简易型(Class 1级)SSL证书,可10分钟快速颁发,能起到加密传输的作用,但无法向用户证明网站的真实身份。 - -目前市面上的免费证书都是这个类型的,只是提供了对数据的加密,但是对提供证书的个人和机构的身份不做验证。 - -#### OV SSL - -OV SSL,提供加密功能,对申请者做严格的身份审核验证,提供可信×××明。 - -和DV SSL的区别在于,OV SSL 提供了对个人或者机构的审核,能确认对方的身份,安全性更高。 - -所以这部分的证书申请是收费的~ - -#### EV SSL - -超安=EV=最安全、最严格 超安EV SSL证书遵循全球统一的严格身份验证标准,是目前业界安全级别最高的顶级 (Class 4级)SSL证书。 - -金融证券、银行、第三方支付、网上商城等,重点强调网站安全、企业可信形象的网站,涉及交易支付、客户隐私信息和账号密码的传输。 - -这部分的验证要求最高,申请费用也是最贵的。 - -选择签发机构时,最好选择行业认可的全球范围内都可以使用的ca机构签发的证书。目前我们国内的证书能够符合标准的还不是特别多,主要原因是有一些证书不能够被国外的浏览器所认可,在使用的时候需要进行一定的额外操作。 - - - ---- - - - -根据保护域名的数量需求,SSL证书又分为: - -**单域名版:**只保护一个域名,例如 [www.abc.com](http://www.abc.com/) 或者 login.abc.com 之类的单个域名 - -**多域名版:**一张证书可以保护多个域名,例如同时保护 [www.abc.com](http://www.abc.com/) , [www.bcd.com,](https://blog.csdn.net/) pay.efg.com 等 - -**通配符版:**一张证书保护同一个主域名下同一级的所有子域名,不限个数,形如 *.abc.com 。注意,通配符版只有 DVSSL 和 OVSSL 具有, EVSSL 不具有通配符版本。 - - - -### 2.5 证书在哪里 - -当你在下载并安装浏览器时,浏览器内部其实已经内嵌了全世界公认的根证书颁发机构的证书。 - -若一个网站的数字证书的证书颁发机构在浏览器中没有,则需要引导用户自行导入。 - -如果你想在 Chrome 中查看有哪些受信任的证书颁发机构,可以点击 `设置` -> `隐私设置与安全性` -> `安全` -> `管理证书` - -![](http://image.iswbm.com/20200717222206.png) - -### 2.6 证书里的信息 - -在上图的位置里,随便双击点开一个证书,就可以查看证书里的内容。 - -内容非常多,最主要的有 - -- 证书是哪个机构的? -- 证书里的公钥是什么? -- 证书有效期是什么时候? -- 采用的哪种加解密的算法? - -### 2.7 证书吊销 - -证书是有生命周期的,如果证书的私钥泄漏了那这个证书就得吊销,一般有两种吊销方式:CRL和OCSP。 - -CRL( Certificate Revocation List)是CA机构维护的一个已经被吊销的证书序列号列表,浏览器需要定时更新这个列表,浏览器在验证证书合法性的时候也会在证书吊销列表中查询是否已经被吊销,如果被吊销了那这个证书也是不可信的。可以看出,这个列表随着被吊销证书的增加而增加,列表会越来越大,浏览器还需要定时更新,实时性也比较差。 - -所以,后来就有了 OCSP (Online Certificate Status Protocol)在线证书状态协议,这个协议就是解决了 CRL 列表越来越大和实时性差的问题而生的。有了这个协议,浏览器就可以不用定期更新CRL了,在验证证书的时候直接去CA服务器实时校验一下证书有没有被吊销就可以,是解决了CRL的问题,但是每次都要去CA服务器上校验也会很慢,在网络环境较差的时候或者跨国访问的时候,体验就非常差了,OCSP虽然解决了CRL的问题但是性能却很差。 - - - -如果你想了解更多关于证书的内容,可以前往华为云的公开文档进行学习:https://support.huaweicloud.com/scm_faq/scm_01_0020.html - -## 4. 如何生成 CSR 文件 - -CSR是Certificate Signing Request的英文缩写,即证书签名请求文件。 - -当申请者申请数字证书时,CSP(加密服务提供者)生成私钥,同时也生成了CSR文件。申请者将CSR文件提交至Certificate Authority (CA)机构后,CA机构使用其根证书私钥签名,从而就生成了数字证书。 - -申请者通过CSR文件,向CA机构申请数字证书。获取证书后,就能证明申请者的网站是可信的,数据传输是加密的。 - -接下来来了解一下,CSR 文件是如何生成的? - -### 使用 OpenSSL 生成 - -假设申请的域名为 **python.iswbm.com**,公司名称为**派森时光科技**,部门是**IT部**,公司在**中国广东省深圳市**。可通过运行下方命令行生成CSR文件: - -```shell -$ openssl req -new –SHA256 -newkey rsa:2048 -nodes -keyout python.iswbm.com.key -out python.iswbm.com.csr -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=派森时光科技/OU=IT/CN=python.iswbm.com" -``` - -此命令行表示: - -- `req`参数:表示证书请求request,用于生成CSR文件。 -- `SHA256`参数:表示CSR签名时用的摘要算法。 -- `newkey`参数:表示指定证书的算法。参数 2048:表示密钥对的长度。 -- `nodes`参数:表示不对私钥加密。 -- `keyout`参数:表示生成的私钥文件。名为`iswbm.key`的私钥文件,需自行保管,用于获取证书后的部署过程。 -- `out`参数:表示生成的 CSR 文件。名为`iswbm.com.csr`的CSR文件,用于提交至CA机构验证信息,从而获取证书。 -- `subj`参数:表示CSR信息,具体有哪些参数,可以继续往下看。 - -subj参数说明: - -- `C`:Country,表示国家,申请者或申请企业所在国家的英文或两位大写国家代码。如:CN -- `ST`:State/Province,表示省份,申请者或申请企业所在地的省/市/自治区英文或拼音全称。如:Guangdong -- `L`:Locality,表示城市,申请者或申请企业所在城市的英文或拼音全称。如:Shenzhen -- `O`:Organization,表示申请者的姓名或申请企业的名称。 -- `OU`:Organizational Unit,表示申请人所在部门的英文或拼音全称。如:IT -- `CN`:Common Name,表示你要为哪个域名申请证书,可是单域名(比如 python.iswbm.com),也可以是泛域名(*.iswbm.com),也可以是为多个域名申请一个证书(具体我没操作过)。 - -上一条命令执行完后,会在你的本地目录下生成俩文件 - -- python.iswbm.com.csr:用于向CA机构申请证书 - -```shell -$ cat python.iswbm.com.csr ------BEGIN CERTIFICATE REQUEST----- -MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ2Rvbmcx -ETAPBgNVBAcMCFNoZW56aGVuMS0wKwYDVQQKDCTDpsK0wr7DpsKjwq7DpsKXwrbD -... -7lgB4QC1aIFz8gi9TGMJU2LqTDJCj+tgM68LDBdMLeQ8XZ33C95Nl0qt7yG+zjlZ -01jBh+T882r8x9gKdwb7nZSWFQY4/YTq+sY++YW/QuCNRcJ2vbM18U/HlIRsZ3su -x6Neh08= ------END CERTIFICATE REQUEST----- -``` - -- python.iswbm.com.key:私钥,自行保存,不要外泄 - -```shell -$ cat python.iswbm.com.key ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4OrcM9hTs9Hao -SzjsVJFX2Mmd+mToMG3u++o2Fd5yrPYq4COkT33lnL9kJNrDWqGp5TRkWqNwLaPl -... -a/lKBWLcvxE+IQ+mxNbN058kEJ3l8WAcAFCebLm5czUqmIVa3JR+cBDLvGFZVn6z -72AP5D/Evds4BOO+VzAiVLU6Ai78qhACuVExZNQCxdvJy4LxpeckUpCem9hAPiIY -LQfiTStBBU6t/+mnDyij+XreGQ== ------END PRIVATE KEY----- -``` - - - -### 使用在线生成工具 - -使用 OpenSSL 工具生成 CSR 文件的方法固然简单,但使用时,需要你的了解代码中参数的意思。 - -如果你不想花心思去记这些,推荐你使用 [CSR在线生成工具](https://myssl.com/csr_create.html)(https://myssl.com/csr_create.html) - -你只需简单地输入如下信息,再点击 `OpenSSL生成`,你就可以获得一条 OpenSSL 命令,这下你再也不用自己拼凑参数啦,真的太方便了。为什么不点击 `生成` 让其直接生成 私钥文件 和 CSR文件 呢?当然是为了安全起见啦。 - -![](http://image.iswbm.com/20200718100052.png) - - - -## 5. TLS/SSL 保证信息的安全 - -在信息安全性问题中,我们常常要做到三点才能保证信息的安全: - -1. 信息的保密性 -2. 信息的完整性 -3. 身份识别 - -将这三者结合起来,就是 TLS/SSL 做的事情 - -![](http://image.iswbm.com/1169376-20191008172456510-1302410435.png) - - - -1、客户端(浏览器)向服务端发出请求,服务端返回证书给客户端。 - -2、客户端拿到证书后,把证书里的签名与及明文信息分别取出来,然后会用自身携带的CA机构的公钥去解密签名,然后信息摘要1,然后再对明文信息进行HASH,得到一个信息摘要2,对比信息摘要1 和信息摘要2,如果一样,说明证书是合法的,也就是证书里的公钥是正确的。 - -以上采用的是非对称加密(CA的公钥和私钥),保证了客户端接收到服务端正确的公钥,有了服务端的公钥后,后面的信息加密都可以使用这个公钥,而用这个公钥加密过后的密文,只有服务端的私钥能解,就算黑客拿到了也没法解开。 - - - -## 参考文章 - -- [HTTPS中CA证书的签发及使用过程](https://www.cnblogs.com/xdyixia/p/11610102.html) - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_08.rst b/source/c10/c10_08.rst deleted file mode 100644 index 90ecf3c..0000000 --- a/source/c10/c10_08.rst +++ /dev/null @@ -1,422 +0,0 @@ -10.8 数字证书、签名到底是什么? -=============================== - -|image0| - -我们都知道 HTTP 协议都是明文传输内容,为了保证数据传输的安全,HTTPS -协议就应运而生了,但它其实并不是一个全新的协议,而是HTTP 协议基本之上 -再加上SSL/TLS 协议。 - -因此当你访问一个支持 https 的网站时,是需要先进行 SSL/TLS -握手建立连接的。 - -SSL/TLS 握手的目的是为了 **安全** -地协商出一份\ **对称加密**\ 的密钥,有了这个密钥之后,后续的数据全部使用这个密钥进行加密。 - -这个过程其实挺有趣的,涉及到的知识点,专业名词也很多,比如对称加密,非对称加密,信息摘要,数字签名,数字证书,公钥和私钥。本篇文章,会详细地介绍这些极易混淆的专业名词。 - -在讲解之前,我先给你提出几个摸底问题,如果你已经还不能够熟练回答,那么本篇文章会给你答案: - -1、 对称加密和非对称加密,各有什么优缺点? - -2、对称加密和非对称加密,是排他关系吗?是否可以搭配使用? - -3、摘要和加密有什么区别?有了摘要算法为什么还要有加密算法? - -4、如何在通信时同时做到保密性、高效性? - -5、如何申请数字证书?证书分为哪几种?SSL 证书如何部署? - -6、完整说下证书申请、证书签发、证书下发客户端、客户端验证证书、数据加密传输的整个流程? - -1. 对称加密与非对称加密 ------------------------ - -对称加密 -~~~~~~~~ - -``对称加密``\ 是通信双方共同拥有一把密钥。 - -这把密钥可以把明文加密(encryption)成密文,也可以把密文解密(decryption)成明文。 - -常见的对称加密算法有AES、DES、RC4,其中最常用的是AES。 - -对称加密的优点是:速度快。 - -同时也有一个缺点,就是不那么安全,一旦你的密钥被别人窃取了,所有的数据就会在网络的世界里裸奔。 - -非对称加密 -~~~~~~~~~~ - -与 对称加密相对的是 ``非对称加密``\ 。 - -通信双方持有不同的密钥。 - -服务端的密钥,称之为 ``私钥``\ (private key),客户端的密钥,称之为 -``公钥`` (public key)。 - -他们二者的区别是: - -1、私钥应仅在服务端保存,绝不可泄露。而公钥可以存在于任何的客户端,即使黑客拿到了也没有关系。 - -2、公钥加密的密文只有相对应的私钥才能解密 - -|image1| - -3、私钥加密的内容,所有与之相对应的公钥都能解密。 - -4、公钥通常用来生成签名,私钥用来验证签名。 - -|image2| - -5、公钥和私钥是相对的,两者本身并没有规定哪一个必须是公钥或私钥。这意味着,公钥只要不对外公开,那就可以做为私钥,私钥公开后也可以做为公钥。 - -典型的非对称加密算法有 RSA 。 - -非对称加密的优点,就是安全系数特别高。缺点就是速度会慢一些。 - -对称与非对称加密结合 -~~~~~~~~~~~~~~~~~~~~ - -当客户端收到的公钥是准确的时候,通信就是安全的。 - -因为用正确公钥加密过的密文,只有服务端的私钥能解。 - -那么如何保证,客户端收到正确的公钥呢? - -答案是:通过非对称加密来协商对称加密的密钥,服务端一旦把正确的公钥安全地送达到客户端后,后续的通信,为了保证高效通信,再采用对称加密来加密数据。 - -具体的过程,后面会更加详细的阐述这一过程。 - -2. 摘要、签名、证书是啥? -------------------------- - -信息摘要 -~~~~~~~~ - -一段信息,经过摘要算法得到一串哈希值,就是摘要(dijest)。 - -常见的摘要算法有MD5、SHA1、SHA256、SHA512等。 - -关于摘要,有几点需要你明白的: - -1、摘要算法,是把任意长度的信息,映射成一个定长的字符串。 - -2、摘要算法,两个不同的信息,是有可能算出同一个摘要值的。 - -3、摘要算法与加密算法不同,不存在解密的过程。 - -4、摘要算法不用于数据的保密,而是用于数据的完整性校验。 - -数字签名 -~~~~~~~~ - -摘要经过私钥的加密后,便有了一个新的名字 – ``数字签名``\ 。 - -``签名`` 是在发送方,这是一个加密的过程。 - -``验签`` 是在接收方,这是一个解密的过程。 - -那搞懂数字签名的意义是什么?只要回答下面两个问题即可。 - -**第一个问题,有了信息摘要,为何还要有数字签名?** - -答:信息摘要,虽然也不可逆,但却容易却被伪造。所以信息摘要只用于校验完整性,而要保证信息摘要的正确性,就要依靠数字签名啦。 - -数字签名的签名和验签是非对称加密,其他人除非拿到私钥,不然没法伪造。 - -**第二个问题,为什么不对内容直接加密,而是对摘要进行加密。** - -答:由上面我们知道了非对称加密的速度非常慢,如果传输的数据量非常大,那这个加密再解密的时间要远比网络传输的时间来得长,这样反而会得不偿失。 - -如果我们对传输的内容只有完整性要求,而安全性没有要求(意思是传输的内容被人知道了也没关系)。那就可以对摘要进行加密,到客户端这里解密后得到摘要明文,再用这个摘要明文与传输的数据二次计算的摘要进行比较,若一致,则说明传输的内容是完整的,没有被篡改。 - -数字证书 -~~~~~~~~ - -在数字签名那里,不知道你有没有发现一个问题? - -数字签名是非对称加密,服务端有一个私钥,客户端一个公钥,只有这两个对上了验签。 - -那假如说你(客户端)拿到的公钥并不是服务端给的呢,而是黑客塞给你的呢?而你却把这个假公钥当成真的,那么当你使用这个假公钥加密一些敏感信息时,黑客就可以截取你的这段信息,由于这信息是用黑客自己的公钥加密的,这样一来,黑客拿自己的私钥就能解密得到你的敏感信息。 - -这就是问题所在。 - -要解决这个问题,其实只要保证『公钥』是可信的。只有服务端发给你的公钥你才能拿,而坏人给你的公钥,你要懂得识别并丢弃它。 - -数字证书就应运而生了。 - -要理解数字证书,同样只要搞懂两个问题即可。 - -1. 数字证书是什么东西?其实它就是一个 ``.crt`` 文件 -2. 数字证书是谁颁发的?由权威证书认证机构颁发,一般我们简称为 CA 机构 - -3. 数字证书如何申请的?或者说如何颁发的? - -为了让你理解这一过程,我画了下面这张图: - -|image3| - -1、在自己的服务器上生成一对公钥和私钥。然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等其他信息整合在一起,生成\ ``.csr`` -文件。 - -2、将这个 ``.csr`` 文件发给 CA 机构,CA -机构收到申请后,会通过各种手段验证申请者的组织信息和个人信息,如无异常(组织存在,企业合法,确实是域名的拥有者),CA -就会使用散列算法对\ ``.csr``\ 里的明文信息先做一个HASH,得到一个信息摘要,再用 -CA 自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的 -**签名**\ 。签名 + ``.csr`` 明文信息,即是 **证书**\ 。CA -把这个证书返回给申请人 - -3. 数字证书(Certificate) ------------------------- - -在HTTPS的传输过程中,有一个非常关键的角色–\ ``数字证书``\ ,那什么是数字证书?又有什么作用呢? - -所谓数字证书,是一种用于电脑的身份识别机制。由数字证书颁发机构(CA)对使用私钥创建的签名请求文件做的签名(盖章),表示CA结构对证书持有者的认可。 - -3.1 数字证书拥有以下几个优点 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- 使用数字证书能够提高用户的可信度; -- 数字证书中的公钥,能够与服务端的私钥配对使用,实现数据传输过程中的加密和解密; -- 在证认使用者身份期间,使用者的敏感个人数据并不会被传输至证书持有者的网络系统上; - -2.2. 证书类型 -~~~~~~~~~~~~~ - -x509的证书编码格式有两种: - -1. PEM(Privacy-enhanced Electronic Mail)是明文格式的,以 —–BEGIN - CERTIFICATE—–开头,已—–END - CERTIFICATE—–结尾。中间是经过base64编码的内容,apache需要的证书就是这类编码的证书.查看这类证书的信息的命令为: - ``openssl x509 -noout -text -in server.pem``\ 。其实PEM就是把DER的内容进行了一次base64编码 -2. DER是二进制格式的证书,查看这类证书的信息的命令为: - ``openssl x509 -noout -text -inform der -in server.der`` - -2.3. 扩展名 -~~~~~~~~~~~ - -- .crt证书文件,可以是DER(二进制)编码的,也可以是PEM(ASCII - (Base64))编码的),在类unix系统中比较常见; -- .cer也是证书,常见于Windows系统。编码类型同样可以是DER或者PEM的,windows下有工具可以转换crt到cer; -- .csr证书签名请求文件,一般是生成请求以后发送给CA,然后CA会给您签名并发回证书 -- .key一般公钥或者密钥都会用这种扩展名,可以是DER编码的或者是PEM编码的。查看DER编码的(公钥或者密钥)的文件的命令为: - ``openssl rsa -inform DER -noout -text -in xxx.key``\ 。查看PEM编码的(公钥或者密钥)的文件的命令为: - ``openssl rsa -inform PEM -noout -text -in xxx.key``; -- .p12证书文件,包含一个X509证书和一个被密码保护的私钥 - -2.4 证书的种类 -~~~~~~~~~~~~~~ - -安全证书主要分为DV、OV和EV三个种类,对应的安全等级为一般、较好和最高三个等级。三者的审核过程、审核标准和对应的域名数量也不同,所以价格在一两百元到几万元不等。 - -DV SSL -^^^^^^ - -DV SSL证书是只验证网站域名所有权的简易型(Class -1级)SSL证书,可10分钟快速颁发,能起到加密传输的作用,但无法向用户证明网站的真实身份。 - -目前市面上的免费证书都是这个类型的,只是提供了对数据的加密,但是对提供证书的个人和机构的身份不做验证。 - -OV SSL -^^^^^^ - -OV SSL,提供加密功能,对申请者做严格的身份审核验证,提供可信×××明。 - -和DV SSL的区别在于,OV SSL -提供了对个人或者机构的审核,能确认对方的身份,安全性更高。 - -所以这部分的证书申请是收费的~ - -EV SSL -^^^^^^ - -超安=EV=最安全、最严格 超安EV -SSL证书遵循全球统一的严格身份验证标准,是目前业界安全级别最高的顶级 -(Class 4级)SSL证书。 - -金融证券、银行、第三方支付、网上商城等,重点强调网站安全、企业可信形象的网站,涉及交易支付、客户隐私信息和账号密码的传输。 - -这部分的验证要求最高,申请费用也是最贵的。 - -选择签发机构时,最好选择行业认可的全球范围内都可以使用的ca机构签发的证书。目前我们国内的证书能够符合标准的还不是特别多,主要原因是有一些证书不能够被国外的浏览器所认可,在使用的时候需要进行一定的额外操作。 - --------------- - -根据保护域名的数量需求,SSL证书又分为: - -**单域名版:**\ 只保护一个域名,例如 -`www.abc.com `__ 或者 login.abc.com 之类的单个域名 - -**多域名版:**\ 一张证书可以保护多个域名,例如同时保护 -`www.abc.com `__ , -`www.bcd.com, `__ pay.efg.com 等 - -**通配符版:**\ 一张证书保护同一个主域名下同一级的所有子域名,不限个数,形如 -\*.abc.com 。注意,通配符版只有 DVSSL 和 OVSSL 具有, EVSSL -不具有通配符版本。 - -2.5 证书在哪里 -~~~~~~~~~~~~~~ - -当你在下载并安装浏览器时,浏览器内部其实已经内嵌了全世界公认的根证书颁发机构的证书。 - -若一个网站的数字证书的证书颁发机构在浏览器中没有,则需要引导用户自行导入。 - -如果你想在 Chrome 中查看有哪些受信任的证书颁发机构,可以点击 ``设置`` -> -``隐私设置与安全性`` -> ``安全`` -> ``管理证书`` - -|image4| - -2.6 证书里的信息 -~~~~~~~~~~~~~~~~ - -在上图的位置里,随便双击点开一个证书,就可以查看证书里的内容。 - -内容非常多,最主要的有 - -- 证书是哪个机构的? -- 证书里的公钥是什么? -- 证书有效期是什么时候? -- 采用的哪种加解密的算法? - -2.7 证书吊销 -~~~~~~~~~~~~ - -证书是有生命周期的,如果证书的私钥泄漏了那这个证书就得吊销,一般有两种吊销方式:CRL和OCSP。 - -CRL( Certificate Revocation -List)是CA机构维护的一个已经被吊销的证书序列号列表,浏览器需要定时更新这个列表,浏览器在验证证书合法性的时候也会在证书吊销列表中查询是否已经被吊销,如果被吊销了那这个证书也是不可信的。可以看出,这个列表随着被吊销证书的增加而增加,列表会越来越大,浏览器还需要定时更新,实时性也比较差。 - -所以,后来就有了 OCSP (Online Certificate Status -Protocol)在线证书状态协议,这个协议就是解决了 CRL -列表越来越大和实时性差的问题而生的。有了这个协议,浏览器就可以不用定期更新CRL了,在验证证书的时候直接去CA服务器实时校验一下证书有没有被吊销就可以,是解决了CRL的问题,但是每次都要去CA服务器上校验也会很慢,在网络环境较差的时候或者跨国访问的时候,体验就非常差了,OCSP虽然解决了CRL的问题但是性能却很差。 - -如果你想了解更多关于证书的内容,可以前往华为云的公开文档进行学习:https://support.huaweicloud.com/scm_faq/scm_01_0020.html - -4. 如何生成 CSR 文件 --------------------- - -CSR是Certificate Signing Request的英文缩写,即证书签名请求文件。 - -当申请者申请数字证书时,CSP(加密服务提供者)生成私钥,同时也生成了CSR文件。申请者将CSR文件提交至Certificate -Authority -(CA)机构后,CA机构使用其根证书私钥签名,从而就生成了数字证书。 - -申请者通过CSR文件,向CA机构申请数字证书。获取证书后,就能证明申请者的网站是可信的,数据传输是加密的。 - -接下来来了解一下,CSR 文件是如何生成的? - -使用 OpenSSL 生成 -~~~~~~~~~~~~~~~~~ - -假设申请的域名为 -**python.iswbm.com**\ ,公司名称为\ **派森时光科技**\ ,部门是\ **IT部**\ ,公司在\ **中国广东省深圳市**\ 。可通过运行下方命令行生成CSR文件: - -.. code:: shell - - $ openssl req -new –SHA256 -newkey rsa:2048 -nodes -keyout python.iswbm.com.key -out python.iswbm.com.csr -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=派森时光科技/OU=IT/CN=python.iswbm.com" - -此命令行表示: - -- ``req``\ 参数:表示证书请求request,用于生成CSR文件。 -- ``SHA256``\ 参数:表示CSR签名时用的摘要算法。 -- ``newkey``\ 参数:表示指定证书的算法。参数 2048:表示密钥对的长度。 -- ``nodes``\ 参数:表示不对私钥加密。 -- ``keyout``\ 参数:表示生成的私钥文件。名为\ ``iswbm.key``\ 的私钥文件,需自行保管,用于获取证书后的部署过程。 -- ``out``\ 参数:表示生成的 CSR - 文件。名为\ ``iswbm.com.csr``\ 的CSR文件,用于提交至CA机构验证信息,从而获取证书。 -- ``subj``\ 参数:表示CSR信息,具体有哪些参数,可以继续往下看。 - -subj参数说明: - -- ``C``\ :Country,表示国家,申请者或申请企业所在国家的英文或两位大写国家代码。如:CN -- ``ST``\ :State/Province,表示省份,申请者或申请企业所在地的省/市/自治区英文或拼音全称。如:Guangdong -- ``L``\ :Locality,表示城市,申请者或申请企业所在城市的英文或拼音全称。如:Shenzhen -- ``O``\ :Organization,表示申请者的姓名或申请企业的名称。 -- ``OU``\ :Organizational - Unit,表示申请人所在部门的英文或拼音全称。如:IT -- ``CN``\ :Common Name,表示你要为哪个域名申请证书,可是单域名(比如 - python.iswbm.com),也可以是泛域名(*.iswbm.com),也可以是为多个域名申请一个证书(具体我没操作过)。 - -上一条命令执行完后,会在你的本地目录下生成俩文件 - -- python.iswbm.com.csr:用于向CA机构申请证书 - -.. code:: shell - - $ cat python.iswbm.com.csr - -----BEGIN CERTIFICATE REQUEST----- - MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ2Rvbmcx - ETAPBgNVBAcMCFNoZW56aGVuMS0wKwYDVQQKDCTDpsK0wr7DpsKjwq7DpsKXwrbD - ... - 7lgB4QC1aIFz8gi9TGMJU2LqTDJCj+tgM68LDBdMLeQ8XZ33C95Nl0qt7yG+zjlZ - 01jBh+T882r8x9gKdwb7nZSWFQY4/YTq+sY++YW/QuCNRcJ2vbM18U/HlIRsZ3su - x6Neh08= - -----END CERTIFICATE REQUEST----- - -- python.iswbm.com.key:私钥,自行保存,不要外泄 - -.. code:: shell - - $ cat python.iswbm.com.key - -----BEGIN PRIVATE KEY----- - MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4OrcM9hTs9Hao - SzjsVJFX2Mmd+mToMG3u++o2Fd5yrPYq4COkT33lnL9kJNrDWqGp5TRkWqNwLaPl - ... - a/lKBWLcvxE+IQ+mxNbN058kEJ3l8WAcAFCebLm5czUqmIVa3JR+cBDLvGFZVn6z - 72AP5D/Evds4BOO+VzAiVLU6Ai78qhACuVExZNQCxdvJy4LxpeckUpCem9hAPiIY - LQfiTStBBU6t/+mnDyij+XreGQ== - -----END PRIVATE KEY----- - -使用在线生成工具 -~~~~~~~~~~~~~~~~ - -使用 OpenSSL 工具生成 CSR -文件的方法固然简单,但使用时,需要你的了解代码中参数的意思。 - -如果你不想花心思去记这些,推荐你使用 -`CSR在线生成工具 `__\ (https://myssl.com/csr_create.html) - -你只需简单地输入如下信息,再点击 ``OpenSSL生成``\ ,你就可以获得一条 -OpenSSL 命令,这下你再也不用自己拼凑参数啦,真的太方便了。为什么不点击 -``生成`` 让其直接生成 私钥文件 和 CSR文件 呢?当然是为了安全起见啦。 - -|image5| - -5. TLS/SSL 保证信息的安全 -------------------------- - -在信息安全性问题中,我们常常要做到三点才能保证信息的安全: - -1. 信息的保密性 -2. 信息的完整性 -3. 身份识别 - -将这三者结合起来,就是 TLS/SSL 做的事情 - -|image6| - -1、客户端(浏览器)向服务端发出请求,服务端返回证书给客户端。 - -2、客户端拿到证书后,把证书里的签名与及明文信息分别取出来,然后会用自身携带的CA机构的公钥去解密签名,然后信息摘要1,然后再对明文信息进行HASH,得到一个信息摘要2,对比信息摘要1 -和信息摘要2,如果一样,说明证书是合法的,也就是证书里的公钥是正确的。 - -以上采用的是非对称加密(CA的公钥和私钥),保证了客户端接收到服务端正确的公钥,有了服务端的公钥后,后面的信息加密都可以使用这个公钥,而用这个公钥加密过后的密文,只有服务端的私钥能解,就算黑客拿到了也没法解开。 - -参考文章 --------- - -- `HTTPS中CA证书的签发及使用过程 `__ - -|image7| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/image-20200723233619429.png -.. |image2| image:: http://image.iswbm.com/image-20200724121333485.png -.. |image3| image:: http://image.iswbm.com/20200724124502.png -.. |image4| image:: http://image.iswbm.com/20200717222206.png -.. |image5| image:: http://image.iswbm.com/20200718100052.png -.. |image6| image:: http://image.iswbm.com/1169376-20191008172456510-1302410435.png -.. |image7| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c10/c10_09.md b/source/c10/c10_09.md deleted file mode 100644 index 3b17ff7..0000000 --- a/source/c10/c10_09.md +++ /dev/null @@ -1,371 +0,0 @@ -# 10.9 如何申请数字证书,点亮你的 HTTPS? - -![](http://image.iswbm.com/20200602135014.png) - -上一节讲解了关于 SSL 数字证书的基础知识,这一篇就来实战演示一下,如何将你的 HTTP 网站加上 HTTPS 。 - -要想使用 HTTPS ,要做的事情还是有点多的: - -1. 准备一个域名和服务器(自行准备) -2. 并将该域名解析到你的服务器ip上 -3. 生成 CSR 证书请求文件 -4. 拿着 CSR 文件去申请证书 -5. 把申请到的证书部署到你的服务器上 - -## 1. 设置 DNS 解析 - -我有一个域名 `iswbm.com`,其下有好多的子域名,比如写 Python 的在线博客 `python.iswbm.com`,还有写 Golang 的在线博客 `golang.iswbm.com`,还有我的图床 `image.iswbm.com` 等等。 - -这里新建了个子域名 `demo.iswbm.com` ,并设置其解析到 我的服务器上,如下图(服务器 ip 已码掉)。 - -![](http://image.iswbm.com/20200728233602.png) - -## 2. 搭建 HTTP 站点 - -为了方便,我这里就使用 Apache 放了一个 HTTP 的静态网页,方法很简单,大家百度即可。 - -![](http://image.iswbm.com/20200729230813.png) - - - -## 3. 申请 SSL 证书 - -SSL 数字证书怎么来的呢? - -你可以自己制作,也可以向CA权威机构申请。 - -二者的区别是: - -1. 自己颁发的证书,需要客户端验证通过,也就是需要用户手动安装证书,并将其设置为受信任的根证书。但即使如此,浏览器上( chrome, firefox)仍不认可这种自签名证书,会在地址栏前面提示连接不安全,手动安装证书后,也会提示该证书无效。若想要继续访问,并忽略该提示,可以选择继续访问。 -2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是绝对可信任的。因此你在访问网站时,不会提示连接不安全。 - -下面,我将分别向你展示这两种方法,都是如何操作的。 - - - -### 第一种:向权威CA机构申请 - -在阿里云和腾讯云都可以 进行 SSL 证书的申请,证书的申请有付费的(价格也不便宜),也有免费的,看了一圈,只有腾讯云有免费的 SSL 证书的申请渠道(阿里云听说以前也有,后来关闭了)。 - -本篇文章,仅以演示教学之用,所以只用腾讯云的免费证书的就足够啦。 - -登陆腾讯云,可以看到SSL 证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 - -![](http://image.iswbm.com/image-20200718102622663.png) - -点击选择 `域名型免费版` - -![](http://image.iswbm.com/image-20200718101358755.png) - -点击 `免费快速申请`后,填写域名和你的个人邮箱 - -![](http://image.iswbm.com/20200729232432.png) - -再点击下一步,会需要你验证域名所有权,验证方式有如下三种 - -1. 自动DNS验证 -2. 手动DNS验证 -3. 文件验证 - -但由于我的域名不是腾讯云平台解析的,因此没有 自动DNS验证的选项,只有其他两种 - -![](http://image.iswbm.com/image-20200718101652899.png) - -点击 `确认申请` 后,会提示你进入域名验证所有权的流程,这里我选择 手动DNS验证。 - -![](http://image.iswbm.com/20200729004207.png) - -审核通过后,3s 内就会给你颁发证书,你可以从控制台点击证书下载。 - -![](http://image.iswbm.com/20200729004307.png) - -下载下来的会是一个 zip 包。 - -解压一下,会有不同的服务器类型(有 Apache、IIS、Nginx、Tomcat)的文件夹。 - -![](http://image.iswbm.com/20200729004456.png) - -我使用的是 Apache ,在这个文件夹下面有三个文件: - -1. `1_root_bundle.crt`:根证书 -2. `2_demo.iswbm.com.crt`:域名证书 -3. `3_demo.iswbm.com.key`:私钥文件 - -这三个文件,下一步会部署于我的服务器上。 - -接下来讲第二种 SSL 证书申请方式。 - -### 第二种:自签名的 SSL 证书 - -没有权威的第三方 CA 机构给自己颁发证书,那就自己给自己颁发咯。 - -自签名 SSL 的证书制作过程,可以分为两步: - -1. 自己要当 CA 机构,那 CA 有的 CA 根证书、私钥 一样都不能少,因此第一步:生成 CA 的 crt 证书 和 CA 的私钥。 -2. 要申请证书,首先服务器自己要有一个密钥对(公钥和私钥) -3. 拿着上面生成的公钥,制作一个 CSR 证书申请文件 -4. 用第一步的 CA 私钥,给这个 CSR 签名,生成咱所需要的 SSL 数字证书文件。 - -步骤很多,命令很多,命令所带的参数更多,对于只想学习证书签发流程的你,把这些命令都背下来,并不是一个好的选择,毕竟这种事可能也干不了几次。 - -因此,我把这些步骤、命令,都整合成一个 shell 脚本,你只要执行这个脚本就行了。 - -```shell -$ bash create_self-signed-cert.sh --ssl-domain=demo.iswbm.com --ssl-trusted-domain=demo.iswbm.com --ssl-size=2048 --ssl-date=3650 -``` - -对应的参数的解释,在脚本中都有解释 - -![](http://image.iswbm.com/20200729235153.png) - -这个脚本过长,不好直接贴上来,我将它放在我的公众号(**Python编程时光**)后台,你可以直接回复『**证书签名**』直接获取下载。 - - 执行完成后,会在当前目录下生成好多个文件。 - -其中,只有两个文件对我们有用 - -![](http://image.iswbm.com/20200730000142.png) - - - -## 4. 部署 SSL 证书 - -根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 - -![](http://image.iswbm.com/20200718105347.png) - -这里我将以 CentOS 7.2 + Apache 为例,演示如何部署 SSL 证书。 - -先安装一下 mod_ssl - -```shell -$ yum install -y mod_ssl -``` - -安装完后,在 /etc/httpd/conf.d/ 目录下 会有个 ssl.conf 文件。 - -编辑修改这个文件,以下是我的配置供你参考 - -```shell - - DocumentRoot "/var/www/html" - #填写证书名称 - ServerName demo.iswbm.com - #启用 SSL 功能 - SSLEngine on - #证书文件的路径 - SSLCertificateFile /etc/pki/tls/certs/demo.iswbm.com.crt - #私钥文件的路径 - SSLCertificateKeyFile /etc/pki/tls/private/demo.iswbm.com.key - #根证书文件的路径 - SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt - -``` - -**如果你的证书是从权威 CA 机构上申请来的。** - -比如我上面从腾讯云上申请来的,那么这三个文件就是从已经从腾讯云的控制台上下载下来的那三个文件。 - -在修改完后,务必记得把下载的这三个文件,放到相应的目录中去。 - -![](http://image.iswbm.com/20200730214826.png) - -配置完 ssl.conf,可能还需要你 check 一下 `/etc/httpd/conf/httpd.conf` 的一些配置,这些配置一般用默认的就可以,但是以防万一,还是写一下吧 - -``` -Include conf.modules.d/*.conf -``` - -写这一行的目的,就是为了 httpd 去加载 mod_ssl 这个模块 - -```shell -$ cat /etc/httpd/conf.modules.d/00-ssl.conf -LoadModule ssl_module modules/mod_ssl.so -``` - -一切配置完成后,记得重启一下 httpd 服务 - -```shell -$ systemctl restart httpd -``` - -然后使用 chrome 访问一下 `https//demo.iswbm.com` 看看,大功告成。 - -![](http://image.iswbm.com/20200730215613.png) - - - -**而如果你的证书是自签名的。** - -ssl.conf 配置文件下的应该改成这样 - -``` - - DocumentRoot "/var/www/html" - #填写证书名称 - ServerName demo.iswbm.com - #启用 SSL 功能 - SSLEngine on - #证书文件的路径 - SSLCertificateFile /etc/pki/tls/certs/tls.crt - #私钥文件的路径 - SSLCertificateKeyFile /etc/pki/tls/private/tls.key - -``` - -同时记得把这两个文件也拷贝到相应的目录下 - -```shell -$ cp tls.crt /etc/pki/tls/certs/ -$ cp tls.key /etc/pki/tls/private/ -``` - -最后还是不要忘了重启 httpd - -```shell -$ systemctl restart httpd -``` - -试着用 chrome 访问一下,可以看到 chrome 提示该连接不安全 - -![](http://image.iswbm.com/20200730220835.png) - -如果执意要访问,可以点击左下方的 `继续前往`,这样以后再访问的时候,就不会再出现这个警告页面了。 - -![](http://image.iswbm.com/20200730221745.png) - -`不安全` 三个字,让人很没有安全感,那有没有办法去掉呢? - -答案是,没有,只要是自签名的证书,在 chrome ,firefox 等主流浏览器看来都是不安全的。 - -即使你把这个根证书添加到你的受信任的证书列表中,也是徒然。 - -下面就试着来安装一下这个根证书。 - -按照下图指示,拖动证书到本地磁盘上。 - -![](http://image.iswbm.com/20200728234740.png) - -打开 Mac 上的 `钥匙串访问` - -![](http://image.iswbm.com/20200730222441.png) - -点击 `登陆`,然后再拖动这个证书到窗口中进行安装 - -![](http://image.iswbm.com/20200728235331.png) - -右键该证书,点击 `显示简介`,跳出下面的界面后,再点击 `信任`,把 IP 安全选择选为 `始终信任`。 - -![](http://image.iswbm.com/20200730222700.png) - -设置完后,再访问下 `demo.iswbm.com` ,仍然显示连接不安全,并且证书是无效的 - -![](http://image.iswbm.com/20200730222827.png) - -点击证书,显示证书,该证书确实已经放入信任列表中了。 - -![](http://image.iswbm.com/20200730222928.png) - -## 5. 宝塔申请证书 - -注册并登陆宝塔(https://bt.cn),然后进入控制面板。 - -![](http://image.iswbm.com/image-20200929123223096.png) - -点击申请证书 - -![](http://image.iswbm.com/image-20200929123355037.png) - -选择免费的就好 - -![](http://image.iswbm.com/image-20200929123418789.png) - -填写你的域名后,支付订单(其实不要钱)。 - -![](http://image.iswbm.com/image-20200929123459545.png) - -然后登陆我的阿里去域名解析,根据如下提示去添加 DNS解析规则 - -![](http://image.iswbm.com/image-20200929123956070.png) - -然后静待一段时间验证成功了,就可以点击如下按钮进行下载 ![](http://image.iswbm.com/image-20200929194100905.png) - -下载到本地后,你会得到一个 zip 包,解压一下,就可以看到证书文件及私钥。 - -![](http://image.iswbm.com/image-20200929201114297.png) - -因为我的博客使用的是 Nginx,因此我该 Nginx 下的两个文件上传到我的服务器上的 nginx 目录下. - -具体怎么上传呢?你可以使用远程拷贝软件,例如 WinSCP,也可以使用 `lrzsz` (推荐使用)。 - -传到哪个目录下呢? - -先使用 find 命令查找一下你的 nginx.conf 路径 - -```shell -$ find / -name nginx.conf -/usr/local/nginx/conf/nginx.conf -``` - -你的证书文件可以和 nginx.conf 放在同一目录下 - -```shell -/usr/local/nginx/conf -``` - -接下来使用 vim 编辑该文件,找到 server,添加如下行( server 原本的内容 我使用 `...` 表示,意思是不需要去动。 ) - -```shell -server - { - listen 443 ssl; - # 注释掉该行 - # listen 80 default_server reuseport; - - #证书文件名称 - ssl_certificate 1_iswbm.com_bundle.pem; - #私钥文件名称 - ssl_certificate_key 0_iswbm.com.key; - - ... - } -``` - -尝试着登陆一下 - -## 6. 七牛去图床开启https - -登陆七牛云 - -然后进入 [证书管理](https://portal.qiniu.com/certificate/ssl ) 上传证书。 - -![](http://image.iswbm.com/image-20200929210645477.png) - -进入对象存储 -> 域名管理,找到 HTTPS 配置的位置,点击 `修改配置`。 - -![](http://image.iswbm.com/image-20200929205400898.png) - - - -将按钮置为开启状态,选择我们刚刚上传的证书。 - -![](http://image.iswbm.com/image-20200929205611442.png) - -设置完后,并不能立马使用 - -![](http://image.iswbm.com/image-20200929210431735.png) - -域名的升级需要一定时间,等待即可。 - -![](http://image.iswbm.com/image-20200929210900562.png) - - - -## 参考文档 - -- [Apache 服务器证书安装](https://cloud.tencent.com/document/product/400/35243) -- [自签名 SSL 证书](https://docs.rancher.cn/rancher2x/install-prepare/self-signed-ssl.html#_2-3-%E6%89%A9%E5%B1%95%E5%90%8D) - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c10/c10_09.rst b/source/c10/c10_09.rst deleted file mode 100644 index 161d0b7..0000000 --- a/source/c10/c10_09.rst +++ /dev/null @@ -1,433 +0,0 @@ -10.9 如何申请数字证书,点亮你的 HTTPS? -======================================= - -|image0| - -上一节讲解了关于 SSL -数字证书的基础知识,这一篇就来实战演示一下,如何将你的 HTTP 网站加上 -HTTPS 。 - -要想使用 HTTPS ,要做的事情还是有点多的: - -1. 准备一个域名和服务器(自行准备) -2. 并将该域名解析到你的服务器ip上 -3. 生成 CSR 证书请求文件 -4. 拿着 CSR 文件去申请证书 -5. 把申请到的证书部署到你的服务器上 - -1. 设置 DNS 解析 ----------------- - -我有一个域名 ``iswbm.com``\ ,其下有好多的子域名,比如写 Python -的在线博客 ``python.iswbm.com``\ ,还有写 Golang 的在线博客 -``golang.iswbm.com``\ ,还有我的图床 ``image.iswbm.com`` 等等。 - -这里新建了个子域名 ``demo.iswbm.com`` ,并设置其解析到 -我的服务器上,如下图(服务器 ip 已码掉)。 - -|image1| - -2. 搭建 HTTP 站点 ------------------ - -为了方便,我这里就使用 Apache 放了一个 HTTP -的静态网页,方法很简单,大家百度即可。 - -|image2| - -3. 申请 SSL 证书 ----------------- - -SSL 数字证书怎么来的呢? - -你可以自己制作,也可以向CA权威机构申请。 - -二者的区别是: - -1. 自己颁发的证书,需要客户端验证通过,也就是需要用户手动安装证书,并将其设置为受信任的根证书。但即使如此,浏览器上( - chrome, - firefox)仍不认可这种自签名证书,会在地址栏前面提示连接不安全,手动安装证书后,也会提示该证书无效。若想要继续访问,并忽略该提示,可以选择继续访问。 -2. 向权威的数字证书认证机构申请,由于这些机构在网民的电脑里都有相应的根证书,且这些机构是绝对可信任的。因此你在访问网站时,不会提示连接不安全。 - -下面,我将分别向你展示这两种方法,都是如何操作的。 - -第一种:向权威CA机构申请 -~~~~~~~~~~~~~~~~~~~~~~~~ - -在阿里云和腾讯云都可以 进行 SSL -证书的申请,证书的申请有付费的(价格也不便宜),也有免费的,看了一圈,只有腾讯云有免费的 -SSL 证书的申请渠道(阿里云听说以前也有,后来关闭了)。 - -本篇文章,仅以演示教学之用,所以只用腾讯云的免费证书的就足够啦。 - -登陆腾讯云,可以看到SSL -证书有分很多种,企业型的,企业型专业版的,增强型,增强型专业版的,还有域名型免费版。 - -|image3| - -点击选择 ``域名型免费版`` - -|image4| - -点击 ``免费快速申请``\ 后,填写域名和你的个人邮箱 - -|image5| - -再点击下一步,会需要你验证域名所有权,验证方式有如下三种 - -1. 自动DNS验证 -2. 手动DNS验证 -3. 文件验证 - -但由于我的域名不是腾讯云平台解析的,因此没有 -自动DNS验证的选项,只有其他两种 - -|image6| - -点击 ``确认申请`` 后,会提示你进入域名验证所有权的流程,这里我选择 -手动DNS验证。 - -|image7| - -审核通过后,3s 内就会给你颁发证书,你可以从控制台点击证书下载。 - -|image8| - -下载下来的会是一个 zip 包。 - -解压一下,会有不同的服务器类型(有 -Apache、IIS、Nginx、Tomcat)的文件夹。 - -|image9| - -我使用的是 Apache ,在这个文件夹下面有三个文件: - -1. ``1_root_bundle.crt``\ :根证书 -2. ``2_demo.iswbm.com.crt``\ :域名证书 -3. ``3_demo.iswbm.com.key``\ :私钥文件 - -这三个文件,下一步会部署于我的服务器上。 - -接下来讲第二种 SSL 证书申请方式。 - -第二种:自签名的 SSL 证书 -~~~~~~~~~~~~~~~~~~~~~~~~~ - -没有权威的第三方 CA 机构给自己颁发证书,那就自己给自己颁发咯。 - -自签名 SSL 的证书制作过程,可以分为两步: - -1. 自己要当 CA 机构,那 CA 有的 CA 根证书、私钥 - 一样都不能少,因此第一步:生成 CA 的 crt 证书 和 CA 的私钥。 -2. 要申请证书,首先服务器自己要有一个密钥对(公钥和私钥) -3. 拿着上面生成的公钥,制作一个 CSR 证书申请文件 -4. 用第一步的 CA 私钥,给这个 CSR 签名,生成咱所需要的 SSL - 数字证书文件。 - -步骤很多,命令很多,命令所带的参数更多,对于只想学习证书签发流程的你,把这些命令都背下来,并不是一个好的选择,毕竟这种事可能也干不了几次。 - -因此,我把这些步骤、命令,都整合成一个 shell -脚本,你只要执行这个脚本就行了。 - -.. code:: shell - - $ bash create_self-signed-cert.sh --ssl-domain=demo.iswbm.com --ssl-trusted-domain=demo.iswbm.com --ssl-size=2048 --ssl-date=3650 - -对应的参数的解释,在脚本中都有解释 - -|image10| - -这个脚本过长,不好直接贴上来,我将它放在我的公众号(\ **Python编程时光**\ )后台,你可以直接回复『\ **证书签名**\ 』直接获取下载。 - -执行完成后,会在当前目录下生成好多个文件。 - -其中,只有两个文件对我们有用 - -|image11| - -4. 部署 SSL 证书 ----------------- - -根据服务器的类型不同,部署安装的方式有有所区别,腾讯云的操作文档已经非常详细了,你可以通过这个链接访问到如下的文档:https://cloud.tencent.com/document/product/400/4143 - -|image12| - -这里我将以 CentOS 7.2 + Apache 为例,演示如何部署 SSL 证书。 - -先安装一下 mod_ssl - -.. code:: shell - - $ yum install -y mod_ssl - -安装完后,在 /etc/httpd/conf.d/ 目录下 会有个 ssl.conf 文件。 - -编辑修改这个文件,以下是我的配置供你参考 - -.. code:: shell - - - DocumentRoot "/var/www/html" - #填写证书名称 - ServerName demo.iswbm.com - #启用 SSL 功能 - SSLEngine on - #证书文件的路径 - SSLCertificateFile /etc/pki/tls/certs/demo.iswbm.com.crt - #私钥文件的路径 - SSLCertificateKeyFile /etc/pki/tls/private/demo.iswbm.com.key - #根证书文件的路径 - SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt - - -**如果你的证书是从权威 CA 机构上申请来的。** - -比如我上面从腾讯云上申请来的,那么这三个文件就是从已经从腾讯云的控制台上下载下来的那三个文件。 - -在修改完后,务必记得把下载的这三个文件,放到相应的目录中去。 - -|image13| - -配置完 ssl.conf,可能还需要你 check 一下 ``/etc/httpd/conf/httpd.conf`` -的一些配置,这些配置一般用默认的就可以,但是以防万一,还是写一下吧 - -:: - - Include conf.modules.d/*.conf - -写这一行的目的,就是为了 httpd 去加载 mod_ssl 这个模块 - -.. code:: shell - - $ cat /etc/httpd/conf.modules.d/00-ssl.conf - LoadModule ssl_module modules/mod_ssl.so - -一切配置完成后,记得重启一下 httpd 服务 - -.. code:: shell - - $ systemctl restart httpd - -然后使用 chrome 访问一下 ``https//demo.iswbm.com`` 看看,大功告成。 - -|image14| - -**而如果你的证书是自签名的。** - -ssl.conf 配置文件下的应该改成这样 - -:: - - - DocumentRoot "/var/www/html" - #填写证书名称 - ServerName demo.iswbm.com - #启用 SSL 功能 - SSLEngine on - #证书文件的路径 - SSLCertificateFile /etc/pki/tls/certs/tls.crt - #私钥文件的路径 - SSLCertificateKeyFile /etc/pki/tls/private/tls.key - - -同时记得把这两个文件也拷贝到相应的目录下 - -.. code:: shell - - $ cp tls.crt /etc/pki/tls/certs/ - $ cp tls.key /etc/pki/tls/private/ - -最后还是不要忘了重启 httpd - -.. code:: shell - - $ systemctl restart httpd - -试着用 chrome 访问一下,可以看到 chrome 提示该连接不安全 - -|image15| - -如果执意要访问,可以点击左下方的 -``继续前往``\ ,这样以后再访问的时候,就不会再出现这个警告页面了。 - -|image16| - -``不安全`` 三个字,让人很没有安全感,那有没有办法去掉呢? - -答案是,没有,只要是自签名的证书,在 chrome ,firefox -等主流浏览器看来都是不安全的。 - -即使你把这个根证书添加到你的受信任的证书列表中,也是徒然。 - -下面就试着来安装一下这个根证书。 - -按照下图指示,拖动证书到本地磁盘上。 - -|image17| - -打开 Mac 上的 ``钥匙串访问`` - -|image18| - -点击 ``登陆``\ ,然后再拖动这个证书到窗口中进行安装 - -|image19| - -右键该证书,点击 ``显示简介``\ ,跳出下面的界面后,再点击 ``信任``\ ,把 -IP 安全选择选为 ``始终信任``\ 。 - -|image20| - -设置完后,再访问下 ``demo.iswbm.com`` -,仍然显示连接不安全,并且证书是无效的 - -|image21| - -点击证书,显示证书,该证书确实已经放入信任列表中了。 - -|image22| - -5. 宝塔申请证书 ---------------- - -注册并登陆宝塔(https://bt.cn),然后进入控制面板。 - -|image23| - -点击申请证书 - -|image24| - -选择免费的就好 - -|image25| - -填写你的域名后,支付订单(其实不要钱)。 - -|image26| - -然后登陆我的阿里去域名解析,根据如下提示去添加 DNS解析规则 - -|image27| - -然后静待一段时间验证成功了,就可以点击如下按钮进行下载 |image28| - -下载到本地后,你会得到一个 zip 包,解压一下,就可以看到证书文件及私钥。 - -|image29| - -因为我的博客使用的是 Nginx,因此我该 Nginx -下的两个文件上传到我的服务器上的 nginx 目录下. - -具体怎么上传呢?你可以使用远程拷贝软件,例如 WinSCP,也可以使用 -``lrzsz`` (推荐使用)。 - -传到哪个目录下呢? - -先使用 find 命令查找一下你的 nginx.conf 路径 - -.. code:: shell - - $ find / -name nginx.conf - /usr/local/nginx/conf/nginx.conf - -你的证书文件可以和 nginx.conf 放在同一目录下 - -.. code:: shell - - /usr/local/nginx/conf - -接下来使用 vim 编辑该文件,找到 server,添加如下行( server 原本的内容 -我使用 ``...`` 表示,意思是不需要去动。 ) - -.. code:: shell - - server - { - listen 443 ssl; - # 注释掉该行 - # listen 80 default_server reuseport; - - #证书文件名称 - ssl_certificate 1_iswbm.com_bundle.pem; - #私钥文件名称 - ssl_certificate_key 0_iswbm.com.key; - - ... - } - -尝试着登陆一下 - -6. 七牛去图床开启https ----------------------- - -登陆七牛云 - -然后进入 `证书管理 `__ -上传证书。 - -|image30| - -进入对象存储 -> 域名管理,找到 HTTPS 配置的位置,点击 ``修改配置``\ 。 - -|image31| - -将按钮置为开启状态,选择我们刚刚上传的证书。 - -|image32| - -设置完后,并不能立马使用 - -|image33| - -域名的升级需要一定时间,等待即可。 - -|image34| - -参考文档 --------- - -- `Apache - 服务器证书安装 `__ -- `自签名 SSL - 证书 `__ - -|image35| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200728233602.png -.. |image2| image:: http://image.iswbm.com/20200729230813.png -.. |image3| image:: http://image.iswbm.com/image-20200718102622663.png -.. |image4| image:: http://image.iswbm.com/image-20200718101358755.png -.. |image5| image:: http://image.iswbm.com/20200729232432.png -.. |image6| image:: http://image.iswbm.com/image-20200718101652899.png -.. |image7| image:: http://image.iswbm.com/20200729004207.png -.. |image8| image:: http://image.iswbm.com/20200729004307.png -.. |image9| image:: http://image.iswbm.com/20200729004456.png -.. |image10| image:: http://image.iswbm.com/20200729235153.png -.. |image11| image:: http://image.iswbm.com/20200730000142.png -.. |image12| image:: http://image.iswbm.com/20200718105347.png -.. |image13| image:: http://image.iswbm.com/20200730214826.png -.. |image14| image:: http://image.iswbm.com/20200730215613.png -.. |image15| image:: http://image.iswbm.com/20200730220835.png -.. |image16| image:: http://image.iswbm.com/20200730221745.png -.. |image17| image:: http://image.iswbm.com/20200728234740.png -.. |image18| image:: http://image.iswbm.com/20200730222441.png -.. |image19| image:: http://image.iswbm.com/20200728235331.png -.. |image20| image:: http://image.iswbm.com/20200730222700.png -.. |image21| image:: http://image.iswbm.com/20200730222827.png -.. |image22| image:: http://image.iswbm.com/20200730222928.png -.. |image23| image:: http://image.iswbm.com/image-20200929123223096.png -.. |image24| image:: http://image.iswbm.com/image-20200929123355037.png -.. |image25| image:: http://image.iswbm.com/image-20200929123418789.png -.. |image26| image:: http://image.iswbm.com/image-20200929123459545.png -.. |image27| image:: http://image.iswbm.com/image-20200929123956070.png -.. |image28| image:: http://image.iswbm.com/image-20200929194100905.png -.. |image29| image:: http://image.iswbm.com/image-20200929201114297.png -.. |image30| image:: http://image.iswbm.com/image-20200929210645477.png -.. |image31| image:: http://image.iswbm.com/image-20200929205400898.png -.. |image32| image:: http://image.iswbm.com/image-20200929205611442.png -.. |image33| image:: http://image.iswbm.com/image-20200929210431735.png -.. |image34| image:: http://image.iswbm.com/image-20200929210900562.png -.. |image35| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c10/c10_12.md b/source/c10/c10_12.md deleted file mode 100644 index 3066705..0000000 --- a/source/c10/c10_12.md +++ /dev/null @@ -1,38 +0,0 @@ -# 10.12 数据包的网络之旅 - -![](http://image.iswbm.com/20200602135014.png) - -当你在学习枯燥的网络基础知识的时候,一定有过这样的疑问吧? - -1、数据包是如何从你的电脑上,到达远端的服务器上的? - -3、OSI 七层到底是如何工作的?怎么把这么抽象的概念化为具体呢? - - - -这些个问题,在我刚刚接触计算机网络的时候,也是困扰了我许久,今天呢,我就围绕【当你在浏览器上敲入网址时,在计算机网络的世界里,都发生了哪些事?】这个问题,把上面的那些问题一个一个地给你解决了。 - - - -## 1. 应用层:浏览器 - -浏览器作用在 OSI 模型里的第七层,它的主要工作就是将你的请求封装成 HTTP 报文然后交给它的下一层,也就是传输层。 - -通过之前的学习,我们知道了 - -1. 传输层的 TCP 报文,需要浏览器进程的端口号(源端口)以及服务器端口号 (目标端口)。 -2. 网络层的 IP 报文,需要有本机的 ip 地址(源ip)以及服务器的ip地址(目标ip)。 - -这意味着,在浏览器调用 socket 模块发送 HTTP 报文前,需要提前准备好目标端口号和服务器的ip地址。 - -- **目标端口**:如果往地址栏输入 `localhost:8080`时,浏览器就知道你要请求的服务器端口是 8080 了,但是我们看到的更多是不带的,比如 `htpps:/www.baidu.com`,因为它们有默认端口号,http 协议的默认端口号是 80,而 https 的默认端口号是 443。 -- **目标ip**:正常我们往浏览器的地址栏输入的都不会直接输入ip,而是域名,比如 `https://www.baidu.com `,浏览器发现你输入了个域名后,会先封装一个DNS 报文, 走的 UDP 协议,去 DNS 服务器获取 `www.baidu.com` 的 ip 地址。 - -有了目标端口和目标 ip 后,自然就能封装 TCP 报文了。 - -## 2. 传输层:TCP 协议 - -HTTP 是基于 TCP 进行可靠传输的。 - - - diff --git a/source/c10/c10_12.rst b/source/c10/c10_12.rst deleted file mode 100644 index cb1bb46..0000000 --- a/source/c10/c10_12.rst +++ /dev/null @@ -1,48 +0,0 @@ -10.12 数据包的网络之旅 -====================== - -|image0| - -当你在学习枯燥的网络基础知识的时候,一定有过这样的疑问吧? - -1、数据包是如何从你的电脑上,到达远端的服务器上的? - -3、OSI 七层到底是如何工作的?怎么把这么抽象的概念化为具体呢? - -这些个问题,在我刚刚接触计算机网络的时候,也是困扰了我许久,今天呢,我就围绕【当你在浏览器上敲入网址时,在计算机网络的世界里,都发生了哪些事?】这个问题,把上面的那些问题一个一个地给你解决了。 - -1. 应用层:浏览器 ------------------ - -浏览器作用在 OSI 模型里的第七层,它的主要工作就是将你的请求封装成 HTTP -报文然后交给它的下一层,也就是传输层。 - -通过之前的学习,我们知道了 - -1. 传输层的 TCP 报文,需要浏览器进程的端口号(源端口)以及服务器端口号 - (目标端口)。 -2. 网络层的 IP 报文,需要有本机的 ip - 地址(源ip)以及服务器的ip地址(目标ip)。 - -这意味着,在浏览器调用 socket 模块发送 HTTP -报文前,需要提前准备好目标端口号和服务器的ip地址。 - -- **目标端口**\ :如果往地址栏输入 - ``localhost:8080``\ 时,浏览器就知道你要请求的服务器端口是 8080 - 了,但是我们看到的更多是不带的,比如 - ``htpps:/www.baidu.com``\ ,因为它们有默认端口号,http - 协议的默认端口号是 80,而 https 的默认端口号是 443。 -- **目标ip**\ :正常我们往浏览器的地址栏输入的都不会直接输入ip,而是域名,比如 - ``https://www.baidu.com``\ ,浏览器发现你输入了个域名后,会先封装一个DNS - 报文, 走的 UDP 协议,去 DNS 服务器获取 ``www.baidu.com`` 的 ip - 地址。 - -有了目标端口和目标 ip 后,自然就能封装 TCP 报文了。 - -2. 传输层:TCP 协议 -------------------- - -HTTP 是基于 TCP 进行可靠传输的。 - -.. |image0| image:: http://image.iswbm.com/20200602135014.png - diff --git a/source/chapters/p10.rst b/source/chapters/p10.rst index b41a354..51f6755 100755 --- a/source/chapters/p10.rst +++ b/source/chapters/p10.rst @@ -1,12 +1,8 @@ ============================= -第十章:网络基础 +第十章:好库推荐 ============================= -在找工作面试的过程中,面试官非常喜欢考察基础知识,除了数据结构与算法之外,网络知识也是一个非常重要的考察对象。 - -而网络知识,通常是很抽象,不容易理解的,有很多同学就在这里裁了跟头。 - -正好以前没在这里分享过有关网络的内容,所以打算重新梳理有关网络的一些知识,这些内容在大家面试的时候可能能用得上。 +推荐那些冷门却非常实用的模块。 本章节,会持续更新,敬请关注… From 6c77d8a55764a6d2b28206b4903931727e675477 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 4 Oct 2020 15:49:19 +0800 Subject: [PATCH 127/147] update --- source/c10/c10_03.md | 2 +- source/c10/c10_03.rst | 4 ++-- source/c10/c10_04.md | 2 +- source/c10/c10_04.rst | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md index 3b8caf0..ddf915a 100644 --- a/source/c10/c10_03.md +++ b/source/c10/c10_03.md @@ -1,4 +1,4 @@ -# 1.47 少有人知的 Python "重试机制" +# 10.3 少有人知的 Python "重试机制":tenacity 为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst index 234bde4..f41d2df 100644 --- a/source/c10/c10_03.rst +++ b/source/c10/c10_03.rst @@ -1,5 +1,5 @@ -1.47 少有人知的 Python “重试机制” -================================= +10.3 少有人知的 Python “重试机制”:tenacity +=========================================== 为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 diff --git a/source/c10/c10_04.md b/source/c10/c10_04.md index 35846e7..09e772d 100644 --- a/source/c10/c10_04.md +++ b/source/c10/c10_04.md @@ -1,4 +1,4 @@ -# 1.34 最优雅的命令调用方式 +# 10.4 Linux 上最优雅的命令调用方式:sh ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c10/c10_04.rst b/source/c10/c10_04.rst index 65fcc70..c521e0b 100644 --- a/source/c10/c10_04.rst +++ b/source/c10/c10_04.rst @@ -1,5 +1,5 @@ -1.34 最优雅的命令调用方式 -========================= +10.4 Linux 上最优雅的命令调用方式:sh +===================================== |image0| From dd7dda83ee30f500354e4dfe04a8c5a1ed8e8e19 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 4 Oct 2020 20:58:28 +0800 Subject: [PATCH 128/147] update --- source/c01/c01_10.md | 193 ++++ source/c01/c01_10.rst | 2379 +++-------------------------------------- source/c01/c01_15.md | 82 +- source/c01/c01_15.rst | 96 +- source/c01/c01_21.md | 223 +++- source/c01/c01_21.rst | 250 ++++- source/c01/c01_22.md | 226 ++-- source/c01/c01_22.rst | 250 ++--- source/c01/c01_23.md | 4 +- source/c01/c01_23.rst | 7 +- source/c01/c01_24.md | 2 +- source/c01/c01_24.rst | 2 +- source/c01/c01_25.md | 2 +- source/c01/c01_25.rst | 2 +- source/c01/c01_26.md | 2 +- source/c01/c01_26.rst | 2 +- source/c01/c01_27.md | 2 +- source/c01/c01_27.rst | 2 +- source/c01/c01_28.md | 216 ---- source/c01/c01_29.md | 175 --- source/c02/c02_01.md | 4 +- source/c02/c02_01.rst | 8 +- source/c02/c02_05.md | 3 - source/c02/c02_05.rst | 6 +- source/c02/c02_06.md | 7 +- source/c02/c02_06.rst | 7 +- source/c02/c02_07.md | 10 +- source/c02/c02_07.rst | 20 +- source/c02/c02_08.md | 12 +- source/c02/c02_08.rst | 24 +- source/c02/c02_09.md | 10 +- source/c02/c02_09.rst | 20 +- source/c02/c02_10.md | 14 +- source/c02/c02_10.rst | 28 +- source/c02/c02_11.md | 4 +- source/c02/c02_11.rst | 8 +- source/c03/c03_01.md | 22 +- source/c03/c03_01.rst | 44 +- source/c03/c03_02.md | 12 +- source/c03/c03_02.rst | 24 +- source/c03/c03_03.md | 12 +- source/c03/c03_03.rst | 24 +- source/c03/c03_05.md | 8 +- source/c03/c03_05.rst | 16 +- source/c03/c03_06.md | 18 +- source/c03/c03_06.rst | 36 +- source/c03/c03_07.md | 4 - source/c03/c03_07.rst | 10 +- source/c04/c04_01.md | 8 +- source/c04/c04_01.rst | 16 +- source/c04/c04_02.md | 12 +- source/c04/c04_02.rst | 24 +- source/c04/c04_03.md | 20 +- source/c04/c04_03.rst | 38 +- source/c04/c04_05.md | 388 +++++-- source/c04/c04_05.rst | 455 +++++--- source/c04/c04_11.md | 18 +- source/c04/c04_11.rst | 36 +- source/c04/c04_12.md | 8 +- source/c04/c04_12.rst | 16 +- source/c04/c04_16.md | 706 +++++++++++- source/c04/c04_16.rst | 759 ++++++++++++- source/c04/c04_19.md | 689 ------------ source/c04/c04_20.md | 378 ------- source/c04/c04_22.md | 6 - source/c10/c10_03.md | 2 + source/c10/c10_03.rst | 7 +- 67 files changed, 3330 insertions(+), 4788 deletions(-) create mode 100644 source/c01/c01_10.md delete mode 100644 source/c01/c01_28.md delete mode 100644 source/c01/c01_29.md delete mode 100644 source/c04/c04_19.md delete mode 100644 source/c04/c04_20.md delete mode 100644 source/c04/c04_22.md diff --git a/source/c01/c01_10.md b/source/c01/c01_10.md new file mode 100644 index 0000000..1538e31 --- /dev/null +++ b/source/c01/c01_10.md @@ -0,0 +1,193 @@ +# 1.10 如何修改 CentOS 6.x 上默认Python + +![](http://image.iswbm.com/20200602135014.png) + +最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 CentOS 6.x,有的是 CentOS 7.x 。 + +需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x 上的 Python 版本是 2.7.x 的,这意味着我要实现的功能要适配这两种版本的系统。 + +你可能会说,这有什么的,自己写的时候,注意一下就好了。 + +事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的 Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和 CentOS 7.x 将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要 Python2.7,而 CentOS 6.x 上只有 Python2.6。 + +这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将 CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。 + +下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。 + + + + +1. 首先确认下你机器上的默认的 Python 版本 + +```shell +$ python -V +Python 2.6.6 + +$ whereis python +python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz +``` + + + +2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。 + +注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。 + +比如不安装 zlib 会无法安装 setuptools,不装 openssl 和 openssl-devel,会无法使用 pip 工具等 + +``` +$ yum install gcc -y +$ yum groupinstall "Development tools" +$ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y +``` + + 如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。 + + + +3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录 + +``` +$ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz +$ tar zxvf Python-2.7.14.tgz +$ cd Python-2.7.14 +``` + + + +4. 配置,编译,安装 + +```shell +# --prefix 指定 python 安装的路径 +$ ./configure --prefix=/usr/local/python/python2.7 +$ make +$ make install +``` + +`./configure` 命令执行完毕之后创建一个文件creating Makefile,供下面的make命令使用 执行 `make install` 之后就会把程序安装到我们指定的目录中去。 + +Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure –help输出详细的选项列表。其中 `--prefix` 选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr /local/bin,库文件默认放在 `/usr/local/lib` ,配置文件默认放在 `/usr/local/etc` ,其它的资源文件放在 `/usr /local/share`。如果配置 `--prefix`,如:`./configure --prefix=/usr/local/test` 可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。 + +用了 `--prefix` 选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次 `make uninstall`,但前提是make文件指定过uninstall。 + + + +5. 查看系统的 Python 版本 + +```shell +$ python -V +Python 2.6.6 +``` + + 如果你查看还是 Python 2.6.6 版本,请继续看第六步。 + + + +6. 修改系统默认的 Python 版本 + +查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x + +```shell +# 这是我们刚安装的 Python +$/usr/local/bin/python2.7 -V +Python 2.7.14 + +# 这是系统默认 Python +$ /usr/bin/python -V +Python 2.6.6 + +# 备份原来的 Python 文件 +$ mv /usr/bin/python /usr/bin/python.bak + +# 建立软链接,将我们刚安装的 python2.7 做为系统默认版本 +ln -s /usr/local/bin/python2.7 /usr/bin/python + +# 再次查看 Python 版本,已经成功切换过来 +$ python -V +Python 2.7.14 +``` + +7. 重新指定 yum 的Python版本 + +上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum 是基于Python2.6 的,为了不影响 yum 的使用,需单独将yum指向python2.6版本。 + +编辑: vim /usr/bin/yum ,将` /usr/bin/python` 改成 `/usr/bin/python2.6` + +```python +#!/usr/bin/python2.6 +``` + + + +8. 安装 setuptools 及 pip + +pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools + +```shell +# 下载 setuptools +$ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4 +``` + +同样的,进行安装: + +```shell +$ tar vxf setuptools-21.0.0.tar.gz +$ cd setuptools-21.0.0 +$ python setup.py install +``` + +安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip + +```shell +# 下载 pip +wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7 +``` + +同样的,进行安装 + +```shell +$ tar vxf pip-8.1.1.tar.gz +$ cd pip-8.1.1 +$ python setup.py install +``` + +安装完成后,执行 `pip list` 查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包 `pip install requests` 。 + + + +8. 转移cloudinit + +上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit 的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ 目录下 + +![](http://image.iswbm.com/20190831160317.png) + +然后安装一些 cloudinit 的依赖包。 + +```shell +$ pip install six requests prettytable jsonpatch configobj + +# 默认还是安装在 python2.6 下 +$ yum install PyYAML -y + +# 将这些文件拷贝到 python2.7 目录下 +# 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印 +$ cd /usr/lib64/python2.6/site-packages +$ cp -r yaml/ /usr/local/lib/python2.7/site-packages/ +$ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/ +$ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/ +``` + +执行一下 cloudinit 的几个命令,没有问题,任务就完成了。 + +```shell +$ cloud-init init -l +$ cloud-init init +``` + + + +**参考文章** + +- https://www.cnblogs.com/stonehe/p/7944366.html + +![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c01/c01_10.rst b/source/c01/c01_10.rst index 2858656..7b9f5a7 100755 --- a/source/c01/c01_10.rst +++ b/source/c01/c01_10.rst @@ -1,2340 +1,207 @@ -1.10 Python 黑魔法指南 50 例 -============================ +1.10 如何修改 CentOS 6.x 上默认Python +===================================== |image0| --------------- +最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 +CentOS 6.x,有的是 CentOS 7.x 。 - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 +需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x +上的 Python 版本是 2.7.x +的,这意味着我要实现的功能要适配这两种版本的系统。 -01. 默默无闻的省略号很好用 --------------------------- +你可能会说,这有什么的,自己写的时候,注意一下就好了。 -在Python中,一切皆对象,省略号也不例外。 +事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的 +Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和 +CentOS 7.x +将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要 +Python2.7,而 CentOS 6.x 上只有 Python2.6。 -在 Python 3 中你可以直接写 ``...`` 来得到它 +这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将 +CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。 -.. code:: python - - >>> ... - Ellipsis - >>> type(...) - - -而在 Python 2 中没有\ ``...`` 这个语法,只能直接写Ellipsis来获取。 - -.. code:: python - - >>> Ellipsis - Ellipsis - >>> type(Ellipsis) - - >>> - -它转为布尔值时为真 - -.. code:: python - - >>> bool(...) - True - -最后,这东西是一个单例。 - -.. code:: python - - >>> id(...) - 4362672336 - >>> id(...) - 4362672336 +下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。 -那这东西有啥用呢? - -1. 它是 Numpy 的一个语法糖 -2. 在 Python 3 中可以使用 … 代替 pass +1. 首先确认下你机器上的默认的 Python 版本 .. code:: shell - $ cat demo.py - def func01(): - ... - - def func02(): - pass + $ python -V + Python 2.6.6 - func01() - func02() + $ whereis python + python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz - print("ok") +2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。 - $ python3 demo.py - ok +注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。 -02. 使用 end 来结束代码块 -------------------------- +比如不安装 zlib 会无法安装 setuptools,不装 openssl 和 +openssl-devel,会无法使用 pip 工具等 -有不少编程语言,循环、判断代码块需要用 end -标明结束,这样一定程度上会使代码逻辑更加清晰一点。 - -但是其实在 Python 这种严格缩进的语言里并没有必要这样做。 - -如果你真的想用,也不是没有办法,具体你看下面这个例子。 +:: -.. code:: python + $ yum install gcc -y + $ yum groupinstall "Development tools" + $ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y - __builtins__.end = None +如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。 +3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录 - def my_abs(x): - if x > 0: - return x - else: - return -x - end - end +:: - print(my_abs(10)) - print(my_abs(-10)) + $ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz + $ tar zxvf Python-2.7.14.tgz + $ cd Python-2.7.14 -执行后,输出如下 +4. 配置,编译,安装 .. code:: shell - [root@localhost ~]$ python demo.py - 10 - 10 - -03. 可直接运行的 zip 包 ------------------------ + # --prefix 指定 python 安装的路径 + $ ./configure --prefix=/usr/local/python/python2.7 + $ make + $ make install -我们可以经常看到有 Python 包,居然可以以 zip -包进行发布,并且可以不用解压直接使用。 +``./configure`` 命令执行完毕之后创建一个文件creating +Makefile,供下面的make命令使用 执行 ``make install`` +之后就会把程序安装到我们指定的目录中去。 -这与大多数人的认识的 Python 包格式不一样,正常人认为 Python 包的格式要嘛 -是 egg,要嘛是whl 格式。 +Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure +–help输出详细的选项列表。其中 ``--prefix`` +选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr +/local/bin,库文件默认放在 ``/usr/local/lib`` ,配置文件默认放在 +``/usr/local/etc`` ,其它的资源文件放在 +``/usr /local/share``\ 。如果配置 +``--prefix``\ ,如:\ ``./configure --prefix=/usr/local/test`` +可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。 -那么这个zip 是如何制作的呢,请看下面的示例。 - -.. code:: shell +用了 ``--prefix`` +选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次 +``make uninstall``\ ,但前提是make文件指定过uninstall。 - [root@localhost ~]# ls -l demo - total 8 - -rw-r--r-- 1 root root 30 May 8 19:27 calc.py - -rw-r--r-- 1 root root 35 May 8 19:33 __main__.py - [root@localhost ~]# - [root@localhost ~]# cat demo/__main__.py - import calc - - print(calc.add(2, 3)) - [root@localhost ~]# - [root@localhost ~]# cat demo/calc.py - def add(x, y): - return x+y - [root@localhost ~]# - [root@localhost ~]# python -m zipfile -c demo.zip demo/* - [root@localhost ~]# - -制作完成后,我们可以执行用 python 去执行它 +5. 查看系统的 Python 版本 .. code:: shell - [root@localhost ~]# python demo.zip - 5 - [root@localhost ~]# - -04. 反斜杠的倔强: 不写最后 --------------------------- - -``\`` 在 Python 中的用法主要有两种 - -**1、在行尾时,用做续行符** - -.. code:: python - - [root@localhost ~]$ cat demo.py - print("hello "\ - "world") - [root@localhost ~]$ - [root@localhost ~]$ python demo.py - hello world - -**2、在字符串中,用做转义字符,可以将普通字符转化为有特殊含义的字符。** - -.. code:: python - - >>> str1='\nhello'  #换行 - >>> print(str1) - - hello - >>> str2='\thello'  #tab - >>> print(str2) - hello - -但是如果你用单\ ``\``\ 结尾是会报语法错误的 - -.. code:: python - - >>> str3="\" - File "", line 1 - str3="\" - ^ - SyntaxError: EOL while scanning string literal - -就算你指定它是个 raw 字符串,也不行。 - -.. code:: python - - >>> str3=r"\" - File "", line 1 - str3=r"\" - ^ - SyntaxError: EOL while scanning string literal - -05. 单行实现 for 死循环如何写? -------------------------------- - -如果让你在不借助 while ,只使用 for 来写一个死循环? - -**你会写吗?** - -**如果你还说简单,你可以自己试一下。** - -… - -如果你尝试后,仍然写不出来,那我给出自己的做法。 - -.. code:: python - - for i in iter(int, 1):pass - -**是不是傻了?iter 还有这种用法?这为啥是个死循环?** - -关于这个问题,你如果看中文网站,可能找不到相关资料。 - -还好你可以通过 IDE 看py源码里的注释内容,介绍了很详细的使用方法。 - -原来iter有两种使用方法。 - -- 通常我们的认知是第一种,将一个列表转化为一个迭代器。 - -- 而第二种方法,他接收一个 callable对象,和一个sentinel - 参数。第一个对象会一直运行,直到它返回 sentinel 值才结束。 - -那\ ``int`` 呢? - -这又是一个知识点,int -是一个内建方法。通过看注释,可以看出它是有默认值0的。你可以在console -模式下输入 ``int()`` 看看是不是返回0。 - -由于int() 永远返回0,永远返回不了1,所以这个 for -循环会没有终点。一直运行下去。 - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -06. 懒人必备技能:使用 “_” --------------------------- - -对于 ``_`` ,大家对于他的印象都是用于 -**占位符**\ ,省得为一个不需要用到的变量,绞尽脑汁的想变量名。 + $ python -V + Python 2.6.6 -今天要介绍的是他的第二种用法,就是在 console 模式下的应用。 +如果你查看还是 Python 2.6.6 版本,请继续看第六步。 -示例如下: +6. 修改系统默认的 Python 版本 -.. code:: python - - >>> 3 + 4 - 7 - >>> _ - 7 - >>> name='公众号: Python编程时光' - >>> name - '公众号: Python编程时光' - >>> _ - '公众号: Python编程时光' - -它可以返回上一次的运行结果。 - -但是,如果是print函数打印出来的就不行了。 - -.. code:: python - - >>> 3 + 4 - 7 - >>> _ - 7 - >>> print("公众号: Python编程时光") - ming - >>> _ - 7 - -我自己写了个例子,验证了下,用\ ``__repr__``\ 输出的内容可以被获取到的。 -首先,在我们的目录下,写一个文件 demo.py。内容如下 - -.. code:: python - - # demo.py - class mytest(): - def __str__(self): - return "hello" - - def __repr__(self): - return "world" - -然后在这个目录下进入交互式环境。 - -.. code:: python - - >>> import demo - >>> mt=demo.mytest() - >>> mt - world - >>> print(mt) - hello - >>> _ - world - -知道这两个魔法方法的人,一看就明白了,这里不再解释啦。 - -07. 最快查看包搜索路径的方式 ----------------------------- - -当你使用 import 导入一个包或模块时,Python -会去一些目录下查找,而这些目录是有优先级顺序的,正常人会使用 sys.path -查看。 - -.. code:: python - - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.path) - ['', - '/usr/local/Python3.7/lib/python37.zip', - '/usr/local/Python3.7/lib/python3.7', - '/usr/local/Python3.7/lib/python3.7/lib-dynload', - '/home/wangbm/.local/lib/python3.7/site-packages', - '/usr/local/Python3.7/lib/python3.7/site-packages'] - >>> - -那有没有更快的方式呢? - -我这有一种连 console 模式都不用进入的方法,一行命令即可解决 +查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x .. code:: shell - [wangbm@localhost ~]$ python3 -m site - sys.path = [ - '/home/wangbm', - '/usr/local/Python3.7/lib/python37.zip', - '/usr/local/Python3.7/lib/python3.7', - '/usr/local/Python3.7/lib/python3.7/lib-dynload', - '/home/wangbm/.local/lib/python3.7/site-packages', - '/usr/local/Python3.7/lib/python3.7/site-packages', - ] - USER_BASE: '/home/wangbm/.local' (exists) - USER_SITE: '/home/wangbm/.local/lib/python3.7/site-packages' (exists) - ENABLE_USER_SITE: True - -从输出你可以发现,这个列的路径会比 sys.path -更全,它包含了用户环境的目录。 - -08. and 和 or 的取值顺序 ------------------------- - -and 和 or 是我们再熟悉不过的两个逻辑运算符,在 Python 也有它有妙用。 - -- 当一个 **or 表达式**\ 中所有值都为真,Python会选择第一个值 - -- 当一个 **and 表达式** 所有值都为真,Python 会选择第二个值。 - -示例如下: - -.. code:: python + # 这是我们刚安装的 Python + $/usr/local/bin/python2.7 -V + Python 2.7.14 - >>>(2 or 3) * (5 and 7) - 14 # 2*7 + # 这是系统默认 Python + $ /usr/bin/python -V + Python 2.6.6 -09. 如何修改解释器提示符 ------------------------- + # 备份原来的 Python 文件 + $ mv /usr/bin/python /usr/bin/python.bak -这个当做今天的一个小彩蛋吧。应该算是比较冷门的,估计知道的人很少了吧。 + # 建立软链接,将我们刚安装的 python2.7 做为系统默认版本 + ln -s /usr/local/bin/python2.7 /usr/bin/python -正常情况下,我们在 终端下 执行Python 命令是这样的。 + # 再次查看 Python 版本,已经成功切换过来 + $ python -V + Python 2.7.14 -.. code:: python +7. 重新指定 yum 的Python版本 - >>> for i in range(2): - ... print (i) - ... - 0 - 1 +上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum +是基于Python2.6 的,为了不影响 yum +的使用,需单独将yum指向python2.6版本。 -你是否想过 ``>>>`` 和 ``...`` 这两个提示符也是可以修改的呢? +编辑: vim /usr/bin/yum ,将\ ``/usr/bin/python`` 改成 +``/usr/bin/python2.6`` .. code:: python - >>> import sys - >>> sys.ps1 - '>>> ' - >>> sys.ps2 - '... ' - >>> - >>> sys.ps2 = '---------------- ' - >>> sys.ps1 = 'Python编程时光>>>' - Python编程时光>>>for i in range(2): - ---------------- print (i) - ---------------- - 0 - 1 - -10. 逗号也有它独特的用法 ------------------------- + #!/usr/bin/python2.6 -逗号,虽然是个很不起眼的符号,但在 Python 中也有他的用武之地。 +8. 安装 setuptools 及 pip -**第一个用法** - -元组的转化 +pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools .. code:: shell - [root@localhost ~]# cat demo.py - def func(): - return "ok", - - print(func()) - [root@localhost ~]# python3 demo.py - ('ok',) + # 下载 setuptools + $ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4 -**第二个用法** - -print 的取消换行 +同样的,进行安装: .. code:: shell - [root@localhost ~]# cat demo.py - for i in range(3): - print i - [root@localhost ~]# - [root@localhost ~]# python demo.py - 0 - 1 - 2 - [root@localhost ~]# - [root@localhost ~]# vim demo.py - [root@localhost ~]# - [root@localhost ~]# cat demo.py - for i in range(3): - print i, - [root@localhost ~]# - [root@localhost ~]# python demo.py - 0 1 2 - [root@localhost ~]# - -.. - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -11. 默认参数最好不为可变对象 ----------------------------- + $ tar vxf setuptools-21.0.0.tar.gz + $ cd setuptools-21.0.0 + $ python setup.py install -函数的参数分三种 - 可变参数 - 默认参数 - 关键字参数 +安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip -当你在传递默认参数时,有新手很容易踩雷的一个坑。 - -先来看一个示例 - -.. code:: python - - def func(item, item_list=[]): - item_list.append(item) - print(item_list) - - func('iphone') - func('xiaomi', item_list=['oppo','vivo']) - func('huawei') +.. code:: shell -在这里,你可以暂停一下,思考一下会输出什么? + # 下载 pip + wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7 -思考过后,你的答案是否和下面的一致呢 +同样的,进行安装 -:: +.. code:: shell - ['iphone'] - ['oppo', 'vivo', 'xiaomi'] - ['iphone', 'huawei'] + $ tar vxf pip-8.1.1.tar.gz + $ cd pip-8.1.1 + $ python setup.py install -如果是,那你可以跳过这部分内容,如果不是,请接着往下看,这里来分析一下。 +安装完成后,执行 ``pip list`` +查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包 +``pip install requests`` 。 -Python 中的 def -语句在每次执行的时候都初始化一个函数对象,这个函数对象就是我们要调用的函数,可以把它当成一个一般的对象,只不过这个对象拥有一个可执行的方法和部分属性。 +8. 转移cloudinit -对于参数中提供了初始值的参数,由于 Python -中的函数参数传递的是对象,也可以认为是传地址,在第一次初始化 def -的时候,会先生成这个可变对象的内存地址,然后将这个默认参数 item_list -会与这个内存地址绑定。在后面的函数调用中,如果调用方指定了新的默认值,就会将原来的默认值覆盖。如果调用方没有指定新的默认值,那就会使用原来的默认值。 +上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit +的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ +目录下 |image1| -12. 访问类中的私有方法 ----------------------- - -大家都知道,类中可供直接调用的方法,只有公有方法(protected类型的方法也可以,但是不建议)。也就是说,类的私有方法是无法直接调用的。 - -这里先看一下例子 - -.. code:: python - - class Kls(): - def public(self): - print('Hello public world!') - - def __private(self): - print('Hello private world!') - - def call_private(self): - self.__private() - - ins = Kls() - - # 调用公有方法,没问题 - ins.public() - - # 直接调用私有方法,不行 - ins.__private() - - # 但你可以通过内部公有方法,进行代理 - ins.call_private() - -既然都是方法,那我们真的没有方法可以直接调用吗? - -当然有啦,只是建议你千万不要这样弄,这里只是普及,让你了解一下。 - -.. code:: python - - # 调用私有方法,以下两种等价 - ins._Kls__private() - ins.call_private() - -13. 时有时无的切片异常 ----------------------- - -这是个简单例子,alist 只有5 个元素,当你取第 6 -个元素时,会抛出索引异常。这与我们的认知一致。 +然后安装一些 cloudinit 的依赖包。 -.. code:: python - - >>> alist = [0, 1, 2, 3, 4] - >>> alist[5] - Traceback (most recent call last): - File "", line 1, in - IndexError: list index out of range - -但是当你使用 alist[5:] 取一个区间时,即使 alist 并没有 第 -6个元素,也不抛出异常,而是会返回一个新的列表。 - -.. code:: python - - >>> alist = [0, 1, 2, 3, 4] - >>> alist[5:] - [] - >>> alist[100:] - [] - -14. 哪些情况下不需要续行符? ----------------------------- - -在写代码时,为了代码的可读性,代码的排版是尤为重要的。 - -为了实现高可读性的代码,我们常常使用到的就是续行符 ``\``\ 。 - -:: - - >>> a = 'talk is cheap,'\ - ... 'show me the code.' - >>> - >>> print(a) - talk is cheap,show me the code. - -那有哪些情况下,是不需要写续行符的呢? - -经过总结,在这些符号中间的代码换行可以省略掉续行符:\ ``[]``,\ ``()``,\ ``{}`` - -:: - - >>> my_list=[1,2,3, - ... 4,5,6] - - >>> my_tuple=(1,2,3, - ... 4,5,6) - - >>> my_dict={"name": "MING", - ... "gender": "male"} - -另外还有,在多行文本注释中 ``'''`` ,续行符也是可以不写的。 - -:: - - >>> text = '''talk is cheap, - ... show me the code''' - -15. Python2下 也能使用 print(“”) --------------------------------- - -可能会有不少人,觉得只有 Python 3 才可以使用 print(),而 Python 2 -只能使用\ ``print ""``\ 。 - -但是其实并不是这样的。 - -在Python 2.6之前,只支持 - -.. code:: python - - print "hello" - -在Python 2.6和2.7中,可以支持如下三种 - -.. code:: python - - print "hello" - print("hello") - print ("hello") - -在Python3.x中,可以支持如下两种 - -.. code:: python - - print("hello") - print ("hello") - -虽然 在 Python 2.6+ 可以和 Python3.x+ 一样,像函数一样去调用 print -,但是这仅用于两个 python 版本之间的代码兼容,并不是说在 -python2.6+下使用 print() 后,就成了函数。 - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -16. 迷一样的字符串 ------------------- - -示例一 - -.. code:: python - - # Python2.7 - >>> a = "Hello_Python" - >>> id(a) - 32045616 - >>> id("Hello" + "_" + "Python") - 32045616 - - # Python3.7 - >>> a = "Hello_Python" - >>> id(a) - 38764272 - >>> id("Hello" + "_" + "Python") - 32045616 - -示例二 - -.. code:: python - - >>> a = "MING" - >>> b = "MING" - >>> a is b - True - - # Python2.7 - >>> a, b = "MING!", "MING!" - >>> a is b - True - - # Python3.7 - >>> a, b = "MING!", "MING!" - >>> a is b - False - -示例三 - -.. code:: python - - # Python2.7 - >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' - True - >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' - False - - # Python3.7 - >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' - True - >>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' - True - -17. return不一定都是函数的终点 ------------------------------- - -众所周知,try…finally… -的用法是:不管try里面是正常执行还是有报异常,最终都能保证finally能够执行。 - -同时我们又知道,一个函数里只要遇到 return 函数就会立马结束。 - -那问题就来了,以上这两种规则,如果同时存在,Python -解释器会如何选择?哪个优先级更高? - -写个示例验证一下,就明白啦 - -.. code:: python - - >>> def func(): - ... try: - ... return 'try' - ... finally: - ... return 'finally' - ... - >>> func() - 'finally' - -从输出中,我们可以发现:在try…finally…语句中,try中的 return -会被直接忽视(这里的 return 不是函数的终点),因为要保证 finally -能够执行。 - -**如果 try 里的 return 真的是直接被忽视吗?** - -我们都知道如果一个函数没有 return,会隐式的返回 None,假设 try 里的 -return 真的是直接被忽视,那当finally 下没有显式的 return -的时候,是不是会返回None呢? - -还是写个 示例来验证一下: - -.. code:: python - - >>> def func(): - ... try: - ... return 'try' - ... finally: - ... print('finally') - ... - >>> - >>> func() - finally - 'try' - >>> - -从结果来看,当 finally 下没有 reutrn ,其实 try 里的 return -仍然还是有效的。 - -那结论就出来了,如果 finally 里有显式的 return,那么这个 return -会直接覆盖 try 里的 return,而如果 finally 里没有 显式的 return,那么 -try 里的 return 仍然有效。 - -18. 用户无感知的小整数池 ------------------------- - -为避免整数频繁申请和销毁内存空间,Python 定义了一个小整数池 [-5, 256] -这些整数对象是提前建立好的,不会被垃圾回收。 - -以上代码请在 终端Python环境下测试,如果你是在IDE中测试,由于 IDE -的影响,效果会有所不同。 - -.. code:: python - - >>> a = -6 - >>> b = -6 - >>> a is b - False - - >>> a = 256 - >>> b = 256 - >>> a is b - True - - >>> a = 257 - >>> b = 257 - >>> a is b - False - - >>> a = 257; b = 257 - >>> a is b - True - -**问题又来了:最后一个示例,为啥是True?** - -因为当你在同一行里,同时给两个变量赋同一值时,解释器知道这个对象已经生成,那么它就会引用到同一个对象。如果分成两成的话,解释器并不知道这个对象已经存在了,就会重新申请内存存放这个对象。 - -19. 神奇的 intern 机制 ----------------------- - -字符串类型作为Python中最常用的数据类型之一,Python解释器为了提高字符串使用的效率和使用性能,做了很多优化. - -例如:Python解释器中使用了 -intern(字符串驻留)的技术来提高字符串效率,什么是intern机制?就是同样的字符串对象仅仅会保存一份,放在一个字符串储蓄池中,是共用的,当然,肯定不能改变,这也决定了字符串必须是不可变对象。 - -:: - - >>> s1="hello" - >>> s2="hello" - >>> s1 is s2 - True - - # 如果有空格,默认不启用intern机制 - >>> s1="hell o" - >>> s2="hell o" - >>> s1 is s2 - False - - # 如果一个字符串长度超过20个字符,不启动intern机制 - >>> s1 = "a" * 20 - >>> s2 = "a" * 20 - >>> s1 is s2 - True - - >>> s1 = "a" * 21 - >>> s2 = "a" * 21 - >>> s1 is s2 - False - - >>> s1 = "ab" * 10 - >>> s2 = "ab" * 10 - >>> s1 is s2 - True - - >>> s1 = "ab" * 11 - >>> s2 = "ab" * 11 - >>> s1 is s2 - False - -20. 反转字符串/列表最优雅的方式 -------------------------------- - -反转序列并不难,但是如何做到最优雅呢? - -先来看看,正常是如何反转的。 - -最简单的方法是使用列表自带的reverse()方法。 - -.. code:: python - - >>> ml = [1,2,3,4,5] - >>> ml.reverse() - >>> ml - [5, 4, 3, 2, 1] - -但如果你要处理的是字符串,reverse就无能为力了。你可以尝试将其转化成list,再reverse,然后再转化成str。转来转去,也太麻烦了吧?需要这么多行代码(后面三行是不能合并成一行的),一点都Pythonic。 - -.. code:: python - - mstr1 = 'abc' - ml1 = list(mstr1) - ml1.reverse() - mstr2 = str(ml1) - -对于字符串还有一种稍微复杂一点的,是自定义递归函数来实现。 - -.. code:: python - - def my_reverse(str): - if str == "": - return str - else: - return my_reverse(str[1:]) + str[0] - -在这里,介绍一种最优雅的反转方式,使用切片,不管你是字符串,还是列表,简直通杀。 - -.. code:: python - - >>> mstr = 'abc' - >>> ml = [1,2,3] - >>> mstr[::-1] - 'cba' - >>> ml[::-1] - [3, 2, 1] - -.. - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -21. 改变默认递归次数限制 ------------------------- - -上面才提到递归,大家都知道使用递归是有风险的,递归深度过深容易导致堆栈的溢出。如果你这字符串太长啦,使用递归方式反转,就会出现问题。 - -那到底,默认递归次数限制是多少呢? - -.. code:: python - - >>> import sys - >>> sys.getrecursionlimit() - 1000 - -可以查,当然也可以自定义修改次数,退出即失效。 +.. code:: shell -.. code:: python + $ pip install six requests prettytable jsonpatch configobj - >>> sys.setrecursionlimit(2000) - >>> sys.getrecursionlimit() - 2000 + # 默认还是安装在 python2.6 下 + $ yum install PyYAML -y -22. 一行代码实现FTP服务器 -------------------------- + # 将这些文件拷贝到 python2.7 目录下 + # 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印 + $ cd /usr/lib64/python2.6/site-packages + $ cp -r yaml/ /usr/local/lib/python2.7/site-packages/ + $ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/ + $ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/ -搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?很简单,使用Python中的SimpleHTTPServer。 +执行一下 cloudinit 的几个命令,没有问题,任务就完成了。 -SimpleHTTPServer是Python -2自带的一个模块,是Python的Web服务器。它在Python -3已经合并到http.server模块中。具体例子如下,如不指定端口,则默认是8000端口。 +.. code:: shell -.. code:: python + $ cloud-init init -l + $ cloud-init init - # python2 - python -m SimpleHTTPServer 8888 +**参考文章** - # python3 - python3 -m http.server 8888 +- https://www.cnblogs.com/stonehe/p/7944366.html |image2| -SimpleHTTPServer有一个特性,如果待共享的目录下有index.html,那么index.html文件会被视为默认主页;如果不存在index.html文件,那么就会显示整个目录列表。 - -23. 让你晕头转向的 else 用法 ----------------------------- - -if else -用法可以说最基础的语法表达式之一,但是今天不是讲这个的,一定要讲点不一样的。 - -if else 早已烂大街,但可能有很多人都不曾见过 for else 和 try else -的用法。为什么说它曾让我晕头转向,因为它不像 if else -那么直白,非黑即白,脑子经常要想一下才能才反应过来代码怎么走。反正我是这样的。 - -先来说说,for else - -.. code:: python - - def check_item(source_list, target): - for item in source_list: - if item == target: - print("Exists!") - break - - else: - print("Does not exist") - -在往下看之前,你可以思考一下,什么情况下才会走 else。是循环被 -break,还是没有break? - -给几个例子,你体会一下。 - -.. code:: python - - check_item(["apple", "huawei", "oppo"], "oppo") - # Exists! - - check_item(["apple", "huawei", "oppo"], "vivo") - # Does not exist - -可以看出,没有被 break 的程序才会正常走else流程。 - -再来看看,try else 用法。 - -.. code:: python - - def test_try_else(attr1 = None): - try: - if attr1: - pass - else: - raise - except: - print("Exception occurred...") - else: - print("No Exception occurred...") - -同样来几个例子。当不传参数时,就抛出异常。 - -.. code:: python - - test_try_else() - # Exception occurred... - - test_try_else("ming") - # No Exception occurred... - -可以看出,没有 try 里面的代码块没有抛出异常的,会正常走else。 - -总结一下,for else 和 try else 相同,只要代码正常走下去不被 -break,不抛出异常,就可以走else。 - -24. 字符串里的缝隙是什么? --------------------------- - -在Python中求一个字符串里,某子字符(串)出现的次数。 - -大家都懂得使用 count() 函数,比如下面几个常规例子: - -.. code:: python - - >>> "aabb".count("a") - 2 - >>> "aabb".count("b") - 2 - >>> "aabb".count("ab") - 1 - -但是如果我想计算空字符串的个数呢? - -.. code:: python - - >>> "aabb".count("") - 5 - -**奇怪了吧?** - -不是应该返回 0 吗?怎么会返回 5? - -实际上,在 Python 看来,两个字符之间都是一个空字符,通俗的说就是缝隙。 - -因此 对于 ``aabb`` 这个字符串在 Python 来看应该是这样的 - -|image3| - -理解了这个“**缝隙**” 的概念后,以下这些就好理解了。 - -.. code:: python - - >>> (" " * 10).count("") - 11 - >>> - >>> "" in "" - True - >>> - >>> "" in "M" - True - -25. 正负得正,负负得正 ----------------------- - -从初中开始,我们就开始接触了\ ``负数`` ,并且都知道了\ ``负负得正`` -的思想。 - -Python 作为一门高级语言,它的编写符合人类的思维逻辑,包括 ``负负得正`` -。 - -.. code:: python - - >>> 5-3 - 2 - >>> 5--3 - 8 - >>> 5+-3 - 2 - >>> 5++3 - 8 - >>> 5---3 - 2 - -.. - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -26. 数值与字符串的比较 ----------------------- - -在 Python2 中,数字可以与字符串直接比较。结果是数值永远比字符串小。 - -.. code:: python - - >>> 100000000 < "" - True - >>> 100000000 < "hello" - True - -但在 Python3 中,却不行。 - -.. code:: python - - >>> 100000000 < "" - TypeError: '<' not supported between instances of 'int' and 'str' - -27. 循环中的局部变量泄露 ------------------------- - -在Python 2中 x 的值在一个循环执行之后被改变了。 - -.. code:: python - - # Python2 - >>> x = 1 - >>> [x for x in range(5)] - [0, 1, 2, 3, 4] - >>> x - 4 - -不过在Python3 中这个问题已经得到解决了。 - -.. code:: python - - # Python3 - >>> x = 1 - >>> [x for x in range(5)] - [0, 1, 2, 3, 4] - >>> x - 1 - -28. 字典居然是可以排序的? --------------------------- - -在 Python 3.6 之前字典不可排序的思想,似乎已经根深蒂固。 - -.. code:: python - - # Python2.7.10 - >>> mydict = {str(i):i for i in range(5)} - >>> mydict - {'1': 1, '0': 0, '3': 3, '2': 2, '4': 4} - -假如哪一天,有人跟你说字典也可以是有序的,不要惊讶,那确实是真的 - -在 Python3.6 + -中字典已经是有序的,并且效率相较之前的还有所提升,具体信息你可以去查询相关资料。 - -.. code:: python - - # Python3.6.7 - >>> mydict = {str(i):i for i in range(5)} - >>> mydict - {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} - -29. 有趣但没啥用的 import 用法 ------------------------------- - -import 是 Python 导包的方式。 - -你知道 Python 中内置了一些很有(wu)趣(liao)的包吗? - -**Hello World** - -:: - - >>> import __hello__ - Hello World! - -**Python之禅** - -:: - - >>> import this - - The Zen of Python, by Tim Peters - - Beautiful is better than ugly. - Explicit is better than implicit. - Simple is better than complex. - Complex is better than complicated. - Flat is better than nested. - Sparse is better than dense. - Readability counts. - Special cases aren't special enough to break the rules. - Although practicality beats purity. - Errors should never pass silently. - Unless explicitly silenced. - In the face of ambiguity, refuse the temptation to guess. - There should be one-- and preferably only one --obvious way to do it. - Although that way may not be obvious at first unless you're Dutch. - Now is better than never. - Although never is often better than *right* now. - If the implementation is hard to explain, it's a bad idea. - If the implementation is easy to explain, it may be a good idea. - Namespaces are one honking great idea -- let's do more of those! - -**反地心引力漫画** - -在 cmd 窗口中导入\ ``antigravity`` - -:: - - >>> import antigravity - -就会自动打开一个网页。 |image4| - -30. 局部/全局变量傻傻分不清 ---------------------------- - -在开始讲之前,你可以试着运行一下下面这小段代码。 - -.. code:: python - - # demo.py - a = 1 - - def add(): - a += 1 - - add() - -看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下: - -.. code:: python - - $ python demo.py - Traceback (most recent call last): - File "demo.py", line 6, in - add() - File "demo.py", line 4, in add - a += 1 - UnboundLocalError: local variable 'a' referenced before assignment - -回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。 - -当程序运行到 ``a += 1`` 时,Python 解释器就认为在函数内部要给 ``a`` -这个变量赋值,当然就把 ``a`` 当做局部变量了,但是做为局部变量的 a -还没有被还没被定义。 - -因此报错是正常的。 - -理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢? - -.. code:: python - - $ cat demo.py - a = 1 - - def output(): - print(a) - - output() - - $ python demo.py - 1 - -.. - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -31. 字母也玩起了障眼法 ----------------------- - -以下我分别在 Python2.7 和 Python 3.7 的 console 模式下,运行了如下代码。 - -**在Python 2.x 中** - -:: - - >>> valuе = 32 - File "", line 1 - valuе = 32 - ^ - SyntaxError: invalid syntax - -**在Python 3.x 中** - -:: - - >>> valuе = 32 - >>> value - 11 - -什么?没有截图你不信? - -|image5| - -如果你在自己的电脑上尝试一下,结果可能是这样的 - -|image6| - -**怎么又好了呢?** - -如果你想复现的话,请复制我这边给出的代码:\ ``valuе = 32`` - -**这是为什么呢?** - -原因在于,我上面使用的 value 变量名里的 ``е`` 又不是我们熟悉的 -``e``\ ,它是 Cyrillic(西里尔)字母。 - -:: - - >>> ord('е') # cyrillic 'e' (Ye) - 1077 - >>> ord('e') # latin 'e', as used in English and typed using standard keyboard - 101 - >>> 'е' == 'e' - False - -细思恐极,在这里可千万不要得罪同事们,万一离职的时候,对方把你项目里的 -``e`` 全局替换成 ``e``\ ,到时候你就哭去吧,肉眼根本看不出来嘛。 - -32. 字符串的分割技巧 --------------------- - -当我们对字符串进行分割时,且分割符是 -``\n``\ ,有可能会出现这样一个窘境: - -.. code:: python - - >>> str = "a\nb\n" - >>> print(str) - a - b - - >>> str.split('\n') - ['a', 'b', ''] - >>> - -会在最后一行多出一个元素,为了应对这种情况,你可以会多加一步处理。 - -但我想说的是,完成没有必要,对于这个场景,你可以使用 ``splitlines`` - -.. code:: python - - >>> str.splitlines() - ['a', 'b'] - -33. 嵌套上下文管理的另类写法 ----------------------------- - -当我们要写一个嵌套的上下文管理器时,可能会这样写 - -.. code:: python - - import contextlib - - @contextlib.contextmanager - def test_context(name): - print('enter, my name is {}'.format(name)) - - yield - - print('exit, my name is {}'.format(name)) - - with test_context('aaa'): - with test_context('bbb'): - print('========== in main ============') - -输出结果如下 - -.. code:: python - - enter, my name is aaa - enter, my name is bbb - ========== in main ============ - exit, my name is bbb - exit, my name is aaa - -除此之外,你可知道,还有另一种嵌套写法 - -.. code:: python - - with test_context('aaa'), test_context('bbb'): - print('========== in main ============') - -34. += 不等同于=+ ------------------ - -对列表 进行\ ``+=`` 操作相当于 extend,而使用 ``=+`` -操作是新增了一个列表。 - -因此会有如下两者的差异。 - -.. code:: python - - # =+ - >>> a = [1, 2, 3, 4] - >>> b = a - >>> a = a + [5, 6, 7, 8] - >>> a - [1, 2, 3, 4, 5, 6, 7, 8] - >>> b - [1, 2, 3, 4] - - - # += - >>> a = [1, 2, 3, 4] - >>> b = a - >>> a += [5, 6, 7, 8] - >>> a - [1, 2, 3, 4, 5, 6, 7, 8] - >>> b - [1, 2, 3, 4, 5, 6, 7, 8] - -35. 增量赋值的性能更好 ----------------------- - -诸如 ``+=`` 和 ``*=`` 这些运算符,叫做 增量赋值运算符。 - -这里使用用 += 举例,以下两种写法,在效果上是等价的。 - -:: - - # 第一种 - a = 1 ; a += 1 - - # 第二种 - a = 1; a = a + 1 - -``+=`` 其背后使用的魔法方法是 -\__iadd__,如果没有实现这个方法则会退而求其次,使用 \__add_\_ 。 - -这两种写法有什么区别呢? - -用列表举例 a += b,使用 \__add_\_ 的话就像是使用了a.extend(b),如果使用 -\__add_\_ 的话,则是 a = -a+b,前者是直接在原列表上进行扩展,而后者是先从原列表中取出值,在一个新的列表中进行扩展,然后再将新的列表对象返回给变量,显然后者的消耗要大些。 - -所以在能使用增量赋值的时候尽量使用它。 - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -36. x == +x 吗? ----------------- - -在大多数情况下,这个等式是成立的。 - -:: - - >>> n1 = 10086 - >>> n2 = +n1 - >>> - >>> n1 == n2 - True - -什么情况下,这个等式会不成立呢? - -由于Counter的机制,\ ``+`` 用于两个 Counter -实例相加,而相加的结果如果元素的个数 ``<=`` 0,就会被丢弃。 - -:: - - >>> from collections import Counter - >>> ct = Counter('abcdbcaa') - >>> ct - Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1}) - >>> ct['c'] = 0 - >>> ct['d'] = -2 - >>> - >>> ct - Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2}) - >>> - >>> +ct - Counter({'a': 3, 'b': 2}) - -37. 如何将 print 内容输出到文件 -------------------------------- - -Python 3 中的 print -作为一个函数,由于可以接收更多的参数,所以功能变为更加强大。 - -比如今天要说的使用 print -将你要打印的内容,输出到日志文件中(但是我并不推荐使用它)。 - -.. code:: python - - >>> with open('test.log', mode='w') as f: - ... print('hello, python', file=f, flush=True) - >>> exit() - - $ cat test.log - hello, python - -38. site-packages和 dist-packages ---------------------------------- - -如果你足够细心,你会在你的机器上,有些包是安装在 **site-packages** -下,而有些包安装在 **dist-packages** 下。 - -**它们有什么区别呢?** - -一般情况下,你只见过 site-packages 这个目录,而你所安装的包也将安装在 -这个目录下。 - -而 dist-packages 其实是 debian 系的 Linux 系统(如 -Ubuntu)才特有的目录,当你使用 apt 去安装的 Python 包会使用 -dist-packages,而你使用 pip 或者 easy_install 安装的包还是照常安装在 -site-packages 下。 - -Debian 这么设计的原因,是为了减少不同来源的 Python 之间产生的冲突。 - -如何查找 Python 安装目录 - -.. code:: python - - >>> from distutils.sysconfig import get_python_lib - >>> print(get_python_lib()) - /usr/lib/python2.7/site-packages - -39. argument 和 parameter 的区别 --------------------------------- - -arguments 和 parameter -的翻译都是参数,在中文场景下,二者混用基本没有问题,毕竟都叫参数嘛。 - -但若要严格再进行区分,它们实际上还有各自的叫法 - -- parameter:形参(\ **formal - parameter**\ ),体现在函数内部,作用域是这个函数体。 -- argument :实参(\ **actual parameter**\ ),调用函数实际传递的参数。 - -举个例子,如下这段代码,\ ``"error"`` 为 argument,而 msg 为 -``parameter``\ 。 - -.. code:: python - - def output_msg(msg): - print(msg) - - output_msg("error") - -40. 简洁而优雅的链式比较 ------------------------- - -先给你看一个示例: - -.. code:: python - - >>> False == False == True - False - -你知道这个表达式为什么会会返回 False 吗? - -它的运行原理与下面这个类似,是不是有点头绪了: - -.. code:: python - - if 80 < score <= 90: - print("成绩良好") - -如果你还是不明白,那我再给你整个第一个例子的等价写法。 - -.. code:: python - - >>> False == False and False == True - False - -这个用法叫做链式比较。 - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -41. 连接多个列表最极客的方式 ----------------------------- - -.. code:: python - - >>> a = [1,2] - >>> b = [3,4] - >>> c = [5,6] - >>> - >>> sum((a,b,c), []) - [1, 2, 3, 4, 5, 6] - -42. 另外 8 种连接列表的方式 ---------------------------- - -**1. 最直观的相加** - -使用 ``+`` 对多个列表进行相加,你应该懂,不多说了。 - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> list03 = [7,8,9] - >>> - >>> list01 + list02 + list03 - [1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> - -**2. 借助 itertools** - -itertools 在 Python -里有一个非常强大的内置模块,它专门用于操作可迭代对象。 - -在前面的文章中也介绍过,使用 ``itertools.chain()`` -函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 - -最后你再利用 list 将其转化为 列表。 - -.. code:: python - - >>> from itertools import chain - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> list03 = [7,8,9] - >>> - >>> list(chain(list01, list02, list03)) - [1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> - -**3. 使用 \* 解包** - -使用 ``*`` 可以解包列表,解包后再合并。 - -示例如下: - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> - >>> [*list01, *list02] - [1, 2, 3, 4, 5, 6] - >>> - -**4. 使用 extend** - -在字典中,使用 update 可实现原地更新,而在列表中,使用 extend -可实现列表的自我扩展。 - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> - >>> list01.extend(list02) - >>> list01 - [1, 2, 3, 4, 5, 6] - -**5. 使用列表推导式** - -Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 - -那就是列表解析式,集合解析式和字典解析式,通常是 Python -发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? - -当然可以,具体示例代码如下: - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> list03 = [7,8,9] - >>> - >>> [x for l in (list01, list02, list03) for x in l] - [1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> - -**6. 使用 heapq** - -heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 - -该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> list03 = [7,8,9] - >>> - >>> from heapq import merge - >>> - >>> list(merge(list01, list02, list03)) - [1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> - -要注意的是,heapq.merge -除了合并多个列表外,它还会将合并后的最终的列表进行排序。 - -.. code:: python - - >>> list01 = [2,5,3] - >>> list02 = [1,4,6] - >>> list03 = [7,9,8] - >>> - >>> from heapq import merge - >>> - >>> list(merge(list01, list02, list03)) - [1, 2, 4, 5, 3, 6, 7, 9, 8] - >>> - -它的效果等价于下面这行代码: - -.. code:: python - - sorted(itertools.chain(*iterables)) - -如果你希望得到一个始终有序的列表,那请第一时间想到 -heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 - -**7. 借助魔法方法** - -有一个魔法方法叫 ``__add__``\ ,当我们使用第一种方法 list01 + list02 -的时候,内部实际上是作用在 ``__add__`` 这个魔法方法上的。 - -所以以下两种方法其实是等价的 - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> - >>> list01 + list02 - [1, 2, 3, 4, 5, 6] - >>> - >>> - >>> list01.__add__(list02) - [1, 2, 3, 4, 5, 6] - >>> - -借用这个魔法特性,我们可以 reduce -这个方法来对多个列表进行合并,示例代码如下 - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> list03 = [7,8,9] - >>> - >>> from functools import reduce - >>> reduce(list.__add__, (list01, list02, list03)) - [1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> - -**8. 使用 yield from** - -在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 - -因此,我们可以像下面这样自定义一个合并列表的工具函数。 - -.. code:: python - - >>> list01 = [1,2,3] - >>> list02 = [4,5,6] - >>> list03 = [7,8,9] - >>> - >>> def merge(*lists): - ... for l in lists: - ... yield from l - ... - >>> list(merge(list01, list02, list03)) - [1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> - -43. 在程序退出前执行代码的技巧 ------------------------------- - -使用 atexit 这个内置模块,可以很方便的注册退出函数。 - -不管你在哪个地方导致程序崩溃,都会执行那些你注册过的函数。 - -示例如下 - -|image7| - -如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。 - -可能你有其他方法可以处理这种需求,但肯定比上不使用 atexit -来得优雅,来得方便,并且它很容易扩展。 - -但是使用 atexit 仍然有一些局限性,比如: - -- 如果程序是被你没有处理过的系统信号杀死的,那么注册的函数无法正常执行。 -- 如果发生了严重的 Python 内部错误,你注册的函数无法正常执行。 -- 如果你手动调用了\ ``os._exit()``\ ,你注册的函数无法正常执行。 - -44. 合并字典的 8 种方法 ------------------------ - -**1. 最简单的原地更新** - -字典对象内置了一个 update 方法,用于把另一个字典更新到自己身上。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> profile.update(ext_info) - >>> print(profile) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -如果想使用 update -这种最简单、最地道原生的方法,但又不想更新到自己身上,而是生成一个新的对象,那请使用深拷贝。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> from copy import deepcopy - >>> - >>> full_profile = deepcopy(profile) - >>> full_profile.update(ext_info) - >>> - >>> print(full_profile) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - >>> print(profile) - {"name": "xiaoming", "age": 27} - -**2. 先解包再合并字典** - -使用 ``**`` 可以解包字典,解包完后再使用 dict 或者 ``{}`` 就可以合并。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> full_profile01 = {**profile, **ext_info} - >>> print(full_profile01) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - >>> - >>> full_profile02 = dict(**profile, **ext_info) - >>> print(full_profile02) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -若你不知道 ``dict(**profile, **ext_info)`` 做了啥,你可以将它等价于 - -.. code:: python - - >>> dict((("name", "xiaoming"), ("age", 27), ("gender", "male"))) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -**3. 借助 itertools** - -在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 - -正好我们字典也是可迭代对象,自然就可以想到,可以使用 -``itertools.chain()`` -函数先将多个字典(可迭代对象)串联起来,组成一个更大的可迭代对象,然后再使用 -dict 转成字典。 - -.. code:: python - - >>> import itertools - >>> - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> - >>> dict(itertools.chain(profile.items(), ext_info.items())) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -**4. 借助 ChainMap** - -如果可以引入一个辅助包,那我就再提一个, ``ChainMap`` 也可以达到和 -``itertools`` 同样的效果。 - -.. code:: python - - >>> from collections import ChainMap - >>> - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> dict(ChainMap(profile, ext_info)) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -使用 ChainMap -有一点需要注意,当字典间有重复的键时,只会取第一个值,排在后面的键值并不会更新掉前面的(使用 -itertools 就不会有这个问题)。 - -.. code:: python - - >>> from collections import ChainMap - >>> - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info={"age": 30} - >>> dict(ChainMap(profile, ext_info)) - {'name': 'xiaoming', 'age': 27} - -**5. 使用dict.items() 合并** - -在 Python 3.9 之前,其实就已经有 ``|`` -操作符了,只不过它通常用于对集合(set)取并集。 - -利用这一点,也可以将它用于字典的合并,只不过得绕个弯子,有点不好理解。 - -你得先利用 ``items`` 方法将 dict 转成 dict_items,再对这两个 dict_items -取并集,最后利用 dict 函数,转成字典。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> full_profile = dict(profile.items() | ext_info.items()) - >>> full_profile - {'gender': 'male', 'age': 27, 'name': 'xiaoming'} - -当然了,你如果嫌这样太麻烦,也可以简单点,直接使用 list -函数再合并(示例为 Python 3.x ) - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> dict(list(profile.items()) + list(ext_info.items())) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -若你在 Python 2.x 下,可以直接省去 list 函数。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> dict(profile.items() + ext_info.items()) - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -**6. 最酷炫的字典解析式** - -Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 - -那就是列表解析式,集合解析式和字典解析式,通常是 Python -发烧友的最爱,那么今天的主题:字典合并,字典解析式还能否胜任呢? - -当然可以,具体示例代码如下: - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> {k:v for d in [profile, ext_info] for k,v in d.items()} - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -**7. Python 3.9 新特性** - -在 2 月份发布的 Python 3.9.04a -版本中,新增了一个抓眼球的新操作符操作符: ``|``\ , PEP584 -将它称之为合并操作符(Union Operator),用它可以很直观地合并多个字典。 - -.. code:: python - - >>> profile = {"name": "xiaoming", "age": 27} - >>> ext_info = {"gender": "male"} - >>> - >>> profile | ext_info - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - >>> - >>> ext_info | profile - {'gender': 'male', 'name': 'xiaoming', 'age': 27} - >>> - >>> - -除了 ``|`` 操作符之外,还有另外一个操作符 ``|=``\ ,类似于原地更新。 - -.. code:: python - - >>> ext_info |= profile - >>> ext_info - {'gender': 'male', 'name': 'xiaoming', 'age': 27} - >>> - >>> - >>> profile |= ext_info - >>> profile - {'name': 'xiaoming', 'age': 27, 'gender': 'male'} - -看到这里,有没有涨姿势了,学了这么久的 Python -,没想到合并字典还有这么多的方法。本篇文章的主旨,并不在于让你全部掌握这 -7 -种合并字典的方法,实际在工作中,你只要选用一种最顺手的方式即可,但是在协同工作中,或者在阅读他人代码时,你不可避免地会碰到各式各样的写法,这时候你能下意识的知道这是在做合并字典的操作,那这篇文章就是有意义的。 - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -45. 条件语句的七种写法 ----------------------- - -**第一种:原代码** - -这是一段非常简单的通过年龄判断一个人是否成年的代码,由于代码行数过多,有些人就不太愿意这样写,因为这体现不出自己多年的 -Python 功力。 - -.. code:: python - - if age > 18: - return "已成年" - else: - return "未成年" - -下面我列举了六种这段代码的变异写法,一个比一个还 6 -,单独拿出来比较好理解,放在工程代码里,没用过这些学法的人,一定会看得一脸懵逼,理解了之后,又不经意大呼:\ **卧槽,还可以这样写?**\ ,而后就要开始骂街了:\ **这是给人看的代码?** -(除了第一种之外) - -**第二种** - -语法: - -.. code:: python - - if else - -例子 - -.. code:: python - - >>> age1 = 20 - >>> age2 = 17 - >>> - >>> - >>> msg1 = "已成年" if age1 > 18 else "未成年" - >>> print msg1 - 已成年 - >>> - >>> msg2 = "已成年" if age2 > 18 else "未成年" - >>> print msg2 - 未成年 - >>> - -**第三种** - -语法 - -.. code:: python - - and or - -例子 - -.. code:: python - - >>> msg1 = age1 > 18 and "已成年" or "未成年" - >>> msg2 = "已成年" if age2 > 18 else "未成年" - >>> - >>> print(msg1) - 已成年 - >>> - >>> print(msg2) - 未成年 - -**第四种** - -语法 - -.. code:: python - - (, )[condition] - -例子 - -.. code:: python - - >>> msg1 = ("未成年", "已成年")[age1 > 18] - >>> print(msg1) - 已成年 - >>> - >>> - >>> msg2 = ("未成年", "已成年")[age2 > 18] - >>> print(msg2) - 未成年 - -**第五种** - -语法 - -.. code:: python - - (lambda: , lambda:)[]() - -例子 - -.. code:: python - - >>> msg1 = (lambda:"未成年", lambda:"已成年")[age1 > 18]() - >>> print(msg1) - 已成年 - >>> - >>> msg2 = (lambda:"未成年", lambda:"已成年")[age2 > 18]() - >>> print(msg2) - 未成年 - -**第六种** - -语法: - -.. code:: python - - {True: , False: }[] - -例子: - -.. code:: python - - >>> msg1 = {True: "已成年", False: "未成年"}[age1 > 18] - >>> print(msg1) - 已成年 - >>> - >>> msg2 = {True: "已成年", False: "未成年"}[age2 > 18] - >>> print(msg2) - 未成年 - -**第七种** - -语法 - -.. code:: python - - (() and (,) or (,))[0] - -例子 - -.. code:: python - - >>> msg1 = ((age1 > 18) and ("已成年",) or ("未成年",))[0] - >>> print(msg1) - 已成年 - >>> - >>> msg2 = ((age2 > 18) and ("已成年",) or ("未成年",))[0] - >>> print(msg2) - 未成年 - -以上代码,都比较简单,仔细看都能看懂,我就不做解释了。 - -看到这里,有没有涨姿势了,学了这么久的 Python -,这么多骚操作,还真是活久见。。这六种写法里,我最推荐使用的是第一种,自己也经常在用,简洁直白,代码行还少。而其他的写法虽然能写,但是不会用,也不希望在我余生里碰到会在公共代码里用这些写法的同事。 - - 作者:王炳明,微信公众号《Python编程时光》。版权归个人所有,欢迎分享,仅用于学习交流,但勿用作商业用途,违者必究。 - -46. /usr/bin/env python 有什么用? ----------------------------------- - -我们经常会在别人的脚本或者项目的入口文件里看到第一行是下面这样 - -.. code:: shell - - #!/usr/bin/python - -或者这样 - -``sh e llsh el #!/usr/bin/env python`` - -这两者有什么区别呢? - -稍微接触过 linux 的人都知道 ``/usr/bin/python`` 就是我们执行 ``python`` -进入console 模式里的 ``python`` - -|image8| - -而当你在可执行文件头里使用 ``#!`` + ``/usr/bin/python`` -,意思就是说你得用哪个软件 (python)来执行这个文件。 - -那么加和不加有什么区别呢? - -不加的话,你每次执行这个脚本时,都得这样: ``python xx.py`` , - -|image9| - -有没有一种方式?可以省去每次都加 ``python`` 呢? - -当然有,你可以文件头里加上\ ``#!/usr/bin/python`` -,那么当这个文件有可执行权限 时,只直接写这个脚本文件,就像下面这样。 - -|image10| - -明白了这个后,再来看看 ``!/usr/bin/env python`` 这个 又是什么意思 ? - -当我执行 ``env python`` 时,自动进入了 python console 的模式。 - -|image11| - -这是为什么?和 直接执行 python 好像没什么区别呀 - -当你执行 ``env python`` 时,它其实会去 ``env | grep PATH`` 里(也就是 -/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin -)这几个路径里去依次查找名为python的可执行文件。 - -找到一个就直接执行,上面我们的 python 路径是在 ``/usr/bin/python`` -里,在 ``PATH`` 列表里倒数第二个目录下,所以当我在 ``/usr/local/sbin`` -下创建一个名字也为 python 的可执行文件时,就会执行 ``/usr/bin/python`` -了。 - -具体演示过程,你可以看下面。 - -|image12| - -那么对于这两者,我们应该使用哪个呢? - -个人感觉应该优先使用 ``#!/usr/bin/env python``\ ,因为不是所有的机器的 -python 解释器都是 ``/usr/bin/python`` 。 - -47. 让我爱不释手的用户环境 --------------------------- - -当你在机器上并没有 root 权限时,如何安装 Python 的第三方包呢? - -可以使用 ``pip install --user pkg`` -将你的包安装在你的用户环境中,该用户环境与全局环境并不冲突,并且多用户之间相互隔离,互不影响。 - -.. code:: shell - - # 在全局环境中未安装 requests - [root@localhost ~]$ pip list | grep requests - [root@localhost ~]$ su - wangbm - - # 由于用户环境继承自全局环境,这里也未安装 - [wangbm@localhost ~]$ pip list | grep requests - [wangbm@localhost ~]$ pip install --user requests - [wangbm@localhost ~]$ pip list | grep requests - requests (2.22.0) - [wangbm@localhost ~]$ - - # 从 Location 属性可发现 requests 只安装在当前用户环境中 - [wangbm@localhost ~]$ pip show requests - --- - Metadata-Version: 2.1 - Name: requests - Version: 2.22.0 - Summary: Python HTTP for Humans. - Home-page: http://python-requests.org - Author: Kenneth Reitz - Author-email: me@kennethreitz.org - Installer: pip - License: Apache 2.0 - Location: /home/wangbm/.local/lib/python2.7/site-packages - [wangbm@localhost ~]$ exit - logout - - # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 - [root@localhost ~]$ pip list | grep requests - [root@localhost ~]$ - -48. 实现类似 defer 的延迟调用 ------------------------------ - -在 Golang 中有一种延迟调用的机制,关键字是 defer,例如下面的示例 - -.. code:: go - - import "fmt" - - func myfunc() { - fmt.Println("B") - } - - func main() { - defer myfunc() - fmt.Println("A") - } - -输出如下,myfunc 的调用会在函数返回前一步完成,即使你将 myfunc -的调用写在函数的第一行,这就是延迟调用。 - -:: - - A - B - -那么在 Python 中否有这种机制呢? - -当然也有,只不过并没有 Golang 这种简便。 - -在 Python 可以使用 **上下文管理器** 达到这种效果 - -.. code:: python - - import contextlib - - def callback(): - print('B') - - with contextlib.ExitStack() as stack: - stack.callback(callback) - print('A') - -输出如下 - -:: - - A - B - -49. 自带的缓存机制不用白不用 ----------------------------- - -缓存是一种将定量数据加以保存,以备迎合后续获取需求的处理方式,旨在加快数据获取的速度。 - -数据的生成过程可能需要经过计算,规整,远程获取等操作,如果是同一份数据需要多次使用,每次都重新生成会大大浪费时间。所以,如果将计算或者远程请求等操作获得的数据缓存下来,会加快后续的数据获取需求。 - -为了实现这个需求,Python 3.2 + -中给我们提供了一个机制,可以很方便的实现,而不需要你去写这样的逻辑代码。 - -这个机制实现于 functool 模块中的 lru_cache 装饰器。 - -.. code:: python - - @functools.lru_cache(maxsize=None, typed=False) - -参数解读: - -- maxsize:最多可以缓存多少个此函数的调用结果,如果为None,则无限制,设置为 - 2 的幂时,性能最佳 -- typed:若为 True,则不同参数类型的调用将分别缓存。 - -举个例子 - -.. code:: python - - from functools import lru_cache - - @lru_cache(None) - def add(x, y): - print("calculating: %s + %s" % (x, y)) - return x + y - - print(add(1, 2)) - print(add(1, 2)) - print(add(2, 3)) - -输出如下,可以看到第二次调用并没有真正的执行函数体,而是直接返回缓存里的结果 - -.. code:: shell - - calculating: 1 + 2 - 3 - 3 - calculating: 2 + 3 - 5 - -50. 重定向标准输出到日志 ------------------------- - -假设你有一个脚本,会执行一些任务,比如说集群健康情况的检查。 - -检查完成后,会把各服务的的健康状况以 JSON 字符串的形式打印到标准输出。 - -如果代码有问题,导致异常处理不足,最终检查失败,是很有可能将一些错误异常栈输出到标准错误或标准输出上。 - -由于最初约定的脚本返回方式是以 JSON -的格式输出,此时你的脚本却输出各种错误异常,异常调用方也无法解析。 - -如何避免这种情况的发生呢? - -我们可以这样做,把你的标准错误输出到日志文件中。 - -.. code:: python - - import contextlib - - log_file="/var/log/you.log" - - def you_task(): - pass - - @contextlib.contextmanager - def close_stdout(): - raw_stdout = sys.stdout - file = open(log_file, 'a+') - sys.stdout = file - - yield - - sys.stdout = raw_stdout - file.close() - - with close_stdout(): - you_task() - --------------- - -|image13| - .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20190511165650.png -.. |image2| image:: http://image.iswbm.com/20190511165716.png -.. |image3| image:: http://image.iswbm.com/20200509172331.png -.. |image4| image:: http://image.iswbm.com/20190511165735.png -.. |image5| image:: http://image.iswbm.com/20200509122954.png -.. |image6| image:: http://image.iswbm.com/20200509123107.png -.. |image7| image:: http://image.iswbm.com/20200510112133.png -.. |image8| image:: http://image.iswbm.com/20200331184021.png -.. |image9| image:: http://image.iswbm.com/20200331185034.png -.. |image10| image:: http://image.iswbm.com/20200331184755.png -.. |image11| image:: http://image.iswbm.com/20200331185741.png -.. |image12| image:: http://image.iswbm.com/20200331190224.png -.. |image13| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20190831160317.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_15.md b/source/c01/c01_15.md index 39a6bea..d6561cd 100644 --- a/source/c01/c01_15.md +++ b/source/c01/c01_15.md @@ -1,73 +1,49 @@ -# 1.15 提升Python性能的7个习惯 +# 1.21 Python 开发的几个小 Tips -![](http://image.iswbm.com/20200602135014.png) +## 1. 重定向标准输出到文件 ---- - -> 转载自:https://zhuanlan.zhihu.com/p/38160586 - -## 1. 使用局部变量 - -尽量使用局部变量代替全局变量:便于维护,提高性能并节省内存。 - -使用局部变量替换模块名字空间中的变量,例如 ls = os.linesep。一方面可以提高程序性能,局部变量查找速度更快;另一方面可用简短标识符替代冗长的模块变量,提高可读性。 - -## 2. 减少函数调用次数 - -对象类型判断时,采用isinstance()最优,采用对象类型身份(id())次之,采用对象值(type())比较最次。 - -``` -#判断变量num是否为整数类型type(num) == type(0) #调用三次函数type(num) is type(0) #身份比较isinstance(num,(int)) #调用一次函数 -``` - -不要在重复操作的内容作为参数放到循环条件中,避免重复运算。 - -``` -#每次循环都需要重新执行len(a)while i < len(a): statement#len(a)仅执行一次m = len(a)while i < m: statement ``` +import contextlib -如需使用模块X中的某个函数或对象Y,应直接使用from X import Y,而不是import X; X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查找到X模块,然后在X模块的字典中查找Y)。 +def unshelve_task(): + pass -## 3. 采用映射替代条件查找 +@contextlib.contextmanager +def close_stdout(): + raw_stdout = sys.stdout + file = open(log_file, 'a+') + sys.stdout = file -映射(比如dict等)的搜索速度远快于条件语句(如if等)。Python中也没有select-case语句。 + yield + sys.stdout = raw_stdout + file.close() + +with close_stdout(): + unshelve_task() ``` -#if查找if a == 1: b = 10elif a == 2: b = 20...#dict查找,性能更优d = {1:10,2:20,...}b = d[a] -``` -## 4. 直接迭代序列元素 +## 2. 将子网掩码转换为cidr + +如何使用netaddr库将ipv4子网掩码转换为cidr表示法? +示例:255.255.255.0到/ 24 -对序列(str、list、tuple等),直接迭代序列元素,比迭代元素的索引速度要更快。 +使用netaddr: ``` -a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range(len(a)): print(a[i]) +>>> from netaddr import IPAddress +>>> IPAddress("255.255.255.0").netmask_bits() +24 ``` -## 5. 采用生成器表达式替代列表解析 - -列表解析(list comprehension),会产生整个列表,对大量数据的迭代会产生负面效应。 - -而生成器表达式则不会,其不会真正创建列表,而是返回一个生成器,在需要时产生一个值(延迟计算),对内存更加友好。 +您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位: ``` -#计算文件f的非空字符个数#生成器表达式l = sum([len(word) for line in f for word in line.split()])#列表解析l = sum(len(word) for line in f for word in line.split()) +>>> netmask = "255.255.255.0" +>>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) +24 ``` -## 6. 先编译后调用 - -使用eval()、exec()函数执行代码时,最好调用代码对象(提前通过compile()函数编译成字节码),而不是直接调用str,可以避免多次执行重复编译过程,提高程序性能。 - -正则表达式模式匹配也类似,也最好先将正则表达式模式编译成regex对象(通过re.complie()函数),然后再执行比较和匹配。 - -## 7. 模块编程习惯 - -模块中的最高级别Python语句(没有缩进的代码)会在模块导入(import)时执行(不论其是否真的必要执行)。因此,应尽量将模块所有的功能代码放到函数中,包括主程序相关的功能代码也可放到main()函数中,主程序本身调用main()函数。 - -可以在模块的main()函数中书写测试代码。在主程序中,检测name的值,如果为'main'(表示模块是被直接执行),则调用main()函数,进行测试;如果为模块名字(表示模块是被调用),则不进行测试。 - - ------- -![](http://image.iswbm.com/20200607174235.png) +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_15.rst b/source/c01/c01_15.rst index 4cf7517..97bab2d 100644 --- a/source/c01/c01_15.rst +++ b/source/c01/c01_15.rst @@ -1,87 +1,53 @@ -1.15 提升Python性能的7个习惯 -============================ +1.21 Python 开发的几个小 Tips +============================= -|image0| - --------------- - - 转载自:https://zhuanlan.zhihu.com/p/38160586 - -1. 使用局部变量 ---------------- - -尽量使用局部变量代替全局变量:便于维护,提高性能并节省内存。 - -使用局部变量替换模块名字空间中的变量,例如 ls = -os.linesep。一方面可以提高程序性能,局部变量查找速度更快;另一方面可用简短标识符替代冗长的模块变量,提高可读性。 - -2. 减少函数调用次数 -------------------- - -对象类型判断时,采用isinstance()最优,采用对象类型身份(id())次之,采用对象值(type())比较最次。 +1. 重定向标准输出到文件 +----------------------- :: - #判断变量num是否为整数类型type(num) == type(0) #调用三次函数type(num) is type(0) #身份比较isinstance(num,(int)) #调用一次函数 + import contextlib -不要在重复操作的内容作为参数放到循环条件中,避免重复运算。 + def unshelve_task(): + pass -:: + @contextlib.contextmanager + def close_stdout(): + raw_stdout = sys.stdout + file = open(log_file, 'a+') + sys.stdout = file - #每次循环都需要重新执行len(a)while i < len(a): statement#len(a)仅执行一次m = len(a)while i < m: statement + yield -如需使用模块X中的某个函数或对象Y,应直接使用from X import -Y,而不是import X; -X.Y。这样在使用Y时,可以减少一次查询(解释器不必首先查找到X模块,然后在X模块的字典中查找Y)。 + sys.stdout = raw_stdout + file.close() + + with close_stdout(): + unshelve_task() -3. 采用映射替代条件查找 +2. 将子网掩码转换为cidr ----------------------- -映射(比如dict等)的搜索速度远快于条件语句(如if等)。Python中也没有select-case语句。 - -:: - - #if查找if a == 1: b = 10elif a == 2: b = 20...#dict查找,性能更优d = {1:10,2:20,...}b = d[a] - -4. 直接迭代序列元素 -------------------- +如何使用netaddr库将ipv4子网掩码转换为cidr表示法? 示例:255.255.255.0到/ +24 -对序列(str、list、tuple等),直接迭代序列元素,比迭代元素的索引速度要更快。 +使用netaddr: :: - a = [1,2,3]#迭代元素for item in a: print(item)#迭代索引for i in range(len(a)): print(a[i]) - -5. 采用生成器表达式替代列表解析 -------------------------------- + >>> from netaddr import IPAddress + >>> IPAddress("255.255.255.0").netmask_bits() + 24 -列表解析(list -comprehension),会产生整个列表,对大量数据的迭代会产生负面效应。 - -而生成器表达式则不会,其不会真正创建列表,而是返回一个生成器,在需要时产生一个值(延迟计算),对内存更加友好。 +您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位: :: - #计算文件f的非空字符个数#生成器表达式l = sum([len(word) for line in f for word in line.split()])#列表解析l = sum(len(word) for line in f for word in line.split()) - -6. 先编译后调用 ---------------- - -使用eval()、exec()函数执行代码时,最好调用代码对象(提前通过compile()函数编译成字节码),而不是直接调用str,可以避免多次执行重复编译过程,提高程序性能。 + >>> netmask = "255.255.255.0" + >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) + 24 -正则表达式模式匹配也类似,也最好先将正则表达式模式编译成regex对象(通过re.complie()函数),然后再执行比较和匹配。 - -7. 模块编程习惯 ---------------- - -模块中的最高级别Python语句(没有缩进的代码)会在模块导入(import)时执行(不论其是否真的必要执行)。因此,应尽量将模块所有的功能代码放到函数中,包括主程序相关的功能代码也可放到main()函数中,主程序本身调用main()函数。 - -可以在模块的main()函数中书写测试代码。在主程序中,检测name的值,如果为’main’(表示模块是被直接执行),则调用main()函数,进行测试;如果为模块名字(表示模块是被调用),则不进行测试。 - --------------- - -|image1| +|image0| -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png +.. |image0| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_21.md b/source/c01/c01_21.md index d6561cd..cf1190c 100644 --- a/source/c01/c01_21.md +++ b/source/c01/c01_21.md @@ -1,49 +1,216 @@ -# 1.21 Python 开发的几个小 Tips +# 1.21 Python 炫技操作:连接列表的八种方法 -## 1. 重定向标准输出到文件 +![](http://image.iswbm.com/20200602135014.png) +Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 + +但你要知道,在团队合作里,炫技是大忌。 + +为什么这么说呢?我说下自己的看法: + +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) + +该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 + +## 1. 最直观的相加 + +使用 `+` 对多个列表进行相加,你应该懂,不多说了。 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> list01 + list02 + list03 +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + + + +## 2. 借助 itertools + +itertools 在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 + +在前面的文章中也介绍过,使用 `itertools.chain()` 函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 + +最后你再利用 list 将其转化为 列表。 + +```python +>>> from itertools import chain +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> list(chain(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> +``` + +## 3. 使用 * 解包 + +在 [Python 炫技操作(02):合并字典的七种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&token=688334198&lang=zh_CN#rd) 提到了使用 `**` 可解包字典。 + +与它相似的,使用 `*` 可以解包列表。 `*` 和 `**` 常用在函数定义时,设置可变参数。 + +现在我将它单独拿出来用在多个列表的合并。 + +示例如下: + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> [*list01, *list02] +[1, 2, 3, 4, 5, 6] +>>> +``` + + + +## 4. 使用 extend + +在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> list01.extend(list02) +>>> list01 +[1, 2, 3, 4, 5, 6] +``` + +## 5. 使用列表推导式 + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? + +当然可以,具体示例代码如下: + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> [x for l in (list01, list02, list03) for x in l] +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> ``` -import contextlib -def unshelve_task(): - pass -@contextlib.contextmanager -def close_stdout(): - raw_stdout = sys.stdout - file = open(log_file, 'a+') - sys.stdout = file - yield +## 6. 使用 heapq + +heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 - sys.stdout = raw_stdout - file.close() - -with close_stdout(): - unshelve_task() +该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> from heapq import merge +>>> +>>> list(merge(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> ``` -## 2. 将子网掩码转换为cidr +要注意的是,heapq.merge 除了合并多个列表外,它还会将合并后的最终的列表进行排序。 -如何使用netaddr库将ipv4子网掩码转换为cidr表示法? -示例:255.255.255.0到/ 24 +```python +>>> list01 = [2,5,3] +>>> list02 = [1,4,6] +>>> list03 = [7,9,8] +>>> +>>> from heapq import merge +>>> +>>> list(merge(list01, list02, list03)) +[1, 2, 4, 5, 3, 6, 7, 9, 8] +>>> +``` -使用netaddr: +它的效果等价于下面这行代码: +```python +sorted(itertools.chain(*iterables)) ``` ->>> from netaddr import IPAddress ->>> IPAddress("255.255.255.0").netmask_bits() -24 + +如果你希望得到一个始终有序的列表,那请第一时间想到 heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 + +## 7. 借助魔法方法 + +在之前的文章里,把魔法方法介绍得很全。 + +[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd) + +[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd) + +其中有一个魔法方法是 `__add__`,实际 上当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的. + +所以以下两种方法其实是等价的 + +``` +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> +>>> list01 + list02 +[1, 2, 3, 4, 5, 6] +>>> +>>> +>>> list01.__add__(list02) +[1, 2, 3, 4, 5, 6] +>>> ``` -您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位: +借用这个魔法特性,我们可以 reduce 这个方法来对多个列表进行合并,示例代码如下 +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> from functools import reduce +>>> reduce(list.__add__, (list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> ``` ->>> netmask = "255.255.255.0" ->>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) -24 + + + +## 8. 使用 yield from + +在很早的一篇文章里([并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect)),我很详细的介绍了 yield from 意义及使用方法。 + +在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 + +因此,我们可以像下面这样自定义一个合并列表的工具函数。 + +```python +>>> list01 = [1,2,3] +>>> list02 = [4,5,6] +>>> list03 = [7,8,9] +>>> +>>> def merge(*lists): +... for l in lists: +... yield from l +... +>>> list(merge(list01, list02, list03)) +[1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> ``` -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file +--- + + + +![](http://image.iswbm.com/20200607174235.png) + + diff --git a/source/c01/c01_21.rst b/source/c01/c01_21.rst index 97bab2d..8302f52 100644 --- a/source/c01/c01_21.rst +++ b/source/c01/c01_21.rst @@ -1,53 +1,233 @@ -1.21 Python 开发的几个小 Tips -============================= +1.21 Python 炫技操作:连接列表的八种方法 +======================================== -1. 重定向标准输出到文件 ------------------------ +|image0| -:: +Python 语言里有许多(而且是越来越多)的高级特性,是 Python +发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - import contextlib +但你要知道,在团队合作里,炫技是大忌。 - def unshelve_task(): - pass +为什么这么说呢?我说下自己的看法: - @contextlib.contextmanager - def close_stdout(): - raw_stdout = sys.stdout - file = open(log_file, 'a+') - sys.stdout = file +1. 越简洁的代码,越清晰的逻辑,就越不容易出错; +2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 +3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - yield +该篇是「\ **炫技系列**\ 」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 +Python +发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - sys.stdout = raw_stdout - file.close() - - with close_stdout(): - unshelve_task() +1. 最直观的相加 +--------------- -2. 将子网掩码转换为cidr ------------------------ +使用 ``+`` 对多个列表进行相加,你应该懂,不多说了。 -如何使用netaddr库将ipv4子网掩码转换为cidr表示法? 示例:255.255.255.0到/ -24 +.. code:: python -使用netaddr: + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> list01 + list02 + list03 + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> -:: +2. 借助 itertools +----------------- - >>> from netaddr import IPAddress - >>> IPAddress("255.255.255.0").netmask_bits() - 24 +itertools 在 Python +里有一个非常强大的内置模块,它专门用于操作可迭代对象。 -您也可以在不使用任何库的情况下执行此操作,只需在网络掩码的二进制表示中计算1位: +在前面的文章中也介绍过,使用 ``itertools.chain()`` +函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 -:: +最后你再利用 list 将其转化为 列表。 - >>> netmask = "255.255.255.0" - >>> sum([bin(int(x)).count("1") for x in netmask.split(".")]) - 24 +.. code:: python -|image0| + >>> from itertools import chain + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> list(chain(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +3. 使用 \* 解包 +--------------- + +在 `Python +炫技操作(02):合并字典的七种方法 `__ +提到了使用 ``**`` 可解包字典。 + +与它相似的,使用 ``*`` 可以解包列表。 ``*`` 和 ``**`` +常用在函数定义时,设置可变参数。 + +现在我将它单独拿出来用在多个列表的合并。 + +示例如下: + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> [*list01, *list02] + [1, 2, 3, 4, 5, 6] + >>> + +4. 使用 extend +-------------- + +在字典中,使用 update 可实现原地更新,而在列表中,使用 extend +可实现列表的自我扩展。 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> list01.extend(list02) + >>> list01 + [1, 2, 3, 4, 5, 6] + +5. 使用列表推导式 +----------------- + +Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 + +那就是列表解析式,集合解析式和字典解析式,通常是 Python +发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? + +当然可以,具体示例代码如下: + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> [x for l in (list01, list02, list03) for x in l] + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +6. 使用 heapq +------------- + +heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 + +该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> from heapq import merge + >>> + >>> list(merge(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +要注意的是,heapq.merge +除了合并多个列表外,它还会将合并后的最终的列表进行排序。 + +.. code:: python + + >>> list01 = [2,5,3] + >>> list02 = [1,4,6] + >>> list03 = [7,9,8] + >>> + >>> from heapq import merge + >>> + >>> list(merge(list01, list02, list03)) + [1, 2, 4, 5, 3, 6, 7, 9, 8] + >>> + +它的效果等价于下面这行代码: + +.. code:: python + + sorted(itertools.chain(*iterables)) + +如果你希望得到一个始终有序的列表,那请第一时间想到 +heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 + +7. 借助魔法方法 +--------------- + +在之前的文章里,把魔法方法介绍得很全。 + +`非常全的通俗易懂 Python +魔法方法指南(上) `__ + +`非常全的通俗易懂 Python +魔法方法指南(下) `__ + +其中有一个魔法方法是 ``__add__``\ ,实际 上当我们使用第一种方法 list01 + +list02 的时候,内部实际上是作用在 ``__add__`` 这个魔法方法上的. + +所以以下两种方法其实是等价的 + +:: -.. |image0| image:: http://image.iswbm.com/20200607174235.png + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> + >>> list01 + list02 + [1, 2, 3, 4, 5, 6] + >>> + >>> + >>> list01.__add__(list02) + [1, 2, 3, 4, 5, 6] + >>> + +借用这个魔法特性,我们可以 reduce +这个方法来对多个列表进行合并,示例代码如下 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> from functools import reduce + >>> reduce(list.__add__, (list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +8. 使用 yield from +------------------ + +在很早的一篇文章里(\ `并发编程08|深入理解yield +from语法 `__\ ),我很详细的介绍了 +yield from 意义及使用方法。 + +在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 + +因此,我们可以像下面这样自定义一个合并列表的工具函数。 + +.. code:: python + + >>> list01 = [1,2,3] + >>> list02 = [4,5,6] + >>> list03 = [7,8,9] + >>> + >>> def merge(*lists): + ... for l in lists: + ... yield from l + ... + >>> list(merge(list01, list02, list03)) + [1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> + +-------------- + +|image1| + +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_22.md b/source/c01/c01_22.md index 89daee8..39cd84d 100644 --- a/source/c01/c01_22.md +++ b/source/c01/c01_22.md @@ -1,193 +1,175 @@ -# 1.22 如何修改 CentOS 6.x 上默认Python +# 1.22 Python 炫技操作:海象运算符的三种用法 ![](http://image.iswbm.com/20200602135014.png) -最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 CentOS 6.x,有的是 CentOS 7.x 。 +Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 -需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x 上的 Python 版本是 2.7.x 的,这意味着我要实现的功能要适配这两种版本的系统。 +很多 Python 3.8 的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 -你可能会说,这有什么的,自己写的时候,注意一下就好了。 +海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 -事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的 Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和 CentOS 7.x 将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要 Python2.7,而 CentOS 6.x 上只有 Python2.6。 +它的英文原名叫 `Assignment Expressions`,翻译过来也就是 `赋值表达式`,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 -这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将 CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。 +![](http://image.iswbm.com/image-20200418122739417.png) -下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。 +## 1. 第一个用法:if/else +可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? +在 Golang 中的条件语句可以直接在 if 中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 +```go +import "fmt" -1. 首先确认下你机器上的默认的 Python 版本 - -```shell -$ python -V -Python 2.6.6 - -$ whereis python -python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz +func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } +} ``` - - -2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。 +若在 Python 3.8 之前,Python 必须得这样子写 -注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。 +```python +age = 20 +if age > 18: + print("已经成年了") +``` -比如不安装 zlib 会无法安装 setuptools,不装 openssl 和 openssl-devel,会无法使用 pip 工具等 +但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 Golang,那这里要注意,Golang 中的 `:=` 叫短变量声明,意思是声明并初始化,它和 Python 中的 `:=` 不是一个概念) -``` -$ yum install gcc -y -$ yum groupinstall "Development tools" -$ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y +```python +if (age:= 20) > 18: + print("已经成年了") ``` - 如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。 +## 2. 第二个用法:while -3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录 +在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 -``` -$ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz -$ tar zxvf Python-2.7.14.tgz -$ cd Python-2.7.14 +```python +file = open("demo.txt", "r") +while True: + line = file.readline() + if not line: + break + print(line.strip()) ``` +但有了海象运算符之后,你可以这样 - -4. 配置,编译,安装 - -```shell -# --prefix 指定 python 安装的路径 -$ ./configure --prefix=/usr/local/python/python2.7 -$ make -$ make install +```python +file = open("demo.txt", "r") +while (line := file.readline()): + print(line.strip()) ``` -`./configure` 命令执行完毕之后创建一个文件creating Makefile,供下面的make命令使用 执行 `make install` 之后就会把程序安装到我们指定的目录中去。 +使用它替换以往的无限 while 循环写法更为惊艳 -Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure –help输出详细的选项列表。其中 `--prefix` 选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr /local/bin,库文件默认放在 `/usr/local/lib` ,配置文件默认放在 `/usr/local/etc` ,其它的资源文件放在 `/usr /local/share`。如果配置 `--prefix`,如:`./configure --prefix=/usr/local/test` 可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。 +比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 -用了 `--prefix` 选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次 `make uninstall`,但前提是make文件指定过uninstall。 - - +```python +while True: + p = input("Enter the password: ") + if p == "youpassword": + break +``` -5. 查看系统的 Python 版本 +有了海象运算符之后,这样子写更为舒服 -```shell -$ python -V -Python 2.6.6 +```python +while (p := input("Enter the password: ")) != "youpassword": + continue ``` - 如果你查看还是 Python 2.6.6 版本,请继续看第六步。 +## 3. 第三个用法:推导式 -6. 修改系统默认的 Python 版本 +这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 -查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x +在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 -```shell -# 这是我们刚安装的 Python -$/usr/local/bin/python2.7 -V -Python 2.7.14 +如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 -# 这是系统默认 Python -$ /usr/bin/python -V -Python 2.6.6 +```python +members = [ + {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, + {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, + {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, +] -# 备份原来的 Python 文件 -$ mv /usr/bin/python /usr/bin/python.bak +count = 0 -# 建立软链接,将我们刚安装的 python2.7 做为系统默认版本 -ln -s /usr/local/bin/python2.7 /usr/bin/python +def get_bmi(info): + global count + count += 1 -# 再次查看 Python 版本,已经成功切换过来 -$ python -V -Python 2.7.14 -``` + print(f"执行了 {count} 次") -7. 重新指定 yum 的Python版本 + height = info["height"] + weight = info["weight"] -上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum 是基于Python2.6 的,为了不影响 yum 的使用,需单独将yum指向python2.6版本。 + return weight / (height**2) -编辑: vim /usr/bin/yum ,将` /usr/bin/python` 改成 `/usr/bin/python2.6` +# 查出所有会员中过于肥胖的人的 bmi 指数 +fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] -```python -#!/usr/bin/python2.6 +print(fat_bmis) ``` +输出如下 - -8. 安装 setuptools 及 pip - -pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools - -```shell -# 下载 setuptools -$ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4 ``` - -同样的,进行安装: - -```shell -$ tar vxf setuptools-21.0.0.tar.gz -$ cd setuptools-21.0.0 -$ python setup.py install +执行了 1 次 +执行了 2 次 +执行了 3 次 +执行了 4 次 +[25.88057063502083] ``` -安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip +可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 -```shell -# 下载 pip -wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7 -``` +如果所有会员都是过于肥胖的,那最终将执行 6 次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for 循环 + if 判断。 -同样的,进行安装 +```python +fat_bmis = [] -```shell -$ tar vxf pip-8.1.1.tar.gz -$ cd pip-8.1.1 -$ python setup.py install +# 查出所有会员中过于肥胖的人的 bmi 指数 +for m in members: + bmi = get_bmi(m) + if bmi > 24: + fat_bmis.append(bmi) ``` -安装完成后,执行 `pip list` 查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包 `pip install requests` 。 - +在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 +```python +# 查出所有会员中过于肥胖的人的 bmi 指数 +fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] +``` -8. 转移cloudinit - -上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit 的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ 目录下 - -![](http://image.iswbm.com/20190831160317.png) +最终从输出结果可以看出,只执行了 3 次 -然后安装一些 cloudinit 的依赖包。 +``` +执行了 1 次 +执行了 2 次 +执行了 3 次 +[25.88057063502083] +``` -```shell -$ pip install six requests prettytable jsonpatch configobj +这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 -# 默认还是安装在 python2.6 下 -$ yum install PyYAML -y -# 将这些文件拷贝到 python2.7 目录下 -# 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印 -$ cd /usr/lib64/python2.6/site-packages -$ cp -r yaml/ /usr/local/lib/python2.7/site-packages/ -$ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/ -$ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/ -``` -执行一下 cloudinit 的几个命令,没有问题,任务就完成了。 +海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 -```shell -$ cloud-init init -l -$ cloud-init init -``` +--- -**参考文章** -- https://www.cnblogs.com/stonehe/p/7944366.html -![](http://image.iswbm.com/20200607174235.png) +![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c01/c01_22.rst b/source/c01/c01_22.rst index 793d914..177b431 100644 --- a/source/c01/c01_22.rst +++ b/source/c01/c01_22.rst @@ -1,207 +1,183 @@ -1.22 如何修改 CentOS 6.x 上默认Python -===================================== +1.22 Python 炫技操作:海象运算符的三种用法 +========================================== |image0| -最近在工作中遇到一个问题,就是有一个功能希望在各种服务器上实现,而服务器上的系统版本可能都不一样,有的是 -CentOS 6.x,有的是 CentOS 7.x 。 +Python 版本发展非常快,如今最新的版本已经是 Pyhton +3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 -需要说明的一点是,CentOS 6.x 上的 Python 版本是 2.6.x 的,而 CentOS 7.x -上的 Python 版本是 2.7.x -的,这意味着我要实现的功能要适配这两种版本的系统。 +很多 Python 3.8 +的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 -你可能会说,这有什么的,自己写的时候,注意一下就好了。 +海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 -事情其实没有那么容易,我要实现的功能是基于一个框架进行定制,需要修改不少的框架代码。这个框架在不同的 -Linux 版本上,是有不同的版本的,而且差异巨大,曾经想过在 CentOS 6.x 和 -CentOS 7.x -将这个框架安装成同一个版本,最后还是失败了,无法安装,原因就是高版本需要 -Python2.7,而 CentOS 6.x 上只有 Python2.6。 +它的英文原名叫 ``Assignment Expressions``\ ,翻译过来也就是 +``赋值表达式``\ ,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 -这个历史问题一直遗留到现在,由于这次的功能影响到的代码较多,如果要对两个版本的框架分别进行定制的话,需要花不少的时间,为了不维护两套版本,避免浪费多余的精力去做适配,我决定将 -CentOS 6.x 上默认的 Python2.6 升级成 Python2.7。 - -下面是整个升级过程,别看步骤简单,这些精简步骤的背后可是有不少的坑,被我踩过后,你可以直接使用了。 - -1. 首先确认下你机器上的默认的 Python 版本 - -.. code:: shell - - $ python -V - Python 2.6.6 +|image1| - $ whereis python - python: /usr/bin/python /usr/bin/python2.6 /usr/lib/python2.6 /usr/lib64/python2.6 /usr/local/bin/python /usr/include/python2.6 /usr/share/man/man1/python.1.gz +1. 第一个用法:if/else +---------------------- -2. 由于我们将使用编译安装的方式,所以要安装下 gcc,及一些工具包。 +可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? -注意一定要全部安装,不然后面会发现有不少 python 的工具用不了。 +在 Golang 中的条件语句可以直接在 if +中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 -比如不安装 zlib 会无法安装 setuptools,不装 openssl 和 -openssl-devel,会无法使用 pip 工具等 +.. code:: go -:: + import "fmt" - $ yum install gcc -y - $ yum groupinstall "Development tools" - $ yum install zlib-devel bzip2-devel openssl openssl-devel ncurses-devel sqlite-devel -y + func main() { + if age := 20;age > 18 { + fmt.Println("已经成年了") + } + } -如果你这里未按照我的步骤来安装,你后面使用的时候出现了各种各样的问题,不要慌,只要再回来这里,把没安装的包装上,安装完成后,你需要进入第四步重新编译安装Python。 +若在 Python 3.8 之前,Python 必须得这样子写 -3. 下载最新的 Python2.7.x 安装包,解压并进入指定目录 - -:: +.. code:: python - $ wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz - $ tar zxvf Python-2.7.14.tgz - $ cd Python-2.7.14 + age = 20 + if age > 18: + print("已经成年了") -4. 配置,编译,安装 +但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 +Golang,那这里要注意,Golang 中的 ``:=`` +叫短变量声明,意思是声明并初始化,它和 Python 中的 ``:=`` 不是一个概念) -.. code:: shell +.. code:: python - # --prefix 指定 python 安装的路径 - $ ./configure --prefix=/usr/local/python/python2.7 - $ make - $ make install + if (age:= 20) > 18: + print("已经成年了") -``./configure`` 命令执行完毕之后创建一个文件creating -Makefile,供下面的make命令使用 执行 ``make install`` -之后就会把程序安装到我们指定的目录中去。 +2. 第二个用法:while +-------------------- -Configure是一个可执行脚本,它有很多选项,在待安装的源码路径下使用命令./configure -–help输出详细的选项列表。其中 ``--prefix`` -选项是配置安装的路径,如果不配置该选项,安装后可执行文件默认放在/usr -/local/bin,库文件默认放在 ``/usr/local/lib`` ,配置文件默认放在 -``/usr/local/etc`` ,其它的资源文件放在 -``/usr /local/share``\ 。如果配置 -``--prefix``\ ,如:\ ``./configure --prefix=/usr/local/test`` -可以把所有资源文件放在/usr/local/test的路径中,不会杂乱。 +在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 -用了 ``--prefix`` -选项的另一个好处是卸载软件或移植软件。当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统)。当然要卸载程序,也可以在原来的make目录下用一次 -``make uninstall``\ ,但前提是make文件指定过uninstall。 +.. code:: python -5. 查看系统的 Python 版本 + file = open("demo.txt", "r") + while True: + line = file.readline() + if not line: + break + print(line.strip()) -.. code:: shell +但有了海象运算符之后,你可以这样 - $ python -V - Python 2.6.6 +.. code:: python -如果你查看还是 Python 2.6.6 版本,请继续看第六步。 + file = open("demo.txt", "r") + while (line := file.readline()): + print(line.strip()) -6. 修改系统默认的 Python 版本 +使用它替换以往的无限 while 循环写法更为惊艳 -查看新安装的Python版本,当前系统的Python版本,并将系统指向的Python从2.6.x修改为2.7.x,再次查看当前系统的Python版本,已经变更为2.7.x +比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 -.. code:: shell +.. code:: python - # 这是我们刚安装的 Python - $/usr/local/bin/python2.7 -V - Python 2.7.14 + while True: + p = input("Enter the password: ") + if p == "youpassword": + break - # 这是系统默认 Python - $ /usr/bin/python -V - Python 2.6.6 +有了海象运算符之后,这样子写更为舒服 - # 备份原来的 Python 文件 - $ mv /usr/bin/python /usr/bin/python.bak +.. code:: python - # 建立软链接,将我们刚安装的 python2.7 做为系统默认版本 - ln -s /usr/local/bin/python2.7 /usr/bin/python + while (p := input("Enter the password: ")) != "youpassword": + continue - # 再次查看 Python 版本,已经成功切换过来 - $ python -V - Python 2.7.14 +3. 第三个用法:推导式 +--------------------- -7. 重新指定 yum 的Python版本 +这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 -上面我们改了系统的默认 Python 版本,由于CentOS 6.x 的 yum -是基于Python2.6 的,为了不影响 yum -的使用,需单独将yum指向python2.6版本。 +在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 -编辑: vim /usr/bin/yum ,将\ ``/usr/bin/python`` 改成 -``/usr/bin/python2.6`` +如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 .. code:: python - #!/usr/bin/python2.6 - -8. 安装 setuptools 及 pip - -pip是python的安装工具,很多python的常用工具,都可以通过pip进行安装。要安装pip,首先要安装setuptools。从这个链接,你可以得到相关信息:https://pypi.python.org/pypi/setuptools - -.. code:: shell + members = [ + {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, + {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, + {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, + ] - # 下载 setuptools - $ wget https://pypi.python.org/packages/ff/d4/209f4939c49e31f5524fa0027bf1c8ec3107abaf7c61fdaad704a648c281/setuptools-21.0.0.tar.gz#md5=81964fdb89534118707742e6d1a1ddb4 + count = 0 -同样的,进行安装: + def get_bmi(info): + global count + count += 1 -.. code:: shell + print(f"执行了 {count} 次") - $ tar vxf setuptools-21.0.0.tar.gz - $ cd setuptools-21.0.0 - $ python setup.py install + height = info["height"] + weight = info["weight"] -安装完成后,下载pip。其信息在如下网站:https://pypi.python.org/pypi/pip + return weight / (height**2) -.. code:: shell + # 查出所有会员中过于肥胖的人的 bmi 指数 + fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] - # 下载 pip - wget https://pypi.python.org/packages/41/27/9a8d24e1b55bd8c85e4d022da2922cb206f183e2d18fee4e320c9547e751/pip-8.1.1.tar.gz#md5=6b86f11841e89c8241d689956ba99ed7 + print(fat_bmis) -同样的,进行安装 +输出如下 -.. code:: shell +:: - $ tar vxf pip-8.1.1.tar.gz - $ cd pip-8.1.1 - $ python setup.py install + 执行了 1 次 + 执行了 2 次 + 执行了 3 次 + 执行了 4 次 + [25.88057063502083] -安装完成后,执行 ``pip list`` -查看一下安装的包,若无异常,则一切顺利。或者你也可以试着安装一下第三方包 -``pip install requests`` 。 +可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 +次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 -8. 转移cloudinit +如果所有会员都是过于肥胖的,那最终将执行 6 +次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for +循环 + if 判断。 -上面说的项目,其实就是 cloudinit。接下来就要将 centos 7.2 上的cloudinit -的目录整体拷贝到 centos 6.5 的/usr/local/lib/python2.7/site-packages/ -目录下 +.. code:: python -|image1| + fat_bmis = [] -然后安装一些 cloudinit 的依赖包。 + # 查出所有会员中过于肥胖的人的 bmi 指数 + for m in members: + bmi = get_bmi(m) + if bmi > 24: + fat_bmis.append(bmi) -.. code:: shell +在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 - $ pip install six requests prettytable jsonpatch configobj +.. code:: python - # 默认还是安装在 python2.6 下 - $ yum install PyYAML -y + # 查出所有会员中过于肥胖的人的 bmi 指数 + fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] - # 将这些文件拷贝到 python2.7 目录下 - # 如果你不知道 python2.7 的目录,使用 import sys;print sys.path 就可以打印 - $ cd /usr/lib64/python2.6/site-packages - $ cp -r yaml/ /usr/local/lib/python2.7/site-packages/ - $ cp -p _yaml.so /usr/local/lib/python2.7/site-packages/ - $ cp -p PyYAML-3.10-py2.6.egg-info /usr/local/lib/python2.7/site-packages/ +最终从输出结果可以看出,只执行了 3 次 -执行一下 cloudinit 的几个命令,没有问题,任务就完成了。 +:: -.. code:: shell + 执行了 1 次 + 执行了 2 次 + 执行了 3 次 + [25.88057063502083] - $ cloud-init init -l - $ cloud-init init +这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 -**参考文章** +海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 -- https://www.cnblogs.com/stonehe/p/7944366.html +-------------- |image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20190831160317.png +.. |image1| image:: http://image.iswbm.com/image-20200418122739417.png .. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c01/c01_23.md b/source/c01/c01_23.md index 08841b5..7142677 100644 --- a/source/c01/c01_23.md +++ b/source/c01/c01_23.md @@ -1,4 +1,6 @@ -# 1.48 Python 炫技操作:模块重载的五种方法 +# 1.23 Python 炫技操作:模块重载的五种方法 + +![](http://image.iswbm.com/20200602135014.png) ## 环境准备 diff --git a/source/c01/c01_23.rst b/source/c01/c01_23.rst index 7876de7..c2f08a9 100644 --- a/source/c01/c01_23.rst +++ b/source/c01/c01_23.rst @@ -1,6 +1,8 @@ -1.48 Python 炫技操作:模块重载的五种方法 +1.23 Python 炫技操作:模块重载的五种方法 ======================================== +|image0| + 环境准备 -------- @@ -149,3 +151,6 @@ sys.modules 来重载模块这种方法是失效的。 >>> del sys.modules['foo.bar'] >>> from foo import bar >>> + +.. |image0| image:: http://image.iswbm.com/20200602135014.png + diff --git a/source/c01/c01_24.md b/source/c01/c01_24.md index 4100dba..ab429b8 100644 --- a/source/c01/c01_24.md +++ b/source/c01/c01_24.md @@ -1,4 +1,4 @@ -# 1.37 Python 炫技操作:条件语句的七种写法 +# 1.24 Python 炫技操作:条件语句的七种写法 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c01/c01_24.rst b/source/c01/c01_24.rst index b8c6ab6..4fa41c6 100644 --- a/source/c01/c01_24.rst +++ b/source/c01/c01_24.rst @@ -1,4 +1,4 @@ -1.37 Python 炫技操作:条件语句的七种写法 +1.24 Python 炫技操作:条件语句的七种写法 ======================================== |image0| diff --git a/source/c01/c01_25.md b/source/c01/c01_25.md index caa4153..428535f 100644 --- a/source/c01/c01_25.md +++ b/source/c01/c01_25.md @@ -1,4 +1,4 @@ -# 1.45 Python炫技操作:花式导包的八种方法 +# 1.25 Python炫技操作:花式导包的八种方法 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c01/c01_25.rst b/source/c01/c01_25.rst index 052224e..90d9581 100644 --- a/source/c01/c01_25.rst +++ b/source/c01/c01_25.rst @@ -1,4 +1,4 @@ -1.45 Python炫技操作:花式导包的八种方法 +1.25 Python炫技操作:花式导包的八种方法 ======================================= |image0| diff --git a/source/c01/c01_26.md b/source/c01/c01_26.md index 3f37049..d75731e 100644 --- a/source/c01/c01_26.md +++ b/source/c01/c01_26.md @@ -1,4 +1,4 @@ -# 1.39 Python 炫技操作:合并字典的七种方法 +# 1.26 Python 炫技操作:合并字典的七种方法 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c01/c01_26.rst b/source/c01/c01_26.rst index 89a0d3d..cb9ad4c 100644 --- a/source/c01/c01_26.rst +++ b/source/c01/c01_26.rst @@ -1,4 +1,4 @@ -1.39 Python 炫技操作:合并字典的七种方法 +1.26 Python 炫技操作:合并字典的七种方法 ======================================== |image0| diff --git a/source/c01/c01_27.md b/source/c01/c01_27.md index 2d70011..e8b2220 100644 --- a/source/c01/c01_27.md +++ b/source/c01/c01_27.md @@ -1,4 +1,4 @@ -# 1.40 Python 炫技操作:判断是否包含子串的七种方法 +# 1.27 Python 炫技操作:判断是否包含子串的七种方法 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c01/c01_27.rst b/source/c01/c01_27.rst index 7bd620a..f65a018 100644 --- a/source/c01/c01_27.rst +++ b/source/c01/c01_27.rst @@ -1,4 +1,4 @@ -1.40 Python 炫技操作:判断是否包含子串的七种方法 +1.27 Python 炫技操作:判断是否包含子串的七种方法 ================================================ |image0| diff --git a/source/c01/c01_28.md b/source/c01/c01_28.md deleted file mode 100644 index 185dfc3..0000000 --- a/source/c01/c01_28.md +++ /dev/null @@ -1,216 +0,0 @@ -# 1.41 Python 炫技操作:连接列表的八种方法 - -![](http://image.iswbm.com/20200602135014.png) - -Python 语言里有许多(而且是越来越多)的高级特性,是 Python 发烧友们非常喜欢的。在这些人的眼里,能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神。 - -但你要知道,在团队合作里,炫技是大忌。 - -为什么这么说呢?我说下自己的看法: - -1. 越简洁的代码,越清晰的逻辑,就越不容易出错; -2. 在团队合作中,你的代码不只有你在维护,降低别人的阅读/理解代码逻辑的成本是一个良好的品德 -3. 简单的代码,只会用到最基本的语法糖,复杂的高级特性,会有更多的依赖(如语言的版本) - -该篇是「**炫技系列**」的第三篇内容,在这个系列里,我将总结盘点一下,我所见过的那些炫技操作。在这里,如果你是 Python 发烧友,你可以学到一些写出超酷的代码书写技巧。同时,看了这些内容,对你在阅读别人的代码时,也许会有些帮助。 - -## 1. 最直观的相加 - -使用 `+` 对多个列表进行相加,你应该懂,不多说了。 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> list01 + list02 + list03 -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - -## 2. 借助 itertools - -itertools 在 Python 里有一个非常强大的内置模块,它专门用于操作可迭代对象。 - -在前面的文章中也介绍过,使用 `itertools.chain()` 函数先可迭代对象(在这里指的是列表)串联起来,组成一个更大的可迭代对象。 - -最后你再利用 list 将其转化为 列表。 - -```python ->>> from itertools import chain ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> list(chain(list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - -## 3. 使用 * 解包 - -在 [Python 炫技操作(02):合并字典的七种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&token=688334198&lang=zh_CN#rd) 提到了使用 `**` 可解包字典。 - -与它相似的,使用 `*` 可以解包列表。 `*` 和 `**` 常用在函数定义时,设置可变参数。 - -现在我将它单独拿出来用在多个列表的合并。 - -示例如下: - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> ->>> [*list01, *list02] -[1, 2, 3, 4, 5, 6] ->>> -``` - - - -## 4. 使用 extend - -在字典中,使用 update 可实现原地更新,而在列表中,使用 extend 可实现列表的自我扩展。 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> ->>> list01.extend(list02) ->>> list01 -[1, 2, 3, 4, 5, 6] -``` - -## 5. 使用列表推导式 - -Python 里对于生成列表、集合、字典,有一套非常 Pythonnic 的写法。 - -那就是列表解析式,集合解析式和字典解析式,通常是 Python 发烧友的最爱,那么今天的主题:列表合并,列表推导式还能否胜任呢? - -当然可以,具体示例代码如下: - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> [x for l in (list01, list02, list03) for x in l] -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - -## 6. 使用 heapq - -heapq 是 Python 的一个标准模块,它提供了堆排序算法的实现。 - -该模块里有一个 merge 方法,可以用于合并多个列表,如下所示 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> from heapq import merge ->>> ->>> list(merge(list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - -要注意的是,heapq.merge 除了合并多个列表外,它还会将合并后的最终的列表进行排序。 - -```python ->>> list01 = [2,5,3] ->>> list02 = [1,4,6] ->>> list03 = [7,9,8] ->>> ->>> from heapq import merge ->>> ->>> list(merge(list01, list02, list03)) -[1, 2, 4, 5, 3, 6, 7, 9, 8] ->>> -``` - -它的效果等价于下面这行代码: - -```python -sorted(itertools.chain(*iterables)) -``` - -如果你希望得到一个始终有序的列表,那请第一时间想到 heapq.merge,因为它采用堆排序,效率非常高。但若你不希望得到一个排过序的列表,就不要使用它了。 - -## 7. 借助魔法方法 - -在之前的文章里,把魔法方法介绍得很全。 - -[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd) - -[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd) - -其中有一个魔法方法是 `__add__`,实际 上当我们使用第一种方法 list01 + list02 的时候,内部实际上是作用在 `__add__` 这个魔法方法上的. - -所以以下两种方法其实是等价的 - -``` ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> ->>> list01 + list02 -[1, 2, 3, 4, 5, 6] ->>> ->>> ->>> list01.__add__(list02) -[1, 2, 3, 4, 5, 6] ->>> -``` - -借用这个魔法特性,我们可以 reduce 这个方法来对多个列表进行合并,示例代码如下 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> from functools import reduce ->>> reduce(list.__add__, (list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - -## 8. 使用 yield from - -在很早的一篇文章里([并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect)),我很详细的介绍了 yield from 意义及使用方法。 - -在 yield from 后可接一个可迭代对象,用于迭代并返回其中的每一个元素。 - -因此,我们可以像下面这样自定义一个合并列表的工具函数。 - -```python ->>> list01 = [1,2,3] ->>> list02 = [4,5,6] ->>> list03 = [7,8,9] ->>> ->>> def merge(*lists): -... for l in lists: -... yield from l -... ->>> list(merge(list01, list02, list03)) -[1, 2, 3, 4, 5, 6, 7, 8, 9] ->>> -``` - - - ---- - - - -![](http://image.iswbm.com/20200607174235.png) - - diff --git a/source/c01/c01_29.md b/source/c01/c01_29.md deleted file mode 100644 index 5c92a8e..0000000 --- a/source/c01/c01_29.md +++ /dev/null @@ -1,175 +0,0 @@ -# 1.42 Python 炫技操作:海象运算符的三种用法 - -![](http://image.iswbm.com/20200602135014.png) - -Python 版本发展非常快,如今最新的版本已经是 Pyhton 3.9,即便如此,有很多人甚至还停留在 3.6 或者 3.7,连 3.8 还没用上。 - -很多 Python 3.8 的特性还没来得及了解,就已经成为旧知识了,比如今天要说的海象运算符。 - -海象运算符是在 PEP 572 被提出的,直到 3.8 版本合入发布。 - -它的英文原名叫 `Assignment Expressions`,翻译过来也就是 `赋值表达式`,不过现在大家更普遍地称之为海象运算符,就是因为它长得真的太像海象了。 - -![](http://image.iswbm.com/image-20200418122739417.png) - -## 1. 第一个用法:if/else - -可能有朋友是第一次接触这个新特性,所以还是简单的介绍一下这个海象运算符有什么用? - -在 Golang 中的条件语句可以直接在 if 中运算变量的获取后直接对这个变量进行判断,可以让你少写一行代码 - -```go -import "fmt" - -func main() { - if age := 20;age > 18 { - fmt.Println("已经成年了") - } -} -``` - -若在 Python 3.8 之前,Python 必须得这样子写 - -```python -age = 20 -if age > 18: - print("已经成年了") -``` - -但有了海象运算符之后,你可以和 Golang 一样(如果你没学过 Golang,那这里要注意,Golang 中的 `:=` 叫短变量声明,意思是声明并初始化,它和 Python 中的 `:=` 不是一个概念) - -```python -if (age:= 20) > 18: - print("已经成年了") -``` - - - -## 2. 第二个用法:while - -在不使用 海象运算符之前,使用 while 循环来读取文件的时候,你也许会这么写 - -```python -file = open("demo.txt", "r") -while True: - line = file.readline() - if not line: - break - print(line.strip()) -``` - -但有了海象运算符之后,你可以这样 - -```python -file = open("demo.txt", "r") -while (line := file.readline()): - print(line.strip()) -``` - -使用它替换以往的无限 while 循环写法更为惊艳 - -比如,实现一个需要命令行交互输入密码并检验的代码,你也许会这样子写 - -```python -while True: - p = input("Enter the password: ") - if p == "youpassword": - break -``` - -有了海象运算符之后,这样子写更为舒服 - -```python -while (p := input("Enter the password: ")) != "youpassword": - continue -``` - - - -## 3. 第三个用法:推导式 - -这个系列的文章,几乎每篇都能看到推导式的身影,这一篇依旧如此。 - -在编码过程中,我很喜欢使用推导式,在简单的应用场景下,它简洁且不失高效。 - -如下这段代码中,我会使用列表推导式得出所有会员中过于肥胖的人的 bmi 指数 - -```python -members = [ - {"name": "小五", "age": 23, "height": 1.75, "weight": 72}, - {"name": "小李", "age": 17, "height": 1.72, "weight": 63}, - {"name": "小陈", "age": 20, "height": 1.78, "weight": 82}, -] - -count = 0 - -def get_bmi(info): - global count - count += 1 - - print(f"执行了 {count} 次") - - height = info["height"] - weight = info["weight"] - - return weight / (height**2) - -# 查出所有会员中过于肥胖的人的 bmi 指数 -fat_bmis = [get_bmi(m) for m in members if get_bmi(m) > 24] - -print(fat_bmis) -``` - -输出如下 - -``` -执行了 1 次 -执行了 2 次 -执行了 3 次 -执行了 4 次 -[25.88057063502083] -``` - -可以看到,会员数只有 3 个,但是 get_bmi 函数却执行了 4 次,原因是在判断时执行了 3 次,而在构造新的列表时又重复执行了一遍。 - -如果所有会员都是过于肥胖的,那最终将执行 6 次,这种在大量的数据下是比较浪费性能的,因此对于这种结构,我通常会使用传统的for 循环 + if 判断。 - -```python -fat_bmis = [] - -# 查出所有会员中过于肥胖的人的 bmi 指数 -for m in members: - bmi = get_bmi(m) - if bmi > 24: - fat_bmis.append(bmi) -``` - -在有了海象运算符之后,你就可以不用在这种场景下做出妥协。 - -```python -# 查出所有会员中过于肥胖的人的 bmi 指数 -fat_bmis = [bmi for m in members if (bmi := get_bmi(m)) > 24] -``` - -最终从输出结果可以看出,只执行了 3 次 - -``` -执行了 1 次 -执行了 2 次 -执行了 3 次 -[25.88057063502083] -``` - -这里仅介绍了列表推导式,但在字典推导式和集合推导式中同样适用。不再演示。 - - - -海象运算符,是一个新奇的特性,有不少人觉得这样这种特性会破坏代码的可读性。确实在一个新鲜事物刚出来时是会这样,但我相信经过时间的沉淀后,越来越多的人使用它并享受它带来的便利时,这种争议也会慢慢消失在历史的长河中。 - - - ---- - - - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file diff --git a/source/c02/c02_01.md b/source/c02/c02_01.md index e4f8ef4..7fdee0e 100644 --- a/source/c02/c02_01.md +++ b/source/c02/c02_01.md @@ -20,7 +20,7 @@ 在之后的章节里,将陆陆续续地给大家介绍到这三个知识点。 -## 2.1.1 基本概念 +## 1. 基本概念 在开始讲解理论知识之前,先过一下几个基本概念。虽然咱是进阶教程,但我也希望写得更小白,更通俗易懂。 @@ -44,7 +44,7 @@ .![](https://i.loli.net/2018/05/08/5af1781f05c29.jpg) -## 2.1.2 单线程VS多线程VS多进程 +## 2. 单线程VS多线程VS多进程 文字总是苍白无力的,千言万语不如几行代码来得孔武有力。 diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index c2021ea..229a185 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -18,8 +18,8 @@ 在之后的章节里,将陆陆续续地给大家介绍到这三个知识点。 -2.1.1 基本概念 --------------- +1. 基本概念 +----------- 在开始讲解理论知识之前,先过一下几个基本概念。虽然咱是进阶教程,但我也希望写得更小白,更通俗易懂。 @@ -47,8 +47,8 @@ .\ |image3| -2.1.2 单线程VS多线程VS多进程 ----------------------------- +2. 单线程VS多线程VS多进程 +------------------------- 文字总是苍白无力的,千言万语不如几行代码来得孔武有力。 diff --git a/source/c02/c02_05.md b/source/c02/c02_05.md index f87c9db..6724712 100644 --- a/source/c02/c02_05.md +++ b/source/c02/c02_05.md @@ -7,9 +7,6 @@ 上一篇我们说,线程与线程之间要通过消息通信来控制程序的执行。 讲完了消息通信,今天就来探讨下线程里的`信息隔离`是如何做到的。 ->**大家注意**: ->`信息隔离`,这并不是官方命名的名词,也不是网上广为流传的名词。是我为了方便理解而自创的,大家知道就好咯。 - ## 2.5.1 初步认识信息隔离 什么是`信息隔离`? diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 4df48c5..5d54df2 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -7,12 +7,8 @@ 上一篇我们说,线程与线程之间要通过消息通信来控制程序的执行。 -讲完了消息通信,今天就来探讨下线程里的\ ``信息隔离``\ 是如何做到的。 ->\ **大家注意**\ : ->\ ``信息隔离``\ ,这并不是官方命名的名词,也不是网上广为流传的名词。是我为了方便理解而自创的,大家知道就好咯。 - +讲完了消息通信,今天就来探讨下线程里的\ ``信息隔离``\ 是如何做到的。 ## 2.5.1 初步认识信息隔离 ----------------------- 什么是\ ``信息隔离``\ ? diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index 19ba6bf..90a9a94 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -1,12 +1,7 @@ -# 2.6 线程池与进程池的创建 +# 2.6 线程池创建的几种方法 ![](http://image.iswbm.com/20200602135014.png) ---- - ->**友情提醒**: ->本系列所有的代码均在Python3下编写。Python2中可能有所差异。 - ## 1. 线程池的创建 ### 使用内置模块 diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 35abcb7..4b753e7 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -1,13 +1,8 @@ -2.6 线程池与进程池的创建 +2.6 线程池创建的几种方法 ======================== |image0| --------------- - - **友情提醒**\ : - 本系列所有的代码均在Python3下编写。Python2中可能有所差异。 - 1. 线程池的创建 --------------- diff --git a/source/c02/c02_07.md b/source/c02/c02_07.md index e50d9ad..a8af238 100644 --- a/source/c02/c02_07.md +++ b/source/c02/c02_07.md @@ -15,7 +15,7 @@ >**友情提醒**: >本系列所有的代码均在Python3下编写。Python2中可能有所差异。 -## 2.7.1 可迭代、迭代器、生成器 +## 1. 可迭代、迭代器、生成器 初学Python的时候,对于这三货真的是傻傻分不清。甚至还认为他们是等价的。 @@ -223,7 +223,7 @@ if __name__ == '__main__': 可迭代对象和迭代器,是将所有的值都生成存放在内存中,而`生成器`则是需要元素才临时生成,节省时间,节省空间。 -## 2.7.2 如何运行/激活生成器 +## 2. 如何运行/激活生成器 由于生成器并不是一次生成所有元素,而是一次一次的执行返回,那么如何刺激生成器执行(或者说激活)呢? @@ -256,7 +256,7 @@ if __name__ == '__main__': 3 ``` -## 2.7.3 生成器的执行状态 +## 3. 生成器的执行状态 生成器在其生命周期中,会有如下四个状态 >`GEN_CREATED` # 等待开始执行 @@ -295,7 +295,7 @@ GEN_SUSPENDED GEN_CLOSED ``` -## 2.7.4 生成器的异常处理 +## 4. 生成器的异常处理 在生成器工作过程中,若生成器不满足生成元素的条件,就`会`/`应该` 抛出异常(`StopIteration`)。 @@ -319,7 +319,7 @@ if __name__ == '__main__': next(gen) ``` -## 2.7.5 从生成器过渡到协程:yield +## 5. 从生成器过渡到协程:yield 通过上面的介绍,我们知道生成器为我们引入了暂停函数执行(`yield`)的功能。当有了暂停的功能之后,人们就想能不能在生成器暂停的时候向其发送一点东西(其实上面也有提及:`send(None)`)。这种向暂停的生成器发送信息的功能通过 `PEP 342` 进入 `Python 2.5` 中,并催生了 `Python` 中`协程`的诞生。根据 `wikipedia` 中的定义 >协程是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序。 diff --git a/source/c02/c02_07.rst b/source/c02/c02_07.rst index 05a81da..93f6a58 100755 --- a/source/c02/c02_07.rst +++ b/source/c02/c02_07.rst @@ -16,8 +16,8 @@ **友情提醒**\ : 本系列所有的代码均在Python3下编写。Python2中可能有所差异。 -2.7.1 可迭代、迭代器、生成器 ----------------------------- +1. 可迭代、迭代器、生成器 +------------------------- 初学Python的时候,对于这三货真的是傻傻分不清。甚至还认为他们是等价的。 @@ -234,8 +234,8 @@ 可迭代对象和迭代器,是将所有的值都生成存放在内存中,而\ ``生成器``\ 则是需要元素才临时生成,节省时间,节省空间。 -2.7.2 如何运行/激活生成器 -------------------------- +2. 如何运行/激活生成器 +---------------------- 由于生成器并不是一次生成所有元素,而是一次一次的执行返回,那么如何刺激生成器执行(或者说激活)呢? @@ -269,8 +269,8 @@ 2 3 -2.7.3 生成器的执行状态 ----------------------- +3. 生成器的执行状态 +------------------- 生成器在其生命周期中,会有如下四个状态 >\ ``GEN_CREATED`` # 等待开始执行 >\ ``GEN_RUNNING`` # @@ -310,8 +310,8 @@ 1 GEN_CLOSED -2.7.4 生成器的异常处理 ----------------------- +4. 生成器的异常处理 +------------------- 在生成器工作过程中,若生成器不满足生成元素的条件,就\ ``会``/``应该`` 抛出异常(\ ``StopIteration``\ )。 @@ -337,8 +337,8 @@ next(gen) next(gen) -2.7.5 从生成器过渡到协程:yield -------------------------------- +5. 从生成器过渡到协程:yield +---------------------------- 通过上面的介绍,我们知道生成器为我们引入了暂停函数执行(\ ``yield``\ )的功能。当有了暂停的功能之后,人们就想能不能在生成器暂停的时候向其发送一点东西(其实上面也有提及:\ ``send(None)``\ )。这种向暂停的生成器发送信息的功能通过 ``PEP 342`` 进入 ``Python 2.5`` 中,并催生了 ``Python`` diff --git a/source/c02/c02_08.md b/source/c02/c02_08.md index ea82375..144925f 100644 --- a/source/c02/c02_08.md +++ b/source/c02/c02_08.md @@ -11,7 +11,7 @@ >**友情提醒**: >本系列所有的代码均在Python3下编写。Python2中可能有所差异。 -## 2.8.1 为什么要使用协程 +## 1. 为什么要使用协程 在上一篇中,我们从生成器的基本认识与使用,成功过渡到了协程。 @@ -51,13 +51,13 @@ def spider_02(url): 2. 利用同步的方式去实现异步 3. 不再需要锁,提高了并发性能 -## 2.8.2 yield from的用法详解 +## 2. yield from的用法详解 `yield from` 是在Python3.3才出现的语法。所以这个特性在Python2中是没有的。 `yield from` 后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。 -### 简单应用:拼接可迭代对象 +### 2.1 简单应用:拼接可迭代对象 我们可以用一个使用`yield`和一个使用`yield from`的例子来对比看下。 @@ -104,7 +104,7 @@ print(list(new_list)) 由上面两种方式对比,可以看出,yield from后面加上可迭代对象,他可以把可迭代对象里的每个元素一个一个的yield出来,对比yield来说代码更加简洁,结构更加清晰。 -### 复杂应用:生成器的嵌套 +### 2.2 复杂应用:生成器的嵌套 如果你认为只是 `yield from` 仅仅只有上述的功能的话,那你就太小瞧了它,它的更强大的功能还在后面。 @@ -215,7 +215,7 @@ if __name__ == '__main__': ``` -## 2.8.3 为什么要使用yield from +## 3. 为什么要使用yield from 学到这里,我相信你肯定要问,既然委托生成器,起到的只是一个双向通道的作用,我还需要委托生成器做什么?我调用方直接调用子生成器不就好啦? @@ -223,7 +223,7 @@ if __name__ == '__main__': 下面我们来一起探讨一下,到底yield from 有什么过人之处,让我们非要用它不可。 -### 因为它可以帮我们处理异常 +### 3.1 因为它可以帮我们处理异常 如果我们去掉委托生成器,而直接调用子生成器。那我们就需要把代码改成像下面这样,我们需要自己捕获异常并处理。而不像使`yield from`那样省心。 diff --git a/source/c02/c02_08.rst b/source/c02/c02_08.rst index 21019c2..2c142be 100755 --- a/source/c02/c02_08.rst +++ b/source/c02/c02_08.rst @@ -12,8 +12,8 @@ **友情提醒**\ : 本系列所有的代码均在Python3下编写。Python2中可能有所差异。 -2.8.1 为什么要使用协程 ----------------------- +1. 为什么要使用协程 +------------------- 在上一篇中,我们从生成器的基本认识与使用,成功过渡到了协程。 @@ -54,8 +54,8 @@ 协程是在单线程里实现任务的切换的 2. 利用同步的方式去实现异步 3. 不再需要锁,提高了并发性能 -2.8.2 yield from的用法详解 --------------------------- +2. yield from的用法详解 +----------------------- ``yield from`` 是在Python3.3才出现的语法。所以这个特性在Python2中是没有的。 @@ -63,8 +63,8 @@ ``yield from`` 后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。 -简单应用:拼接可迭代对象 -~~~~~~~~~~~~~~~~~~~~~~~~ +2.1 简单应用:拼接可迭代对象 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 我们可以用一个使用\ ``yield``\ 和一个使用\ ``yield from``\ 的例子来对比看下。 @@ -114,8 +114,8 @@ 由上面两种方式对比,可以看出,yield from后面加上可迭代对象,他可以把可迭代对象里的每个元素一个一个的yield出来,对比yield来说代码更加简洁,结构更加清晰。 -复杂应用:生成器的嵌套 -~~~~~~~~~~~~~~~~~~~~~~ +2.2 复杂应用:生成器的嵌套 +~~~~~~~~~~~~~~~~~~~~~~~~~~ 如果你认为只是 ``yield from`` 仅仅只有上述的功能的话,那你就太小瞧了它,它的更强大的功能还在后面。 @@ -231,8 +231,8 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 计算完毕!! 总共传入 3 个数值, 总和:60,平均数:20.0 -2.8.3 为什么要使用yield from ----------------------------- +3. 为什么要使用yield from +------------------------- 学到这里,我相信你肯定要问,既然委托生成器,起到的只是一个双向通道的作用,我还需要委托生成器做什么?我调用方直接调用子生成器不就好啦? @@ -241,8 +241,8 @@ from后面加上可迭代对象,他可以把可迭代对象里的每个元素 下面我们来一起探讨一下,到底yield from 有什么过人之处,让我们非要用它不可。 -因为它可以帮我们处理异常 -~~~~~~~~~~~~~~~~~~~~~~~~ +3.1 因为它可以帮我们处理异常 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 如果我们去掉委托生成器,而直接调用子生成器。那我们就需要把代码改成像下面这样,我们需要自己捕获异常并处理。而不像使\ ``yield from``\ 那样省心。 diff --git a/source/c02/c02_09.md b/source/c02/c02_09.md index 540d0c8..bde67f4 100644 --- a/source/c02/c02_09.md +++ b/source/c02/c02_09.md @@ -19,7 +19,7 @@ 那是因为,我们现在还缺少一个成熟的框架,帮助你完成那些复杂的动作。这个时候,`ayncio`就这么应运而生了。 -## 2.9.1 如何定义/创建协程 +## 1. 如何定义/创建协程 还记得在前两章节的时候,我们创建了生成器,是如何去检验我们创建的是不是生成器对象吗? @@ -67,7 +67,7 @@ if __name__ == '__main__': print(isinstance(coroutine, Coroutine)) # False ``` -## 2.9.2 asyncio的几个概念 +## 2. asyncio的几个概念 在了解`asyncio`的使用方法前,首先有必要先介绍一下,这几个贯穿始终的概念。 @@ -79,7 +79,7 @@ if __name__ == '__main__': 这几个概念,干看可能很难以理解,没事,往下看实例,然后再回来,我相信你一定能够理解。 -## 2.9.3 学习协程是如何工作的 +## 3. 学习协程是如何工作的 协程完整的工作流程是这样的 - 定义/创建协程对象 @@ -112,7 +112,7 @@ loop.run_until_complete(task) Hello, World ``` -## 2.9.4 await与yield对比 +## 4. await与yield对比 前面我们说,`await`用于挂起阻塞的异步调用接口。其作用在`一定程度上`类似于yield。 @@ -162,7 +162,7 @@ print(isinstance(task, Future)) # True 好了,接下来,开始验证。 ![验证通过](https://i.loli.net/2018/05/26/5b09814dc4714.png) -## 2.9.5 绑定回调函数 +## 5. 绑定回调函数 异步IO的实现原理,就是在IO高的地方挂起,等IO结束后,再继续执行。在绝大部分时候,我们后续的代码的执行是需要依赖IO的返回值的,这就要用到回调了。 diff --git a/source/c02/c02_09.rst b/source/c02/c02_09.rst index 42ec222..7edb981 100755 --- a/source/c02/c02_09.rst +++ b/source/c02/c02_09.rst @@ -22,8 +22,8 @@ 那是因为,我们现在还缺少一个成熟的框架,帮助你完成那些复杂的动作。这个时候,\ ``ayncio``\ 就这么应运而生了。 -2.9.1 如何定义/创建协程 ------------------------ +1. 如何定义/创建协程 +-------------------- 还记得在前两章节的时候,我们创建了生成器,是如何去检验我们创建的是不是生成器对象吗? @@ -75,8 +75,8 @@ print(isinstance(coroutine, Generator)) # True print(isinstance(coroutine, Coroutine)) # False -2.9.2 asyncio的几个概念 ------------------------ +2. asyncio的几个概念 +-------------------- 在了解\ ``asyncio``\ 的使用方法前,首先有必要先介绍一下,这几个贯穿始终的概念。 @@ -92,8 +92,8 @@ 这几个概念,干看可能很难以理解,没事,往下看实例,然后再回来,我相信你一定能够理解。 -2.9.3 学习协程是如何工作的 --------------------------- +3. 学习协程是如何工作的 +----------------------- 协程完整的工作流程是这样的 - 定义/创建协程对象 - 将协程转为task任务 - 定义事件循环对象容器 - 将task任务扔进事件循环对象中触发 @@ -126,8 +126,8 @@ Hello, World -2.9.4 await与yield对比 ----------------------- +4. await与yield对比 +------------------- 前面我们说,\ ``await``\ 用于挂起阻塞的异步调用接口。其作用在\ ``一定程度上``\ 类似于yield。 @@ -179,8 +179,8 @@ 好了,接下来,开始验证。 |验证通过| -2.9.5 绑定回调函数 ------------------- +5. 绑定回调函数 +--------------- 异步IO的实现原理,就是在IO高的地方挂起,等IO结束后,再继续执行。在绝大部分时候,我们后续的代码的执行是需要依赖IO的返回值的,这就要用到回调了。 diff --git a/source/c02/c02_10.md b/source/c02/c02_10.md index 82a253e..e219c04 100644 --- a/source/c02/c02_10.md +++ b/source/c02/c02_10.md @@ -27,7 +27,7 @@ 那么这一节,我们就来看下,协程中的`多任务`。 -## 2.10.1 协程中的并发 +## 1. 协程中的并发 协程的并发,和线程一样。举个例子来说,就好像 一个人同时吃三个馒头,咬了第一个馒头一口,就得等这口咽下去,才能去啃第其他两个馒头。就这样交替换着吃。 @@ -114,7 +114,7 @@ Task ret: Done after 2s Task ret: Done after 4s ``` -## 2.10.2 协程中的嵌套 +## 2. 协程中的嵌套 使用async可以定义协程,协程用于耗时的io操作,我们也可以封装更多的io操作过程,这样就实现了嵌套的协程,即一个协程中await了另外一个协程,如此连接起来。 @@ -236,7 +236,7 @@ async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED): return await _wait(fs, timeout, return_when, loop) ``` -## 2.10.3协程中的状态 +## 3. 协程中的状态 还记得我们在讲生成器的时候,有提及过生成器的状态。同样,在协程这里,我们也了解一下协程(准确的说,应该是Future对象,或者Task任务)有哪些状态。 @@ -289,7 +289,7 @@ if __name__ == '__main__': 假如,执行后 立马按下 Ctrl+C,则会触发task取消,就会打印 `Pending` -> `Cancelling` -> `Cancelling` 的状态变化。 -## 2.10.4 gather与wait +## 4. gather与wait 还记得上面我说,把多个协程注册进一个事件循环中有两种方法吗? - 使用`asyncio.wait()` @@ -320,7 +320,7 @@ async def factorial(name, number): print("Task %s: factorial(%s) = %s" % (name, number, f)) ``` -## 2.10.5 接收参数方式 +## 5. 接收参数方式 ### asyncio.wait @@ -388,7 +388,7 @@ group3 = asyncio.gather(*[factorial("B", i) for i in range(1, 7)]) loop.run_until_complete(asyncio.gather(group1, group2, group3)) ``` -## 2.10.6 返回结果不同 +## 6. 返回结果不同 ### asyncio.wait @@ -414,7 +414,7 @@ for result in results: print('Task ret: ', result) ``` -## 2.10.7wait有控制功能 +## 7. wait有控制功能 ```python import asyncio diff --git a/source/c02/c02_10.rst b/source/c02/c02_10.rst index ea98273..0451b3a 100755 --- a/source/c02/c02_10.rst +++ b/source/c02/c02_10.rst @@ -25,8 +25,8 @@ 那么这一节,我们就来看下,协程中的\ ``多任务``\ 。 -2.10.1 协程中的并发 -------------------- +1. 协程中的并发 +--------------- 协程的并发,和线程一样。举个例子来说,就好像 一个人同时吃三个馒头,咬了第一个馒头一口,就得等这口咽下去,才能去啃第其他两个馒头。就这样交替换着吃。 @@ -121,8 +121,8 @@ Task ret: Done after 2s Task ret: Done after 4s -2.10.2 协程中的嵌套 -------------------- +2. 协程中的嵌套 +--------------- 使用async可以定义协程,协程用于耗时的io操作,我们也可以封装更多的io操作过程,这样就实现了嵌套的协程,即一个协程中await了另外一个协程,如此连接起来。 @@ -254,8 +254,8 @@ # 【重点】:await一个内部协程 return await _wait(fs, timeout, return_when, loop) -2.10.3协程中的状态 ------------------- +3. 协程中的状态 +--------------- 还记得我们在讲生成器的时候,有提及过生成器的状态。同样,在协程这里,我们也了解一下协程(准确的说,应该是Future对象,或者Task任务)有哪些状态。 @@ -310,8 +310,8 @@ 假如,执行后 立马按下 Ctrl+C,则会触发task取消,就会打印 ``Pending`` -> ``Cancelling`` -> ``Cancelling`` 的状态变化。 -2.10.4 gather与wait -------------------- +4. gather与wait +--------------- 还记得上面我说,把多个协程注册进一个事件循环中有两种方法吗? - 使用\ ``asyncio.wait()`` @@ -346,8 +346,8 @@ f *= i print("Task %s: factorial(%s) = %s" % (name, number, f)) -2.10.5 接收参数方式 -------------------- +5. 接收参数方式 +--------------- asyncio.wait ~~~~~~~~~~~~ @@ -426,8 +426,8 @@ asyncio.gather loop.run_until_complete(asyncio.gather(group1, group2, group3)) -2.10.6 返回结果不同 -------------------- +6. 返回结果不同 +--------------- .. _asyncio.wait-1: @@ -460,8 +460,8 @@ asyncio.gather for result in results: print('Task ret: ', result) -2.10.7wait有控制功能 --------------------- +7. wait有控制功能 +----------------- .. code:: python diff --git a/source/c02/c02_11.md b/source/c02/c02_11.md index 602e681..34dba9b 100644 --- a/source/c02/c02_11.md +++ b/source/c02/c02_11.md @@ -16,7 +16,7 @@ - Redis的基本使用 - asyncio的使用 -## 2.11.1 动态添加协程 +## 1. 动态添加协程 在实战之前,我们要先了解下在asyncio中如何将协程态添加到事件循环中的。这是前提。 如何实现呢,有两种方法: @@ -114,7 +114,7 @@ Thu May 31 22:23:38 2018 Thu May 31 22:23:41 2018 ``` -## 2.11.2 利用redis实现动态添加任务 +## 2. 利用redis实现动态添加任务 对于并发任务,通常是用生成消费模型,对队列的处理可以使用类似master-worker的方式,master主要用户获取队列的msg,worker用户处理消息。 为了简单起见,并且协程更适合单线程的方式,我们的主线程用来监听队列,子线程用于处理队列。这里使用redis的队列。主线程中有一个是无限循环,用户消费队列。 diff --git a/source/c02/c02_11.rst b/source/c02/c02_11.rst index f747cd4..27271cf 100755 --- a/source/c02/c02_11.rst +++ b/source/c02/c02_11.rst @@ -14,8 +14,8 @@ 在实战中,将会用到以下知识点: - 多线程的基本使用 - Queue消息队列的使用 - Redis的基本使用 - asyncio的使用 -2.11.1 动态添加协程 -------------------- +1. 动态添加协程 +--------------- 在实战之前,我们要先了解下在asyncio中如何将协程态添加到事件循环中的。这是前提。 @@ -119,8 +119,8 @@ 第一个 协程运行完.. Thu May 31 22:23:41 2018 -2.11.2 利用redis实现动态添加任务 --------------------------------- +2. 利用redis实现动态添加任务 +---------------------------- 对于并发任务,通常是用生成消费模型,对队列的处理可以使用类似master-worker的方式,master主要用户获取队列的msg,worker用户处理消息。 diff --git a/source/c03/c03_01.md b/source/c03/c03_01.md index a915b0d..5c3562e 100644 --- a/source/c03/c03_01.md +++ b/source/c03/c03_01.md @@ -20,7 +20,7 @@ ![](http://image.iswbm.com/20190811100737.png) -## 3.1.1 Hello,装饰器 +## 1. Hello,装饰器 装饰器的使用方法很固定 - 先定义一个装饰器(帽子) @@ -47,7 +47,7 @@ def function(): 接下来,我将以实例讲解,如何编写出各种简单及复杂的装饰器。 -## 3.1.2 入门:日志打印器 +## 2. 入门:日志打印器 首先是**日志打印器**。 实现的功能: @@ -83,7 +83,7 @@ add(200, 50) 主人,我执行完啦。 ``` -## 3.1.3 入门:时间计时器 +## 3. 入门:时间计时器 再来看看 **时间计时器** 实现功能:顾名思义,就是计算一个函数的执行时长。 @@ -118,7 +118,7 @@ want_sleep(10) ``` -## 3.1.4 进阶:带参数的函数装饰器 +## 4. 进阶:带参数的函数装饰器 通过上面两个简单的入门示例,你应该能体会到装饰器的工作原理了。 @@ -188,7 +188,7 @@ jack() ------------ hello. ``` -## 3.1.5 高阶:不带参数的类装饰器 +## 5. 高阶:不带参数的类装饰器 以上都是基于函数实现的装饰器,在阅读别人代码时,还可以时常发现还有基于类实现的装饰器。 @@ -220,7 +220,7 @@ say("hello") say hello! ``` -## 3.1.6 高阶:带参数的类装饰器 +## 6. 高阶:带参数的类装饰器 上面不带参数的例子,你发现没有,只能打印`INFO`级别的日志,正常情况下,我们还需要打印`DEBUG` `WARNING`等级别的日志。 这就需要给类装饰器传入参数,给这个函数指定级别了。 @@ -253,7 +253,7 @@ say("hello") say hello! ``` -## 3.1.7 使用偏函数与类实现装饰器 +## 7. 使用偏函数与类实现装饰器 绝大多数装饰器都是基于函数和闭包实现的,但这并非制造装饰器的唯一方式。 @@ -319,7 +319,7 @@ Wait for 2 seconds... ``` -## 3.1.8 如何写能装饰类的装饰器? +## 8. 如何写能装饰类的装饰器? 用 Python 写单例模式的时候,常用的有三种写法。其中一种,是用装饰器来实现的。 @@ -354,7 +354,7 @@ class User: ![](http://image.iswbm.com/20190512113917.png) -## 3.1.9 wraps 装饰器有啥用? +## 9. wraps 装饰器有啥用? 在 functools 标准库中有提供一个 wraps 装饰器,你应该也经常见过,那他有啥用呢? 先来看一个例子 @@ -444,7 +444,7 @@ def wrapped(): print(wrapped.__name__) ``` -## 3.1.10 内置装饰器:property +## 10. 内置装饰器:property 以上,我们介绍的都是自定义的装饰器。 @@ -666,7 +666,7 @@ in __get__ 如对上面代码的运行原理,有疑问的同学,请务必结合上面两点说明加以理解,那两点相当关键。 -## 3.1.11 其他装饰器:装饰器实战 +## 11. 其他装饰器:装饰器实战 读完并理解了上面的内容,你可以说是Python高手了。别怀疑,自信点,因为很多人都不知道装饰器有这么多用法呢。 diff --git a/source/c03/c03_01.rst b/source/c03/c03_01.rst index 8ee4115..0fcf09e 100755 --- a/source/c03/c03_01.rst +++ b/source/c03/c03_01.rst @@ -24,8 +24,8 @@ |image1| -3.1.1 Hello,装饰器 -------------------- +1. Hello,装饰器 +---------------- 装饰器的使用方法很固定 - 先定义一个装饰器(帽子) - 再定义你的业务函数或者类(人) - @@ -51,8 +51,8 @@ 接下来,我将以实例讲解,如何编写出各种简单及复杂的装饰器。 -3.1.2 入门:日志打印器 ----------------------- +2. 入门:日志打印器 +------------------- 首先是\ **日志打印器**\ 。 实现的功能: - 在函数执行前,先打印一行日志告知一下主人,我要执行函数了。 - @@ -93,8 +93,8 @@ 200 + 50 = 250 主人,我执行完啦。 -3.1.3 入门:时间计时器 ----------------------- +3. 入门:时间计时器 +------------------- 再来看看 **时间计时器** 实现功能:顾名思义,就是计算一个函数的执行时长。 @@ -131,8 +131,8 @@ 花费时间:10.0073800086975098秒 -3.1.4 进阶:带参数的函数装饰器 ------------------------------- +4. 进阶:带参数的函数装饰器 +--------------------------- 通过上面两个简单的入门示例,你应该能体会到装饰器的工作原理了。 @@ -209,8 +209,8 @@ periodic_task ------------ hello. -3.1.5 高阶:不带参数的类装饰器 ------------------------------- +5. 高阶:不带参数的类装饰器 +--------------------------- 以上都是基于函数实现的装饰器,在阅读别人代码时,还可以时常发现还有基于类实现的装饰器。 @@ -244,8 +244,8 @@ periodic_task [INFO]: the function say() is running... say hello! -3.1.6 高阶:带参数的类装饰器 ----------------------------- +6. 高阶:带参数的类装饰器 +------------------------- 上面不带参数的例子,你发现没有,只能打印\ ``INFO``\ 级别的日志,正常情况下,我们还需要打印\ ``DEBUG`` ``WARNING``\ 等级别的日志。 @@ -282,8 +282,8 @@ periodic_task [WARNING]: the function say() is running... say hello! -3.1.7 使用偏函数与类实现装饰器 ------------------------------- +7. 使用偏函数与类实现装饰器 +--------------------------- 绝大多数装饰器都是基于函数和闭包实现的,但这并非制造装饰器的唯一方式。 @@ -354,8 +354,8 @@ Python工匠:使用装饰器的小技巧) >>> add.func # 实现实例方法 -3.1.8 如何写能装饰类的装饰器? ------------------------------- +8. 如何写能装饰类的装饰器? +--------------------------- 用 Python 写单例模式的时候,常用的有三种写法。其中一种,是用装饰器来实现的。 @@ -392,8 +392,8 @@ Python工匠:使用装饰器的小技巧) |image2| -3.1.9 wraps 装饰器有啥用? --------------------------- +9. wraps 装饰器有啥用? +----------------------- 在 functools 标准库中有提供一个 wraps 装饰器,你应该也经常见过,那他有啥用呢? @@ -488,8 +488,8 @@ wraps的情况下,也可以让 ``wrapped.__name__`` 打印出 wrapped,代码 print(wrapped.__name__) -3.1.10 内置装饰器:property ---------------------------- +10. 内置装饰器:property +------------------------ 以上,我们介绍的都是自定义的装饰器。 @@ -732,8 +732,8 @@ property 如对上面代码的运行原理,有疑问的同学,请务必结合上面两点说明加以理解,那两点相当关键。 -3.1.11 其他装饰器:装饰器实战 ------------------------------ +11. 其他装饰器:装饰器实战 +-------------------------- 读完并理解了上面的内容,你可以说是Python高手了。别怀疑,自信点,因为很多人都不知道装饰器有这么多用法呢。 diff --git a/source/c03/c03_02.md b/source/c03/c03_02.md index 8ee46db..45edc25 100644 --- a/source/c03/c03_02.md +++ b/source/c03/c03_02.md @@ -4,7 +4,7 @@ --- -## 3.2.1 类是如何产生的 +## 1. 类是如何产生的 类是如何产生? @@ -15,7 +15,7 @@ type?这不是判断对象类型的函数吗? 是的,type通常用法就是用来判断对象的类型。但除此之外,他最大的用途是用来动态创建类。当Python扫描到class的语法的时候,就会调用type函数进行类的创建。 -## 3.2.2 如何使用type创建类 +## 2. 如何使用type创建类 首先,type()需要接收三个参数 ``` @@ -39,7 +39,7 @@ def say(self): User = type("User", (BaseClass, ), {"name":"user", "say":say}) ``` -## 3.2.3 理解什么是元类 +## 3. 理解什么是元类 什么是类?可能谁都知道,类就是用来创建对象的「模板」。 @@ -157,7 +157,7 @@ True -## 3.3.4 使用元类的意义 +## 4. 使用元类的意义 正常情况下,我们都不会使用到元类。但是这并不意味着,它不重要。假如某一天,我们需要写一个框架,很有可能就需要你对元类要有进一步的研究。 @@ -173,7 +173,7 @@ True 但是,这样说,你一定不会服气,到底元类用来干什么?其实元类的作用就是`创建API`,一个最典型的应用是 `Django ORM`。 -## 3.3.5 元类实战:ORM +## 5. 元类实战:ORM 使用过Django ORM的人都知道,有了ORM,使得我们操作数据库,变得异常简单。 @@ -307,7 +307,7 @@ class ModelMetaClass(type): return super().__new__(cls, name, bases, attrs) ``` -## 3.2.6 \__new__ 有什么用? +## 6. \__new__ 有什么用? 在没有元类的情况下,每次创建实例,在先进入 `__init__` 之前都会先进入 ` __new__`。 diff --git a/source/c03/c03_02.rst b/source/c03/c03_02.rst index a134058..38fa73b 100755 --- a/source/c03/c03_02.rst +++ b/source/c03/c03_02.rst @@ -5,8 +5,8 @@ -------------- -3.2.1 类是如何产生的 --------------------- +1. 类是如何产生的 +----------------- 类是如何产生? @@ -16,8 +16,8 @@ type?这不是判断对象类型的函数吗? 是的,type通常用法就是用来判断对象的类型。但除此之外,他最大的用途是用来动态创建类。当Python扫描到class的语法的时候,就会调用type函数进行类的创建。 -3.2.2 如何使用type创建类 ------------------------- +2. 如何使用type创建类 +--------------------- 首先,type()需要接收三个参数 @@ -43,8 +43,8 @@ type?这不是判断对象类型的函数吗? # 使用type来创建User类 User = type("User", (BaseClass, ), {"name":"user", "say":say}) -3.2.3 理解什么是元类 --------------------- +3. 理解什么是元类 +----------------- 什么是类?可能谁都知道,类就是用来创建对象的「模板」。 @@ -170,8 +170,8 @@ type是Python在背后用来创建所有类的元类,我们熟知的类的始 >>> u1 is u2 True -3.3.4 使用元类的意义 --------------------- +4. 使用元类的意义 +----------------- 正常情况下,我们都不会使用到元类。但是这并不意味着,它不重要。假如某一天,我们需要写一个框架,很有可能就需要你对元类要有进一步的研究。 @@ -188,8 +188,8 @@ type是Python在背后用来创建所有类的元类,我们熟知的类的始 但是,这样说,你一定不会服气,到底元类用来干什么?其实元类的作用就是\ ``创建API``\ ,一个最典型的应用是 ``Django ORM``\ 。 -3.3.5 元类实战:ORM -------------------- +5. 元类实战:ORM +---------------- 使用过Django ORM的人都知道,有了ORM,使得我们操作数据库,变得异常简单。 @@ -330,8 +330,8 @@ ORM的一个类(User),就对应数据库中的一张表。id,name,email,passwo attrs["fields"] = fields return super().__new__(cls, name, bases, attrs) -3.2.6 \__new_\_ 有什么用? --------------------------- +6. \__new_\_ 有什么用? +----------------------- 在没有元类的情况下,每次创建实例,在先进入 ``__init__`` 之前都会先进入 ``__new__``\ 。 diff --git a/source/c03/c03_03.md b/source/c03/c03_03.md index bc5dee3..a6347d5 100644 --- a/source/c03/c03_03.md +++ b/source/c03/c03_03.md @@ -4,7 +4,7 @@ --- -## 3.3.1 什么是socket? +## 1. 什么是socket? 说到网络编程,难免要提到socket? @@ -14,7 +14,7 @@ 不管是不同主机,还是同一主机。既然是通信,必定有一个发送方,一个接收方。对应一个客户端,和一个服务端。 -## 3.3.2 创建客户端 +## 2. 创建客户端 - 创建socket,建立连接 ```python @@ -47,7 +47,7 @@ while True: data = ''.join(buffer) ``` -## 3.3.3 创建服务端 +## 3. 创建服务端 - 创建socket ```python import socket @@ -84,11 +84,11 @@ def tcplink(sock, addr): sock.close() ``` -## 3.3.4 socket工作流程 +## 4. socket工作流程 ![](https://i.loli.net/2018/04/30/5ae6c303c870c.png) -## 3.3.5 socket公共函数汇总 +## 5. socket公共函数汇总 - 发送数据 ```python # 发送TCP数据,返回值:发送的字节当量 @@ -145,7 +145,7 @@ sk.setsockopt(level,optname,value) sk.getsockopt(level,optname) ``` -## 3.3.6 搭建在线聊天机器人 +## 6. 搭建在线聊天机器人 通过上面的学习,我们知道,同主机下或不同主机下的两个进程要进行通信(TCP/UDP,不管是消息传输还是文件传输),必定要借助socket这个桥梁。 diff --git a/source/c03/c03_03.rst b/source/c03/c03_03.rst index 8c032a5..00502ce 100755 --- a/source/c03/c03_03.rst +++ b/source/c03/c03_03.rst @@ -5,8 +5,8 @@ -------------- -3.3.1 什么是socket? --------------------- +1. 什么是socket? +----------------- 说到网络编程,难免要提到socket? @@ -16,8 +16,8 @@ 不管是不同主机,还是同一主机。既然是通信,必定有一个发送方,一个接收方。对应一个客户端,和一个服务端。 -3.3.2 创建客户端 ----------------- +2. 创建客户端 +------------- - 创建socket,建立连接 @@ -55,8 +55,8 @@ break data = ''.join(buffer) -3.3.3 创建服务端 ----------------- +3. 创建服务端 +------------- - 创建socket @@ -103,13 +103,13 @@ sock.send('Hello, %s!' % data) sock.close() -3.3.4 socket工作流程 --------------------- +4. socket工作流程 +----------------- |image1| -3.3.5 socket公共函数汇总 ------------------------- +5. socket公共函数汇总 +--------------------- - 发送数据 @@ -176,8 +176,8 @@ sk.setsockopt(level,optname,value) sk.getsockopt(level,optname) -3.3.6 搭建在线聊天机器人 ------------------------- +6. 搭建在线聊天机器人 +--------------------- 通过上面的学习,我们知道,同主机下或不同主机下的两个进程要进行通信(TCP/UDP,不管是消息传输还是文件传输),必定要借助socket这个桥梁。 diff --git a/source/c03/c03_05.md b/source/c03/c03_05.md index cc2bb7d..06afe19 100644 --- a/source/c03/c03_05.md +++ b/source/c03/c03_05.md @@ -31,7 +31,7 @@ 这三个对象都是 `werkzeug` 里提供的,定义的 `local.py` 里,所以它们并不是Flask 中特有的, 这就意味着我们可以直接在自己的项目中使用它们,而不用依托于 Flask 的环境。 -## 3.5.1 Local +## 1. Local 首先是 `Local` ,记得在以前的「并发编程系列」的第五篇线程的 `信息隔离` 的时候,提过了 `threading.local` ,它是专门用来存储当前线程的变量,从而实现对象的线程隔离。 @@ -189,7 +189,7 @@ class Local(object): -## 3.5.2 LocalStack +## 2. LocalStack 通过对 Local 的介绍,可以知道 Local 其实是通过封装了字典的,以此实现了线程隔离。 @@ -267,7 +267,7 @@ request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) ``` -## 3.5.3 LocalProxy +## 3. LocalProxy 通过上面的代码,你可以发现,我们访问LocalStack里的元素的时候,都是通过`LocalProxy` 来进行的有没有? @@ -359,7 +359,7 @@ def _get_current_object(self): 这样就能实现每次对栈顶元素的操作,都是面对最新元素执行的。 -## 3.5.4 经典错误 +## 4. 经典错误 在 Flask 中经常会遇到的一个错误是: diff --git a/source/c03/c03_05.rst b/source/c03/c03_05.rst index 264c1ab..72c8fca 100644 --- a/source/c03/c03_05.rst +++ b/source/c03/c03_05.rst @@ -40,8 +40,8 @@ request。这是它们之间的对应关系。 这就意味着我们可以直接在自己的项目中使用它们,而不用依托于 Flask 的环境。 -3.5.1 Local ------------ +1. Local +-------- 首先是 ``Local`` ,记得在以前的「并发编程系列」的第五篇线程的 ``信息隔离`` 的时候,提过了 ``threading.local`` @@ -206,8 +206,8 @@ local 是需要被 ``localmanager`` 管理的,在请求结束后,会调用 except KeyError: raise AttributeError(name) -3.5.2 LocalStack ----------------- +2. LocalStack +------------- 通过对 Local 的介绍,可以知道 Local 其实是通过封装了字典的,以此实现了线程隔离。 @@ -295,8 +295,8 @@ local 是需要被 ``localmanager`` 管理的,在请求结束后,会调用 request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) -3.5.3 LocalProxy ----------------- +3. LocalProxy +------------- 通过上面的代码,你可以发现,我们访问LocalStack里的元素的时候,都是通过\ ``LocalProxy`` 来进行的有没有? @@ -394,8 +394,8 @@ local 是需要被 ``localmanager`` 管理的,在请求结束后,会调用 这样就能实现每次对栈顶元素的操作,都是面对最新元素执行的。 -3.5.4 经典错误 --------------- +4. 经典错误 +----------- 在 Flask 中经常会遇到的一个错误是: diff --git a/source/c03/c03_06.md b/source/c03/c03_06.md index 93696a6..820bbdf 100644 --- a/source/c03/c03_06.md +++ b/source/c03/c03_06.md @@ -34,7 +34,7 @@ 8. webob.dec.wsgify 装饰器 9. 第二次路由:中间件 routes 路由 -## 3.6.1 WSGI 是什么,因何而生? +## 1. WSGI 是什么,因何而生? WSGI是 Web Server Gateway Interface 的缩写。 @@ -58,7 +58,7 @@ WSGI 对于 application 对象有如下三点要求 -## 3.6.2 为什么要有 WSGI? +## 2. 为什么要有 WSGI? 这是来自我的知乎专栏一个朋友在评论区的一个问题,我觉得问得很好,所以来答一下,更新在这里。 @@ -82,7 +82,7 @@ web 服务器 和 web 框架,分工不同,职责不同(web 服务器专注 最好的情况应该是,由专业的团队去开发专业的web服务器,而开发出来的web服务器需要具备框架通用性,Django可以用,Flask也可以用,开发者也可以自由选择用哪个web 服务器软件,用哪个web 框架,灵活组合。 -## 3.6.3 HTTP请求是如何到应用程序的? +## 3. HTTP请求是如何到应用程序的? 当客户端发出一个 HTTP 请求后,是如何转到我们的应用程序处理并返回的呢? @@ -104,7 +104,7 @@ web 服务器 和 web 框架,分工不同,职责不同(web 服务器专注 nginx可以做负载均衡(前提是有多个服务器),保护了实际的web服务器(客户端是和nginx交互而不是uWSGI) -## 3.6.4 实现一个简单的 WSGI Server +## 4. 实现一个简单的 WSGI Server 在上面的架构图里,不知道你发现没有,有个库叫做 `wsgiref` ,它是 Python 自带的一个 wsgi 服务器模块。 @@ -129,7 +129,7 @@ server.serve_forever() 以上使用 wsgiref 写了一个demo,让你对wsgi有个初步的了解。其由于只适合在学习测试使用,在生产环境中应该另寻他道。 -## 3.6.5 实现“高并发”的 WSGI Server +## 5. 实现“高并发”的 WSGI Server 上面我们说不能在生产中使用 wsgiref ,那在生产中应该使用什么呢?选择有挺多的,比如优秀的 uWSGI,Gunicore等。但是今天我并不准备讲这些,一是因为我不怎么熟悉,二是因为我本人从事 OpenStack 的二次开发,对它比较熟悉。 @@ -198,7 +198,7 @@ self._server = utils.spawn(**wsgi_kwargs) 就这样,nova 开启了一个可以接受1000个绿色协程并发的 WSGI Server。 -## 3.6.6 第一次路由:PasteDeploy +## 6. 第一次路由:PasteDeploy 上面我们提到 WSGI Server 的创建要传入一个 Application,用来处理接收到的请求,对于一个有多个 app 的项目。 @@ -253,7 +253,7 @@ paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory 这是 OpenStack 使用 PasteDeploy 实现的第一层的路由,如果你不感兴趣,可以直接略过本节,进入下一节,下一节是 介绍 PasteDeploy 的使用,教你实现一个简易的web server demo。推荐一定要看。 -## 3.6.7 PasteDeploy 使用说明 +## 7. PasteDeploy 使用说明 到上一步,我已经得到了 application 的有用的线索。考虑到很多人是第一次接触 PasteDeploy,所以这里结合网上博客做了下总结。对你入门会有帮助。 @@ -460,7 +460,7 @@ if __name__ == "__main__": 到此,你学会了使用 PasteDeploy 的简单使用。 -## 3.6.8 webob.dec.wsgify 装饰器 +## 8. webob.dec.wsgify 装饰器 经过了 PasteDeploy 的路由调度,我们找到了 `nova.api.openstack.compute:APIRouterV21.factory` 这个 application 的入口,看代码知道它其实返回了 APIRouterV21 类的一个实例。 @@ -493,7 +493,7 @@ APIRouterV21 本身没有实现 `__call__` ,但它的父类 Router实现了 `_ 带着这个问题,我们了解下 routes 是如何为我们实现第二次路由。 -## 3.6.9 第二次路由:中间件 routes 路由 +## 9. 第二次路由:中间件 routes 路由 在文章最开始处,我们给大家画了一张图。 diff --git a/source/c03/c03_06.rst b/source/c03/c03_06.rst index 14a1b82..1f275a0 100644 --- a/source/c03/c03_06.rst +++ b/source/c03/c03_06.rst @@ -41,8 +41,8 @@ Server,第二阶段是从WSGI Server 到WSGI Application 8. webob.dec.wsgify 装饰器 9. 第二次路由:中间件 routes 路由 -3.6.1 WSGI 是什么,因何而生? ------------------------------ +1. WSGI 是什么,因何而生? +-------------------------- WSGI是 Web Server Gateway Interface 的缩写。 @@ -71,8 +71,8 @@ WSGI 对于 application 对象有如下三点要求 2. 接收两个必选参数environ、start_response。 3. 返回值必须是可迭代对象,用来表示http body。 -3.6.2 为什么要有 WSGI? ------------------------ +2. 为什么要有 WSGI? +-------------------- 这是来自我的知乎专栏一个朋友在评论区的一个问题,我觉得问得很好,所以来答一下,更新在这里。 @@ -99,8 +99,8 @@ web 服务器 和 web 框架,分工不同,职责不同(web 最好的情况应该是,由专业的团队去开发专业的web服务器,而开发出来的web服务器需要具备框架通用性,Django可以用,Flask也可以用,开发者也可以自由选择用哪个web 服务器软件,用哪个web 框架,灵活组合。 -3.6.3 HTTP请求是如何到应用程序的? ----------------------------------- +3. HTTP请求是如何到应用程序的? +------------------------------- 当客户端发出一个 HTTP 请求后,是如何转到我们的应用程序处理并返回的呢? @@ -125,8 +125,8 @@ server性能(uWSGI处理静态资源不如nginx;nginx会在收到一个完整 nginx可以做负载均衡(前提是有多个服务器),保护了实际的web服务器(客户端是和nginx交互而不是uWSGI) -3.6.4 实现一个简单的 WSGI Server --------------------------------- +4. 实现一个简单的 WSGI Server +----------------------------- 在上面的架构图里,不知道你发现没有,有个库叫做 ``wsgiref`` ,它是 Python 自带的一个 wsgi 服务器模块。 @@ -154,8 +154,8 @@ nginx可以做负载均衡(前提是有多个服务器),保护了实际的web 以上使用 wsgiref 写了一个demo,让你对wsgi有个初步的了解。其由于只适合在学习测试使用,在生产环境中应该另寻他道。 -3.6.5 实现“高并发”的 WSGI Server --------------------------------- +5. 实现“高并发”的 WSGI Server +----------------------------- 上面我们说不能在生产中使用 wsgiref ,那在生产中应该使用什么呢?选择有挺多的,比如优秀的 @@ -235,8 +235,8 @@ Server,还是使用的 eventlet。 就这样,nova 开启了一个可以接受1000个绿色协程并发的 WSGI Server。 -3.6.6 第一次路由:PasteDeploy ------------------------------ +6. 第一次路由:PasteDeploy +-------------------------- 上面我们提到 WSGI Server 的创建要传入一个 Application,用来处理接收到的请求,对于一个有多个 app 的项目。 @@ -308,8 +308,8 @@ APIRouterV21 类 的factory方法,这是一个工厂函数,返回 APIRouterV 介绍 PasteDeploy 的使用,教你实现一个简易的web server demo。推荐一定要看。 -3.6.7 PasteDeploy 使用说明 --------------------------- +7. PasteDeploy 使用说明 +----------------------- 到上一步,我已经得到了 application 的有用的线索。考虑到很多人是第一次接触 @@ -524,8 +524,8 @@ applications 是URLMap 对象。 到此,你学会了使用 PasteDeploy 的简单使用。 -3.6.8 webob.dec.wsgify 装饰器 ------------------------------ +8. webob.dec.wsgify 装饰器 +-------------------------- 经过了 PasteDeploy 的路由调度,我们找到了 ``nova.api.openstack.compute:APIRouterV21.factory`` 这个 application @@ -574,8 +574,8 @@ RoutesMiddleware对象)是如何找到真正的 application呢? 带着这个问题,我们了解下 routes 是如何为我们实现第二次路由。 -3.6.9 第二次路由:中间件 routes 路由 ------------------------------------- +9. 第二次路由:中间件 routes 路由 +--------------------------------- 在文章最开始处,我们给大家画了一张图。 diff --git a/source/c03/c03_07.md b/source/c03/c03_07.md index 392be63..2767bdf 100644 --- a/source/c03/c03_07.md +++ b/source/c03/c03_07.md @@ -10,10 +10,6 @@ ![](http://image.iswbm.com/20200617085313.png) -你可以在按照如下方法,后台发送『黑魔法』就可以获取精美排版的 PDF 电子书。 - -![](http://image.iswbm.com/20200617085001.png) - ## 1. 如何在运行状态查看源代码? 查看函数的源代码,我们通常会使用 IDE 来完成。 diff --git a/source/c03/c03_07.rst b/source/c03/c03_07.rst index ffad685..cfffaaf 100644 --- a/source/c03/c03_07.rst +++ b/source/c03/c03_07.rst @@ -12,11 +12,6 @@ |image2| -你可以在按照如下方法,后台发送『黑魔法』就可以获取精美排版的 PDF -电子书。 - -|image3| - 1. 如何在运行状态查看源代码? ----------------------------- @@ -424,7 +419,7 @@ print 的内容输出到日志文件中 示例如下 -|image4| +|image3| 如果\ ``clean()``\ 函数有参数,那么你可以不用装饰器,而是直接调用\ ``atexit.register(clean_1, 参数1, 参数2, 参数3='xxx')``\ 。 @@ -547,6 +542,5 @@ print 的内容输出到日志文件中 .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20200617084208.png .. |image2| image:: http://image.iswbm.com/20200617085313.png -.. |image3| image:: http://image.iswbm.com/20200617085001.png -.. |image4| image:: http://image.iswbm.com/20200510112133.png +.. |image3| image:: http://image.iswbm.com/20200510112133.png diff --git a/source/c04/c04_01.md b/source/c04/c04_01.md index 50b29be..b5434ca 100644 --- a/source/c04/c04_01.md +++ b/source/c04/c04_01.md @@ -5,7 +5,7 @@ --- -## 1.0 什么是虚拟环境? +## 1. 什么是虚拟环境? 虚拟环境的意义,就如同 虚拟机 一样,它可以实现不同环境中Python依赖包相互独立,互不干扰。 @@ -27,7 +27,7 @@ 工具很多,但个人认为最好用的,当属 `virtualenvwrapper`,推荐大家也使用。 -## 2.0 virtualenv +## 2. virtualenv 由于 virtualenvwrapper 是 virtualenv 的一组扩展,所以如果要使用 virtualenvwrapper,就必须先安装 virtualenv。 @@ -94,7 +94,7 @@ $ pip freeze > requirements.txt $ pip install -r requirements.txt ``` -## 3.0 virtualenvwrapper +## 3. virtualenvwrapper virtualenv 虽然已经相当好用了,可是功能还是不够完善。 @@ -195,7 +195,7 @@ $ lssitepackages 更多内容,可查看 官方文档 https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html -## 4.0 实战演示 +## 4. 实战演示 以上内容,是一份使用指南。接下来,一起来看看,如何在项目中使用虚拟环境。 diff --git a/source/c04/c04_01.rst b/source/c04/c04_01.rst index a86b76a..02f0a19 100755 --- a/source/c04/c04_01.rst +++ b/source/c04/c04_01.rst @@ -5,8 +5,8 @@ -------------- -1.0 什么是虚拟环境? --------------------- +1. 什么是虚拟环境? +------------------- 虚拟环境的意义,就如同 虚拟机 一样,它可以实现不同环境中Python依赖包相互独立,互不干扰。 @@ -29,8 +29,8 @@ Python 版本管理工具。 - ``Vex``\ :可以在虚拟环境中执行命令 工具很多,但个人认为最好用的,当属 ``virtualenvwrapper``\ ,推荐大家也使用。 -2.0 virtualenv --------------- +2. virtualenv +------------- 由于 virtualenvwrapper 是 virtualenv 的一组扩展,所以如果要使用 virtualenvwrapper,就必须先安装 virtualenv。 @@ -103,8 +103,8 @@ virtualenvwrapper,就必须先安装 virtualenv。 # 安装依赖包 $ pip install -r requirements.txt -3.0 virtualenvwrapper ---------------------- +3. virtualenvwrapper +-------------------- virtualenv 虽然已经相当好用了,可是功能还是不够完善。 @@ -206,8 +206,8 @@ mkvirtualenv [-a project_path] [-i package] [-r requirements_file] 更多内容,可查看 官方文档 https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html -4.0 实战演示 ------------- +4. 实战演示 +----------- 以上内容,是一份使用指南。接下来,一起来看看,如何在项目中使用虚拟环境。 diff --git a/source/c04/c04_02.md b/source/c04/c04_02.md index 45f4f70..607c775 100644 --- a/source/c04/c04_02.md +++ b/source/c04/c04_02.md @@ -12,7 +12,7 @@ - 可以使用 `pipenv graph`很方便的看出包的依赖关系。 - 通过加载`.env`文件简化开发工作流程 -## 4.14.1 安装pipenv +## 1. 安装pipenv 如果你的电脑上没有安装 pipenv,可以使用如下方法安装 @@ -34,7 +34,7 @@ pip install [--user] pipenv 然后需要重启一下,CMD 终端才能够刷新环境变量。 -## 4.14.2 创建虚拟环境 +## 2. 创建虚拟环境 DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 @@ -63,7 +63,7 @@ pipenv install --python 2 ![](http://image.iswbm.com/20190612213015.png) -## 4.14.3 查询虚拟环境 +## 3. 查询虚拟环境 ```shell # 返回项目的路径 @@ -80,7 +80,7 @@ $ pipenv --py ![](http://image.iswbm.com/20190612213950.png) -## 4.14.4 操作虚拟环境 +## 4. 操作虚拟环境 ```shell # 进入这个虚拟环境 @@ -105,7 +105,7 @@ $ pipenv run python 文件名 # 运行文件 $ pipenv run pip ... # 运行pip ``` -## 4.14.5 虚拟环境包管理 +## 5. 虚拟环境包管理 ```shell # 安装一个本地包(setup.py)到虚拟环境(Pipfile) @@ -133,7 +133,7 @@ $ pipenv lock -r > requirements.txt $ pipenv lock -r --dev # 若只想导出开发用的包 ``` -## 4.14.5 其他命令 +## 6. 其他命令 ```shell diff --git a/source/c04/c04_02.rst b/source/c04/c04_02.rst index 7d897a7..5508abc 100755 --- a/source/c04/c04_02.rst +++ b/source/c04/c04_02.rst @@ -13,8 +13,8 @@ - 可以使用 ``pipenv graph``\ 很方便的看出包的依赖关系。 - 通过加载\ ``.env``\ 文件简化开发工作流程 -4.14.1 安装pipenv ------------------ +1. 安装pipenv +------------- 如果你的电脑上没有安装 pipenv,可以使用如下方法安装 @@ -36,8 +36,8 @@ 然后需要重启一下,CMD 终端才能够刷新环境变量。 -4.14.2 创建虚拟环境 -------------------- +2. 创建虚拟环境 +--------------- DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 @@ -68,8 +68,8 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 |image4| -4.14.3 查询虚拟环境 -------------------- +3. 查询虚拟环境 +--------------- .. code:: shell @@ -86,8 +86,8 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 |image5| -4.14.4 操作虚拟环境 -------------------- +4. 操作虚拟环境 +--------------- .. code:: shell @@ -113,8 +113,8 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 $ pipenv run python 文件名 # 运行文件 $ pipenv run pip ... # 运行pip -4.14.5 虚拟环境包管理 ---------------------- +5. 虚拟环境包管理 +----------------- .. code:: shell @@ -142,8 +142,8 @@ DjangoWebBlog 是我们的项目目录,进入这个目录下创建虚拟环境 $ pipenv lock -r > requirements.txt $ pipenv lock -r --dev # 若只想导出开发用的包 -4.14.5 其他命令 ---------------- +6. 其他命令 +----------- .. code:: shell diff --git a/source/c04/c04_03.md b/source/c04/c04_03.md index 051fac8..58d7ef3 100644 --- a/source/c04/c04_03.md +++ b/source/c04/c04_03.md @@ -23,7 +23,7 @@ - ReadtheDocs:发布网页 -## 4.3.1 成品展示 +## 1. 成品展示 以我的博客(`python.iswbm.com`)为例,先给大家展示一下。 @@ -46,7 +46,7 @@ 只要你认真往下看,30分钟搭建这样一个博客不在话下。 -## 4.3.2 安装Sphinx +## 2. 安装Sphinx 安装之前,请确认下Python版本。我这里使用的是Python 2.7.14,其他版本请自行尝试噢,Python3.6好像有些坑,你需要踩一下。 @@ -103,7 +103,7 @@ F:. - Makefile:编译文件。完全不用管。 - make.bat:bat脚本。你也不用管。 -## 4.3.3 配置及扩展 +## 3. 配置及扩展 Sphinx 的配置文件是 source\conifg.py @@ -129,7 +129,7 @@ Sphinx 的配置文件是 source\conifg.py pip install -r requirements.txt -i https://pypi.douban.com/simple/ ``` -## 4.3.4 撰写文章 +## 4. 撰写文章 万事俱备,接下来要写文档了。 @@ -215,7 +215,7 @@ The HTML pages are in build\html. -## 4.3.5 托管项目 +## 5. 托管项目 看到网页的那一刻是不是相当激动。 @@ -235,7 +235,7 @@ build/ -## 4.3.6 发布上线 +## 6. 发布上线 托管完成后,我们要发布它,让别人也可以使用公网访问。 @@ -273,7 +273,7 @@ pandoc -V mainfont="SimSun" -f markdown -t rst hello.md -o hello.rst -## 4.3.7 自定义插件 +## 7. 自定义插件 之前有不少同学看过我的个人博客(http://python.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 @@ -289,7 +289,7 @@ pandoc -V mainfont="SimSun" -f markdown -t rst hello.md -o hello.rst 方法就是引入两个 JavaSript 插件实现。 -## 第一个插件:导流工具 +### 7.1 第一个插件:导流工具 **作用**:用于将自己博客上流量引导到自己的公众号上。 @@ -395,7 +395,7 @@ html_js_files = [ -## 第二个插件:百度统计 +### 3.2 第二个插件:百度统计 **作用**:用于收集个人网站的访问数据。 @@ -454,7 +454,7 @@ html_js_files = [ 数据真的非常全面,你可以知道,访客都是从哪里访问(直接访问,Google等),每篇文章的点击量(你就知道哪篇是爆款?),每天有多少老访问客,多少新访客等等,更多维度的数据你可以自己去体验一下。 -##第三个插件:评论系统 +###3.3 第三个插件:评论系统 先到这个[网站](http://disqus.com/admin/create)去注册一个 disqus 帐号,我使用了 gmail 帐号进行注册 diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 6d03640..aa88e6e 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -23,8 +23,8 @@ - GitHub:托管项目 - ReadtheDocs:发布网页 -4.3.1 成品展示 --------------- +1. 成品展示 +----------- 以我的博客(\ ``python.iswbm.com``)为例,先给大家展示一下。 @@ -42,8 +42,8 @@ 只要你认真往下看,30分钟搭建这样一个博客不在话下。 -4.3.2 安装Sphinx ----------------- +2. 安装Sphinx +------------- 安装之前,请确认下Python版本。我这里使用的是Python 2.7.14,其他版本请自行尝试噢,Python3.6好像有些坑,你需要踩一下。 @@ -102,8 +102,8 @@ - Makefile:编译文件。完全不用管。 - make.bat:bat脚本。你也不用管。 -4.3.3 配置及扩展 ----------------- +3. 配置及扩展 +------------- Sphinx 的配置文件是 source:raw-latex:`\conifg`.py @@ -131,8 +131,8 @@ exts pip install -r requirements.txt -i https://pypi.douban.com/simple/ -4.3.4 撰写文章 --------------- +4. 撰写文章 +----------- 万事俱备,接下来要写文档了。 @@ -217,8 +217,8 @@ source:raw-latex:`\index`.rst,千万要注意中间的空行不可忽略。 真棒,已经完成了一半了。点击 我们刚写的 暴富指南。 |image6| -4.3.5 托管项目 --------------- +5. 托管项目 +----------- 看到网页的那一刻是不是相当激动。 @@ -236,8 +236,8 @@ source:raw-latex:`\index`.rst,千万要注意中间的空行不可忽略。 接下来,在你的GitHub上新建一个仓库。然后把mkdocs这个目录下的所有文件都提交上去。步骤很简单,这里就不细讲了。 -4.3.6 发布上线 --------------- +6. 发布上线 +----------- 托管完成后,我们要发布它,让别人也可以使用公网访问。 @@ -271,8 +271,8 @@ source:raw-latex:`\index`.rst,千万要注意中间的空行不可忽略。 到这里,属于你的个人博客就搭建好了,快去试一下吧。 最后,整个项目的源码和模块包我都放在公众号(\ ``Python编程时光``\ )后台,请关注后,回复「\ ``Sphinx``\ 」领取。 -4.3.7 自定义插件 ----------------- +7. 自定义插件 +------------- 之前有不少同学看过我的个人博客(http://python.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 @@ -289,8 +289,8 @@ WordPress 真的是没法比,因为这两种产品定位本身就不一样。 方法就是引入两个 JavaSript 插件实现。 -第一个插件:导流工具 --------------------- +7.1 第一个插件:导流工具 +~~~~~~~~~~~~~~~~~~~~~~~~ **作用**\ :用于将自己博客上流量引导到自己的公众号上。 @@ -409,8 +409,8 @@ Python 3.x ,所以这里的代码也要对应修改。 一切按照上面的步骤全部设置完成后,提交Github后,再次从 readthedocs 构建就可以看到效果了。 -第二个插件:百度统计 --------------------- +3.2 第二个插件:百度统计 +~~~~~~~~~~~~~~~~~~~~~~~~ **作用**\ :用于收集个人网站的访问数据。 @@ -469,7 +469,7 @@ Python 3.x ,所以这里的代码也要对应修改。 数据真的非常全面,你可以知道,访客都是从哪里访问(直接访问,Google等),每篇文章的点击量(你就知道哪篇是爆款?),每天有多少老访问客,多少新访客等等,更多维度的数据你可以自己去体验一下。 -##第三个插件:评论系统 +###3.3 第三个插件:评论系统 先到这个\ `网站 `__\ 去注册一个 disqus 帐号,我使用了 gmail 帐号进行注册 diff --git a/source/c04/c04_05.md b/source/c04/c04_05.md index 405113f..0f9ad0e 100644 --- a/source/c04/c04_05.md +++ b/source/c04/c04_05.md @@ -1,132 +1,378 @@ -# 4.5 Win10+Ubuntu 双系统安装教程 +# 4.5 最全的 pip 使用指南 ![](http://image.iswbm.com/20200602135014.png) ---- +所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python 为基础封装出各种有利于开发的第三方工具包。 + +这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 + +Python 从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 + +当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 + +pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python 的标配。 + +当然也有其他的包管理工具 + +- **distutils**:仅用于打包和安装,严格来讲不算是包管理工具 + +- **setuptools**:distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 egg 的文件格式。 + +- **Pipenv**:一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 + +- 还有其他的,这里不一一列出。 + + + +今天的主角是 pip ,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 + + + +## 1. 查询软件包 + +查询当前环境安装的所有软件包 + +```shell +$ pip list +``` + +查询 pypi 上含有某名字的包 + +```shell +$ pip search pkg +``` + +查询当前环境中可升级的包 -在大多数情况下,对于一个程序开发人员,电脑的操作系统的最佳选择不应该是 Windows,而是 Mac 或者 Linux。 +```shell +$ pip list --outdated +``` -Mac 固然很好,但是价钱实在不亲民,让很多人连体验都体验不了,只能停留在想象中,那是属于“别人家的电脑”。如果你想装个黑苹果,那我劝你还是放弃吧,你可以去尝试一下。 +查询一个包的详细内容 -除了 Mac 外,还有开源免费的 Linux 可以选择,可以让你 远离游戏和一切娱乐项目,而专注于开发。 +```shell +$ pip show pkg +``` -Linux 的发行版本有很多,这里选择 Ubuntu 并没有什么特殊的缘由。只是个人感觉在桌面版本 Ubuntu 会比其他发行版做得更加人性,更加成熟。而你如果想使用 CentOS,Debian等,在本篇教程中,换汤不换药,应该也是同样适用的。 +## 2. 下载软件包 -## 4.5.1 准备工作 +在不安装软件包的情况下下载软件包到本地 -由于本教程,是在双硬盘下安装的。所以你要确保你的电脑上有两块硬盘,如果没有,可以去购买一块,这里强烈推荐 SSD,不要再买 HDD 了。因为在我的对比之下,安装速度差的不是一点半点,HDD 下安装可能需要20-30分钟吧(没有去注意,反正挺久),SSD 下安装,啾的一下,很快就好,真的超级快的。 +```shell +$ pip download --destination-directory /local/wheels -r requirements.txt +``` -另外,你还需要两个软件,这里我会打包准备好,你可以 扫描二维码,关注我公众号后,回复「双系统」直接获取。 +下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi 上安装。 -最后,如果你怕误操作,将新系统覆盖到你原来的物理盘上,导致你的数据丢失,你最好进行备份,但是我相信你只要跟着文章一步一步来操作,不会出现这种意外。 +```shell +$ pip install --no-index --find-links=/local/wheels -r requirements.txt +``` -总结如下: +当然你也从你下载的包中,自己构建生成 wheel 文件 -- 双硬盘(SSD) -- U盘(4G+) -- 两个软件(UltraISO + DiskGenius) -- iso文件(Ubuntu 16.04) -- 数据无价,注意备份 +```shell +$ pip install wheel +$ pip wheel --wheel-dir=/local/wheels -r requirements.txt +``` -## 4.5.2 安装硬盘 -若你的电脑已经有两块硬盘(都是HDD也无防,没有强制),那你可以直接跳过,进入第二步。 -由于我此前只有一块硬盘,所以为了这次顺利的安装,我特地网购了一块 SSD。其实在此之前,我有尝试在单硬盘上尝试安装,也已经成功安装了,但是在开机重启后,电脑无法得知该从哪个分区引导系统,导致我的电脑直接无法使用,不论是 Windows 还是新装的 Ubuntu 都进入不到系统,好在我不慌,我家里有好几个 U盘,其中一个 U盘,装的是 PE系统,里面有DiskGenius工具,可以自动修复引导。这才得以重新进入 Windows。 +## 3. 安装软件包 -关于单个硬盘引导的问题,我曾尝试使用 EasyBCD 和 NTBOOTautofix 来创建新的引导。但是都没有成功,为了减少折腾的成本,我才直接选择双硬盘安装的。如果有单硬盘安装经验的朋友,欢迎与我交流,我也想学习一下。 +使用 `pip install ` 可以很方便地从 pypi 上搜索下载并安装 python 包。 -这里上一张我安装硬盘的记念图。 +如下所示 -![](http://image.iswbm.com/20190511163441.png) +```shell +$ pip install requests +``` -你没有看错,我的是台式机。说起这台电脑,还是前年我自己搜罗配置单,自己从各大电商平台,有京东,淘宝,还有天猫买了所有的配件,然后自己一件一件组装起来的。还是挺有感情的,虽然也是渣渣配置,但是这个过程还是很愉快的。在有了每一次装体验后,后面我还给别人装过好几台,如果说高三是我知识储备最高的时期,那前年就是我动手能力最强的时期的。组装过将近十台的电脑。这都是题外话了。 +这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 -第一次装好硬盘后呢,要进入原先的 Windows 系统,检查一下,我们安装的硬盘有没有装成功。由于我装了工具,开始键和你们的可能不一样(不过真的是Win10),你也可以右击桌面 「我的电脑」,再点击「管理」。 -![](http://image.iswbm.com/20190511163457.png) -如果硬盘安装成功,这里会有一块未分配的盘,如图中的 硬盘0。 -第一次使用,需要初始化硬盘,记得选 GPT。 -![](http://image.iswbm.com/20190511163510.png) +**3.1 只从本地安装,而不从 pypi 安装** -这里可以不用急着分区(在后面安装系统时会让你分的),如果你要提前分好(使用DiskGenius),也没有关系。 +```shell +# 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 +$ pip install --no-index --find-links=/local/wheels pkg +``` -## 4.5.3 制作U盘系统 +**3.2 限定版本进行软件包安装** -有安装过系统的人(Windows),正常都知道,我们使用U盘安装一个 PE 系统,然后通过这个 U盘 过渡,将真正的操作系统写入硬盘中。 +以下三种,对单个 python 包的版本进行了约束 -那么如何安装这个 U盘 系统呢,你首先需要先去官网下载一个对应的系统的 iso文件,Ubuntu 的话,你可以去官网下载:https://www.ubuntu.com/download/desktop,我这里下载的是 16.04 的。 +```shell +# 所安装的包的版本为 2.1.2 +$ pip install pkg==2.1.2 -此外,你还需要一个可以 iso文件 写入到U盘中的工具,这里我使用UltraISO ,你可以关注公众号后,在后台回复关键字「双系统」直接获取下载地址。 +# 所安装的包必须大于等于 2.1.2 +$ pip install pkg>=2.1.2 -iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 +# 所安装的包必须小于等于 2.1.2 +$ pip install pkg<=2.1.2 +``` -首先,打开软件,点击 文件 - 打开,选择你所下载的 iso 文件。出现如下界面 +以下命令用于管理/控制整个 python 环境的包版本 -![](http://image.iswbm.com/20190511163520.png) +```shell +# 导出依赖包列表 +pip freeze >requirements.txt -再点击 启动 - 写入硬盘映像 - 写入 +# 从依赖包列表中安装 +pip install -r requirements.txt -![](http://image.iswbm.com/20190511163531.png) +# 确保当前环境软件包的版本(并不确保安装) +pip install -c constraints.txt +``` -如一切顺利,U盘就制作完成,一般 99.99% 都不会在这地方出错。 -## 4.5.4 关闭快速启动 -大家都知道,win10的开机速度有多快,具体原理我就不讲了,有兴趣可以搜索引擎查找。 +**3.3 限制不使用二进制包安装** -但在这里,必须关闭 win10的快速启动功能。方法如下:取消勾选「启用快速启动」,点击保存修改。然后就可以正常关机了。 +由于默认情况下,wheel 包的平台是运行 pip download 命令 的平台,所以可能出现平台不适配的情况。 -![](http://image.iswbm.com/20190511163542.png) +比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl 就不能在 linux_x86_64 安装。 +使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 -## 4.5.5 安装Ubuntu +比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 -在安装之前呢,首先你要根据你的主板,查找选择 启动顺序 的快捷键。由于我的主板是 MSI ,所以我的快捷键是 F11。当然你可以选择,按DEL键进入 Bios,更改顺序。但是我这里不想这么麻烦,因为安装完后又要改回来。 +```shell +# 下载非二进制的包 +$ pip download --no-binary=:all: pkg -查找完后,你可以对电脑开机了,按住你的快捷键,选择启动方式。由于我们要从U盘启动,所以这里选择 +# 安装非二进制的包 +$ pip install pkg --no-binary +``` -这里一定要注意,选择最后一个,自定义选项。 -![](http://image.iswbm.com/20190511163550.png) +**3.4 指定代理服务器安装** +当你身处在一个内网环境中时,无法直接连接公网。这时候你使用`pip install` 安装包,就会失败。 -终于到了分区的这一步了,这是最关键的一步。对于Linux不熟悉的人,到这里可能会懵逼。不用怕,这里给你提供一个最简单的分区配置。具体为什么这么分,我想你并不关心吧? +面对这种情况,可以有两种方法: -- efi:系统分区,我分20G,分太多了。 -- swap:系统交换分区,设置为8G -- /:剩下全部分给根目录,类型选 ext4 +1. 下载离线包拷贝到内网机器中安装 +2. 使用代理服务器转发请求 -对,就是这么简单粗暴。如果你想更加精细一点,你还可以自定义 /home,/usr等。 +第一种方法,虽说可行,但有相当多的弊端 -分区完成后,一定要注意如下这二个红框,选择安装启动引导器的设备为 我们刚刚设置的efi分区。检查无误后,就可以点击「现在安装」。 -![](http://image.iswbm.com/20190511163559.png) +- 步骤繁杂,耗时耗力 +- 无法处理包的依赖问题 -接下来就是 选择时区 - 配置键盘。 -![](http://image.iswbm.com/20190511163612.png) +这里重点来介绍,第二种方法: -![](http://image.iswbm.com/20190511163633.png) +```shell +$ pip install --proxy [user:passwd@]http_server_ip:port pkg +``` +每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:`$HOME/.config/pip/pip.conf` +对于这个路径,说明几点 -到了这里,你应该可以长舒第一口气了。成功了一半了。 -![](http://image.iswbm.com/20190511163700.png) +- 不同的操作系统,路径各不相同 -如果你和我一样使用 SSD ,应该不出5分钟系统就可以安装完毕。弹出如下界面。点击现在重启。 -![](http://image.iswbm.com/20190511163711.png) +```shell +# Linux/Unix: +/etc/pip.conf +~/.pip/pip.conf +~/.config/pip/pip.conf + +# Mac OSX: +~/Library/Application Support/pip/pip.conf +~/.pip/pip.conf +/Library/Application Support/pip/pip.conf + +# Windows: +%APPDATA%\pip\pip.ini +%HOME%\pip\pip.ini +C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) +C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) +``` -重启的过程,记住还是一样按住你的快捷键,我这里仍然是 F11,看到没有,已经有一个叫 ubuntu的启动设备。就它了,选择进入系统。接下来,就是选择要以哪种模式进入ubuntu,你根据需要去选吧。 -![](http://image.iswbm.com/20190511163722.png) +- 若在你的机子上没有此文件,则自行创建即可 -## 4.5.6 效果展示 +如何配置,这边给个样例: + +```ini +[global] +index-url = http://mirrors.aliyun.com/pypi/simple/ + +# 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port +proxy=http://xxx.xxx.xxx.xxx:8080 + +[install] +# 信任阿里云的镜像源,否则会有警告 +trusted-host=mirrors.aliyun.com +``` + +**3.5 安装用户私有软件包** + +很多人可能还不清楚,python 的安装包是可以用户隔离的。 + +如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 + +如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 + +面对这种情况,我们就想能否安装单独为我所用的包呢? + +庆幸的是,还真有。 + +我能想到的有两种方法: + +1. 使用虚拟环境 +2. 将包安装在用户的环境中 + +虚拟环境,之前写过几篇文章,这里不再展开讲。 + +今天的重点是第二种方法,教你如何安装用户私有的包? + +命令也很简单,只要加上 `--user` 参数,pip 就会将其安装在当前用户的 `~/.local/lib/python3.x/site-packages` 下,而其他用户的 python 则不会受影响。 + +```shell +pip install --user pkg +``` + +来举个例子 + +```shell +# 在全局环境中未安装 requests +[root@localhost ~]# pip list | grep requests +[root@localhost ~]# su - wangbm +[root@localhost ~]# + +# 由于用户环境继承自全局环境,这里也未安装 +[wangbm@localhost ~]# pip list | grep requests +[wangbm@localhost ~]# pip install --user requests +[wangbm@localhost ~]# pip list | grep requests +requests (2.22.0) +[wangbm@localhost ~]# + +# 从 Location 属性可发现 requests 只安装在当前用户环境中 +[wangbm@ws_compute01 ~]$ pip show requests +--- +Metadata-Version: 2.1 +Name: requests +Version: 2.22.0 +Summary: Python HTTP for Humans. +Home-page: http://python-requests.org +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +Installer: pip +License: Apache 2.0 +Location: /home/wangbm/.local/lib/python2.7/site-packages +[wangbm@localhost ~]$ exit +logout -由于默认的Ubuntu主题也是丑得可以,经过一个晚上的美化,它变成如下这般帅气逼人。 +# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 +[root@localhost ~]$ pip list | grep requests +[root@localhost ~]$ +``` -![](http://image.iswbm.com/20190511163731.png) +当你身处个人用户环境中,python 导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 -![](http://image.iswbm.com/20190511163750.png) +验证如下: + +```python +>>> import sys +>>> from pprint import pprint +>>> pprint(sys.path) +['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] +>>> + +``` + +**3.6 延长超时时间** + +若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 + +对于这种情况,一般重试几次就好了。 + +但是这样难免有些麻烦,有没有更好的解决方法呢? + +有的,可以通过延长超时时间。 + +```shell +$ pip install --default-timeout=100 +``` + + + +## 4. 卸载软件包 + +就一条命令,不再赘述 + +```shell +$ pip uninstall pkg +``` + + + +## 5. 升级软件包 + +想要对现有的 python 进行升级,其本质上也是先从 pypi 上下载最新版本的包,再对其进行安装。所以升级也是使用 `pip install`,只不过要加一个参数 `--upgrade`。 + +``` +$ pip install --upgrade pkg +``` + +在升级的时候,其实还有一个不怎么用到的选项 `--upgrade-strategy`,它是用来指定升级策略。 + +它的可选项只有两个: + +- `eager` :升级全部依赖包 +- `only-if-need`:只有当旧版本不能适配新的父依赖包时,才会升级。 + +在 pip 10.0 版本之后,这个选项的默认值是 `only-if-need`,因此如下两种写法是一互致的。 + +```shell +pip install --upgrade pkg1 +pip install --upgrade pkg1 --upgrade-strategy only-if-need +``` + +## 6. 配置文件 + +由于在使用 pip 安装一些包时,默认会使用 pip 的官方源,所以经常会报网络超时失败。 + +常用的解决办法是,在安装包时,使用 `-i` 参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 + +这时候,其实可以将这个源写进 pip 的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 + +那怎么配置呢?文件文件在哪? + +使用` win+r` 输入 `%APPDATA%` 进入用户资料文件夹,查看有没有一个 pip 的文件夹,若没有则创建之。 + +然后进入这个 文件夹,新建一个 `pip.ini` 的文件,内容如下 + +```ini +[global] +time-out=60 +index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ +[install] +trusted-host=tsinghua.edu.cn +``` + + + + + +以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 + +![](http://image.iswbm.com/20191105200041.png) -![](http://image.iswbm.com/20190511163757.png) -![](http://image.iswbm.com/20190511163805.png) ----- ![](http://image.iswbm.com/20200607174235.png) + + + diff --git a/source/c04/c04_05.rst b/source/c04/c04_05.rst index 51104c5..7353b73 100755 --- a/source/c04/c04_05.rst +++ b/source/c04/c04_05.rst @@ -1,183 +1,392 @@ -4.5 Win10+Ubuntu 双系统安装教程 -=============================== +4.5 最全的 pip 使用指南 +======================= |image0| --------------- +所有的 Python 开发者都清楚,Python +之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 +Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python +为基础封装出各种有利于开发的第三方工具包。 -在大多数情况下,对于一个程序开发人员,电脑的操作系统的最佳选择不应该是 -Windows,而是 Mac 或者 Linux。 +这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 -Mac -固然很好,但是价钱实在不亲民,让很多人连体验都体验不了,只能停留在想象中,那是属于“别人家的电脑”。如果你想装个黑苹果,那我劝你还是放弃吧,你可以去尝试一下。 +Python +从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 -除了 Mac 外,还有开源免费的 Linux 可以选择,可以让你 -远离游戏和一切娱乐项目,而专注于开发。 +当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 -Linux 的发行版本有很多,这里选择 Ubuntu -并没有什么特殊的缘由。只是个人感觉在桌面版本 Ubuntu -会比其他发行版做得更加人性,更加成熟。而你如果想使用 -CentOS,Debian等,在本篇教程中,换汤不换药,应该也是同样适用的。 +pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python +的标配。 -4.5.1 准备工作 --------------- +当然也有其他的包管理工具 -由于本教程,是在双硬盘下安装的。所以你要确保你的电脑上有两块硬盘,如果没有,可以去购买一块,这里强烈推荐 -SSD,不要再买 HDD 了。因为在我的对比之下,安装速度差的不是一点半点,HDD -下安装可能需要20-30分钟吧(没有去注意,反正挺久),SSD -下安装,啾的一下,很快就好,真的超级快的。 +- **distutils**\ :仅用于打包和安装,严格来讲不算是包管理工具 -另外,你还需要两个软件,这里我会打包准备好,你可以 -扫描二维码,关注我公众号后,回复「双系统」直接获取。 +- **setuptools**\ :distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 + egg 的文件格式。 -最后,如果你怕误操作,将新系统覆盖到你原来的物理盘上,导致你的数据丢失,你最好进行备份,但是我相信你只要跟着文章一步一步来操作,不会出现这种意外。 +- **Pipenv**\ :一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 -总结如下: +- 还有其他的,这里不一一列出。 -- 双硬盘(SSD) -- U盘(4G+) -- 两个软件(UltraISO + DiskGenius) -- iso文件(Ubuntu 16.04) -- 数据无价,注意备份 +今天的主角是 pip +,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 -4.5.2 安装硬盘 --------------- +1. 查询软件包 +------------- -若你的电脑已经有两块硬盘(都是HDD也无防,没有强制),那你可以直接跳过,进入第二步。 +查询当前环境安装的所有软件包 -由于我此前只有一块硬盘,所以为了这次顺利的安装,我特地网购了一块 -SSD。其实在此之前,我有尝试在单硬盘上尝试安装,也已经成功安装了,但是在开机重启后,电脑无法得知该从哪个分区引导系统,导致我的电脑直接无法使用,不论是 -Windows 还是新装的 Ubuntu 都进入不到系统,好在我不慌,我家里有好几个 -U盘,其中一个 U盘,装的是 -PE系统,里面有DiskGenius工具,可以自动修复引导。这才得以重新进入 -Windows。 +.. code:: shell -关于单个硬盘引导的问题,我曾尝试使用 EasyBCD 和 NTBOOTautofix -来创建新的引导。但是都没有成功,为了减少折腾的成本,我才直接选择双硬盘安装的。如果有单硬盘安装经验的朋友,欢迎与我交流,我也想学习一下。 + $ pip list -这里上一张我安装硬盘的记念图。 +查询 pypi 上含有某名字的包 -|image1| +.. code:: shell + + $ pip search pkg + +查询当前环境中可升级的包 + +.. code:: shell + + $ pip list --outdated + +查询一个包的详细内容 + +.. code:: shell + + $ pip show pkg + +2. 下载软件包 +------------- + +在不安装软件包的情况下下载软件包到本地 + +.. code:: shell + + $ pip download --destination-directory /local/wheels -r requirements.txt + +下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi +上安装。 + +.. code:: shell + + $ pip install --no-index --find-links=/local/wheels -r requirements.txt + +当然你也从你下载的包中,自己构建生成 wheel 文件 + +.. code:: shell + + $ pip install wheel + $ pip wheel --wheel-dir=/local/wheels -r requirements.txt + +3. 安装软件包 +------------- + +使用 ``pip install `` 可以很方便地从 pypi 上搜索下载并安装 python +包。 + +如下所示 + +.. code:: shell + + $ pip install requests + +这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 + +**3.1 只从本地安装,而不从 pypi 安装** + +.. code:: shell + + # 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 + $ pip install --no-index --find-links=/local/wheels pkg + +**3.2 限定版本进行软件包安装** + +以下三种,对单个 python 包的版本进行了约束 + +.. code:: shell + + # 所安装的包的版本为 2.1.2 + $ pip install pkg==2.1.2 + + # 所安装的包必须大于等于 2.1.2 + $ pip install pkg>=2.1.2 + + # 所安装的包必须小于等于 2.1.2 + $ pip install pkg<=2.1.2 + +以下命令用于管理/控制整个 python 环境的包版本 + +.. code:: shell + + # 导出依赖包列表 + pip freeze >requirements.txt + + # 从依赖包列表中安装 + pip install -r requirements.txt + + # 确保当前环境软件包的版本(并不确保安装) + pip install -c constraints.txt + +**3.3 限制不使用二进制包安装** + +由于默认情况下,wheel 包的平台是运行 pip download 命令 +的平台,所以可能出现平台不适配的情况。 + +比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl +就不能在 linux_x86_64 安装。 + +使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 + +比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 -你没有看错,我的是台式机。说起这台电脑,还是前年我自己搜罗配置单,自己从各大电商平台,有京东,淘宝,还有天猫买了所有的配件,然后自己一件一件组装起来的。还是挺有感情的,虽然也是渣渣配置,但是这个过程还是很愉快的。在有了每一次装体验后,后面我还给别人装过好几台,如果说高三是我知识储备最高的时期,那前年就是我动手能力最强的时期的。组装过将近十台的电脑。这都是题外话了。 +.. code:: shell -第一次装好硬盘后呢,要进入原先的 Windows -系统,检查一下,我们安装的硬盘有没有装成功。由于我装了工具,开始键和你们的可能不一样(不过真的是Win10),你也可以右击桌面 -「我的电脑」,再点击「管理」。 |image2| -如果硬盘安装成功,这里会有一块未分配的盘,如图中的 硬盘0。 -第一次使用,需要初始化硬盘,记得选 GPT。 |image3| + # 下载非二进制的包 + $ pip download --no-binary=:all: pkg -这里可以不用急着分区(在后面安装系统时会让你分的),如果你要提前分好(使用DiskGenius),也没有关系。 + # 安装非二进制的包 + $ pip install pkg --no-binary -4.5.3 制作U盘系统 ------------------ +**3.4 指定代理服务器安装** -有安装过系统的人(Windows),正常都知道,我们使用U盘安装一个 PE -系统,然后通过这个 U盘 过渡,将真正的操作系统写入硬盘中。 +当你身处在一个内网环境中时,无法直接连接公网。这时候你使用\ ``pip install`` +安装包,就会失败。 -那么如何安装这个 U盘 系统呢,你首先需要先去官网下载一个对应的系统的 -iso文件,Ubuntu -的话,你可以去官网下载:https://www.ubuntu.com/download/desktop,我这里下载的是 -16.04 的。 +面对这种情况,可以有两种方法: -此外,你还需要一个可以 iso文件 写入到U盘中的工具,这里我使用UltraISO -,你可以关注公众号后,在后台回复关键字「双系统」直接获取下载地址。 +1. 下载离线包拷贝到内网机器中安装 +2. 使用代理服务器转发请求 -iso 和 UltraISO 都准备完成后,就可以安装U盘系统了。 +第一种方法,虽说可行,但有相当多的弊端 -首先,打开软件,点击 文件 - 打开,选择你所下载的 iso 文件。出现如下界面 +- 步骤繁杂,耗时耗力 +- 无法处理包的依赖问题 -|image4| +这里重点来介绍,第二种方法: -再点击 启动 - 写入硬盘映像 - 写入 +.. code:: shell -|image5| + $ pip install --proxy [user:passwd@]http_server_ip:port pkg -如一切顺利,U盘就制作完成,一般 99.99% 都不会在这地方出错。 +每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:\ ``$HOME/.config/pip/pip.conf`` -4.5.4 关闭快速启动 ------------------- +对于这个路径,说明几点 -大家都知道,win10的开机速度有多快,具体原理我就不讲了,有兴趣可以搜索引擎查找。 +- 不同的操作系统,路径各不相同 -但在这里,必须关闭 -win10的快速启动功能。方法如下:取消勾选「启用快速启动」,点击保存修改。然后就可以正常关机了。 +.. code:: shell -|image6| + # Linux/Unix: + /etc/pip.conf + ~/.pip/pip.conf + ~/.config/pip/pip.conf + + # Mac OSX: + ~/Library/Application Support/pip/pip.conf + ~/.pip/pip.conf + /Library/Application Support/pip/pip.conf + + # Windows: + %APPDATA%\pip\pip.ini + %HOME%\pip\pip.ini + C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) + C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) -4.5.5 安装Ubuntu ----------------- +- 若在你的机子上没有此文件,则自行创建即可 -在安装之前呢,首先你要根据你的主板,查找选择 启动顺序 -的快捷键。由于我的主板是 MSI ,所以我的快捷键是 -F11。当然你可以选择,按DEL键进入 -Bios,更改顺序。但是我这里不想这么麻烦,因为安装完后又要改回来。 +如何配置,这边给个样例: -查找完后,你可以对电脑开机了,按住你的快捷键,选择启动方式。由于我们要从U盘启动,所以这里选择 +.. code:: ini -这里一定要注意,选择最后一个,自定义选项。 |image7| + [global] + index-url = http://mirrors.aliyun.com/pypi/simple/ -终于到了分区的这一步了,这是最关键的一步。对于Linux不熟悉的人,到这里可能会懵逼。不用怕,这里给你提供一个最简单的分区配置。具体为什么这么分,我想你并不关心吧? + # 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port + proxy=http://xxx.xxx.xxx.xxx:8080 -- efi:系统分区,我分20G,分太多了。 -- swap:系统交换分区,设置为8G -- /:剩下全部分给根目录,类型选 ext4 + [install] + # 信任阿里云的镜像源,否则会有警告 + trusted-host=mirrors.aliyun.com -对,就是这么简单粗暴。如果你想更加精细一点,你还可以自定义 -/home,/usr等。 +**3.5 安装用户私有软件包** -分区完成后,一定要注意如下这二个红框,选择安装启动引导器的设备为 -我们刚刚设置的efi分区。检查无误后,就可以点击「现在安装」。 |image8| +很多人可能还不清楚,python 的安装包是可以用户隔离的。 -接下来就是 选择时区 - 配置键盘。 |image9| +如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 -|image10| +如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 -到了这里,你应该可以长舒第一口气了。成功了一半了。 |image11| +面对这种情况,我们就想能否安装单独为我所用的包呢? -如果你和我一样使用 SSD -,应该不出5分钟系统就可以安装完毕。弹出如下界面。点击现在重启。 -|image12| +庆幸的是,还真有。 -重启的过程,记住还是一样按住你的快捷键,我这里仍然是 -F11,看到没有,已经有一个叫 -ubuntu的启动设备。就它了,选择进入系统。接下来,就是选择要以哪种模式进入ubuntu,你根据需要去选吧。 -|image13| +我能想到的有两种方法: -4.5.6 效果展示 --------------- +1. 使用虚拟环境 +2. 将包安装在用户的环境中 -由于默认的Ubuntu主题也是丑得可以,经过一个晚上的美化,它变成如下这般帅气逼人。 +虚拟环境,之前写过几篇文章,这里不再展开讲。 -|image14| +今天的重点是第二种方法,教你如何安装用户私有的包? -|image15| +命令也很简单,只要加上 ``--user`` 参数,pip 就会将其安装在当前用户的 +``~/.local/lib/python3.x/site-packages`` 下,而其他用户的 python +则不会受影响。 -|image16| +.. code:: shell -|image17| + pip install --user pkg --------------- +来举个例子 + +.. code:: shell + + # 在全局环境中未安装 requests + [root@localhost ~]# pip list | grep requests + [root@localhost ~]# su - wangbm + [root@localhost ~]# + + # 由于用户环境继承自全局环境,这里也未安装 + [wangbm@localhost ~]# pip list | grep requests + [wangbm@localhost ~]# pip install --user requests + [wangbm@localhost ~]# pip list | grep requests + requests (2.22.0) + [wangbm@localhost ~]# + + # 从 Location 属性可发现 requests 只安装在当前用户环境中 + [wangbm@ws_compute01 ~]$ pip show requests + --- + Metadata-Version: 2.1 + Name: requests + Version: 2.22.0 + Summary: Python HTTP for Humans. + Home-page: http://python-requests.org + Author: Kenneth Reitz + Author-email: me@kennethreitz.org + Installer: pip + License: Apache 2.0 + Location: /home/wangbm/.local/lib/python2.7/site-packages + [wangbm@localhost ~]$ exit + logout + + # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 + [root@localhost ~]$ pip list | grep requests + [root@localhost ~]$ + +当你身处个人用户环境中,python +导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 + +验证如下: + +.. code:: python + + >>> import sys + >>> from pprint import pprint + >>> pprint(sys.path) + ['', + '/usr/lib64/python27.zip', + '/usr/lib64/python2.7', + '/usr/lib64/python2.7/plat-linux2', + '/usr/lib64/python2.7/lib-tk', + '/usr/lib64/python2.7/lib-old', + '/usr/lib64/python2.7/lib-dynload', + '/home/wangbm/.local/lib/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages', + '/usr/lib64/python2.7/site-packages/gtk-2.0', + '/usr/lib/python2.7/site-packages', + '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', + '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] + >>> + +**3.6 延长超时时间** + +若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 + +对于这种情况,一般重试几次就好了。 + +但是这样难免有些麻烦,有没有更好的解决方法呢? + +有的,可以通过延长超时时间。 + +.. code:: shell + + $ pip install --default-timeout=100 + +4. 卸载软件包 +------------- + +就一条命令,不再赘述 + +.. code:: shell + + $ pip uninstall pkg + +5. 升级软件包 +------------- + +想要对现有的 python 进行升级,其本质上也是先从 pypi +上下载最新版本的包,再对其进行安装。所以升级也是使用 +``pip install``\ ,只不过要加一个参数 ``--upgrade``\ 。 + +:: + + $ pip install --upgrade pkg + +在升级的时候,其实还有一个不怎么用到的选项 +``--upgrade-strategy``\ ,它是用来指定升级策略。 + +它的可选项只有两个: + +- ``eager`` :升级全部依赖包 +- ``only-if-need``\ :只有当旧版本不能适配新的父依赖包时,才会升级。 + +在 pip 10.0 版本之后,这个选项的默认值是 +``only-if-need``\ ,因此如下两种写法是一互致的。 + +.. code:: shell + + pip install --upgrade pkg1 + pip install --upgrade pkg1 --upgrade-strategy only-if-need + +6. 配置文件 +----------- + +由于在使用 pip 安装一些包时,默认会使用 pip +的官方源,所以经常会报网络超时失败。 + +常用的解决办法是,在安装包时,使用 ``-i`` +参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 + +这时候,其实可以将这个源写进 pip +的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 + +那怎么配置呢?文件文件在哪? + +使用\ ``win+r`` 输入 ``%APPDATA%`` 进入用户资料文件夹,查看有没有一个 +pip 的文件夹,若没有则创建之。 + +然后进入这个 文件夹,新建一个 ``pip.ini`` 的文件,内容如下 + +.. code:: ini + + [global] + time-out=60 + index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ + [install] + trusted-host=tsinghua.edu.cn + +以上几乎包含了 pip +的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 + +|image1| -|image18| +|image2| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20190511163441.png -.. |image2| image:: http://image.iswbm.com/20190511163457.png -.. |image3| image:: http://image.iswbm.com/20190511163510.png -.. |image4| image:: http://image.iswbm.com/20190511163520.png -.. |image5| image:: http://image.iswbm.com/20190511163531.png -.. |image6| image:: http://image.iswbm.com/20190511163542.png -.. |image7| image:: http://image.iswbm.com/20190511163550.png -.. |image8| image:: http://image.iswbm.com/20190511163559.png -.. |image9| image:: http://image.iswbm.com/20190511163612.png -.. |image10| image:: http://image.iswbm.com/20190511163633.png -.. |image11| image:: http://image.iswbm.com/20190511163700.png -.. |image12| image:: http://image.iswbm.com/20190511163711.png -.. |image13| image:: http://image.iswbm.com/20190511163722.png -.. |image14| image:: http://image.iswbm.com/20190511163731.png -.. |image15| image:: http://image.iswbm.com/20190511163750.png -.. |image16| image:: http://image.iswbm.com/20190511163757.png -.. |image17| image:: http://image.iswbm.com/20190511163805.png -.. |image18| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: http://image.iswbm.com/20191105200041.png +.. |image2| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c04/c04_11.md b/source/c04/c04_11.md index 6a476e4..49fb61f 100644 --- a/source/c04/c04_11.md +++ b/source/c04/c04_11.md @@ -1,4 +1,4 @@ -# 4.11 不能不会的远程调试技巧 +# 4.11 超详细图文教你如何使用 PyCharm 进行远程调试 ![](http://image.iswbm.com/20200602135014.png) @@ -19,13 +19,13 @@ 区别就在于,本地调试不需要事前配置,只要你的代码准备好了,随时可以开始 Debug 。而远程调试需要不少前置步骤,这些设置过程,也是本文的主要内容。 -### 4.11.1 新建一个项目 +### 1. 新建一个项目 首先,要在Pycharm中新建一个空的项目,后面我们拉服务器上的项目代码就会放置在这个项目目录下。我这边的名字是 NOVA,你可以自己定义。 ![](http://image.iswbm.com/20190113104817.png) -### 4.11.2 配置连接服务器 +### 2. 配置连接服务器 Tools -> Deployment -> configuration @@ -63,7 +63,7 @@ Tools -> Deployment -> Browse Remote Host ![](http://image.iswbm.com/20190113111042.png) -### 4.11.3 下载项目代码 +### 3. 下载项目代码 如果之前填写的服务器登陆信息准确无误的话,现在就可以看到远程的项目代码。 @@ -81,7 +81,7 @@ Tools -> Deployment -> Browse Remote Host ![](http://image.iswbm.com/20190113111307.png) -### 4.11.4 下载远程解释器 +### 4. 下载远程解释器 为什么需要这步呢? @@ -98,7 +98,7 @@ Tools -> Deployment -> Browse Remote Host 点击`OK`后,会自动下载远程解释器。如果你的项目比较大,这个时间可能会比较久,请耐心等待。 -### 4.11.5 添加程序入口 +### 5. 添加程序入口 因为我们要在本地DEBUG,所以你一定要知道你的项目的入口程序。如果这个入口程序已经包含在你的项目代码中,那么请略过这一步。 @@ -130,7 +130,7 @@ WantedBy=multi-user.target ![](http://image.iswbm.com/20190113112004.png) -### 4.11.6 调试前设置 +### 6. 调试前设置 开启代码自动同步,这样,我们对代码的修改Pycharm都能识别,并且为我们提交到远程服务器。 @@ -140,7 +140,7 @@ WantedBy=multi-user.target ![](http://image.iswbm.com/20190113113211.png) -### 4.11.7 开始调试代码 +### 7. 开始调试代码 在你的程序入口文件处,点击右键,选择Debug即可。 @@ -152,7 +152,7 @@ WantedBy=multi-user.target ![](http://image.iswbm.com/20190113112649.png) -### 4.11.8 友情提醒 +### 8. 友情提醒 按照文章的试调试代码,会自动同步代码至远端,千万不要在生产环境使用,一定要在开发环境中使用,否则后果自负。 diff --git a/source/c04/c04_11.rst b/source/c04/c04_11.rst index fa378e4..a27e659 100644 --- a/source/c04/c04_11.rst +++ b/source/c04/c04_11.rst @@ -1,5 +1,5 @@ -4.11 不能不会的远程调试技巧 -=========================== +4.11 超详细图文教你如何使用 PyCharm 进行远程调试 +================================================ |image0| @@ -25,16 +25,16 @@ Google,这里不涉及。 区别就在于,本地调试不需要事前配置,只要你的代码准备好了,随时可以开始 Debug 。而远程调试需要不少前置步骤,这些设置过程,也是本文的主要内容。 -4.11.1 新建一个项目 -~~~~~~~~~~~~~~~~~~~ +1. 新建一个项目 +~~~~~~~~~~~~~~~ 首先,要在Pycharm中新建一个空的项目,后面我们拉服务器上的项目代码就会放置在这个项目目录下。我这边的名字是 NOVA,你可以自己定义。 |image1| -4.11.2 配置连接服务器 -~~~~~~~~~~~~~~~~~~~~~ +2. 配置连接服务器 +~~~~~~~~~~~~~~~~~ Tools -> Deployment -> configuration @@ -72,8 +72,8 @@ Host |image6| -4.11.3 下载项目代码 -~~~~~~~~~~~~~~~~~~~ +3. 下载项目代码 +~~~~~~~~~~~~~~~ 如果之前填写的服务器登陆信息准确无误的话,现在就可以看到远程的项目代码。 @@ -91,8 +91,8 @@ Host |image10| -4.11.4 下载远程解释器 -~~~~~~~~~~~~~~~~~~~~~ +4. 下载远程解释器 +~~~~~~~~~~~~~~~~~ 为什么需要这步呢? @@ -108,8 +108,8 @@ Host 点击\ ``OK``\ 后,会自动下载远程解释器。如果你的项目比较大,这个时间可能会比较久,请耐心等待。 -4.11.5 添加程序入口 -~~~~~~~~~~~~~~~~~~~ +5. 添加程序入口 +~~~~~~~~~~~~~~~ 因为我们要在本地DEBUG,所以你一定要知道你的项目的入口程序。如果这个入口程序已经包含在你的项目代码中,那么请略过这一步。 @@ -141,8 +141,8 @@ Host |image13| -4.11.6 调试前设置 -~~~~~~~~~~~~~~~~~ +6. 调试前设置 +~~~~~~~~~~~~~ 开启代码自动同步,这样,我们对代码的修改Pycharm都能识别,并且为我们提交到远程服务器。 @@ -153,8 +153,8 @@ Host |image15| -4.11.7 开始调试代码 -~~~~~~~~~~~~~~~~~~~ +7. 开始调试代码 +~~~~~~~~~~~~~~~ 在你的程序入口文件处,点击右键,选择Debug即可。 @@ -166,8 +166,8 @@ Host |image17| -4.11.8 友情提醒 -~~~~~~~~~~~~~~~ +8. 友情提醒 +~~~~~~~~~~~ 按照文章的试调试代码,会自动同步代码至远端,千万不要在生产环境使用,一定要在开发环境中使用,否则后果自负。 diff --git a/source/c04/c04_12.md b/source/c04/c04_12.md index 64d312f..c52c148 100644 --- a/source/c04/c04_12.md +++ b/source/c04/c04_12.md @@ -8,7 +8,7 @@ Pycharm 的图形化界面虽然好用,但是在某些场景中,是无法使用的。而 Python 本身已经给我们提供了一个调试神器 -- pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文章来帮助你轻松的理解它。 -## 4.12.1 准备文件 +## 1. 准备文件 在调试之前先将这两个文件准备好(做为演示用),并放在同级目录中。 @@ -40,7 +40,7 @@ if __name__ == '__main__': -## 4.12.2 进入调试模式 +## 2. 进入调试模式 主要有两种方法 @@ -73,7 +73,7 @@ pdb.set_trace() -## 4.12.3 调试指令 +## 3. 调试指令 熟悉 Pycharm 的人都知道,我们执行下一步,执行到下一个断点是 @@ -133,7 +133,7 @@ pdb.set_trace() -## 4.12.4 开始调试 +## 4. 开始调试 这里就几个最常用的指定,来演示一遍。 diff --git a/source/c04/c04_12.rst b/source/c04/c04_12.rst index 1cd10d2..cfee977 100644 --- a/source/c04/c04_12.rst +++ b/source/c04/c04_12.rst @@ -12,8 +12,8 @@ Pycharm 的图形化界面虽然好用,但是在某些场景中,是无法使 本身已经给我们提供了一个调试神器 – pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文章来帮助你轻松的理解它。 -4.12.1 准备文件 ---------------- +1. 准备文件 +----------- 在调试之前先将这两个文件准备好(做为演示用),并放在同级目录中。 @@ -43,8 +43,8 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 myfunc([1,2,3,4]) print("----end-----") -4.12.2 进入调试模式 -------------------- +2. 进入调试模式 +--------------- 主要有两种方法 @@ -76,8 +76,8 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 |image3| -4.12.3 调试指令 ---------------- +3. 调试指令 +----------- 熟悉 Pycharm 的人都知道,我们执行下一步,执行到下一个断点是 @@ -161,8 +161,8 @@ pdb,可能你还不知道它,为了讲解这个神器,我写了这篇文 |image4| -4.12.4 开始调试 ---------------- +4. 开始调试 +----------- 这里就几个最常用的指定,来演示一遍。 diff --git a/source/c04/c04_16.md b/source/c04/c04_16.md index 22f8307..36dd84f 100644 --- a/source/c04/c04_16.md +++ b/source/c04/c04_16.md @@ -1,76 +1,688 @@ -# 4.16 Python 开发技巧集合 +# 4.16 程序员编码必学:Vim ![](http://image.iswbm.com/20200602135014.png) -## 4.16.1 如何在被调用方法中获取调用者的方法名? +我本人是 Vim 的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim 可以让我对文本的操作更加精准、高效。 -Python:如何在被调用方法中获取调用者的方法名? +对于未使用过 Vim 的朋友来说,可能还无法体会到这种感觉。由于使用 Vim 有一定的学习成本,只有做到非常熟练的程度才能感受到它带来的快捷。 -假设我有2种方法: +这里我就自己日常有使用过的 Vim 指令做一个总结,总共分成 21 点,建议有想学习 Vim 的同学,可以按照文章**配合搜索引擎**多多尝试,相信你会慢慢喜欢上 Vim。 +本文更倾向于有一定基础的同学,因为有很多点,如果写得太详细的话,一定会变得相当啰嗦。 + +## 1. vim模式 + +```shell +正常模式(按Esc或Ctrl+[进入) 左下角显示文件名或为空 +插入模式(按i进入) 左下角显示--INSERT-- +可视模式(按v进入) 左下角显示--VISUAL-- +替换模式(按r或R开始) 左下角显示 --REPLACE-- +命令行模式(按:或者/或者?开始) +ex模式 没用过,有兴趣的同学可以自行了解 +``` +## 2. 打开文件 + + +```shell +# 打开单个文件 +vim file +# 同时打开多个文件 +vim file1 file2.. + +# 在vim窗口中打开一个新文件 +:open [file] + +【举个例子】 +# 当前打开1.txt,做了一些编辑没保存 +:open! 放弃这些修改,并重新打开未修改的文件 + +# 当前打开1.txt,做了一些编辑并保存 +:open 2.txt 直接退出对1.txt的编辑,直接打开2.txt编辑,省了退出:wq再重新vim 2.txt的步骤 + + +# 打开远程文件,比如ftp或者share folder +:e ftp://192.168.10.76/abc.txt +:e \qadrive\test\1.txt + +# 以只读形式打开文件,但是仍然可以使用 :wq! 写入 +vim -R file + +# 强制性关闭修改功能,无法使用 :wq! 写入 +vim -M file +``` + +## 3. 插入命令 + + +```shell +i 在当前位置生前插入 +I 在当前行首插入 + +a 在当前位置后插入 +A 在当前行尾插入 + +o 在当前行之后插入一行 +O 在当前行之前插入一行 +``` + +## 4. 查找命令 + +最简单的查找 + +```shell +/text  查找text,按n健查找下一个,按N健查找前一个。 +?text  查找text,反向查找,按n健查找下一个,按N健查找前一个。 + +vim中有一些特殊字符在查找时需要转义  .*[]^%/?~$ + +:set ignorecase  忽略大小写的查找 +:set noignorecase  不忽略大小写的查找 +``` + +快速查找,不需要手打字符即可查找 + +``` +* 向后(下)寻找游标所在处的单词 +# 向前(上)寻找游标所在处的单词 + + +以上两种查找,n,N 的继续查找命令依然可以适用 +``` + +精准查找:匹配单词查找 + +如果文本中有 `hello`,` helloworld`,` hellopython` + +那我使用 /hello ,这三个词都会匹配到。 + +有没有办法实现精准查找呢?可以使用 + +```shell +/hello\> +``` + +精准查找:匹配行首、行末 + +```shell +# hello位于行首 +/^hello + +# world位于行末 +/world$ +``` + + +## 5. 替换命令 + +```shell +~ 反转游标字母大小写 + +r<字母> 将当前字符替换为所写字母 +R<字母><字母>... 连续替换字母 + +cc 替换整行(就是删除当前行,并在下一行插入) +cw 替换一个单词(就是删除一个单词,就进入插入模式),前提是游标处于单词第一个字母(可用b定位) +C (大写C)替换至行尾(和D有所区别,D是删除(剪切)至行尾,C是删除至行位并进入插入模式) + +# % 就表示所有行,不加就表求当前行 +# 加g表示对每一行的所有匹配到的进行替换,不加就是第一个 +# 如果不加g,而改成c,就会让你再进行确认,选择a进行全部替换,选择y替换,选择n不替换 + +:s/old/new/ 用old替换new,替换当前行的第一个匹配 +:s/old/new/g 用old替换new,替换当前行的所有匹配 + +:%s/old/new/ 用old替换new,替换所有行的第一个匹配 +:%s/old/new/g 用old替换new,替换整个文件的所有匹配 + + +:10,20 s/^/ /g 在第10行至第20行每行前面加四个空格,用于缩进。 + +ddp 交换光标所在行和其下紧邻的一行。 +``` + +## 6. 撤销与重做 + + +```shell +u 撤销(Undo) + +U 撤销对整行的操作 + +Ctrl + r 重做(Redo),即撤销的撤销。 +``` + +## 7. 删除命令 + +需要说明的是,vim 其实并没有单纯的删除命令,下面你或许理解为剪切更加准确。 + +以字符为单位删除 + +```shell +x 删除当前字符 +3x 删除当前字符3次 + +X 删除当前字符的前一个字符。 +3X 删除当前光标向前三个字符 + +dl 删除当前字符, dl=x +dh 删除前一个字符,X=dh + +D 删除当前字符至行尾。D=d$ +d$ 删除当前字符至行尾 +d^ 删除当前字符之前至行首 +``` + +以单词为单位删除 + +```shell +dw 删除当前字符到单词尾 +daw 删除当前字符所在单词 +``` + +以行为单位删除 + +```shell +dd 删除当前行 +dj 删除下一行 +dk 删除上一行 + +dgg 删除当前行至文档首部 +d1G 删除当前行至文档首部 +dG 删除当前行至文档尾部 + +kdgg 删除当前行之前所有行(不包括当前行) +jdG 删除当前行之后所有行(不包括当前行) + + + +10d 删除当前行开始的10行。 +:1,10d 删除1-10行 +:11,$d 删除11行及以后所有的行 +:1,$d 删除所有行 +J   删除两行之间的空行,实际上是合并两行。 +``` + + + +## 8. 复制粘贴 + +普通模式中使用y复制 +``` +yy 复制游标所在的整行(3yy表示复制3行) + +y^ 复制至行首,或y0。不含光标所在处字符。 +y$ 复制至行尾。含光标所在处字符。 + +yw 复制一个单词。 +y2w 复制两个单词。 + +yG 复制至文本末。 +y1G 复制至文本开头。 ``` -def method1(self): - ... - a = A.method2() -def method2(self): - ... +普通模式中使用p粘贴 +``` +p(小写):代表粘贴至光标后(下边,右边) +P(大写):代表粘贴至光标前(上边,左边) ``` -如果我不想对method1做任何更改,如何在method2中获取调用者的名称(在此示例中,名称为method1)? +## 9. 剪切粘贴 + + +```shell +dd 其实就是剪切命令,剪切当前行 +ddp 剪切当前行并粘贴,可实现当前行和下一行调换位置 -`inspect.getframeinfo` -等相关功能检查可以帮助: +正常模式下按v(逐字)或V(逐行)进入可视模式 +然后用jklh命令移动即可选择某些行或字符,再按d即可剪切 +ndd 剪切当前行之后的n行。利用p命令可以对剪切的内容进行粘贴 + +:1,10d 将1-10行剪切。利用p命令可将剪切后的内容进行粘贴。 + +:1, 10 m 20 将第1-10行移动到第20行之后。 ``` ->>> import inspect ->>> def f1(): f2() -... ->>> def f2(): -... curframe = inspect.currentframe() -... calframe = inspect.getouterframes(curframe, 2) -... print 'caller name:', calframe[1][3] -... ->>> f1() -caller name: f1 ->>> + +## 10. 退出保存 + + +```shell +:wq 保存并退出 + +ZZ 保存并退出 + +:q! 强制退出并忽略所有更改 + +:e! 放弃所有修改,并打开原来文件。 +:open! 放弃所有修改,并打开原来文件。 + +:sav(eas) new.txt 另存为一个新文件,退出原文件的编辑且不会保存 +:f(ile) new.txt 新开一个文件,并不保存,退出原文件的编辑且不会保存 ``` -这种内省旨在帮助调试和开发;为了生产功能的目的,不建议依赖它。 +## 11. 移动命令 + +以字符为单位移动 -## 4.16.2 pprint +```shell +h 左移一个字符 +l 右移一个字符 +k 上移一个字符 +j 下移一个字符 -比如说这段 -```python ->>> a -{'version': 1, 'config': [{'subnets': [{u'routes': [{u'netmask': u'0.0.0.0', u'network': u'0.0.0.0', u'gateway': u'172.20.22.200'}], u'netmask': u'255.255.255.0', u'type': 'static', 'ipv4': True, 'address': u'172.20.22.182'}], u'mtu': 1500, u'type': 'physical', 'name': 'eth0', 'mac_address': u'fa:16:3e:29:27:c6'}, {u'type': 'nameserver', u'address': u'114.114.114.114'}, {u'type': 'nameserver', u'address': u'8.8.8.8'}]} +# 【定位字符】f和F +fx 找到光标后第一个为x的字符 +3fd 找到光标后第三个为d的字符 + +F 同f,反向查找。 ``` -可以使用pprint +以行为单位移动 -```python ->>> import pprint ->>> pprint.pprint(a) -{'config': [{'mac_address': u'fa:16:3e:29:27:c6', - u'mtu': 1500, - 'name': 'eth0', - 'subnets': [{'address': u'172.20.22.182', - 'ipv4': True, - u'netmask': u'255.255.255.0', - u'routes': [{u'gateway': u'172.20.22.200', - u'netmask': u'0.0.0.0', - u'network': u'0.0.0.0'}], - u'type': 'static'}], - u'type': 'physical'}, - {u'address': u'114.114.114.114', u'type': 'nameserver'}, - {u'address': u'8.8.8.8', u'type': 'nameserver'}], - 'version': 1} +```shell +# 10指代所有数字,可任意指定 +10h 左移10个字符 +10l 右移10个字符 +10k 上移10行 +10j 下移10行 +$ 移动到行尾 +3$ 移动到下面3行的行尾 ``` +以单词为单位移动 + +```shell +w 向前移动一个单词(光标停在单词首部) +b 向后移动一个单词 +e,同w,只不过是光标停在单词尾部 +ge 同b,光标停在单词尾部。 +``` + +以句为单位移动 + +```shell +( 移动到句首 +) 移动到句尾 +``` + +跳转到文件的首尾 + +```shell +gg 移动到文件头。 = [[ == `` +G 移动到文件尾。 = ]] +``` + +其他移动方法 + +```shell +^ 移动到本行第一个非空白字符上。 +0 移动到本行第一个字符上(可以是空格) +``` + +使用 `具名标记` 跳转,个人感觉这个很好用,因为可以跨文件。 + + +```shell +使用 ma ,可以将此处标记为 a,使用 'a 进行跳转 +使用 :marks 可以查看所有的标记 +使用 :delm!可以删除所有的标记 +``` + +当在查看错误日志时,正常的步骤是,vim打开文件,然后使用 `shift+g` 再跳转到最后一行,这里有个更简单的操作可以在打开文件时立即跳到最后一行。只要在 vim 和 文件 中间加个 `+` 即可。 + +```shell +vim + you.log +``` + +举一反三,当你想打开文件立即跳转到指定行时,可以这样 + +```shell +# 打开文件并跳转到 20 行 +vim you.log +20 +``` + +当你使用 `/` 搜索定位跳转或者使用 `:行号` 进行精准跳转时,有时我们想返回到上一次的位置,如何实现? + +只要使用 Ctrl+o 即可返回上一次的位置。 + +## 12. 排版功能 + +**缩进** + +``` +:set shiftwidth? 查看缩进值 +:set shiftwidth=4 设置缩进值为4 + +# 缩进相关 最好写到配置文件中 ~/.vimrc +:set tabstop=4 +:set softtabstop=4 +:set shiftwidth=4 +:set expandtab + +>> 向右缩进 +<< 取消缩进 +``` + +如何你要对代码进行缩进,还可以用 `==` 对当前行缩进,如果要对多行对待缩进,则使用 n`==`,这种方式要求你所编辑的文件的扩展名是被vim所识别的,比如`.py`文件。 + +**排版** + +``` +:ce 居中 +:le 靠左 +:ri 靠右 +``` +## 13. 注释命令 + +**多行注释** + +``` +进入命令行模式,按ctrl + v进入 visual block模式,然后按j, 或者k选中多行,把需要注释的行标记起来 + +按大写字母I,再插入注释符,例如// + +按esc键就会全部注释了 + +``` +**取消多行注释** + +``` +进入命令行模式,按ctrl + v进入 visual block模式,按字母l横向选中列的个数,例如 // 需要选中2列 + +按字母j,或者k选中注释符号 + +按d键就可全部取消注释 +``` +**复杂注释** + +```shell +:3,5 s/^/#/g 注释第3-5行 +:3,5 s/^#//g 解除3-5行的注释 + + +:1,$ s/^/#/g 注释整个文档 +:1,$ s/^#//g 取消注释整个文档 + + +:%s/^/#/g 注释整个文档,此法更快 +:%s/^#//g 取消注释整个文档 +``` + +## 14. 调整视野 +``` +"zz":命令会把当前行置为屏幕正中央, +"zt":命令会把当前行置于屏幕顶端 +"zb":则把当前行置于屏幕底端. + +Ctrl + e 向下滚动一行 +Ctrl + y 向上滚动一行 + +Ctrl + d 向下滚动半屏 +Ctrl + u 向上滚动半屏 + +Ctrl + f 向下滚动一屏 +Ctrl + b 向上滚动一屏 + + +【跳到指定行】:两种方法 + +可以先把行号打开 +:set nu 打开行号 + +:20 跳到第20行 +20G 跳到第20行 +``` + +## 15. 区域选择 + +``` +要进行区域选择,要先进入可视模式 + +v 以字符为单位,上下左右选择 +V 以行为单位,上下选择 + +选择后可进行操作 +d 剪切/删除 +y 复制 + +Ctrl+v 如果当前是V(大写)模式,就变成v(小写) + 如果当前是v(小写)模式,就变成普通模式。 + 如果当前是普通模式,就进入v(小写)模式 + +利用这个,可以进行多行缩进。 + +ggVG 选择全文 +``` + + +## 16. 窗口控制 + +**新建窗口** + +```shell +# 打开两个文件分属两个窗口 +vim -o 1.txt 2.txt + + +# 假设现在已经打开了1.txt + +:sp 2.txt 开启一个横向的窗口,编辑2.txt +:vsp 2.txt 开启一个竖向的窗口,编辑2.txt + +:split 将当前窗口再复制一个窗口出来,内容同步,游标可以不同 +:split 2.txt 在新窗口打开2.txt的横向窗口 + +# 需要注意:内容同步,但是游标位置是独立的 + +Ctrl-w s 将当前窗口分成水平窗口 +Ctrl-w v 将当前窗口分成竖直窗口 + +Ctrl-w q 等同:q 结束分割出来的视窗。 +Ctrl-w q! 等同:q! 结束分割出来的视窗。 +Ctrl-w o 打开一个视窗并且隐藏之前的所有视窗 +``` +**窗口切换** + +```shell +# 特别说明:Ctrl w <字母> 不需要同时按 + +Ctrl-w h 切换到左边窗口 +Ctrl-w l 切换到右边窗口 + +Ctrl-w j 切换到下边窗口 +Ctrl-w k 切换到上边窗口 + + +# 特别说明:全屏模式下 +:n 切换下一个窗口 +:N 切换上一个窗口 +:bp 切换上一个窗口 + +# 特别说明:非全屏模式 + +:bn 切换下一个窗口,就当前位置的窗口的内容变了,其他窗口不变 +:bN 切换上一个窗口,就当前位置的窗口的内容变了,其他窗口不变 +``` +**窗口移动** + +```shell +# 特别说明:Ctrl w <字母> 不需要同时按 + +Ctrl-w J 将当前视窗移至最下面 +Ctrl-w K 将当前视窗移最上面 + +Ctrl-w H 将当前视窗移至最左边 +Ctrl-w L 将当前视窗移至最右边 + +Ctrl-ww 按顺序切换窗口 +``` +**调整尺寸** + +```shell +# 友情提示:键盘切记不要处于中文状态 + +Ctrl-w + 增加窗口高度 +Ctrl-w - 减少窗口高度 +``` +**退出窗口** + +```shell +:close 关闭当前窗口 +:close! 强制关闭当前窗口 + +:q 退出,不保存 +:q! 强制退出,不保存 + +:x 保存退出 +:wq 保存退出 +:wq! 强制保存退出 + +:w <[路径/]文件名> 另存为 +:savesa <[路径/]文件名> 另存为 + +ZZ 保存并退出。 + +:only 关闭所有窗口,只保留当前窗口(前提:其他窗口内容有改变的话都要先保存) +:only! 关闭所有窗口,只保留当前窗口 + +:qall 放弃所有操作并退出 +:wall 保存所有, +:wqall 保存所有并退出。 +``` + +## 17. 文档加密 + +``` +vim -x file_name + +然后输入密码: +确认密码: + +如果不修改内容也要保存。:wq,不然密码设定不会生效。 +``` + +## 18. 录制宏 + +按q键加任意字母开始录制,再按q键结束录制(这意味着vim中的宏不可嵌套),使用的时候@加宏名,比如qa。。。q录制名为a的宏,@a使用这个宏。 + +## 19. 执行命令 + + +```shell + +# 重复前一次命令 +. + +# 执行shell命令 +:!command + +# 比如列出当前目录下文件 +:!ls + +# 执行脚本 +:!perl -c script.pl 检查perl脚本语法,可以不用退出vim,非常方便。 +:!perl script.pl 执行perl脚本,可以不用退出vim,非常方便。 + +:suspend或Ctrl - Z 挂起vim,回到shell,按fg可以返回vim。 + +# 执行完当前命令后,自动回到vim环境,但不知为何文件内容变成空了 +:silent !command +``` + +## 20. 帮助命令 + + +```shell +在Unix/Linux系统上 +$ vimtutor + +# 普通模式下 +键盘输入vim或F1 + +# 命令行模式下 + +:help 显示整个帮助 +:help xxx 显示xxx的帮助,比如 :help i, :help CTRL-[(即Ctrl+[的帮助)。 +:help 'number' Vim选项的帮助用单引号括起 + + +在Windows系统上 +:help tutor +``` + +## 21. 配置命令 + +显示当前设定 + +```shell +:set或者:se显示所有修改过的配置 +:set all 显示所有的设定值 +:set option? 显示option的设定值 +:set nooption 取消当期设定值 +:ver 显示vim的所有信息(包括版本和参数等) + +# 需要注意:全屏模式下 +:args 查看当前打开的文件列表,当前正在编辑的文件会用[]括起来 +``` + +更改设定 + +```shell +:set nu 显示行号 + +set autoindent(ai) 设置自动缩进 +set autowrite(aw) 设置自动存档,默认未打开 +set backup(bk) 设置自动备份,默认未打开 + +set background=dark或light,设置背景风格 + +set cindent(cin) 设置C语言风格缩进 + +:set ts=4 设置tab键转换为4个空格 + +:set ff=unix # 修改文件dos文件为unix + +:set shiftwidth? 查看缩进值 +:set shiftwidth=4 设置缩进值为4 + +:set ignorecase  忽略大小写的查找 +:set noignorecase  不忽略大小写的查找 + +:set paste # insert模式下,粘贴格式不会乱掉 + +:set ruler?  查看是否设置了ruler,在.vimrc中,使用set命令设制的选项都可以通过这个命令查看 + +:scriptnames  查看vim脚本文件的位置,比如.vimrc文件,语法文件及plugin等。 + +:set list 显示非打印字符,如tab,空格,行尾等。如果tab无法显示,请确定用set lcs=tab:>-命令设置了.vimrc文件,并确保你的文件中的确有tab,如果开启了expendtab,那么tab将被扩展为空格。 + + +:syntax 列出已经定义的语法项 +:syntax clear 清除已定义的语法规则 + +:syntax case match 大小写敏感,int和Int将视为不同的语法元素 +:syntax case ignore 大小写无关,int和Int将视为相同的语法元素,并使用同样的配色方案 + +``` + + + +以上就是我使用 Vim 的一些使用总结,希望对你能有帮助。 + +------ + +最后,送你一张 Vim 的键盘图,你可以将它设置为你的电脑桌面,对你初学 Vim 可能会有帮助。 + +你可以**关注本公众号「Python编程时光」**,在后台回复“**vim**” ,即可获取高清大图。 + +![图1](http://image.iswbm.com/20190804222221.png) + + +![图2](http://image.iswbm.com/20190804222247.png) --- diff --git a/source/c04/c04_16.rst b/source/c04/c04_16.rst index b26a9e1..7c6fba3 100644 --- a/source/c04/c04_16.rst +++ b/source/c04/c04_16.rst @@ -1,76 +1,723 @@ -4.16 Python 开发技巧集合 +4.16 程序员编码必学:Vim ======================== |image0| -4.16.1 如何在被调用方法中获取调用者的方法名? ---------------------------------------------- +我本人是 Vim +的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim +可以让我对文本的操作更加精准、高效。 -Python:如何在被调用方法中获取调用者的方法名? +对于未使用过 Vim 的朋友来说,可能还无法体会到这种感觉。由于使用 Vim +有一定的学习成本,只有做到非常熟练的程度才能感受到它带来的快捷。 -假设我有2种方法: +这里我就自己日常有使用过的 Vim 指令做一个总结,总共分成 21 +点,建议有想学习 Vim +的同学,可以按照文章\ **配合搜索引擎**\ 多多尝试,相信你会慢慢喜欢上 +Vim。 -:: +本文更倾向于有一定基础的同学,因为有很多点,如果写得太详细的话,一定会变得相当啰嗦。 + +1. vim模式 +---------- + +.. code:: shell + + 正常模式(按Esc或Ctrl+[进入) 左下角显示文件名或为空 + 插入模式(按i进入) 左下角显示--INSERT-- + 可视模式(按v进入) 左下角显示--VISUAL-- + 替换模式(按r或R开始) 左下角显示 --REPLACE-- + 命令行模式(按:或者/或者?开始) + ex模式 没用过,有兴趣的同学可以自行了解 + +2. 打开文件 +----------- + +.. code:: shell + + # 打开单个文件 + vim file + # 同时打开多个文件 + vim file1 file2.. + + # 在vim窗口中打开一个新文件 + :open [file] + + 【举个例子】 + # 当前打开1.txt,做了一些编辑没保存 + :open! 放弃这些修改,并重新打开未修改的文件 + + # 当前打开1.txt,做了一些编辑并保存 + :open 2.txt 直接退出对1.txt的编辑,直接打开2.txt编辑,省了退出:wq再重新vim 2.txt的步骤 + + + # 打开远程文件,比如ftp或者share folder + :e ftp://192.168.10.76/abc.txt + :e \qadrive\test\1.txt + + # 以只读形式打开文件,但是仍然可以使用 :wq! 写入 + vim -R file + + # 强制性关闭修改功能,无法使用 :wq! 写入 + vim -M file + +3. 插入命令 +----------- + +.. code:: shell + + i 在当前位置生前插入 + I 在当前行首插入 + + a 在当前位置后插入 + A 在当前行尾插入 - def method1(self): - ... - a = A.method2() + o 在当前行之后插入一行 + O 在当前行之前插入一行 - def method2(self): - ... +4. 查找命令 +----------- -如果我不想对method1做任何更改,如何在method2中获取调用者的名称(在此示例中,名称为method1)? +最简单的查找 -``inspect.getframeinfo`` +.. code:: shell -等相关功能检查可以帮助: + /text  查找text,按n健查找下一个,按N健查找前一个。 + ?text  查找text,反向查找,按n健查找下一个,按N健查找前一个。 + + vim中有一些特殊字符在查找时需要转义  .*[]^%/?~$ + + :set ignorecase  忽略大小写的查找 + :set noignorecase  不忽略大小写的查找 + +快速查找,不需要手打字符即可查找 :: - >>> import inspect - >>> def f1(): f2() - ... - >>> def f2(): - ... curframe = inspect.currentframe() - ... calframe = inspect.getouterframes(curframe, 2) - ... print 'caller name:', calframe[1][3] - ... - >>> f1() - caller name: f1 - >>> - -这种内省旨在帮助调试和开发;为了生产功能的目的,不建议依赖它。 - -4.16.2 pprint + * 向后(下)寻找游标所在处的单词 + # 向前(上)寻找游标所在处的单词 + + + 以上两种查找,n,N 的继续查找命令依然可以适用 + +精准查找:匹配单词查找 + +如果文本中有 ``hello``\ ,\ ``helloworld``\ ,\ ``hellopython`` + +那我使用 /hello ,这三个词都会匹配到。 + +有没有办法实现精准查找呢?可以使用 + +.. code:: shell + + /hello\> + +精准查找:匹配行首、行末 + +.. code:: shell + + # hello位于行首 + /^hello + + # world位于行末 + /world$ + +5. 替换命令 +----------- + +.. code:: shell + + ~ 反转游标字母大小写 + + r<字母> 将当前字符替换为所写字母 + R<字母><字母>... 连续替换字母 + + cc 替换整行(就是删除当前行,并在下一行插入) + cw 替换一个单词(就是删除一个单词,就进入插入模式),前提是游标处于单词第一个字母(可用b定位) + C (大写C)替换至行尾(和D有所区别,D是删除(剪切)至行尾,C是删除至行位并进入插入模式) + + # % 就表示所有行,不加就表求当前行 + # 加g表示对每一行的所有匹配到的进行替换,不加就是第一个 + # 如果不加g,而改成c,就会让你再进行确认,选择a进行全部替换,选择y替换,选择n不替换 + + :s/old/new/ 用old替换new,替换当前行的第一个匹配 + :s/old/new/g 用old替换new,替换当前行的所有匹配 + + :%s/old/new/ 用old替换new,替换所有行的第一个匹配 + :%s/old/new/g 用old替换new,替换整个文件的所有匹配 + + + :10,20 s/^/ /g 在第10行至第20行每行前面加四个空格,用于缩进。 + + ddp 交换光标所在行和其下紧邻的一行。 + +6. 撤销与重做 ------------- -比如说这段 - -.. code:: python - - >>> a - {'version': 1, 'config': [{'subnets': [{u'routes': [{u'netmask': u'0.0.0.0', u'network': u'0.0.0.0', u'gateway': u'172.20.22.200'}], u'netmask': u'255.255.255.0', u'type': 'static', 'ipv4': True, 'address': u'172.20.22.182'}], u'mtu': 1500, u'type': 'physical', 'name': 'eth0', 'mac_address': u'fa:16:3e:29:27:c6'}, {u'type': 'nameserver', u'address': u'114.114.114.114'}, {u'type': 'nameserver', u'address': u'8.8.8.8'}]} - -可以使用pprint - -.. code:: python - - >>> import pprint - >>> pprint.pprint(a) - {'config': [{'mac_address': u'fa:16:3e:29:27:c6', - u'mtu': 1500, - 'name': 'eth0', - 'subnets': [{'address': u'172.20.22.182', - 'ipv4': True, - u'netmask': u'255.255.255.0', - u'routes': [{u'gateway': u'172.20.22.200', - u'netmask': u'0.0.0.0', - u'network': u'0.0.0.0'}], - u'type': 'static'}], - u'type': 'physical'}, - {u'address': u'114.114.114.114', u'type': 'nameserver'}, - {u'address': u'8.8.8.8', u'type': 'nameserver'}], - 'version': 1} +.. code:: shell + + u 撤销(Undo) + + U 撤销对整行的操作 + + Ctrl + r 重做(Redo),即撤销的撤销。 + +7. 删除命令 +----------- + +需要说明的是,vim +其实并没有单纯的删除命令,下面你或许理解为剪切更加准确。 + +以字符为单位删除 + +.. code:: shell + + x 删除当前字符 + 3x 删除当前字符3次 + + X 删除当前字符的前一个字符。 + 3X 删除当前光标向前三个字符 + + dl 删除当前字符, dl=x + dh 删除前一个字符,X=dh + + D 删除当前字符至行尾。D=d$ + d$ 删除当前字符至行尾 + d^ 删除当前字符之前至行首 + +以单词为单位删除 + +.. code:: shell + + dw 删除当前字符到单词尾 + daw 删除当前字符所在单词 + +以行为单位删除 + +.. code:: shell + + dd 删除当前行 + dj 删除下一行 + dk 删除上一行 + + dgg 删除当前行至文档首部 + d1G 删除当前行至文档首部 + dG 删除当前行至文档尾部 + + kdgg 删除当前行之前所有行(不包括当前行) + jdG 删除当前行之后所有行(不包括当前行) + + + + 10d 删除当前行开始的10行。 + :1,10d 删除1-10行 + :11,$d 删除11行及以后所有的行 + :1,$d 删除所有行 + J   删除两行之间的空行,实际上是合并两行。 + +8. 复制粘贴 +----------- + +普通模式中使用y复制 + +:: + + yy 复制游标所在的整行(3yy表示复制3行) + + y^ 复制至行首,或y0。不含光标所在处字符。 + y$ 复制至行尾。含光标所在处字符。 + + yw 复制一个单词。 + y2w 复制两个单词。 + + yG 复制至文本末。 + y1G 复制至文本开头。 + +普通模式中使用p粘贴 + +:: + + p(小写):代表粘贴至光标后(下边,右边) + P(大写):代表粘贴至光标前(上边,左边) + +9. 剪切粘贴 +----------- + +.. code:: shell + + dd 其实就是剪切命令,剪切当前行 + ddp 剪切当前行并粘贴,可实现当前行和下一行调换位置 + + + 正常模式下按v(逐字)或V(逐行)进入可视模式 + 然后用jklh命令移动即可选择某些行或字符,再按d即可剪切 + + ndd 剪切当前行之后的n行。利用p命令可以对剪切的内容进行粘贴 + + :1,10d 将1-10行剪切。利用p命令可将剪切后的内容进行粘贴。 + + :1, 10 m 20 将第1-10行移动到第20行之后。 + +10. 退出保存 +------------ + +.. code:: shell + + :wq 保存并退出 + + ZZ 保存并退出 + + :q! 强制退出并忽略所有更改 + + :e! 放弃所有修改,并打开原来文件。 + :open! 放弃所有修改,并打开原来文件。 + + :sav(eas) new.txt 另存为一个新文件,退出原文件的编辑且不会保存 + :f(ile) new.txt 新开一个文件,并不保存,退出原文件的编辑且不会保存 + +11. 移动命令 +------------ + +以字符为单位移动 + +.. code:: shell + + h 左移一个字符 + l 右移一个字符 + k 上移一个字符 + j 下移一个字符 + + + # 【定位字符】f和F + fx 找到光标后第一个为x的字符 + 3fd 找到光标后第三个为d的字符 + + F 同f,反向查找。 + +以行为单位移动 + +.. code:: shell + + # 10指代所有数字,可任意指定 + 10h 左移10个字符 + 10l 右移10个字符 + 10k 上移10行 + 10j 下移10行 + + $ 移动到行尾 + 3$ 移动到下面3行的行尾 + +以单词为单位移动 + +.. code:: shell + + w 向前移动一个单词(光标停在单词首部) + b 向后移动一个单词 + e,同w,只不过是光标停在单词尾部 + ge 同b,光标停在单词尾部。 + +以句为单位移动 + +.. code:: shell + + ( 移动到句首 + ) 移动到句尾 + +跳转到文件的首尾 + +.. code:: shell + + gg 移动到文件头。 = [[ == `` + G 移动到文件尾。 = ]] + +其他移动方法 + +.. code:: shell + + ^ 移动到本行第一个非空白字符上。 + 0 移动到本行第一个字符上(可以是空格) + +使用 ``具名标记`` 跳转,个人感觉这个很好用,因为可以跨文件。 + +.. code:: shell + + 使用 ma ,可以将此处标记为 a,使用 'a 进行跳转 + 使用 :marks 可以查看所有的标记 + 使用 :delm!可以删除所有的标记 + +当在查看错误日志时,正常的步骤是,vim打开文件,然后使用 ``shift+g`` +再跳转到最后一行,这里有个更简单的操作可以在打开文件时立即跳到最后一行。只要在 +vim 和 文件 中间加个 ``+`` 即可。 + +.. code:: shell + + vim + you.log + +举一反三,当你想打开文件立即跳转到指定行时,可以这样 + +.. code:: shell + + # 打开文件并跳转到 20 行 + vim you.log +20 + +当你使用 ``/`` 搜索定位跳转或者使用 ``:行号`` +进行精准跳转时,有时我们想返回到上一次的位置,如何实现? + +只要使用 Ctrl+o 即可返回上一次的位置。 + +12. 排版功能 +------------ + +**缩进** + +:: + + :set shiftwidth? 查看缩进值 + :set shiftwidth=4 设置缩进值为4 + + # 缩进相关 最好写到配置文件中 ~/.vimrc + :set tabstop=4 + :set softtabstop=4 + :set shiftwidth=4 + :set expandtab + + >> 向右缩进 + << 取消缩进 + +如何你要对代码进行缩进,还可以用 ``==`` +对当前行缩进,如果要对多行对待缩进,则使用 +n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识别的,比如\ ``.py``\ 文件。 + +**排版** + +:: + + :ce 居中 + :le 靠左 + :ri 靠右 + +13. 注释命令 +------------ + +**多行注释** + +:: + + 进入命令行模式,按ctrl + v进入 visual block模式,然后按j, 或者k选中多行,把需要注释的行标记起来 + + 按大写字母I,再插入注释符,例如// + + 按esc键就会全部注释了 + +**取消多行注释** + +:: + + 进入命令行模式,按ctrl + v进入 visual block模式,按字母l横向选中列的个数,例如 // 需要选中2列 + + 按字母j,或者k选中注释符号 + + 按d键就可全部取消注释 + +**复杂注释** + +.. code:: shell + + :3,5 s/^/#/g 注释第3-5行 + :3,5 s/^#//g 解除3-5行的注释 + + + :1,$ s/^/#/g 注释整个文档 + :1,$ s/^#//g 取消注释整个文档 + + + :%s/^/#/g 注释整个文档,此法更快 + :%s/^#//g 取消注释整个文档 + +14. 调整视野 +------------ + +:: + + "zz":命令会把当前行置为屏幕正中央, + "zt":命令会把当前行置于屏幕顶端 + "zb":则把当前行置于屏幕底端. + + Ctrl + e 向下滚动一行 + Ctrl + y 向上滚动一行 + + Ctrl + d 向下滚动半屏 + Ctrl + u 向上滚动半屏 + + Ctrl + f 向下滚动一屏 + Ctrl + b 向上滚动一屏 + + + 【跳到指定行】:两种方法 + + 可以先把行号打开 + :set nu 打开行号 + + :20 跳到第20行 + 20G 跳到第20行 + +15. 区域选择 +------------ + +:: + + 要进行区域选择,要先进入可视模式 + + v 以字符为单位,上下左右选择 + V 以行为单位,上下选择 + + 选择后可进行操作 + d 剪切/删除 + y 复制 + + Ctrl+v 如果当前是V(大写)模式,就变成v(小写) + 如果当前是v(小写)模式,就变成普通模式。 + 如果当前是普通模式,就进入v(小写)模式 + + 利用这个,可以进行多行缩进。 + + ggVG 选择全文 + +16. 窗口控制 +------------ + +**新建窗口** + +.. code:: shell + + # 打开两个文件分属两个窗口 + vim -o 1.txt 2.txt + + + # 假设现在已经打开了1.txt + + :sp 2.txt 开启一个横向的窗口,编辑2.txt + :vsp 2.txt 开启一个竖向的窗口,编辑2.txt + + :split 将当前窗口再复制一个窗口出来,内容同步,游标可以不同 + :split 2.txt 在新窗口打开2.txt的横向窗口 + + # 需要注意:内容同步,但是游标位置是独立的 + + Ctrl-w s 将当前窗口分成水平窗口 + Ctrl-w v 将当前窗口分成竖直窗口 + + Ctrl-w q 等同:q 结束分割出来的视窗。 + Ctrl-w q! 等同:q! 结束分割出来的视窗。 + Ctrl-w o 打开一个视窗并且隐藏之前的所有视窗 + +**窗口切换** + +.. code:: shell + + # 特别说明:Ctrl w <字母> 不需要同时按 + + Ctrl-w h 切换到左边窗口 + Ctrl-w l 切换到右边窗口 + + Ctrl-w j 切换到下边窗口 + Ctrl-w k 切换到上边窗口 + + + # 特别说明:全屏模式下 + :n 切换下一个窗口 + :N 切换上一个窗口 + :bp 切换上一个窗口 + + # 特别说明:非全屏模式 + + :bn 切换下一个窗口,就当前位置的窗口的内容变了,其他窗口不变 + :bN 切换上一个窗口,就当前位置的窗口的内容变了,其他窗口不变 + +**窗口移动** + +.. code:: shell + + # 特别说明:Ctrl w <字母> 不需要同时按 + + Ctrl-w J 将当前视窗移至最下面 + Ctrl-w K 将当前视窗移最上面 + + Ctrl-w H 将当前视窗移至最左边 + Ctrl-w L 将当前视窗移至最右边 + + Ctrl-ww 按顺序切换窗口 + +**调整尺寸** + +.. code:: shell + + # 友情提示:键盘切记不要处于中文状态 + + Ctrl-w + 增加窗口高度 + Ctrl-w - 减少窗口高度 + +**退出窗口** + +.. code:: shell + + :close 关闭当前窗口 + :close! 强制关闭当前窗口 + + :q 退出,不保存 + :q! 强制退出,不保存 + + :x 保存退出 + :wq 保存退出 + :wq! 强制保存退出 + + :w <[路径/]文件名> 另存为 + :savesa <[路径/]文件名> 另存为 + + ZZ 保存并退出。 + + :only 关闭所有窗口,只保留当前窗口(前提:其他窗口内容有改变的话都要先保存) + :only! 关闭所有窗口,只保留当前窗口 + + :qall 放弃所有操作并退出 + :wall 保存所有, + :wqall 保存所有并退出。 + +17. 文档加密 +------------ + +:: + + vim -x file_name + + 然后输入密码: + 确认密码: + + 如果不修改内容也要保存。:wq,不然密码设定不会生效。 + +18. 录制宏 +---------- + +按q键加任意字母开始录制,再按q键结束录制(这意味着vim中的宏不可嵌套),使用的时候@加宏名,比如qa。。。q录制名为a的宏,@a使用这个宏。 + +19. 执行命令 +------------ + +.. code:: shell + + + # 重复前一次命令 + . + + # 执行shell命令 + :!command + + # 比如列出当前目录下文件 + :!ls + + # 执行脚本 + :!perl -c script.pl 检查perl脚本语法,可以不用退出vim,非常方便。 + :!perl script.pl 执行perl脚本,可以不用退出vim,非常方便。 + + :suspend或Ctrl - Z 挂起vim,回到shell,按fg可以返回vim。 + + # 执行完当前命令后,自动回到vim环境,但不知为何文件内容变成空了 + :silent !command + +20. 帮助命令 +------------ + +.. code:: shell + + 在Unix/Linux系统上 + $ vimtutor + + # 普通模式下 + 键盘输入vim或F1 + + # 命令行模式下 + + :help 显示整个帮助 + :help xxx 显示xxx的帮助,比如 :help i, :help CTRL-[(即Ctrl+[的帮助)。 + :help 'number' Vim选项的帮助用单引号括起 + + + 在Windows系统上 + :help tutor + +21. 配置命令 +------------ + +显示当前设定 + +.. code:: shell + + :set或者:se显示所有修改过的配置 + :set all 显示所有的设定值 + :set option? 显示option的设定值 + :set nooption 取消当期设定值 + :ver 显示vim的所有信息(包括版本和参数等) + + # 需要注意:全屏模式下 + :args 查看当前打开的文件列表,当前正在编辑的文件会用[]括起来 + +更改设定 + +.. code:: shell + + :set nu 显示行号 + + set autoindent(ai) 设置自动缩进 + set autowrite(aw) 设置自动存档,默认未打开 + set backup(bk) 设置自动备份,默认未打开 + + set background=dark或light,设置背景风格 + + set cindent(cin) 设置C语言风格缩进 + + :set ts=4 设置tab键转换为4个空格 + + :set ff=unix # 修改文件dos文件为unix + + :set shiftwidth? 查看缩进值 + :set shiftwidth=4 设置缩进值为4 + + :set ignorecase  忽略大小写的查找 + :set noignorecase  不忽略大小写的查找 + + :set paste # insert模式下,粘贴格式不会乱掉 + + :set ruler?  查看是否设置了ruler,在.vimrc中,使用set命令设制的选项都可以通过这个命令查看 + + :scriptnames  查看vim脚本文件的位置,比如.vimrc文件,语法文件及plugin等。 + + :set list 显示非打印字符,如tab,空格,行尾等。如果tab无法显示,请确定用set lcs=tab:>-命令设置了.vimrc文件,并确保你的文件中的确有tab,如果开启了expendtab,那么tab将被扩展为空格。 + + + :syntax 列出已经定义的语法项 + :syntax clear 清除已定义的语法规则 + + :syntax case match 大小写敏感,int和Int将视为不同的语法元素 + :syntax case ignore 大小写无关,int和Int将视为相同的语法元素,并使用同样的配色方案 + +以上就是我使用 Vim 的一些使用总结,希望对你能有帮助。 + +-------------- + +最后,送你一张 Vim 的键盘图,你可以将它设置为你的电脑桌面,对你初学 Vim +可能会有帮助。 + +你可以\ **关注本公众号「Python编程时光」**\ ,在后台回复“**vim**” +,即可获取高清大图。 + +.. figure:: http://image.iswbm.com/20190804222221.png + :alt: 图1 + + 图1 + +.. figure:: http://image.iswbm.com/20190804222247.png + :alt: 图2 + + 图2 -------------- diff --git a/source/c04/c04_19.md b/source/c04/c04_19.md deleted file mode 100644 index 64423b9..0000000 --- a/source/c04/c04_19.md +++ /dev/null @@ -1,689 +0,0 @@ -# 4.19 程序员编码必学:Vim - -![](http://image.iswbm.com/20200602135014.png) - -我本人是 Vim 的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim 可以让我对文本的操作更加精准、高效。 - -对于未使用过 Vim 的朋友来说,可能还无法体会到这种感觉。由于使用 Vim 有一定的学习成本,只有做到非常熟练的程度才能感受到它带来的快捷。 - -这里我就自己日常有使用过的 Vim 指令做一个总结,总共分成 21 点,建议有想学习 Vim 的同学,可以按照文章**配合搜索引擎**多多尝试,相信你会慢慢喜欢上 Vim。 - -本文更倾向于有一定基础的同学,因为有很多点,如果写得太详细的话,一定会变得相当啰嗦。 - -## 1. vim模式 - -```shell -正常模式(按Esc或Ctrl+[进入) 左下角显示文件名或为空 -插入模式(按i进入) 左下角显示--INSERT-- -可视模式(按v进入) 左下角显示--VISUAL-- -替换模式(按r或R开始) 左下角显示 --REPLACE-- -命令行模式(按:或者/或者?开始) -ex模式 没用过,有兴趣的同学可以自行了解 -``` -## 2. 打开文件 - - -```shell -# 打开单个文件 -vim file -# 同时打开多个文件 -vim file1 file2.. - -# 在vim窗口中打开一个新文件 -:open [file] - -【举个例子】 -# 当前打开1.txt,做了一些编辑没保存 -:open! 放弃这些修改,并重新打开未修改的文件 - -# 当前打开1.txt,做了一些编辑并保存 -:open 2.txt 直接退出对1.txt的编辑,直接打开2.txt编辑,省了退出:wq再重新vim 2.txt的步骤 - - -# 打开远程文件,比如ftp或者share folder -:e ftp://192.168.10.76/abc.txt -:e \qadrive\test\1.txt - -# 以只读形式打开文件,但是仍然可以使用 :wq! 写入 -vim -R file - -# 强制性关闭修改功能,无法使用 :wq! 写入 -vim -M file -``` - -## 3. 插入命令 - - -```shell -i 在当前位置生前插入 -I 在当前行首插入 - -a 在当前位置后插入 -A 在当前行尾插入 - -o 在当前行之后插入一行 -O 在当前行之前插入一行 -``` - -## 4. 查找命令 - -最简单的查找 - -```shell -/text  查找text,按n健查找下一个,按N健查找前一个。 -?text  查找text,反向查找,按n健查找下一个,按N健查找前一个。 - -vim中有一些特殊字符在查找时需要转义  .*[]^%/?~$ - -:set ignorecase  忽略大小写的查找 -:set noignorecase  不忽略大小写的查找 -``` - -快速查找,不需要手打字符即可查找 - -``` -* 向后(下)寻找游标所在处的单词 -# 向前(上)寻找游标所在处的单词 - - -以上两种查找,n,N 的继续查找命令依然可以适用 -``` - -精准查找:匹配单词查找 - -如果文本中有 `hello`,` helloworld`,` hellopython` - -那我使用 /hello ,这三个词都会匹配到。 - -有没有办法实现精准查找呢?可以使用 - -```shell -/hello\> -``` - -精准查找:匹配行首、行末 - -```shell -# hello位于行首 -/^hello - -# world位于行末 -/world$ -``` - - -## 5. 替换命令 - -```shell -~ 反转游标字母大小写 - -r<字母> 将当前字符替换为所写字母 -R<字母><字母>... 连续替换字母 - -cc 替换整行(就是删除当前行,并在下一行插入) -cw 替换一个单词(就是删除一个单词,就进入插入模式),前提是游标处于单词第一个字母(可用b定位) -C (大写C)替换至行尾(和D有所区别,D是删除(剪切)至行尾,C是删除至行位并进入插入模式) - -# % 就表示所有行,不加就表求当前行 -# 加g表示对每一行的所有匹配到的进行替换,不加就是第一个 -# 如果不加g,而改成c,就会让你再进行确认,选择a进行全部替换,选择y替换,选择n不替换 - -:s/old/new/ 用old替换new,替换当前行的第一个匹配 -:s/old/new/g 用old替换new,替换当前行的所有匹配 - -:%s/old/new/ 用old替换new,替换所有行的第一个匹配 -:%s/old/new/g 用old替换new,替换整个文件的所有匹配 - - -:10,20 s/^/ /g 在第10行至第20行每行前面加四个空格,用于缩进。 - -ddp 交换光标所在行和其下紧邻的一行。 -``` - -## 6. 撤销与重做 - - -```shell -u 撤销(Undo) - -U 撤销对整行的操作 - -Ctrl + r 重做(Redo),即撤销的撤销。 -``` - -## 7. 删除命令 - -需要说明的是,vim 其实并没有单纯的删除命令,下面你或许理解为剪切更加准确。 - -以字符为单位删除 - -```shell -x 删除当前字符 -3x 删除当前字符3次 - -X 删除当前字符的前一个字符。 -3X 删除当前光标向前三个字符 - -dl 删除当前字符, dl=x -dh 删除前一个字符,X=dh - -D 删除当前字符至行尾。D=d$ -d$ 删除当前字符至行尾 -d^ 删除当前字符之前至行首 -``` - -以单词为单位删除 - -```shell -dw 删除当前字符到单词尾 -daw 删除当前字符所在单词 -``` - -以行为单位删除 - -```shell -dd 删除当前行 -dj 删除下一行 -dk 删除上一行 - -dgg 删除当前行至文档首部 -d1G 删除当前行至文档首部 -dG 删除当前行至文档尾部 - -kdgg 删除当前行之前所有行(不包括当前行) -jdG 删除当前行之后所有行(不包括当前行) - - - -10d 删除当前行开始的10行。 -:1,10d 删除1-10行 -:11,$d 删除11行及以后所有的行 -:1,$d 删除所有行 -J   删除两行之间的空行,实际上是合并两行。 -``` - - - -## 8. 复制粘贴 - -普通模式中使用y复制 -``` -yy 复制游标所在的整行(3yy表示复制3行) - -y^ 复制至行首,或y0。不含光标所在处字符。 -y$ 复制至行尾。含光标所在处字符。 - -yw 复制一个单词。 -y2w 复制两个单词。 - -yG 复制至文本末。 -y1G 复制至文本开头。 -``` - -普通模式中使用p粘贴 -``` -p(小写):代表粘贴至光标后(下边,右边) -P(大写):代表粘贴至光标前(上边,左边) -``` - -## 9. 剪切粘贴 - - -```shell -dd 其实就是剪切命令,剪切当前行 -ddp 剪切当前行并粘贴,可实现当前行和下一行调换位置 - - -正常模式下按v(逐字)或V(逐行)进入可视模式 -然后用jklh命令移动即可选择某些行或字符,再按d即可剪切 - -ndd 剪切当前行之后的n行。利用p命令可以对剪切的内容进行粘贴 - -:1,10d 将1-10行剪切。利用p命令可将剪切后的内容进行粘贴。 - -:1, 10 m 20 将第1-10行移动到第20行之后。 -``` - -## 10. 退出保存 - - -```shell -:wq 保存并退出 - -ZZ 保存并退出 - -:q! 强制退出并忽略所有更改 - -:e! 放弃所有修改,并打开原来文件。 -:open! 放弃所有修改,并打开原来文件。 - -:sav(eas) new.txt 另存为一个新文件,退出原文件的编辑且不会保存 -:f(ile) new.txt 新开一个文件,并不保存,退出原文件的编辑且不会保存 -``` - -## 11. 移动命令 - -以字符为单位移动 - -```shell -h 左移一个字符 -l 右移一个字符 -k 上移一个字符 -j 下移一个字符 - - -# 【定位字符】f和F -fx 找到光标后第一个为x的字符 -3fd 找到光标后第三个为d的字符 - -F 同f,反向查找。 -``` - -以行为单位移动 - -```shell -# 10指代所有数字,可任意指定 -10h 左移10个字符 -10l 右移10个字符 -10k 上移10行 -10j 下移10行 - -$ 移动到行尾 -3$ 移动到下面3行的行尾 -``` - -以单词为单位移动 - -```shell -w 向前移动一个单词(光标停在单词首部) -b 向后移动一个单词 -e,同w,只不过是光标停在单词尾部 -ge 同b,光标停在单词尾部。 -``` - -以句为单位移动 - -```shell -( 移动到句首 -) 移动到句尾 -``` - -跳转到文件的首尾 - -```shell -gg 移动到文件头。 = [[ == `` -G 移动到文件尾。 = ]] -``` - -其他移动方法 - -```shell -^ 移动到本行第一个非空白字符上。 -0 移动到本行第一个字符上(可以是空格) -``` - -使用 `具名标记` 跳转,个人感觉这个很好用,因为可以跨文件。 - - -```shell -使用 ma ,可以将此处标记为 a,使用 'a 进行跳转 -使用 :marks 可以查看所有的标记 -使用 :delm!可以删除所有的标记 -``` - -当在查看错误日志时,正常的步骤是,vim打开文件,然后使用 `shift+g` 再跳转到最后一行,这里有个更简单的操作可以在打开文件时立即跳到最后一行。只要在 vim 和 文件 中间加个 `+` 即可。 - -```shell -vim + you.log -``` - -举一反三,当你想打开文件立即跳转到指定行时,可以这样 - -```shell -# 打开文件并跳转到 20 行 -vim you.log +20 -``` - -当你使用 `/` 搜索定位跳转或者使用 `:行号` 进行精准跳转时,有时我们想返回到上一次的位置,如何实现? - -只要使用 Ctrl+o 即可返回上一次的位置。 - -## 12. 排版功能 - -**缩进** - -``` -:set shiftwidth? 查看缩进值 -:set shiftwidth=4 设置缩进值为4 - -# 缩进相关 最好写到配置文件中 ~/.vimrc -:set tabstop=4 -:set softtabstop=4 -:set shiftwidth=4 -:set expandtab - ->> 向右缩进 -<< 取消缩进 -``` - -如何你要对代码进行缩进,还可以用 `==` 对当前行缩进,如果要对多行对待缩进,则使用 n`==`,这种方式要求你所编辑的文件的扩展名是被vim所识别的,比如`.py`文件。 - -**排版** - -``` -:ce 居中 -:le 靠左 -:ri 靠右 -``` -## 13. 注释命令 - -**多行注释** - -``` -进入命令行模式,按ctrl + v进入 visual block模式,然后按j, 或者k选中多行,把需要注释的行标记起来 - -按大写字母I,再插入注释符,例如// - -按esc键就会全部注释了 - -``` -**取消多行注释** - -``` -进入命令行模式,按ctrl + v进入 visual block模式,按字母l横向选中列的个数,例如 // 需要选中2列 - -按字母j,或者k选中注释符号 - -按d键就可全部取消注释 -``` -**复杂注释** - -```shell -:3,5 s/^/#/g 注释第3-5行 -:3,5 s/^#//g 解除3-5行的注释 - - -:1,$ s/^/#/g 注释整个文档 -:1,$ s/^#//g 取消注释整个文档 - - -:%s/^/#/g 注释整个文档,此法更快 -:%s/^#//g 取消注释整个文档 -``` - -## 14. 调整视野 -``` -"zz":命令会把当前行置为屏幕正中央, -"zt":命令会把当前行置于屏幕顶端 -"zb":则把当前行置于屏幕底端. - -Ctrl + e 向下滚动一行 -Ctrl + y 向上滚动一行 - -Ctrl + d 向下滚动半屏 -Ctrl + u 向上滚动半屏 - -Ctrl + f 向下滚动一屏 -Ctrl + b 向上滚动一屏 - - -【跳到指定行】:两种方法 - -可以先把行号打开 -:set nu 打开行号 - -:20 跳到第20行 -20G 跳到第20行 -``` - -## 15. 区域选择 - -``` -要进行区域选择,要先进入可视模式 - -v 以字符为单位,上下左右选择 -V 以行为单位,上下选择 - -选择后可进行操作 -d 剪切/删除 -y 复制 - -Ctrl+v 如果当前是V(大写)模式,就变成v(小写) - 如果当前是v(小写)模式,就变成普通模式。 - 如果当前是普通模式,就进入v(小写)模式 - -利用这个,可以进行多行缩进。 - -ggVG 选择全文 -``` - - -## 16. 窗口控制 - -**新建窗口** - -```shell -# 打开两个文件分属两个窗口 -vim -o 1.txt 2.txt - - -# 假设现在已经打开了1.txt - -:sp 2.txt 开启一个横向的窗口,编辑2.txt -:vsp 2.txt 开启一个竖向的窗口,编辑2.txt - -:split 将当前窗口再复制一个窗口出来,内容同步,游标可以不同 -:split 2.txt 在新窗口打开2.txt的横向窗口 - -# 需要注意:内容同步,但是游标位置是独立的 - -Ctrl-w s 将当前窗口分成水平窗口 -Ctrl-w v 将当前窗口分成竖直窗口 - -Ctrl-w q 等同:q 结束分割出来的视窗。 -Ctrl-w q! 等同:q! 结束分割出来的视窗。 -Ctrl-w o 打开一个视窗并且隐藏之前的所有视窗 -``` -**窗口切换** - -```shell -# 特别说明:Ctrl w <字母> 不需要同时按 - -Ctrl-w h 切换到左边窗口 -Ctrl-w l 切换到右边窗口 - -Ctrl-w j 切换到下边窗口 -Ctrl-w k 切换到上边窗口 - - -# 特别说明:全屏模式下 -:n 切换下一个窗口 -:N 切换上一个窗口 -:bp 切换上一个窗口 - -# 特别说明:非全屏模式 - -:bn 切换下一个窗口,就当前位置的窗口的内容变了,其他窗口不变 -:bN 切换上一个窗口,就当前位置的窗口的内容变了,其他窗口不变 -``` -**窗口移动** - -```shell -# 特别说明:Ctrl w <字母> 不需要同时按 - -Ctrl-w J 将当前视窗移至最下面 -Ctrl-w K 将当前视窗移最上面 - -Ctrl-w H 将当前视窗移至最左边 -Ctrl-w L 将当前视窗移至最右边 - -Ctrl-ww 按顺序切换窗口 -``` -**调整尺寸** - -```shell -# 友情提示:键盘切记不要处于中文状态 - -Ctrl-w + 增加窗口高度 -Ctrl-w - 减少窗口高度 -``` -**退出窗口** - -```shell -:close 关闭当前窗口 -:close! 强制关闭当前窗口 - -:q 退出,不保存 -:q! 强制退出,不保存 - -:x 保存退出 -:wq 保存退出 -:wq! 强制保存退出 - -:w <[路径/]文件名> 另存为 -:savesa <[路径/]文件名> 另存为 - -ZZ 保存并退出。 - -:only 关闭所有窗口,只保留当前窗口(前提:其他窗口内容有改变的话都要先保存) -:only! 关闭所有窗口,只保留当前窗口 - -:qall 放弃所有操作并退出 -:wall 保存所有, -:wqall 保存所有并退出。 -``` - -## 17. 文档加密 - -``` -vim -x file_name - -然后输入密码: -确认密码: - -如果不修改内容也要保存。:wq,不然密码设定不会生效。 -``` - -## 18. 录制宏 - -按q键加任意字母开始录制,再按q键结束录制(这意味着vim中的宏不可嵌套),使用的时候@加宏名,比如qa。。。q录制名为a的宏,@a使用这个宏。 - -## 19. 执行命令 - - -```shell - -# 重复前一次命令 -. - -# 执行shell命令 -:!command - -# 比如列出当前目录下文件 -:!ls - -# 执行脚本 -:!perl -c script.pl 检查perl脚本语法,可以不用退出vim,非常方便。 -:!perl script.pl 执行perl脚本,可以不用退出vim,非常方便。 - -:suspend或Ctrl - Z 挂起vim,回到shell,按fg可以返回vim。 - -# 执行完当前命令后,自动回到vim环境,但不知为何文件内容变成空了 -:silent !command -``` - -## 20. 帮助命令 - - -```shell -在Unix/Linux系统上 -$ vimtutor - -# 普通模式下 -键盘输入vim或F1 - -# 命令行模式下 - -:help 显示整个帮助 -:help xxx 显示xxx的帮助,比如 :help i, :help CTRL-[(即Ctrl+[的帮助)。 -:help 'number' Vim选项的帮助用单引号括起 - - -在Windows系统上 -:help tutor -``` - -## 21. 配置命令 - -显示当前设定 - -```shell -:set或者:se显示所有修改过的配置 -:set all 显示所有的设定值 -:set option? 显示option的设定值 -:set nooption 取消当期设定值 -:ver 显示vim的所有信息(包括版本和参数等) - -# 需要注意:全屏模式下 -:args 查看当前打开的文件列表,当前正在编辑的文件会用[]括起来 -``` - -更改设定 - -```shell -:set nu 显示行号 - -set autoindent(ai) 设置自动缩进 -set autowrite(aw) 设置自动存档,默认未打开 -set backup(bk) 设置自动备份,默认未打开 - -set background=dark或light,设置背景风格 - -set cindent(cin) 设置C语言风格缩进 - -:set ts=4 设置tab键转换为4个空格 - -:set ff=unix # 修改文件dos文件为unix - -:set shiftwidth? 查看缩进值 -:set shiftwidth=4 设置缩进值为4 - -:set ignorecase  忽略大小写的查找 -:set noignorecase  不忽略大小写的查找 - -:set paste # insert模式下,粘贴格式不会乱掉 - -:set ruler?  查看是否设置了ruler,在.vimrc中,使用set命令设制的选项都可以通过这个命令查看 - -:scriptnames  查看vim脚本文件的位置,比如.vimrc文件,语法文件及plugin等。 - -:set list 显示非打印字符,如tab,空格,行尾等。如果tab无法显示,请确定用set lcs=tab:>-命令设置了.vimrc文件,并确保你的文件中的确有tab,如果开启了expendtab,那么tab将被扩展为空格。 - - -:syntax 列出已经定义的语法项 -:syntax clear 清除已定义的语法规则 - -:syntax case match 大小写敏感,int和Int将视为不同的语法元素 -:syntax case ignore 大小写无关,int和Int将视为相同的语法元素,并使用同样的配色方案 - -``` - - - -以上就是我使用 Vim 的一些使用总结,希望对你能有帮助。 - ------- - -最后,送你一张 Vim 的键盘图,你可以将它设置为你的电脑桌面,对你初学 Vim 可能会有帮助。 - -你可以**关注本公众号「Python编程时光」**,在后台回复“**vim**” ,即可获取高清大图。 - -![图1](http://image.iswbm.com/20190804222221.png) - - - -![图2](http://image.iswbm.com/20190804222247.png) - ---- - -![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c04/c04_20.md b/source/c04/c04_20.md deleted file mode 100644 index 3e477cb..0000000 --- a/source/c04/c04_20.md +++ /dev/null @@ -1,378 +0,0 @@ -# 4.20 最全的 pip 使用指南 - -![](http://image.iswbm.com/20200602135014.png) - -所有的 Python 开发者都清楚,Python 之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python 为基础封装出各种有利于开发的第三方工具包。 - -这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 - -Python 从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 - -当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 - -pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python 的标配。 - -当然也有其他的包管理工具 - -- **distutils**:仅用于打包和安装,严格来讲不算是包管理工具 - -- **setuptools**:distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 egg 的文件格式。 - -- **Pipenv**:一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 - -- 还有其他的,这里不一一列出。 - - - -今天的主角是 pip ,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 - - - -## 1. 查询软件包 - -查询当前环境安装的所有软件包 - -```shell -$ pip list -``` - -查询 pypi 上含有某名字的包 - -```shell -$ pip search pkg -``` - -查询当前环境中可升级的包 - -```shell -$ pip list --outdated -``` - -查询一个包的详细内容 - -```shell -$ pip show pkg -``` - -## 2. 下载软件包 - -在不安装软件包的情况下下载软件包到本地 - -```shell -$ pip download --destination-directory /local/wheels -r requirements.txt -``` - -下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi 上安装。 - -```shell -$ pip install --no-index --find-links=/local/wheels -r requirements.txt -``` - -当然你也从你下载的包中,自己构建生成 wheel 文件 - -```shell -$ pip install wheel -$ pip wheel --wheel-dir=/local/wheels -r requirements.txt -``` - - - -## 3. 安装软件包 - -使用 `pip install ` 可以很方便地从 pypi 上搜索下载并安装 python 包。 - -如下所示 - -```shell -$ pip install requests -``` - -这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 - -**3.1 只从本地安装,而不从 pypi 安装** - -```shell -# 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 -$ pip install --no-index --find-links=/local/wheels pkg -``` - -**3.2 限定版本进行软件包安装** - -以下三种,对单个 python 包的版本进行了约束 - -```shell -# 所安装的包的版本为 2.1.2 -$ pip install pkg==2.1.2 - -# 所安装的包必须大于等于 2.1.2 -$ pip install pkg>=2.1.2 - -# 所安装的包必须小于等于 2.1.2 -$ pip install pkg<=2.1.2 -``` - -以下命令用于管理/控制整个 python 环境的包版本 - -```shell -# 导出依赖包列表 -pip freeze >requirements.txt - -# 从依赖包列表中安装 -pip install -r requirements.txt - -# 确保当前环境软件包的版本(并不确保安装) -pip install -c constraints.txt -``` - - - -**3.3 限制不使用二进制包安装** - -由于默认情况下,wheel 包的平台是运行 pip download 命令 的平台,所以可能出现平台不适配的情况。 - -比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl 就不能在 linux_x86_64 安装。 - -使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 - -比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 - -```shell -# 下载非二进制的包 -$ pip download --no-binary=:all: pkg - -# 安装非二进制的包 -$ pip install pkg --no-binary -``` - -**3.4 指定代理服务器安装** - -当你身处在一个内网环境中时,无法直接连接公网。这时候你使用`pip install` 安装包,就会失败。 - -面对这种情况,可以有两种方法: - -1. 下载离线包拷贝到内网机器中安装 -2. 使用代理服务器转发请求 - -第一种方法,虽说可行,但有相当多的弊端 - -- 步骤繁杂,耗时耗力 -- 无法处理包的依赖问题 - -这里重点来介绍,第二种方法: - -```shell -$ pip install --proxy [user:passwd@]http_server_ip:port pkg -``` - -每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:`$HOME/.config/pip/pip.conf` - -对于这个路径,说明几点 - -- 不同的操作系统,路径各不相同 - -```shell -# Linux/Unix: -/etc/pip.conf -~/.pip/pip.conf -~/.config/pip/pip.conf - -# Mac OSX: -~/Library/Application Support/pip/pip.conf -~/.pip/pip.conf -/Library/Application Support/pip/pip.conf - -# Windows: -%APPDATA%\pip\pip.ini -%HOME%\pip\pip.ini -C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) -C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) -``` - -- 若在你的机子上没有此文件,则自行创建即可 - -如何配置,这边给个样例: - -```ini -[global] -index-url = http://mirrors.aliyun.com/pypi/simple/ - -# 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port -proxy=http://xxx.xxx.xxx.xxx:8080 - -[install] -# 信任阿里云的镜像源,否则会有警告 -trusted-host=mirrors.aliyun.com -``` - -**3.5 安装用户私有软件包** - -很多人可能还不清楚,python 的安装包是可以用户隔离的。 - -如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 - -如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 - -面对这种情况,我们就想能否安装单独为我所用的包呢? - -庆幸的是,还真有。 - -我能想到的有两种方法: - -1. 使用虚拟环境 -2. 将包安装在用户的环境中 - -虚拟环境,之前写过几篇文章,这里不再展开讲。 - -今天的重点是第二种方法,教你如何安装用户私有的包? - -命令也很简单,只要加上 `--user` 参数,pip 就会将其安装在当前用户的 `~/.local/lib/python3.x/site-packages` 下,而其他用户的 python 则不会受影响。 - -```shell -pip install --user pkg -``` - -来举个例子 - -```shell -# 在全局环境中未安装 requests -[root@localhost ~]# pip list | grep requests -[root@localhost ~]# su - wangbm -[root@localhost ~]# - -# 由于用户环境继承自全局环境,这里也未安装 -[wangbm@localhost ~]# pip list | grep requests -[wangbm@localhost ~]# pip install --user requests -[wangbm@localhost ~]# pip list | grep requests -requests (2.22.0) -[wangbm@localhost ~]# - -# 从 Location 属性可发现 requests 只安装在当前用户环境中 -[wangbm@ws_compute01 ~]$ pip show requests ---- -Metadata-Version: 2.1 -Name: requests -Version: 2.22.0 -Summary: Python HTTP for Humans. -Home-page: http://python-requests.org -Author: Kenneth Reitz -Author-email: me@kennethreitz.org -Installer: pip -License: Apache 2.0 -Location: /home/wangbm/.local/lib/python2.7/site-packages -[wangbm@localhost ~]$ exit -logout - -# 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 -[root@localhost ~]$ pip list | grep requests -[root@localhost ~]$ -``` - -当你身处个人用户环境中,python 导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 - -验证如下: - -```python ->>> import sys ->>> from pprint import pprint ->>> pprint(sys.path) -['', - '/usr/lib64/python27.zip', - '/usr/lib64/python2.7', - '/usr/lib64/python2.7/plat-linux2', - '/usr/lib64/python2.7/lib-tk', - '/usr/lib64/python2.7/lib-old', - '/usr/lib64/python2.7/lib-dynload', - '/home/wangbm/.local/lib/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages/gtk-2.0', - '/usr/lib/python2.7/site-packages', - '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', - '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] ->>> - -``` - -**3.6 延长超时时间** - -若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 - -对于这种情况,一般重试几次就好了。 - -但是这样难免有些麻烦,有没有更好的解决方法呢? - -有的,可以通过延长超时时间。 - -```shell -$ pip install --default-timeout=100 -``` - - - -## 4. 卸载软件包 - -就一条命令,不再赘述 - -```shell -$ pip uninstall pkg -``` - - - -## 5. 升级软件包 - -想要对现有的 python 进行升级,其本质上也是先从 pypi 上下载最新版本的包,再对其进行安装。所以升级也是使用 `pip install`,只不过要加一个参数 `--upgrade`。 - -``` -$ pip install --upgrade pkg -``` - -在升级的时候,其实还有一个不怎么用到的选项 `--upgrade-strategy`,它是用来指定升级策略。 - -它的可选项只有两个: - -- `eager` :升级全部依赖包 -- `only-if-need`:只有当旧版本不能适配新的父依赖包时,才会升级。 - -在 pip 10.0 版本之后,这个选项的默认值是 `only-if-need`,因此如下两种写法是一互致的。 - -```shell -pip install --upgrade pkg1 -pip install --upgrade pkg1 --upgrade-strategy only-if-need -``` - -## 6. 配置文件 - -由于在使用 pip 安装一些包时,默认会使用 pip 的官方源,所以经常会报网络超时失败。 - -常用的解决办法是,在安装包时,使用 `-i` 参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 - -这时候,其实可以将这个源写进 pip 的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 - -那怎么配置呢?文件文件在哪? - -使用` win+r` 输入 `%APPDATA%` 进入用户资料文件夹,查看有没有一个 pip 的文件夹,若没有则创建之。 - -然后进入这个 文件夹,新建一个 `pip.ini` 的文件,内容如下 - -```ini -[global] -time-out=60 -index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ -[install] -trusted-host=tsinghua.edu.cn -``` - - - - - -以上几乎包含了 pip 的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 - -![](http://image.iswbm.com/20191105200041.png) - - - -![](http://image.iswbm.com/20200607174235.png) - - - diff --git a/source/c04/c04_22.md b/source/c04/c04_22.md deleted file mode 100644 index c3043b3..0000000 --- a/source/c04/c04_22.md +++ /dev/null @@ -1,6 +0,0 @@ -# 4.22 用好 Chrome 必看 - -![](http://image.iswbm.com/20200602135014.png) - - - diff --git a/source/c10/c10_03.md b/source/c10/c10_03.md index ddf915a..ad4d53c 100644 --- a/source/c10/c10_03.md +++ b/source/c10/c10_03.md @@ -1,5 +1,7 @@ # 10.3 少有人知的 Python "重试机制":tenacity +![](http://image.iswbm.com/20200602135014.png) + 为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 diff --git a/source/c10/c10_03.rst b/source/c10/c10_03.rst index f41d2df..64c04b8 100644 --- a/source/c10/c10_03.rst +++ b/source/c10/c10_03.rst @@ -1,6 +1,8 @@ 10.3 少有人知的 Python “重试机制”:tenacity =========================================== +|image0| + 为了避免由于一些网络或等其他不可控因素,而引起的功能性问题。比如在发送请求时,会因为网络不稳定,往往会有请求超时的问题。 这种情况下,我们通常会在代码中加入重试的代码。重试的代码本身不难实现,但如何写得优雅、易用,是我们要考虑的问题。 @@ -181,7 +183,8 @@ RetryError,而不是最根本的原因。 执行回调函数 False -|image0| +|image1| -.. |image0| image:: http://image.iswbm.com/20200607174235.png +.. |image0| image:: http://image.iswbm.com/20200602135014.png +.. |image1| image:: http://image.iswbm.com/20200607174235.png From 610da0fffb79c6f7a4a9f7b5380d682872522123 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 5 Oct 2020 23:50:18 +0800 Subject: [PATCH 129/147] update --- source/py_mp_index.md | 440 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 400 insertions(+), 40 deletions(-) diff --git a/source/py_mp_index.md b/source/py_mp_index.md index 9c4fc32..a3a47a4 100644 --- a/source/py_mp_index.md +++ b/source/py_mp_index.md @@ -2,6 +2,8 @@ 为了方便读者们能从本号真正学到一些有用的内容,我将 Python 编程时光干货文章进行了整理,方便你学习。 +![](http://image.iswbm.com/20201005142053.png) + ## 01. 基础系列 ### 1.1 基础必学 @@ -58,19 +60,42 @@ 26、[Python 3.9 发布,字典的合并操作符终于来了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485825&idx=1&sn=04075357936423097f692fa19857c3aa&chksm=e8866963dff1e075707b605fb1352f7760fb1b41040bdda4dec6b83370af37e009424d24f65e&token=2013245174&lang=zh_CN#rd) -### 1.2 开发技巧 +27、[Python 3 入门,看这篇就够了(超全整理)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=1&sn=7985df89a647c271a9c99b1a25f87cb4&chksm=e8858366dff20a7062c54d79cfc85945d4ae91e96c0a0dcb8383d24680c6eeb80d68a6f2b718&scene=27#wechat_redirect) -1、[让Python中类的属性具有惰性求值的能力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485759&idx=2&sn=e32b66c4666f96f84572738549a47a24&chksm=e88669dddff1e0cb0a4b6e6e965c9570b273236fa58a54351ae3b118d6a4bcbd52e452109a63&token=1148998814&lang=zh_CN#rd) +28、[掌握 Python 中下划线的 5 个潜规则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492133&idx=2&sn=ba4631b32f8fb4cd018a68b5411771b1&chksm=e88582c7dff20bd1c417c97e6798ba5bcacc90b55161976d7529d12f1850e413ede66110cbe3&scene=27#wechat_redirect) -2、[ 小技巧:如何在 Python 中实现延迟调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485689&idx=2&sn=43e35ff93918ef090480672d4685bce3&chksm=e886681bdff1e10dd0edee95c73a977da51fdeb514dcf8c3bb88f7f9aacc22ce155f45e84268#rd) +29、[还傻傻分不清什么是方法,什么是函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491545&idx=2&sn=193bb88089cc7583989db44707aeca90&chksm=e8867f3bdff1f62d159dca793efc1c937b1c7a135b6f25ff9d77d8d1b819c7ddc66b3ba1f8d0&scene=27#wechat_redirect) +30、[Python 如何像 awk一样分割字符串?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491382&idx=2&sn=6e3824fa540fe7225d0cd1e7180559b6&chksm=e8867fd4dff1f6c2d2c024b6a07f616794c5b89a1b2b08ab05d029e5de1a59ff751bb03a6561&scene=27#wechat_redirect) -3、[三个异常处理的好习惯](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484947&idx=1&sn=2bd31279dd9714045ed4dc5481b44208&scene=21#wechat_redirect) +31、[一篇文章带你剖析Python 字节流处理神器struct](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=2&sn=889bf712f4115a27f321e791b3862405&chksm=e8867e4bdff1f75dc9e1e2374c3302753c3a6a28b42436e6e2550aa8ac528652b2561ad2ca48&scene=27#wechat_redirect) -4、[超实用的 30 段 Python 案例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484866&idx=1&sn=9c85dd1b58f319bb0339e2db577a0be6&scene=21#wechat_redirect) +32、[一篇文章掌握 Python 内置 zip() 的全部内容](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491227&idx=2&sn=61efa9b45c112a25be348a94a9ac8151&chksm=e8867e79dff1f76fcc3858afdfd4e68aef0a5d0430f0420aacd2045aac86d3c31ff88bac464d&scene=27#wechat_redirect) -5、[删除系统 Python 引发的惨案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485386&idx=1&sn=59eed0894159f4d0599be3fb787b5201&chksm=e8866728dff1ee3ef9fb7a0464df481306be11d9aeba944804e7ea74e660501d42e159ada7fc#rd) +33、[字典访问不存在的key 时,如何才能不报错?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490809&idx=2&sn=a256a0d8b86bae3a7dc65f7a88e4ab39&chksm=e8867c1bdff1f50d1da0e11d2dca288b085064062ecb8f68891b0bd48b8a1fca4f8ca594fc22&scene=27#wechat_redirect) + +34、[太干了!一张图整理了 Python 所有内置异常](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=2&sn=59578093d82362c023ce305b2fc55103&chksm=e886791adff1f00c0a76154c7c7101d39b151891cc07b33dfe2523c95d9e38250c3684f674bb&scene=27#wechat_redirect) + +35、[Python 代码覆盖率工具 - Coverage](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489752&idx=2&sn=b115d9377feb0129985295e5a3c8ec5e&chksm=e886783adff1f12c7bb14c55560f06f6a5327d6beee9bba38b9965784baf197918ae3898e95d&scene=27#wechat_redirect) + +36、[超赞!100 道让你练习 Python 基础的题目](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488558&idx=1&sn=58a53b85a7e49989794d8d0e9885f335&chksm=e88674ccdff1fdda61609dc4aa811dc6a52a819fe56da3bd0480f9f21ae248c94d8f8f3f8798&scene=27#wechat_redirect) + +37、[看完这篇,你也是字符编码大神!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488475&idx=2&sn=17ca56435158c6c19134e30bda6e2b2d&chksm=e8867339dff1fa2f99cb0d85d99a2befa3689951778fbef8f2e0bad1042ae0c4813b99860c33&scene=27#wechat_redirect) + +38、[一道 3 行代码的 Python面试题,我懵逼了一天](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486901&idx=1&sn=73fe3a92ac2639706d9573a0eaffd48f&chksm=e8866d57dff1e441d6ffad3c4f00b053db39df1cec4e6cf69992641b359cc96448f7f3068918&scene=27#wechat_redirect) + +39、[学习 Python 的指南大纲,从基础到核心知识全都有](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486246&idx=1&sn=58665e2be95f71415ac3e2c1c5f4f690&chksm=e8866bc4dff1e2d212a4d93c0c83cd39cd534f64a8e113b208f96a6156e75ef652ee1ac32270&scene=27#wechat_redirect) + +40、[打基础一定要吃透这12类 Python 内置函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486198&idx=1&sn=efea0e57956188eeafb163d7bb2448c4&chksm=e8866a14dff1e3022f361245b182a30fbbf40550f3e8dc4456e1acf260aa73d35a20c010559d&scene=27#wechat_redirect) + +41、[有了这篇文章, Python 中的编码不再是噩梦](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486174&idx=1&sn=ceb21deb2ff3ce750117c23f414416e3&chksm=e8866a3cdff1e32a1fcf07880803a7a3c350cc40d612bbd4f34b84cb06bf39250932f1ba5f05&scene=27#wechat_redirect) + +42、[OrderedDict 是如何保证 Key 的插入顺序的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486136&idx=2&sn=81fd4039611666917ae8801bd73fe921&chksm=e8866a5adff1e34c603500e97c4b7e1cecb7a1fb43e01c6ff5a794d526d54e5a02b35240d2db&scene=27#wechat_redirect) -6、[ 利用 Flask 动态展示 Pyecharts 图表数据的几种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485658&idx=1&sn=f78e976889de52c0ce35e862bafacd8a&chksm=e8866838dff1e12e41b8f2dd1a43dc540ff39484ae64831332d233b5a4f7ca3b439d370ef1ed#rd) +43、[盘点 Python 高手都写不出来的几个错误](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485974&idx=1&sn=6a6a2fb8bc5c2acd300ebecdf625086c&chksm=e8866af4dff1e3e2c528594310475edaf2839d7b09048270acbb0dccc124581eaa65b123d393&scene=27#wechat_redirect) + +44、[8个超好用的Python内置函数,提升效率必备!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485943&idx=2&sn=ecb57c8dbf0e4965d4842cc6f614da64&chksm=e8866915dff1e0035ded3974097c1f0e00561962eeb0498c633fdf664cd429749aa6e6eca806&scene=27#wechat_redirect) + +45、[如何使用 Python 执行 js 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491161&idx=2&sn=d89bbc4c214563807427703aa584a26d&chksm=e8867ebbdff1f7ad301b58ac567faec95be2d8f5be921b23ea5d3722d377c2926f32ff7b68ac&scene=27#wechat_redirect) ## 02. 进阶系列 @@ -130,6 +155,25 @@ 27、[教你如何阅读 Python 开源项目代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485451&idx=1&sn=e15856352c18297770c67d9df66ec3b0&chksm=e88668e9dff1e1ff0f6a39f3885f4126232213149ac09daa1fc0ec9e0de2095bd11fb727d63a#rd) +28、[这个 Python 知识点,90% 的人都得挂~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490984&idx=1&sn=135b7b187d8d41b3c10179dbaedaf77e&chksm=e8867d4adff1f45cd90fe2609fe7d1840b07fab8cf19cb7406ebd038c31df0028e611077d80e&scene=27#wechat_redirect) + +29、[一篇长文学懂 PyTorch](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489158&idx=1&sn=2e2e88565131d11271aea61b0bedf182&chksm=e8867664dff1ff728ee30e1baab0d7df0139fc67e2514ffcaebda6d50e5d08bd2866ad7dee35&scene=27#wechat_redirect) + +30、[学了这么久,你知道Python是如何运作的吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488458&idx=1&sn=23040c906e83462c4f64039cba6e3dfb&chksm=e8867328dff1fa3e149248e53762444b1abc72df395a597639357b5525d16b1b6db242339d20&scene=27#wechat_redirect) + +31、[求你了,别再使用 pprint 打印字典了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486288&idx=1&sn=7647e1ee63c49c14472baec19fb79b87&chksm=e8866bb2dff1e2a4d5b85ae9db1e8f4d8dda839643fb02b9e0b7f7fe6143cb627773e56d44bf&scene=27#wechat_redirect) + +32、[Python实现RabbitMQ中6种消息模型](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486017&idx=1&sn=9b95896abee7a345e3abee1ac4f2edab&chksm=e8866aa3dff1e3b547ef5062c30516b2d54a051fd4a07d85078223ea673af1400be1a6c56ad5&scene=27#wechat_redirect) + +33、[想写好面向对象的代码,这几篇一定要看(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485938&idx=1&sn=a725785ef8000868b7cb85606ee754d3&chksm=e8866910dff1e006f195d6ea0d66f4b4f1fe5a9b97debc18ae4b04a239a347d342fbc67b44e4&scene=27#wechat_redirect) + +34、[想写好面向对象的代码,这几篇一定要看(中)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485926&idx=1&sn=ce148af333d9f377d8a6fb9577bbf57c&chksm=e8866904dff1e012ec1bb8f418c43cca7954979bd1481707761b29d1e632ace6369efd50210b&scene=27#wechat_redirect) + +35、[想写好面向对象的代码,这几篇一定要看(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485913&idx=1&sn=74b34bd95d513a94201ad05604c4a05f&chksm=e886693bdff1e02dd2079cb90458aac914e5cb832aec59677da8b2f85200baaa57e804a4b58e&scene=27#wechat_redirect) + +36、[教你 10 分钟构建一套 RESTful API 服务](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=2&sn=d593dbf907217ff4dea1daf636062778&chksm=e88674f4dff1fde26b1a1074a0a0c78be0630a34b4aed705755946bd8cfcde1a23e0df916392&scene=27#wechat_redirect) + + ### 2.2 包的管理 1、[最全的 pip 使用指南,50% 你可能没用过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484827&idx=1&sn=df0923856c820e10baca20c9873b336b&scene=21#wechat_redirect) @@ -140,6 +184,14 @@ 4、[盘点 Python 依赖库管理的工具:pip、pipreqs、pigar、pip-tools、pipdeptree](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485120&idx=1&sn=170ef1173eabc5bed28c724e19d6c0ac&scene=21#wechat_redirect) +5、[如何使用 pyenv 运行Python的多个版本?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486239&idx=2&sn=863e80589f5eb9b2daa9701f6b494997&chksm=e8866bfddff1e2ebc244354238aedfa9291aa27ad8f7e466213236369dae9800b29b5766c0cf&scene=27#wechat_redirect) + +6、[如何管理 Python 的虚拟环境?超全讲解virtualenv的使用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&scene=21#wechat_redirect) + +7、[一款让Python开发效率提升50%的工具包](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=1&sn=eead45b3c34777085465f9854a433e36&chksm=e886750cdff1fc1ae2791adba1231e045630417f9b4185217817183aa1f260815acabcd39363&scene=27#wechat_redirect) + +8、[记 Python “用户环境”的一次完美应用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486191&idx=1&sn=e556df305ce1df4c4969d2fdbe884cfa&chksm=e8866a0ddff1e31b2ca8ca8fbbe0355fb8c24376d715224ef2176e613b5601764be0193e3aa4&scene=27#wechat_redirect) + ### 2.3 性能优化 1、[Python高效代码实践:性能、内存和可用性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485161&idx=1&sn=07625f06db330897bc8bfee880ceac18&scene=21#wechat_redirect) @@ -156,6 +208,8 @@ 7、[实战讲解:Python 性能分析与优化实践](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485404&idx=1&sn=486fcf898d6dd21b0feb990de3c1e08f&chksm=e886673edff1ee28b8c6e1d520b2732b2f66f4c4691ef2696b54743b1ad0769356e12a1e98b2#rd) +8、[如何调试Python 程序的内存泄露问题](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488577&idx=2&sn=16aff9687a4a16faa492118cd5c1bfae&chksm=e88674a3dff1fdb5b80a4547cb863fcf4255f90ab2024ae8c3bbf1f270efb13333b2f7426baf&scene=27#wechat_redirect) + ### 2.4 并发编程 1、[并发编程01|从性能角度来初探并发编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485074&idx=1&sn=a859c6ab1d9b95c30c9f8b06f9489887&scene=21#wechat_redirect) @@ -194,18 +248,99 @@ 18、[非常适合小白的 Asyncio 教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485842&idx=1&sn=ab85dab95cf47046ddcc35b60e633c40&chksm=e8866970dff1e066381c0ba53fe09e34b8f1ce52bbbfe568c1ee66b9cbad87f43b442895c770&token=2013245174&lang=zh_CN#rd) +19、[说说 Python 里关于线程安全的那些事儿](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486279&idx=1&sn=2b6acf717b7f5cc6a2b72c62266dbe01&chksm=e8866ba5dff1e2b318c679413fd7d0ac3a69db7cab1ceda03eca26fb71f6d2837af80e2143d0&scene=27#wechat_redirect) + ### 2.5 实战练习 1、[适合 Python 新手练习的绝佳项目](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485898&idx=2&sn=36a1b1a94dece0a290652da23679cff0&chksm=e8866928dff1e03e58b95b3cd24dbc23e368b6eb2c0599b2efe814924e21ec5c01935b2e0455&token=2013245174&lang=zh_CN#rd) 2、[4个Python实战项目,让你瞬间读懂Python!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485852&idx=2&sn=eb3471a260cce835a755a780d6cc690d&chksm=e886697edff1e068bd0f9d456e26ee5ebe482580457951e42807e731d266c95f00c37de22fea&token=2013245174&lang=zh_CN#rd) +### 2.6 GUI 应用 + +1、[如何在Python中编写精美图形界面?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=1&sn=eed86ec2f44a92fadb94f237178b3f8a&chksm=e8866a01dff1e317f450d91b88e91d6912d6a1b2ac7a34ebb86b74a2abc0f912d8ced5a3baac&scene=27#wechat_redirect) + +2、[或许,这是最强大的一款Python GUI工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492571&idx=2&sn=6ac2c0c19d86d653acd82d5b4ef86126&chksm=e8858339dff20a2f595159499e8eb8e072b52e61521e4a1322337f174bff942134a25b3b6b9b&scene=27#wechat_redirect) + +### 2.7 自动化办公 + +1、[付费?是不可能的!20行Python代码实现一款永久免费PDF编辑工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492085&idx=1&sn=785968af86bf998ecad9b11583a23ad6&chksm=e8858117dff2080119f596c6447d9a0c7b73c6bced8306883900f2f4d9582929f9d5c26ac45d&scene=27#wechat_redirect) + +2、[带你用 Python 实现自动化群控(入门篇)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491979&idx=2&sn=7489738d3225a96767173e4b54023f95&chksm=e8858169dff2087fed0f11ad6b880c110e3530eae28ed7702343f6d81163860ed20a2f056294&scene=27#wechat_redirect) + +3、[最全总结:Python 发送邮件的几种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489568&idx=2&sn=bdac42b533784bf3a8ee523da36bacba&chksm=e88678c2dff1f1d499321032e6871f2810608909121bf56aee184c6d7770d3b310f39cad7c43&scene=27#wechat_redirect) + +4、[太全了!使用 Python 转换 PDF,看这一篇总结就够了。](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=2&sn=eed1cab8e03c908fdfe311bf4bbd91a6&chksm=e8867544dff1fc52e0d9dd62c61d36ec5a5b6847aeb9992e5423eb4e01f08995461160adbb45#rd) + +5、[Python吊打Excel?屁!那是你不会用!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=2&sn=9c4a697758af01c086cced44b98a3380&chksm=e8866e4ddff1e75bd2f19e036c3e338edee50871364d2f185afa51cb361c83f3bbda8ce2d92f&scene=27#wechat_redirect) + +### 2.8 网络爬虫 + +1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect) + +2、[教你实现一个可视化爬虫监控系统](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484208&idx=1&sn=d04421526cf0e12089541063b4ec448d&scene=21#wechat_redirect) + +3、[估计是讲得最清楚的「异步爬虫」指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484929&idx=1&sn=7055414a33d7cbadaeb5645cbd5203ec&scene=21#wechat_redirect) + +4、[10 个爬虫工程师必备的工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484958&idx=1&sn=3422e53de4ae701a922104ad26f8e256&scene=21#wechat_redirect) + +5、[Selenium自动登录淘宝,我无意间发现了登录漏洞!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=2&sn=6a33873b3777afd420f3c56078a88687&chksm=e8867c53dff1f5455fe54e1ee0044cc97638fbc96983f36e19938810bea4e06d5af0ba3036df&scene=27#wechat_redirect) + +6、[干!一篇文章讲了这么多爬虫的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490557&idx=2&sn=f5128b1dc827cc4a6035d46714a484b1&chksm=e8867b1fdff1f20993b024fbfaa4445763a5949174913a58d9f17ddcb0286be8b325516cb449&scene=27#wechat_redirect) + +7、[谈谈常见网站加密和混淆技术](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486218&idx=2&sn=6261fa602eb4f73365a9d372376594ed&chksm=e8866be8dff1e2fea5082dd13dc7799a1cbc829f2e41a32891db411152153cb261d01be5807b&scene=27#wechat_redirect) + +8、[初识网络爬虫之夜探老王家](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=2&sn=3a1b880c7f1b37d095359b35ff96e9b4&chksm=e8866a01dff1e31711d7fb093a96f54eaf374b46c686b0ea4e49d6f926018ad5b6c27aebfa9d&scene=27#wechat_redirect) + +9、[不懂爬虫也能轻易爬取数据的 6 大工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486085&idx=1&sn=51c740e1d848f1624a5a8e582434f661&chksm=e8866a67dff1e371de89a64503591ae0c42b67cd43434fa394125f09a2f65bc23817a266ac13&scene=27#wechat_redirect) + +### 2.9 实用系列 + +1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect) + +2、[30分钟教你快速搭建一个顔值超高的博客](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485045&idx=1&sn=8b250c0c174e418e2025d86f42c695b6&scene=21#wechat_redirect) + +3、[用Python写一个表白神器让你七夕脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) + +4、[用 Sphinx 搭建博客时,如何导流到公众号?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484848&idx=1&sn=80ae18e7f53a64e62ac9c0ef0c21362e&scene=21#wechat_redirect) + +5、[10 行 Python 代码写 1 个 USB 病毒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484832&idx=1&sn=43c353856c7dd9ea2cb1a9bbcd077fa2&scene=21#wechat_redirect) + +6、[废旧 Android 手机如何改造成 Linux 服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485279&idx=1&sn=7166a2c9445438eaaf63a25b598e5427&scene=21#wechat_redirect) + +7、[如何将手机打造成 Python 开发利器?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485256&idx=1&sn=2e646e28287707c67c22fbe68112e622&scene=21#wechat_redirect) + +8、[我用 Python 做了一回黑客,批量破解了朋友的网站密码](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485156&idx=1&sn=05142ae7b4bf7e97ebe042e394aa7084&scene=21#wechat_redirect) + +9、[手把手教你安装Win+Ubuntu双系统](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485030&idx=1&sn=8383a7306381f36781957b807fa93961&scene=21#wechat_redirect) + +10、[一篇文章让你的 MacBook 进入超神状态](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484906&idx=1&sn=b2c3c969e53beae53aa7be7959227b5b&scene=21#wechat_redirect) + +11、[情人节来了,教你个用 Python 表白的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485701&idx=1&sn=ef7f0b83e60f397c1839259b275575bf&chksm=e88669e7dff1e0f1317ec429cf9e70442bb3a08ec46efe9a0d06f4ec9847574ea30b53904670&token=1148998814&lang=zh_CN#rd) + +12、[“Hack” 微信实战:如何用 Python 分析微信群聊记录?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486141&idx=1&sn=8b32c57dce0f3a33127c39c9cd35509e&chksm=e8866a5fdff1e349f16f677304716e40a305e60168fe7e30361de675dad5dfa956c9d7c79089&scene=27#wechat_redirect) + +13、[5 行 Python 代码生成自定义二维码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488795&idx=2&sn=8e03e01e861753f7ccf3adeb1f3cb848&chksm=e88675f9dff1fcef328c69b1519743fcc3ae83b80dd3d66cb934acb346781497f83c5a9051a5&scene=27#wechat_redirect) + +14、[女朋友背着我,用 Python 偷偷隐藏了她的行踪](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487175&idx=1&sn=c3ba19bdbcb6fd59b37ff04d02bce63b&chksm=e8866e25dff1e733b31386fda99271eefadc77b3e0579df4d74617b561ecf0fa0db70f9f2860&scene=27#wechat_redirect) + +15、[zip 解压炸弹? ?在 Python 面前,啥也不是..](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=2&sn=307a29d71776f6d1c68567a16727a7e5&chksm=e88581e3dff208f55a48081cb8f951ca7d977c9b1ec3ac9aaf26bc8c53904ac0e15035e5c680&scene=27#wechat_redirect) + +16、[Chrome 的小恐龙游戏,被我破解了...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491911&idx=1&sn=1dcab055e480fe626d50b0324ed5b9f1&chksm=e88581a5dff208b3fbf88017c15ccfa966eec3b25e4804a72401773d7b87d5b6a2bcd8db9ac7&scene=27#wechat_redirect) + +17、[深扒微信多开的秘密后,我竟然发现了个 bug](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489704&idx=1&sn=a33559f22517c744367a660f78dbb25a&chksm=e886784adff1f15c1524f90487d6957c86dee0f5c499528630f43b62320b28e66977afe4d884&scene=27#wechat_redirect) + + ## 03. 数据分析 ### 3.1 基础库 1、[有关 NumPy 和数据表达的可视化介绍](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485295&idx=1&sn=6220c2ed0d73feade0c9691fbf214ba7&scene=21#wechat_redirect) +2、[快速提升效率,这6个 Pandas 技巧一定要知道](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491909&idx=2&sn=3f3bf16784689a5e8bd5f9ef9478040f&chksm=e88581a7dff208b12e25ddaf6a047abc47878eee56f85309b6618cfd92a4dfeb2445f23e2f05#rd) + +3、[50题 讲透 matplotlib :从入门到精通](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486026&idx=1&sn=7b333759709c30fe442732b7a107e3c1&chksm=e8866aa8dff1e3be37f5169ac53bbcf71a76514e99fbbcbe458ec15aa1204d988ad44d689eb3&scene=27#wechat_redirect) + ### 3.2 数据可视化 1、[可视化01|一图带你入门matplotlib](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485022&idx=1&sn=76d4270a15c430217588bd9f8be8303b&chksm=e88666bcdff1efaa35c6d685f19b04e62f58e768dc9d1819bcb9f8211f65d3fd83296259d923&token=1148998814&lang=zh_CN#rd) @@ -222,12 +357,44 @@ 7、[可视化07|50个最有价值的图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484920&idx=1&sn=92eb2fd55f13a8bda75a7103a73f8d50&scene=21#wechat_redirect) +8、[ 可视货08|利用 Flask 动态展示 Pyecharts 图表数据的几种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485658&idx=1&sn=f78e976889de52c0ce35e862bafacd8a&chksm=e8866838dff1e12e41b8f2dd1a43dc540ff39484ae64831332d233b5a4f7ca3b439d370ef1ed#rd) + +9、[一个没法商用,但是好玩有趣的 Python 手绘图形库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491212&idx=2&sn=66a5fd4f672b668aee6bb2c3a01a92f6&chksm=e8867e6edff1f77813b2b59f6219d18041963e6011afa1a590925c71ebd79c7e90b03e82c606&scene=27#wechat_redirect) + +10、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect) + +11、[一文学会制作 6 种炫酷的 Python 动态图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486111&idx=2&sn=fadf51d43bc468c58ba6885db3006e7a&chksm=e8866a7ddff1e36b5815a9693150799d5925727f5a425e642fd136283ff4a58b7f19ba9da39d&scene=27#wechat_redirect) + +12、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect) + +13、[超硬核的 Python 数据可视化教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=2&sn=2aede70646f9ee34ad526d2d99b6549e&chksm=e885824bdff20b5d428dbb09daeae2f3e96f1362372bf6ffb16fae1f06263703379873025d43&scene=27#wechat_redirect) + +14、[收藏!最全的可视化学入门算法教程(Python实现)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491520&idx=1&sn=55804b80dcc56a3923255abf58e08968&chksm=e8867f22dff1f6340a7f61f7796a1e78bd695278a340351860efc244c8a9bbf80cc02bbfaf05&scene=27#wechat_redirect) + +15、[50 款数据可视化分析工具大集合,总有一款适合你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=1&sn=f447d6e27504611baf8a4ad4056c6e5a&chksm=e8867c53dff1f5450ff808baec53797bd772cdd3c0e43f2f0c278cd4b8867223d5591ab2aa7d&scene=27#wechat_redirect) + +16、[用Python画漂亮的专业插图 ?So easy!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=1&sn=14df276f09201930fdc413d8f9a772df&chksm=e8867a29dff1f33fd0f85cf4412c42cc1145e917b3cd06e158aa31356525799033dd8c6e620f&scene=27#wechat_redirect) + +17、[太好玩了,看看我用 Pyecharts绘制的“时间轮播图”](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=2&sn=6359a534033c0a6f21af28a7d9f8b768&chksm=e886789bdff1f18d6719558e305453cba2b6c8a55d66c9bcb6dd5678209d89c1594d87de0a87&scene=27#wechat_redirect) + +18、[实战!用60行Python代码画出30万条房产数据分析图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=1&sn=6b9c7281d3090783a9a486b81f749789&chksm=e88676eadff1fffcec00d3c44ee9b34496dc886417da55aeabed017d496da85d442f205c227d&scene=27#wechat_redirect) + ### 3.3 工具使用 1、[ 整理了 50个 IPython 的实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485613&idx=1&sn=70f89faf2573b3025edc73f525f22a0a&chksm=e886684fdff1e159509d30fd0a24854da39fb5a6ae55e0ed6e40b7971d3219968006f80aad7b#rd) 2、[ Jupyter NoteBook 的使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485024&idx=1&sn=aac0db4b2c97c7f5c2871342b936eaa2&chksm=e8866682dff1ef947ec6474b8c03f668e462232ebd0ec0f137d42e5e63b9b66a3d9e303c1caf&token=1148998814&lang=zh_CN#rd) +3、[Jupyter Notebook最常用的五大配置技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491411&idx=2&sn=88c290af7793ddc024cf2a39875b97e8&chksm=e8867fb1dff1f6a731077d749b1ea44fd558995f3448e27da9becf7b035ed777774d1b0bb0ec&scene=27#wechat_redirect) + +4、[强烈安利!这十二个 IPython 魔法命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488777&idx=2&sn=3b14fe7faa4d2a26b702c15ebe6b6103&chksm=e88675ebdff1fcfd471dc84c802dab2c37889d59e55711c5d521c80fdbf38291b4092b840083&scene=27#wechat_redirect) + +5、[真香!安利 6 个 Python 数据分析神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488619&idx=1&sn=0c6c76b9deaa4f1976dcacaaee018312&chksm=e8867489dff1fd9fa2b0ac08696c4dae1b48054ca64aefef6d2117179dbbffbfe4ae9d1a4f81&scene=27#wechat_redirect) + +6、[使用 pyecharts 打造酷炫的 BI 大屏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=2&sn=b2ee3058d63ebf702f3f0f9a6ca9c623&chksm=e8867010dff1f90686948269ca5e0907d726fc775029cc414285d71c0176ce859b818d2e34d5&scene=27#wechat_redirect) + +7、[酷炫的动态可视化交互大屏,用Excel就能做!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=2&sn=2826a69402912a70955209192e761c4d&chksm=e8866bb5dff1e2a30d374d5f46afa1cb9d952a0c939701b9f8b5bc52df4169d5e2da30236505&scene=27#wechat_redirect) + ## 04. 开发工具 1、[代码调试|无图形调试工具 - pdb](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484969&idx=1&sn=a99fc31865edc4b439707d2be6f66654&scene=21#wechat_redirect) @@ -244,7 +411,7 @@ 7、[开发工具|盘点 Xshell 的那些奇淫技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485043&idx=1&sn=8c240e569c60607cd4cea8888a6aa2e1&scene=21#wechat_redirect) -8、[开发工具|给你的项目买份保险:Python虚拟环境](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&scene=21#wechat_redirect) +8、[告别996,全靠这个Python补全利器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484824&idx=2&sn=8f20af371954dfd2a5c890bb5814b4c9&scene=21#wechat_redirect) 9、[学会这21条,你离 Vim 大神就不远了!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484902&idx=1&sn=d677261fda90b67bf5eda886447a4f77&scene=21#wechat_redirect) @@ -262,9 +429,9 @@ 16、[Win 平台做 Python 开发的最佳组合](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484837&idx=2&sn=68935077ae9eeb6fe4f708565aa0a9ed&scene=21#wechat_redirect) -17、[告别996,全靠这个Python补全利器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484824&idx=2&sn=8f20af371954dfd2a5c890bb5814b4c9&scene=21#wechat_redirect) -18、 + + ### 4.1 PyCharm @@ -286,12 +453,30 @@ 9、[手把手教你打造一个顔值超高的IDE](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485052&idx=1&sn=2e0bb1a3af4206fd2cf8e3650f695e6b&scene=21#wechat_redirect) +10、[玩转 PyCharm ,这篇文章就够了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487629&idx=1&sn=9a19cbcce2effcc242cc8d6d33cd2762&chksm=e886706fdff1f97955e73495c0be5b104f834d569d75e9e1470417f79e1d19ff4937683484ad&scene=27#wechat_redirect) + +11、[用动画展示 Pycharm 十大实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486118&idx=1&sn=990c35adb86919bda2f2cb82e7c5fb9e&chksm=e8866a44dff1e35200244e3d369ad6c0f736d3511dcfb086b7641339e20ae62e35b8fd60bdd9&scene=27#wechat_redirect) + ### 4.2 VSCode 1、[这 21 个VSCode 快捷键,能让你的代码飞起来](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484893&idx=1&sn=421b544115efad388314ccd027761b40&scene=21#wechat_redirect) 2、[神技巧!在浏览器中也能用 VS Code](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484859&idx=2&sn=70abd19426374271a10cbef8e1645c7a&scene=21#wechat_redirect) +3、[生产力终极指南:用了两年,如今才算真正会用VS Code](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=2&sn=a39a7cb70908d442a399ac80a272f90e&chksm=e8858311dff20a0782cca942c7e9f3444bb443cf816fd05d76c5f7c234930afacb6c948802bf&scene=27#wechat_redirect) + +4、[VS Code的7个开源替代品,全都知道算我输!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=2&sn=ef0eca9da6eff8405a5a48d89a6b2e54&chksm=e8867a29dff1f33f29487864efb1389b02705d81d0198df581b5b71085ff238f24ffe99f154c&scene=27#wechat_redirect) + +5、[再见了,PyCharm](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489522&idx=1&sn=d7d45dba4a7c9e2f010d09f757639a19&chksm=e8867710dff1fe0657a16b6087ad4a50fdceb8f324bef5f950b3320e33e09ce10d1fa045827d&scene=27#wechat_redirect) + +6、[微软推出 Pylance,改善 VS Code 中的 Python 体验](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=2&sn=58f6dbe10cba98cebab0fd19f124e4df&chksm=e886750cdff1fc1a6cfe249a2f76213fef1a789f4a5006704e33d00b9abadde489af7052a8a9&scene=27#wechat_redirect) + +7、[VS Code 连接远程服务器运行 Jupyter Notebook](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486773&idx=2&sn=75b2453513b1445d9881f5ec8c1f44d1&chksm=e8866dd7dff1e4c167b27eccec9a6469b7988d6b05010160247b3e1a549764201807212ed581&scene=27#wechat_redirect) + +8、[GitHub 发布重磅更新:你电脑上的 IDE 可以删了?!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=1&sn=e5bd46de0616fad6105d2e46a359cd3d&chksm=e8866bb5dff1e2a37058e1cbc502863f8b8a885a493b7ef9f5e442a1b554ffeb0ae09d77026e&scene=27#wechat_redirect) + +9、[用 VS Code 写 Python,这8个扩展装上后无敌了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492505&idx=1&sn=ba354ab86500280d46038d2fa2f3572b&chksm=e885837bdff20a6dd2d4411719e44a3b88371b5006e9dc2b2580e6862f3161e70cb339240512&scene=27#wechat_redirect) + ### 4.3 好用的库 1、[使用 Python 远程登陆服务器的最佳实践](https://mp.weixin.qq.com/s/aRXYAP9D9rgil-0_Etb0SQ) @@ -314,43 +499,55 @@ 10、[如何使用 Python 输出漂亮的表格?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485841&idx=2&sn=705c123abed5441a52f8640b65f3c400&chksm=e8866973dff1e0650e4b2e025d0e86b3aedeb7ea8f68ad560f37cb033f36660b6eee15fb3ce4&token=2013245174&lang=zh_CN#rd) -## 05. 网络爬虫 +11、[用 Python 写出来的进度条,竟如此美妙~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492108&idx=2&sn=2fa1e31f9684df6d6f3da21aa4cba6e5&chksm=e88582eedff20bf886c29d480245d835c0a3fbea49e54909abbeb703bb7149d4e3444202e97c&scene=27#wechat_redirect) -1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect) +12、[推荐一些能提高生产力的 Python 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491947&idx=2&sn=765ca757e0aee7fd4dd3f7779f409ff3&chksm=e8858189dff2089f74a97972c1f213291a8680a766b328a74c609f465f7dcf16c324df57a660&scene=27#wechat_redirect) -2、[教你实现一个可视化爬虫监控系统](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484208&idx=1&sn=d04421526cf0e12089541063b4ec448d&scene=21#wechat_redirect) +13、[规整字符串的数据提取神器:parse 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491617&idx=1&sn=b77cd25d6c92f86c1492f913edcb51c7&chksm=e88580c3dff209d500525bbc81464034ca431c92603b5ec83046faaac7ca4c012034964805e0&scene=27#wechat_redirect) -3、[估计是讲得最清楚的「异步爬虫」指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484929&idx=1&sn=7055414a33d7cbadaeb5645cbd5203ec&scene=21#wechat_redirect) +14、[微软再出神器,这次终于对 Python下手了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491407&idx=1&sn=0044bf683ddc4799ef2e7c04fb75ebf8&chksm=e8867faddff1f6bbbae3e716e7b686eeb4a5b49f19a2fb2b8743ae2e641ee529189d313f6fe8&scene=27#wechat_redirect) -4、[10 个爬虫工程师必备的工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484958&idx=1&sn=3422e53de4ae701a922104ad26f8e256&scene=21#wechat_redirect) +15、[这个 Python 库有点黑科技](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=1&sn=700979655adce734f5300e7ee380d4ab&chksm=e8867e4bdff1f75d355b34ca8561844c920a2434357350e06a87c99cdbdfb094ee29e7957988&scene=27#wechat_redirect) -## 06. 实用系列 +16、[适合 Python 入门的 8 款强大工具!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491088&idx=2&sn=23cd3b06b8117a5d996d258dd9c0eb23&chksm=e8867ef2dff1f7e479ad5a633295f1be0fc272f6b4de2526c21129b2dee23cb517e51b508f8f&scene=27#wechat_redirect) -1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect) +17、[这些Python库虽然冷门,但功能真的很强大!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490868&idx=2&sn=7bda9c9021cd26eb43d752f6ff84dbe7&chksm=e8867dd6dff1f4c0398b427f13787c99f24cea2a7b2c7b10fbc3316b0215b3ea861f197e604f&scene=27#wechat_redirect) -2、[30分钟教你快速搭建一个顔值超高的博客](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485045&idx=1&sn=8b250c0c174e418e2025d86f42c695b6&scene=21#wechat_redirect) +18、[那些被低估了的 Python 库,看看你用过几个?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490782&idx=1&sn=2c710eca844da200dac14ab017ffcdb3&chksm=e8867c3cdff1f52a86c291e34e458f57bd5572c6fdeb75cdb84fdfba2d9bb1731067dca7d195&scene=27#wechat_redirect) -3、[用Python写一个表白神器让你七夕脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) +19、[用 Python 写出这样的进度条,刷新了我对进度条的认知](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490326&idx=1&sn=867061f4ad793e957161c30be77ef9bb&chksm=e8867bf4dff1f2e2f1cdc5af4366540d33fc0edfc52810af09b82a590fcd6ec4c5c2d7a528c6&scene=27#wechat_redirect) -4、[用 Sphinx 搭建博客时,如何导流到公众号?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484848&idx=1&sn=80ae18e7f53a64e62ac9c0ef0c21362e&scene=21#wechat_redirect) +20、[少有人知的 Python "重试机制",请了解一下 tenacity ](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489274&idx=1&sn=7ddc4505f932b386ee22c7873ff5df1a&chksm=e8867618dff1ff0ea5aa55c6acfeb85944028f1bb0be7fb2697f4ec018de297579b797ce048b&scene=27#wechat_redirect) -5、[10 行 Python 代码写 1 个 USB 病毒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484832&idx=1&sn=43c353856c7dd9ea2cb1a9bbcd077fa2&scene=21#wechat_redirect) +21、[一个极具意义的 Python 前端开发工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489090&idx=1&sn=1e7ee21422440dc32d7a9a40eaab6c4e&chksm=e88676a0dff1ffb6731df2996d3a4011eebdd8af4926920eefb30d086fa909943179e83d8957&scene=27#wechat_redirect) -6、[废旧 Android 手机如何改造成 Linux 服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485279&idx=1&sn=7166a2c9445438eaaf63a25b598e5427&scene=21#wechat_redirect) +## 05. 网络基础 -7、[如何将手机打造成 Python 开发利器?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485256&idx=1&sn=2e646e28287707c67c22fbe68112e622&scene=21#wechat_redirect) +1、[点亮你的 HTTPS?原来这么简单!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=1&sn=46b095516bf3c0ebfc367ab64f8fd42c&chksm=e885824bdff20b5db2861a411a07653135ffbafaa1e772588bcf86cf9ad54433a0ff492355ad&scene=27#wechat_redirect) -8、[我用 Python 做了一回黑客,批量破解了朋友的网站密码](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485156&idx=1&sn=05142ae7b4bf7e97ebe042e394aa7084&scene=21#wechat_redirect) +2、[手绘 10 张图,把 CSRF 跨域攻击、JWT 跨域认证说得明明白白的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489610&idx=1&sn=c9442c4ad370c318ea7ebafb26208b9c&chksm=e88678a8dff1f1bec43cd601b0c0e3eaff32d168dc2ad69f6273e27f2e6d72b49b7585046057&scene=27#wechat_redirect) -9、[手把手教你安装Win+Ubuntu双系统](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485030&idx=1&sn=8383a7306381f36781957b807fa93961&scene=21#wechat_redirect) +3、[网络出了问题,如何排查? 这篇文章告诉你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488835&idx=2&sn=edac7045eb00479481365334ba198f50&chksm=e88675a1dff1fcb76f10049aa97ade7c0669fc1a8c32649a810c1d43b37c7dbe964c17b0730f&scene=27#wechat_redirect) -10、[一篇文章让你的 MacBook 进入超神状态](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484906&idx=1&sn=b2c3c969e53beae53aa7be7959227b5b&scene=21#wechat_redirect) +4、[肝了三天,万字长文教你玩转 tcpdump,从此抓包不用愁](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488684&idx=1&sn=9a9d717a6c00f978c6575cb934c5c942&chksm=e886744edff1fd58ebb593338484013442c8c2f481497d6c5246cabce3ad43c1ee28299f9098&scene=27#wechat_redirect) -11、[情人节来了,教你个用 Python 表白的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485701&idx=1&sn=ef7f0b83e60f397c1839259b275575bf&chksm=e88669e7dff1e0f1317ec429cf9e70442bb3a08ec46efe9a0d06f4ec9847574ea30b53904670&token=1148998814&lang=zh_CN#rd) +5、[三张图彻底搞懂 iptables 和 netfilter](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=1&sn=44856bba34b906930780535a7000a5af&chksm=e88674f4dff1fde263c119bb66a5fa879e6c07b12d50035552dbd529757bedc45a3a41823013&scene=27#wechat_redirect) + +6、[tcpdump / wireshark 抓包及分析](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488505&idx=2&sn=5e7d7a204c9eef4537fd352090086685&chksm=e886731bdff1fa0d3ba2c9a951fdbd849858a58ea630adab247f3b8b3ff7e76d7de022a8270d&scene=27#wechat_redirect) + +7、[100 个网络基础知识,看完成半个网络高手](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488490&idx=1&sn=a8725e200b3ac351ab4f0a2b6d112199&chksm=e8867308dff1fa1e1d55c89b691394902d5c7f8965063acd8551ad14269135d603f7bef22281&scene=27#wechat_redirect) + +8、[网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&scene=27#wechat_redirect) + +9、[网络知识扫盲:一篇文章搞懂 DNS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487859&idx=1&sn=55c2c40b807a6bd7036763e99954f063&chksm=e8867191dff1f88743bf88b809471926245a0ec3ef9c9d2c9f311d99533ed5656b7b3617431c&scene=27#wechat_redirect) -## 07. 实用工具 +10、[路由器里的广告秘密](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=2&sn=4cf42aeef93396f7665a06b2d1dc7fba&chksm=e886768edff1ff98bbd9572d85ddd97ef91ad8d19fdd951c15520cd9756d2c40a53d7cd8111c&scene=27#wechat_redirect) -### 7.1 Linux +11、[10张动图,让你搞懂计算中很重要的名词](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487331&idx=2&sn=70146e780ca63c55ca3446a5e238b487&chksm=e8866f81dff1e6974610866f756b099e0b8bed5389cfa594f4b94b6092ae31db305b1485aadd&scene=27#wechat_redirect) + +## 06. 实用工具 + +### 6.1 Linux 1、[值得收藏的 14 个 Linux 下 CPU 监控工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485356&idx=1&sn=9dc2f440299ad179e1874febdffa04d6&scene=21#wechat_redirect) @@ -358,7 +555,17 @@ 3、[相见恨晚的15个 Linux 神器,你可能一个都没见过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484887&idx=1&sn=3473df33893a23b4de1e176985a5265e&scene=21#wechat_redirect) -### 7.2 Git +4、[这 22 款 CLI工具,每一个都是精品](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491148&idx=1&sn=92b194ad574e8b793ebb608319c7d1df&chksm=e8867eaedff1f7b8e257ce6e80890cef6aa72b9078d20a0e1f0241a1c05f660c69891b6a7d12&scene=27#wechat_redirect) + +5、[Github 热榜项目:如何让你的终端酷炫到没朋友](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488641&idx=2&sn=c8ea38a8e216f123ca320d3ec8449bc0&chksm=e8867463dff1fd7522e9556f3ca97bb691ea92625ca664919dddabd35ead37dbd06a16b75b17&scene=27#wechat_redirect) + +6、[如何在1s内创建上百G的超大文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487906&idx=1&sn=a892ae3f3ab0ed0a2a20cf5d51ff1c65&chksm=e8867140dff1f85674d8c01df3c3d4a92cc75cdd34c68c3b86f8c5eb660f500f033ed35deef8&scene=27#wechat_redirect) + +7、[千万不要运行的 Linux 命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486845&idx=2&sn=31c1ba636db4d687b16b10981833bfbc&chksm=e8866d9fdff1e48976cab2cb62e3b4a4e2e373aeaa0e961847e49cd13ec4ea417b5162fa165b&scene=27#wechat_redirect) + +8、[不想装系统?这8个网站让你在线体验 Linux](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486303&idx=1&sn=fc6c9b73fcb12c5c0c8257e223c8f657&chksm=e8866bbddff1e2ab5edf8b3d4783bcc65975c0385c140d1c954e7d4949e334b9d6ed92ae98a2&scene=27#wechat_redirect) + +### 6.2 版本管理 1、[关于 Git 和 GitHub,你所不知道的十件事](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485232&idx=2&sn=c3314e79af9029bdd76ae187f7b4eb19&scene=21#wechat_redirect) @@ -372,27 +579,57 @@ 6、[Git 中冷门却又非常需要的高级用法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485890&idx=2&sn=528df28585ec16114324e391dbfebab4&chksm=e8866920dff1e036d5da7f5dfe3c944ef8b13d72f022d72b209117120c7e27ebb71036b99893&token=2013245174&lang=zh_CN#rd) -### 7.3 MySQL +7、[别乱提交代码了,来围观下大厂的 Git 提交规范](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486877&idx=1&sn=f14a8add6cee89f8312c7077e4e1ed22&chksm=e8866d7fdff1e4691e6b3627fdd7963942d4d568a1e1ba1d7e8ba81944e6afaadc6923429006&scene=27#wechat_redirect) + +8、[盘点提高国内访问 Github 的速度的 9 种方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491802&idx=2&sn=30df1c954dd8505639c6cd71c0e37d76&chksm=e8858038dff2092eed0b603635b648f2c52d720e98536aee1d50b774c9feac6b25fec6ca9a7c&scene=27#wechat_redirect) + +9、[工作流一目了然,看小姐姐用动图展示10大Git命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486154&idx=1&sn=9a409d493eb789c66cb5c3596195582c&chksm=e8866a28dff1e33e13148dbb98dd8a3899392481f4806805bc96de3b6dc7a954ef3eb899bace&scene=27#wechat_redirect) + +10、[如何用 GitHub Actions 写出高质量的 Python代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485985&idx=2&sn=ec7a102012096847b8fdf7156d9260f3&chksm=e8866ac3dff1e3d53d2ae587e0769b1c42ab0176a7982751fc9a740ae45fe1a592691f373dcc&scene=27#wechat_redirect) + +### 6.3 数据库 1、[写给程序员的 MySQL 面试高频 100 问](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485321&idx=1&sn=30432df24d4e7858a475192bcf233914&scene=21#wechat_redirect) 2、[开发人员必学的几点 SQL 优化点](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485892&idx=1&sn=e0b526002a5568391e75fb0cac60ab0b&chksm=e8866926dff1e0303f2717e95ff801c2550d97812c1a775cc232d48183af802db1be54f26105&token=2013245174&lang=zh_CN#rd) -### 7.4 Chrome +3、[再见,Navicat!同事安利的这个 PyCharm 的兄弟,真的香!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=1&sn=754ca33e3ddbde88ee1b7b918e89d39c&chksm=e886791adff1f00c3cd0ac513cae55ac3bbb2732c637433d15bf42c22139dd54ee47f399b492&scene=27#wechat_redirect) + +4、[太牛逼了!一款软件几乎可以操作所有的数据库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=1&sn=b83bab19f921bcd104caf3ed0f1cf0e3&chksm=e8858311dff20a070cd8063efbca76c74439b18f6c8863f3da14621afb082d246f204ccdba92&scene=27#wechat_redirect) + +### 6.4 Chrome 1、[推荐 8个 超实用的 Chrome 插件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484925&idx=1&sn=110e4da78a20e8b5721f00bb36ba9d4c&scene=21#wechat_redirect) 2、[没有这 42 款插件的Chrome是没有灵魂的](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485016&idx=1&sn=ba38963413ee2926f6f5e69b1427491d&scene=21#wechat_redirect) -### 7.5 其他工具 +### 6.5 Windows + +1、[没钱买 mac?一招教你如何让 Windows 秒变 macOS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=2&sn=d82c0faaf39c420729fba238c65d2fc8&chksm=e8858366dff20a70abbabb14693f265d7fdb1c831cd2b464c9cf1ce8431feb4f07cbb76627ec&scene=27#wechat_redirect) + +2、[微软太良心,这么强大的软件竟然完全免费!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=1&sn=80c8c0247f9100e29f7066c874400755&chksm=e88581e3dff208f503da7dafdee037e8db578186dbef79df648c054f54c6e86b9336ff86bde3&scene=27#wechat_redirect) + +3、[双系统的日子结束了:Windows和Linux将合二为一](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491306&idx=1&sn=dfdbc7e02b54622c2eed5f36501444f0&chksm=e8867e08dff1f71e02038aa720fdd99c524adbc6687c2f32070af638b713a154f0f9767205cc&scene=27#wechat_redirect) + +4、[这个只有1.5M的软件,能让你的网速快3倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488856&idx=1&sn=8a96c373b04c4ce7d78c7f87f5928c1e&chksm=e88675badff1fcac0086cb2ec20845821d2441d0909b9a3395b249744217944b1fb8f3e442c9&scene=27#wechat_redirect) + +### 6.6 其他工具 1、[整理了 11 个好用的代码质量审核和管理工具](https://mp.weixin.qq.com/s/DH_TOA3Cr3y37yZ5F4bU5A) 2、[11 个最佳的 Python 编译器和解释器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485682&idx=1&sn=31b1b68432ca1f1db3866fb0f670270b&chksm=e8866810dff1e106a21c63c1ee198069223047afc72482c1f14ee4f473211aca85273c57d529#rd) -## 08. 代码优化 +3、[太酷了!用上这 5 款神器后,你写的代码逼格瞬间爆表](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486248&idx=1&sn=ff17f64475d82e99d32e35f290a49edd&chksm=e8866bcadff1e2dc2ba61ca6a0f63fd753d60e77891ee0ae93207890ee891a9097a6d27b5695&scene=27#wechat_redirect) + +4、[我最喜欢的云 IDE 有哪些?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485965&idx=1&sn=3457ed667d64773bd83bdc3432c06dc7&chksm=e8866aefdff1e3f9407dce655688e2899c177c17a6472b0d50c15e2454cd6450165aee81877d&scene=27#wechat_redirect) + +5、[整理了6个好用的程序员开发在线工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489186&idx=2&sn=6099d58947654ddb35b43d7067d574d6&chksm=e8867640dff1ff56fb33df9d0de1ea820f14f3690d11184b1fcf02fa9480450a0b5639a754df&scene=27#wechat_redirect) + +6、[Github上这些可视化面板也太好看了吧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488270&idx=1&sn=459867f1761d29e4a46f2e450e3ea975&chksm=e88673ecdff1fafaa0f1f89c9739b928e0227ac54059d4ff96c15faf8b88cd3752a929e018e1&scene=27#wechat_redirect) -### 8.1 算法讲解 +## 07. 代码优化 + +### 7.1 算法讲解 1、[策略模式:商场促销活动背后的代码哲学](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484937&idx=1&sn=61b81dddfb20d80060fee5f22ab28d5f&scene=21#wechat_redirect) @@ -408,17 +645,48 @@ 7、[ 用Python手写十大经典排序算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485494&idx=1&sn=1580e9fc17086bc83e30b0fa4c850bd8&chksm=e88668d4dff1e1c27a26b31fda2f0e413be83519aa9dd0e3c8d3cfd08a2478eb2596ed7ba535#rd) -### 8.32设计模式 +### 7.2设计模式 1、[单例模式告诉你只能娶一个老婆](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484921&idx=1&sn=493d0a492277ecb223ddfff9de8720ff&scene=21#wechat_redirect) -### 8.3 代码美化 +### 7.3 代码优化 1、[看了同事的代码,我忍不住写了这份代码指南](https://mp.weixin.qq.com/s/goqj1YVdKV171LLpjtXruQ) 2、[怎样才能写好一个 Python 函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485416&idx=1&sn=85b14857e795a92985230ba3f51b73f6&chksm=e886670adff1ee1c7f4266d4e3b931d2e3d1de4dff30dc869eae2cd174da7f54080787f60a1b#rd) -## 09. Python 冷知识 +3、[三个异常处理的好习惯](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484947&idx=1&sn=2bd31279dd9714045ed4dc5481b44208&scene=21#wechat_redirect) + +4、[超实用的 30 段 Python 案例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484866&idx=1&sn=9c85dd1b58f319bb0339e2db577a0be6&scene=21#wechat_redirect) + +5、[删除系统 Python 引发的惨案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485386&idx=1&sn=59eed0894159f4d0599be3fb787b5201&chksm=e8866728dff1ee3ef9fb7a0464df481306be11d9aeba944804e7ea74e660501d42e159ada7fc#rd) + +6、[提速72倍,在Python里面调用Golang函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492536&idx=2&sn=360f13b4678fcc6a6f14ee6221474285&chksm=e885835adff20a4ca2025c926c754b9bf6accc6b5b7021f97878163491122ea97d2721c279f6&scene=27#wechat_redirect) + +7、[这一行代码,能让你的 Python运行速度提高100倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492321&idx=1&sn=87dc6b52a0c63b8326bcc50b2c7b3a68&chksm=e8858203dff20b15d1e4730e2851d7523c30db9b404fdfa5b1b19978a1e326e2205e3d6bf7ac&scene=27#wechat_redirect) + +8、[三行Python代码,让数据处理速度提高2到6倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491111&idx=2&sn=d27c6398b82749a20c46490b8fdd900f&chksm=e8867ec5dff1f7d3e9f95f221a2f33003bec76da1f0c002f97a59c7887cf460c887e1ad5a579&scene=27#wechat_redirect) + +9、[一文教你节省 90% 的内存占用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489739&idx=1&sn=6589629f52a3a6ec18e67a3ca4952616&chksm=e8867829dff1f13fa87b08ecd09ce319dbd955c8ee15b44c60509e171f1920a6da600075dcd0&scene=27#wechat_redirect) + +10、[写出漂亮 Python 代码的 20条准则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488402&idx=1&sn=231c2954b0ccca44a18268ab80ddbe88&chksm=e8867370dff1fa66a12d6af4adb66a1d80cbccbf9093c3a8b966a406bf5d8fc005a08166ef45&scene=27#wechat_redirect) + +11、[阅读优秀的项目代码,几点实用的经验分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487922&idx=2&sn=c330d9b4c7bbc1b838e8a9fe90283c27&chksm=e8867150dff1f846628f181ff9687ebf91ba5d06f7721a67bf746698c6f02ea398cdc33c5a0d&scene=27#wechat_redirect) + +12、[一行 Python 代码实现并行](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487299&idx=2&sn=5dffb95993b6fd87c6a3dd1a56b56814&chksm=e8866fa1dff1e6b78239ffa2fe8d124e4ee3af2b7b71f8e9593bdeae2221fcd2afa2b5773e26&scene=27#wechat_redirect) + +13、[代码被反编译了?这两个小技巧能帮到你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487287&idx=1&sn=05c7ca030257db25623e98ed5d302878&chksm=e8866fd5dff1e6c3219dc6d78717411f716bfa38d89a34ed64a88c0702765258fd34b4edd428&scene=27#wechat_redirect) + +14、[一份可以令 Python 变快的工具清单](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486107&idx=1&sn=83a3f95fea5cb2c24d7f44bf11f2acdb&chksm=e8866a79dff1e36ff4e6c8c546568bca0a36ea3de0550bd87a785ed9ce5cbda4a6d84b5b5fc9&scene=27#wechat_redirect) + +15、[记一次 Python Web 接口优化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486053&idx=1&sn=862e6fbaa8a85a4a10ee35d9eba00196&chksm=e8866a87dff1e391399c252ffcb3168ef1f4c85d5ea52d6f43ceaf81e3f7cb3e548f6de505df&scene=27#wechat_redirect) + +16、[翻车了!pyc 文件居然曝光了我的密码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=1&sn=eed309620be1092cf5af030be6b354ff&chksm=e8867010dff1f90611e155156075c2479bab5c9237e8a514a2c7ffe2a317ba16baa30b76d9c9&scene=27#wechat_redirect) + + +## 08. 冷技巧集锦 + +### 8.1 冷知识 1、[Python那些不为人知的冷知识(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485031&idx=1&sn=e5ab670ce6485a50c4b7eeaee11e6f34&scene=21#wechat_redirect) @@ -434,7 +702,82 @@ 7、[Python那些不为人知的冷知识(七)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect) -## 10. 云计算 +8、[Python 之父为什么嫌弃 lambda 匿名函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492291&idx=2&sn=3cfdde7bc5e7b55aae00a30c63abe562&chksm=e8858221dff20b3719dfed632d181b95599b843006ae0c2186b5c69593ee7c22c5a5d2c82509&scene=27#wechat_redirect) + +9、[Python 到底是强类型语言,还是弱类型语言?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491532&idx=1&sn=a86bccc1d270390331c36205d3e46497&chksm=e8867f2edff1f6388d80289bff567445c2c9c9414efec74e4a308f7e8a1cabf662c8fc0a6cc0&scene=27#wechat_redirect) + +10、[Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489219&idx=2&sn=0dfa7ae7d4dcdda538b35bbea80b34e4&chksm=e8867621dff1ff372969bf3901835e67a1bfa7cb72709b491987fda3daa0e5f8d15a86c2b6b5&scene=27#wechat_redirect) + +11、[Python 为什么没有 main 函数?为什么我不推荐写 main 函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489175&idx=2&sn=2f8f9fb4b31c91ef71d1f34841da6450&chksm=e8867675dff1ff63e6e1f28eec9f18c41c30c050118eb65cde6960e5f6ee5866d7515f2b0040&scene=27#wechat_redirect) + +12、[Python 列表的这 8 个实用技巧,你都 OK 么?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=2&sn=38d3985803cb5b6ed10e4cc11972eff7&chksm=e88676eadff1fffc88a3a3a2e3815b2abff43318c597344857c9c5f7806b6b0e6c457b33842d&scene=27#wechat_redirect) + +13、[Python 为什么不用分号作终止符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488816&idx=2&sn=6bc8aa32d9a266cbffabcd87664e75fd&chksm=e88675d2dff1fcc4e845a90903bda5dfe59262a2b60533045a03e574076e92aa6f30974a73f5&scene=27#wechat_redirect) + +14、[Python 为什么推荐蛇形命名法?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488517&idx=1&sn=8b449c2d7c831600be6c5d57e9cb0837&chksm=e88674e7dff1fdf1ab8092ac3cdd2f3978ee6b219daa18a5407c390f62e11448c39455437bb0&scene=27#wechat_redirect) + +15、[Python 为什么用 # 号作注释符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=1&sn=820a070f05096dbe2bdb979fc9492cdb&chksm=e886789bdff1f18d420ee3efa8f85468303690232c4005f775191b0a84a1921430e37aa7e74f&scene=27#wechat_redirect) + +16、[!/usr/bin/python与#!/usr/bin/env python的区别](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486008&idx=2&sn=8637cdc12799f084cd5ecdba9534fb76&chksm=e8866adadff1e3cc49b369dddfaeecc1d9b51f13de1f2733266b2f9fc8426d9e3944a15eb074&scene=27#wechat_redirect) + +17、[pip install 和conda install有什么区别吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487164&idx=2&sn=18de23de9c79f3c20e5a432bb675c262&chksm=e8866e5edff1e748cb6c83d2f4368cbab805fd7bfd41820b60da22061818e8d84548ac14714c&scene=27#wechat_redirect) + +18、[Python 什么情况下会生成 pyc 文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486071&idx=2&sn=533257b9b6156bd5588199b349266a3d&chksm=e8866a95dff1e383991b311dc73994ca103c380433a620c5bdf9f8d09b1c49daa74dce6ea941&scene=27#wechat_redirect) + + +### 8.2 冷技巧 + +1、[让Python中类的属性具有惰性求值的能力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485759&idx=2&sn=e32b66c4666f96f84572738549a47a24&chksm=e88669dddff1e0cb0a4b6e6e965c9570b273236fa58a54351ae3b118d6a4bcbd52e452109a63&token=1148998814&lang=zh_CN#rd) + +2、[ 小技巧:如何在 Python 中实现延迟调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485689&idx=2&sn=43e35ff93918ef090480672d4685bce3&chksm=e886681bdff1e10dd0edee95c73a977da51fdeb514dcf8c3bb88f7f9aacc22ce155f45e84268#rd) + +3、[整理了 18个 Python 高效编程技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491418&idx=1&sn=89c35f5d69a54196bd7f32a64171e05d&chksm=e8867fb8dff1f6aee18414c89c31ce118550ba11b645e2e4f6014af4d15d319decc000a70c91&scene=27#wechat_redirect) + +4、[Python 程序报错崩溃后,如何倒回到崩溃的位置?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488434&idx=1&sn=e2b4ca12b228c16e944ff36abec53e22&chksm=e8867350dff1fa46ee7799e460d2b304dbb0c227d33a39d18099761c205319d99a9b4135f9fa&scene=27#wechat_redirect) + +5、[如何用简单的位操作实现高级算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=1&sn=5376f42c8990027132deb52a6b66307e&chksm=e8866e4ddff1e75b3af85905177130d23118254d42e9452930cc784b209cdf294d9bb73aa247&scene=27#wechat_redirect) + +6、[如何限定Python函数只能被特定函数调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490777&idx=2&sn=6572f957117e0a0256da2b85c7221a37&chksm=e8867c3bdff1f52d045cadb962a2da16b015d8455147afe374cf3067dd89c580e1f9514d57d0&scene=27#wechat_redirect) + +7、[pylint 除了检查代码风格,居然还能...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491737&idx=2&sn=f6781d158c5af171f1b916ee7b0c7a68&chksm=e885807bdff2096dc1580abbba594ed1cc1c0994c06f6bd5ede233df0593a43842f7af3e2f8b&scene=27#wechat_redirect) + +8、[让Python在退出时强制运行一段代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486207&idx=2&sn=5586a204b9537d37c02b9926a01719dc&chksm=e8866a1ddff1e30bb77d72ba0013d870f80e0029546729f0e8c65512c4969f5f0733f60ecddf&scene=27#wechat_redirect) + +9、[不使用 if-elif 语句,如何优雅地判断某个数字所属的等级?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486167&idx=2&sn=a31cbf6d3526bfc860b0daa69889193f&chksm=e8866a35dff1e323ed958858223dda1af6ae0d371be0037c35f9ae6894e776dec1ee53c2a00b&scene=27#wechat_redirect) + +10、[涨姿势了,raise...from... 是个什么操作?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486001&idx=1&sn=b9a89305dae7d4f57105be1d274fae43&chksm=e8866ad3dff1e3c51b5fa132147b9f9c9e4ba14b41c5409e62e2f2696d1389c6ac4ddd1e7bec&scene=27#wechat_redirect) + +11、[一些我日常使用的 Python 技巧分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488382&idx=2&sn=f2b090fe47bb56e0463126c69726bf10&chksm=e886739cdff1fa8a65ce4869a8bdb8fbd28396e3fbcdac2a4441b7c58ba5c77cea1765858146&scene=27#wechat_redirect) + +12、[5年 Python 功力,总结了 10 个开发技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488243&idx=1&sn=66d2f534ddcbed582e7ba734b53701f5&chksm=e8867211dff1fb07f2edcebfda9e7863ee57266c15d4276bc89c2ee69baafc46fbc0e85fe703&scene=27#wechat_redirect) + +13、[我发现了个 Python 黑魔法,执行任意代码都会自动念上一段 『平安经』](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490548&idx=1&sn=c71772cc40992d8912fd8a3fa1607038&chksm=e8867b16dff1f20081431362c99fb981d0cd75f2bd5baf981b7db18e08492138aab10f98efb8&scene=27#wechat_redirect) + +14、[实用技巧分享:如何批量更新已安装的库?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487862&idx=2&sn=a1c092467b471db82060b395c341be6c&chksm=e8867194dff1f882da87863f4d4a4ccbd687daa0b4a742dc6d01fd01d0a6ca6c5fb7cc28629b&scene=27#wechat_redirect) + +### 8.3 炫技操作 + +1、[Python 炫技操作(01):条件语句的七种写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485991&idx=1&sn=98e17f91d2f66f62c9aa644c03d8259d&chksm=e8866ac5dff1e3d3f6810a3f9e388d46d95945af9748baea0380ecec83c768eaac09738cf959&scene=27#wechat_redirect) + + +2、[Python 炫技操作(02):合并字典的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&scene=27#wechat_redirect) + +3、[Python 炫技操作(03):连接列表的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486132&idx=1&sn=3827cf8672d4121b77225dbf9d377d69&chksm=e8866a56dff1e34094f2f834a613800480c0cdb078ab5656640fcb8c445ffa020b175b0f55cb&scene=27#wechat_redirect) + +4、[Python 炫技操作(04):海象运算符的三种用法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486158&idx=1&sn=0bd0647702a1599082e5bf1710d89e37&chksm=e8866a2cdff1e33ab4465d88497ea3505ad8fe614365eec7592a8c75053a6c8218206807c1a3&scene=27#wechat_redirect) + +5、[Python 炫技操作(05):花式导包的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486799&idx=1&sn=1be136d4e45c1b6f8a5eda567aaf259d&chksm=e8866daddff1e4bbddc0164ae795d6c302e36226cb3ba76ffc57e5c993b0861ef4ad693c2a56&scene=27#wechat_redirect) + +6、[Python 炫技操作(06):判断是否包含子串的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=2&sn=4596043fb72b75a04a778942c61cc2af&chksm=e8867e81dff1f7979e21f58716b3ce3e09f8515829b15acd5169dd3f160e302d617a1b94cb2d&scene=27#wechat_redirect) + +7、[最全总结:把模块当做脚本来执行的 7 种案例及其原理](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490904&idx=1&sn=ca3725510c7510965cbc76bd1bf06949&chksm=e8867dbadff1f4acb08c0daf08272ca61757909b0b73a4cde1aa4d86965c149c40f44417f7d0&scene=27#wechat_redirect) + +8、[这个 Python 炫技操作千万不要用,别问我怎么知道的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=1&sn=27d55a4b8b717450f1ab0596263f453f&chksm=e8867e81dff1f7975674ab7fcc0019654ae0e867db045fb575623f91cb143801d03aa365be2d&scene=27#wechat_redirect) + +9、[涨见识了,在终端执行 Python 代码的 6 种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488283&idx=1&sn=24a78834ec3434a90ca7c3e6e972495c&chksm=e88673f9dff1faef908c43a41133f65695a68579e0edb2d7b15f131d9be26bcf919d66a7d9e3&scene=27#wechat_redirect) + + +## 9. 云计算 1、[一份面向初学者的云计算通识指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484908&idx=1&sn=3085e048da685440408e1bdc54feb4aa&scene=21#wechat_redirect) @@ -442,7 +785,7 @@ 3、[超详细教你如何阅读 OpenStack 源码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485899&idx=1&sn=7a578a553c67e794ef6ec2f463864a46&chksm=e8866929dff1e03fa8152751967106e9262980db6afdff106fe9e39854d24d25494b3e8a3a4e&token=2013245174&lang=zh_CN#rd) -## 11. 职场相关 +## 10. 职场相关 1、[一个专科生的 Python 转行之路](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485012&idx=1&sn=751796e63a30d84be01486619b5fb806&scene=21#wechat_redirect) @@ -454,6 +797,23 @@ 5、[Python 从业十年是种什么体验?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485601&idx=1&sn=0dbebc02002ab01a8cd42265822ff81c&chksm=e8866843dff1e15509f2a9d2cd551b2360607cf88e73e5d3036388716ee57949d726dcea209c#rd) +6、[40条提升编程技能的小妙招](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489765&idx=2&sn=79287da847c25ce0d72db6a33eb0c5a0&chksm=e8867807dff1f111043fe22bbfc776a52331c28abf85c2f658cbbaba3235c4e9d3c070c44d8c&scene=27#wechat_redirect) + +7、[我,一个靠GitHub打赏谋生的码农,年入十万美元](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=1&sn=56f51b946908ed89a58b311fc77f47d6&chksm=e8867544dff1fc52e30288685c89e86cefd063d5d37f5b1aba5fb30fd3665cf18346533cad96&scene=27#wechat_redirect) + +8、[5 年 Python 的我,总结了这 90 条写 Python 程序的建议](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487323&idx=1&sn=1b6594d63319faaadefad1f5e178235f&chksm=e8866fb9dff1e6af732304c8f3645cecca8b4bfa4158e45b0ea6c5167c2675bd748ce23e8239&scene=27#wechat_redirect) + +9、[Python这么慢,为啥大公司还在用?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=1&sn=55dda8a6a10429b3a7016393bc058493&chksm=e886768edff1ff981d67b2ecfe52ebf5b8dbc0085289e6bd3f8a6c6631ee6fc4a633fb6bc4b8&scene=27#wechat_redirect) + +## 11. 明哥的作品 + +1、[太赞了!《Python 黑魔法指南》终于面世了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486834&idx=1&sn=f5b94c3a520624786162f78246e60246&chksm=e8866d90dff1e486a3dae97aaf347e83834c8cf204849e5f9ae23dc9fdf0c470f97e0298cf74&scene=27#wechat_redirect) + +2、[《Python黑魔法指南》全新版本 v2.0 上线发布](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490543&idx=1&sn=449a1c7adfafdd5cf37874ed67620e89&chksm=e8867b0ddff1f21b9d622ba7a53b35143cd7fbb3da0d7c091b3d2cdd71d817a4397a99fa424e&scene=27#wechat_redirect) + +3、[明哥写了两个月,这 200页《PyCharm 中文指南》 终于可以发布了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491716&idx=1&sn=76a9f3ad5163a9ffdbaf0fefd0259261&chksm=e8858066dff20970fa9988675154fff6a6f84793a80b8ea1d6fe5d5bbb5bc816d2feabc50664#rd) + + --- From 4eded8f5f6dbbdae16523865b68f25e1295c97be Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 11 Oct 2020 23:42:52 +0800 Subject: [PATCH 130/147] update --- source/c01/c01_06.md | 4 +- source/c01/c01_06.rst | 6 +- source/c02/c02_01.md | 19 +------ source/c02/c02_01.rst | 40 ++++--------- source/c02/c02_02.md | 4 +- source/c02/c02_02.rst | 7 +-- source/c02/c02_03.md | 2 +- source/c02/c02_03.rst | 2 +- source/c02/c02_04.md | 118 +++++++++++++++++++++++++++++++++++++-- source/c02/c02_04.rst | 127 +++++++++++++++++++++++++++++++++++++++--- source/c02/c02_05.md | 116 +------------------------------------- source/c02/c02_05.rst | 119 +-------------------------------------- source/c02/c02_06.md | 4 -- source/c02/c02_06.rst | 3 - 14 files changed, 261 insertions(+), 310 deletions(-) diff --git a/source/c01/c01_06.md b/source/c01/c01_06.md index 69e7887..ab2a7a2 100644 --- a/source/c01/c01_06.md +++ b/source/c01/c01_06.md @@ -1,4 +1,4 @@ -# 1.6 深入理解元组存在的意义 +# 1.6 有了列表,为什么 Python 还有元组? ![](http://image.iswbm.com/20200602135014.png) @@ -97,7 +97,7 @@ print(Xiamen_dict) # OrderedDict([('name', 'Xiemen'), ('country', 'China'), ('polulation', '40,54'), ('coordinates', LatLong(lat=24.26, long=118.03))]) ``` -总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。 +总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。(以上都是个人看法,如有不同见解,欢迎留言讨论) -------------- diff --git a/source/c01/c01_06.rst b/source/c01/c01_06.rst index 3a1df17..2609574 100755 --- a/source/c01/c01_06.rst +++ b/source/c01/c01_06.rst @@ -1,5 +1,5 @@ -1.6 深入理解元组存在的意义 -========================== +1.6 有了列表,为什么 Python 还有元组? +====================================== |image0| @@ -102,7 +102,7 @@ Python中有一个基础的数据结构,叫做元组(tuple),但是一般 print(Xiamen_dict) # OrderedDict([('name', 'Xiemen'), ('country', 'China'), ('polulation', '40,54'), ('coordinates', LatLong(lat=24.26, long=118.03))]) -总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。 +总结一下,元组是一种很强大的可以当作记录来用的数据类型,这才是他存在的价值和意义所在。而为人所熟知的,它的第二个角色才是充当一个不可变的列表。(以上都是个人看法,如有不同见解,欢迎留言讨论) -------------- diff --git a/source/c02/c02_01.md b/source/c02/c02_01.md index 7fdee0e..0024d85 100644 --- a/source/c02/c02_01.md +++ b/source/c02/c02_01.md @@ -2,23 +2,6 @@ ![](http://image.iswbm.com/20200602135014.png) ---- - -作为进阶系列的一个分支「`并发编程`」,我觉得这是每个程序员都应该会的。 - -`并发编程` 这个系列,我准备了将近一个星期,从知识点梳理,到思考要举哪些例子才能更加让人容易吃透这些知识点。希望呈现出来的效果真能如想象中的那样,对小白也一样的友好。 - -昨天大致整理了下,这个系列我大概会讲如下内容(后期可能调整): -![课程大纲](https://i.loli.net/2018/05/27/5b0a1523a0730.png) - - -对于并发编程,Python 的实现,总结了一下,大致有如下三种方法: -- 多线程 -- 多进程 -- 协程(生成器) - -在之后的章节里,将陆陆续续地给大家介绍到这三个知识点。 - ## 1. 基本概念 @@ -215,7 +198,7 @@ multi_process(io_simulation, type="模拟IO密集型") 【多进程】-模拟IO密集型花费时间:2.0076842308044434秒 ``` -## 2.1.3 性能对比成果总结 +## 3. 性能对比成果总结 将结果汇总一下,制成表格。 diff --git a/source/c02/c02_01.rst b/source/c02/c02_01.rst index 229a185..3b42f98 100755 --- a/source/c02/c02_01.rst +++ b/source/c02/c02_01.rst @@ -3,21 +3,6 @@ |image0| --------------- - -作为进阶系列的一个分支「\ ``并发编程``\ 」,我觉得这是每个程序员都应该会的。 - -``并发编程`` -这个系列,我准备了将近一个星期,从知识点梳理,到思考要举哪些例子才能更加让人容易吃透这些知识点。希望呈现出来的效果真能如想象中的那样,对小白也一样的友好。 - -昨天大致整理了下,这个系列我大概会讲如下内容(后期可能调整): -|课程大纲| - -对于并发编程,Python 的实现,总结了一下,大致有如下三种方法: - 多线程 - -多进程 - 协程(生成器) - -在之后的章节里,将陆陆续续地给大家介绍到这三个知识点。 - 1. 基本概念 ----------- @@ -41,11 +26,11 @@ - ``多线程``\ ,交替执行,另一种意义上的串行。 -.\ |image2| +.\ |image1| - ``多进程``\ ,并行执行,真正意义上的并发。 -.\ |image3| +.\ |image2| 2. 单线程VS多线程VS多进程 ------------------------- @@ -54,7 +39,7 @@ 首先,我的实验环境配置如下 -|image4| +|image3| **注意** 以下代码,若要理解,对小白有如下知识点要求: @@ -227,12 +212,12 @@ 【多进程】-网络IO密集型花费时间:0.13074755668640137秒 【多进程】-模拟IO密集型花费时间:2.0076842308044434秒 -2.1.3 性能对比成果总结 ----------------------- +3. 性能对比成果总结 +------------------- 将结果汇总一下,制成表格。 -|image5| +|image4| 我们来分析下这个表格。 @@ -250,13 +235,12 @@ -------------- -|image6| +|image5| .. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |课程大纲| image:: https://i.loli.net/2018/05/27/5b0a1523a0730.png -.. |image2| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg -.. |image3| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg -.. |image4| image:: http://image.iswbm.com/20190112205155.png -.. |image5| image:: http://image.iswbm.com/20190112204930.png -.. |image6| image:: http://image.iswbm.com/20200607174235.png +.. |image1| image:: https://i.loli.net/2018/05/08/5af1781dbad7c.jpg +.. |image2| image:: https://i.loli.net/2018/05/08/5af1781f05c29.jpg +.. |image3| image:: http://image.iswbm.com/20190112205155.png +.. |image4| image:: http://image.iswbm.com/20190112204930.png +.. |image5| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/c02/c02_02.md b/source/c02/c02_02.md index e451a67..78af0fb 100644 --- a/source/c02/c02_02.md +++ b/source/c02/c02_02.md @@ -10,7 +10,7 @@ 经过总结,Python创建多线程主要有如下两种方法: -- - 函数 +- 函数 - 类 接下来,我们就来揭开多线程的神秘面纱。 @@ -130,7 +130,7 @@ t.daemon = False t.name = "My-Thread" ``` -至此,Python线程基础知识,我们大概都介绍完了。 + ---- ![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_02.rst b/source/c02/c02_02.rst index 14ee3ea..2c9b6e2 100755 --- a/source/c02/c02_02.rst +++ b/source/c02/c02_02.rst @@ -9,10 +9,7 @@ 经过总结,Python创建多线程主要有如下两种方法: -- - - - 函数 - +- 函数 - 类 接下来,我们就来揭开多线程的神秘面纱。 @@ -143,8 +140,6 @@ # 设置线程名 t.name = "My-Thread" -至此,Python线程基础知识,我们大概都介绍完了。 - -------------- |image1| diff --git a/source/c02/c02_03.md b/source/c02/c02_03.md index 095a76c..519ff92 100644 --- a/source/c02/c02_03.md +++ b/source/c02/c02_03.md @@ -307,7 +307,7 @@ t2.start() ## 6. 饱受争议的GIL(全局锁) -在第一章的时候,我就和大家介绍到,多线程和多进程是不一样的。 +在第一节的时候,我就和大家介绍到,多线程和多进程是不一样的。 多进程是真正的并行,而多线程是伪并行,实际上他只是交替执行。 diff --git a/source/c02/c02_03.rst b/source/c02/c02_03.rst index 03cde42..eea5c95 100755 --- a/source/c02/c02_03.rst +++ b/source/c02/c02_03.rst @@ -335,7 +335,7 @@ lock.release()必须成对出现。否则就有可能造成死锁。 6. 饱受争议的GIL(全局锁) -------------------------- -在第一章的时候,我就和大家介绍到,多线程和多进程是不一样的。 +在第一节的时候,我就和大家介绍到,多线程和多进程是不一样的。 多进程是真正的并行,而多线程是伪并行,实际上他只是交替执行。 diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 643eac3..272b66b 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -21,7 +21,7 @@ --- -## 2.4.1 Event事件 +## 1. Event事件 Python提供了非常简单的通信机制 `Threading.Event`,通用的条件变量。多个线程可以`等待某个事件的发生`,在事件发生后,`所有的线程`都会被`激活`。 @@ -93,7 +93,7 @@ Thread: 3 finish at Sun May 13 20:38:13 2018 可见在所有线程都启动(`start()`)后,并不会执行完,而是都在`self.event.wait()`止住了,需要我们通过`event.set()`来给所有线程发送执行指令才能往下执行。 -## 2.4.2 Condition +## 2. Condition Condition和Event 是类似的,并没有多大区别。 @@ -167,7 +167,7 @@ hider: 我赢了 seeker: 被你找到了,哎~~~ ``` -## 2.4.3 Queue队列 +## 3. Queue队列 最后一个,队列,它是本节的重点,因为它是我们日常开发中最使用频率最高的。 @@ -320,7 +320,117 @@ teacher.call("exit") 要理解这个过程,请参考 http://python.iswbm.com/en/latest/c02/c02_06.html 里自定义线程池的的例子。 -## 2.4.4 总结一下 +## 4. 消息队列的先进先出 + +消息队列可不是只有`queue.Queue`这一个类,除它之外,还有`queue.LifoQueue`和`queue.PriorityQueue`这两个类。 + +从名字上,对于他们之间的区别,你大概也能猜到一二吧。 + +> `queue.Queue`:先进先出队列 +> `queue.LifoQueue`:后进先出队列 +> `queue.PriorityQueue`:优先级队列 + +先来看看,我们的老朋友,`queue.Queue`。 +所谓的`先进先出`(FIFO,First in First Out),就是先进入队列的消息,将优先被消费。 +这和我们日常排队买菜是一样的,先排队的人肯定是先买到菜。 + +用代码来说明一下 + +```python +import queue + +q = queue.Queue() + +for i in range(5): + q.put(i) + +while not q.empty(): + print q.get() +``` + +看看输出,符合我们先进先出的预期。存入队列的顺序是`01234`,被消费的顺序也是`01234`。 + +``` +0 +1 +2 +3 +4 +``` + +再来看看`Queue.LifoQueue`,后进先出,就是后进入消息队列的,将优先被消费。 + +这和我们羽毛球筒是一样的,最后放进羽毛球筒的球,会被第一个取出使用。 + +用代码来看下 + +```python +import queue + +q = queue.LifoQueue() + +for i in range(5): + q.put(i) + +while not q.empty(): + print q.get() +``` + +来看看输出,符合我们后进后出的预期。存入队列的顺序是`01234`,被消费的顺序也是`43210`。 + +``` +4 +3 +2 +1 +0 +``` + +最后来看看`Queue.PriorityQueue`,优先级队列。 +这和我们日常生活中的会员机制有些类似,办了金卡的人比银卡的服务优先,办了银卡的人比不办卡的人服务优先。 + +来用代码看一下 + +```python +from queue import PriorityQueue + +# 重新定义一个类,继承自PriorityQueue +class MyPriorityQueue(PriorityQueue): + def __init__(self): + PriorityQueue.__init__(self) + self.counter = 0 + + def put(self, item, priority): + PriorityQueue.put(self, (priority, self.counter, item)) + self.counter += 1 + + def get(self, *args, **kwargs): + _, _, item = PriorityQueue.get(self, *args, **kwargs) + return item + + +queue = MyPriorityQueue() +queue.put('item2', 2) +queue.put('item5', 5) +queue.put('item3', 3) +queue.put('item4', 4) +queue.put('item1', 1) + +while True: + print(queue.get()) +``` + +来看看输出,符合我们的预期。我们存入入队列的顺序是`25341`,对应的优先级也是`25341`,可是被消费的顺序丝毫不受传入顺序的影响,而是根据指定的优先级来消费。 + +```python +item1 +item2 +item3 +item4 +item5 +``` + +## 5. 总结一下 学习了以上三种通信方法,我们很容易就能发现`Event` 和 `Condition` 是threading模块原生提供的模块,原理简单,功能单一,它能发送 `True` 和 `False` 的指令,所以只能适用于某些简单的场景中。 diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 38ee9d5..d21aa7a 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -20,8 +20,8 @@ threading.Condition - queue.Queue -------------- -2.4.1 Event事件 ---------------- +1. Event事件 +------------ Python提供了非常简单的通信机制 ``Threading.Event``\ ,通用的条件变量。多个线程可以\ ``等待某个事件的发生``\ ,在事件发生后,\ ``所有的线程``\ 都会被\ ``激活``\ 。 @@ -99,8 +99,8 @@ Python提供了非常简单的通信机制 可见在所有线程都启动(\ ``start()``\ )后,并不会执行完,而是都在\ ``self.event.wait()``\ 止住了,需要我们通过\ ``event.set()``\ 来给所有线程发送执行指令才能往下执行。 -2.4.2 Condition ---------------- +2. Condition +------------ Condition和Event 是类似的,并没有多大区别。 @@ -179,8 +179,8 @@ Condition和Event 是类似的,并没有多大区别。 hider: 我赢了 seeker: 被你找到了,哎~~~ -2.4.3 Queue队列 ---------------- +3. Queue队列 +------------ 最后一个,队列,它是本节的重点,因为它是我们日常开发中最使用频率最高的。 @@ -342,8 +342,119 @@ Queue.task_done(),说明队列这个任务已经结束了。 要理解这个过程,请参考 http://python.iswbm.com/en/latest/c02/c02_06.html 里自定义线程池的的例子。 -2.4.4 总结一下 --------------- +4. 消息队列的先进先出 +--------------------- + +消息队列可不是只有\ ``queue.Queue``\ 这一个类,除它之外,还有\ ``queue.LifoQueue``\ 和\ ``queue.PriorityQueue``\ 这两个类。 + +从名字上,对于他们之间的区别,你大概也能猜到一二吧。 + + ``queue.Queue``\ :先进先出队列 ``queue.LifoQueue``\ :后进先出队列 + ``queue.PriorityQueue``\ :优先级队列 + +先来看看,我们的老朋友,\ ``queue.Queue``\ 。 +所谓的\ ``先进先出``\ (FIFO,First in First +Out),就是先进入队列的消息,将优先被消费。 +这和我们日常排队买菜是一样的,先排队的人肯定是先买到菜。 + +用代码来说明一下 + +.. code:: python + + import queue + + q = queue.Queue() + + for i in range(5): + q.put(i) + + while not q.empty(): + print q.get() + +看看输出,符合我们先进先出的预期。存入队列的顺序是\ ``01234``\ ,被消费的顺序也是\ ``01234``\ 。 + +:: + + 0 + 1 + 2 + 3 + 4 + +再来看看\ ``Queue.LifoQueue``\ ,后进先出,就是后进入消息队列的,将优先被消费。 + +这和我们羽毛球筒是一样的,最后放进羽毛球筒的球,会被第一个取出使用。 + +用代码来看下 + +.. code:: python + + import queue + + q = queue.LifoQueue() + + for i in range(5): + q.put(i) + + while not q.empty(): + print q.get() + +来看看输出,符合我们后进后出的预期。存入队列的顺序是\ ``01234``\ ,被消费的顺序也是\ ``43210``\ 。 + +:: + + 4 + 3 + 2 + 1 + 0 + +最后来看看\ ``Queue.PriorityQueue``\ ,优先级队列。 +这和我们日常生活中的会员机制有些类似,办了金卡的人比银卡的服务优先,办了银卡的人比不办卡的人服务优先。 + +来用代码看一下 + +.. code:: python + + from queue import PriorityQueue + + # 重新定义一个类,继承自PriorityQueue + class MyPriorityQueue(PriorityQueue): + def __init__(self): + PriorityQueue.__init__(self) + self.counter = 0 + + def put(self, item, priority): + PriorityQueue.put(self, (priority, self.counter, item)) + self.counter += 1 + + def get(self, *args, **kwargs): + _, _, item = PriorityQueue.get(self, *args, **kwargs) + return item + + + queue = MyPriorityQueue() + queue.put('item2', 2) + queue.put('item5', 5) + queue.put('item3', 3) + queue.put('item4', 4) + queue.put('item1', 1) + + while True: + print(queue.get()) + +来看看输出,符合我们的预期。我们存入入队列的顺序是\ ``25341``\ ,对应的优先级也是\ ``25341``\ ,可是被消费的顺序丝毫不受传入顺序的影响,而是根据指定的优先级来消费。 + +.. code:: python + + item1 + item2 + item3 + item4 + item5 + +5. 总结一下 +----------- 学习了以上三种通信方法,我们很容易就能发现\ ``Event`` 和 ``Condition`` 是threading模块原生提供的模块,原理简单,功能单一,它能发送 ``True`` 和 diff --git a/source/c02/c02_05.md b/source/c02/c02_05.md index 6724712..52d86d8 100644 --- a/source/c02/c02_05.md +++ b/source/c02/c02_05.md @@ -7,7 +7,7 @@ 上一篇我们说,线程与线程之间要通过消息通信来控制程序的执行。 讲完了消息通信,今天就来探讨下线程里的`信息隔离`是如何做到的。 -## 2.5.1 初步认识信息隔离 +## 1. 初步认识信息隔离 什么是`信息隔离`? @@ -73,7 +73,7 @@ if __name__ == '__main__': 所以如果想在当前线程保存一个全局值,并且各自线程(包括主线程)互不干扰,使用local类吧。 -## 2.5.2 信息隔离的意义何在 +## 2. 信息隔离的意义何在 细心的你,一定已经发现了,上面那个例子,即使我们不用`threading.local`来做信息隔离,两个线程`self.getName()`本身就是隔离的,没有任何关系的。因为这两个线程是由一个class实例出的两个不同的实例对象。自然是可以不用做隔离,因为其本身就是隔离的。 @@ -136,116 +136,4 @@ Got 513469 bytes 如果是在这种场景下,要做到线程之间的状态信息的隔离,就肯定要借助`threading.local`,所以`threading.local`的存在是有存在的意义的。其他还有很多场景是必须借助`threading.local`才能实现的,而这些就要靠你们在真正的业务开发中去发现咯。 -## 2.5.3 消息队列的先进先出 - -首先,要告诉大家的事,消息队列可不是只有`queue.Queue`这一个类,除它之外,还有`queue.LifoQueue`和`queue.PriorityQueue`这两个类。 - -从名字上,对于他们之间的区别,你大概也能猜到一二吧。 - -> `queue.Queue`:先进先出队列 -> `queue.LifoQueue`:后进先出队列 -> `queue.PriorityQueue`:优先级队列 - -先来看看,我们的老朋友,`queue.Queue`。 -所谓的`先进先出`(FIFO,First in First Out),就是先进入队列的消息,将优先被消费。 -这和我们日常排队买菜是一样的,先排队的人肯定是先买到菜。 - -用代码来说明一下 - -```python -import queue - -q = queue.Queue() - -for i in range(5): - q.put(i) - -while not q.empty(): - print q.get() -``` - -看看输出,符合我们先进先出的预期。存入队列的顺序是`01234`,被消费的顺序也是`01234`。 - -``` -0 -1 -2 -3 -4 -``` - -再来看看`Queue.LifoQueue`,后进先出,就是后进入消息队列的,将优先被消费。 - -这和我们羽毛球筒是一样的,最后放进羽毛球筒的球,会被第一个取出使用。 - -用代码来看下 - -```python -import queue - -q = queue.LifoQueue() - -for i in range(5): - q.put(i) - -while not q.empty(): - print q.get() -``` - -来看看输出,符合我们后进后出的预期。存入队列的顺序是`01234`,被消费的顺序也是`43210`。 - -``` -4 -3 -2 -1 -0 -``` - -最后来看看`Queue.PriorityQueue`,优先级队列。 -这和我们日常生活中的会员机制有些类似,办了金卡的人比银卡的服务优先,办了银卡的人比不办卡的人服务优先。 - -来用代码看一下 - -```python -from queue import PriorityQueue - -# 重新定义一个类,继承自PriorityQueue -class MyPriorityQueue(PriorityQueue): - def __init__(self): - PriorityQueue.__init__(self) - self.counter = 0 - - def put(self, item, priority): - PriorityQueue.put(self, (priority, self.counter, item)) - self.counter += 1 - - def get(self, *args, **kwargs): - _, _, item = PriorityQueue.get(self, *args, **kwargs) - return item - - -queue = MyPriorityQueue() -queue.put('item2', 2) -queue.put('item5', 5) -queue.put('item3', 3) -queue.put('item4', 4) -queue.put('item1', 1) - -while True: - print(queue.get()) -``` - -来看看输出,符合我们的预期。我们存入入队列的顺序是`25341`,对应的优先级也是`25341`,可是被消费的顺序丝毫不受传入顺序的影响,而是根据指定的优先级来消费。 - -```python -item1 -item2 -item3 -item4 -item5 -``` - ----- - ![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_05.rst b/source/c02/c02_05.rst index 5d54df2..e9038f3 100755 --- a/source/c02/c02_05.rst +++ b/source/c02/c02_05.rst @@ -8,7 +8,7 @@ 上一篇我们说,线程与线程之间要通过消息通信来控制程序的执行。 讲完了消息通信,今天就来探讨下线程里的\ ``信息隔离``\ 是如何做到的。 ## -2.5.1 初步认识信息隔离 +1. 初步认识信息隔离 什么是\ ``信息隔离``\ ? @@ -78,8 +78,8 @@ 所以如果想在当前线程保存一个全局值,并且各自线程(包括主线程)互不干扰,使用local类吧。 -2.5.2 信息隔离的意义何在 ------------------------- +2. 信息隔离的意义何在 +--------------------- 细心的你,一定已经发现了,上面那个例子,即使我们不用\ ``threading.local``\ 来做信息隔离,两个线程\ ``self.getName()``\ 本身就是隔离的,没有任何关系的。因为这两个线程是由一个class实例出的两个不同的实例对象。自然是可以不用做隔离,因为其本身就是隔离的。 @@ -144,119 +144,6 @@ 如果是在这种场景下,要做到线程之间的状态信息的隔离,就肯定要借助\ ``threading.local``\ ,所以\ ``threading.local``\ 的存在是有存在的意义的。其他还有很多场景是必须借助\ ``threading.local``\ 才能实现的,而这些就要靠你们在真正的业务开发中去发现咯。 -2.5.3 消息队列的先进先出 ------------------------- - -首先,要告诉大家的事,消息队列可不是只有\ ``queue.Queue``\ 这一个类,除它之外,还有\ ``queue.LifoQueue``\ 和\ ``queue.PriorityQueue``\ 这两个类。 - -从名字上,对于他们之间的区别,你大概也能猜到一二吧。 - - ``queue.Queue``\ :先进先出队列 ``queue.LifoQueue``\ :后进先出队列 - ``queue.PriorityQueue``\ :优先级队列 - -先来看看,我们的老朋友,\ ``queue.Queue``\ 。 -所谓的\ ``先进先出``\ (FIFO,First in First -Out),就是先进入队列的消息,将优先被消费。 -这和我们日常排队买菜是一样的,先排队的人肯定是先买到菜。 - -用代码来说明一下 - -.. code:: python - - import queue - - q = queue.Queue() - - for i in range(5): - q.put(i) - - while not q.empty(): - print q.get() - -看看输出,符合我们先进先出的预期。存入队列的顺序是\ ``01234``\ ,被消费的顺序也是\ ``01234``\ 。 - -:: - - 0 - 1 - 2 - 3 - 4 - -再来看看\ ``Queue.LifoQueue``\ ,后进先出,就是后进入消息队列的,将优先被消费。 - -这和我们羽毛球筒是一样的,最后放进羽毛球筒的球,会被第一个取出使用。 - -用代码来看下 - -.. code:: python - - import queue - - q = queue.LifoQueue() - - for i in range(5): - q.put(i) - - while not q.empty(): - print q.get() - -来看看输出,符合我们后进后出的预期。存入队列的顺序是\ ``01234``\ ,被消费的顺序也是\ ``43210``\ 。 - -:: - - 4 - 3 - 2 - 1 - 0 - -最后来看看\ ``Queue.PriorityQueue``\ ,优先级队列。 -这和我们日常生活中的会员机制有些类似,办了金卡的人比银卡的服务优先,办了银卡的人比不办卡的人服务优先。 - -来用代码看一下 - -.. code:: python - - from queue import PriorityQueue - - # 重新定义一个类,继承自PriorityQueue - class MyPriorityQueue(PriorityQueue): - def __init__(self): - PriorityQueue.__init__(self) - self.counter = 0 - - def put(self, item, priority): - PriorityQueue.put(self, (priority, self.counter, item)) - self.counter += 1 - - def get(self, *args, **kwargs): - _, _, item = PriorityQueue.get(self, *args, **kwargs) - return item - - - queue = MyPriorityQueue() - queue.put('item2', 2) - queue.put('item5', 5) - queue.put('item3', 3) - queue.put('item4', 4) - queue.put('item1', 1) - - while True: - print(queue.get()) - -来看看输出,符合我们的预期。我们存入入队列的顺序是\ ``25341``\ ,对应的优先级也是\ ``25341``\ ,可是被消费的顺序丝毫不受传入顺序的影响,而是根据指定的优先级来消费。 - -.. code:: python - - item1 - item2 - item3 - item4 - item5 - --------------- - |image1| .. |image0| image:: http://image.iswbm.com/20200602135014.png diff --git a/source/c02/c02_06.md b/source/c02/c02_06.md index 90a9a94..95e51f5 100644 --- a/source/c02/c02_06.md +++ b/source/c02/c02_06.md @@ -139,9 +139,5 @@ running thread-123145485651968:1 -## 2. 进程池的创建 - - - ---- ![](http://image.iswbm.com/20200607174235.png) diff --git a/source/c02/c02_06.rst b/source/c02/c02_06.rst index 4b753e7..78be5e5 100755 --- a/source/c02/c02_06.rst +++ b/source/c02/c02_06.rst @@ -146,9 +146,6 @@ 构建线程池的方法,是可以很灵活的,大家有空可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。 -2. 进程池的创建 ---------------- - -------------- |image1| From 31905d5bc8799055c6c08b8fe2840968678f5e9e Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 11 Oct 2020 23:47:35 +0800 Subject: [PATCH 131/147] change keyword --- source/_static/js/readmore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/_static/js/readmore.js b/source/_static/js/readmore.js index 5d8f2e3..f2a29c2 100644 --- a/source/_static/js/readmore.js +++ b/source/_static/js/readmore.js @@ -41,7 +41,7 @@ var setIdTimer = setInterval(function () { blogId: '15406-1578143418297-890', name: 'Python编程时光', qrcode: 'http://image.iswbm.com/20200104210733.png', - keyword: 'vip' + keyword: 'more' }); } From 907f164c61238443576bc29b29c256a29132ee9f Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 1 Nov 2020 00:07:21 +0800 Subject: [PATCH 132/147] update --- source/c01/c01_07.md | 184 +++++----- source/c01/c01_07.rst | 183 +++++----- source/c02/c02_04.md | 4 +- source/c02/c02_04.rst | 4 +- source/c04/c04_01.md | 11 +- source/c04/c04_01.rst | 11 +- source/c04/c04_03.md | 6 +- source/c04/c04_03.rst | 22 +- source/py_mp_index.md | 821 ------------------------------------------ 9 files changed, 191 insertions(+), 1055 deletions(-) delete mode 100644 source/py_mp_index.md diff --git a/source/c01/c01_07.md b/source/c01/c01_07.md index c4e1e39..f7f78a1 100644 --- a/source/c01/c01_07.md +++ b/source/c01/c01_07.md @@ -1,62 +1,60 @@ # 1.7 15个Pythonic的代码示例 ![](http://image.iswbm.com/20200602135014.png) - ---- - Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。 -要写出 Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,Github 上有很多非常优秀的源代码值得阅读,比如:requests、flask、tornado,这里小明收集了一些常见的 Pythonic 写法,帮助你养成写优秀代码的习惯。 +要写出 Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,这里明哥收集了一些比较常见的 Pythonic 写法,帮助你养成写优秀代码的习惯。 ## 01. 变量交换 -Bad +交换两个变量的值,正常都会想利用一个中间临时变量来过渡。 ```python tmp = a a = b b = tmp ``` -Pythonic +能用一行代码解决的(并且不影响可读性的),决不用三行代码。 ```python a,b = b,a ``` ## 02. 列表推导 -Bad +下面是一个非常简单的 for 循环。 ```python my_list = [] for i in range(10): my_list.append(i*2) ``` -Pythonic +在一个 for 循环中,如果逻辑比较简单,不如试用一下列表的列表推导式,虽然只有一行代码,但也逻辑清晰。 + ```python my_list = [i*2 for i in range(10)] ``` ## 03. 单行表达式 -虽然列表推导式由于其简洁性及表达性,被广受推崇。 +上面两个案例,都将多行代码用另一种方式写成了一行代码。 -但是有许多可以写成单行的表达式,并不是好的做法。 +这并不意味着,代码行数越少,就越 Pythonic 。 -Bad +比如下面这样写,就不推荐。 ```python -print 'one'; print 'two' +print('hello'); print('world') -if x == 1: print 'one' +if x == 1: print('hello,world') if and : # do something ``` -Pythonic +建议还是按照如下的写法来 ```python -print 'one' -print 'two' +print('hello') +print('world') if x == 1: - print 'one' + print('hello,world') cond1 = cond2 = @@ -66,12 +64,12 @@ if cond1 and cond2: ## 04. 带索引遍历 -Bad +使用 for 循环时,如何取得对应的索引,初学者习惯使用 range + len 函数 ```python for i in range(len(my_list)): print(i, "-->", my_list[i]) ``` -Pythonic +更好的做法是利用 enumerate 这个内置函数 ```python for i,item in enumerate(my_list): print(i, "-->",item) @@ -79,7 +77,8 @@ for i,item in enumerate(my_list): ## 05. 序列解包 -Pythonic +使用 `*` 可以对一个列表解包 + ```python a, *rest = [1, 2, 3] # a = 1, rest = [2, 3] @@ -90,7 +89,7 @@ a, *middle, c = [1, 2, 3, 4] ## 06. 字符串拼接 -Bad +如果一个列表(或者可迭代对象)中的所有元素都是字符串对象,想要将他们连接起来,通常做法是 ```python letters = ['s', 'p', 'a', 'm'] s="" @@ -98,7 +97,7 @@ for let in letters: s += let ``` -Pythonic +更推荐的做法是使用 join 函数 ```python letters = ['s', 'p', 'a', 'm'] word = ''.join(letters) @@ -106,54 +105,47 @@ word = ''.join(letters) ## 07. 真假判断 -Bad +判断一个变量是否为真(假),新手习惯直接使用 `==` 与 True、False、None 进行对比 ```python if attr == True: - print 'True!' + print('True!') if attr == None: - print 'attr is None!' + print('attr is None!') ``` -Pythonic +实际上,`""`、`[]`、`{}` 这些没有任何元素的容器都是假值,可直接使用 `if not xx` 来判断。 ```python if attr: - print 'attr is truthy!' + print('attr is truthy!') if not attr: - print 'attr is falsey!' - -if attr is None: - print 'attr is None!' + print('attr is falsey!') ``` ## 08. 访问字典元素 -Bad +当直接使用 `[]` 来访问字典里的元素时,若key不存在,是会抛异常的,所以新会可能会先判断一下是否有这个 key,有再取之。 ```python d = {'hello': 'world'} if d.has_key('hello'): - print d['hello'] # prints 'world' + print(d['hello']) # prints 'world' else: - print 'default_value' + print('default_value') ``` -Pythonic +更推荐的做法是使用 `get` 来取,如果没有该 key 会默认返回 None(当然你也可以设置默认返回值) ```python d = {'hello': 'world'} -print d.get('hello', 'default_value') # prints 'world' -print d.get('thingy', 'default_value') # prints 'default_value' - -# Or: -if 'hello' in d: - print d['hello'] +print(d.get('hello', 'default_value')) # prints 'world' +print(d.get('thingy', 'default_value')) # prints 'default_value' ``` ## 09. 操作列表 -Bad +下面这段代码,会根据条件过滤过列表中的元素 ```python a = [3, 4, 5] b = [] @@ -161,8 +153,7 @@ for i in a: if i > 4: b.append(i) ``` - -Pythonic +实际上可以使用列表推导或者高阶函数 filter 来实现 ```python a = [3, 4, 5] b = [i for i in a if i > 4] @@ -170,69 +161,68 @@ b = [i for i in a if i > 4] b = filter(lambda x: x > 4, a) ``` -Bad +除了 filter 之外,还有 map、reduce 这两个函数也很好用 ```python a = [3, 4, 5] -for i in range(len(a)): - a[i] += 3 -``` - -Pythonic -```python -a = [3, 4, 5] -a = [i + 3 for i in a] -# Or: -a = map(lambda i: i + 3, a) +b = map(lambda i: i + 3, a) +# b: [6,7,8] ``` ## 10. 文件读取 -Bad + +文件读取是非常常用的操作,在使用完句柄后,是需要手动调用 close 函数来关闭句柄的 ```python -f = open('file.txt') -a = f.read() -print a -f.close() +fp = open('file.txt') +print(fp.read()) +fp.close() ``` - -Pythonic +如果代码写得太长,即使你知道需要手动关闭句柄,却也会经常会漏掉。因此推荐养成习惯使用 `with open` 来读写文件,上下文管理器会自动执行关闭句柄的操作 ```python -with open('file.txt') as f: - for line in f: - print line +with open('file.txt') as fp: + for line in fp.readlines(): + print(line) ``` ## 11. 代码续行 -Bad + +将一个长度较长的字符串放在一行中,是很影响代码可读性的(下面代码可向左滑动) + ```python -my_very_big_string = """For a long time I used to go to bed early. Sometimes, \ - when I had put out my candle, my eyes would close so quickly that I had not even \ - time to say “I’m going to sleep.”""" +long_string = 'For a long time I used to go to bed early. Sometimes, when I had put out my candle, my eyes would close so quickly that I had not even time to say “I’m going to sleep.”' +``` + +稍等注重代码可读性的人,会使用三个引号 `\`来续写 -from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \ - yet_another_nice_function +```python +long_string = 'For a long time I used to go to bed early. ' \ + 'Sometimes, when I had put out my candle, ' \ + 'my eyes would close so quickly that I had not even time to say “I’m going to sleep.”' ``` -Pythonic +不过,对我来说,我更喜欢这样子写 使用括号包裹 `()` ```python -my_very_big_string = ( +long_string = ( "For a long time I used to go to bed early. Sometimes, " "when I had put out my candle, my eyes would close so quickly " "that I had not even time to say “I’m going to sleep.”" ) +``` +导包的时候亦是如此 +```python from some.deep.module.inside.a.module import ( a_nice_function, another_nice_function, yet_another_nice_function) ``` ## 12. 显式代码 -Bad + +有时候出于需要,我们会使用一些特殊的魔法来使代码适应更多的场景不确定性。 ```python def make_complex(*args): x, y = args return dict(**locals()) ``` - -Pythonic +但若非必要,请不要那么做。无端增加代码的不确定性,会让原先本就动态的语言写出更加动态的代码。 ```python def make_complex(x, y): return {'x': x, 'y': y} @@ -240,7 +230,7 @@ def make_complex(x, y): ## 13. 使用占位符 -Pythonic +对于暂不需要,却又不得不接收的的变量,请使用占位符 ```python filename = 'foobar.txt' basename, _, ext = filename.rpartition('.') @@ -248,19 +238,20 @@ basename, _, ext = filename.rpartition('.') ## 14. 链式比较 -Bad +对于下面这种写法 ```python -if age > 18 and age < 60: - print("young man") +score = 85 +if score > 80 and score < 90: + print("良好") ``` - -Pythonic +其实还有更好的写法 ```python -if 18 < age < 60: - print("young man") +score = 85 +if 80 < score < 90: + print("良好") ``` -理解了链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False +如果你理解了上面的链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False ``` >>> False == False == True False @@ -268,25 +259,22 @@ False ## 15. 三目运算 -这个保留意见。随使用习惯就好。 - -Bad +对于简单的判断并赋值 ```python -if a > 2: - b = 2 +age = 20 +if age > 18: + type = "adult" else: - b = 1 -#b = 2 - + type = "teenager" ``` -Pythonic +其实是可以使用三目运算,一行搞定。 ```python -a = 3 - -b = 2 if a > 2 else 1 -#b = 2 +age = 20 +b = "adult" if age > 18 else "teenager" ``` + + ## 参考文档 - http://docs.python-guide.org/en/latest/writing/style/ diff --git a/source/c01/c01_07.rst b/source/c01/c01_07.rst index 24a196f..52a6391 100755 --- a/source/c01/c01_07.rst +++ b/source/c01/c01_07.rst @@ -2,20 +2,16 @@ ========================== |image0| - --------------- - Python由于语言的简洁性,让我们以人类思考的方式来写代码,新手更容易上手,老鸟更爱不释手。 要写出 -Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,Github -上有很多非常优秀的源代码值得阅读,比如:requests、flask、tornado,这里小明收集了一些常见的 +Pythonic(优雅的、地道的、整洁的)代码,还要平时多观察那些大牛代码,这里明哥收集了一些比较常见的 Pythonic 写法,帮助你养成写优秀代码的习惯。 01. 变量交换 ------------ -Bad +交换两个变量的值,正常都会想利用一个中间临时变量来过渡。 .. code:: python @@ -23,7 +19,7 @@ Bad a = b b = tmp -Pythonic +能用一行代码解决的(并且不影响可读性的),决不用三行代码。 .. code:: python @@ -32,7 +28,7 @@ Pythonic 02. 列表推导 ------------ -Bad +下面是一个非常简单的 for 循环。 .. code:: python @@ -40,7 +36,8 @@ Bad for i in range(10): my_list.append(i*2) -Pythonic +在一个 for +循环中,如果逻辑比较简单,不如试用一下列表的列表推导式,虽然只有一行代码,但也逻辑清晰。 .. code:: python @@ -49,30 +46,30 @@ Pythonic 03. 单行表达式 -------------- -虽然列表推导式由于其简洁性及表达性,被广受推崇。 +上面两个案例,都将多行代码用另一种方式写成了一行代码。 -但是有许多可以写成单行的表达式,并不是好的做法。 +这并不意味着,代码行数越少,就越 Pythonic 。 -Bad +比如下面这样写,就不推荐。 .. code:: python - print 'one'; print 'two' + print('hello'); print('world') - if x == 1: print 'one' + if x == 1: print('hello,world') if and : # do something -Pythonic +建议还是按照如下的写法来 .. code:: python - print 'one' - print 'two' + print('hello') + print('world') if x == 1: - print 'one' + print('hello,world') cond1 = cond2 = @@ -82,14 +79,14 @@ Pythonic 04. 带索引遍历 -------------- -Bad +使用 for 循环时,如何取得对应的索引,初学者习惯使用 range + len 函数 .. code:: python for i in range(len(my_list)): print(i, "-->", my_list[i]) -Pythonic +更好的做法是利用 enumerate 这个内置函数 .. code:: python @@ -99,7 +96,7 @@ Pythonic 05. 序列解包 ------------ -Pythonic +使用 ``*`` 可以对一个列表解包 .. code:: python @@ -112,7 +109,7 @@ Pythonic 06. 字符串拼接 -------------- -Bad +如果一个列表(或者可迭代对象)中的所有元素都是字符串对象,想要将他们连接起来,通常做法是 .. code:: python @@ -121,7 +118,7 @@ Bad for let in letters: s += let -Pythonic +更推荐的做法是使用 join 函数 .. code:: python @@ -131,59 +128,57 @@ Pythonic 07. 真假判断 ------------ -Bad +判断一个变量是否为真(假),新手习惯直接使用 ``==`` 与 True、False、None +进行对比 .. code:: python if attr == True: - print 'True!' + print('True!') if attr == None: - print 'attr is None!' + print('attr is None!') -Pythonic +实际上,\ ``""``\ 、\ ``[]``\ 、\ ``{}`` +这些没有任何元素的容器都是假值,可直接使用 ``if not xx`` 来判断。 .. code:: python if attr: - print 'attr is truthy!' + print('attr is truthy!') if not attr: - print 'attr is falsey!' - - if attr is None: - print 'attr is None!' + print('attr is falsey!') 08. 访问字典元素 ---------------- -Bad +当直接使用 ``[]`` +来访问字典里的元素时,若key不存在,是会抛异常的,所以新会可能会先判断一下是否有这个 +key,有再取之。 .. code:: python d = {'hello': 'world'} if d.has_key('hello'): - print d['hello'] # prints 'world' + print(d['hello']) # prints 'world' else: - print 'default_value' + print('default_value') -Pythonic +更推荐的做法是使用 ``get`` 来取,如果没有该 key 会默认返回 +None(当然你也可以设置默认返回值) .. code:: python d = {'hello': 'world'} - print d.get('hello', 'default_value') # prints 'world' - print d.get('thingy', 'default_value') # prints 'default_value' - - # Or: - if 'hello' in d: - print d['hello'] + print(d.get('hello', 'default_value')) # prints 'world' + print(d.get('thingy', 'default_value')) # prints 'default_value' 09. 操作列表 ------------ -Bad +下面这段代码,会根据条件过滤过列表中的元素 .. code:: python @@ -193,7 +188,7 @@ Bad if i > 4: b.append(i) -Pythonic +实际上可以使用列表推导或者高阶函数 filter 来实现 .. code:: python @@ -202,74 +197,73 @@ Pythonic # Or: b = filter(lambda x: x > 4, a) -Bad +除了 filter 之外,还有 map、reduce 这两个函数也很好用 .. code:: python a = [3, 4, 5] - for i in range(len(a)): - a[i] += 3 - -Pythonic - -.. code:: python - - a = [3, 4, 5] - a = [i + 3 for i in a] - # Or: - a = map(lambda i: i + 3, a) + b = map(lambda i: i + 3, a) + # b: [6,7,8] 10. 文件读取 ------------ -Bad +文件读取是非常常用的操作,在使用完句柄后,是需要手动调用 close +函数来关闭句柄的 .. code:: python - f = open('file.txt') - a = f.read() - print a - f.close() + fp = open('file.txt') + print(fp.read()) + fp.close() -Pythonic +如果代码写得太长,即使你知道需要手动关闭句柄,却也会经常会漏掉。因此推荐养成习惯使用 +``with open`` 来读写文件,上下文管理器会自动执行关闭句柄的操作 .. code:: python - with open('file.txt') as f: - for line in f: - print line + with open('file.txt') as fp: + for line in fp.readlines(): + print(line) 11. 代码续行 ------------ -Bad +将一个长度较长的字符串放在一行中,是很影响代码可读性的(下面代码可向左滑动) .. code:: python - my_very_big_string = """For a long time I used to go to bed early. Sometimes, \ - when I had put out my candle, my eyes would close so quickly that I had not even \ - time to say “I’m going to sleep.”""" + long_string = 'For a long time I used to go to bed early. Sometimes, when I had put out my candle, my eyes would close so quickly that I had not even time to say “I’m going to sleep.”' + +稍等注重代码可读性的人,会使用三个引号 ``\``\ 来续写 + +.. code:: python - from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \ - yet_another_nice_function + long_string = 'For a long time I used to go to bed early. ' \ + 'Sometimes, when I had put out my candle, ' \ + 'my eyes would close so quickly that I had not even time to say “I’m going to sleep.”' -Pythonic +不过,对我来说,我更喜欢这样子写 使用括号包裹 ``()`` .. code:: python - my_very_big_string = ( + long_string = ( "For a long time I used to go to bed early. Sometimes, " "when I had put out my candle, my eyes would close so quickly " "that I had not even time to say “I’m going to sleep.”" ) +导包的时候亦是如此 + +.. code:: python + from some.deep.module.inside.a.module import ( a_nice_function, another_nice_function, yet_another_nice_function) 12. 显式代码 ------------ -Bad +有时候出于需要,我们会使用一些特殊的魔法来使代码适应更多的场景不确定性。 .. code:: python @@ -277,7 +271,7 @@ Bad x, y = args return dict(**locals()) -Pythonic +但若非必要,请不要那么做。无端增加代码的不确定性,会让原先本就动态的语言写出更加动态的代码。 .. code:: python @@ -287,7 +281,7 @@ Pythonic 13. 使用占位符 -------------- -Pythonic +对于暂不需要,却又不得不接收的的变量,请使用占位符 .. code:: python @@ -297,21 +291,24 @@ Pythonic 14. 链式比较 ------------ -Bad +对于下面这种写法 .. code:: python - if age > 18 and age < 60: - print("young man") + score = 85 + if score > 80 and score < 90: + print("良好") -Pythonic +其实还有更好的写法 .. code:: python - if 18 < age < 60: - print("young man") + score = 85 + if 80 < score < 90: + print("良好") -理解了链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 False +如果你理解了上面的链式比较操作,那么你应该知道为什么下面这行代码输出的结果是 +False :: @@ -321,26 +318,22 @@ Pythonic 15. 三目运算 ------------ -这个保留意见。随使用习惯就好。 - -Bad +对于简单的判断并赋值 .. code:: python - if a > 2: - b = 2 + age = 20 + if age > 18: + type = "adult" else: - b = 1 - #b = 2 + type = "teenager" -Pythonic +其实是可以使用三目运算,一行搞定。 .. code:: python - a = 3 - - b = 2 if a > 2 else 1 - #b = 2 + age = 20 + b = "adult" if age > 18 else "teenager" 参考文档 -------- diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 272b66b..571d252 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -154,8 +154,8 @@ class Seeker(threading.Thread): cond = threading.Condition() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') -seeker.run() -hider.run() +seeker.start() +hider.start() ``` 通过cond来通信,阻塞自己,并使对方执行。从而,达到有顺序的执行。 看下结果 diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index d21aa7a..866b46d 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -165,8 +165,8 @@ Condition和Event 是类似的,并没有多大区别。 cond = threading.Condition() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') - seeker.run() - hider.run() + seeker.start() + hider.start() 通过cond来通信,阻塞自己,并使对方执行。从而,达到有顺序的执行。 看下结果 diff --git a/source/c04/c04_01.md b/source/c04/c04_01.md index b5434ca..0bf4881 100644 --- a/source/c04/c04_01.md +++ b/source/c04/c04_01.md @@ -31,16 +31,7 @@ 由于 virtualenvwrapper 是 virtualenv 的一组扩展,所以如果要使用 virtualenvwrapper,就必须先安装 virtualenv。 - -**安装** -```bash -$ pip install virtualenv - -# 检查版本 -$ virtualenv --version -``` - -**基本使用** +**安装***基本使用** 由于virtualenv创建虚拟环境是在当前环境下创建的。所以我们要准备一个专门存放虚拟环境的目录。(以下操作在Linux在完成,windows相对简单,请自行完成,有不明白的请微信与我联系。) diff --git a/source/c04/c04_01.rst b/source/c04/c04_01.rst index 02f0a19..129c3ac 100755 --- a/source/c04/c04_01.rst +++ b/source/c04/c04_01.rst @@ -35,16 +35,7 @@ Python 版本管理工具。 - ``Vex``\ :可以在虚拟环境中执行命令 由于 virtualenvwrapper 是 virtualenv 的一组扩展,所以如果要使用 virtualenvwrapper,就必须先安装 virtualenv。 -**安装** - -.. code:: bash - - $ pip install virtualenv - - # 检查版本 - $ virtualenv --version - -**基本使用** +**安装**\ \*基本使用*\* 由于virtualenv创建虚拟环境是在当前环境下创建的。所以我们要准备一个专门存放虚拟环境的目录。(以下操作在Linux在完成,windows相对简单,请自行完成,有不明白的请微信与我联系。) diff --git a/source/c04/c04_03.md b/source/c04/c04_03.md index 58d7ef3..ed89f75 100644 --- a/source/c04/c04_03.md +++ b/source/c04/c04_03.md @@ -462,13 +462,13 @@ html_js_files = [ 然后根据指引填写好资料 -![image-20200704154846176](/Users/MING/Library/Application Support/typora-user-images/image-20200704154846176.png) +![](http://image.iswbm.com/image-20200704154846176.png) 选择基础版 -![image-20200704155335679](/Users/MING/Library/Application Support/typora-user-images/image-20200704155335679.png) +![](http://image.iswbm.com/image-20200704155335679.png) -![image-20200704155410411](/Users/MING/Library/Application Support/typora-user-images/image-20200704155410411.png) +![](http://image.iswbm.com/image-20200704155410411.png) ## 附录:参考文档 diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index aa88e6e..8452a5e 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -478,22 +478,13 @@ Python 3.x ,所以这里的代码也要对应修改。 然后根据指引填写好资料 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200704154846176.png - :alt: image-20200704154846176 - - image-20200704154846176 +|image20| 选择基础版 -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200704155335679.png - :alt: image-20200704155335679 - - image-20200704155335679 +|image21| -.. figure:: /Users/MING/Library/Application%20Support/typora-user-images/image-20200704155410411.png - :alt: image-20200704155410411 - - image-20200704155410411 +|image22| 附录:参考文档 -------------- @@ -504,7 +495,7 @@ Python 3.x ,所以这里的代码也要对应修改。 -------------- -|image20| +|image23| .. |image0| image:: http://image.iswbm.com/20200602135014.png .. |image1| image:: http://image.iswbm.com/20190511160523.png @@ -526,5 +517,8 @@ Python 3.x ,所以这里的代码也要对应修改。 .. |image17| image:: http://image.iswbm.com/20191015225652.png .. |image18| image:: http://image.iswbm.com/20191016211012.png .. |image19| image:: http://image.iswbm.com/image-20200704154427375.png -.. |image20| image:: http://image.iswbm.com/20200607174235.png +.. |image20| image:: http://image.iswbm.com/image-20200704154846176.png +.. |image21| image:: http://image.iswbm.com/image-20200704155335679.png +.. |image22| image:: http://image.iswbm.com/image-20200704155410411.png +.. |image23| image:: http://image.iswbm.com/20200607174235.png diff --git a/source/py_mp_index.md b/source/py_mp_index.md deleted file mode 100644 index a3a47a4..0000000 --- a/source/py_mp_index.md +++ /dev/null @@ -1,821 +0,0 @@ -# Python编程时光 - 学习索引 - -为了方便读者们能从本号真正学到一些有用的内容,我将 Python 编程时光干货文章进行了整理,方便你学习。 - -![](http://image.iswbm.com/20201005142053.png) - -## 01. 基础系列 - -### 1.1 基础必学 - -1、[15个Pythonic的代码示例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485036&idx=1&sn=24de1996a63bf25b0c0deec782f688cf&scene=21#wechat_redirect) - -2、[Python基础|深入闭包与变量作用域](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485039&idx=1&sn=23557ac2640819b568a426b2db4df69c&scene=21#wechat_redirect) - -3、[Python基础|类方法的强制重写与禁止重写](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485037&idx=1&sn=8f4838b5bc919631c5cb642120b010c2&scene=21#wechat_redirect) - -4、[Python基础|多继承与Mixin设计模式](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485032&idx=1&sn=35e1c7014bc3f668cc4b48d9c318f1f9&scene=21#wechat_redirect) - -5、[Python基础|理解元组存在的意义](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485038&idx=1&sn=d0c7ab3fc20b299e4a0b9f6b414e4070&scene=21#wechat_redirect) - -6、[你知道 Python里的「单分派泛函数」?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484938&idx=1&sn=061fdb00ccc499aa0cfc304852adb143&scene=21#wechat_redirect) - -7、[类型注解的福音,提高Python代码可读性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484901&idx=1&sn=8f506cb46edb94dfb668525399f30f9e&scene=21#wechat_redirect) - -8、[写几个 Python 进阶必备函数](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484973&idx=1&sn=451d381fa3021e14b514cc15907ce0c3&scene=21#wechat_redirect) - -9、[Python 字符串连接,哪种的效率最高?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485016&idx=1&sn=88497c09c8785b656ae1fee1406827dc&scene=21#wechat_redirect) - -10、[深入理解Python中的上下文管理器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484950&idx=1&sn=9d78e469f8f190ef0484842b0391bec9&scene=21#wechat_redirect) - -11、[秒杀市面 90% 的 Python 入门教程 (上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484852&idx=1&sn=2362e034c2740d9f53c3cc99a6908cbd&scene=21#wechat_redirect) - -12、[秒杀市面 90% 的 Python 入门教程 (中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484856&idx=1&sn=881ee5d8154a85479d71ca4594f90142&scene=21#wechat_redirect) - -13、[秒杀市面 90% 的 Python 入门教程 (下)](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485231&idx=1&sn=fa7146937f51110eb7356154bc2e2282&scene=21#wechat_redirect) - -14、[检验你 Python 基本功的 17 个骚操作](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484863&idx=2&sn=ff11a17ad82938b3a4f83ee5fbc2e497&scene=21#wechat_redirect) - -25、[和import说再见,这个库教你怎么偷懒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485325&idx=2&sn=90c6dfbdbb36f63db72d1c0af67c1115&scene=21#wechat_redirect) - -16、[如何修改 CentOS 6.x 上默认Python 版本](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484881&idx=1&sn=ecb99878d3e2fca2bedc808b1e7aae9c&scene=21#wechat_redirect) - -17、[写 Python 时你要避免的十个错误](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=1&sn=6acba3bf872fe1309308d9ef6cde53ce&scene=21#wechat_redirect) - -18、[为何无法使用 ip 访问网站?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484976&idx=1&sn=503d75faea7a633729ec90d6f26e21a2&scene=21#wechat_redirect) - -19、[大白话解释什么是 Python Launcher?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485268&idx=1&sn=9cea766f1e1a1f6278fca4665fc33a87&scene=21#wechat_redirect) - -20、[13条Python2.x和3.x的区别,你知道几条?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485044&idx=1&sn=f38e2036317893be8388900dde3077e8&scene=21#wechat_redirect) - -21、[Python基础|新式类和经典类的区别?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485033&idx=1&sn=8b6357d5a66f9a06347bf0922ae11946&scene=21#wechat_redirect) - -22、[Python 中有 3 个不可思议的返回功能](https://mp.weixin.qq.com/s/dSky88bOCPgYDprzJhnlbg) - -23、[每天花 30 秒,就可以练习的 Python 小技巧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484861&idx=1&sn=e5fa97570b3c180e5a5edc753fc62669&scene=21#wechat_redirect) - -24、[常见 Python 简洁代码的样例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484823&idx=2&sn=4459a7edc4a4989068b22b31c76f2c92&scene=21#wechat_redirect) - -25、[ 别笑!Python 新手这五大坑你躲不过](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485573&idx=1&sn=362e7c5e73c27942e8250375257620e2&chksm=e8866867dff1e17140dc043b5dd33eec0e94e1616eab7e4b415bb767883d46d58a7243fb198f#rd) - -26、[Python 3.9 发布,字典的合并操作符终于来了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485825&idx=1&sn=04075357936423097f692fa19857c3aa&chksm=e8866963dff1e075707b605fb1352f7760fb1b41040bdda4dec6b83370af37e009424d24f65e&token=2013245174&lang=zh_CN#rd) - -27、[Python 3 入门,看这篇就够了(超全整理)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=1&sn=7985df89a647c271a9c99b1a25f87cb4&chksm=e8858366dff20a7062c54d79cfc85945d4ae91e96c0a0dcb8383d24680c6eeb80d68a6f2b718&scene=27#wechat_redirect) - -28、[掌握 Python 中下划线的 5 个潜规则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492133&idx=2&sn=ba4631b32f8fb4cd018a68b5411771b1&chksm=e88582c7dff20bd1c417c97e6798ba5bcacc90b55161976d7529d12f1850e413ede66110cbe3&scene=27#wechat_redirect) - -29、[还傻傻分不清什么是方法,什么是函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491545&idx=2&sn=193bb88089cc7583989db44707aeca90&chksm=e8867f3bdff1f62d159dca793efc1c937b1c7a135b6f25ff9d77d8d1b819c7ddc66b3ba1f8d0&scene=27#wechat_redirect) -30、[Python 如何像 awk一样分割字符串?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491382&idx=2&sn=6e3824fa540fe7225d0cd1e7180559b6&chksm=e8867fd4dff1f6c2d2c024b6a07f616794c5b89a1b2b08ab05d029e5de1a59ff751bb03a6561&scene=27#wechat_redirect) - -31、[一篇文章带你剖析Python 字节流处理神器struct](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=2&sn=889bf712f4115a27f321e791b3862405&chksm=e8867e4bdff1f75dc9e1e2374c3302753c3a6a28b42436e6e2550aa8ac528652b2561ad2ca48&scene=27#wechat_redirect) - -32、[一篇文章掌握 Python 内置 zip() 的全部内容](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491227&idx=2&sn=61efa9b45c112a25be348a94a9ac8151&chksm=e8867e79dff1f76fcc3858afdfd4e68aef0a5d0430f0420aacd2045aac86d3c31ff88bac464d&scene=27#wechat_redirect) - -33、[字典访问不存在的key 时,如何才能不报错?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490809&idx=2&sn=a256a0d8b86bae3a7dc65f7a88e4ab39&chksm=e8867c1bdff1f50d1da0e11d2dca288b085064062ecb8f68891b0bd48b8a1fca4f8ca594fc22&scene=27#wechat_redirect) - -34、[太干了!一张图整理了 Python 所有内置异常](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=2&sn=59578093d82362c023ce305b2fc55103&chksm=e886791adff1f00c0a76154c7c7101d39b151891cc07b33dfe2523c95d9e38250c3684f674bb&scene=27#wechat_redirect) - -35、[Python 代码覆盖率工具 - Coverage](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489752&idx=2&sn=b115d9377feb0129985295e5a3c8ec5e&chksm=e886783adff1f12c7bb14c55560f06f6a5327d6beee9bba38b9965784baf197918ae3898e95d&scene=27#wechat_redirect) - -36、[超赞!100 道让你练习 Python 基础的题目](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488558&idx=1&sn=58a53b85a7e49989794d8d0e9885f335&chksm=e88674ccdff1fdda61609dc4aa811dc6a52a819fe56da3bd0480f9f21ae248c94d8f8f3f8798&scene=27#wechat_redirect) - -37、[看完这篇,你也是字符编码大神!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488475&idx=2&sn=17ca56435158c6c19134e30bda6e2b2d&chksm=e8867339dff1fa2f99cb0d85d99a2befa3689951778fbef8f2e0bad1042ae0c4813b99860c33&scene=27#wechat_redirect) - -38、[一道 3 行代码的 Python面试题,我懵逼了一天](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486901&idx=1&sn=73fe3a92ac2639706d9573a0eaffd48f&chksm=e8866d57dff1e441d6ffad3c4f00b053db39df1cec4e6cf69992641b359cc96448f7f3068918&scene=27#wechat_redirect) - -39、[学习 Python 的指南大纲,从基础到核心知识全都有](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486246&idx=1&sn=58665e2be95f71415ac3e2c1c5f4f690&chksm=e8866bc4dff1e2d212a4d93c0c83cd39cd534f64a8e113b208f96a6156e75ef652ee1ac32270&scene=27#wechat_redirect) - -40、[打基础一定要吃透这12类 Python 内置函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486198&idx=1&sn=efea0e57956188eeafb163d7bb2448c4&chksm=e8866a14dff1e3022f361245b182a30fbbf40550f3e8dc4456e1acf260aa73d35a20c010559d&scene=27#wechat_redirect) - -41、[有了这篇文章, Python 中的编码不再是噩梦](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486174&idx=1&sn=ceb21deb2ff3ce750117c23f414416e3&chksm=e8866a3cdff1e32a1fcf07880803a7a3c350cc40d612bbd4f34b84cb06bf39250932f1ba5f05&scene=27#wechat_redirect) - -42、[OrderedDict 是如何保证 Key 的插入顺序的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486136&idx=2&sn=81fd4039611666917ae8801bd73fe921&chksm=e8866a5adff1e34c603500e97c4b7e1cecb7a1fb43e01c6ff5a794d526d54e5a02b35240d2db&scene=27#wechat_redirect) - -43、[盘点 Python 高手都写不出来的几个错误](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485974&idx=1&sn=6a6a2fb8bc5c2acd300ebecdf625086c&chksm=e8866af4dff1e3e2c528594310475edaf2839d7b09048270acbb0dccc124581eaa65b123d393&scene=27#wechat_redirect) - -44、[8个超好用的Python内置函数,提升效率必备!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485943&idx=2&sn=ecb57c8dbf0e4965d4842cc6f614da64&chksm=e8866915dff1e0035ded3974097c1f0e00561962eeb0498c633fdf664cd429749aa6e6eca806&scene=27#wechat_redirect) - -45、[如何使用 Python 执行 js 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491161&idx=2&sn=d89bbc4c214563807427703aa584a26d&chksm=e8867ebbdff1f7ad301b58ac567faec95be2d8f5be921b23ea5d3722d377c2926f32ff7b68ac&scene=27#wechat_redirect) - -## 02. 进阶系列 - -### 2.1 进阶必学 - -1、[描述符:其实你不懂我(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484934&idx=1&sn=ef8b30ffe2467e5736036bd083b340ca&scene=21#wechat_redirect) - -2、[描述符:我无处不在!(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484927&idx=1&sn=8a3d673ee20bd418d93a249380ca8e76&scene=21#wechat_redirect) - -3、[Python静态方法其实暗藏玄机](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484911&idx=1&sn=a16846030c3589c912aec9efdf4f7f80&scene=21#wechat_redirect) - -4、[全面深入理解 Python 面向对象](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485208&idx=1&sn=61bf8d3ec81fa85b0bc9de6e5f5d6611&scene=21#wechat_redirect) - -5、[几个使用装饰器的小技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484930&idx=1&sn=ed731e32b6e95e7d83d74a544f97142a&scene=21#wechat_redirect) - -6、[围观大神是如何用 Python 处理文件的?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484905&idx=1&sn=7de0eaab0a4c9f8b44cba01d28ad7254&scene=21#wechat_redirect) - -7、[Python进阶开发|元类编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485080&idx=1&sn=45e6d9995e4469d8b7bce2f800ac0f9b&scene=21#wechat_redirect) - -8、[Python进阶开发之网络编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485078&idx=1&sn=e0a3855d959c178b8c01bd196a048dce&scene=21#wechat_redirect) - -9、[Python 进阶:深入 GIL (上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484875&idx=2&sn=8780a349c60522ad1b7aa20a3fe7a19c&scene=21#wechat_redirect) - -10、[没掌握好这24条,别说Python慢。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484910&idx=1&sn=df8fb32a840a28954614b09bce730b72&scene=21#wechat_redirect) - -11、[花了两个星期,我终于把WSGI整明白了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484919&idx=1&sn=bd7d2bc0ab8a41110d5d93e44ad20b1f&scene=21#wechat_redirect) - -12、[源码解读|Flask 上下文核心机制](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484943&idx=1&sn=ca846404d30a2ec775ab7db5df73aa8e&scene=21#wechat_redirect) - -13、[说说几个 Python 内存分配时的小秘密](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484853&idx=2&sn=edf7c3225d119291fae5d05820f55aac&scene=21#wechat_redirect) - -14、[写了三年代码,还是不懂 Python 世界的规则](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484886&idx=1&sn=f46917f6e23f8961c912606a281a354d&scene=21#wechat_redirect) - -15、[如何保护你写的 Python 代码?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484858&idx=1&sn=e18a1c89f14d4e4043833e3ff5cc8d32&scene=21#wechat_redirect) - -16、[27 个问题,告诉你 Python 为什么如此设计?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484862&idx=1&sn=89500433bc174dfc0530eebea2fb91d1&scene=21#wechat_redirect) - -17、[精心整理 30 个Python代码实现的常用功能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485147&idx=2&sn=c5b871e1cebde6b311609f6de703f2e3&scene=21#wechat_redirect) - -18、[高手之路:从零开始打造一个Web服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484891&idx=1&sn=ed12d175452c2efedeefb1635063177c&scene=21#wechat_redirect) - -19、[从0到1:全面理解 RPC 远程调用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484915&idx=1&sn=427b9dda155e4201c2f939c3919def91&scene=21#wechat_redirect) - -20、[一篇 Python 函数式编程指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484834&idx=1&sn=559bac4ff85f7082bc989a6fde04d3f3&scene=21#wechat_redirect) - -21、[没看完这11 条,别说你精通 Python 装饰器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484900&idx=1&sn=3997a2a377577e3d16a9b7f8e6a5ea53&scene=21#wechat_redirect) - -22、[程序卡住了?教你如何调试已在运行的程序](https://mp.weixin.qq.com/s/QC-Pc-0iifaVKOfsiNTYmA) - -23、[字符串在Python内部是如何省内存的](https://mp.weixin.qq.com/s/jp_I82fnr0-3cd6ioZcoGQ) - -24、[巧用 traceback 定位 Python 内存泄漏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485751&idx=1&sn=8a054a575767c103c830a6b3ef5f73c8&chksm=e88669d5dff1e0c3ae1c7c32f35a8f46a3a2e0a637fb4b947566f417dd8f4419bec636c8a667#rd) - -25、[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd) - -26、[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd) - -27、[教你如何阅读 Python 开源项目代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485451&idx=1&sn=e15856352c18297770c67d9df66ec3b0&chksm=e88668e9dff1e1ff0f6a39f3885f4126232213149ac09daa1fc0ec9e0de2095bd11fb727d63a#rd) - -28、[这个 Python 知识点,90% 的人都得挂~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490984&idx=1&sn=135b7b187d8d41b3c10179dbaedaf77e&chksm=e8867d4adff1f45cd90fe2609fe7d1840b07fab8cf19cb7406ebd038c31df0028e611077d80e&scene=27#wechat_redirect) - -29、[一篇长文学懂 PyTorch](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489158&idx=1&sn=2e2e88565131d11271aea61b0bedf182&chksm=e8867664dff1ff728ee30e1baab0d7df0139fc67e2514ffcaebda6d50e5d08bd2866ad7dee35&scene=27#wechat_redirect) - -30、[学了这么久,你知道Python是如何运作的吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488458&idx=1&sn=23040c906e83462c4f64039cba6e3dfb&chksm=e8867328dff1fa3e149248e53762444b1abc72df395a597639357b5525d16b1b6db242339d20&scene=27#wechat_redirect) - -31、[求你了,别再使用 pprint 打印字典了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486288&idx=1&sn=7647e1ee63c49c14472baec19fb79b87&chksm=e8866bb2dff1e2a4d5b85ae9db1e8f4d8dda839643fb02b9e0b7f7fe6143cb627773e56d44bf&scene=27#wechat_redirect) - -32、[Python实现RabbitMQ中6种消息模型](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486017&idx=1&sn=9b95896abee7a345e3abee1ac4f2edab&chksm=e8866aa3dff1e3b547ef5062c30516b2d54a051fd4a07d85078223ea673af1400be1a6c56ad5&scene=27#wechat_redirect) - -33、[想写好面向对象的代码,这几篇一定要看(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485938&idx=1&sn=a725785ef8000868b7cb85606ee754d3&chksm=e8866910dff1e006f195d6ea0d66f4b4f1fe5a9b97debc18ae4b04a239a347d342fbc67b44e4&scene=27#wechat_redirect) - -34、[想写好面向对象的代码,这几篇一定要看(中)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485926&idx=1&sn=ce148af333d9f377d8a6fb9577bbf57c&chksm=e8866904dff1e012ec1bb8f418c43cca7954979bd1481707761b29d1e632ace6369efd50210b&scene=27#wechat_redirect) - -35、[想写好面向对象的代码,这几篇一定要看(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485913&idx=1&sn=74b34bd95d513a94201ad05604c4a05f&chksm=e886693bdff1e02dd2079cb90458aac914e5cb832aec59677da8b2f85200baaa57e804a4b58e&scene=27#wechat_redirect) - -36、[教你 10 分钟构建一套 RESTful API 服务](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=2&sn=d593dbf907217ff4dea1daf636062778&chksm=e88674f4dff1fde26b1a1074a0a0c78be0630a34b4aed705755946bd8cfcde1a23e0df916392&scene=27#wechat_redirect) - - -### 2.2 包的管理 - -1、[最全的 pip 使用指南,50% 你可能没用过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484827&idx=1&sn=df0923856c820e10baca20c9873b336b&scene=21#wechat_redirect) - -2、[全面学习 Python 包:包的构建与分发](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485349&idx=1&sn=8d6da555a2852a14506a491c4cf9a234&scene=21#wechat_redirect) - -3、[深入探讨 Python 的import机制:实现远程导入模块](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&scene=21#wechat_redirect) - -4、[盘点 Python 依赖库管理的工具:pip、pipreqs、pigar、pip-tools、pipdeptree](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485120&idx=1&sn=170ef1173eabc5bed28c724e19d6c0ac&scene=21#wechat_redirect) - -5、[如何使用 pyenv 运行Python的多个版本?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486239&idx=2&sn=863e80589f5eb9b2daa9701f6b494997&chksm=e8866bfddff1e2ebc244354238aedfa9291aa27ad8f7e466213236369dae9800b29b5766c0cf&scene=27#wechat_redirect) - -6、[如何管理 Python 的虚拟环境?超全讲解virtualenv的使用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&scene=21#wechat_redirect) - -7、[一款让Python开发效率提升50%的工具包](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=1&sn=eead45b3c34777085465f9854a433e36&chksm=e886750cdff1fc1ae2791adba1231e045630417f9b4185217817183aa1f260815acabcd39363&scene=27#wechat_redirect) - -8、[记 Python “用户环境”的一次完美应用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486191&idx=1&sn=e556df305ce1df4c4969d2fdbe884cfa&chksm=e8866a0ddff1e31b2ca8ca8fbbe0355fb8c24376d715224ef2176e613b5601764be0193e3aa4&scene=27#wechat_redirect) - -### 2.3 性能优化 - -1、[Python高效代码实践:性能、内存和可用性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485161&idx=1&sn=07625f06db330897bc8bfee880ceac18&scene=21#wechat_redirect) - -2、[Python 代码的性能优化之道](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485004&idx=1&sn=8dc07a40bcac30c93874398b17a52831&scene=21#wechat_redirect) - -3、[7 个习惯帮你提升Python运行性能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484957&idx=1&sn=9b3c644e6b1777b77ce7f84047c6d500&scene=21#wechat_redirect) - -4、[如何提升你的 Python 代码健壮性(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484871&idx=1&sn=985b79143d0cafd21e5263ee79afa777&scene=21#wechat_redirect) - -5、[如何提升你的 Python 代码健壮性(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484867&idx=1&sn=b8eab8416229dc74f1ac96e099a3df71&scene=21#wechat_redirect) - -6、[将 Python 运行速度提升 30 倍的神技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484877&idx=1&sn=f6067875f1e807d19291e383f8421a3b&scene=21#wechat_redirect) - -7、[实战讲解:Python 性能分析与优化实践](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485404&idx=1&sn=486fcf898d6dd21b0feb990de3c1e08f&chksm=e886673edff1ee28b8c6e1d520b2732b2f66f4c4691ef2696b54743b1ad0769356e12a1e98b2#rd) - -8、[如何调试Python 程序的内存泄露问题](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488577&idx=2&sn=16aff9687a4a16faa492118cd5c1bfae&chksm=e88674a3dff1fdb5b80a4547cb863fcf4255f90ab2024ae8c3bbf1f270efb13333b2f7426baf&scene=27#wechat_redirect) - -### 2.4 并发编程 - -1、[并发编程01|从性能角度来初探并发编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485074&idx=1&sn=a859c6ab1d9b95c30c9f8b06f9489887&scene=21#wechat_redirect) - -2、[并发编程02|创建多线程的几种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485073&idx=1&sn=854ff524645247e5020d977e57d9c0e6&scene=21#wechat_redirect) - -3、[并发编程03|谈谈线程中的“锁机制”](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485069&idx=1&sn=52370a27d4a5c4541969921ada890e0b&scene=21#wechat_redirect) - -4、[并发编程04|消息通信机制/任务协调](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485068&idx=1&sn=fc0798e84e7845cd9efee1809f932f15&scene=21#wechat_redirect) - -5、[并发编程05|线程中的信息隔离](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485066&idx=1&sn=0bb9d6c82a6d062e50d09e1eefd09427&scene=21#wechat_redirect) - -6、[并发编程06|如何创建线程池](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485065&idx=1&sn=60891b67b139806cf6bf4c05f5861f02&scene=21#wechat_redirect) - -7、[并发编程07|从生成器使用入门协程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485064&idx=1&sn=8584bb778152a12ca335970bed9fdbdc&scene=21#wechat_redirect) - -8、[并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect) - -9、[并发编程09|初识异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485061&idx=1&sn=9e846df1a5cb57e0bc6254dcc953e243&scene=21#wechat_redirect) - -10、[并发编程12|学习异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485058&idx=1&sn=92ef1f79ce6488670ae13e5d6a1c7908&scene=21#wechat_redirect) - -11、[并发编程11|实战异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485057&idx=1&sn=8bacf0f2b42de5962fbdf32796903f27&scene=21#wechat_redirect) - -12、[百万「并发」之Python异步编程(上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484896&idx=2&sn=5d8458ede3440ae501d19c5ffd5f8a99&scene=21#wechat_redirect) - -13、[百万「并发」之Python异步编程(中篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484889&idx=2&sn=25d11042e5b2ab17dff40535d354b8f8&scene=21#wechat_redirect) - -14、[百万「并发」之Python异步编程(下篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=2&sn=b9e62a9b024358aec629e876b76e0eb5&scene=21#wechat_redirect) - -15、[如何一行 Python 代码实现并行?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484826&idx=1&sn=6c5b575b7b134642077cfde2bb8b613f&scene=21#wechat_redirect) - -16、[asyncio:从原理、源码到实现](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485245&idx=1&sn=a08826c3d28037479190fd652438bcca&scene=21#wechat_redirect) - -17、[为什么说线程是CPU调度的基本单位?](https://mp.weixin.qq.com/s/c3aZ-6UzZVD3_PvVhiDPWA) - -18、[非常适合小白的 Asyncio 教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485842&idx=1&sn=ab85dab95cf47046ddcc35b60e633c40&chksm=e8866970dff1e066381c0ba53fe09e34b8f1ce52bbbfe568c1ee66b9cbad87f43b442895c770&token=2013245174&lang=zh_CN#rd) - -19、[说说 Python 里关于线程安全的那些事儿](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486279&idx=1&sn=2b6acf717b7f5cc6a2b72c62266dbe01&chksm=e8866ba5dff1e2b318c679413fd7d0ac3a69db7cab1ceda03eca26fb71f6d2837af80e2143d0&scene=27#wechat_redirect) - -### 2.5 实战练习 - -1、[适合 Python 新手练习的绝佳项目](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485898&idx=2&sn=36a1b1a94dece0a290652da23679cff0&chksm=e8866928dff1e03e58b95b3cd24dbc23e368b6eb2c0599b2efe814924e21ec5c01935b2e0455&token=2013245174&lang=zh_CN#rd) - -2、[4个Python实战项目,让你瞬间读懂Python!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485852&idx=2&sn=eb3471a260cce835a755a780d6cc690d&chksm=e886697edff1e068bd0f9d456e26ee5ebe482580457951e42807e731d266c95f00c37de22fea&token=2013245174&lang=zh_CN#rd) - -### 2.6 GUI 应用 - -1、[如何在Python中编写精美图形界面?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=1&sn=eed86ec2f44a92fadb94f237178b3f8a&chksm=e8866a01dff1e317f450d91b88e91d6912d6a1b2ac7a34ebb86b74a2abc0f912d8ced5a3baac&scene=27#wechat_redirect) - -2、[或许,这是最强大的一款Python GUI工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492571&idx=2&sn=6ac2c0c19d86d653acd82d5b4ef86126&chksm=e8858339dff20a2f595159499e8eb8e072b52e61521e4a1322337f174bff942134a25b3b6b9b&scene=27#wechat_redirect) - -### 2.7 自动化办公 - -1、[付费?是不可能的!20行Python代码实现一款永久免费PDF编辑工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492085&idx=1&sn=785968af86bf998ecad9b11583a23ad6&chksm=e8858117dff2080119f596c6447d9a0c7b73c6bced8306883900f2f4d9582929f9d5c26ac45d&scene=27#wechat_redirect) - -2、[带你用 Python 实现自动化群控(入门篇)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491979&idx=2&sn=7489738d3225a96767173e4b54023f95&chksm=e8858169dff2087fed0f11ad6b880c110e3530eae28ed7702343f6d81163860ed20a2f056294&scene=27#wechat_redirect) - -3、[最全总结:Python 发送邮件的几种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489568&idx=2&sn=bdac42b533784bf3a8ee523da36bacba&chksm=e88678c2dff1f1d499321032e6871f2810608909121bf56aee184c6d7770d3b310f39cad7c43&scene=27#wechat_redirect) - -4、[太全了!使用 Python 转换 PDF,看这一篇总结就够了。](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=2&sn=eed1cab8e03c908fdfe311bf4bbd91a6&chksm=e8867544dff1fc52e0d9dd62c61d36ec5a5b6847aeb9992e5423eb4e01f08995461160adbb45#rd) - -5、[Python吊打Excel?屁!那是你不会用!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=2&sn=9c4a697758af01c086cced44b98a3380&chksm=e8866e4ddff1e75bd2f19e036c3e338edee50871364d2f185afa51cb361c83f3bbda8ce2d92f&scene=27#wechat_redirect) - -### 2.8 网络爬虫 - -1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect) - -2、[教你实现一个可视化爬虫监控系统](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484208&idx=1&sn=d04421526cf0e12089541063b4ec448d&scene=21#wechat_redirect) - -3、[估计是讲得最清楚的「异步爬虫」指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484929&idx=1&sn=7055414a33d7cbadaeb5645cbd5203ec&scene=21#wechat_redirect) - -4、[10 个爬虫工程师必备的工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484958&idx=1&sn=3422e53de4ae701a922104ad26f8e256&scene=21#wechat_redirect) - -5、[Selenium自动登录淘宝,我无意间发现了登录漏洞!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=2&sn=6a33873b3777afd420f3c56078a88687&chksm=e8867c53dff1f5455fe54e1ee0044cc97638fbc96983f36e19938810bea4e06d5af0ba3036df&scene=27#wechat_redirect) - -6、[干!一篇文章讲了这么多爬虫的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490557&idx=2&sn=f5128b1dc827cc4a6035d46714a484b1&chksm=e8867b1fdff1f20993b024fbfaa4445763a5949174913a58d9f17ddcb0286be8b325516cb449&scene=27#wechat_redirect) - -7、[谈谈常见网站加密和混淆技术](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486218&idx=2&sn=6261fa602eb4f73365a9d372376594ed&chksm=e8866be8dff1e2fea5082dd13dc7799a1cbc829f2e41a32891db411152153cb261d01be5807b&scene=27#wechat_redirect) - -8、[初识网络爬虫之夜探老王家](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=2&sn=3a1b880c7f1b37d095359b35ff96e9b4&chksm=e8866a01dff1e31711d7fb093a96f54eaf374b46c686b0ea4e49d6f926018ad5b6c27aebfa9d&scene=27#wechat_redirect) - -9、[不懂爬虫也能轻易爬取数据的 6 大工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486085&idx=1&sn=51c740e1d848f1624a5a8e582434f661&chksm=e8866a67dff1e371de89a64503591ae0c42b67cd43434fa394125f09a2f65bc23817a266ac13&scene=27#wechat_redirect) - -### 2.9 实用系列 - -1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect) - -2、[30分钟教你快速搭建一个顔值超高的博客](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485045&idx=1&sn=8b250c0c174e418e2025d86f42c695b6&scene=21#wechat_redirect) - -3、[用Python写一个表白神器让你七夕脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) - -4、[用 Sphinx 搭建博客时,如何导流到公众号?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484848&idx=1&sn=80ae18e7f53a64e62ac9c0ef0c21362e&scene=21#wechat_redirect) - -5、[10 行 Python 代码写 1 个 USB 病毒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484832&idx=1&sn=43c353856c7dd9ea2cb1a9bbcd077fa2&scene=21#wechat_redirect) - -6、[废旧 Android 手机如何改造成 Linux 服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485279&idx=1&sn=7166a2c9445438eaaf63a25b598e5427&scene=21#wechat_redirect) - -7、[如何将手机打造成 Python 开发利器?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485256&idx=1&sn=2e646e28287707c67c22fbe68112e622&scene=21#wechat_redirect) - -8、[我用 Python 做了一回黑客,批量破解了朋友的网站密码](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485156&idx=1&sn=05142ae7b4bf7e97ebe042e394aa7084&scene=21#wechat_redirect) - -9、[手把手教你安装Win+Ubuntu双系统](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485030&idx=1&sn=8383a7306381f36781957b807fa93961&scene=21#wechat_redirect) - -10、[一篇文章让你的 MacBook 进入超神状态](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484906&idx=1&sn=b2c3c969e53beae53aa7be7959227b5b&scene=21#wechat_redirect) - -11、[情人节来了,教你个用 Python 表白的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485701&idx=1&sn=ef7f0b83e60f397c1839259b275575bf&chksm=e88669e7dff1e0f1317ec429cf9e70442bb3a08ec46efe9a0d06f4ec9847574ea30b53904670&token=1148998814&lang=zh_CN#rd) - -12、[“Hack” 微信实战:如何用 Python 分析微信群聊记录?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486141&idx=1&sn=8b32c57dce0f3a33127c39c9cd35509e&chksm=e8866a5fdff1e349f16f677304716e40a305e60168fe7e30361de675dad5dfa956c9d7c79089&scene=27#wechat_redirect) - -13、[5 行 Python 代码生成自定义二维码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488795&idx=2&sn=8e03e01e861753f7ccf3adeb1f3cb848&chksm=e88675f9dff1fcef328c69b1519743fcc3ae83b80dd3d66cb934acb346781497f83c5a9051a5&scene=27#wechat_redirect) - -14、[女朋友背着我,用 Python 偷偷隐藏了她的行踪](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487175&idx=1&sn=c3ba19bdbcb6fd59b37ff04d02bce63b&chksm=e8866e25dff1e733b31386fda99271eefadc77b3e0579df4d74617b561ecf0fa0db70f9f2860&scene=27#wechat_redirect) - -15、[zip 解压炸弹? ?在 Python 面前,啥也不是..](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=2&sn=307a29d71776f6d1c68567a16727a7e5&chksm=e88581e3dff208f55a48081cb8f951ca7d977c9b1ec3ac9aaf26bc8c53904ac0e15035e5c680&scene=27#wechat_redirect) - -16、[Chrome 的小恐龙游戏,被我破解了...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491911&idx=1&sn=1dcab055e480fe626d50b0324ed5b9f1&chksm=e88581a5dff208b3fbf88017c15ccfa966eec3b25e4804a72401773d7b87d5b6a2bcd8db9ac7&scene=27#wechat_redirect) - -17、[深扒微信多开的秘密后,我竟然发现了个 bug](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489704&idx=1&sn=a33559f22517c744367a660f78dbb25a&chksm=e886784adff1f15c1524f90487d6957c86dee0f5c499528630f43b62320b28e66977afe4d884&scene=27#wechat_redirect) - - -## 03. 数据分析 - -### 3.1 基础库 - -1、[有关 NumPy 和数据表达的可视化介绍](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485295&idx=1&sn=6220c2ed0d73feade0c9691fbf214ba7&scene=21#wechat_redirect) - -2、[快速提升效率,这6个 Pandas 技巧一定要知道](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491909&idx=2&sn=3f3bf16784689a5e8bd5f9ef9478040f&chksm=e88581a7dff208b12e25ddaf6a047abc47878eee56f85309b6618cfd92a4dfeb2445f23e2f05#rd) - -3、[50题 讲透 matplotlib :从入门到精通](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486026&idx=1&sn=7b333759709c30fe442732b7a107e3c1&chksm=e8866aa8dff1e3be37f5169ac53bbcf71a76514e99fbbcbe458ec15aa1204d988ad44d689eb3&scene=27#wechat_redirect) - -### 3.2 数据可视化 - -1、[可视化01|一图带你入门matplotlib](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485022&idx=1&sn=76d4270a15c430217588bd9f8be8303b&chksm=e88666bcdff1efaa35c6d685f19b04e62f58e768dc9d1819bcb9f8211f65d3fd83296259d923&token=1148998814&lang=zh_CN#rd) - -2、[可视化02|详解六种可视化图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485020&idx=1&sn=7f82736e6a2e5442ed59c82d8e242ca4&scene=21#wechat_redirect) - -3、[可视化03|用正余弦学习matplotlib](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485019&idx=1&sn=9d44ee27fd888831e94845d6d0256deb&scene=21#wechat_redirect) - -4、[可视化04|子图与子区难点剖析](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485014&idx=1&sn=90dbdf17f049c152944a4c28642df249&scene=21#wechat_redirect) - -5、[可视化05|绘制酷炫的GIF动态图](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485007&idx=1&sn=1464dee30d006ddb5111f9827b0c4081&scene=21#wechat_redirect) - -6、[可视化06|自动生成图像视频](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485002&idx=1&sn=f09c089328eb0fe39721b73272c81214&scene=21#wechat_redirect) - -7、[可视化07|50个最有价值的图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484920&idx=1&sn=92eb2fd55f13a8bda75a7103a73f8d50&scene=21#wechat_redirect) - -8、[ 可视货08|利用 Flask 动态展示 Pyecharts 图表数据的几种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485658&idx=1&sn=f78e976889de52c0ce35e862bafacd8a&chksm=e8866838dff1e12e41b8f2dd1a43dc540ff39484ae64831332d233b5a4f7ca3b439d370ef1ed#rd) - -9、[一个没法商用,但是好玩有趣的 Python 手绘图形库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491212&idx=2&sn=66a5fd4f672b668aee6bb2c3a01a92f6&chksm=e8867e6edff1f77813b2b59f6219d18041963e6011afa1a590925c71ebd79c7e90b03e82c606&scene=27#wechat_redirect) - -10、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect) - -11、[一文学会制作 6 种炫酷的 Python 动态图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486111&idx=2&sn=fadf51d43bc468c58ba6885db3006e7a&chksm=e8866a7ddff1e36b5815a9693150799d5925727f5a425e642fd136283ff4a58b7f19ba9da39d&scene=27#wechat_redirect) - -12、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect) - -13、[超硬核的 Python 数据可视化教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=2&sn=2aede70646f9ee34ad526d2d99b6549e&chksm=e885824bdff20b5d428dbb09daeae2f3e96f1362372bf6ffb16fae1f06263703379873025d43&scene=27#wechat_redirect) - -14、[收藏!最全的可视化学入门算法教程(Python实现)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491520&idx=1&sn=55804b80dcc56a3923255abf58e08968&chksm=e8867f22dff1f6340a7f61f7796a1e78bd695278a340351860efc244c8a9bbf80cc02bbfaf05&scene=27#wechat_redirect) - -15、[50 款数据可视化分析工具大集合,总有一款适合你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=1&sn=f447d6e27504611baf8a4ad4056c6e5a&chksm=e8867c53dff1f5450ff808baec53797bd772cdd3c0e43f2f0c278cd4b8867223d5591ab2aa7d&scene=27#wechat_redirect) - -16、[用Python画漂亮的专业插图 ?So easy!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=1&sn=14df276f09201930fdc413d8f9a772df&chksm=e8867a29dff1f33fd0f85cf4412c42cc1145e917b3cd06e158aa31356525799033dd8c6e620f&scene=27#wechat_redirect) - -17、[太好玩了,看看我用 Pyecharts绘制的“时间轮播图”](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=2&sn=6359a534033c0a6f21af28a7d9f8b768&chksm=e886789bdff1f18d6719558e305453cba2b6c8a55d66c9bcb6dd5678209d89c1594d87de0a87&scene=27#wechat_redirect) - -18、[实战!用60行Python代码画出30万条房产数据分析图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=1&sn=6b9c7281d3090783a9a486b81f749789&chksm=e88676eadff1fffcec00d3c44ee9b34496dc886417da55aeabed017d496da85d442f205c227d&scene=27#wechat_redirect) - -### 3.3 工具使用 - -1、[ 整理了 50个 IPython 的实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485613&idx=1&sn=70f89faf2573b3025edc73f525f22a0a&chksm=e886684fdff1e159509d30fd0a24854da39fb5a6ae55e0ed6e40b7971d3219968006f80aad7b#rd) - -2、[ Jupyter NoteBook 的使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485024&idx=1&sn=aac0db4b2c97c7f5c2871342b936eaa2&chksm=e8866682dff1ef947ec6474b8c03f668e462232ebd0ec0f137d42e5e63b9b66a3d9e303c1caf&token=1148998814&lang=zh_CN#rd) - -3、[Jupyter Notebook最常用的五大配置技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491411&idx=2&sn=88c290af7793ddc024cf2a39875b97e8&chksm=e8867fb1dff1f6a731077d749b1ea44fd558995f3448e27da9becf7b035ed777774d1b0bb0ec&scene=27#wechat_redirect) - -4、[强烈安利!这十二个 IPython 魔法命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488777&idx=2&sn=3b14fe7faa4d2a26b702c15ebe6b6103&chksm=e88675ebdff1fcfd471dc84c802dab2c37889d59e55711c5d521c80fdbf38291b4092b840083&scene=27#wechat_redirect) - -5、[真香!安利 6 个 Python 数据分析神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488619&idx=1&sn=0c6c76b9deaa4f1976dcacaaee018312&chksm=e8867489dff1fd9fa2b0ac08696c4dae1b48054ca64aefef6d2117179dbbffbfe4ae9d1a4f81&scene=27#wechat_redirect) - -6、[使用 pyecharts 打造酷炫的 BI 大屏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=2&sn=b2ee3058d63ebf702f3f0f9a6ca9c623&chksm=e8867010dff1f90686948269ca5e0907d726fc775029cc414285d71c0176ce859b818d2e34d5&scene=27#wechat_redirect) - -7、[酷炫的动态可视化交互大屏,用Excel就能做!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=2&sn=2826a69402912a70955209192e761c4d&chksm=e8866bb5dff1e2a30d374d5f46afa1cb9d952a0c939701b9f8b5bc52df4169d5e2da30236505&scene=27#wechat_redirect) - -## 04. 开发工具 - -1、[代码调试|无图形调试工具 - pdb](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484969&idx=1&sn=a99fc31865edc4b439707d2be6f66654&scene=21#wechat_redirect) - -2、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484970&idx=1&sn=9ac9c5dfcdc8c6b48a7b4c9854825734&scene=21#wechat_redirect) - -3、[优化Python开发环境的几个技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485131&idx=1&sn=f1bd7b31a3624f015c74f56a8888a695&scene=21#wechat_redirect) - -4、[让你重新爱上 Windows 的小众软件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484964&idx=1&sn=1479536416788a6c55dddef70167eb88&scene=21#wechat_redirect) - -5、[Python 的命令行参数解析工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484960&idx=1&sn=3456d9a05b35588b060833ceb189db6e&scene=21#wechat_redirect) - -6、[搜索神器 EveryThing,你把它的潜力用到极致了吗?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484894&idx=1&sn=59877fe818739175d3d075458594f563&scene=21#wechat_redirect) - -7、[开发工具|盘点 Xshell 的那些奇淫技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485043&idx=1&sn=8c240e569c60607cd4cea8888a6aa2e1&scene=21#wechat_redirect) - -8、[告别996,全靠这个Python补全利器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484824&idx=2&sn=8f20af371954dfd2a5c890bb5814b4c9&scene=21#wechat_redirect) - -9、[学会这21条,你离 Vim 大神就不远了!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484902&idx=1&sn=d677261fda90b67bf5eda886447a4f77&scene=21#wechat_redirect) - -10、[用 Python 做开发,做到这些才能一直爽](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect) - -11、[这款神器,能把 Python 代码执行过程看地一清二楚](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484878&idx=1&sn=9bc941ff19ec38e7c7137a22df981f27&scene=21#wechat_redirect) - -12、[如何把自己的 Python 包发布到 PYPI?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484835&idx=2&sn=f24a415d47d4122c4d6b1a6ccf254a51&scene=21#wechat_redirect) - -13、[谁说 Vim 不好用?送你一个五彩斑斓的编辑器!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484872&idx=2&sn=ce718e511b754fe936172724a824a716&scene=21#wechat_redirect) - -14、[迄今为止,我见过最好的正则入门教程(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484831&idx=1&sn=077d14410db6a906c3b962f0ba18b2d4&scene=21#wechat_redirect) - -15、[迄今为止,我见过最好的正则入门教程(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484830&idx=1&sn=4d48b1c7f2b5234992732f8f15b868df&scene=21#wechat_redirect) - -16、[Win 平台做 Python 开发的最佳组合](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484837&idx=2&sn=68935077ae9eeb6fe4f708565aa0a9ed&scene=21#wechat_redirect) - - - - - -### 4.1 PyCharm - -1、[受用一生的高效PyCharm使用技巧(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484946&idx=1&sn=12a891db18e9d1233535fa4aca9a3892&scene=21#wechat_redirect) - -2、[受用一生的高效PyCharm使用技巧(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484942&idx=1&sn=9dbb2cfe2a277bd3519d37414fcc608e&scene=21#wechat_redirect) - -3、[受用一生的高效PyCharm使用技巧(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484932&idx=1&sn=347b35ccdee94895652caa86191360c8&scene=21#wechat_redirect) - -4、[受用一生的高效PyCharm使用技巧(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484918&idx=1&sn=66b3fd784050e5cb85109faa3d063993&scene=21#wechat_redirect) - -5、[受用一生的高的PyCharm使用技巧(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484914&idx=1&sn=cd1282f94f9326647b05ad9090afd2c8&scene=21#wechat_redirect) - -6、[受用一生的高效PyCharm使用技巧(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484907&idx=1&sn=647a75b96884b39c57664f62ead11b89&scene=21#wechat_redirect) - -7、[受用一生的高效 PyCharm 使用技巧(七)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485372&idx=1&sn=3ed61c4ccbb089358213304cd70bb0f7&chksm=e886675edff1ee487911e922279675264916dd08120f5f40113f37dc080b94c7bf9ad67719c4&token=1148998814&lang=zh_CN#rd) - -8、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484304&idx=1&sn=7612f0c2fd6232723ca2733fb1e8f46d&scene=21#wechat_redirect) - -9、[手把手教你打造一个顔值超高的IDE](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485052&idx=1&sn=2e0bb1a3af4206fd2cf8e3650f695e6b&scene=21#wechat_redirect) - -10、[玩转 PyCharm ,这篇文章就够了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487629&idx=1&sn=9a19cbcce2effcc242cc8d6d33cd2762&chksm=e886706fdff1f97955e73495c0be5b104f834d569d75e9e1470417f79e1d19ff4937683484ad&scene=27#wechat_redirect) - -11、[用动画展示 Pycharm 十大实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486118&idx=1&sn=990c35adb86919bda2f2cb82e7c5fb9e&chksm=e8866a44dff1e35200244e3d369ad6c0f736d3511dcfb086b7641339e20ae62e35b8fd60bdd9&scene=27#wechat_redirect) - -### 4.2 VSCode - -1、[这 21 个VSCode 快捷键,能让你的代码飞起来](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484893&idx=1&sn=421b544115efad388314ccd027761b40&scene=21#wechat_redirect) - -2、[神技巧!在浏览器中也能用 VS Code](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484859&idx=2&sn=70abd19426374271a10cbef8e1645c7a&scene=21#wechat_redirect) - -3、[生产力终极指南:用了两年,如今才算真正会用VS Code](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=2&sn=a39a7cb70908d442a399ac80a272f90e&chksm=e8858311dff20a0782cca942c7e9f3444bb443cf816fd05d76c5f7c234930afacb6c948802bf&scene=27#wechat_redirect) - -4、[VS Code的7个开源替代品,全都知道算我输!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=2&sn=ef0eca9da6eff8405a5a48d89a6b2e54&chksm=e8867a29dff1f33f29487864efb1389b02705d81d0198df581b5b71085ff238f24ffe99f154c&scene=27#wechat_redirect) - -5、[再见了,PyCharm](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489522&idx=1&sn=d7d45dba4a7c9e2f010d09f757639a19&chksm=e8867710dff1fe0657a16b6087ad4a50fdceb8f324bef5f950b3320e33e09ce10d1fa045827d&scene=27#wechat_redirect) - -6、[微软推出 Pylance,改善 VS Code 中的 Python 体验](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=2&sn=58f6dbe10cba98cebab0fd19f124e4df&chksm=e886750cdff1fc1a6cfe249a2f76213fef1a789f4a5006704e33d00b9abadde489af7052a8a9&scene=27#wechat_redirect) - -7、[VS Code 连接远程服务器运行 Jupyter Notebook](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486773&idx=2&sn=75b2453513b1445d9881f5ec8c1f44d1&chksm=e8866dd7dff1e4c167b27eccec9a6469b7988d6b05010160247b3e1a549764201807212ed581&scene=27#wechat_redirect) - -8、[GitHub 发布重磅更新:你电脑上的 IDE 可以删了?!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=1&sn=e5bd46de0616fad6105d2e46a359cd3d&chksm=e8866bb5dff1e2a37058e1cbc502863f8b8a885a493b7ef9f5e442a1b554ffeb0ae09d77026e&scene=27#wechat_redirect) - -9、[用 VS Code 写 Python,这8个扩展装上后无敌了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492505&idx=1&sn=ba354ab86500280d46038d2fa2f3572b&chksm=e885837bdff20a6dd2d4411719e44a3b88371b5006e9dc2b2580e6862f3161e70cb339240512&scene=27#wechat_redirect) - -### 4.3 好用的库 - -1、[使用 Python 远程登陆服务器的最佳实践](https://mp.weixin.qq.com/s/aRXYAP9D9rgil-0_Etb0SQ) - -2、[这么设置 Python 的环境变量,我还是第一次见](https://mp.weixin.qq.com/s/LcjIPZPSg0KbL9X-i6vigg) - -3、[太强了!Python中完美的日志解决方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485741&idx=1&sn=505c06669baa44873171a3eed81f32b4&chksm=e88669cfdff1e0d91635b75f54998667e04bfb05a1aebae096930bd23acdd9d35ea858861bb2#rd) - -4、[如何使用 Python 操作 Git 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485723&idx=1&sn=35511b71347111c00a9e1633309efd13&chksm=e88669f9dff1e0ef7aec03f2bdbddb8408bf12a57a36add441b75e486dd72df13be7629625ab#rd) - -5、[用它5分钟以后,我放弃用了四年的 Flask](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485657&idx=1&sn=e21ddc5b670a9b667135df2ca97ec99c&chksm=e886683bdff1e12d9e1d64a7f5bfbc65ca2372711cd9b08ef36ce0cdd99811ce44ff162585df#rd) - -6、[整理了 34 个被吹爆了的Python开源框架](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485653&idx=1&sn=edd893dd711b3681aa0bacdf8ab384fd&chksm=e8866837dff1e1216f6fbf413118f839e6e1dc5ed7b45117df55f13e25670c5ffba34966abff#rd) - -7、[ 为了选出最合适的 http客户端,我做了个测评](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485651&idx=1&sn=647c22ac66e469ccc33478457dadf224&chksm=e8866831dff1e1279149ff1e3af84bef86d24c524b7b7c14e9c519cf7c4739fa8f10dcc4a8d0#rd) - -8、[凭什么 FastAPI 火成这样?看这篇文章就知道了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485891&idx=1&sn=82bde3e83435ebb6beb7e3581f12f7b0&chksm=e8866921dff1e0376befb029e9f1d81f33b0fa55242182f8de20d2a6e6b0cce0b5efd02f9aef&token=2013245174&lang=zh_CN#rd) - -9、[要想 BUG 变得富有美感,这个库你一定要看](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485889&idx=1&sn=0258eb138dce2c71b533d340770d5d09&chksm=e8866923dff1e03548dee4f780b410c22b51e1c3ffde3ef814f6a9b94e49af941fc04c79a660&token=2013245174&lang=zh_CN#rd) - -10、[如何使用 Python 输出漂亮的表格?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485841&idx=2&sn=705c123abed5441a52f8640b65f3c400&chksm=e8866973dff1e0650e4b2e025d0e86b3aedeb7ea8f68ad560f37cb033f36660b6eee15fb3ce4&token=2013245174&lang=zh_CN#rd) - -11、[用 Python 写出来的进度条,竟如此美妙~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492108&idx=2&sn=2fa1e31f9684df6d6f3da21aa4cba6e5&chksm=e88582eedff20bf886c29d480245d835c0a3fbea49e54909abbeb703bb7149d4e3444202e97c&scene=27#wechat_redirect) - -12、[推荐一些能提高生产力的 Python 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491947&idx=2&sn=765ca757e0aee7fd4dd3f7779f409ff3&chksm=e8858189dff2089f74a97972c1f213291a8680a766b328a74c609f465f7dcf16c324df57a660&scene=27#wechat_redirect) - -13、[规整字符串的数据提取神器:parse 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491617&idx=1&sn=b77cd25d6c92f86c1492f913edcb51c7&chksm=e88580c3dff209d500525bbc81464034ca431c92603b5ec83046faaac7ca4c012034964805e0&scene=27#wechat_redirect) - -14、[微软再出神器,这次终于对 Python下手了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491407&idx=1&sn=0044bf683ddc4799ef2e7c04fb75ebf8&chksm=e8867faddff1f6bbbae3e716e7b686eeb4a5b49f19a2fb2b8743ae2e641ee529189d313f6fe8&scene=27#wechat_redirect) - -15、[这个 Python 库有点黑科技](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=1&sn=700979655adce734f5300e7ee380d4ab&chksm=e8867e4bdff1f75d355b34ca8561844c920a2434357350e06a87c99cdbdfb094ee29e7957988&scene=27#wechat_redirect) - -16、[适合 Python 入门的 8 款强大工具!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491088&idx=2&sn=23cd3b06b8117a5d996d258dd9c0eb23&chksm=e8867ef2dff1f7e479ad5a633295f1be0fc272f6b4de2526c21129b2dee23cb517e51b508f8f&scene=27#wechat_redirect) - -17、[这些Python库虽然冷门,但功能真的很强大!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490868&idx=2&sn=7bda9c9021cd26eb43d752f6ff84dbe7&chksm=e8867dd6dff1f4c0398b427f13787c99f24cea2a7b2c7b10fbc3316b0215b3ea861f197e604f&scene=27#wechat_redirect) - -18、[那些被低估了的 Python 库,看看你用过几个?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490782&idx=1&sn=2c710eca844da200dac14ab017ffcdb3&chksm=e8867c3cdff1f52a86c291e34e458f57bd5572c6fdeb75cdb84fdfba2d9bb1731067dca7d195&scene=27#wechat_redirect) - -19、[用 Python 写出这样的进度条,刷新了我对进度条的认知](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490326&idx=1&sn=867061f4ad793e957161c30be77ef9bb&chksm=e8867bf4dff1f2e2f1cdc5af4366540d33fc0edfc52810af09b82a590fcd6ec4c5c2d7a528c6&scene=27#wechat_redirect) - -20、[少有人知的 Python "重试机制",请了解一下 tenacity ](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489274&idx=1&sn=7ddc4505f932b386ee22c7873ff5df1a&chksm=e8867618dff1ff0ea5aa55c6acfeb85944028f1bb0be7fb2697f4ec018de297579b797ce048b&scene=27#wechat_redirect) - -21、[一个极具意义的 Python 前端开发工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489090&idx=1&sn=1e7ee21422440dc32d7a9a40eaab6c4e&chksm=e88676a0dff1ffb6731df2996d3a4011eebdd8af4926920eefb30d086fa909943179e83d8957&scene=27#wechat_redirect) - -## 05. 网络基础 - -1、[点亮你的 HTTPS?原来这么简单!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=1&sn=46b095516bf3c0ebfc367ab64f8fd42c&chksm=e885824bdff20b5db2861a411a07653135ffbafaa1e772588bcf86cf9ad54433a0ff492355ad&scene=27#wechat_redirect) - -2、[手绘 10 张图,把 CSRF 跨域攻击、JWT 跨域认证说得明明白白的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489610&idx=1&sn=c9442c4ad370c318ea7ebafb26208b9c&chksm=e88678a8dff1f1bec43cd601b0c0e3eaff32d168dc2ad69f6273e27f2e6d72b49b7585046057&scene=27#wechat_redirect) - -3、[网络出了问题,如何排查? 这篇文章告诉你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488835&idx=2&sn=edac7045eb00479481365334ba198f50&chksm=e88675a1dff1fcb76f10049aa97ade7c0669fc1a8c32649a810c1d43b37c7dbe964c17b0730f&scene=27#wechat_redirect) - -4、[肝了三天,万字长文教你玩转 tcpdump,从此抓包不用愁](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488684&idx=1&sn=9a9d717a6c00f978c6575cb934c5c942&chksm=e886744edff1fd58ebb593338484013442c8c2f481497d6c5246cabce3ad43c1ee28299f9098&scene=27#wechat_redirect) - -5、[三张图彻底搞懂 iptables 和 netfilter](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=1&sn=44856bba34b906930780535a7000a5af&chksm=e88674f4dff1fde263c119bb66a5fa879e6c07b12d50035552dbd529757bedc45a3a41823013&scene=27#wechat_redirect) - -6、[tcpdump / wireshark 抓包及分析](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488505&idx=2&sn=5e7d7a204c9eef4537fd352090086685&chksm=e886731bdff1fa0d3ba2c9a951fdbd849858a58ea630adab247f3b8b3ff7e76d7de022a8270d&scene=27#wechat_redirect) - -7、[100 个网络基础知识,看完成半个网络高手](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488490&idx=1&sn=a8725e200b3ac351ab4f0a2b6d112199&chksm=e8867308dff1fa1e1d55c89b691394902d5c7f8965063acd8551ad14269135d603f7bef22281&scene=27#wechat_redirect) - -8、[网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&scene=27#wechat_redirect) - -9、[网络知识扫盲:一篇文章搞懂 DNS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487859&idx=1&sn=55c2c40b807a6bd7036763e99954f063&chksm=e8867191dff1f88743bf88b809471926245a0ec3ef9c9d2c9f311d99533ed5656b7b3617431c&scene=27#wechat_redirect) - -10、[路由器里的广告秘密](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=2&sn=4cf42aeef93396f7665a06b2d1dc7fba&chksm=e886768edff1ff98bbd9572d85ddd97ef91ad8d19fdd951c15520cd9756d2c40a53d7cd8111c&scene=27#wechat_redirect) - -11、[10张动图,让你搞懂计算中很重要的名词](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487331&idx=2&sn=70146e780ca63c55ca3446a5e238b487&chksm=e8866f81dff1e6974610866f756b099e0b8bed5389cfa594f4b94b6092ae31db305b1485aadd&scene=27#wechat_redirect) - -## 06. 实用工具 - -### 6.1 Linux - -1、[值得收藏的 14 个 Linux 下 CPU 监控工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485356&idx=1&sn=9dc2f440299ad179e1874febdffa04d6&scene=21#wechat_redirect) - -2、[19 个没什么用,但是”特好玩“的命令](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485142&idx=2&sn=97b0f02f8f0153b1c7ba4899359d55e4&scene=21#wechat_redirect) - -3、[相见恨晚的15个 Linux 神器,你可能一个都没见过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484887&idx=1&sn=3473df33893a23b4de1e176985a5265e&scene=21#wechat_redirect) - -4、[这 22 款 CLI工具,每一个都是精品](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491148&idx=1&sn=92b194ad574e8b793ebb608319c7d1df&chksm=e8867eaedff1f7b8e257ce6e80890cef6aa72b9078d20a0e1f0241a1c05f660c69891b6a7d12&scene=27#wechat_redirect) - -5、[Github 热榜项目:如何让你的终端酷炫到没朋友](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488641&idx=2&sn=c8ea38a8e216f123ca320d3ec8449bc0&chksm=e8867463dff1fd7522e9556f3ca97bb691ea92625ca664919dddabd35ead37dbd06a16b75b17&scene=27#wechat_redirect) - -6、[如何在1s内创建上百G的超大文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487906&idx=1&sn=a892ae3f3ab0ed0a2a20cf5d51ff1c65&chksm=e8867140dff1f85674d8c01df3c3d4a92cc75cdd34c68c3b86f8c5eb660f500f033ed35deef8&scene=27#wechat_redirect) - -7、[千万不要运行的 Linux 命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486845&idx=2&sn=31c1ba636db4d687b16b10981833bfbc&chksm=e8866d9fdff1e48976cab2cb62e3b4a4e2e373aeaa0e961847e49cd13ec4ea417b5162fa165b&scene=27#wechat_redirect) - -8、[不想装系统?这8个网站让你在线体验 Linux](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486303&idx=1&sn=fc6c9b73fcb12c5c0c8257e223c8f657&chksm=e8866bbddff1e2ab5edf8b3d4783bcc65975c0385c140d1c954e7d4949e334b9d6ed92ae98a2&scene=27#wechat_redirect) - -### 6.2 版本管理 - -1、[关于 Git 和 GitHub,你所不知道的十件事](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485232&idx=2&sn=c3314e79af9029bdd76ae187f7b4eb19&scene=21#wechat_redirect) - -2、[关于 Git 图谱,这一篇文章讲得很细。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485179&idx=2&sn=a9f0a8a669d70c0dccedf71a530d1d93&scene=21#wechat_redirect) - -3、[如何巧妙处理 Git 多平台换行符问题(LF or CRLF)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485333&idx=2&sn=4e8e95e5cb12d27ea6b510aaaf15b4c0&scene=21#wechat_redirect) - -4、[这 7 个高级技巧,不会还怎么玩GitHub](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484940&idx=1&sn=9ad168943b03dff09d53e5d73e13ed46&scene=21#wechat_redirect) - -5、[ 神器!这款VSCode插件能填满Github绿色格子](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485595&idx=1&sn=65df21ab0b4042953dd8257bea8b1848&chksm=e8866879dff1e16fff97584467f2f1728f9c2da007894ae062e9201d53b82a30054036f8e81e#rd) - -6、[Git 中冷门却又非常需要的高级用法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485890&idx=2&sn=528df28585ec16114324e391dbfebab4&chksm=e8866920dff1e036d5da7f5dfe3c944ef8b13d72f022d72b209117120c7e27ebb71036b99893&token=2013245174&lang=zh_CN#rd) - -7、[别乱提交代码了,来围观下大厂的 Git 提交规范](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486877&idx=1&sn=f14a8add6cee89f8312c7077e4e1ed22&chksm=e8866d7fdff1e4691e6b3627fdd7963942d4d568a1e1ba1d7e8ba81944e6afaadc6923429006&scene=27#wechat_redirect) - -8、[盘点提高国内访问 Github 的速度的 9 种方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491802&idx=2&sn=30df1c954dd8505639c6cd71c0e37d76&chksm=e8858038dff2092eed0b603635b648f2c52d720e98536aee1d50b774c9feac6b25fec6ca9a7c&scene=27#wechat_redirect) - -9、[工作流一目了然,看小姐姐用动图展示10大Git命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486154&idx=1&sn=9a409d493eb789c66cb5c3596195582c&chksm=e8866a28dff1e33e13148dbb98dd8a3899392481f4806805bc96de3b6dc7a954ef3eb899bace&scene=27#wechat_redirect) - -10、[如何用 GitHub Actions 写出高质量的 Python代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485985&idx=2&sn=ec7a102012096847b8fdf7156d9260f3&chksm=e8866ac3dff1e3d53d2ae587e0769b1c42ab0176a7982751fc9a740ae45fe1a592691f373dcc&scene=27#wechat_redirect) - -### 6.3 数据库 - -1、[写给程序员的 MySQL 面试高频 100 问](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485321&idx=1&sn=30432df24d4e7858a475192bcf233914&scene=21#wechat_redirect) - -2、[开发人员必学的几点 SQL 优化点](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485892&idx=1&sn=e0b526002a5568391e75fb0cac60ab0b&chksm=e8866926dff1e0303f2717e95ff801c2550d97812c1a775cc232d48183af802db1be54f26105&token=2013245174&lang=zh_CN#rd) - -3、[再见,Navicat!同事安利的这个 PyCharm 的兄弟,真的香!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=1&sn=754ca33e3ddbde88ee1b7b918e89d39c&chksm=e886791adff1f00c3cd0ac513cae55ac3bbb2732c637433d15bf42c22139dd54ee47f399b492&scene=27#wechat_redirect) - -4、[太牛逼了!一款软件几乎可以操作所有的数据库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=1&sn=b83bab19f921bcd104caf3ed0f1cf0e3&chksm=e8858311dff20a070cd8063efbca76c74439b18f6c8863f3da14621afb082d246f204ccdba92&scene=27#wechat_redirect) - -### 6.4 Chrome - -1、[推荐 8个 超实用的 Chrome 插件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484925&idx=1&sn=110e4da78a20e8b5721f00bb36ba9d4c&scene=21#wechat_redirect) - -2、[没有这 42 款插件的Chrome是没有灵魂的](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485016&idx=1&sn=ba38963413ee2926f6f5e69b1427491d&scene=21#wechat_redirect) - -### 6.5 Windows - -1、[没钱买 mac?一招教你如何让 Windows 秒变 macOS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=2&sn=d82c0faaf39c420729fba238c65d2fc8&chksm=e8858366dff20a70abbabb14693f265d7fdb1c831cd2b464c9cf1ce8431feb4f07cbb76627ec&scene=27#wechat_redirect) - -2、[微软太良心,这么强大的软件竟然完全免费!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=1&sn=80c8c0247f9100e29f7066c874400755&chksm=e88581e3dff208f503da7dafdee037e8db578186dbef79df648c054f54c6e86b9336ff86bde3&scene=27#wechat_redirect) - -3、[双系统的日子结束了:Windows和Linux将合二为一](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491306&idx=1&sn=dfdbc7e02b54622c2eed5f36501444f0&chksm=e8867e08dff1f71e02038aa720fdd99c524adbc6687c2f32070af638b713a154f0f9767205cc&scene=27#wechat_redirect) - -4、[这个只有1.5M的软件,能让你的网速快3倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488856&idx=1&sn=8a96c373b04c4ce7d78c7f87f5928c1e&chksm=e88675badff1fcac0086cb2ec20845821d2441d0909b9a3395b249744217944b1fb8f3e442c9&scene=27#wechat_redirect) - -### 6.6 其他工具 - -1、[整理了 11 个好用的代码质量审核和管理工具](https://mp.weixin.qq.com/s/DH_TOA3Cr3y37yZ5F4bU5A) - -2、[11 个最佳的 Python 编译器和解释器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485682&idx=1&sn=31b1b68432ca1f1db3866fb0f670270b&chksm=e8866810dff1e106a21c63c1ee198069223047afc72482c1f14ee4f473211aca85273c57d529#rd) - -3、[太酷了!用上这 5 款神器后,你写的代码逼格瞬间爆表](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486248&idx=1&sn=ff17f64475d82e99d32e35f290a49edd&chksm=e8866bcadff1e2dc2ba61ca6a0f63fd753d60e77891ee0ae93207890ee891a9097a6d27b5695&scene=27#wechat_redirect) - -4、[我最喜欢的云 IDE 有哪些?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485965&idx=1&sn=3457ed667d64773bd83bdc3432c06dc7&chksm=e8866aefdff1e3f9407dce655688e2899c177c17a6472b0d50c15e2454cd6450165aee81877d&scene=27#wechat_redirect) - -5、[整理了6个好用的程序员开发在线工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489186&idx=2&sn=6099d58947654ddb35b43d7067d574d6&chksm=e8867640dff1ff56fb33df9d0de1ea820f14f3690d11184b1fcf02fa9480450a0b5639a754df&scene=27#wechat_redirect) - -6、[Github上这些可视化面板也太好看了吧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488270&idx=1&sn=459867f1761d29e4a46f2e450e3ea975&chksm=e88673ecdff1fafaa0f1f89c9739b928e0227ac54059d4ff96c15faf8b88cd3752a929e018e1&scene=27#wechat_redirect) - -## 07. 代码优化 - -### 7.1 算法讲解 - -1、[策略模式:商场促销活动背后的代码哲学](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484937&idx=1&sn=61b81dddfb20d80060fee5f22ab28d5f&scene=21#wechat_redirect) - -2、[算法教程|八张图带你轻松理解经典排序算法(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485042&idx=1&sn=b9e8503905be5a960119c2893995fb6d&scene=21#wechat_redirect) - -3、[算法教程|八张图带你轻松理解经典排序算法(中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485041&idx=1&sn=12d05e0316c8866e3876c3ed484ced15&scene=21#wechat_redirect) - -4、[一次忘记密码引发的算法思考](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484981&idx=1&sn=96f49ab2225c61b45601b68521135dd6&scene=21#wechat_redirect) - -5、[程序员走楼梯都会思考的一道题](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484993&idx=1&sn=36cc1466ed73af2fcf4a8430368a62ee&scene=21#wechat_redirect) - -6、[以为是高性能神仙算法,一看源代码才发现...](https://mp.weixin.qq.com/s/L_OvCcpIFKBLckLBvit8UQ) - -7、[ 用Python手写十大经典排序算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485494&idx=1&sn=1580e9fc17086bc83e30b0fa4c850bd8&chksm=e88668d4dff1e1c27a26b31fda2f0e413be83519aa9dd0e3c8d3cfd08a2478eb2596ed7ba535#rd) - -### 7.2设计模式 - -1、[单例模式告诉你只能娶一个老婆](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484921&idx=1&sn=493d0a492277ecb223ddfff9de8720ff&scene=21#wechat_redirect) - -### 7.3 代码优化 - -1、[看了同事的代码,我忍不住写了这份代码指南](https://mp.weixin.qq.com/s/goqj1YVdKV171LLpjtXruQ) - -2、[怎样才能写好一个 Python 函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485416&idx=1&sn=85b14857e795a92985230ba3f51b73f6&chksm=e886670adff1ee1c7f4266d4e3b931d2e3d1de4dff30dc869eae2cd174da7f54080787f60a1b#rd) - -3、[三个异常处理的好习惯](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484947&idx=1&sn=2bd31279dd9714045ed4dc5481b44208&scene=21#wechat_redirect) - -4、[超实用的 30 段 Python 案例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484866&idx=1&sn=9c85dd1b58f319bb0339e2db577a0be6&scene=21#wechat_redirect) - -5、[删除系统 Python 引发的惨案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485386&idx=1&sn=59eed0894159f4d0599be3fb787b5201&chksm=e8866728dff1ee3ef9fb7a0464df481306be11d9aeba944804e7ea74e660501d42e159ada7fc#rd) - -6、[提速72倍,在Python里面调用Golang函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492536&idx=2&sn=360f13b4678fcc6a6f14ee6221474285&chksm=e885835adff20a4ca2025c926c754b9bf6accc6b5b7021f97878163491122ea97d2721c279f6&scene=27#wechat_redirect) - -7、[这一行代码,能让你的 Python运行速度提高100倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492321&idx=1&sn=87dc6b52a0c63b8326bcc50b2c7b3a68&chksm=e8858203dff20b15d1e4730e2851d7523c30db9b404fdfa5b1b19978a1e326e2205e3d6bf7ac&scene=27#wechat_redirect) - -8、[三行Python代码,让数据处理速度提高2到6倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491111&idx=2&sn=d27c6398b82749a20c46490b8fdd900f&chksm=e8867ec5dff1f7d3e9f95f221a2f33003bec76da1f0c002f97a59c7887cf460c887e1ad5a579&scene=27#wechat_redirect) - -9、[一文教你节省 90% 的内存占用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489739&idx=1&sn=6589629f52a3a6ec18e67a3ca4952616&chksm=e8867829dff1f13fa87b08ecd09ce319dbd955c8ee15b44c60509e171f1920a6da600075dcd0&scene=27#wechat_redirect) - -10、[写出漂亮 Python 代码的 20条准则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488402&idx=1&sn=231c2954b0ccca44a18268ab80ddbe88&chksm=e8867370dff1fa66a12d6af4adb66a1d80cbccbf9093c3a8b966a406bf5d8fc005a08166ef45&scene=27#wechat_redirect) - -11、[阅读优秀的项目代码,几点实用的经验分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487922&idx=2&sn=c330d9b4c7bbc1b838e8a9fe90283c27&chksm=e8867150dff1f846628f181ff9687ebf91ba5d06f7721a67bf746698c6f02ea398cdc33c5a0d&scene=27#wechat_redirect) - -12、[一行 Python 代码实现并行](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487299&idx=2&sn=5dffb95993b6fd87c6a3dd1a56b56814&chksm=e8866fa1dff1e6b78239ffa2fe8d124e4ee3af2b7b71f8e9593bdeae2221fcd2afa2b5773e26&scene=27#wechat_redirect) - -13、[代码被反编译了?这两个小技巧能帮到你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487287&idx=1&sn=05c7ca030257db25623e98ed5d302878&chksm=e8866fd5dff1e6c3219dc6d78717411f716bfa38d89a34ed64a88c0702765258fd34b4edd428&scene=27#wechat_redirect) - -14、[一份可以令 Python 变快的工具清单](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486107&idx=1&sn=83a3f95fea5cb2c24d7f44bf11f2acdb&chksm=e8866a79dff1e36ff4e6c8c546568bca0a36ea3de0550bd87a785ed9ce5cbda4a6d84b5b5fc9&scene=27#wechat_redirect) - -15、[记一次 Python Web 接口优化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486053&idx=1&sn=862e6fbaa8a85a4a10ee35d9eba00196&chksm=e8866a87dff1e391399c252ffcb3168ef1f4c85d5ea52d6f43ceaf81e3f7cb3e548f6de505df&scene=27#wechat_redirect) - -16、[翻车了!pyc 文件居然曝光了我的密码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=1&sn=eed309620be1092cf5af030be6b354ff&chksm=e8867010dff1f90611e155156075c2479bab5c9237e8a514a2c7ffe2a317ba16baa30b76d9c9&scene=27#wechat_redirect) - - -## 08. 冷技巧集锦 - -### 8.1 冷知识 - -1、[Python那些不为人知的冷知识(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485031&idx=1&sn=e5ab670ce6485a50c4b7eeaee11e6f34&scene=21#wechat_redirect) - -2、[Python那些不为人知的冷知识(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485029&idx=1&sn=5db54b5777348e827c274cd932a15a15&scene=21#wechat_redirect) - -3、[Python那些不为人知的冷知识(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485009&idx=1&sn=92e268c20c6f01278e220e11397cc2f0&scene=21#wechat_redirect) - -4、[Python那些不为人知的冷知识(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485001&idx=1&sn=5a02f8c912518d15a0b96319cf2da7d1&scene=21#wechat_redirect) - -5、[Python那些不为人知的冷知识(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484997&idx=1&sn=5b8cf44b62550e1bd67284fb2b0780dc&scene=21#wechat_redirect) - -6、[Python那些不为人知的冷知识(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484990&idx=1&sn=bd28368eff832ba2b24a64f637a6d0c8&scene=21#wechat_redirect) - -7、[Python那些不为人知的冷知识(七)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect) - -8、[Python 之父为什么嫌弃 lambda 匿名函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492291&idx=2&sn=3cfdde7bc5e7b55aae00a30c63abe562&chksm=e8858221dff20b3719dfed632d181b95599b843006ae0c2186b5c69593ee7c22c5a5d2c82509&scene=27#wechat_redirect) - -9、[Python 到底是强类型语言,还是弱类型语言?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491532&idx=1&sn=a86bccc1d270390331c36205d3e46497&chksm=e8867f2edff1f6388d80289bff567445c2c9c9414efec74e4a308f7e8a1cabf662c8fc0a6cc0&scene=27#wechat_redirect) - -10、[Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489219&idx=2&sn=0dfa7ae7d4dcdda538b35bbea80b34e4&chksm=e8867621dff1ff372969bf3901835e67a1bfa7cb72709b491987fda3daa0e5f8d15a86c2b6b5&scene=27#wechat_redirect) - -11、[Python 为什么没有 main 函数?为什么我不推荐写 main 函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489175&idx=2&sn=2f8f9fb4b31c91ef71d1f34841da6450&chksm=e8867675dff1ff63e6e1f28eec9f18c41c30c050118eb65cde6960e5f6ee5866d7515f2b0040&scene=27#wechat_redirect) - -12、[Python 列表的这 8 个实用技巧,你都 OK 么?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=2&sn=38d3985803cb5b6ed10e4cc11972eff7&chksm=e88676eadff1fffc88a3a3a2e3815b2abff43318c597344857c9c5f7806b6b0e6c457b33842d&scene=27#wechat_redirect) - -13、[Python 为什么不用分号作终止符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488816&idx=2&sn=6bc8aa32d9a266cbffabcd87664e75fd&chksm=e88675d2dff1fcc4e845a90903bda5dfe59262a2b60533045a03e574076e92aa6f30974a73f5&scene=27#wechat_redirect) - -14、[Python 为什么推荐蛇形命名法?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488517&idx=1&sn=8b449c2d7c831600be6c5d57e9cb0837&chksm=e88674e7dff1fdf1ab8092ac3cdd2f3978ee6b219daa18a5407c390f62e11448c39455437bb0&scene=27#wechat_redirect) - -15、[Python 为什么用 # 号作注释符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=1&sn=820a070f05096dbe2bdb979fc9492cdb&chksm=e886789bdff1f18d420ee3efa8f85468303690232c4005f775191b0a84a1921430e37aa7e74f&scene=27#wechat_redirect) - -16、[!/usr/bin/python与#!/usr/bin/env python的区别](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486008&idx=2&sn=8637cdc12799f084cd5ecdba9534fb76&chksm=e8866adadff1e3cc49b369dddfaeecc1d9b51f13de1f2733266b2f9fc8426d9e3944a15eb074&scene=27#wechat_redirect) - -17、[pip install 和conda install有什么区别吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487164&idx=2&sn=18de23de9c79f3c20e5a432bb675c262&chksm=e8866e5edff1e748cb6c83d2f4368cbab805fd7bfd41820b60da22061818e8d84548ac14714c&scene=27#wechat_redirect) - -18、[Python 什么情况下会生成 pyc 文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486071&idx=2&sn=533257b9b6156bd5588199b349266a3d&chksm=e8866a95dff1e383991b311dc73994ca103c380433a620c5bdf9f8d09b1c49daa74dce6ea941&scene=27#wechat_redirect) - - -### 8.2 冷技巧 - -1、[让Python中类的属性具有惰性求值的能力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485759&idx=2&sn=e32b66c4666f96f84572738549a47a24&chksm=e88669dddff1e0cb0a4b6e6e965c9570b273236fa58a54351ae3b118d6a4bcbd52e452109a63&token=1148998814&lang=zh_CN#rd) - -2、[ 小技巧:如何在 Python 中实现延迟调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485689&idx=2&sn=43e35ff93918ef090480672d4685bce3&chksm=e886681bdff1e10dd0edee95c73a977da51fdeb514dcf8c3bb88f7f9aacc22ce155f45e84268#rd) - -3、[整理了 18个 Python 高效编程技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491418&idx=1&sn=89c35f5d69a54196bd7f32a64171e05d&chksm=e8867fb8dff1f6aee18414c89c31ce118550ba11b645e2e4f6014af4d15d319decc000a70c91&scene=27#wechat_redirect) - -4、[Python 程序报错崩溃后,如何倒回到崩溃的位置?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488434&idx=1&sn=e2b4ca12b228c16e944ff36abec53e22&chksm=e8867350dff1fa46ee7799e460d2b304dbb0c227d33a39d18099761c205319d99a9b4135f9fa&scene=27#wechat_redirect) - -5、[如何用简单的位操作实现高级算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=1&sn=5376f42c8990027132deb52a6b66307e&chksm=e8866e4ddff1e75b3af85905177130d23118254d42e9452930cc784b209cdf294d9bb73aa247&scene=27#wechat_redirect) - -6、[如何限定Python函数只能被特定函数调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490777&idx=2&sn=6572f957117e0a0256da2b85c7221a37&chksm=e8867c3bdff1f52d045cadb962a2da16b015d8455147afe374cf3067dd89c580e1f9514d57d0&scene=27#wechat_redirect) - -7、[pylint 除了检查代码风格,居然还能...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491737&idx=2&sn=f6781d158c5af171f1b916ee7b0c7a68&chksm=e885807bdff2096dc1580abbba594ed1cc1c0994c06f6bd5ede233df0593a43842f7af3e2f8b&scene=27#wechat_redirect) - -8、[让Python在退出时强制运行一段代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486207&idx=2&sn=5586a204b9537d37c02b9926a01719dc&chksm=e8866a1ddff1e30bb77d72ba0013d870f80e0029546729f0e8c65512c4969f5f0733f60ecddf&scene=27#wechat_redirect) - -9、[不使用 if-elif 语句,如何优雅地判断某个数字所属的等级?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486167&idx=2&sn=a31cbf6d3526bfc860b0daa69889193f&chksm=e8866a35dff1e323ed958858223dda1af6ae0d371be0037c35f9ae6894e776dec1ee53c2a00b&scene=27#wechat_redirect) - -10、[涨姿势了,raise...from... 是个什么操作?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486001&idx=1&sn=b9a89305dae7d4f57105be1d274fae43&chksm=e8866ad3dff1e3c51b5fa132147b9f9c9e4ba14b41c5409e62e2f2696d1389c6ac4ddd1e7bec&scene=27#wechat_redirect) - -11、[一些我日常使用的 Python 技巧分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488382&idx=2&sn=f2b090fe47bb56e0463126c69726bf10&chksm=e886739cdff1fa8a65ce4869a8bdb8fbd28396e3fbcdac2a4441b7c58ba5c77cea1765858146&scene=27#wechat_redirect) - -12、[5年 Python 功力,总结了 10 个开发技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488243&idx=1&sn=66d2f534ddcbed582e7ba734b53701f5&chksm=e8867211dff1fb07f2edcebfda9e7863ee57266c15d4276bc89c2ee69baafc46fbc0e85fe703&scene=27#wechat_redirect) - -13、[我发现了个 Python 黑魔法,执行任意代码都会自动念上一段 『平安经』](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490548&idx=1&sn=c71772cc40992d8912fd8a3fa1607038&chksm=e8867b16dff1f20081431362c99fb981d0cd75f2bd5baf981b7db18e08492138aab10f98efb8&scene=27#wechat_redirect) - -14、[实用技巧分享:如何批量更新已安装的库?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487862&idx=2&sn=a1c092467b471db82060b395c341be6c&chksm=e8867194dff1f882da87863f4d4a4ccbd687daa0b4a742dc6d01fd01d0a6ca6c5fb7cc28629b&scene=27#wechat_redirect) - -### 8.3 炫技操作 - -1、[Python 炫技操作(01):条件语句的七种写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485991&idx=1&sn=98e17f91d2f66f62c9aa644c03d8259d&chksm=e8866ac5dff1e3d3f6810a3f9e388d46d95945af9748baea0380ecec83c768eaac09738cf959&scene=27#wechat_redirect) - - -2、[Python 炫技操作(02):合并字典的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&scene=27#wechat_redirect) - -3、[Python 炫技操作(03):连接列表的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486132&idx=1&sn=3827cf8672d4121b77225dbf9d377d69&chksm=e8866a56dff1e34094f2f834a613800480c0cdb078ab5656640fcb8c445ffa020b175b0f55cb&scene=27#wechat_redirect) - -4、[Python 炫技操作(04):海象运算符的三种用法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486158&idx=1&sn=0bd0647702a1599082e5bf1710d89e37&chksm=e8866a2cdff1e33ab4465d88497ea3505ad8fe614365eec7592a8c75053a6c8218206807c1a3&scene=27#wechat_redirect) - -5、[Python 炫技操作(05):花式导包的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486799&idx=1&sn=1be136d4e45c1b6f8a5eda567aaf259d&chksm=e8866daddff1e4bbddc0164ae795d6c302e36226cb3ba76ffc57e5c993b0861ef4ad693c2a56&scene=27#wechat_redirect) - -6、[Python 炫技操作(06):判断是否包含子串的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=2&sn=4596043fb72b75a04a778942c61cc2af&chksm=e8867e81dff1f7979e21f58716b3ce3e09f8515829b15acd5169dd3f160e302d617a1b94cb2d&scene=27#wechat_redirect) - -7、[最全总结:把模块当做脚本来执行的 7 种案例及其原理](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490904&idx=1&sn=ca3725510c7510965cbc76bd1bf06949&chksm=e8867dbadff1f4acb08c0daf08272ca61757909b0b73a4cde1aa4d86965c149c40f44417f7d0&scene=27#wechat_redirect) - -8、[这个 Python 炫技操作千万不要用,别问我怎么知道的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=1&sn=27d55a4b8b717450f1ab0596263f453f&chksm=e8867e81dff1f7975674ab7fcc0019654ae0e867db045fb575623f91cb143801d03aa365be2d&scene=27#wechat_redirect) - -9、[涨见识了,在终端执行 Python 代码的 6 种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488283&idx=1&sn=24a78834ec3434a90ca7c3e6e972495c&chksm=e88673f9dff1faef908c43a41133f65695a68579e0edb2d7b15f131d9be26bcf919d66a7d9e3&scene=27#wechat_redirect) - - -## 9. 云计算 - -1、[一份面向初学者的云计算通识指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484908&idx=1&sn=3085e048da685440408e1bdc54feb4aa&scene=21#wechat_redirect) - -2、[ 如何探测虚拟环境是物理机、虚拟机还是容器?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485401&idx=2&sn=8bfb6e1a71e0777a4d5e55f64b55ace5&chksm=e886673bdff1ee2d8acce291e402cd2ae0c7f64e455fdeea158eb53e535ee79b2aadc449935e#rd) - -3、[超详细教你如何阅读 OpenStack 源码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485899&idx=1&sn=7a578a553c67e794ef6ec2f463864a46&chksm=e8866929dff1e03fa8152751967106e9262980db6afdff106fe9e39854d24d25494b3e8a3a4e&token=2013245174&lang=zh_CN#rd) - -## 10. 职场相关 - -1、[一个专科生的 Python 转行之路](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485012&idx=1&sn=751796e63a30d84be01486619b5fb806&scene=21#wechat_redirect) - -2、[一个机械生的Python转行自述](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484979&idx=1&sn=6513e940bc7c0677dedf7efff80aa4c6&scene=21#wechat_redirect) - -3、[自学 Python后端开发 到什么程度可以找工作?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485010&idx=1&sn=e76d031f91633f9bfde9da36d8dcf996&scene=21#wechat_redirect) - -4、[工作不是游戏,写给编程新人的五点建议](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484984&idx=1&sn=0934415cc48004ae76345dcfb8e33b94&scene=21#wechat_redirect) - -5、[Python 从业十年是种什么体验?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485601&idx=1&sn=0dbebc02002ab01a8cd42265822ff81c&chksm=e8866843dff1e15509f2a9d2cd551b2360607cf88e73e5d3036388716ee57949d726dcea209c#rd) - -6、[40条提升编程技能的小妙招](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489765&idx=2&sn=79287da847c25ce0d72db6a33eb0c5a0&chksm=e8867807dff1f111043fe22bbfc776a52331c28abf85c2f658cbbaba3235c4e9d3c070c44d8c&scene=27#wechat_redirect) - -7、[我,一个靠GitHub打赏谋生的码农,年入十万美元](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=1&sn=56f51b946908ed89a58b311fc77f47d6&chksm=e8867544dff1fc52e30288685c89e86cefd063d5d37f5b1aba5fb30fd3665cf18346533cad96&scene=27#wechat_redirect) - -8、[5 年 Python 的我,总结了这 90 条写 Python 程序的建议](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487323&idx=1&sn=1b6594d63319faaadefad1f5e178235f&chksm=e8866fb9dff1e6af732304c8f3645cecca8b4bfa4158e45b0ea6c5167c2675bd748ce23e8239&scene=27#wechat_redirect) - -9、[Python这么慢,为啥大公司还在用?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=1&sn=55dda8a6a10429b3a7016393bc058493&chksm=e886768edff1ff981d67b2ecfe52ebf5b8dbc0085289e6bd3f8a6c6631ee6fc4a633fb6bc4b8&scene=27#wechat_redirect) - -## 11. 明哥的作品 - -1、[太赞了!《Python 黑魔法指南》终于面世了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486834&idx=1&sn=f5b94c3a520624786162f78246e60246&chksm=e8866d90dff1e486a3dae97aaf347e83834c8cf204849e5f9ae23dc9fdf0c470f97e0298cf74&scene=27#wechat_redirect) - -2、[《Python黑魔法指南》全新版本 v2.0 上线发布](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490543&idx=1&sn=449a1c7adfafdd5cf37874ed67620e89&chksm=e8867b0ddff1f21b9d622ba7a53b35143cd7fbb3da0d7c091b3d2cdd71d817a4397a99fa424e&scene=27#wechat_redirect) - -3、[明哥写了两个月,这 200页《PyCharm 中文指南》 终于可以发布了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491716&idx=1&sn=76a9f3ad5163a9ffdbaf0fefd0259261&chksm=e8858066dff20970fa9988675154fff6a6f84793a80b8ea1d6fe5d5bbb5bc816d2feabc50664#rd) - - - - ---- - -![](http://image.iswbm.com/20200607174235.png) \ No newline at end of file From e1f8eb5f172fb1d79e1e575c88e2043bcb11ad1d Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 17 Nov 2020 21:56:23 +0800 Subject: [PATCH 133/147] update --- README.md | 147 +++++++++++++++++++++++++++++++-- github-toc-maker-for-sphinx.py | 71 ++++++++++++++++ 2 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 github-toc-maker-for-sphinx.py diff --git a/README.md b/README.md index fa7d9f1..0512d0d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![](http://image.iswbm.com/image-20200607140915244.png) +![](http://image.iswbm.com/image-20201117214825880.png)

    Build Status @@ -13,18 +13,147 @@ 在线阅读:[Python 编程时光](http://python.iswbm.com/) -![](http://image.iswbm.com/20200607130051.png) - -## 文章结构 - -![](http://image.iswbm.com/20200607131339.png) - - +- **第一章:基础知识** + * [1.1 13条Python2.x和3.x的区别?](http://python.iswbm.com/en/latest/c01/c01_01.html) + * [1.2 Python 自省机制详解](http://python.iswbm.com/en/latest/c01/c01_02.html) + * [1.3 /usr/bin/env python 有什么用?](http://python.iswbm.com/en/latest/c01/c01_03.html) + * [1.4 什么是猴子补丁?](http://python.iswbm.com/en/latest/c01/c01_04.html) + * [1.5 深入闭包与变量作用域](http://python.iswbm.com/en/latest/c01/c01_05.html) + * [1.6 有了列表,为什么 Python 还有元组?](http://python.iswbm.com/en/latest/c01/c01_06.html) + * [1.7 15个Pythonic的代码示例](http://python.iswbm.com/en/latest/c01/c01_07.html) + * [1.8 新式类和经典类的区别?](http://python.iswbm.com/en/latest/c01/c01_08.html) + * [1.9 多继承与Mixin设计模式](http://python.iswbm.com/en/latest/c01/c01_09.html) + * [1.10 如何修改 CentOS 6.x 上默认Python](http://python.iswbm.com/en/latest/c01/c01_10.html) + * [1.11 正则表达式必知必会](http://python.iswbm.com/en/latest/c01/c01_11.html) + * [1.12 搞懂字符编码的前世今生](http://python.iswbm.com/en/latest/c01/c01_12.html) + * [1.13 别再使用 pprint 打印字典了](http://python.iswbm.com/en/latest/c01/c01_13.html) + * [1.14 with 与 上下文管理器](http://python.iswbm.com/en/latest/c01/c01_14.html) + * [1.21 Python 开发的几个小 Tips](http://python.iswbm.com/en/latest/c01/c01_15.html) + * [1.16 泛型函数怎么写?](http://python.iswbm.com/en/latest/c01/c01_16.html) + * [1.17 详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_17.html) + * [1.18 MySQL 使用总结](http://python.iswbm.com/en/latest/c01/c01_18.html) + * [1.19 如何调试已经运行中的程序](http://python.iswbm.com/en/latest/c01/c01_19.html) + * [1.20 在 CentOS 7.2 上安装 Python3.7](http://python.iswbm.com/en/latest/c01/c01_20.html) + * [1.21 Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_21.html) + * [1.22 Python 炫技操作:海象运算符的三种用法](http://python.iswbm.com/en/latest/c01/c01_22.html) + * [1.23 Python 炫技操作:模块重载的五种方法](http://python.iswbm.com/en/latest/c01/c01_23.html) + * [1.24 Python 炫技操作:条件语句的七种写法](http://python.iswbm.com/en/latest/c01/c01_24.html) + * [1.25 Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_25.html) + * [1.26 Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_26.html) + * [1.27 Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_27.html) +- **第二章:并发编程** + * [2.1 从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) + * [2.2 创建多线程的几种方法](http://python.iswbm.com/en/latest/c02/c02_02.html) + * [2.3 谈谈线程中的“锁机制”](http://python.iswbm.com/en/latest/c02/c02_03.html) + * [2.4 线程消息通信机制](http://python.iswbm.com/en/latest/c02/c02_04.html) + * [2.5 线程中的信息隔离](http://python.iswbm.com/en/latest/c02/c02_05.html) + * [2.6 线程池创建的几种方法](http://python.iswbm.com/en/latest/c02/c02_06.html) + * [2.7 从生成器使用入门协程](http://python.iswbm.com/en/latest/c02/c02_07.html) + * [2.8 深入理解yield from语法](http://python.iswbm.com/en/latest/c02/c02_08.html) + * [2.9 初识异步IO框架:asyncio 上篇](http://python.iswbm.com/en/latest/c02/c02_09.html) + * [2.10 深入异步IO框架:asyncio 中篇](http://python.iswbm.com/en/latest/c02/c02_10.html) + * [2.11 实战异步IO框架:asyncio 下篇](http://python.iswbm.com/en/latest/c02/c02_11.html) + * [2.12 生成器与协程,你分清了吗?](http://python.iswbm.com/en/latest/c02/c02_12.html) + * [2.13 I/O多路复用:select/poll/epoll](http://python.iswbm.com/en/latest/c02/c02_13.html) + * [2.14 浅谈线程安全那些事儿](http://python.iswbm.com/en/latest/c02/c02_14.html) +- **第三章:高级编程** + * [3.1 装饰器进阶用法详解](http://python.iswbm.com/en/latest/c03/c03_01.html) + * [3.2 深入理解Python元类](http://python.iswbm.com/en/latest/c03/c03_02.html) + * [3.3 Socket编程实现在线聊天](http://python.iswbm.com/en/latest/c03/c03_03.html) + * [3.4 Django+Uwsgi部署网站](http://python.iswbm.com/en/latest/c03/c03_04.html) + * [3.5 源码解读:Flask上下文与代理模式](http://python.iswbm.com/en/latest/c03/c03_05.html) + * [3.6 Web开发者必看:理解WSGI](http://python.iswbm.com/en/latest/c03/c03_06.html) + * [3.7 超实用10 条 Python使用技巧](http://python.iswbm.com/en/latest/c03/c03_07.html) + * [3.8 深入理解 Python 中的描述符](http://python.iswbm.com/en/latest/c03/c03_08.html) + * [1.24 深入探讨 Python 的 import 机制:实现远程导入模块](http://python.iswbm.com/en/latest/c03/c03_09.html) + * [3.10 Python几个高阶函数](http://python.iswbm.com/en/latest/c03/c03_10.html) + * [3.11 with 与 上下文管理器](http://python.iswbm.com/en/latest/c03/c03_11.html) + * [3.12 静态方法其实暗藏玄机](http://python.iswbm.com/en/latest/c03/c03_12.html) + * [3.13 包导入的三个冷门知识点](http://python.iswbm.com/en/latest/c03/c03_13.html) + * [3.14 全面学习 Python 包:包的构建与分发](http://python.iswbm.com/en/latest/c03/c03_14.html) +- **第四章:开发工具** + * [4.1 虚拟环境:virtualenv](http://python.iswbm.com/en/latest/c04/c04_01.html) + * [4.2 虚拟环境:Pipenv](http://python.iswbm.com/en/latest/c04/c04_02.html) + * [4.3 30分钟教你搭建一个博客](http://python.iswbm.com/en/latest/c04/c04_03.html) + * [4.4 Jupyter NoteBook 使用指南](http://python.iswbm.com/en/latest/c04/c04_04.html) + * [4.5 最全的 pip 使用指南](http://python.iswbm.com/en/latest/c04/c04_05.html) + * [4.6 我的 Git 使用指南](http://python.iswbm.com/en/latest/c04/c04_06.html) + * [4.7 Hexo 搭建博客教程](http://python.iswbm.com/en/latest/c04/c04_07.html) + * [4.8 珍惜生命,远离鼠标](http://python.iswbm.com/en/latest/c04/c04_08.html) + * [4.9 MySQL的基本使用](http://python.iswbm.com/en/latest/c04/c04_09.html) + * [4.10 MySQL的高级进阶](http://python.iswbm.com/en/latest/c04/c04_10.html) + * [4.11 超详细图文教你如何使用 PyCharm 进行远程调试](http://python.iswbm.com/en/latest/c04/c04_11.html) + * [4.12 服务器调试神器:pdb](http://python.iswbm.com/en/latest/c04/c04_12.html) + * [4.13 命令行解析工具:argparse](http://python.iswbm.com/en/latest/c04/c04_13.html) + * [4.2 Xshell的高效使用手册](http://python.iswbm.com/en/latest/c04/c04_14.html) + * [4.15 30个 PyCharm 实用技巧](http://python.iswbm.com/en/latest/c04/c04_15.html) + * [4.16 程序员编码必学:Vim](http://python.iswbm.com/en/latest/c04/c04_16.html) + * [4.17 详解 23 种设计模式](http://python.iswbm.com/en/latest/c04/c04_17.html) + * [4.18 如何用好 Python的用户环境?](http://python.iswbm.com/en/latest/c04/c04_18.html) +- **第五章:算法教程** + * [5.1 图解九大经典排序算法](http://python.iswbm.com/en/latest/c05/c05_01.html) + * [5.2 递归算法:走楼梯会思考的题](http://python.iswbm.com/en/latest/c05/c05_02.html) + * [5.3 哈希算法:安全方面的算法应用](http://python.iswbm.com/en/latest/c05/c05_03.html) +- **第六章:数据分析** + * [6.1 一张图带你入门matplotlib](http://python.iswbm.com/en/latest/c06/c06_01.html) + * [6.2 详解六种可视化图表](http://python.iswbm.com/en/latest/c06/c06_02.html) + * [6.3 如何绘制正余弦函数图象](http://python.iswbm.com/en/latest/c06/c06_03.html) + * [6.4 子图与子区 难点突破](http://python.iswbm.com/en/latest/c06/c06_04.html) + * [6.5 绘制酷炫的gif动态图](http://python.iswbm.com/en/latest/c06/c06_05.html) + * [6.6 自动生成图像视频](http://python.iswbm.com/en/latest/c06/c06_06.html) +- **第七章:运维人生** + * [7.1 Linux 命令行的艺术](http://python.iswbm.com/en/latest/c07/c07_01.html) + * [7.2 Zabbix 监控部署文档](http://python.iswbm.com/en/latest/c07/c07_02.html) + * [7.3 Docker:Hello world](http://python.iswbm.com/en/latest/c07/c07_03.html) + * [7.4 Docker:关于镜像](http://python.iswbm.com/en/latest/c07/c07_04.html) + * [7.5 Docker:网络通信](http://python.iswbm.com/en/latest/c07/c07_05.html) + * [7.6 Docker:存储与多主机](http://python.iswbm.com/en/latest/c07/c07_06.html) + * [7.7 SaltStack 入门指南](http://python.iswbm.com/en/latest/c07/c07_07.html) + * [7.8 Keepalived 部署文档](http://python.iswbm.com/en/latest/c07/c07_08.html) + * [7.9 Ansible 入门指南使用手册](http://python.iswbm.com/en/latest/c07/c07_09.html) + * [7.10 Ansible API 最全使用文档(中文)](http://python.iswbm.com/en/latest/c07/c07_10.html) + * [7.11 K8S:基础入门](http://python.iswbm.com/en/latest/c07/c07_11.html) + * [7.12 yum 命令使用指南及问题排查方法](http://python.iswbm.com/en/latest/c07/c07_12.html) + * [7.13 基于 ansible-api 二次开发](http://python.iswbm.com/en/latest/c07/c07_13.html) + * [7.14 Linux 如何写判断语句](http://python.iswbm.com/en/latest/c07/c07_14.html) + * [7.15 Mariadb 与 Galera 集群总结](http://python.iswbm.com/en/latest/c07/c07_15.html) + * [7.16 Linux 运维之路](http://python.iswbm.com/en/latest/c07/c07_16.html) + * [1.17 ansible 自定义 Jinja2 过滤器](http://python.iswbm.com/en/latest/c07/c07_17.html) + * [7.18 Shell中去除字符串前后空格的方法](http://python.iswbm.com/en/latest/c07/c07_18.html) + * [7.19 Ansible 使用教程](http://python.iswbm.com/en/latest/c07/c07_19.html) + * [7.20 使用 Shell 删除文件的多种方法](http://python.iswbm.com/en/latest/c07/c07_20.html) + * [7.21 如何快速创建超大文件?](http://python.iswbm.com/en/latest/c07/c07_21.html) + * [7.22 rpm 命令详细用法](http://python.iswbm.com/en/latest/c07/c07_22.html) +- **第八章:OpenStack** + * [8.1 OpenStack 运维命令](http://python.iswbm.com/en/latest/c08/c08_01.html) + * [8.2 OpenStack 部署SR-IOV](http://python.iswbm.com/en/latest/c08/c08_02.html) + * [8.3 制作 OpenStack 镜像](http://python.iswbm.com/en/latest/c08/c08_03.html) + * [8.4 云计算与虚拟化入门通识](http://python.iswbm.com/en/latest/c08/c08_04.html) + * [8.5 OpenStack 源码剖析与改造](http://python.iswbm.com/en/latest/c08/c08_05.html) + * [8.6 源码解读:Cloud-Init](http://python.iswbm.com/en/latest/c08/c08_06.html) + * [8.7 OpenStack 实现GPU直通](http://python.iswbm.com/en/latest/c08/c08_07.html) + * [8.8 OpenStack 如何使用DHCP?](http://python.iswbm.com/en/latest/c08/c08_08.html) + * [8.9 从0到1:全面理解RPC远程调用](http://python.iswbm.com/en/latest/c08/c08_09.html) + * [8.16 修改 KVM 镜像文件的三种方法](http://python.iswbm.com/en/latest/c08/c08_10.html) + * [8.11 OpenStack 问题排查](http://python.iswbm.com/en/latest/c08/c08_11.html) + * [8.12 OpenStack之主机调度](http://python.iswbm.com/en/latest/c08/c08_12.html) + * [8.13 网络知识必知必会](http://python.iswbm.com/en/latest/c08/c08_13.html) + * [8.14 支持 IPv6以及多运营商](http://python.iswbm.com/en/latest/c08/c08_14.html) + * [8.15 Neutron 源码解读](http://python.iswbm.com/en/latest/c08/c08_15.html) + * [8.16 详解 Neutron 的 QoS](http://python.iswbm.com/en/latest/c08/c08_16.html) +- **第九章:有趣的工具** + * [9.1 情人节来了,教你使用 Python 来表白](http://python.iswbm.com/en/latest/c09/c09_01.html) + * [9.2 我在Mac 上使用哪些APP?](http://python.iswbm.com/en/latest/c09/c09_02.html) + * [9.3 明哥的在线工具集](http://python.iswbm.com/en/latest/c09/c09_03.html) + * [9.4 书籍推荐](http://python.iswbm.com/en/latest/c09/c09_04.html) + * [9.5 让Python 执行任意代码时,都会自动念一段 『平安经』](http://python.iswbm.com/en/latest/c09/c09_05.html) + * [9.6 赶紧收藏!学习 Python 的好网站](http://python.iswbm.com/en/latest/c09/c09_06.html) + * [9.7 解决网页上不能复制的几个小技巧](http://python.iswbm.com/en/latest/c09/c09_07.html) ## 欢迎交流 对文章有什么疑问,对项目有什么建议,可以添加微信与我交流,同时欢迎关注我的个人微信公众号。 -![](http://image.iswbm.com/20200607140327.png) +![](http://image.iswbm.com/image-20201117215520960.png) diff --git a/github-toc-maker-for-sphinx.py b/github-toc-maker-for-sphinx.py new file mode 100644 index 0000000..154d794 --- /dev/null +++ b/github-toc-maker-for-sphinx.py @@ -0,0 +1,71 @@ +import os +import re +from glob import glob + +pwd = os.getcwd() +all_chapters_path = glob(pwd + "/source/c0*") + + +def get_chapter_name(file): + with open(file) as f: + f.readline() + chapter_name = f.readline().strip() + + return chapter_name + +def get_title(file): + with open(file) as f: + first_line = f.readline() + + if first_line.startswith("#"): + return first_line[1:].strip() + +def generate_mapping(): + mapping = dict.fromkeys([os.path.basename(chapter_path) for chapter_path in all_chapters_path]) + for key in mapping.keys(): + chapter_file = os.path.join(pwd, "source", "chapters", key.replace("c", "p") + ".rst") + mapping[key] = get_chapter_name(chapter_file) + + return mapping + +def get_toc_info(): + toc = {} + for dir_path in all_chapters_path: + dir_name = os.path.basename(dir_path) + + chapter_toc = {} + files = glob(dir_path + "/*.md") + + for file in files: + file_name = os.path.basename(file) + section = int(re.findall(r"c\d{2}_(\d{2}).md", file_name)[0]) + + #md_path = os.path.join("./source/", dir_name, file_name) + md_path = os.path.join("http://python.iswbm.com/en/latest/", dir_name, file_name.replace("md", "html")) + title = get_title(file) + if not title: + continue + + chapter_toc[section] = (title, md_path) + + toc[dir_name] = chapter_toc + + return toc + +def print_md_toc(toc_info, mapping): + for chapter in sorted(toc_info.items(), key=lambda item: item[0]): + posts = chapter[1] + chapter_name = mapping[chapter[0]] + print(f"- **{chapter_name}**") + for post in sorted(posts.items(), key=lambda item:item[0]): + # print title only + # print(f"{post[1][0]}") + print(" ", f"* [{post[1][0]}]({post[1][1]})") + +def main(): + mapping = generate_mapping() + toc_info = get_toc_info() + print_md_toc(toc_info, mapping) + +if __name__ == '__main__': + main() From c6d28f1006faf80f95779a586fad301ae34e1e53 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Mon, 7 Dec 2020 21:50:55 +0800 Subject: [PATCH 134/147] update --- README.md | 6 +- source/c03/c03_09.md | 2 +- source/c03/c03_09.rst | 4 +- source/c04/c04_14.md | 2 +- source/c04/c04_14.rst | 4 +- source/c04/c04_19.rst | 728 ------------------------------------------ source/c04/c04_20.rst | 392 ----------------------- source/c04/c04_21.rst | 392 ----------------------- source/c04/c04_22.rst | 7 - source/c08/c08_10.md | 2 +- source/c08/c08_10.rst | 2 +- 11 files changed, 11 insertions(+), 1530 deletions(-) delete mode 100644 source/c04/c04_19.rst delete mode 100644 source/c04/c04_20.rst delete mode 100644 source/c04/c04_21.rst delete mode 100644 source/c04/c04_22.rst diff --git a/README.md b/README.md index 0512d0d..2675cf2 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ * [3.6 Web开发者必看:理解WSGI](http://python.iswbm.com/en/latest/c03/c03_06.html) * [3.7 超实用10 条 Python使用技巧](http://python.iswbm.com/en/latest/c03/c03_07.html) * [3.8 深入理解 Python 中的描述符](http://python.iswbm.com/en/latest/c03/c03_08.html) - * [1.24 深入探讨 Python 的 import 机制:实现远程导入模块](http://python.iswbm.com/en/latest/c03/c03_09.html) + * [3.9 深入探讨 Python 的 import 机制](http://python.iswbm.com/en/latest/c03/c03_09.html) * [3.10 Python几个高阶函数](http://python.iswbm.com/en/latest/c03/c03_10.html) * [3.11 with 与 上下文管理器](http://python.iswbm.com/en/latest/c03/c03_11.html) * [3.12 静态方法其实暗藏玄机](http://python.iswbm.com/en/latest/c03/c03_12.html) @@ -85,7 +85,7 @@ * [4.11 超详细图文教你如何使用 PyCharm 进行远程调试](http://python.iswbm.com/en/latest/c04/c04_11.html) * [4.12 服务器调试神器:pdb](http://python.iswbm.com/en/latest/c04/c04_12.html) * [4.13 命令行解析工具:argparse](http://python.iswbm.com/en/latest/c04/c04_13.html) - * [4.2 Xshell的高效使用手册](http://python.iswbm.com/en/latest/c04/c04_14.html) + * [4.14 Xshell的高效使用手册](http://python.iswbm.com/en/latest/c04/c04_14.html) * [4.15 30个 PyCharm 实用技巧](http://python.iswbm.com/en/latest/c04/c04_15.html) * [4.16 程序员编码必学:Vim](http://python.iswbm.com/en/latest/c04/c04_16.html) * [4.17 详解 23 种设计模式](http://python.iswbm.com/en/latest/c04/c04_17.html) @@ -134,7 +134,7 @@ * [8.7 OpenStack 实现GPU直通](http://python.iswbm.com/en/latest/c08/c08_07.html) * [8.8 OpenStack 如何使用DHCP?](http://python.iswbm.com/en/latest/c08/c08_08.html) * [8.9 从0到1:全面理解RPC远程调用](http://python.iswbm.com/en/latest/c08/c08_09.html) - * [8.16 修改 KVM 镜像文件的三种方法](http://python.iswbm.com/en/latest/c08/c08_10.html) + * [8.10 修改 KVM 镜像文件的三种方法](http://python.iswbm.com/en/latest/c08/c08_10.html) * [8.11 OpenStack 问题排查](http://python.iswbm.com/en/latest/c08/c08_11.html) * [8.12 OpenStack之主机调度](http://python.iswbm.com/en/latest/c08/c08_12.html) * [8.13 网络知识必知必会](http://python.iswbm.com/en/latest/c08/c08_13.html) diff --git a/source/c03/c03_09.md b/source/c03/c03_09.md index defb819..0da8bd3 100644 --- a/source/c03/c03_09.md +++ b/source/c03/c03_09.md @@ -1,4 +1,4 @@ -# 1.24 深入探讨 Python 的 import 机制:实现远程导入模块 +# 3.9 深入探讨 Python 的 import 机制 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c03/c03_09.rst b/source/c03/c03_09.rst index fae70f7..6ab4a1b 100644 --- a/source/c03/c03_09.rst +++ b/source/c03/c03_09.rst @@ -1,5 +1,5 @@ -1.24 深入探讨 Python 的 import 机制:实现远程导入模块 -===================================================== +3.9 深入探讨 Python 的 import 机制 +================================== |image0| diff --git a/source/c04/c04_14.md b/source/c04/c04_14.md index b5ec958..672b5ce 100644 --- a/source/c04/c04_14.md +++ b/source/c04/c04_14.md @@ -1,4 +1,4 @@ -# 4.2 Xshell的高效使用手册 +# 4.14 Xshell的高效使用手册 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c04/c04_14.rst b/source/c04/c04_14.rst index 1dbdb5e..61ecafb 100644 --- a/source/c04/c04_14.rst +++ b/source/c04/c04_14.rst @@ -1,5 +1,5 @@ -4.2 Xshell的高效使用手册 -======================== +4.14 Xshell的高效使用手册 +========================= |image0| diff --git a/source/c04/c04_19.rst b/source/c04/c04_19.rst deleted file mode 100644 index bceb761..0000000 --- a/source/c04/c04_19.rst +++ /dev/null @@ -1,728 +0,0 @@ -4.19 程序员编码必学:Vim -======================== - -|image0| - -我本人是 Vim -的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim -可以让我对文本的操作更加精准、高效。 - -对于未使用过 Vim 的朋友来说,可能还无法体会到这种感觉。由于使用 Vim -有一定的学习成本,只有做到非常熟练的程度才能感受到它带来的快捷。 - -这里我就自己日常有使用过的 Vim 指令做一个总结,总共分成 21 -点,建议有想学习 Vim -的同学,可以按照文章\ **配合搜索引擎**\ 多多尝试,相信你会慢慢喜欢上 -Vim。 - -本文更倾向于有一定基础的同学,因为有很多点,如果写得太详细的话,一定会变得相当啰嗦。 - -1. vim模式 ----------- - -.. code:: shell - - 正常模式(按Esc或Ctrl+[进入) 左下角显示文件名或为空 - 插入模式(按i进入) 左下角显示--INSERT-- - 可视模式(按v进入) 左下角显示--VISUAL-- - 替换模式(按r或R开始) 左下角显示 --REPLACE-- - 命令行模式(按:或者/或者?开始) - ex模式 没用过,有兴趣的同学可以自行了解 - -2. 打开文件 ------------ - -.. code:: shell - - # 打开单个文件 - vim file - # 同时打开多个文件 - vim file1 file2.. - - # 在vim窗口中打开一个新文件 - :open [file] - - 【举个例子】 - # 当前打开1.txt,做了一些编辑没保存 - :open! 放弃这些修改,并重新打开未修改的文件 - - # 当前打开1.txt,做了一些编辑并保存 - :open 2.txt 直接退出对1.txt的编辑,直接打开2.txt编辑,省了退出:wq再重新vim 2.txt的步骤 - - - # 打开远程文件,比如ftp或者share folder - :e ftp://192.168.10.76/abc.txt - :e \qadrive\test\1.txt - - # 以只读形式打开文件,但是仍然可以使用 :wq! 写入 - vim -R file - - # 强制性关闭修改功能,无法使用 :wq! 写入 - vim -M file - -3. 插入命令 ------------ - -.. code:: shell - - i 在当前位置生前插入 - I 在当前行首插入 - - a 在当前位置后插入 - A 在当前行尾插入 - - o 在当前行之后插入一行 - O 在当前行之前插入一行 - -4. 查找命令 ------------ - -最简单的查找 - -.. code:: shell - - /text  查找text,按n健查找下一个,按N健查找前一个。 - ?text  查找text,反向查找,按n健查找下一个,按N健查找前一个。 - - vim中有一些特殊字符在查找时需要转义  .*[]^%/?~$ - - :set ignorecase  忽略大小写的查找 - :set noignorecase  不忽略大小写的查找 - -快速查找,不需要手打字符即可查找 - -:: - - * 向后(下)寻找游标所在处的单词 - # 向前(上)寻找游标所在处的单词 - - - 以上两种查找,n,N 的继续查找命令依然可以适用 - -精准查找:匹配单词查找 - -如果文本中有 ``hello``\ ,\ ``helloworld``\ ,\ ``hellopython`` - -那我使用 /hello ,这三个词都会匹配到。 - -有没有办法实现精准查找呢?可以使用 - -.. code:: shell - - /hello\> - -精准查找:匹配行首、行末 - -.. code:: shell - - # hello位于行首 - /^hello - - # world位于行末 - /world$ - -5. 替换命令 ------------ - -.. code:: shell - - ~ 反转游标字母大小写 - - r<字母> 将当前字符替换为所写字母 - R<字母><字母>... 连续替换字母 - - cc 替换整行(就是删除当前行,并在下一行插入) - cw 替换一个单词(就是删除一个单词,就进入插入模式),前提是游标处于单词第一个字母(可用b定位) - C (大写C)替换至行尾(和D有所区别,D是删除(剪切)至行尾,C是删除至行位并进入插入模式) - - # % 就表示所有行,不加就表求当前行 - # 加g表示对每一行的所有匹配到的进行替换,不加就是第一个 - # 如果不加g,而改成c,就会让你再进行确认,选择a进行全部替换,选择y替换,选择n不替换 - - :s/old/new/ 用old替换new,替换当前行的第一个匹配 - :s/old/new/g 用old替换new,替换当前行的所有匹配 - - :%s/old/new/ 用old替换new,替换所有行的第一个匹配 - :%s/old/new/g 用old替换new,替换整个文件的所有匹配 - - - :10,20 s/^/ /g 在第10行至第20行每行前面加四个空格,用于缩进。 - - ddp 交换光标所在行和其下紧邻的一行。 - -6. 撤销与重做 -------------- - -.. code:: shell - - u 撤销(Undo) - - U 撤销对整行的操作 - - Ctrl + r 重做(Redo),即撤销的撤销。 - -7. 删除命令 ------------ - -需要说明的是,vim -其实并没有单纯的删除命令,下面你或许理解为剪切更加准确。 - -以字符为单位删除 - -.. code:: shell - - x 删除当前字符 - 3x 删除当前字符3次 - - X 删除当前字符的前一个字符。 - 3X 删除当前光标向前三个字符 - - dl 删除当前字符, dl=x - dh 删除前一个字符,X=dh - - D 删除当前字符至行尾。D=d$ - d$ 删除当前字符至行尾 - d^ 删除当前字符之前至行首 - -以单词为单位删除 - -.. code:: shell - - dw 删除当前字符到单词尾 - daw 删除当前字符所在单词 - -以行为单位删除 - -.. code:: shell - - dd 删除当前行 - dj 删除下一行 - dk 删除上一行 - - dgg 删除当前行至文档首部 - d1G 删除当前行至文档首部 - dG 删除当前行至文档尾部 - - kdgg 删除当前行之前所有行(不包括当前行) - jdG 删除当前行之后所有行(不包括当前行) - - - - 10d 删除当前行开始的10行。 - :1,10d 删除1-10行 - :11,$d 删除11行及以后所有的行 - :1,$d 删除所有行 - J   删除两行之间的空行,实际上是合并两行。 - -8. 复制粘贴 ------------ - -普通模式中使用y复制 - -:: - - yy 复制游标所在的整行(3yy表示复制3行) - - y^ 复制至行首,或y0。不含光标所在处字符。 - y$ 复制至行尾。含光标所在处字符。 - - yw 复制一个单词。 - y2w 复制两个单词。 - - yG 复制至文本末。 - y1G 复制至文本开头。 - -普通模式中使用p粘贴 - -:: - - p(小写):代表粘贴至光标后(下边,右边) - P(大写):代表粘贴至光标前(上边,左边) - -9. 剪切粘贴 ------------ - -.. code:: shell - - dd 其实就是剪切命令,剪切当前行 - ddp 剪切当前行并粘贴,可实现当前行和下一行调换位置 - - - 正常模式下按v(逐字)或V(逐行)进入可视模式 - 然后用jklh命令移动即可选择某些行或字符,再按d即可剪切 - - ndd 剪切当前行之后的n行。利用p命令可以对剪切的内容进行粘贴 - - :1,10d 将1-10行剪切。利用p命令可将剪切后的内容进行粘贴。 - - :1, 10 m 20 将第1-10行移动到第20行之后。 - -10. 退出保存 ------------- - -.. code:: shell - - :wq 保存并退出 - - ZZ 保存并退出 - - :q! 强制退出并忽略所有更改 - - :e! 放弃所有修改,并打开原来文件。 - :open! 放弃所有修改,并打开原来文件。 - - :sav(eas) new.txt 另存为一个新文件,退出原文件的编辑且不会保存 - :f(ile) new.txt 新开一个文件,并不保存,退出原文件的编辑且不会保存 - -11. 移动命令 ------------- - -以字符为单位移动 - -.. code:: shell - - h 左移一个字符 - l 右移一个字符 - k 上移一个字符 - j 下移一个字符 - - - # 【定位字符】f和F - fx 找到光标后第一个为x的字符 - 3fd 找到光标后第三个为d的字符 - - F 同f,反向查找。 - -以行为单位移动 - -.. code:: shell - - # 10指代所有数字,可任意指定 - 10h 左移10个字符 - 10l 右移10个字符 - 10k 上移10行 - 10j 下移10行 - - $ 移动到行尾 - 3$ 移动到下面3行的行尾 - -以单词为单位移动 - -.. code:: shell - - w 向前移动一个单词(光标停在单词首部) - b 向后移动一个单词 - e,同w,只不过是光标停在单词尾部 - ge 同b,光标停在单词尾部。 - -以句为单位移动 - -.. code:: shell - - ( 移动到句首 - ) 移动到句尾 - -跳转到文件的首尾 - -.. code:: shell - - gg 移动到文件头。 = [[ == `` - G 移动到文件尾。 = ]] - -其他移动方法 - -.. code:: shell - - ^ 移动到本行第一个非空白字符上。 - 0 移动到本行第一个字符上(可以是空格) - -使用 ``具名标记`` 跳转,个人感觉这个很好用,因为可以跨文件。 - -.. code:: shell - - 使用 ma ,可以将此处标记为 a,使用 'a 进行跳转 - 使用 :marks 可以查看所有的标记 - 使用 :delm!可以删除所有的标记 - -当在查看错误日志时,正常的步骤是,vim打开文件,然后使用 ``shift+g`` -再跳转到最后一行,这里有个更简单的操作可以在打开文件时立即跳到最后一行。只要在 -vim 和 文件 中间加个 ``+`` 即可。 - -.. code:: shell - - vim + you.log - -举一反三,当你想打开文件立即跳转到指定行时,可以这样 - -.. code:: shell - - # 打开文件并跳转到 20 行 - vim you.log +20 - -当你使用 ``/`` 搜索定位跳转或者使用 ``:行号`` -进行精准跳转时,有时我们想返回到上一次的位置,如何实现? - -只要使用 Ctrl+o 即可返回上一次的位置。 - -12. 排版功能 ------------- - -**缩进** - -:: - - :set shiftwidth? 查看缩进值 - :set shiftwidth=4 设置缩进值为4 - - # 缩进相关 最好写到配置文件中 ~/.vimrc - :set tabstop=4 - :set softtabstop=4 - :set shiftwidth=4 - :set expandtab - - >> 向右缩进 - << 取消缩进 - -如何你要对代码进行缩进,还可以用 ``==`` -对当前行缩进,如果要对多行对待缩进,则使用 -n\ ``==``\ ,这种方式要求你所编辑的文件的扩展名是被vim所识别的,比如\ ``.py``\ 文件。 - -**排版** - -:: - - :ce 居中 - :le 靠左 - :ri 靠右 - -13. 注释命令 ------------- - -**多行注释** - -:: - - 进入命令行模式,按ctrl + v进入 visual block模式,然后按j, 或者k选中多行,把需要注释的行标记起来 - - 按大写字母I,再插入注释符,例如// - - 按esc键就会全部注释了 - -**取消多行注释** - -:: - - 进入命令行模式,按ctrl + v进入 visual block模式,按字母l横向选中列的个数,例如 // 需要选中2列 - - 按字母j,或者k选中注释符号 - - 按d键就可全部取消注释 - -**复杂注释** - -.. code:: shell - - :3,5 s/^/#/g 注释第3-5行 - :3,5 s/^#//g 解除3-5行的注释 - - - :1,$ s/^/#/g 注释整个文档 - :1,$ s/^#//g 取消注释整个文档 - - - :%s/^/#/g 注释整个文档,此法更快 - :%s/^#//g 取消注释整个文档 - -14. 调整视野 ------------- - -:: - - "zz":命令会把当前行置为屏幕正中央, - "zt":命令会把当前行置于屏幕顶端 - "zb":则把当前行置于屏幕底端. - - Ctrl + e 向下滚动一行 - Ctrl + y 向上滚动一行 - - Ctrl + d 向下滚动半屏 - Ctrl + u 向上滚动半屏 - - Ctrl + f 向下滚动一屏 - Ctrl + b 向上滚动一屏 - - - 【跳到指定行】:两种方法 - - 可以先把行号打开 - :set nu 打开行号 - - :20 跳到第20行 - 20G 跳到第20行 - -15. 区域选择 ------------- - -:: - - 要进行区域选择,要先进入可视模式 - - v 以字符为单位,上下左右选择 - V 以行为单位,上下选择 - - 选择后可进行操作 - d 剪切/删除 - y 复制 - - Ctrl+v 如果当前是V(大写)模式,就变成v(小写) - 如果当前是v(小写)模式,就变成普通模式。 - 如果当前是普通模式,就进入v(小写)模式 - - 利用这个,可以进行多行缩进。 - - ggVG 选择全文 - -16. 窗口控制 ------------- - -**新建窗口** - -.. code:: shell - - # 打开两个文件分属两个窗口 - vim -o 1.txt 2.txt - - - # 假设现在已经打开了1.txt - - :sp 2.txt 开启一个横向的窗口,编辑2.txt - :vsp 2.txt 开启一个竖向的窗口,编辑2.txt - - :split 将当前窗口再复制一个窗口出来,内容同步,游标可以不同 - :split 2.txt 在新窗口打开2.txt的横向窗口 - - # 需要注意:内容同步,但是游标位置是独立的 - - Ctrl-w s 将当前窗口分成水平窗口 - Ctrl-w v 将当前窗口分成竖直窗口 - - Ctrl-w q 等同:q 结束分割出来的视窗。 - Ctrl-w q! 等同:q! 结束分割出来的视窗。 - Ctrl-w o 打开一个视窗并且隐藏之前的所有视窗 - -**窗口切换** - -.. code:: shell - - # 特别说明:Ctrl w <字母> 不需要同时按 - - Ctrl-w h 切换到左边窗口 - Ctrl-w l 切换到右边窗口 - - Ctrl-w j 切换到下边窗口 - Ctrl-w k 切换到上边窗口 - - - # 特别说明:全屏模式下 - :n 切换下一个窗口 - :N 切换上一个窗口 - :bp 切换上一个窗口 - - # 特别说明:非全屏模式 - - :bn 切换下一个窗口,就当前位置的窗口的内容变了,其他窗口不变 - :bN 切换上一个窗口,就当前位置的窗口的内容变了,其他窗口不变 - -**窗口移动** - -.. code:: shell - - # 特别说明:Ctrl w <字母> 不需要同时按 - - Ctrl-w J 将当前视窗移至最下面 - Ctrl-w K 将当前视窗移最上面 - - Ctrl-w H 将当前视窗移至最左边 - Ctrl-w L 将当前视窗移至最右边 - - Ctrl-ww 按顺序切换窗口 - -**调整尺寸** - -.. code:: shell - - # 友情提示:键盘切记不要处于中文状态 - - Ctrl-w + 增加窗口高度 - Ctrl-w - 减少窗口高度 - -**退出窗口** - -.. code:: shell - - :close 关闭当前窗口 - :close! 强制关闭当前窗口 - - :q 退出,不保存 - :q! 强制退出,不保存 - - :x 保存退出 - :wq 保存退出 - :wq! 强制保存退出 - - :w <[路径/]文件名> 另存为 - :savesa <[路径/]文件名> 另存为 - - ZZ 保存并退出。 - - :only 关闭所有窗口,只保留当前窗口(前提:其他窗口内容有改变的话都要先保存) - :only! 关闭所有窗口,只保留当前窗口 - - :qall 放弃所有操作并退出 - :wall 保存所有, - :wqall 保存所有并退出。 - -17. 文档加密 ------------- - -:: - - vim -x file_name - - 然后输入密码: - 确认密码: - - 如果不修改内容也要保存。:wq,不然密码设定不会生效。 - -18. 录制宏 ----------- - -按q键加任意字母开始录制,再按q键结束录制(这意味着vim中的宏不可嵌套),使用的时候@加宏名,比如qa。。。q录制名为a的宏,@a使用这个宏。 - -19. 执行命令 ------------- - -.. code:: shell - - - # 重复前一次命令 - . - - # 执行shell命令 - :!command - - # 比如列出当前目录下文件 - :!ls - - # 执行脚本 - :!perl -c script.pl 检查perl脚本语法,可以不用退出vim,非常方便。 - :!perl script.pl 执行perl脚本,可以不用退出vim,非常方便。 - - :suspend或Ctrl - Z 挂起vim,回到shell,按fg可以返回vim。 - - # 执行完当前命令后,自动回到vim环境,但不知为何文件内容变成空了 - :silent !command - -20. 帮助命令 ------------- - -.. code:: shell - - 在Unix/Linux系统上 - $ vimtutor - - # 普通模式下 - 键盘输入vim或F1 - - # 命令行模式下 - - :help 显示整个帮助 - :help xxx 显示xxx的帮助,比如 :help i, :help CTRL-[(即Ctrl+[的帮助)。 - :help 'number' Vim选项的帮助用单引号括起 - - - 在Windows系统上 - :help tutor - -21. 配置命令 ------------- - -显示当前设定 - -.. code:: shell - - :set或者:se显示所有修改过的配置 - :set all 显示所有的设定值 - :set option? 显示option的设定值 - :set nooption 取消当期设定值 - :ver 显示vim的所有信息(包括版本和参数等) - - # 需要注意:全屏模式下 - :args 查看当前打开的文件列表,当前正在编辑的文件会用[]括起来 - -更改设定 - -.. code:: shell - - :set nu 显示行号 - - set autoindent(ai) 设置自动缩进 - set autowrite(aw) 设置自动存档,默认未打开 - set backup(bk) 设置自动备份,默认未打开 - - set background=dark或light,设置背景风格 - - set cindent(cin) 设置C语言风格缩进 - - :set ts=4 设置tab键转换为4个空格 - - :set ff=unix # 修改文件dos文件为unix - - :set shiftwidth? 查看缩进值 - :set shiftwidth=4 设置缩进值为4 - - :set ignorecase  忽略大小写的查找 - :set noignorecase  不忽略大小写的查找 - - :set paste # insert模式下,粘贴格式不会乱掉 - - :set ruler?  查看是否设置了ruler,在.vimrc中,使用set命令设制的选项都可以通过这个命令查看 - - :scriptnames  查看vim脚本文件的位置,比如.vimrc文件,语法文件及plugin等。 - - :set list 显示非打印字符,如tab,空格,行尾等。如果tab无法显示,请确定用set lcs=tab:>-命令设置了.vimrc文件,并确保你的文件中的确有tab,如果开启了expendtab,那么tab将被扩展为空格。 - - - :syntax 列出已经定义的语法项 - :syntax clear 清除已定义的语法规则 - - :syntax case match 大小写敏感,int和Int将视为不同的语法元素 - :syntax case ignore 大小写无关,int和Int将视为相同的语法元素,并使用同样的配色方案 - -以上就是我使用 Vim 的一些使用总结,希望对你能有帮助。 - --------------- - -最后,送你一张 Vim 的键盘图,你可以将它设置为你的电脑桌面,对你初学 Vim -可能会有帮助。 - -你可以\ **关注本公众号「Python编程时光」**\ ,在后台回复“**vim**” -,即可获取高清大图。 - -.. figure:: http://image.iswbm.com/20190804222221.png - :alt: 图1 - - 图1 - -.. figure:: http://image.iswbm.com/20190804222247.png - :alt: 图2 - - 图2 - --------------- - -|image1| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c04/c04_20.rst b/source/c04/c04_20.rst deleted file mode 100644 index c7a4ad6..0000000 --- a/source/c04/c04_20.rst +++ /dev/null @@ -1,392 +0,0 @@ -4.20 最全的 pip 使用指南 -======================== - -|image0| - -所有的 Python 开发者都清楚,Python -之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 -Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python -为基础封装出各种有利于开发的第三方工具包。 - -这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 - -Python -从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 - -当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 - -pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python -的标配。 - -当然也有其他的包管理工具 - -- **distutils**\ :仅用于打包和安装,严格来讲不算是包管理工具 - -- **setuptools**\ :distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 - egg 的文件格式。 - -- **Pipenv**\ :一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 - -- 还有其他的,这里不一一列出。 - -今天的主角是 pip -,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 - -1. 查询软件包 -------------- - -查询当前环境安装的所有软件包 - -.. code:: shell - - $ pip list - -查询 pypi 上含有某名字的包 - -.. code:: shell - - $ pip search pkg - -查询当前环境中可升级的包 - -.. code:: shell - - $ pip list --outdated - -查询一个包的详细内容 - -.. code:: shell - - $ pip show pkg - -2. 下载软件包 -------------- - -在不安装软件包的情况下下载软件包到本地 - -.. code:: shell - - $ pip download --destination-directory /local/wheels -r requirements.txt - -下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi -上安装。 - -.. code:: shell - - $ pip install --no-index --find-links=/local/wheels -r requirements.txt - -当然你也从你下载的包中,自己构建生成 wheel 文件 - -.. code:: shell - - $ pip install wheel - $ pip wheel --wheel-dir=/local/wheels -r requirements.txt - -3. 安装软件包 -------------- - -使用 ``pip install `` 可以很方便地从 pypi 上搜索下载并安装 python -包。 - -如下所示 - -.. code:: shell - - $ pip install requests - -这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 - -**3.1 只从本地安装,而不从 pypi 安装** - -.. code:: shell - - # 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 - $ pip install --no-index --find-links=/local/wheels pkg - -**3.2 限定版本进行软件包安装** - -以下三种,对单个 python 包的版本进行了约束 - -.. code:: shell - - # 所安装的包的版本为 2.1.2 - $ pip install pkg==2.1.2 - - # 所安装的包必须大于等于 2.1.2 - $ pip install pkg>=2.1.2 - - # 所安装的包必须小于等于 2.1.2 - $ pip install pkg<=2.1.2 - -以下命令用于管理/控制整个 python 环境的包版本 - -.. code:: shell - - # 导出依赖包列表 - pip freeze >requirements.txt - - # 从依赖包列表中安装 - pip install -r requirements.txt - - # 确保当前环境软件包的版本(并不确保安装) - pip install -c constraints.txt - -**3.3 限制不使用二进制包安装** - -由于默认情况下,wheel 包的平台是运行 pip download 命令 -的平台,所以可能出现平台不适配的情况。 - -比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl -就不能在 linux_x86_64 安装。 - -使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 - -比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 - -.. code:: shell - - # 下载非二进制的包 - $ pip download --no-binary=:all: pkg - - # 安装非二进制的包 - $ pip install pkg --no-binary - -**3.4 指定代理服务器安装** - -当你身处在一个内网环境中时,无法直接连接公网。这时候你使用\ ``pip install`` -安装包,就会失败。 - -面对这种情况,可以有两种方法: - -1. 下载离线包拷贝到内网机器中安装 -2. 使用代理服务器转发请求 - -第一种方法,虽说可行,但有相当多的弊端 - -- 步骤繁杂,耗时耗力 -- 无法处理包的依赖问题 - -这里重点来介绍,第二种方法: - -.. code:: shell - - $ pip install --proxy [user:passwd@]http_server_ip:port pkg - -每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:\ ``$HOME/.config/pip/pip.conf`` - -对于这个路径,说明几点 - -- 不同的操作系统,路径各不相同 - -.. code:: shell - - # Linux/Unix: - /etc/pip.conf - ~/.pip/pip.conf - ~/.config/pip/pip.conf - - # Mac OSX: - ~/Library/Application Support/pip/pip.conf - ~/.pip/pip.conf - /Library/Application Support/pip/pip.conf - - # Windows: - %APPDATA%\pip\pip.ini - %HOME%\pip\pip.ini - C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) - C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) - -- 若在你的机子上没有此文件,则自行创建即可 - -如何配置,这边给个样例: - -.. code:: ini - - [global] - index-url = http://mirrors.aliyun.com/pypi/simple/ - - # 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port - proxy=http://xxx.xxx.xxx.xxx:8080 - - [install] - # 信任阿里云的镜像源,否则会有警告 - trusted-host=mirrors.aliyun.com - -**3.5 安装用户私有软件包** - -很多人可能还不清楚,python 的安装包是可以用户隔离的。 - -如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 - -如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 - -面对这种情况,我们就想能否安装单独为我所用的包呢? - -庆幸的是,还真有。 - -我能想到的有两种方法: - -1. 使用虚拟环境 -2. 将包安装在用户的环境中 - -虚拟环境,之前写过几篇文章,这里不再展开讲。 - -今天的重点是第二种方法,教你如何安装用户私有的包? - -命令也很简单,只要加上 ``--user`` 参数,pip 就会将其安装在当前用户的 -``~/.local/lib/python3.x/site-packages`` 下,而其他用户的 python -则不会受影响。 - -.. code:: shell - - pip install --user pkg - -来举个例子 - -.. code:: shell - - # 在全局环境中未安装 requests - [root@localhost ~]# pip list | grep requests - [root@localhost ~]# su - wangbm - [root@localhost ~]# - - # 由于用户环境继承自全局环境,这里也未安装 - [wangbm@localhost ~]# pip list | grep requests - [wangbm@localhost ~]# pip install --user requests - [wangbm@localhost ~]# pip list | grep requests - requests (2.22.0) - [wangbm@localhost ~]# - - # 从 Location 属性可发现 requests 只安装在当前用户环境中 - [wangbm@ws_compute01 ~]$ pip show requests - --- - Metadata-Version: 2.1 - Name: requests - Version: 2.22.0 - Summary: Python HTTP for Humans. - Home-page: http://python-requests.org - Author: Kenneth Reitz - Author-email: me@kennethreitz.org - Installer: pip - License: Apache 2.0 - Location: /home/wangbm/.local/lib/python2.7/site-packages - [wangbm@localhost ~]$ exit - logout - - # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 - [root@localhost ~]$ pip list | grep requests - [root@localhost ~]$ - -当你身处个人用户环境中,python -导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 - -验证如下: - -.. code:: python - - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.path) - ['', - '/usr/lib64/python27.zip', - '/usr/lib64/python2.7', - '/usr/lib64/python2.7/plat-linux2', - '/usr/lib64/python2.7/lib-tk', - '/usr/lib64/python2.7/lib-old', - '/usr/lib64/python2.7/lib-dynload', - '/home/wangbm/.local/lib/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages/gtk-2.0', - '/usr/lib/python2.7/site-packages', - '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', - '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] - >>> - -**3.6 延长超时时间** - -若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 - -对于这种情况,一般重试几次就好了。 - -但是这样难免有些麻烦,有没有更好的解决方法呢? - -有的,可以通过延长超时时间。 - -.. code:: shell - - $ pip install --default-timeout=100 - -4. 卸载软件包 -------------- - -就一条命令,不再赘述 - -.. code:: shell - - $ pip uninstall pkg - -5. 升级软件包 -------------- - -想要对现有的 python 进行升级,其本质上也是先从 pypi -上下载最新版本的包,再对其进行安装。所以升级也是使用 -``pip install``\ ,只不过要加一个参数 ``--upgrade``\ 。 - -:: - - $ pip install --upgrade pkg - -在升级的时候,其实还有一个不怎么用到的选项 -``--upgrade-strategy``\ ,它是用来指定升级策略。 - -它的可选项只有两个: - -- ``eager`` :升级全部依赖包 -- ``only-if-need``\ :只有当旧版本不能适配新的父依赖包时,才会升级。 - -在 pip 10.0 版本之后,这个选项的默认值是 -``only-if-need``\ ,因此如下两种写法是一互致的。 - -.. code:: shell - - pip install --upgrade pkg1 - pip install --upgrade pkg1 --upgrade-strategy only-if-need - -6. 配置文件 ------------ - -由于在使用 pip 安装一些包时,默认会使用 pip -的官方源,所以经常会报网络超时失败。 - -常用的解决办法是,在安装包时,使用 ``-i`` -参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 - -这时候,其实可以将这个源写进 pip -的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 - -那怎么配置呢?文件文件在哪? - -使用\ ``win+r`` 输入 ``%APPDATA%`` 进入用户资料文件夹,查看有没有一个 -pip 的文件夹,若没有则创建之。 - -然后进入这个 文件夹,新建一个 ``pip.ini`` 的文件,内容如下 - -.. code:: ini - - [global] - time-out=60 - index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ - [install] - trusted-host=tsinghua.edu.cn - -以上几乎包含了 pip -的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 - -|image1| - -|image2| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20191105200041.png -.. |image2| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c04/c04_21.rst b/source/c04/c04_21.rst deleted file mode 100644 index f8323f2..0000000 --- a/source/c04/c04_21.rst +++ /dev/null @@ -1,392 +0,0 @@ -4.21 最全的 pip 使用指南,50% 你可能没用过 -========================================== - -|image0| - -所有的 Python 开发者都清楚,Python -之所以如此受欢迎,能够在众多高级语言中,脱颖而出,除了语法简单,上手容易之外,更多还要归功于 -Python 生态的完备,有数以万计的 Python 爱好者愿意以 Python -为基础封装出各种有利于开发的第三方工具包。 - -这才使用我们能够以最快的速度开发出一个满足基本需要的项目,而不是每次都重复造轮子。 - -Python -从1991年诞生到现在,已经过去28个年头了,这其间产生了数以万计的第三方包,且每个包都会不断更新,会有越来越多的版本。 - -当你在一个复杂的项目环境中,如果没有一个有效的依赖包管理方案,项目的维护将会是一个大问题。 - -pip 是官方推荐的包管理工具,在大多数开发者眼里,pip 几乎是 Python -的标配。 - -当然也有其他的包管理工具 - -- **distutils**\ :仅用于打包和安装,严格来讲不算是包管理工具 - -- **setuptools**\ :distutils的增强版,扩展了distutils,提供更多的功能,引入包依赖的管理,easy_install就是它的一个命令行工具,引入了 - egg 的文件格式。 - -- **Pipenv**\ :一个集依赖包管理(pip)及虚拟环境管理(virtualenv)的工具 - -- 还有其他的,这里不一一列出。 - -今天的主角是 pip -,大家肯定不会陌生。但我相信不少人,只是熟悉几个常用的用法,而对于其他几个低频且实用的用法,却知之甚少,这两天,我查阅官方文档,把这些用法整理了一下,应该是网络上比较全的介绍。 - -1. 查询软件包 -------------- - -查询当前环境安装的所有软件包 - -.. code:: shell - - $ pip list - -查询 pypi 上含有某名字的包 - -.. code:: shell - - $ pip search pkg - -查询当前环境中可升级的包 - -.. code:: shell - - $ pip list --outdated - -查询一个包的详细内容 - -.. code:: shell - - $ pip show pkg - -2. 下载软件包 -------------- - -在不安装软件包的情况下下载软件包到本地 - -.. code:: shell - - $ pip download --destination-directory /local/wheels -r requirements.txt - -下载完,总归是要安装的,可以指定这个目录中安装软件包,而不从 pypi -上安装。 - -.. code:: shell - - $ pip install --no-index --find-links=/local/wheels -r requirements.txt - -当然你也从你下载的包中,自己构建生成 wheel 文件 - -.. code:: shell - - $ pip install wheel - $ pip wheel --wheel-dir=/local/wheels -r requirements.txt - -3. 安装软件包 -------------- - -使用 ``pip install `` 可以很方便地从 pypi 上搜索下载并安装 python -包。 - -如下所示 - -.. code:: shell - - $ pip install requests - -这是安装包的基本格式,我们也可以为其添加更多参数来实现不同的效果。 - -**3.1 只从本地安装,而不从 pypi 安装** - -.. code:: shell - - # 前提你得保证你已经下载 pkg 包到 /local/wheels 目录下 - $ pip install --no-index --find-links=/local/wheels pkg - -**3.2 限定版本进行软件包安装** - -以下三种,对单个 python 包的版本进行了约束 - -.. code:: shell - - # 所安装的包的版本为 2.1.2 - $ pip install pkg==2.1.2 - - # 所安装的包必须大于等于 2.1.2 - $ pip install pkg>=2.1.2 - - # 所安装的包必须小于等于 2.1.2 - $ pip install pkg<=2.1.2 - -以下命令用于管理/控制整个 python 环境的包版本 - -.. code:: shell - - # 导出依赖包列表 - pip freeze >requirements.txt - - # 从依赖包列表中安装 - pip install -r requirements.txt - - # 确保当前环境软件包的版本(并不确保安装) - pip install -c constraints.txt - -**3.3 限制不使用二进制包安装** - -由于默认情况下,wheel 包的平台是运行 pip download 命令 -的平台,所以可能出现平台不适配的情况。 - -比如在 MacOS 系统下得到的 pymongo-2.8-cp27-none-macosx_10_10_intel.whl -就不能在 linux_x86_64 安装。 - -使用下面这条命令下载的是 tar.gz 的包,可以直接使用 pip install 安装。 - -比 wheel 包,这种包在安装时会进行编译,所以花费的时间会长一些。 - -.. code:: shell - - # 下载非二进制的包 - $ pip download --no-binary=:all: pkg - - # 安装非二进制的包 - $ pip install pkg --no-binary - -**3.4 指定代理服务器安装** - -当你身处在一个内网环境中时,无法直接连接公网。这时候你使用\ ``pip install`` -安装包,就会失败。 - -面对这种情况,可以有两种方法: - -1. 下载离线包拷贝到内网机器中安装 -2. 使用代理服务器转发请求 - -第一种方法,虽说可行,但有相当多的弊端 - -- 步骤繁杂,耗时耗力 -- 无法处理包的依赖问题 - -这里重点来介绍,第二种方法: - -.. code:: shell - - $ pip install --proxy [user:passwd@]http_server_ip:port pkg - -每次安装包就发输入长长的参数,未免有些麻烦,为此你可以将其写入配置文件中:\ ``$HOME/.config/pip/pip.conf`` - -对于这个路径,说明几点 - -- 不同的操作系统,路径各不相同 - -.. code:: shell - - # Linux/Unix: - /etc/pip.conf - ~/.pip/pip.conf - ~/.config/pip/pip.conf - - # Mac OSX: - ~/Library/Application Support/pip/pip.conf - ~/.pip/pip.conf - /Library/Application Support/pip/pip.conf - - # Windows: - %APPDATA%\pip\pip.ini - %HOME%\pip\pip.ini - C:\Documents and Settings\All Users\Application Data\PyPA\pip\pip.conf (Windows XP) - C:\ProgramData\PyPA\pip\pip.conf (Windows 7及以后) - -- 若在你的机子上没有此文件,则自行创建即可 - -如何配置,这边给个样例: - -.. code:: ini - - [global] - index-url = http://mirrors.aliyun.com/pypi/simple/ - - # 替换出自己的代理地址,格式为[user:passwd@]proxy.server:port - proxy=http://xxx.xxx.xxx.xxx:8080 - - [install] - # 信任阿里云的镜像源,否则会有警告 - trusted-host=mirrors.aliyun.com - -**3.5 安装用户私有软件包** - -很多人可能还不清楚,python 的安装包是可以用户隔离的。 - -如果你拥有管理员权限,你可以将包安装在全局环境中。在全局环境中的这个包可被该机器上的所有拥有管理员权限的用户使用。 - -如果一台机器上的使用者不只一样,自私地将在全局环境中安装或者升级某个包,是不负责任且危险的做法。 - -面对这种情况,我们就想能否安装单独为我所用的包呢? - -庆幸的是,还真有。 - -我能想到的有两种方法: - -1. 使用虚拟环境 -2. 将包安装在用户的环境中 - -虚拟环境,之前写过几篇文章,这里不再展开讲。 - -今天的重点是第二种方法,教你如何安装用户私有的包? - -命令也很简单,只要加上 ``--user`` 参数,pip 就会将其安装在当前用户的 -``~/.local/lib/python3.x/site-packages`` 下,而其他用户的 python -则不会受影响。 - -.. code:: shell - - pip install --user pkg - -来举个例子 - -.. code:: shell - - # 在全局环境中未安装 requests - [root@localhost ~]# pip list | grep requests - [root@localhost ~]# su - wangbm - [root@localhost ~]# - - # 由于用户环境继承自全局环境,这里也未安装 - [wangbm@localhost ~]# pip list | grep requests - [wangbm@localhost ~]# pip install --user requests - [wangbm@localhost ~]# pip list | grep requests - requests (2.22.0) - [wangbm@localhost ~]# - - # 从 Location 属性可发现 requests 只安装在当前用户环境中 - [wangbm@ws_compute01 ~]$ pip show requests - --- - Metadata-Version: 2.1 - Name: requests - Version: 2.22.0 - Summary: Python HTTP for Humans. - Home-page: http://python-requests.org - Author: Kenneth Reitz - Author-email: me@kennethreitz.org - Installer: pip - License: Apache 2.0 - Location: /home/wangbm/.local/lib/python2.7/site-packages - [wangbm@localhost ~]$ exit - logout - - # 退出 wangbm 用户,在 root 用户环境中发现 requests 未安装 - [root@localhost ~]$ pip list | grep requests - [root@localhost ~]$ - -当你身处个人用户环境中,python -导包时会先检索当前用户环境中是否已安装这个包,已安装则优先使用,未安装则使用全局环境中的包。 - -验证如下: - -.. code:: python - - >>> import sys - >>> from pprint import pprint - >>> pprint(sys.path) - ['', - '/usr/lib64/python27.zip', - '/usr/lib64/python2.7', - '/usr/lib64/python2.7/plat-linux2', - '/usr/lib64/python2.7/lib-tk', - '/usr/lib64/python2.7/lib-old', - '/usr/lib64/python2.7/lib-dynload', - '/home/wangbm/.local/lib/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages', - '/usr/lib64/python2.7/site-packages/gtk-2.0', - '/usr/lib/python2.7/site-packages', - '/usr/lib/python2.7/site-packages/pip-18.1-py2.7.egg', - '/usr/lib/python2.7/site-packages/lockfile-0.12.2-py2.7.egg'] - >>> - -**3.6 延长超时时间** - -若网络情况不是很好,在安装某些包时经常会因为 ReadTimeout 而失败。 - -对于这种情况,一般重试几次就好了。 - -但是这样难免有些麻烦,有没有更好的解决方法呢? - -有的,可以通过延长超时时间。 - -.. code:: shell - - $ pip install --default-timeout=100 - -4. 卸载软件包 -------------- - -就一条命令,不再赘述 - -.. code:: shell - - $ pip uninstall pkg - -5. 升级软件包 -------------- - -想要对现有的 python 进行升级,其本质上也是先从 pypi -上下载最新版本的包,再对其进行安装。所以升级也是使用 -``pip install``\ ,只不过要加一个参数 ``--upgrade``\ 。 - -:: - - $ pip install --upgrade pkg - -在升级的时候,其实还有一个不怎么用到的选项 -``--upgrade-strategy``\ ,它是用来指定升级策略。 - -它的可选项只有两个: - -- ``eager`` :升级全部依赖包 -- ``only-if-need``\ :只有当旧版本不能适配新的父依赖包时,才会升级。 - -在 pip 10.0 版本之后,这个选项的默认值是 -``only-if-need``\ ,因此如下两种写法是一互致的。 - -.. code:: shell - - pip install --upgrade pkg1 - pip install --upgrade pkg1 --upgrade-strategy only-if-need - -6. 配置文件 ------------ - -由于在使用 pip 安装一些包时,默认会使用 pip -的官方源,所以经常会报网络超时失败。 - -常用的解决办法是,在安装包时,使用 ``-i`` -参数指定一个国内的镜像源。但是每次指定就很麻烦呀,还要打超长的一串字母。 - -这时候,其实可以将这个源写进 pip -的配置文件里。以后安装的时候,就默认从你配置的这个 源里安装了。 - -那怎么配置呢?文件文件在哪? - -使用\ ``win+r`` 输入 ``%APPDATA%`` 进入用户资料文件夹,查看有没有一个 -pip 的文件夹,若没有则创建之。 - -然后进入这个 文件夹,新建一个 ``pip.ini`` 的文件,内容如下 - -.. code:: ini - - [global] - time-out=60 - index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ - [install] - trusted-host=tsinghua.edu.cn - -以上几乎包含了 pip -的所有常用使用场景,为了方便,我将其整理成一张表格,如果你需要,可以关注我的公众号(Python编程时光),后台回复“pip”,可获取高清无水印图片。 - -|image1| - -|image2| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png -.. |image1| image:: http://image.iswbm.com/20191105200041.png -.. |image2| image:: http://image.iswbm.com/20200607174235.png - diff --git a/source/c04/c04_22.rst b/source/c04/c04_22.rst deleted file mode 100644 index d7da5a8..0000000 --- a/source/c04/c04_22.rst +++ /dev/null @@ -1,7 +0,0 @@ -4.22 用好 Chrome 必看 -===================== - -|image0| - -.. |image0| image:: http://image.iswbm.com/20200602135014.png - diff --git a/source/c08/c08_10.md b/source/c08/c08_10.md index cb3c029..e2d53e0 100644 --- a/source/c08/c08_10.md +++ b/source/c08/c08_10.md @@ -1,4 +1,4 @@ -# 8.16 修改 KVM 镜像文件的三种方法 +# 8.10 修改 KVM 镜像文件的三种方法 ![](http://image.iswbm.com/20200602135014.png) diff --git a/source/c08/c08_10.rst b/source/c08/c08_10.rst index 8f66fff..20abedb 100644 --- a/source/c08/c08_10.rst +++ b/source/c08/c08_10.rst @@ -1,4 +1,4 @@ -8.16 修改 KVM 镜像文件的三种方法 +8.10 修改 KVM 镜像文件的三种方法 ================================ |image0| From f9cc6ce27ef611be8be97807b8f38ef7e272029b Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 23 Dec 2020 23:29:06 +0800 Subject: [PATCH 135/147] update --- README.md | 1114 +++++++++++++++++++++++++++----- github-toc-maker-for-sphinx.py | 2 +- md2rst.py | 6 +- source/c02/c02_04.md | 2 +- source/c02/c02_04.rst | 2 +- source/c04/c04_03.md | 4 +- source/c04/c04_03.rst | 4 +- 7 files changed, 973 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index 2675cf2..6b17275 100644 --- a/README.md +++ b/README.md @@ -1,154 +1,966 @@ -![](http://image.iswbm.com/image-20201117214825880.png) - -

    - Build Status - - - - -

    - - -## [项目主页](http://python.iswbm.com/) - -在线阅读:[Python 编程时光](http://python.iswbm.com/) - -- **第一章:基础知识** - * [1.1 13条Python2.x和3.x的区别?](http://python.iswbm.com/en/latest/c01/c01_01.html) - * [1.2 Python 自省机制详解](http://python.iswbm.com/en/latest/c01/c01_02.html) - * [1.3 /usr/bin/env python 有什么用?](http://python.iswbm.com/en/latest/c01/c01_03.html) - * [1.4 什么是猴子补丁?](http://python.iswbm.com/en/latest/c01/c01_04.html) - * [1.5 深入闭包与变量作用域](http://python.iswbm.com/en/latest/c01/c01_05.html) - * [1.6 有了列表,为什么 Python 还有元组?](http://python.iswbm.com/en/latest/c01/c01_06.html) - * [1.7 15个Pythonic的代码示例](http://python.iswbm.com/en/latest/c01/c01_07.html) - * [1.8 新式类和经典类的区别?](http://python.iswbm.com/en/latest/c01/c01_08.html) - * [1.9 多继承与Mixin设计模式](http://python.iswbm.com/en/latest/c01/c01_09.html) - * [1.10 如何修改 CentOS 6.x 上默认Python](http://python.iswbm.com/en/latest/c01/c01_10.html) - * [1.11 正则表达式必知必会](http://python.iswbm.com/en/latest/c01/c01_11.html) - * [1.12 搞懂字符编码的前世今生](http://python.iswbm.com/en/latest/c01/c01_12.html) - * [1.13 别再使用 pprint 打印字典了](http://python.iswbm.com/en/latest/c01/c01_13.html) - * [1.14 with 与 上下文管理器](http://python.iswbm.com/en/latest/c01/c01_14.html) - * [1.21 Python 开发的几个小 Tips](http://python.iswbm.com/en/latest/c01/c01_15.html) - * [1.16 泛型函数怎么写?](http://python.iswbm.com/en/latest/c01/c01_16.html) - * [1.17 详解 Python 中的编码问题](http://python.iswbm.com/en/latest/c01/c01_17.html) - * [1.18 MySQL 使用总结](http://python.iswbm.com/en/latest/c01/c01_18.html) - * [1.19 如何调试已经运行中的程序](http://python.iswbm.com/en/latest/c01/c01_19.html) - * [1.20 在 CentOS 7.2 上安装 Python3.7](http://python.iswbm.com/en/latest/c01/c01_20.html) - * [1.21 Python 炫技操作:连接列表的八种方法](http://python.iswbm.com/en/latest/c01/c01_21.html) - * [1.22 Python 炫技操作:海象运算符的三种用法](http://python.iswbm.com/en/latest/c01/c01_22.html) - * [1.23 Python 炫技操作:模块重载的五种方法](http://python.iswbm.com/en/latest/c01/c01_23.html) - * [1.24 Python 炫技操作:条件语句的七种写法](http://python.iswbm.com/en/latest/c01/c01_24.html) - * [1.25 Python炫技操作:花式导包的八种方法](http://python.iswbm.com/en/latest/c01/c01_25.html) - * [1.26 Python 炫技操作:合并字典的七种方法](http://python.iswbm.com/en/latest/c01/c01_26.html) - * [1.27 Python 炫技操作:判断是否包含子串的七种方法](http://python.iswbm.com/en/latest/c01/c01_27.html) -- **第二章:并发编程** - * [2.1 从性能角度初探并发编程](http://python.iswbm.com/en/latest/c02/c02_01.html) - * [2.2 创建多线程的几种方法](http://python.iswbm.com/en/latest/c02/c02_02.html) - * [2.3 谈谈线程中的“锁机制”](http://python.iswbm.com/en/latest/c02/c02_03.html) - * [2.4 线程消息通信机制](http://python.iswbm.com/en/latest/c02/c02_04.html) - * [2.5 线程中的信息隔离](http://python.iswbm.com/en/latest/c02/c02_05.html) - * [2.6 线程池创建的几种方法](http://python.iswbm.com/en/latest/c02/c02_06.html) - * [2.7 从生成器使用入门协程](http://python.iswbm.com/en/latest/c02/c02_07.html) - * [2.8 深入理解yield from语法](http://python.iswbm.com/en/latest/c02/c02_08.html) - * [2.9 初识异步IO框架:asyncio 上篇](http://python.iswbm.com/en/latest/c02/c02_09.html) - * [2.10 深入异步IO框架:asyncio 中篇](http://python.iswbm.com/en/latest/c02/c02_10.html) - * [2.11 实战异步IO框架:asyncio 下篇](http://python.iswbm.com/en/latest/c02/c02_11.html) - * [2.12 生成器与协程,你分清了吗?](http://python.iswbm.com/en/latest/c02/c02_12.html) - * [2.13 I/O多路复用:select/poll/epoll](http://python.iswbm.com/en/latest/c02/c02_13.html) - * [2.14 浅谈线程安全那些事儿](http://python.iswbm.com/en/latest/c02/c02_14.html) -- **第三章:高级编程** - * [3.1 装饰器进阶用法详解](http://python.iswbm.com/en/latest/c03/c03_01.html) - * [3.2 深入理解Python元类](http://python.iswbm.com/en/latest/c03/c03_02.html) - * [3.3 Socket编程实现在线聊天](http://python.iswbm.com/en/latest/c03/c03_03.html) - * [3.4 Django+Uwsgi部署网站](http://python.iswbm.com/en/latest/c03/c03_04.html) - * [3.5 源码解读:Flask上下文与代理模式](http://python.iswbm.com/en/latest/c03/c03_05.html) - * [3.6 Web开发者必看:理解WSGI](http://python.iswbm.com/en/latest/c03/c03_06.html) - * [3.7 超实用10 条 Python使用技巧](http://python.iswbm.com/en/latest/c03/c03_07.html) - * [3.8 深入理解 Python 中的描述符](http://python.iswbm.com/en/latest/c03/c03_08.html) - * [3.9 深入探讨 Python 的 import 机制](http://python.iswbm.com/en/latest/c03/c03_09.html) - * [3.10 Python几个高阶函数](http://python.iswbm.com/en/latest/c03/c03_10.html) - * [3.11 with 与 上下文管理器](http://python.iswbm.com/en/latest/c03/c03_11.html) - * [3.12 静态方法其实暗藏玄机](http://python.iswbm.com/en/latest/c03/c03_12.html) - * [3.13 包导入的三个冷门知识点](http://python.iswbm.com/en/latest/c03/c03_13.html) - * [3.14 全面学习 Python 包:包的构建与分发](http://python.iswbm.com/en/latest/c03/c03_14.html) -- **第四章:开发工具** - * [4.1 虚拟环境:virtualenv](http://python.iswbm.com/en/latest/c04/c04_01.html) - * [4.2 虚拟环境:Pipenv](http://python.iswbm.com/en/latest/c04/c04_02.html) - * [4.3 30分钟教你搭建一个博客](http://python.iswbm.com/en/latest/c04/c04_03.html) - * [4.4 Jupyter NoteBook 使用指南](http://python.iswbm.com/en/latest/c04/c04_04.html) - * [4.5 最全的 pip 使用指南](http://python.iswbm.com/en/latest/c04/c04_05.html) - * [4.6 我的 Git 使用指南](http://python.iswbm.com/en/latest/c04/c04_06.html) - * [4.7 Hexo 搭建博客教程](http://python.iswbm.com/en/latest/c04/c04_07.html) - * [4.8 珍惜生命,远离鼠标](http://python.iswbm.com/en/latest/c04/c04_08.html) - * [4.9 MySQL的基本使用](http://python.iswbm.com/en/latest/c04/c04_09.html) - * [4.10 MySQL的高级进阶](http://python.iswbm.com/en/latest/c04/c04_10.html) - * [4.11 超详细图文教你如何使用 PyCharm 进行远程调试](http://python.iswbm.com/en/latest/c04/c04_11.html) - * [4.12 服务器调试神器:pdb](http://python.iswbm.com/en/latest/c04/c04_12.html) - * [4.13 命令行解析工具:argparse](http://python.iswbm.com/en/latest/c04/c04_13.html) - * [4.14 Xshell的高效使用手册](http://python.iswbm.com/en/latest/c04/c04_14.html) - * [4.15 30个 PyCharm 实用技巧](http://python.iswbm.com/en/latest/c04/c04_15.html) - * [4.16 程序员编码必学:Vim](http://python.iswbm.com/en/latest/c04/c04_16.html) - * [4.17 详解 23 种设计模式](http://python.iswbm.com/en/latest/c04/c04_17.html) - * [4.18 如何用好 Python的用户环境?](http://python.iswbm.com/en/latest/c04/c04_18.html) -- **第五章:算法教程** - * [5.1 图解九大经典排序算法](http://python.iswbm.com/en/latest/c05/c05_01.html) - * [5.2 递归算法:走楼梯会思考的题](http://python.iswbm.com/en/latest/c05/c05_02.html) - * [5.3 哈希算法:安全方面的算法应用](http://python.iswbm.com/en/latest/c05/c05_03.html) -- **第六章:数据分析** - * [6.1 一张图带你入门matplotlib](http://python.iswbm.com/en/latest/c06/c06_01.html) - * [6.2 详解六种可视化图表](http://python.iswbm.com/en/latest/c06/c06_02.html) - * [6.3 如何绘制正余弦函数图象](http://python.iswbm.com/en/latest/c06/c06_03.html) - * [6.4 子图与子区 难点突破](http://python.iswbm.com/en/latest/c06/c06_04.html) - * [6.5 绘制酷炫的gif动态图](http://python.iswbm.com/en/latest/c06/c06_05.html) - * [6.6 自动生成图像视频](http://python.iswbm.com/en/latest/c06/c06_06.html) -- **第七章:运维人生** - * [7.1 Linux 命令行的艺术](http://python.iswbm.com/en/latest/c07/c07_01.html) - * [7.2 Zabbix 监控部署文档](http://python.iswbm.com/en/latest/c07/c07_02.html) - * [7.3 Docker:Hello world](http://python.iswbm.com/en/latest/c07/c07_03.html) - * [7.4 Docker:关于镜像](http://python.iswbm.com/en/latest/c07/c07_04.html) - * [7.5 Docker:网络通信](http://python.iswbm.com/en/latest/c07/c07_05.html) - * [7.6 Docker:存储与多主机](http://python.iswbm.com/en/latest/c07/c07_06.html) - * [7.7 SaltStack 入门指南](http://python.iswbm.com/en/latest/c07/c07_07.html) - * [7.8 Keepalived 部署文档](http://python.iswbm.com/en/latest/c07/c07_08.html) - * [7.9 Ansible 入门指南使用手册](http://python.iswbm.com/en/latest/c07/c07_09.html) - * [7.10 Ansible API 最全使用文档(中文)](http://python.iswbm.com/en/latest/c07/c07_10.html) - * [7.11 K8S:基础入门](http://python.iswbm.com/en/latest/c07/c07_11.html) - * [7.12 yum 命令使用指南及问题排查方法](http://python.iswbm.com/en/latest/c07/c07_12.html) - * [7.13 基于 ansible-api 二次开发](http://python.iswbm.com/en/latest/c07/c07_13.html) - * [7.14 Linux 如何写判断语句](http://python.iswbm.com/en/latest/c07/c07_14.html) - * [7.15 Mariadb 与 Galera 集群总结](http://python.iswbm.com/en/latest/c07/c07_15.html) - * [7.16 Linux 运维之路](http://python.iswbm.com/en/latest/c07/c07_16.html) - * [1.17 ansible 自定义 Jinja2 过滤器](http://python.iswbm.com/en/latest/c07/c07_17.html) - * [7.18 Shell中去除字符串前后空格的方法](http://python.iswbm.com/en/latest/c07/c07_18.html) - * [7.19 Ansible 使用教程](http://python.iswbm.com/en/latest/c07/c07_19.html) - * [7.20 使用 Shell 删除文件的多种方法](http://python.iswbm.com/en/latest/c07/c07_20.html) - * [7.21 如何快速创建超大文件?](http://python.iswbm.com/en/latest/c07/c07_21.html) - * [7.22 rpm 命令详细用法](http://python.iswbm.com/en/latest/c07/c07_22.html) -- **第八章:OpenStack** - * [8.1 OpenStack 运维命令](http://python.iswbm.com/en/latest/c08/c08_01.html) - * [8.2 OpenStack 部署SR-IOV](http://python.iswbm.com/en/latest/c08/c08_02.html) - * [8.3 制作 OpenStack 镜像](http://python.iswbm.com/en/latest/c08/c08_03.html) - * [8.4 云计算与虚拟化入门通识](http://python.iswbm.com/en/latest/c08/c08_04.html) - * [8.5 OpenStack 源码剖析与改造](http://python.iswbm.com/en/latest/c08/c08_05.html) - * [8.6 源码解读:Cloud-Init](http://python.iswbm.com/en/latest/c08/c08_06.html) - * [8.7 OpenStack 实现GPU直通](http://python.iswbm.com/en/latest/c08/c08_07.html) - * [8.8 OpenStack 如何使用DHCP?](http://python.iswbm.com/en/latest/c08/c08_08.html) - * [8.9 从0到1:全面理解RPC远程调用](http://python.iswbm.com/en/latest/c08/c08_09.html) - * [8.10 修改 KVM 镜像文件的三种方法](http://python.iswbm.com/en/latest/c08/c08_10.html) - * [8.11 OpenStack 问题排查](http://python.iswbm.com/en/latest/c08/c08_11.html) - * [8.12 OpenStack之主机调度](http://python.iswbm.com/en/latest/c08/c08_12.html) - * [8.13 网络知识必知必会](http://python.iswbm.com/en/latest/c08/c08_13.html) - * [8.14 支持 IPv6以及多运营商](http://python.iswbm.com/en/latest/c08/c08_14.html) - * [8.15 Neutron 源码解读](http://python.iswbm.com/en/latest/c08/c08_15.html) - * [8.16 详解 Neutron 的 QoS](http://python.iswbm.com/en/latest/c08/c08_16.html) -- **第九章:有趣的工具** - * [9.1 情人节来了,教你使用 Python 来表白](http://python.iswbm.com/en/latest/c09/c09_01.html) - * [9.2 我在Mac 上使用哪些APP?](http://python.iswbm.com/en/latest/c09/c09_02.html) - * [9.3 明哥的在线工具集](http://python.iswbm.com/en/latest/c09/c09_03.html) - * [9.4 书籍推荐](http://python.iswbm.com/en/latest/c09/c09_04.html) - * [9.5 让Python 执行任意代码时,都会自动念一段 『平安经』](http://python.iswbm.com/en/latest/c09/c09_05.html) - * [9.6 赶紧收藏!学习 Python 的好网站](http://python.iswbm.com/en/latest/c09/c09_06.html) - * [9.7 解决网页上不能复制的几个小技巧](http://python.iswbm.com/en/latest/c09/c09_07.html) +# 从零到一的 Python 学习路线 + +我在我的个人公众号(Python编程时光)分享过非常多的 Python 干货,由于公众号是个十分封闭的生态,读过之后,就没什么人会记住它了。不像网站那样有搜索引擎会给它们持续的曝光,历久弥香。 + +我自认为在我公众号里,发布的文章质量是非常高的,为了不让这些干货沉入海底,我开了这个仓库,方便有需要的人进行索引,择需阅读。 + + + +目前目录更新内容至 2020/12/4 发的文章。 + +## 01. 基础系列 + +### 1.1 基础必学 + +1、[盘点 Python 高手都写不出来的几个错误](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485974&idx=1&sn=6a6a2fb8bc5c2acd300ebecdf625086c&chksm=e8866af4dff1e3e2c528594310475edaf2839d7b09048270acbb0dccc124581eaa65b123d393&scene=27#wechat_redirect) + +2、[Python基础|深入闭包与变量作用域](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485039&idx=1&sn=23557ac2640819b568a426b2db4df69c&scene=21#wechat_redirect) + +3、[Python基础|类方法的强制重写与禁止重写](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485037&idx=1&sn=8f4838b5bc919631c5cb642120b010c2&scene=21#wechat_redirect) + +4、[Python基础|多继承与Mixin设计模式](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485032&idx=1&sn=35e1c7014bc3f668cc4b48d9c318f1f9&scene=21#wechat_redirect) + +5、[Python基础|理解元组存在的意义](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485038&idx=1&sn=d0c7ab3fc20b299e4a0b9f6b414e4070&scene=21#wechat_redirect) + +6、[你知道 Python里的「单分派泛函数」?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484938&idx=1&sn=061fdb00ccc499aa0cfc304852adb143&scene=21#wechat_redirect) + +7、[类型注解的福音,提高Python代码可读性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484901&idx=1&sn=8f506cb46edb94dfb668525399f30f9e&scene=21#wechat_redirect) + +8、[写几个 Python 进阶必备函数](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484973&idx=1&sn=451d381fa3021e14b514cc15907ce0c3&scene=21#wechat_redirect) + +9、[Python 字符串连接,哪种的效率最高?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485016&idx=1&sn=88497c09c8785b656ae1fee1406827dc&scene=21#wechat_redirect) + +10、[深入理解Python中的上下文管理器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484950&idx=1&sn=9d78e469f8f190ef0484842b0391bec9&scene=21#wechat_redirect) + +11、[秒杀市面 90% 的 Python 入门教程 (上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484852&idx=1&sn=2362e034c2740d9f53c3cc99a6908cbd&scene=21#wechat_redirect) + +12、[秒杀市面 90% 的 Python 入门教程 (中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484856&idx=1&sn=881ee5d8154a85479d71ca4594f90142&scene=21#wechat_redirect) + +13、[秒杀市面 90% 的 Python 入门教程 (下)](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485231&idx=1&sn=fa7146937f51110eb7356154bc2e2282&scene=21#wechat_redirect) + +14、[检验你 Python 基本功的 17 个骚操作](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484863&idx=2&sn=ff11a17ad82938b3a4f83ee5fbc2e497&scene=21#wechat_redirect) + +25、[和import说再见,这个库教你怎么偷懒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485325&idx=2&sn=90c6dfbdbb36f63db72d1c0af67c1115&scene=21#wechat_redirect) + +16、[如何修改 CentOS 6.x 上默认Python 版本](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484881&idx=1&sn=ecb99878d3e2fca2bedc808b1e7aae9c&scene=21#wechat_redirect) + +17、[写 Python 时你要避免的十个错误](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=1&sn=6acba3bf872fe1309308d9ef6cde53ce&scene=21#wechat_redirect) + +18、[看完这篇,你也是字符编码大神!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488475&idx=2&sn=17ca56435158c6c19134e30bda6e2b2d&chksm=e8867339dff1fa2f99cb0d85d99a2befa3689951778fbef8f2e0bad1042ae0c4813b99860c33&scene=27#wechat_redirect) + +19、[大白话解释什么是 Python Launcher?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485268&idx=1&sn=9cea766f1e1a1f6278fca4665fc33a87&scene=21#wechat_redirect) + +20、[13条Python2.x和3.x的区别,你知道几条?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485044&idx=1&sn=f38e2036317893be8388900dde3077e8&scene=21#wechat_redirect) + +21、[Python基础|新式类和经典类的区别?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485033&idx=1&sn=8b6357d5a66f9a06347bf0922ae11946&scene=21#wechat_redirect) + +22、[Python 中有 3 个不可思议的返回功能](https://mp.weixin.qq.com/s/dSky88bOCPgYDprzJhnlbg) + +23、[太干了!一张图整理了 Python 所有内置异常](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=2&sn=59578093d82362c023ce305b2fc55103&chksm=e886791adff1f00c0a76154c7c7101d39b151891cc07b33dfe2523c95d9e38250c3684f674bb&scene=27#wechat_redirect) + +24、[有了这篇文章, Python 中的编码不再是噩梦](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486174&idx=1&sn=ceb21deb2ff3ce750117c23f414416e3&chksm=e8866a3cdff1e32a1fcf07880803a7a3c350cc40d612bbd4f34b84cb06bf39250932f1ba5f05&scene=27#wechat_redirect) + +25、[ 别笑!Python 新手这五大坑你躲不过](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485573&idx=1&sn=362e7c5e73c27942e8250375257620e2&chksm=e8866867dff1e17140dc043b5dd33eec0e94e1616eab7e4b415bb767883d46d58a7243fb198f#rd) + +26、[Python 3.9 发布,字典的合并操作符终于来了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485825&idx=1&sn=04075357936423097f692fa19857c3aa&chksm=e8866963dff1e075707b605fb1352f7760fb1b41040bdda4dec6b83370af37e009424d24f65e&token=2013245174&lang=zh_CN#rd) + +27、[Python 3 入门,看这篇就够了(超全整理)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=1&sn=7985df89a647c271a9c99b1a25f87cb4&chksm=e8858366dff20a7062c54d79cfc85945d4ae91e96c0a0dcb8383d24680c6eeb80d68a6f2b718&scene=27#wechat_redirect) + +28、[掌握 Python 中下划线的 5 个潜规则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492133&idx=2&sn=ba4631b32f8fb4cd018a68b5411771b1&chksm=e88582c7dff20bd1c417c97e6798ba5bcacc90b55161976d7529d12f1850e413ede66110cbe3&scene=27#wechat_redirect) + +29、[还傻傻分不清什么是方法,什么是函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491545&idx=2&sn=193bb88089cc7583989db44707aeca90&chksm=e8867f3bdff1f62d159dca793efc1c937b1c7a135b6f25ff9d77d8d1b819c7ddc66b3ba1f8d0&scene=27#wechat_redirect) + +30、[Python 如何像 awk一样分割字符串?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491382&idx=2&sn=6e3824fa540fe7225d0cd1e7180559b6&chksm=e8867fd4dff1f6c2d2c024b6a07f616794c5b89a1b2b08ab05d029e5de1a59ff751bb03a6561&scene=27#wechat_redirect) + +31、[一篇文章带你剖析Python 字节流处理神器struct](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=2&sn=889bf712f4115a27f321e791b3862405&chksm=e8867e4bdff1f75dc9e1e2374c3302753c3a6a28b42436e6e2550aa8ac528652b2561ad2ca48&scene=27#wechat_redirect) + +32、[OrderedDict 是如何保证 Key 的插入顺序的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486136&idx=2&sn=81fd4039611666917ae8801bd73fe921&chksm=e8866a5adff1e34c603500e97c4b7e1cecb7a1fb43e01c6ff5a794d526d54e5a02b35240d2db&scene=27#wechat_redirect) + +33、[字典访问不存在的key 时,如何才能不报错?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490809&idx=2&sn=a256a0d8b86bae3a7dc65f7a88e4ab39&chksm=e8867c1bdff1f50d1da0e11d2dca288b085064062ecb8f68891b0bd48b8a1fca4f8ca594fc22&scene=27#wechat_redirect) + +34、[如何使用 Python 执行 js 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491161&idx=2&sn=d89bbc4c214563807427703aa584a26d&chksm=e8867ebbdff1f7ad301b58ac567faec95be2d8f5be921b23ea5d3722d377c2926f32ff7b68ac&scene=27#wechat_redirect) + +35、[Python 代码覆盖率工具 - Coverage](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489752&idx=2&sn=b115d9377feb0129985295e5a3c8ec5e&chksm=e886783adff1f12c7bb14c55560f06f6a5327d6beee9bba38b9965784baf197918ae3898e95d&scene=27#wechat_redirect) + +36、[超赞!100 道让你练习 Python 基础的题目](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488558&idx=1&sn=58a53b85a7e49989794d8d0e9885f335&chksm=e88674ccdff1fdda61609dc4aa811dc6a52a819fe56da3bd0480f9f21ae248c94d8f8f3f8798&scene=27#wechat_redirect) + +37、[一道 3 行代码的 Python面试题,我懵逼了一天](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486901&idx=1&sn=73fe3a92ac2639706d9573a0eaffd48f&chksm=e8866d57dff1e441d6ffad3c4f00b053db39df1cec4e6cf69992641b359cc96448f7f3068918&scene=27#wechat_redirect) + +38、[学习 Python 的指南大纲,从基础到核心知识全都有](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486246&idx=1&sn=58665e2be95f71415ac3e2c1c5f4f690&chksm=e8866bc4dff1e2d212a4d93c0c83cd39cd534f64a8e113b208f96a6156e75ef652ee1ac32270&scene=27#wechat_redirect) + +### 1.2 基础库 + +1、[Python时间模块,超实用总结!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493317&idx=2&sn=b821b2765cfcdf7da8858b962f092980&chksm=e8858627dff20f31b14090d0ff178002ea0544196d8d63e595f5f6787f8e95d60df292f80bb4&scene=27#wechat_redirect) + +2、[用 Python 玩转正则表达式,这篇讲得太好了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493207&idx=2&sn=ca2c44f056ce22d132089d200951ee6d&chksm=e88586b5dff20fa3db38273731c3521a278c09ff81570d01512f203d280d253ae1f3d5fc1799&scene=27#wechat_redirect) + +3、[打基础一定要吃透这12类 Python 内置函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486198&idx=1&sn=efea0e57956188eeafb163d7bb2448c4&chksm=e8866a14dff1e3022f361245b182a30fbbf40550f3e8dc4456e1acf260aa73d35a20c010559d&scene=27#wechat_redirect) + +4、[8个超好用的Python内置函数,提升效率必备!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485943&idx=2&sn=ecb57c8dbf0e4965d4842cc6f614da64&chksm=e8866915dff1e0035ded3974097c1f0e00561962eeb0498c633fdf664cd429749aa6e6eca806&scene=27#wechat_redirect) + +5、[用 Python 操作 Redis,看这一篇就够了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492907&idx=2&sn=f44d9ef0e624b150c080dd075974e540&chksm=e88585c9dff20cdf6ed8b913312602838fbdffcade584412e43326d11f0a1cc41bd4ffbce94c&scene=27#wechat_redirect) + +6、[超全!我把 Python 的 200个标准库整理出来了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492755&idx=1&sn=c0f99bb14839677d228d029a564d457c&chksm=e8858471dff20d67bc162863a6193998f39aae3e1103a5b3015ceb79f4cea1dde4fda5d2cff3&scene=27#wechat_redirect) + +7、[一篇文章掌握 Python 内置 zip() 的全部内容](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491227&idx=2&sn=61efa9b45c112a25be348a94a9ac8151&chksm=e8867e79dff1f76fcc3858afdfd4e68aef0a5d0430f0420aacd2045aac86d3c31ff88bac464d&scene=27#wechat_redirect) + +8、[通过“四不要”,掌握 Python 的 Lambda 函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493501&idx=2&sn=696cb2ef4c3fcc6713f148d42f6eb0be&chksm=e885879fdff20e897b71e791caa6a6dbfb783c22a3ea7bf9c4cfb8406b668e57914c1c209707#rd) + +9、[原来 collections 这么好用!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493894&idx=2&sn=3f16362bfa3ec2ecf9f7318cc50e2f95&chksm=e88589e4dff200f25c4c03294f59716431c57a65161f6c364d610950650b24a7cb4b037345e4#rd) + +10、[使用 Python 打印漂亮的表格,这两项基本功你可会?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494135&idx=2&sn=f055f78da070dc71c2593540f3272988&chksm=e8858915dff20003c05bc328b09a8cdb8dda3e37118201fb2eb317c1296e788aa1f50c8edf45&scene=27#wechat_redirect) + +### 1.3 代码案例 + +1、[15个Pythonic的代码示例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485036&idx=1&sn=24de1996a63bf25b0c0deec782f688cf&scene=21#wechat_redirect) + +2、[常见 Python 简洁代码的样例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484823&idx=2&sn=4459a7edc4a4989068b22b31c76f2c92&scene=21#wechat_redirect) + +3、[每天花 30 秒,就可以练习的 Python 小技巧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484861&idx=1&sn=e5fa97570b3c180e5a5edc753fc62669&scene=21#wechat_redirect) + +4、[精心整理!9个 Python 实用案例分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492921&idx=2&sn=800c245cf392dfed915c618c68a5fe80&chksm=e88585dbdff20ccdd85dc21989092ab3e028dd2e3bcfb8f349bc3a7df99b299b6f8a43b57371&scene=27#wechat_redirect) + +5、[瞧瞧,这样的代码才叫 Pythonic](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493439&idx=1&sn=2ed198c261ee285f2e3c1cbdf5c78541&chksm=e88587dddff20ecb24804e6b191b690d7950584104b5ed94b2f1e6a043a24909dfe418e64df6#rd) + +6、[瞧瞧,这样的『函数』才叫 Pythonic](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493550&idx=1&sn=cd5cf944c3e3ac3f2b567def827de2d4&chksm=e885874cdff20e5aeb438aab72e2f87329a403024bb0cae3308e61d5f9cc9a15609da6d8b7dd#rd) + +7、[这样的奇技淫巧不可取,切记切记](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493635&idx=2&sn=cc61ae5798c278c52a07e3e7129a5bb1&chksm=e88588e1dff201f7c4997d9fe8004d86973833bbe91ded31fbf93cdf00d1ec22f19f8cff825c&token=58703854&lang=zh_CN#rd) + +8、[别这样直接运行Python命令,否则电脑等于“裸奔”](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494114&idx=1&sn=202d3492ae48d73431173b76caa64345&chksm=e8858900dff2001626f47454f8dee79679f43b74701c7730e1c499f4c895edafca5aa68e280f&scene=27#wechat_redirect) + +9、[6 个例子教你重构 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494309&idx=2&sn=e1cffffa71b5cc76434b6771cfcc0bae&chksm=e8858a47dff20351575b1cd29e35a51b30c7859c9dffbf816df883bc7ccee9ea15aa5eaad812&scene=27#wechat_redirect) + +## 02. 进阶系列 + +### 2.1 进阶必学 + +1、[描述符:其实你不懂我(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484934&idx=1&sn=ef8b30ffe2467e5736036bd083b340ca&scene=21#wechat_redirect) + +2、[描述符:我无处不在!(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484927&idx=1&sn=8a3d673ee20bd418d93a249380ca8e76&scene=21#wechat_redirect) + +3、[Python静态方法其实暗藏玄机](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484911&idx=1&sn=a16846030c3589c912aec9efdf4f7f80&scene=21#wechat_redirect) + +4、[全面深入理解 Python 面向对象](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485208&idx=1&sn=61bf8d3ec81fa85b0bc9de6e5f5d6611&scene=21#wechat_redirect) + +5、[几个使用装饰器的小技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484930&idx=1&sn=ed731e32b6e95e7d83d74a544f97142a&scene=21#wechat_redirect) + +6、[围观大神是如何用 Python 处理文件的?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484905&idx=1&sn=7de0eaab0a4c9f8b44cba01d28ad7254&scene=21#wechat_redirect) + +7、[Python进阶开发|元类编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485080&idx=1&sn=45e6d9995e4469d8b7bce2f800ac0f9b&scene=21#wechat_redirect) + +8、[Python进阶开发之网络编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485078&idx=1&sn=e0a3855d959c178b8c01bd196a048dce&scene=21#wechat_redirect) + +9、[Python 进阶:深入 GIL (上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484875&idx=2&sn=8780a349c60522ad1b7aa20a3fe7a19c&scene=21#wechat_redirect) + +10、[没掌握好这24条,别说Python慢。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484910&idx=1&sn=df8fb32a840a28954614b09bce730b72&scene=21#wechat_redirect) + +11、[花了两个星期,我终于把WSGI整明白了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484919&idx=1&sn=bd7d2bc0ab8a41110d5d93e44ad20b1f&scene=21#wechat_redirect) + +12、[源码解读|Flask 上下文核心机制](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484943&idx=1&sn=ca846404d30a2ec775ab7db5df73aa8e&scene=21#wechat_redirect) + +13、[说说几个 Python 内存分配时的小秘密](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484853&idx=2&sn=edf7c3225d119291fae5d05820f55aac&scene=21#wechat_redirect) + +14、[写了三年代码,还是不懂 Python 世界的规则](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484886&idx=1&sn=f46917f6e23f8961c912606a281a354d&scene=21#wechat_redirect) + +15、[如何保护你写的 Python 代码?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484858&idx=1&sn=e18a1c89f14d4e4043833e3ff5cc8d32&scene=21#wechat_redirect) + +16、[27 个问题,告诉你 Python 为什么如此设计?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484862&idx=1&sn=89500433bc174dfc0530eebea2fb91d1&scene=21#wechat_redirect) + +17、[精心整理 30 个Python代码实现的常用功能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485147&idx=2&sn=c5b871e1cebde6b311609f6de703f2e3&scene=21#wechat_redirect) + +18、[高手之路:从零开始打造一个Web服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484891&idx=1&sn=ed12d175452c2efedeefb1635063177c&scene=21#wechat_redirect) + +19、[从0到1:全面理解 RPC 远程调用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484915&idx=1&sn=427b9dda155e4201c2f939c3919def91&scene=21#wechat_redirect) + +20、[一篇 Python 函数式编程指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484834&idx=1&sn=559bac4ff85f7082bc989a6fde04d3f3&scene=21#wechat_redirect) + +21、[没看完这11 条,别说你精通 Python 装饰器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484900&idx=1&sn=3997a2a377577e3d16a9b7f8e6a5ea53&scene=21#wechat_redirect) + +22、[程序卡住了?教你如何调试已在运行的程序](https://mp.weixin.qq.com/s/QC-Pc-0iifaVKOfsiNTYmA) + +23、[字符串在Python内部是如何省内存的](https://mp.weixin.qq.com/s/jp_I82fnr0-3cd6ioZcoGQ) + +24、[巧用 traceback 定位 Python 内存泄漏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485751&idx=1&sn=8a054a575767c103c830a6b3ef5f73c8&chksm=e88669d5dff1e0c3ae1c7c32f35a8f46a3a2e0a637fb4b947566f417dd8f4419bec636c8a667#rd) + +25、[非常全的通俗易懂 Python 魔法方法指南(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485551&idx=1&sn=4c0983f22269a113bcdf83690e5e2b20&chksm=e886688ddff1e19b9ad230128a67ee1a9ee1eced0720c14b5d48f68943be10b1b85b23d8ca2d#rd) + +26、[非常全的通俗易懂 Python 魔法方法指南(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485555&idx=1&sn=0a218b796e651b451a17112e22790d07&chksm=e8866891dff1e18771a9392da7f509732244ebc4d1a6e2427acd39ee8b59b146e3d4961a2a62#rd) + +27、[教你如何阅读 Python 开源项目代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485451&idx=1&sn=e15856352c18297770c67d9df66ec3b0&chksm=e88668e9dff1e1ff0f6a39f3885f4126232213149ac09daa1fc0ec9e0de2095bd11fb727d63a#rd) + +28、[这个 Python 知识点,90% 的人都得挂~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490984&idx=1&sn=135b7b187d8d41b3c10179dbaedaf77e&chksm=e8867d4adff1f45cd90fe2609fe7d1840b07fab8cf19cb7406ebd038c31df0028e611077d80e&scene=27#wechat_redirect) + +29、[一篇长文学懂 PyTorch](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489158&idx=1&sn=2e2e88565131d11271aea61b0bedf182&chksm=e8867664dff1ff728ee30e1baab0d7df0139fc67e2514ffcaebda6d50e5d08bd2866ad7dee35&scene=27#wechat_redirect) + +30、[学了这么久,你知道Python是如何运作的吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488458&idx=1&sn=23040c906e83462c4f64039cba6e3dfb&chksm=e8867328dff1fa3e149248e53762444b1abc72df395a597639357b5525d16b1b6db242339d20&scene=27#wechat_redirect) + +31、[求你了,别再使用 pprint 打印字典了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486288&idx=1&sn=7647e1ee63c49c14472baec19fb79b87&chksm=e8866bb2dff1e2a4d5b85ae9db1e8f4d8dda839643fb02b9e0b7f7fe6143cb627773e56d44bf&scene=27#wechat_redirect) + +32、[Python实现RabbitMQ中6种消息模型](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486017&idx=1&sn=9b95896abee7a345e3abee1ac4f2edab&chksm=e8866aa3dff1e3b547ef5062c30516b2d54a051fd4a07d85078223ea673af1400be1a6c56ad5&scene=27#wechat_redirect) + +33、[想写好面向对象的代码,这几篇一定要看(下)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485938&idx=1&sn=a725785ef8000868b7cb85606ee754d3&chksm=e8866910dff1e006f195d6ea0d66f4b4f1fe5a9b97debc18ae4b04a239a347d342fbc67b44e4&scene=27#wechat_redirect) + +34、[想写好面向对象的代码,这几篇一定要看(中)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485926&idx=1&sn=ce148af333d9f377d8a6fb9577bbf57c&chksm=e8866904dff1e012ec1bb8f418c43cca7954979bd1481707761b29d1e632ace6369efd50210b&scene=27#wechat_redirect) + +35、[想写好面向对象的代码,这几篇一定要看(上)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485913&idx=1&sn=74b34bd95d513a94201ad05604c4a05f&chksm=e886693bdff1e02dd2079cb90458aac914e5cb832aec59677da8b2f85200baaa57e804a4b58e&scene=27#wechat_redirect) + +36、[教你 10 分钟构建一套 RESTful API 服务](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=2&sn=d593dbf907217ff4dea1daf636062778&chksm=e88674f4dff1fde26b1a1074a0a0c78be0630a34b4aed705755946bd8cfcde1a23e0df916392&scene=27#wechat_redirect) + +37、[要搞懂元类(metaclasses),这篇文章一定要看](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493280&idx=2&sn=d1a0aa45b9ecd03bd940328bc031458c&chksm=e8858642dff20f54ecbe8d03aebe2dacdc1fe0fdb1daa3855e09f565f05b711c9aa4eabd9ddb&scene=27#wechat_redirect) + +38、[弄懂这 6 个问题,拿下 Python 生成器!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493273&idx=2&sn=89bd3d18e7837f59c41d2f021047a1f9&chksm=e885867bdff20f6d0d72d463a255d2a51225321f8bc6115523411e540305e669a24580e58d78&scene=27#wechat_redirect) + +39、[`[]` 与 list() 哪个快?为什么快?快多少呢?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493457&idx=2&sn=67d576d2aeab16d4ad9c1b491e10deaf&chksm=e88587b3dff20ea501789da4c67560e21708f89032732eaca544e22c65780ea249e98e6ab50e#rd) + +40、[连 Python 生成器的原理都解释不了,还敢说 Python 用了 5 年?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493530&idx=1&sn=5096c680a63a57b1f55d1a9d372ec302&chksm=e8858778dff20e6e257fcacfb644bc4736afac692ab19f41717f4cdc084eb43951aba0912187#rd) + +41、[Python 列表去重的4种方式及性能对比](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493746&idx=2&sn=218b1a28af72347363557bdaac32d016&chksm=e8858890dff20186a8fefe81e11a1aa64e787a75b3930ecb6e4421e132a8322dbc5fe3b81b30#rd) + +42、[第一次把 Python 的切片理解得如此透彻](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493987&idx=2&sn=ca90bbc53a31c0ccc84fea4869fd1b81&chksm=e8858981dff20097a5167ef9d60fe4c54479b6f50b85188ffc43490c03b8cb2bec8464975793#rd) + +43、[为什么继承 Python 内置类型会出问题?!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494268&idx=2&sn=67ea6f78e7add1d43174f601626d1cb5&chksm=e8858a9edff20388c08f6e3f9c8ce9db6969174fc3674bf04d65cf0fbcda48c08217de5d622b&scene=27#wechat_redirect) + + +### 2.2 包的管理 + +1、[最全的 pip 使用指南,50% 你可能没用过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484827&idx=1&sn=df0923856c820e10baca20c9873b336b&scene=21#wechat_redirect) + +2、[全面学习 Python 包:包的构建与分发](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485349&idx=1&sn=8d6da555a2852a14506a491c4cf9a234&scene=21#wechat_redirect) + +3、[深入探讨 Python 的import机制:实现远程导入模块](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&scene=21#wechat_redirect) + +4、[盘点 Python 依赖库管理的工具:pip、pipreqs、pigar、pip-tools、pipdeptree](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485120&idx=1&sn=170ef1173eabc5bed28c724e19d6c0ac&scene=21#wechat_redirect) + +5、[如何使用 pyenv 运行Python的多个版本?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486239&idx=2&sn=863e80589f5eb9b2daa9701f6b494997&chksm=e8866bfddff1e2ebc244354238aedfa9291aa27ad8f7e466213236369dae9800b29b5766c0cf&scene=27#wechat_redirect) + +6、[如何管理 Python 的虚拟环境?超全讲解virtualenv的使用](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485049&idx=1&sn=c16383d6cc91a7ed8254e344d994f101&scene=21#wechat_redirect) + +7、[一款让Python开发效率提升50%的工具包](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=1&sn=eead45b3c34777085465f9854a433e36&chksm=e886750cdff1fc1ae2791adba1231e045630417f9b4185217817183aa1f260815acabcd39363&scene=27#wechat_redirect) + +8、[记 Python “用户环境”的一次完美应用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486191&idx=1&sn=e556df305ce1df4c4969d2fdbe884cfa&chksm=e8866a0ddff1e31b2ca8ca8fbbe0355fb8c24376d715224ef2176e613b5601764be0193e3aa4&scene=27#wechat_redirect) + +9、[学了半天,import 到底在干啥?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493413&idx=2&sn=770c9bb7104dfb8c53c544f546cf7aa4&chksm=e88587c7dff20ed143cb93b4cd1b0f64af8b9c849fd9d531e58a9ba27a449d4a28cd96091b40&scene=27#wechat_redirect) + +10、[非常干货:Python 探针实现原理](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493427&idx=1&sn=d9e0c646a98cf2b54b5724ebc3262255&chksm=e88587d1dff20ec71ed33c5165bbdec377eea89307da0f924784cff0f4c01c05a5f3247de50e&token=58703854&lang=zh_CN#rd) + +### 2.3 性能优化 + +1、[Python高效代码实践:性能、内存和可用性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485161&idx=1&sn=07625f06db330897bc8bfee880ceac18&scene=21#wechat_redirect) + +2、[Python 代码的性能优化之道](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485004&idx=1&sn=8dc07a40bcac30c93874398b17a52831&scene=21#wechat_redirect) + +3、[7 个习惯帮你提升Python运行性能](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484957&idx=1&sn=9b3c644e6b1777b77ce7f84047c6d500&scene=21#wechat_redirect) + +4、[如何提升你的 Python 代码健壮性(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484871&idx=1&sn=985b79143d0cafd21e5263ee79afa777&scene=21#wechat_redirect) + +5、[如何提升你的 Python 代码健壮性(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484867&idx=1&sn=b8eab8416229dc74f1ac96e099a3df71&scene=21#wechat_redirect) + +6、[将 Python 运行速度提升 30 倍的神技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484877&idx=1&sn=f6067875f1e807d19291e383f8421a3b&scene=21#wechat_redirect) + +7、[实战讲解:Python 性能分析与优化实践](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485404&idx=1&sn=486fcf898d6dd21b0feb990de3c1e08f&chksm=e886673edff1ee28b8c6e1d520b2732b2f66f4c4691ef2696b54743b1ad0769356e12a1e98b2#rd) + +8、[如何调试Python 程序的内存泄露问题](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488577&idx=2&sn=16aff9687a4a16faa492118cd5c1bfae&chksm=e88674a3dff1fdb5b80a4547cb863fcf4255f90ab2024ae8c3bbf1f270efb13333b2f7426baf&scene=27#wechat_redirect) + +9、[牛逼!一行代码让 pandas 的 apply 速度飙到极致!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493908&idx=2&sn=0413803e1a73bf632c2d5ba24c6e7ce4&chksm=e88589f6dff200e020a32578706e7c4791be700ce9f16f810d5b19dcc6a99bce2d5c7ba96b6d#rd) + +10、[3 倍性能提升!升级 Flask 到 Quart](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494567&idx=1&sn=d91202b10ab957a58c5eb70fa159fe67&chksm=e8858b45dff2025387626bac42e8441e8d0c8c290070f5fc1a885776fb6a3a06ce3467b1b8d2&scene=27#wechat_redirect) + +11、[超干分享!如何提高Python的运行速度?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=2&sn=00c8aa00a85f8710fada38d35b2c8388&chksm=e8858b3fdff2022991a0024757c64faaed81da830cbbf1fa62b6b218d7271b0837b11922c1f0&scene=27#wechat_redirect) + +### 2.4 并发编程 + +1、[并发编程01|从性能角度来初探并发编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485074&idx=1&sn=a859c6ab1d9b95c30c9f8b06f9489887&scene=21#wechat_redirect) + +2、[并发编程02|创建多线程的几种方法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485073&idx=1&sn=854ff524645247e5020d977e57d9c0e6&scene=21#wechat_redirect) + +3、[并发编程03|谈谈线程中的“锁机制”](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485069&idx=1&sn=52370a27d4a5c4541969921ada890e0b&scene=21#wechat_redirect) + +4、[并发编程04|消息通信机制/任务协调](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485068&idx=1&sn=fc0798e84e7845cd9efee1809f932f15&scene=21#wechat_redirect) + +5、[并发编程05|线程中的信息隔离](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485066&idx=1&sn=0bb9d6c82a6d062e50d09e1eefd09427&scene=21#wechat_redirect) + +6、[并发编程06|如何创建线程池](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485065&idx=1&sn=60891b67b139806cf6bf4c05f5861f02&scene=21#wechat_redirect) + +7、[并发编程07|从生成器使用入门协程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485064&idx=1&sn=8584bb778152a12ca335970bed9fdbdc&scene=21#wechat_redirect) + +8、[并发编程08|深入理解yield from语法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485063&idx=1&sn=0ff7a99058320ff90a6237e7e03367fb&scene=21#wechat_redirect) + +9、[并发编程09|初识异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485061&idx=1&sn=9e846df1a5cb57e0bc6254dcc953e243&scene=21#wechat_redirect) + +10、[并发编程12|学习异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485058&idx=1&sn=92ef1f79ce6488670ae13e5d6a1c7908&scene=21#wechat_redirect) + +11、[并发编程11|实战异步IO框架:asyncio](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485057&idx=1&sn=8bacf0f2b42de5962fbdf32796903f27&scene=21#wechat_redirect) + +12、[百万「并发」之Python异步编程(上篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484896&idx=2&sn=5d8458ede3440ae501d19c5ffd5f8a99&scene=21#wechat_redirect) + +13、[百万「并发」之Python异步编程(中篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484889&idx=2&sn=25d11042e5b2ab17dff40535d354b8f8&scene=21#wechat_redirect) + +14、[百万「并发」之Python异步编程(下篇)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484885&idx=2&sn=b9e62a9b024358aec629e876b76e0eb5&scene=21#wechat_redirect) + +15、[如何一行 Python 代码实现并行?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484826&idx=1&sn=6c5b575b7b134642077cfde2bb8b613f&scene=21#wechat_redirect) + +16、[asyncio:从原理、源码到实现](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485245&idx=1&sn=a08826c3d28037479190fd652438bcca&scene=21#wechat_redirect) + +17、[为什么说线程是CPU调度的基本单位?](https://mp.weixin.qq.com/s/c3aZ-6UzZVD3_PvVhiDPWA) + +18、[非常适合小白的 Asyncio 教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485842&idx=1&sn=ab85dab95cf47046ddcc35b60e633c40&chksm=e8866970dff1e066381c0ba53fe09e34b8f1ce52bbbfe568c1ee66b9cbad87f43b442895c770&token=2013245174&lang=zh_CN#rd) + +19、[说说 Python 里关于线程安全的那些事儿](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486279&idx=1&sn=2b6acf717b7f5cc6a2b72c62266dbe01&chksm=e8866ba5dff1e2b318c679413fd7d0ac3a69db7cab1ceda03eca26fb71f6d2837af80e2143d0&scene=27#wechat_redirect) + +20、[异步 Python 比同步 Python 快在哪里?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494549&idx=2&sn=1120b33a12bf8ab25df805cfab9afbc8&chksm=e8858b77dff20261afdf788de30c7e14f3d66ff52215b8c0db66320a118e4544283424340d4e&scene=27#wechat_redirect) + +21、[为什么 Python 多线程无法利用多核?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494567&idx=2&sn=653795446bacff2d445cabd417748756&chksm=e8858b45dff202530afc05a00469b20a0a95f53bcaefc49472240e1c905459ecfb74edbe0461&scene=27#wechat_redirect) + +### 2.5 实战练习 + +1、[适合 Python 新手练习的绝佳项目](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485898&idx=2&sn=36a1b1a94dece0a290652da23679cff0&chksm=e8866928dff1e03e58b95b3cd24dbc23e368b6eb2c0599b2efe814924e21ec5c01935b2e0455&token=2013245174&lang=zh_CN#rd) + +2、[4个Python实战项目,让你瞬间读懂Python!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485852&idx=2&sn=eb3471a260cce835a755a780d6cc690d&chksm=e886697edff1e068bd0f9d456e26ee5ebe482580457951e42807e731d266c95f00c37de22fea&token=2013245174&lang=zh_CN#rd) + +### 2.6 GUI 应用 + +1、[如何在Python中编写精美图形界面?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=1&sn=eed86ec2f44a92fadb94f237178b3f8a&chksm=e8866a01dff1e317f450d91b88e91d6912d6a1b2ac7a34ebb86b74a2abc0f912d8ced5a3baac&scene=27#wechat_redirect) + +2、[或许,这是最强大的一款Python GUI工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492571&idx=2&sn=6ac2c0c19d86d653acd82d5b4ef86126&chksm=e8858339dff20a2f595159499e8eb8e072b52e61521e4a1322337f174bff942134a25b3b6b9b&scene=27#wechat_redirect) + +### 2.7 自动化办公 + +1、[付费?是不可能的!20行Python代码实现一款永久免费PDF编辑工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492085&idx=1&sn=785968af86bf998ecad9b11583a23ad6&chksm=e8858117dff2080119f596c6447d9a0c7b73c6bced8306883900f2f4d9582929f9d5c26ac45d&scene=27#wechat_redirect) + +2、[带你用 Python 实现自动化群控(入门篇)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491979&idx=2&sn=7489738d3225a96767173e4b54023f95&chksm=e8858169dff2087fed0f11ad6b880c110e3530eae28ed7702343f6d81163860ed20a2f056294&scene=27#wechat_redirect) + +3、[最全总结:Python 发送邮件的几种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489568&idx=2&sn=bdac42b533784bf3a8ee523da36bacba&chksm=e88678c2dff1f1d499321032e6871f2810608909121bf56aee184c6d7770d3b310f39cad7c43&scene=27#wechat_redirect) + +4、[太全了!使用 Python 转换 PDF,看这一篇总结就够了。](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=2&sn=eed1cab8e03c908fdfe311bf4bbd91a6&chksm=e8867544dff1fc52e0d9dd62c61d36ec5a5b6847aeb9992e5423eb4e01f08995461160adbb45#rd) + +5、[Python吊打Excel?屁!那是你不会用!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=2&sn=9c4a697758af01c086cced44b98a3380&chksm=e8866e4ddff1e75bd2f19e036c3e338edee50871364d2f185afa51cb361c83f3bbda8ce2d92f&scene=27#wechat_redirect) + +### 2.8 网络爬虫 + +1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect) + +2、[教你实现一个可视化爬虫监控系统](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484208&idx=1&sn=d04421526cf0e12089541063b4ec448d&scene=21#wechat_redirect) + +3、[估计是讲得最清楚的「异步爬虫」指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484929&idx=1&sn=7055414a33d7cbadaeb5645cbd5203ec&scene=21#wechat_redirect) + +4、[10 个爬虫工程师必备的工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484958&idx=1&sn=3422e53de4ae701a922104ad26f8e256&scene=21#wechat_redirect) + +5、[Selenium自动登录淘宝,我无意间发现了登录漏洞!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=2&sn=6a33873b3777afd420f3c56078a88687&chksm=e8867c53dff1f5455fe54e1ee0044cc97638fbc96983f36e19938810bea4e06d5af0ba3036df&scene=27#wechat_redirect) + +6、[干!一篇文章讲了这么多爬虫的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490557&idx=2&sn=f5128b1dc827cc4a6035d46714a484b1&chksm=e8867b1fdff1f20993b024fbfaa4445763a5949174913a58d9f17ddcb0286be8b325516cb449&scene=27#wechat_redirect) + +7、[谈谈常见网站加密和混淆技术](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486218&idx=2&sn=6261fa602eb4f73365a9d372376594ed&chksm=e8866be8dff1e2fea5082dd13dc7799a1cbc829f2e41a32891db411152153cb261d01be5807b&scene=27#wechat_redirect) + +8、[初识网络爬虫之夜探老王家](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486179&idx=2&sn=3a1b880c7f1b37d095359b35ff96e9b4&chksm=e8866a01dff1e31711d7fb093a96f54eaf374b46c686b0ea4e49d6f926018ad5b6c27aebfa9d&scene=27#wechat_redirect) + +9、[不懂爬虫也能轻易爬取数据的 6 大工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486085&idx=1&sn=51c740e1d848f1624a5a8e582434f661&chksm=e8866a67dff1e371de89a64503591ae0c42b67cd43434fa394125f09a2f65bc23817a266ac13&scene=27#wechat_redirect) + +### 2.9 实用系列 + +1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect) + +2、[30分钟教你快速搭建一个顔值超高的博客](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485045&idx=1&sn=8b250c0c174e418e2025d86f42c695b6&scene=21#wechat_redirect) + +3、[用Python写一个表白神器让你七夕脱离单身](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485021&idx=1&sn=123b39391d11e9c7160b47a4c6a3dcb1&scene=21#wechat_redirect) + +4、[用 Sphinx 搭建博客时,如何导流到公众号?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484848&idx=1&sn=80ae18e7f53a64e62ac9c0ef0c21362e&scene=21#wechat_redirect) + +5、[10 行 Python 代码写 1 个 USB 病毒](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484832&idx=1&sn=43c353856c7dd9ea2cb1a9bbcd077fa2&scene=21#wechat_redirect) + +6、[废旧 Android 手机如何改造成 Linux 服务器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485279&idx=1&sn=7166a2c9445438eaaf63a25b598e5427&scene=21#wechat_redirect) + +7、[如何将手机打造成 Python 开发利器?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485256&idx=1&sn=2e646e28287707c67c22fbe68112e622&scene=21#wechat_redirect) + +8、[我用 Python 做了一回黑客,批量破解了朋友的网站密码](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485156&idx=1&sn=05142ae7b4bf7e97ebe042e394aa7084&scene=21#wechat_redirect) + +9、[手把手教你安装Win+Ubuntu双系统](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485030&idx=1&sn=8383a7306381f36781957b807fa93961&scene=21#wechat_redirect) + +10、[一篇文章让你的 MacBook 进入超神状态](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484906&idx=1&sn=b2c3c969e53beae53aa7be7959227b5b&scene=21#wechat_redirect) + +11、[情人节来了,教你个用 Python 表白的技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485701&idx=1&sn=ef7f0b83e60f397c1839259b275575bf&chksm=e88669e7dff1e0f1317ec429cf9e70442bb3a08ec46efe9a0d06f4ec9847574ea30b53904670&token=1148998814&lang=zh_CN#rd) + +12、[“Hack” 微信实战:如何用 Python 分析微信群聊记录?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486141&idx=1&sn=8b32c57dce0f3a33127c39c9cd35509e&chksm=e8866a5fdff1e349f16f677304716e40a305e60168fe7e30361de675dad5dfa956c9d7c79089&scene=27#wechat_redirect) + +13、[5 行 Python 代码生成自定义二维码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488795&idx=2&sn=8e03e01e861753f7ccf3adeb1f3cb848&chksm=e88675f9dff1fcef328c69b1519743fcc3ae83b80dd3d66cb934acb346781497f83c5a9051a5&scene=27#wechat_redirect) + +14、[女朋友背着我,用 Python 偷偷隐藏了她的行踪](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487175&idx=1&sn=c3ba19bdbcb6fd59b37ff04d02bce63b&chksm=e8866e25dff1e733b31386fda99271eefadc77b3e0579df4d74617b561ecf0fa0db70f9f2860&scene=27#wechat_redirect) + +15、[zip 解压炸弹? ?在 Python 面前,啥也不是..](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=2&sn=307a29d71776f6d1c68567a16727a7e5&chksm=e88581e3dff208f55a48081cb8f951ca7d977c9b1ec3ac9aaf26bc8c53904ac0e15035e5c680&scene=27#wechat_redirect) + +16、[把你的朋友变成表情包?Python:So easy](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493765&idx=2&sn=cd3cd2a891cb3bc47956ac3bc8a06dd1&chksm=e8858867dff20171241f81274eec097f48ea23558029b42a3e7565833c8a62d20e52d2dea87e#rd) + +17、[超干!如何建立一个完美的 Python 项目?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493789&idx=1&sn=7f0f4c40e410a3cfb01706dd9c8c8fc6&chksm=e885887fdff2016930accb8ea6d4e2cb67d4d6983ca716148da7f63c62a6b96f518497ed2575&token=58703854&lang=zh_CN#rd) + +18、[10 个“疯狂”的 Python 项目创意](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493963&idx=2&sn=e4ff38bc55f4ad4d990191e81a316421&chksm=e88589a9dff200bff3e65c19744e59dd3407a48ff5b104bc7b71771762b331daca5c7772892a#rd) + + +## 03. 数据分析 + +### 3.1 基础库 + +1、[有关 NumPy 和数据表达的可视化介绍](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485295&idx=1&sn=6220c2ed0d73feade0c9691fbf214ba7&scene=21#wechat_redirect) + +2、[快速提升效率,这6个 Pandas 技巧一定要知道](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491909&idx=2&sn=3f3bf16784689a5e8bd5f9ef9478040f&chksm=e88581a7dff208b12e25ddaf6a047abc47878eee56f85309b6618cfd92a4dfeb2445f23e2f05#rd) + +3、[50题 讲透 matplotlib :从入门到精通](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486026&idx=1&sn=7b333759709c30fe442732b7a107e3c1&chksm=e8866aa8dff1e3be37f5169ac53bbcf71a76514e99fbbcbe458ec15aa1204d988ad44d689eb3&scene=27#wechat_redirect) + +### 3.2 数据可视化 + +1、[可视化01|一图带你入门matplotlib](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485022&idx=1&sn=76d4270a15c430217588bd9f8be8303b&chksm=e88666bcdff1efaa35c6d685f19b04e62f58e768dc9d1819bcb9f8211f65d3fd83296259d923&token=1148998814&lang=zh_CN#rd) + +2、[可视化02|详解六种可视化图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485020&idx=1&sn=7f82736e6a2e5442ed59c82d8e242ca4&scene=21#wechat_redirect) + +3、[可视化03|用正余弦学习matplotlib](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485019&idx=1&sn=9d44ee27fd888831e94845d6d0256deb&scene=21#wechat_redirect) + +4、[可视化04|子图与子区难点剖析](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485014&idx=1&sn=90dbdf17f049c152944a4c28642df249&scene=21#wechat_redirect) + +5、[可视化05|绘制酷炫的GIF动态图](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485007&idx=1&sn=1464dee30d006ddb5111f9827b0c4081&scene=21#wechat_redirect) + +6、[可视化06|自动生成图像视频](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485002&idx=1&sn=f09c089328eb0fe39721b73272c81214&scene=21#wechat_redirect) + +7、[可视化07|50个最有价值的图表](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484920&idx=1&sn=92eb2fd55f13a8bda75a7103a73f8d50&scene=21#wechat_redirect) + +8、[ 可视货08|利用 Flask 动态展示 Pyecharts 图表数据的几种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485658&idx=1&sn=f78e976889de52c0ce35e862bafacd8a&chksm=e8866838dff1e12e41b8f2dd1a43dc540ff39484ae64831332d233b5a4f7ca3b439d370ef1ed#rd) + +9、[一个没法商用,但是好玩有趣的 Python 手绘图形库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491212&idx=2&sn=66a5fd4f672b668aee6bb2c3a01a92f6&chksm=e8867e6edff1f77813b2b59f6219d18041963e6011afa1a590925c71ebd79c7e90b03e82c606&scene=27#wechat_redirect) + +10、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect) + +11、[一文学会制作 6 种炫酷的 Python 动态图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486111&idx=2&sn=fadf51d43bc468c58ba6885db3006e7a&chksm=e8866a7ddff1e36b5815a9693150799d5925727f5a425e642fd136283ff4a58b7f19ba9da39d&scene=27#wechat_redirect) + +12、[如何使用 Python 绘制一套动态图形?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488616&idx=1&sn=ef094168fc5ff4c46a66b2068cb1011e&chksm=e886748adff1fd9cc9f9f3ed3b2210c9f9f2c770458b3207f9fd6b12ee37a5451e9444b41e59&scene=27#wechat_redirect) + +13、[超硬核的 Python 数据可视化教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=2&sn=2aede70646f9ee34ad526d2d99b6549e&chksm=e885824bdff20b5d428dbb09daeae2f3e96f1362372bf6ffb16fae1f06263703379873025d43&scene=27#wechat_redirect) + +14、[收藏!最全的可视化学入门算法教程(Python实现)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491520&idx=1&sn=55804b80dcc56a3923255abf58e08968&chksm=e8867f22dff1f6340a7f61f7796a1e78bd695278a340351860efc244c8a9bbf80cc02bbfaf05&scene=27#wechat_redirect) + +15、[50 款数据可视化分析工具大集合,总有一款适合你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490737&idx=1&sn=f447d6e27504611baf8a4ad4056c6e5a&chksm=e8867c53dff1f5450ff808baec53797bd772cdd3c0e43f2f0c278cd4b8867223d5591ab2aa7d&scene=27#wechat_redirect) + +16、[用Python画漂亮的专业插图 ?So easy!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=1&sn=14df276f09201930fdc413d8f9a772df&chksm=e8867a29dff1f33fd0f85cf4412c42cc1145e917b3cd06e158aa31356525799033dd8c6e620f&scene=27#wechat_redirect) + +17、[太好玩了,看看我用 Pyecharts绘制的“时间轮播图”](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=2&sn=6359a534033c0a6f21af28a7d9f8b768&chksm=e886789bdff1f18d6719558e305453cba2b6c8a55d66c9bcb6dd5678209d89c1594d87de0a87&scene=27#wechat_redirect) + +18、[实战!用60行Python代码画出30万条房产数据分析图](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=1&sn=6b9c7281d3090783a9a486b81f749789&chksm=e88676eadff1fffcec00d3c44ee9b34496dc886417da55aeabed017d496da85d442f205c227d&scene=27#wechat_redirect) + +19、[刷爆全网的动态条形图,原来5行Python代码就能实现!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492725&idx=1&sn=964c45002220761dafcf537d5f9fe802&chksm=e8858497dff20d8175b87ddb1997b1dd71c91a03a92e60611718fdbf2bdbe3cee41d239b360d&scene=27#wechat_redirect) + +20、[这 10 个 Python 可视化动图,学会了就吹牛了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493551&idx=2&sn=dee235d803b491163f74a49f5cf0d12f&chksm=e885874ddff20e5bf639454c226f6e74e5180dbab3803db08c2bd72d67347aa52dede40da4c9#rd) + +21、[牛批了,1行python代码就可实现炫酷可视化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494580&idx=2&sn=bf87885645377449f3a94d6d8122e1f6&chksm=e8858b56dff2024046c48ccf0ff087c42fa2608c0d0b6183ea61ffb85278a9c52138cdfbef13&scene=27#wechat_redirect) + +### 3.3 工具使用 + +1、[ 整理了 50个 IPython 的实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485613&idx=1&sn=70f89faf2573b3025edc73f525f22a0a&chksm=e886684fdff1e159509d30fd0a24854da39fb5a6ae55e0ed6e40b7971d3219968006f80aad7b#rd) + +2、[ Jupyter NoteBook 的使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485024&idx=1&sn=aac0db4b2c97c7f5c2871342b936eaa2&chksm=e8866682dff1ef947ec6474b8c03f668e462232ebd0ec0f137d42e5e63b9b66a3d9e303c1caf&token=1148998814&lang=zh_CN#rd) + +3、[Jupyter Notebook最常用的五大配置技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491411&idx=2&sn=88c290af7793ddc024cf2a39875b97e8&chksm=e8867fb1dff1f6a731077d749b1ea44fd558995f3448e27da9becf7b035ed777774d1b0bb0ec&scene=27#wechat_redirect) + +4、[强烈安利!这十二个 IPython 魔法命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488777&idx=2&sn=3b14fe7faa4d2a26b702c15ebe6b6103&chksm=e88675ebdff1fcfd471dc84c802dab2c37889d59e55711c5d521c80fdbf38291b4092b840083&scene=27#wechat_redirect) + +5、[真香!安利 6 个 Python 数据分析神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488619&idx=1&sn=0c6c76b9deaa4f1976dcacaaee018312&chksm=e8867489dff1fd9fa2b0ac08696c4dae1b48054ca64aefef6d2117179dbbffbfe4ae9d1a4f81&scene=27#wechat_redirect) + +6、[使用 pyecharts 打造酷炫的 BI 大屏](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=2&sn=b2ee3058d63ebf702f3f0f9a6ca9c623&chksm=e8867010dff1f90686948269ca5e0907d726fc775029cc414285d71c0176ce859b818d2e34d5&scene=27#wechat_redirect) + +7、[酷炫的动态可视化交互大屏,用Excel就能做!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=2&sn=2826a69402912a70955209192e761c4d&chksm=e8866bb5dff1e2a30d374d5f46afa1cb9d952a0c939701b9f8b5bc52df4169d5e2da30236505&scene=27#wechat_redirect) + +8、[像操作Excel一样玩Pandas,这款可视化神器太棒了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493689&idx=1&sn=2e614500c8359e4b87de55128fe69593&chksm=e88588dbdff201cd761e20605f10f1aa9525d6d89072cbbc9a307e35788b8a425c2044d337d4#rd) + +9、[8 个 Jupyter Notebook Tips,隐藏得太深了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494062&idx=2&sn=860539d7eae82c53791cd7a2eb005536&chksm=e885894cdff2005ae9169c7dfabe3760c19eded392ec18805f1d763ad8dce77c21150698178a&scene=27#wechat_redirect) + + +## 04. 开发工具 + +1、[代码调试|无图形调试工具 - pdb](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484969&idx=1&sn=a99fc31865edc4b439707d2be6f66654&scene=21#wechat_redirect) + +2、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484970&idx=1&sn=9ac9c5dfcdc8c6b48a7b4c9854825734&scene=21#wechat_redirect) + +3、[优化Python开发环境的几个技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485131&idx=1&sn=f1bd7b31a3624f015c74f56a8888a695&scene=21#wechat_redirect) + +4、[让你重新爱上 Windows 的小众软件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484964&idx=1&sn=1479536416788a6c55dddef70167eb88&scene=21#wechat_redirect) + +5、[Python 的命令行参数解析工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484960&idx=1&sn=3456d9a05b35588b060833ceb189db6e&scene=21#wechat_redirect) + +6、[搜索神器 EveryThing,你把它的潜力用到极致了吗?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484894&idx=1&sn=59877fe818739175d3d075458594f563&scene=21#wechat_redirect) + +7、[开发工具|盘点 Xshell 的那些奇淫技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485043&idx=1&sn=8c240e569c60607cd4cea8888a6aa2e1&scene=21#wechat_redirect) + +8、[告别996,全靠这个Python补全利器](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484824&idx=2&sn=8f20af371954dfd2a5c890bb5814b4c9&scene=21#wechat_redirect) + +9、[学会这21条,你离 Vim 大神就不远了!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484902&idx=1&sn=d677261fda90b67bf5eda886447a4f77&scene=21#wechat_redirect) + +10、[用 Python 做开发,做到这些才能一直爽](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect) + +11、[这款神器,能把 Python 代码执行过程看地一清二楚](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484878&idx=1&sn=9bc941ff19ec38e7c7137a22df981f27&scene=21#wechat_redirect) + +12、[如何把自己的 Python 包发布到 PYPI?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484835&idx=2&sn=f24a415d47d4122c4d6b1a6ccf254a51&scene=21#wechat_redirect) + +13、[谁说 Vim 不好用?送你一个五彩斑斓的编辑器!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484872&idx=2&sn=ce718e511b754fe936172724a824a716&scene=21#wechat_redirect) + +14、[迄今为止,我见过最好的正则入门教程(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484831&idx=1&sn=077d14410db6a906c3b962f0ba18b2d4&scene=21#wechat_redirect) + +15、[迄今为止,我见过最好的正则入门教程(下)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484830&idx=1&sn=4d48b1c7f2b5234992732f8f15b868df&scene=21#wechat_redirect) + +16、[Win 平台做 Python 开发的最佳组合](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484837&idx=2&sn=68935077ae9eeb6fe4f708565aa0a9ed&scene=21#wechat_redirect) + +17、[15 款Python编辑器的优缺点,别再问我“选什么编辑器”啦!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492759&idx=1&sn=6c6e6606fd9da9d2b951bda9e8a1b223&chksm=e8858475dff20d638f4b731a44dd48fe01a68f16480bcaed2516fc7d3d56c61cc6c62afccb9d&scene=27#wechat_redirect) + + + +### 4.1 PyCharm + +1、[受用一生的高效PyCharm使用技巧(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484946&idx=1&sn=12a891db18e9d1233535fa4aca9a3892&scene=21#wechat_redirect) + +2、[受用一生的高效PyCharm使用技巧(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484942&idx=1&sn=9dbb2cfe2a277bd3519d37414fcc608e&scene=21#wechat_redirect) + +3、[受用一生的高效PyCharm使用技巧(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484932&idx=1&sn=347b35ccdee94895652caa86191360c8&scene=21#wechat_redirect) + +4、[受用一生的高效PyCharm使用技巧(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484918&idx=1&sn=66b3fd784050e5cb85109faa3d063993&scene=21#wechat_redirect) + +5、[受用一生的高的PyCharm使用技巧(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484914&idx=1&sn=cd1282f94f9326647b05ad9090afd2c8&scene=21#wechat_redirect) + +6、[受用一生的高效PyCharm使用技巧(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484907&idx=1&sn=647a75b96884b39c57664f62ead11b89&scene=21#wechat_redirect) + +7、[受用一生的高效 PyCharm 使用技巧(七)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485372&idx=1&sn=3ed61c4ccbb089358213304cd70bb0f7&chksm=e886675edff1ee487911e922279675264916dd08120f5f40113f37dc080b94c7bf9ad67719c4&token=1148998814&lang=zh_CN#rd) + +8、[代码调试|远程调试图文超详细教程](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247484304&idx=1&sn=7612f0c2fd6232723ca2733fb1e8f46d&scene=21#wechat_redirect) + +9、[手把手教你打造一个顔值超高的IDE](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485052&idx=1&sn=2e0bb1a3af4206fd2cf8e3650f695e6b&scene=21#wechat_redirect) + +10、[玩转 PyCharm ,这篇文章就够了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487629&idx=1&sn=9a19cbcce2effcc242cc8d6d33cd2762&chksm=e886706fdff1f97955e73495c0be5b104f834d569d75e9e1470417f79e1d19ff4937683484ad&scene=27#wechat_redirect) + +11、[用动画展示 Pycharm 十大实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486118&idx=1&sn=990c35adb86919bda2f2cb82e7c5fb9e&chksm=e8866a44dff1e35200244e3d369ad6c0f736d3511dcfb086b7641339e20ae62e35b8fd60bdd9&scene=27#wechat_redirect) + +### 4.2 VSCode + +1、[这 21 个VSCode 快捷键,能让你的代码飞起来](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484893&idx=1&sn=421b544115efad388314ccd027761b40&scene=21#wechat_redirect) + +2、[神技巧!在浏览器中也能用 VS Code](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484859&idx=2&sn=70abd19426374271a10cbef8e1645c7a&scene=21#wechat_redirect) + +3、[生产力终极指南:用了两年,如今才算真正会用VS Code](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=2&sn=a39a7cb70908d442a399ac80a272f90e&chksm=e8858311dff20a0782cca942c7e9f3444bb443cf816fd05d76c5f7c234930afacb6c948802bf&scene=27#wechat_redirect) + +4、[VS Code的7个开源替代品,全都知道算我输!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490251&idx=2&sn=ef0eca9da6eff8405a5a48d89a6b2e54&chksm=e8867a29dff1f33f29487864efb1389b02705d81d0198df581b5b71085ff238f24ffe99f154c&scene=27#wechat_redirect) + +5、[再见了,PyCharm](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489522&idx=1&sn=d7d45dba4a7c9e2f010d09f757639a19&chksm=e8867710dff1fe0657a16b6087ad4a50fdceb8f324bef5f950b3320e33e09ce10d1fa045827d&scene=27#wechat_redirect) + +6、[微软推出 Pylance,改善 VS Code 中的 Python 体验](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489006&idx=2&sn=58f6dbe10cba98cebab0fd19f124e4df&chksm=e886750cdff1fc1a6cfe249a2f76213fef1a789f4a5006704e33d00b9abadde489af7052a8a9&scene=27#wechat_redirect) + +7、[VS Code 连接远程服务器运行 Jupyter Notebook](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486773&idx=2&sn=75b2453513b1445d9881f5ec8c1f44d1&chksm=e8866dd7dff1e4c167b27eccec9a6469b7988d6b05010160247b3e1a549764201807212ed581&scene=27#wechat_redirect) + +8、[GitHub 发布重磅更新:你电脑上的 IDE 可以删了?!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486295&idx=1&sn=e5bd46de0616fad6105d2e46a359cd3d&chksm=e8866bb5dff1e2a37058e1cbc502863f8b8a885a493b7ef9f5e442a1b554ffeb0ae09d77026e&scene=27#wechat_redirect) + +9、[用 VS Code 写 Python,这8个扩展装上后无敌了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492505&idx=1&sn=ba354ab86500280d46038d2fa2f3572b&chksm=e885837bdff20a6dd2d4411719e44a3b88371b5006e9dc2b2580e6862f3161e70cb339240512&scene=27#wechat_redirect) + +### 4.3 好用的库 + +1、[使用 Python 远程登陆服务器的最佳实践](https://mp.weixin.qq.com/s/aRXYAP9D9rgil-0_Etb0SQ) + +2、[这么设置 Python 的环境变量,我还是第一次见](https://mp.weixin.qq.com/s/LcjIPZPSg0KbL9X-i6vigg) + +3、[太强了!Python中完美的日志解决方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485741&idx=1&sn=505c06669baa44873171a3eed81f32b4&chksm=e88669cfdff1e0d91635b75f54998667e04bfb05a1aebae096930bd23acdd9d35ea858861bb2#rd) + +4、[如何使用 Python 操作 Git 代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485723&idx=1&sn=35511b71347111c00a9e1633309efd13&chksm=e88669f9dff1e0ef7aec03f2bdbddb8408bf12a57a36add441b75e486dd72df13be7629625ab#rd) + +5、[用它5分钟以后,我放弃用了四年的 Flask](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485657&idx=1&sn=e21ddc5b670a9b667135df2ca97ec99c&chksm=e886683bdff1e12d9e1d64a7f5bfbc65ca2372711cd9b08ef36ce0cdd99811ce44ff162585df#rd) + +6、[整理了 34 个被吹爆了的Python开源框架](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485653&idx=1&sn=edd893dd711b3681aa0bacdf8ab384fd&chksm=e8866837dff1e1216f6fbf413118f839e6e1dc5ed7b45117df55f13e25670c5ffba34966abff#rd) + +7、[ 为了选出最合适的 http客户端,我做了个测评](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485651&idx=1&sn=647c22ac66e469ccc33478457dadf224&chksm=e8866831dff1e1279149ff1e3af84bef86d24c524b7b7c14e9c519cf7c4739fa8f10dcc4a8d0#rd) + +8、[凭什么 FastAPI 火成这样?看这篇文章就知道了](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485891&idx=1&sn=82bde3e83435ebb6beb7e3581f12f7b0&chksm=e8866921dff1e0376befb029e9f1d81f33b0fa55242182f8de20d2a6e6b0cce0b5efd02f9aef&token=2013245174&lang=zh_CN#rd) + +9、[要想 BUG 变得富有美感,这个库你一定要看](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485889&idx=1&sn=0258eb138dce2c71b533d340770d5d09&chksm=e8866923dff1e03548dee4f780b410c22b51e1c3ffde3ef814f6a9b94e49af941fc04c79a660&token=2013245174&lang=zh_CN#rd) + +10、[如何使用 Python 输出漂亮的表格?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485841&idx=2&sn=705c123abed5441a52f8640b65f3c400&chksm=e8866973dff1e0650e4b2e025d0e86b3aedeb7ea8f68ad560f37cb033f36660b6eee15fb3ce4&token=2013245174&lang=zh_CN#rd) + +11、[用 Python 写出来的进度条,竟如此美妙~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492108&idx=2&sn=2fa1e31f9684df6d6f3da21aa4cba6e5&chksm=e88582eedff20bf886c29d480245d835c0a3fbea49e54909abbeb703bb7149d4e3444202e97c&scene=27#wechat_redirect) + +12、[推荐一些能提高生产力的 Python 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491947&idx=2&sn=765ca757e0aee7fd4dd3f7779f409ff3&chksm=e8858189dff2089f74a97972c1f213291a8680a766b328a74c609f465f7dcf16c324df57a660&scene=27#wechat_redirect) + +13、[规整字符串的数据提取神器:parse 库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491617&idx=1&sn=b77cd25d6c92f86c1492f913edcb51c7&chksm=e88580c3dff209d500525bbc81464034ca431c92603b5ec83046faaac7ca4c012034964805e0&scene=27#wechat_redirect) + +14、[微软再出神器,这次终于对 Python下手了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491407&idx=1&sn=0044bf683ddc4799ef2e7c04fb75ebf8&chksm=e8867faddff1f6bbbae3e716e7b686eeb4a5b49f19a2fb2b8743ae2e641ee529189d313f6fe8&scene=27#wechat_redirect) + +15、[这个 Python 库有点黑科技](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491241&idx=1&sn=700979655adce734f5300e7ee380d4ab&chksm=e8867e4bdff1f75d355b34ca8561844c920a2434357350e06a87c99cdbdfb094ee29e7957988&scene=27#wechat_redirect) + +16、[适合 Python 入门的 8 款强大工具!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491088&idx=2&sn=23cd3b06b8117a5d996d258dd9c0eb23&chksm=e8867ef2dff1f7e479ad5a633295f1be0fc272f6b4de2526c21129b2dee23cb517e51b508f8f&scene=27#wechat_redirect) + +17、[这些Python库虽然冷门,但功能真的很强大!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490868&idx=2&sn=7bda9c9021cd26eb43d752f6ff84dbe7&chksm=e8867dd6dff1f4c0398b427f13787c99f24cea2a7b2c7b10fbc3316b0215b3ea861f197e604f&scene=27#wechat_redirect) + +18、[那些被低估了的 Python 库,看看你用过几个?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490782&idx=1&sn=2c710eca844da200dac14ab017ffcdb3&chksm=e8867c3cdff1f52a86c291e34e458f57bd5572c6fdeb75cdb84fdfba2d9bb1731067dca7d195&scene=27#wechat_redirect) + +19、[用 Python 写出这样的进度条,刷新了我对进度条的认知](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490326&idx=1&sn=867061f4ad793e957161c30be77ef9bb&chksm=e8867bf4dff1f2e2f1cdc5af4366540d33fc0edfc52810af09b82a590fcd6ec4c5c2d7a528c6&scene=27#wechat_redirect) + +20、[少有人知的 Python "重试机制",请了解一下 tenacity ](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489274&idx=1&sn=7ddc4505f932b386ee22c7873ff5df1a&chksm=e8867618dff1ff0ea5aa55c6acfeb85944028f1bb0be7fb2697f4ec018de297579b797ce048b&scene=27#wechat_redirect) + +21、[一个极具意义的 Python 前端开发工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489090&idx=1&sn=1e7ee21422440dc32d7a9a40eaab6c4e&chksm=e88676a0dff1ffb6731df2996d3a4011eebdd8af4926920eefb30d086fa909943179e83d8957&scene=27#wechat_redirect) + +22、[非常实用的 Python 库,推一次火一次](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493413&idx=1&sn=2405d851af05549d5cb5f79353fdc393&chksm=e88587c7dff20ed1c2f93c9a042b293eae294f17bf13cbbebdf50682c8fd24e09e4647dbbe6b&scene=27#wechat_redirect) + +23、[新一代 Notebook 的神器出现,替代 JuPyter 指日可待](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493296&idx=2&sn=075bc9476933eec961bc2e9a7245d2d0&chksm=e8858652dff20f44a4d9cb30ad03841bc991a0efc2a63e63b04d5c8f68b3c6a67fbfa884551e&scene=27#wechat_redirect) + +24、[使用 Python 下载的 11 种姿势,一种比一种高级](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493230&idx=2&sn=8d356b9e0a9d597fbca1ef6ea2f8ca4d&chksm=e885868cdff20f9a2d220f18ecc27909c1217568728f213f492bf566578da5742190bcff4862&scene=27#wechat_redirect) + +25、[一个非常好用的 Python 魔法库](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493501&idx=1&sn=fc2575646311350cb47f6caed6102539&chksm=e885879fdff20e89c9600b6147885523fa0dbb7182fdf458c5aad5cd76ba6b415d25694f6bed#rd) + +26、[终于来了!!Pyston v2.0 发布,解决 Python 慢速的救星](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493602&idx=1&sn=3ba266f9460d94eb4d16c50bffe3ca94&chksm=e8858700dff20e1621fa34dae0b983c38fbcb5575d03f4c951c34a6d0a5f3b81e6c61fddd68a&token=58703854&lang=zh_CN#rd) + +27、[献给 Python 开发人员的 25 个最佳 GitHub 代码库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493878&idx=1&sn=f476bbed89543828b54e7c9fcd93abbb&chksm=e8858814dff20102b366afa61e870c2d82d3f8fd332dbf39f3824867a3f8b5e7a5ad4b0bfb89#rd) + +28、[求你了,别再用 print 调试代码了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494015&idx=1&sn=dc0dd7179d00d074e3013658760fe41d&chksm=e885899ddff2008be8fde435d8ed2fb80057386aa13f8eee273fd02e8c1ca99201130881380e#rd) + +29、[爱了爱了!0.052秒打开100GB数据,这个Python开源库火爆了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494268&idx=1&sn=db20e8232caa59512c852dcd723e3a46&chksm=e8858a9edff2038857440f061ec0cef305011d967dfd738df8187f10a2f44d90e3fd7c07e946&scene=27#wechat_redirect) + +30、[用 Hypothesis 快速测试你的 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494299&idx=2&sn=d8af7430ff62c90179491a097a5b43fd&chksm=e8858a79dff2036fae8f7ec05e6827c52cb27149d00a32f9a7dea9ab0e8ed1736fe4b0b3495c&scene=27#wechat_redirect) + +31、[微软最强 Python 自动化工具开源了!不用写一行代码!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=1&sn=61d9f8b7557cb2ff517fde1a414b219d&chksm=e8858b3fdff2022954d383579b6392362e99e99af4a6309028dfa6ddd7bfc90a02c85d599b50&scene=27#wechat_redirect) + +## 05. 网络基础 + +1、[点亮你的 HTTPS?原来这么简单!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=1&sn=46b095516bf3c0ebfc367ab64f8fd42c&chksm=e885824bdff20b5db2861a411a07653135ffbafaa1e772588bcf86cf9ad54433a0ff492355ad&scene=27#wechat_redirect) + +2、[手绘 10 张图,把 CSRF 跨域攻击、JWT 跨域认证说得明明白白的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489610&idx=1&sn=c9442c4ad370c318ea7ebafb26208b9c&chksm=e88678a8dff1f1bec43cd601b0c0e3eaff32d168dc2ad69f6273e27f2e6d72b49b7585046057&scene=27#wechat_redirect) + +3、[网络出了问题,如何排查? 这篇文章告诉你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488835&idx=2&sn=edac7045eb00479481365334ba198f50&chksm=e88675a1dff1fcb76f10049aa97ade7c0669fc1a8c32649a810c1d43b37c7dbe964c17b0730f&scene=27#wechat_redirect) + +4、[肝了三天,万字长文教你玩转 tcpdump,从此抓包不用愁](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488684&idx=1&sn=9a9d717a6c00f978c6575cb934c5c942&chksm=e886744edff1fd58ebb593338484013442c8c2f481497d6c5246cabce3ad43c1ee28299f9098&scene=27#wechat_redirect) + +5、[三张图彻底搞懂 iptables 和 netfilter](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488534&idx=1&sn=44856bba34b906930780535a7000a5af&chksm=e88674f4dff1fde263c119bb66a5fa879e6c07b12d50035552dbd529757bedc45a3a41823013&scene=27#wechat_redirect) + +6、[tcpdump / wireshark 抓包及分析](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488505&idx=2&sn=5e7d7a204c9eef4537fd352090086685&chksm=e886731bdff1fa0d3ba2c9a951fdbd849858a58ea630adab247f3b8b3ff7e76d7de022a8270d&scene=27#wechat_redirect) + +7、[100 个网络基础知识,看完成半个网络高手](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488490&idx=1&sn=a8725e200b3ac351ab4f0a2b6d112199&chksm=e8867308dff1fa1e1d55c89b691394902d5c7f8965063acd8551ad14269135d603f7bef22281&scene=27#wechat_redirect) + +8、[网络知识扫盲:扒开 TCP 的外衣,我看清了 TCP 的本质](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488180&idx=1&sn=09526224732ebfcccb52847f27298c70&chksm=e8867256dff1fb40c9f47bafd0e87a9237c5a9ebf33c8a3d0a598276b496d29cdaa3fbff8d26&scene=27#wechat_redirect) + +9、[网络知识扫盲:一篇文章搞懂 DNS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487859&idx=1&sn=55c2c40b807a6bd7036763e99954f063&chksm=e8867191dff1f88743bf88b809471926245a0ec3ef9c9d2c9f311d99533ed5656b7b3617431c&scene=27#wechat_redirect) + +10、[路由器里的广告秘密](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=2&sn=4cf42aeef93396f7665a06b2d1dc7fba&chksm=e886768edff1ff98bbd9572d85ddd97ef91ad8d19fdd951c15520cd9756d2c40a53d7cd8111c&scene=27#wechat_redirect) + +11、[10张动图,让你搞懂计算中很重要的名词](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487331&idx=2&sn=70146e780ca63c55ca3446a5e238b487&chksm=e8866f81dff1e6974610866f756b099e0b8bed5389cfa594f4b94b6092ae31db305b1485aadd&scene=27#wechat_redirect) + +12、[数字证书、签名到底是什么?这篇文章讲得太好了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493263&idx=1&sn=8b26b39da435b619d1344b4aca79b9f7&chksm=e885866ddff20f7b936601facff760969e1ba36d981a9d26920f0acce65c96562ebffcb7d954&scene=27#wechat_redirect) + +13、[为何无法使用 ip 访问网站?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484976&idx=1&sn=503d75faea7a633729ec90d6f26e21a2&scene=21#wechat_redirect) + +## 06. 实用工具 + +### 6.1 Linux + +1、[值得收藏的 14 个 Linux 下 CPU 监控工具](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485356&idx=1&sn=9dc2f440299ad179e1874febdffa04d6&scene=21#wechat_redirect) + +2、[19 个没什么用,但是”特好玩“的命令](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485142&idx=2&sn=97b0f02f8f0153b1c7ba4899359d55e4&scene=21#wechat_redirect) + +3、[相见恨晚的15个 Linux 神器,你可能一个都没见过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484887&idx=1&sn=3473df33893a23b4de1e176985a5265e&scene=21#wechat_redirect) + +4、[这 22 款 CLI工具,每一个都是精品](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491148&idx=1&sn=92b194ad574e8b793ebb608319c7d1df&chksm=e8867eaedff1f7b8e257ce6e80890cef6aa72b9078d20a0e1f0241a1c05f660c69891b6a7d12&scene=27#wechat_redirect) + +5、[Github 热榜项目:如何让你的终端酷炫到没朋友](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488641&idx=2&sn=c8ea38a8e216f123ca320d3ec8449bc0&chksm=e8867463dff1fd7522e9556f3ca97bb691ea92625ca664919dddabd35ead37dbd06a16b75b17&scene=27#wechat_redirect) + +6、[如何在1s内创建上百G的超大文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487906&idx=1&sn=a892ae3f3ab0ed0a2a20cf5d51ff1c65&chksm=e8867140dff1f85674d8c01df3c3d4a92cc75cdd34c68c3b86f8c5eb660f500f033ed35deef8&scene=27#wechat_redirect) + +7、[千万不要运行的 Linux 命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486845&idx=2&sn=31c1ba636db4d687b16b10981833bfbc&chksm=e8866d9fdff1e48976cab2cb62e3b4a4e2e373aeaa0e961847e49cd13ec4ea417b5162fa165b&scene=27#wechat_redirect) + +8、[不想装系统?这8个网站让你在线体验 Linux](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486303&idx=1&sn=fc6c9b73fcb12c5c0c8257e223c8f657&chksm=e8866bbddff1e2ab5edf8b3d4783bcc65975c0385c140d1c954e7d4949e334b9d6ed92ae98a2&scene=27#wechat_redirect) + +### 6.2 版本管理 + +1、[关于 Git 和 GitHub,你所不知道的十件事](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485232&idx=2&sn=c3314e79af9029bdd76ae187f7b4eb19&scene=21#wechat_redirect) + +2、[关于 Git 图谱,这一篇文章讲得很细。](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485179&idx=2&sn=a9f0a8a669d70c0dccedf71a530d1d93&scene=21#wechat_redirect) + +3、[如何巧妙处理 Git 多平台换行符问题(LF or CRLF)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485333&idx=2&sn=4e8e95e5cb12d27ea6b510aaaf15b4c0&scene=21#wechat_redirect) + +4、[这 7 个高级技巧,不会还怎么玩GitHub](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484940&idx=1&sn=9ad168943b03dff09d53e5d73e13ed46&scene=21#wechat_redirect) + +5、[ 神器!这款VSCode插件能填满Github绿色格子](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485595&idx=1&sn=65df21ab0b4042953dd8257bea8b1848&chksm=e8866879dff1e16fff97584467f2f1728f9c2da007894ae062e9201d53b82a30054036f8e81e#rd) + +6、[Git 中冷门却又非常需要的高级用法](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485890&idx=2&sn=528df28585ec16114324e391dbfebab4&chksm=e8866920dff1e036d5da7f5dfe3c944ef8b13d72f022d72b209117120c7e27ebb71036b99893&token=2013245174&lang=zh_CN#rd) + +7、[别乱提交代码了,来围观下大厂的 Git 提交规范](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486877&idx=1&sn=f14a8add6cee89f8312c7077e4e1ed22&chksm=e8866d7fdff1e4691e6b3627fdd7963942d4d568a1e1ba1d7e8ba81944e6afaadc6923429006&scene=27#wechat_redirect) + +8、[盘点提高国内访问 Github 的速度的 9 种方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491802&idx=2&sn=30df1c954dd8505639c6cd71c0e37d76&chksm=e8858038dff2092eed0b603635b648f2c52d720e98536aee1d50b774c9feac6b25fec6ca9a7c&scene=27#wechat_redirect) + +9、[工作流一目了然,看小姐姐用动图展示10大Git命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486154&idx=1&sn=9a409d493eb789c66cb5c3596195582c&chksm=e8866a28dff1e33e13148dbb98dd8a3899392481f4806805bc96de3b6dc7a954ef3eb899bace&scene=27#wechat_redirect) + +10、[如何用 GitHub Actions 写出高质量的 Python代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485985&idx=2&sn=ec7a102012096847b8fdf7156d9260f3&chksm=e8866ac3dff1e3d53d2ae587e0769b1c42ab0176a7982751fc9a740ae45fe1a592691f373dcc&scene=27#wechat_redirect) + +11、[连 Git 都玩不转,还写什么代码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493645&idx=2&sn=d9408f63f9afccd28482e41efe2ba11e&chksm=e88588efdff201f9d3cc513591ad0c7b791616e8ca10a7b091038094b8b52eb1f2585c22e799#rd) + +12、[5 个 Git 工作流,改善你的开发流程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494114&idx=2&sn=f75412a52b9c36e268decf013fffa6de&chksm=e8858900dff2001686dc51e3227a9565f7e97033539a1f2af3d9f38aad87b55a8b5d2ea6d405&scene=27#wechat_redirect) + + +### 6.3 数据库 + +1、[写给程序员的 MySQL 面试高频 100 问](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485321&idx=1&sn=30432df24d4e7858a475192bcf233914&scene=21#wechat_redirect) + +2、[开发人员必学的几点 SQL 优化点](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485892&idx=1&sn=e0b526002a5568391e75fb0cac60ab0b&chksm=e8866926dff1e0303f2717e95ff801c2550d97812c1a775cc232d48183af802db1be54f26105&token=2013245174&lang=zh_CN#rd) + +3、[再见,Navicat!同事安利的这个 PyCharm 的兄弟,真的香!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490040&idx=1&sn=754ca33e3ddbde88ee1b7b918e89d39c&chksm=e886791adff1f00c3cd0ac513cae55ac3bbb2732c637433d15bf42c22139dd54ee47f399b492&scene=27#wechat_redirect) + +4、[太牛逼了!一款软件几乎可以操作所有的数据库!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492595&idx=1&sn=b83bab19f921bcd104caf3ed0f1cf0e3&chksm=e8858311dff20a070cd8063efbca76c74439b18f6c8863f3da14621afb082d246f204ccdba92&scene=27#wechat_redirect) + +5、[硬核!15张图解Redis为什么这么快](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494364&idx=2&sn=c1fcdfda4df97a61cf765f2b5ef1873d&chksm=e8858a3edff20328886dc88e78a2b6ea670825a3e4531d635a1bb5684671c293ac67fa079e68&scene=27#wechat_redirect) + +6、[败家玩意儿!Redis 竟然浪费了这么多内存!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494605&idx=2&sn=3e7c69fece14132d8c8df75cf282c19f&chksm=e8858b2fdff202393289d3aa73f5702ab53afb27758a64bfe4288e4489d68a896411fb33c7c2&scene=27#wechat_redirect) + +### 6.4 Chrome + +1、[推荐 8个 超实用的 Chrome 插件](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484925&idx=1&sn=110e4da78a20e8b5721f00bb36ba9d4c&scene=21#wechat_redirect) + +2、[没有这 42 款插件的Chrome是没有灵魂的](https://mp.weixin.qq.com/s?__biz=MzU4OTUwMDE1Mw==&mid=2247485016&idx=1&sn=ba38963413ee2926f6f5e69b1427491d&scene=21#wechat_redirect) + +### 6.5 Windows + +1、[没钱买 mac?一招教你如何让 Windows 秒变 macOS](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492484&idx=2&sn=d82c0faaf39c420729fba238c65d2fc8&chksm=e8858366dff20a70abbabb14693f265d7fdb1c831cd2b464c9cf1ce8431feb4f07cbb76627ec&scene=27#wechat_redirect) + +2、[微软太良心,这么强大的软件竟然完全免费!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491841&idx=1&sn=80c8c0247f9100e29f7066c874400755&chksm=e88581e3dff208f503da7dafdee037e8db578186dbef79df648c054f54c6e86b9336ff86bde3&scene=27#wechat_redirect) + +3、[双系统的日子结束了:Windows和Linux将合二为一](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491306&idx=1&sn=dfdbc7e02b54622c2eed5f36501444f0&chksm=e8867e08dff1f71e02038aa720fdd99c524adbc6687c2f32070af638b713a154f0f9767205cc&scene=27#wechat_redirect) + +4、[这个只有1.5M的软件,能让你的网速快3倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488856&idx=1&sn=8a96c373b04c4ce7d78c7f87f5928c1e&chksm=e88675badff1fcac0086cb2ec20845821d2441d0909b9a3395b249744217944b1fb8f3e442c9&scene=27#wechat_redirect) + +5、[原来,我一直都不会用Windows](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494412&idx=2&sn=044b47f18f70e5179c5e5c84cb6f4ec4&chksm=e8858beedff202f8728666709b385c476fe41c7d41ed1c63e40afd198db5fe27fbeed2c1639b&scene=27#wechat_redirect) + +### 6.6 其他工具 + +1、[整理了 11 个好用的代码质量审核和管理工具](https://mp.weixin.qq.com/s/DH_TOA3Cr3y37yZ5F4bU5A) + +2、[11 个最佳的 Python 编译器和解释器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485682&idx=1&sn=31b1b68432ca1f1db3866fb0f670270b&chksm=e8866810dff1e106a21c63c1ee198069223047afc72482c1f14ee4f473211aca85273c57d529#rd) + +3、[太酷了!用上这 5 款神器后,你写的代码逼格瞬间爆表](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486248&idx=1&sn=ff17f64475d82e99d32e35f290a49edd&chksm=e8866bcadff1e2dc2ba61ca6a0f63fd753d60e77891ee0ae93207890ee891a9097a6d27b5695&scene=27#wechat_redirect) + +4、[我最喜欢的云 IDE 有哪些?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485965&idx=1&sn=3457ed667d64773bd83bdc3432c06dc7&chksm=e8866aefdff1e3f9407dce655688e2899c177c17a6472b0d50c15e2454cd6450165aee81877d&scene=27#wechat_redirect) + +5、[整理了6个好用的程序员开发在线工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489186&idx=2&sn=6099d58947654ddb35b43d7067d574d6&chksm=e8867640dff1ff56fb33df9d0de1ea820f14f3690d11184b1fcf02fa9480450a0b5639a754df&scene=27#wechat_redirect) + +6、[Github上这些可视化面板也太好看了吧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488270&idx=1&sn=459867f1761d29e4a46f2e450e3ea975&chksm=e88673ecdff1fafaa0f1f89c9739b928e0227ac54059d4ff96c15faf8b88cd3752a929e018e1&scene=27#wechat_redirect) + +7、[腾讯终于良心了!桌面混乱终于有救了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492724&idx=1&sn=58cc2c03fffcbc87995a9927b6168ba7&chksm=e8858496dff20d80365246706c32bfebceb008f28cc04cb420cddd73cd50446495d02204099b&scene=27#wechat_redirect) + +8、[奇技淫巧:在 ssh 里面把服务器的文本复制到本地电脑](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493550&idx=2&sn=2619c1402e8951ff812c69994d922eb0&chksm=e885874cdff20e5adfcd9a95bf199cfcb73fbc7962c6de11c42240e3741eb5b5de75cc711649&token=58703854&lang=zh_CN#rd) + +9、[真神器!不用手写一行代码就能做网站~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494412&idx=1&sn=6789d2aa73a05671c219db659d5ad739&chksm=e8858beedff202f880c3255210e7880bb7c56ad378a1f7b9b82f4e2f24126e4ef8ca7c435103&scene=27#wechat_redirect) + +10、[用 Python 使用 Google Colab?岂止是炫酷](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494533&idx=2&sn=58e2fd5fc63936d89853c5d550293d00&chksm=e8858b67dff2027179fd492d796d537bad57caf830e0aa3fbd51061cd91bd7c7f3f2ce56eb80&scene=27#wechat_redirect) + +## 07. 代码优化 + +### 7.1 算法讲解 + +1、[策略模式:商场促销活动背后的代码哲学](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484937&idx=1&sn=61b81dddfb20d80060fee5f22ab28d5f&scene=21#wechat_redirect) + +2、[算法教程|八张图带你轻松理解经典排序算法(上)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485042&idx=1&sn=b9e8503905be5a960119c2893995fb6d&scene=21#wechat_redirect) + +3、[算法教程|八张图带你轻松理解经典排序算法(中)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485041&idx=1&sn=12d05e0316c8866e3876c3ed484ced15&scene=21#wechat_redirect) + +4、[一次忘记密码引发的算法思考](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484981&idx=1&sn=96f49ab2225c61b45601b68521135dd6&scene=21#wechat_redirect) + +5、[程序员走楼梯都会思考的一道题](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484993&idx=1&sn=36cc1466ed73af2fcf4a8430368a62ee&scene=21#wechat_redirect) + +6、[以为是高性能神仙算法,一看源代码才发现...](https://mp.weixin.qq.com/s/L_OvCcpIFKBLckLBvit8UQ) + +7、[ 用Python手写十大经典排序算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485494&idx=1&sn=1580e9fc17086bc83e30b0fa4c850bd8&chksm=e88668d4dff1e1c27a26b31fda2f0e413be83519aa9dd0e3c8d3cfd08a2478eb2596ed7ba535#rd) + +### 7.2设计模式 + +1、[单例模式告诉你只能娶一个老婆](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484921&idx=1&sn=493d0a492277ecb223ddfff9de8720ff&scene=21#wechat_redirect) + +### 7.3 代码优化 + +1、[看了同事的代码,我忍不住写了这份代码指南](https://mp.weixin.qq.com/s/goqj1YVdKV171LLpjtXruQ) + +2、[怎样才能写好一个 Python 函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485416&idx=1&sn=85b14857e795a92985230ba3f51b73f6&chksm=e886670adff1ee1c7f4266d4e3b931d2e3d1de4dff30dc869eae2cd174da7f54080787f60a1b#rd) + +3、[三个异常处理的好习惯](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484947&idx=1&sn=2bd31279dd9714045ed4dc5481b44208&scene=21#wechat_redirect) + +4、[超实用的 30 段 Python 案例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484866&idx=1&sn=9c85dd1b58f319bb0339e2db577a0be6&scene=21#wechat_redirect) + +5、[删除系统 Python 引发的惨案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485386&idx=1&sn=59eed0894159f4d0599be3fb787b5201&chksm=e8866728dff1ee3ef9fb7a0464df481306be11d9aeba944804e7ea74e660501d42e159ada7fc#rd) + +6、[提速72倍,在Python里面调用Golang函数](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492536&idx=2&sn=360f13b4678fcc6a6f14ee6221474285&chksm=e885835adff20a4ca2025c926c754b9bf6accc6b5b7021f97878163491122ea97d2721c279f6&scene=27#wechat_redirect) + +7、[这一行代码,能让你的 Python运行速度提高100倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492321&idx=1&sn=87dc6b52a0c63b8326bcc50b2c7b3a68&chksm=e8858203dff20b15d1e4730e2851d7523c30db9b404fdfa5b1b19978a1e326e2205e3d6bf7ac&scene=27#wechat_redirect) + +8、[三行Python代码,让数据处理速度提高2到6倍](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491111&idx=2&sn=d27c6398b82749a20c46490b8fdd900f&chksm=e8867ec5dff1f7d3e9f95f221a2f33003bec76da1f0c002f97a59c7887cf460c887e1ad5a579&scene=27#wechat_redirect) + +9、[一文教你节省 90% 的内存占用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489739&idx=1&sn=6589629f52a3a6ec18e67a3ca4952616&chksm=e8867829dff1f13fa87b08ecd09ce319dbd955c8ee15b44c60509e171f1920a6da600075dcd0&scene=27#wechat_redirect) + +10、[写出漂亮 Python 代码的 20条准则](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488402&idx=1&sn=231c2954b0ccca44a18268ab80ddbe88&chksm=e8867370dff1fa66a12d6af4adb66a1d80cbccbf9093c3a8b966a406bf5d8fc005a08166ef45&scene=27#wechat_redirect) + +11、[阅读优秀的项目代码,几点实用的经验分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487922&idx=2&sn=c330d9b4c7bbc1b838e8a9fe90283c27&chksm=e8867150dff1f846628f181ff9687ebf91ba5d06f7721a67bf746698c6f02ea398cdc33c5a0d&scene=27#wechat_redirect) + +12、[一行 Python 代码实现并行](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487299&idx=2&sn=5dffb95993b6fd87c6a3dd1a56b56814&chksm=e8866fa1dff1e6b78239ffa2fe8d124e4ee3af2b7b71f8e9593bdeae2221fcd2afa2b5773e26&scene=27#wechat_redirect) + +13、[代码被反编译了?这两个小技巧能帮到你](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487287&idx=1&sn=05c7ca030257db25623e98ed5d302878&chksm=e8866fd5dff1e6c3219dc6d78717411f716bfa38d89a34ed64a88c0702765258fd34b4edd428&scene=27#wechat_redirect) + +14、[一份可以令 Python 变快的工具清单](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486107&idx=1&sn=83a3f95fea5cb2c24d7f44bf11f2acdb&chksm=e8866a79dff1e36ff4e6c8c546568bca0a36ea3de0550bd87a785ed9ce5cbda4a6d84b5b5fc9&scene=27#wechat_redirect) + +15、[记一次 Python Web 接口优化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486053&idx=1&sn=862e6fbaa8a85a4a10ee35d9eba00196&chksm=e8866a87dff1e391399c252ffcb3168ef1f4c85d5ea52d6f43ceaf81e3f7cb3e548f6de505df&scene=27#wechat_redirect) + +16、[翻车了!pyc 文件居然曝光了我的密码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487730&idx=1&sn=eed309620be1092cf5af030be6b354ff&chksm=e8867010dff1f90611e155156075c2479bab5c9237e8a514a2c7ffe2a317ba16baa30b76d9c9&scene=27#wechat_redirect) + +17、[没有什么内存问题,是一行Python代码解决不了的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492724&idx=2&sn=a84245d93d4191f157bac3e6a38d7066&chksm=e8858496dff20d808bb6ce5876bb0669a72722b6f90ec74f3b43de49ca1e07101c410138e113&scene=27#wechat_redirect) + + +## 08. 冷技巧集锦 + +### 8.1 冷知识 + +1、[Python那些不为人知的冷知识(一)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485031&idx=1&sn=e5ab670ce6485a50c4b7eeaee11e6f34&scene=21#wechat_redirect) + +2、[Python那些不为人知的冷知识(二)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485029&idx=1&sn=5db54b5777348e827c274cd932a15a15&scene=21#wechat_redirect) + +3、[Python那些不为人知的冷知识(三)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485009&idx=1&sn=92e268c20c6f01278e220e11397cc2f0&scene=21#wechat_redirect) + +4、[Python那些不为人知的冷知识(四)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485001&idx=1&sn=5a02f8c912518d15a0b96319cf2da7d1&scene=21#wechat_redirect) + +5、[Python那些不为人知的冷知识(五)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484997&idx=1&sn=5b8cf44b62550e1bd67284fb2b0780dc&scene=21#wechat_redirect) + +6、[Python那些不为人知的冷知识(六)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484990&idx=1&sn=bd28368eff832ba2b24a64f637a6d0c8&scene=21#wechat_redirect) + +7、[Python那些不为人知的冷知识(七)](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484874&idx=1&sn=abffab70b1e265ede1fd3b994dedab90&scene=21#wechat_redirect) + +8、[Python 之父为什么嫌弃 lambda 匿名函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492291&idx=2&sn=3cfdde7bc5e7b55aae00a30c63abe562&chksm=e8858221dff20b3719dfed632d181b95599b843006ae0c2186b5c69593ee7c22c5a5d2c82509&scene=27#wechat_redirect) + +9、[Python 到底是强类型语言,还是弱类型语言?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491532&idx=1&sn=a86bccc1d270390331c36205d3e46497&chksm=e8867f2edff1f6388d80289bff567445c2c9c9414efec74e4a308f7e8a1cabf662c8fc0a6cc0&scene=27#wechat_redirect) + +10、[Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489219&idx=2&sn=0dfa7ae7d4dcdda538b35bbea80b34e4&chksm=e8867621dff1ff372969bf3901835e67a1bfa7cb72709b491987fda3daa0e5f8d15a86c2b6b5&scene=27#wechat_redirect) + +11、[Python 为什么没有 main 函数?为什么我不推荐写 main 函数?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489175&idx=2&sn=2f8f9fb4b31c91ef71d1f34841da6450&chksm=e8867675dff1ff63e6e1f28eec9f18c41c30c050118eb65cde6960e5f6ee5866d7515f2b0040&scene=27#wechat_redirect) + +12、[Python 列表的这 8 个实用技巧,你都 OK 么?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489032&idx=2&sn=38d3985803cb5b6ed10e4cc11972eff7&chksm=e88676eadff1fffc88a3a3a2e3815b2abff43318c597344857c9c5f7806b6b0e6c457b33842d&scene=27#wechat_redirect) + +13、[Python 为什么不用分号作终止符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488816&idx=2&sn=6bc8aa32d9a266cbffabcd87664e75fd&chksm=e88675d2dff1fcc4e845a90903bda5dfe59262a2b60533045a03e574076e92aa6f30974a73f5&scene=27#wechat_redirect) + +14、[Python 为什么推荐蛇形命名法?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488517&idx=1&sn=8b449c2d7c831600be6c5d57e9cb0837&chksm=e88674e7dff1fdf1ab8092ac3cdd2f3978ee6b219daa18a5407c390f62e11448c39455437bb0&scene=27#wechat_redirect) + +15、[Python 为什么用 # 号作注释符?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489657&idx=1&sn=820a070f05096dbe2bdb979fc9492cdb&chksm=e886789bdff1f18d420ee3efa8f85468303690232c4005f775191b0a84a1921430e37aa7e74f&scene=27#wechat_redirect) + +16、[!/usr/bin/python与#!/usr/bin/env python的区别](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486008&idx=2&sn=8637cdc12799f084cd5ecdba9534fb76&chksm=e8866adadff1e3cc49b369dddfaeecc1d9b51f13de1f2733266b2f9fc8426d9e3944a15eb074&scene=27#wechat_redirect) + +17、[pip install 和conda install有什么区别吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487164&idx=2&sn=18de23de9c79f3c20e5a432bb675c262&chksm=e8866e5edff1e748cb6c83d2f4368cbab805fd7bfd41820b60da22061818e8d84548ac14714c&scene=27#wechat_redirect) + +18、[Python 什么情况下会生成 pyc 文件?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486071&idx=2&sn=533257b9b6156bd5588199b349266a3d&chksm=e8866a95dff1e383991b311dc73994ca103c380433a620c5bdf9f8d09b1c49daa74dce6ea941&scene=27#wechat_redirect) + + +### 8.2 冷技巧 + +1、[让Python中类的属性具有惰性求值的能力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485759&idx=2&sn=e32b66c4666f96f84572738549a47a24&chksm=e88669dddff1e0cb0a4b6e6e965c9570b273236fa58a54351ae3b118d6a4bcbd52e452109a63&token=1148998814&lang=zh_CN#rd) + +2、[ 小技巧:如何在 Python 中实现延迟调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485689&idx=2&sn=43e35ff93918ef090480672d4685bce3&chksm=e886681bdff1e10dd0edee95c73a977da51fdeb514dcf8c3bb88f7f9aacc22ce155f45e84268#rd) + +3、[整理了 18个 Python 高效编程技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491418&idx=1&sn=89c35f5d69a54196bd7f32a64171e05d&chksm=e8867fb8dff1f6aee18414c89c31ce118550ba11b645e2e4f6014af4d15d319decc000a70c91&scene=27#wechat_redirect) + +4、[Python 程序报错崩溃后,如何倒回到崩溃的位置?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488434&idx=1&sn=e2b4ca12b228c16e944ff36abec53e22&chksm=e8867350dff1fa46ee7799e460d2b304dbb0c227d33a39d18099761c205319d99a9b4135f9fa&scene=27#wechat_redirect) + +5、[如何用简单的位操作实现高级算法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=1&sn=5376f42c8990027132deb52a6b66307e&chksm=e8866e4ddff1e75b3af85905177130d23118254d42e9452930cc784b209cdf294d9bb73aa247&scene=27#wechat_redirect) + +6、[如何限定Python函数只能被特定函数调用](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490777&idx=2&sn=6572f957117e0a0256da2b85c7221a37&chksm=e8867c3bdff1f52d045cadb962a2da16b015d8455147afe374cf3067dd89c580e1f9514d57d0&scene=27#wechat_redirect) + +7、[pylint 除了检查代码风格,居然还能...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491737&idx=2&sn=f6781d158c5af171f1b916ee7b0c7a68&chksm=e885807bdff2096dc1580abbba594ed1cc1c0994c06f6bd5ede233df0593a43842f7af3e2f8b&scene=27#wechat_redirect) + +8、[让Python在退出时强制运行一段代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486207&idx=2&sn=5586a204b9537d37c02b9926a01719dc&chksm=e8866a1ddff1e30bb77d72ba0013d870f80e0029546729f0e8c65512c4969f5f0733f60ecddf&scene=27#wechat_redirect) + +9、[不使用 if-elif 语句,如何优雅地判断某个数字所属的等级?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486167&idx=2&sn=a31cbf6d3526bfc860b0daa69889193f&chksm=e8866a35dff1e323ed958858223dda1af6ae0d371be0037c35f9ae6894e776dec1ee53c2a00b&scene=27#wechat_redirect) + +10、[涨姿势了,raise...from... 是个什么操作?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486001&idx=1&sn=b9a89305dae7d4f57105be1d274fae43&chksm=e8866ad3dff1e3c51b5fa132147b9f9c9e4ba14b41c5409e62e2f2696d1389c6ac4ddd1e7bec&scene=27#wechat_redirect) + +11、[一些我日常使用的 Python 技巧分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488382&idx=2&sn=f2b090fe47bb56e0463126c69726bf10&chksm=e886739cdff1fa8a65ce4869a8bdb8fbd28396e3fbcdac2a4441b7c58ba5c77cea1765858146&scene=27#wechat_redirect) + +12、[5年 Python 功力,总结了 10 个开发技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488243&idx=1&sn=66d2f534ddcbed582e7ba734b53701f5&chksm=e8867211dff1fb07f2edcebfda9e7863ee57266c15d4276bc89c2ee69baafc46fbc0e85fe703&scene=27#wechat_redirect) + +13、[我发现了个 Python 黑魔法,执行任意代码都会自动念上一段 『平安经』](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490548&idx=1&sn=c71772cc40992d8912fd8a3fa1607038&chksm=e8867b16dff1f20081431362c99fb981d0cd75f2bd5baf981b7db18e08492138aab10f98efb8&scene=27#wechat_redirect) + +14、[实用技巧分享:如何批量更新已安装的库?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487862&idx=2&sn=a1c092467b471db82060b395c341be6c&chksm=e8867194dff1f882da87863f4d4a4ccbd687daa0b4a742dc6d01fd01d0a6ca6c5fb7cc28629b&scene=27#wechat_redirect) + +15、[如何导入父文件夹中的模块并读取当前文件夹内的资源](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493878&idx=2&sn=36c70addd7c0c0640d1285ab590410e4&chksm=e8858814dff201026397c7b08034ad513c4b9e5dcf8307325951ae4077e386f87f5685ca528f#rd) + +16、[用 Python 读取资源文件?这个技巧保你涨姿势](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494041&idx=2&sn=3b3a43157290f0ff542185982aa4b537&chksm=e885897bdff2006dd634e39ccc3c753704ad0f74e2fec95249d0d7f0c30bee85426ef86028e0#rd) + +### 8.3 炫技操作 + +1、[Python 炫技操作(01):条件语句的七种写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485991&idx=1&sn=98e17f91d2f66f62c9aa644c03d8259d&chksm=e8866ac5dff1e3d3f6810a3f9e388d46d95945af9748baea0380ecec83c768eaac09738cf959&scene=27#wechat_redirect) + + +2、[Python 炫技操作(02):合并字典的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486080&idx=1&sn=f1c5c4fc5363a1d787b9ae9baba0d07b&chksm=e8866a62dff1e374ad1e5ae2e51bc6cbeb41f631899cde980555ded61be840f0fe6c76c44b7d&scene=27#wechat_redirect) + +3、[Python 炫技操作(03):连接列表的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486132&idx=1&sn=3827cf8672d4121b77225dbf9d377d69&chksm=e8866a56dff1e34094f2f834a613800480c0cdb078ab5656640fcb8c445ffa020b175b0f55cb&scene=27#wechat_redirect) + +4、[Python 炫技操作(04):海象运算符的三种用法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486158&idx=1&sn=0bd0647702a1599082e5bf1710d89e37&chksm=e8866a2cdff1e33ab4465d88497ea3505ad8fe614365eec7592a8c75053a6c8218206807c1a3&scene=27#wechat_redirect) + +5、[Python 炫技操作(05):花式导包的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486799&idx=1&sn=1be136d4e45c1b6f8a5eda567aaf259d&chksm=e8866daddff1e4bbddc0164ae795d6c302e36226cb3ba76ffc57e5c993b0861ef4ad693c2a56&scene=27#wechat_redirect) + +6、[Python 炫技操作(06):判断是否包含子串的七种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=2&sn=4596043fb72b75a04a778942c61cc2af&chksm=e8867e81dff1f7979e21f58716b3ce3e09f8515829b15acd5169dd3f160e302d617a1b94cb2d&scene=27#wechat_redirect) + +7、[Python 炫技操作(07):模块重载的五种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492817&idx=1&sn=cfb52d1a3f88a61e7b22a63306dac0e9&chksm=e8858433dff20d25f06ac0b5b767121907a4679b9e6da0905694d15c432ba596ce7b202ec7bb&scene=27#wechat_redirect) + +8、[Python 炫技操作(08):五种 Python 转义表示法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494334&idx=1&sn=9f55122545b7b5d81049af27eff7800c&chksm=e8858a5cdff2034a8158b14669aaf695656c1227428ce86f22b19230769b5b45104b0a885205&scene=27#wechat_redirect) + +9、[最全总结:把模块当做脚本来执行的 7 种案例及其原理](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490904&idx=1&sn=ca3725510c7510965cbc76bd1bf06949&chksm=e8867dbadff1f4acb08c0daf08272ca61757909b0b73a4cde1aa4d86965c149c40f44417f7d0&scene=27#wechat_redirect) + +10、[这个 Python 炫技操作千万不要用,别问我怎么知道的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491171&idx=1&sn=27d55a4b8b717450f1ab0596263f453f&chksm=e8867e81dff1f7975674ab7fcc0019654ae0e867db045fb575623f91cb143801d03aa365be2d&scene=27#wechat_redirect) + +11、[涨见识了,在终端执行 Python 代码的 6 种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488283&idx=1&sn=24a78834ec3434a90ca7c3e6e972495c&chksm=e88673f9dff1faef908c43a41133f65695a68579e0edb2d7b15f131d9be26bcf919d66a7d9e3&scene=27#wechat_redirect) + + +## 9. 云计算 + +1、[一份面向初学者的云计算通识指南](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484908&idx=1&sn=3085e048da685440408e1bdc54feb4aa&scene=21#wechat_redirect) + +2、[ 如何探测虚拟环境是物理机、虚拟机还是容器?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485401&idx=2&sn=8bfb6e1a71e0777a4d5e55f64b55ace5&chksm=e886673bdff1ee2d8acce291e402cd2ae0c7f64e455fdeea158eb53e535ee79b2aadc449935e#rd) + +3、[超详细教你如何阅读 OpenStack 源码?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485899&idx=1&sn=7a578a553c67e794ef6ec2f463864a46&chksm=e8866929dff1e03fa8152751967106e9262980db6afdff106fe9e39854d24d25494b3e8a3a4e&token=2013245174&lang=zh_CN#rd) + +## 10. 职场相关 + +1、[一个专科生的 Python 转行之路](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485012&idx=1&sn=751796e63a30d84be01486619b5fb806&scene=21#wechat_redirect) + +2、[一个机械生的Python转行自述](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484979&idx=1&sn=6513e940bc7c0677dedf7efff80aa4c6&scene=21#wechat_redirect) + +3、[自学 Python后端开发 到什么程度可以找工作?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485010&idx=1&sn=e76d031f91633f9bfde9da36d8dcf996&scene=21#wechat_redirect) + +4、[工作不是游戏,写给编程新人的五点建议](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484984&idx=1&sn=0934415cc48004ae76345dcfb8e33b94&scene=21#wechat_redirect) + +5、[Python 从业十年是种什么体验?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485601&idx=1&sn=0dbebc02002ab01a8cd42265822ff81c&chksm=e8866843dff1e15509f2a9d2cd551b2360607cf88e73e5d3036388716ee57949d726dcea209c#rd) + +6、[40条提升编程技能的小妙招](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489765&idx=2&sn=79287da847c25ce0d72db6a33eb0c5a0&chksm=e8867807dff1f111043fe22bbfc776a52331c28abf85c2f658cbbaba3235c4e9d3c070c44d8c&scene=27#wechat_redirect) + +7、[我,一个靠GitHub打赏谋生的码农,年入十万美元](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488934&idx=1&sn=56f51b946908ed89a58b311fc77f47d6&chksm=e8867544dff1fc52e30288685c89e86cefd063d5d37f5b1aba5fb30fd3665cf18346533cad96&scene=27#wechat_redirect) + +8、[5 年 Python 的我,总结了这 90 条写 Python 程序的建议](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487323&idx=1&sn=1b6594d63319faaadefad1f5e178235f&chksm=e8866fb9dff1e6af732304c8f3645cecca8b4bfa4158e45b0ea6c5167c2675bd748ce23e8239&scene=27#wechat_redirect) + +9、[Python这么慢,为啥大公司还在用?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=1&sn=55dda8a6a10429b3a7016393bc058493&chksm=e886768edff1ff981d67b2ecfe52ebf5b8dbc0085289e6bd3f8a6c6631ee6fc4a633fb6bc4b8&scene=27#wechat_redirect) + +## 11. 通用文章 + +1、[假如有人把支付宝存储服务器炸了,你的存款会怎样?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485171&idx=1&sn=d6e2e24f68f3c6eb35e880f32e12d4ab&chksm=e8866611dff1ef0791cf5aae05697592cb804010aecd783a8592f7a90a387645fe4b47a376ea&scene=27#wechat_redirect) + +2、[手机没网了,却还能支付,这是什么原理?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493296&idx=1&sn=8dae2411c61b7e5ab4a1fcdf45b86c79&chksm=e8858652dff20f44519c328ba22b4f22c8b2320bc1464319e98d6084697dfab3ef2346a5cf49&scene=27#wechat_redirect) + +3、[Chrome 的小恐龙游戏,被我破解了...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491911&idx=1&sn=1dcab055e480fe626d50b0324ed5b9f1&chksm=e88581a5dff208b3fbf88017c15ccfa966eec3b25e4804a72401773d7b87d5b6a2bcd8db9ac7&scene=27#wechat_redirect) + +4、[深扒微信多开的秘密后,我竟然发现了个 bug](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489704&idx=1&sn=a33559f22517c744367a660f78dbb25a&chksm=e886784adff1f15c1524f90487d6957c86dee0f5c499528630f43b62320b28e66977afe4d884&scene=27#wechat_redirect) + +## 12. 明哥的作品 + +1、[太赞了!《Python 黑魔法指南》终于面世了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486834&idx=1&sn=f5b94c3a520624786162f78246e60246&chksm=e8866d90dff1e486a3dae97aaf347e83834c8cf204849e5f9ae23dc9fdf0c470f97e0298cf74&scene=27#wechat_redirect) + +2、[《Python黑魔法指南》全新版本 v2.0 上线发布](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247490543&idx=1&sn=449a1c7adfafdd5cf37874ed67620e89&chksm=e8867b0ddff1f21b9d622ba7a53b35143cd7fbb3da0d7c091b3d2cdd71d817a4397a99fa424e&scene=27#wechat_redirect) + +3、[明哥写了两个月,这 200页《PyCharm 中文指南》 终于可以发布了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247491716&idx=1&sn=76a9f3ad5163a9ffdbaf0fefd0259261&chksm=e8858066dff20970fa9988675154fff6a6f84793a80b8ea1d6fe5d5bbb5bc816d2feabc50664#rd) + +4、[一个明哥真正意义上的个人网站,现在来啦~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493207&idx=1&sn=ced486f99dc7d9ab665849da1016ceb1&chksm=e88586b5dff20fa32db6f3074ee954cbe30b34846c5852d8772184a41e9ad58293947e22095d&scene=27#wechat_redirect) + + + +--- + + + +都看到这儿了,还不点个关注嘛? `*^_^*` + +![](http://image.iswbm.com/20201024132326.png) ## 欢迎交流 diff --git a/github-toc-maker-for-sphinx.py b/github-toc-maker-for-sphinx.py index 154d794..49f3f5c 100644 --- a/github-toc-maker-for-sphinx.py +++ b/github-toc-maker-for-sphinx.py @@ -41,7 +41,7 @@ def get_toc_info(): section = int(re.findall(r"c\d{2}_(\d{2}).md", file_name)[0]) #md_path = os.path.join("./source/", dir_name, file_name) - md_path = os.path.join("http://python.iswbm.com/en/latest/", dir_name, file_name.replace("md", "html")) + md_path = os.path.join("http://pythontime.iswbm.com/en/latest/", dir_name, file_name.replace("md", "html")) title = get_title(file) if not title: continue diff --git a/md2rst.py b/md2rst.py index 3dca78a..51a7a32 100644 --- a/md2rst.py +++ b/md2rst.py @@ -24,7 +24,7 @@ # 没有文件变更 os._exit(0) -base_link = "http://python.iswbm.com/en/latest/" +base_link = "http://pythontime.iswbm.com/en/latest/" readme_header = ''' ![](http://image.iswbm.com/20200607120940.png) @@ -36,9 +36,9 @@

    -## [项目主页](http://python.iswbm.com/) +## [项目主页](http://pythontime.iswbm.com/) -在线阅读:[Python 编程时光](http://python.iswbm.com/) +在线阅读:[Python 编程时光](http://pythontime.iswbm.com/) ![](http://image.iswbm.com/20200607130051.png) diff --git a/source/c02/c02_04.md b/source/c02/c02_04.md index 571d252..c6f70dc 100644 --- a/source/c02/c02_04.md +++ b/source/c02/c02_04.md @@ -318,7 +318,7 @@ teacher.call("exit") 当队列内部的任务计数器归于零时,调用 Queue.join() 就不会再阻塞了。 -要理解这个过程,请参考 http://python.iswbm.com/en/latest/c02/c02_06.html 里自定义线程池的的例子。 +要理解这个过程,请参考 http://pythontime.iswbm.com/en/latest/c02/c02_06.html 里自定义线程池的的例子。 ## 4. 消息队列的先进先出 diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 866b46d..38e5da5 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -339,7 +339,7 @@ Queue.task_done(),说明队列这个任务已经结束了。 当队列内部的任务计数器归于零时,调用 Queue.join() 就不会再阻塞了。 -要理解这个过程,请参考 http://python.iswbm.com/en/latest/c02/c02_06.html +要理解这个过程,请参考 http://pythontime.iswbm.com/en/latest/c02/c02_06.html 里自定义线程池的的例子。 4. 消息队列的先进先出 diff --git a/source/c04/c04_03.md b/source/c04/c04_03.md index ed89f75..ec5afbd 100644 --- a/source/c04/c04_03.md +++ b/source/c04/c04_03.md @@ -25,7 +25,7 @@ ## 1. 成品展示 -以我的博客(`python.iswbm.com`)为例,先给大家展示一下。 +以我的博客(`pythontime.iswbm.com`)为例,先给大家展示一下。 这是首页。显示了你所有的文章索引。 ![](http://image.iswbm.com/20190511160523.png) @@ -275,7 +275,7 @@ pandoc -V mainfont="SimSun" -f markdown -t rst hello.md -o hello.rst ## 7. 自定义插件 -之前有不少同学看过我的个人博客(http://python.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 +之前有不少同学看过我的个人博客(http://pythontime.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 使用这个方法搭建的站点,一直有一个痛点,就是无法自定义页面,自由度非常的低(和 WordPress 真的是没法比,因为这两种产品定位本身就不一样。) diff --git a/source/c04/c04_03.rst b/source/c04/c04_03.rst index 8452a5e..33ddd0d 100755 --- a/source/c04/c04_03.rst +++ b/source/c04/c04_03.rst @@ -26,7 +26,7 @@ 1. 成品展示 ----------- -以我的博客(\ ``python.iswbm.com``)为例,先给大家展示一下。 +以我的博客(\ ``pythontime.iswbm.com``)为例,先给大家展示一下。 这是首页。显示了你所有的文章索引。 |image1| @@ -274,7 +274,7 @@ source:raw-latex:`\index`.rst,千万要注意中间的空行不可忽略。 7. 自定义插件 ------------- -之前有不少同学看过我的个人博客(http://python.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 +之前有不少同学看过我的个人博客(http://pythontime.iswbm.com),也根据我写的搭建教程,完成了自己的个人站点。 使用这个方法搭建的站点,一直有一个痛点,就是无法自定义页面,自由度非常的低(和 WordPress 真的是没法比,因为这两种产品定位本身就不一样。) From 2e5367a96e8170deec8081c935b2db5ecd13bdd0 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 26 Dec 2020 11:13:44 +0800 Subject: [PATCH 136/147] unlock --- source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/conf.py b/source/conf.py index 6fb6b49..afa5845 100755 --- a/source/conf.py +++ b/source/conf.py @@ -127,7 +127,7 @@ sys.path.append(os.path.abspath(_exts)) html_js_files = [ - 'js/readmore.js', +# 'js/readmore.js', 'js/baidutongji.js', ] From e0814006877810d9b323b4d6d2a7d3fab748bbe1 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sat, 26 Dec 2020 14:08:37 +0800 Subject: [PATCH 137/147] lock --- source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/conf.py b/source/conf.py index afa5845..6fb6b49 100755 --- a/source/conf.py +++ b/source/conf.py @@ -127,7 +127,7 @@ sys.path.append(os.path.abspath(_exts)) html_js_files = [ -# 'js/readmore.js', + 'js/readmore.js', 'js/baidutongji.js', ] From c7223d25ece8a5af096b998ad205a56cbb6cce84 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Tue, 26 Jan 2021 23:37:12 +0800 Subject: [PATCH 138/147] update --- README.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6b17275..34e6517 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ -目前目录更新内容至 2020/12/4 发的文章。 +目前目录更新内容至 2021/1/26 发的文章。 ## 01. 基础系列 @@ -88,6 +88,10 @@ 38、[学习 Python 的指南大纲,从基础到核心知识全都有](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486246&idx=1&sn=58665e2be95f71415ac3e2c1c5f4f690&chksm=e8866bc4dff1e2d212a4d93c0c83cd39cd534f64a8e113b208f96a6156e75ef652ee1ac32270&scene=27#wechat_redirect) +39、[Python 中常见的配置文件写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496552&idx=2&sn=cc57aee69875c855fcc7e7d4098dda44&chksm=e885938adff21a9cb0cd1ee105af9001ca2a597ae37fba35c76de13eb2a69033133a866950d6&scene=27#wechat_redirect) + +40、[Python 的 \__name__ 变量,到底是个什么东西?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496153&idx=3&sn=44fbf9d2c5bf1c629dab6abc1f429a1b&chksm=e885913bdff2182d69117d5f85ede8eed726f1bf00192cb270dc4dfd6e20a1e51638c6520bee&scene=27#wechat_redirect) + ### 1.2 基础库 1、[Python时间模块,超实用总结!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493317&idx=2&sn=b821b2765cfcdf7da8858b962f092980&chksm=e8858627dff20f31b14090d0ff178002ea0544196d8d63e595f5f6787f8e95d60df292f80bb4&scene=27#wechat_redirect) @@ -110,6 +114,10 @@ 10、[使用 Python 打印漂亮的表格,这两项基本功你可会?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494135&idx=2&sn=f055f78da070dc71c2593540f3272988&chksm=e8858915dff20003c05bc328b09a8cdb8dda3e37118201fb2eb317c1296e788aa1f50c8edf45&scene=27#wechat_redirect) +11、[73个f-string的例子,帮你吃透字符串格式化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497032&idx=2&sn=dc4761acf90fa7ff71833f0e7f952189&chksm=e88595aadff21cbca930dfa432106b6645b01ae1485d09f1d860a5779311b1d9c1bf23123146&scene=27#wechat_redirect) + +12、[一学就会的 Python 时间转化总结(超全)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496446&idx=1&sn=98c71ceefb10fb049ec33c3c1aa2bc6c&chksm=e885921cdff21b0adf359a9b75e5a44033a6cfa86a05cb6ed9e19f07c6b73b6cf97c4e3cd3c1&scene=27#wechat_redirect) + ### 1.3 代码案例 1、[15个Pythonic的代码示例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485036&idx=1&sn=24de1996a63bf25b0c0deec782f688cf&scene=21#wechat_redirect) @@ -130,6 +138,14 @@ 9、[6 个例子教你重构 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494309&idx=2&sn=e1cffffa71b5cc76434b6771cfcc0bae&chksm=e8858a47dff20351575b1cd29e35a51b30c7859c9dffbf816df883bc7ccee9ea15aa5eaad812&scene=27#wechat_redirect) +10、[25 条很棒的 Python 一行代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496971&idx=2&sn=9196437b9976a382b277c7780f8aa04b&chksm=e88595e9dff21cffc32c8da9d2a5d59ab40d7e1d1d32f9193687c35cba490919c624d04d7ef6&scene=27#wechat_redirect) + +11、[Python 中的 EAFP 和 LBYL 代码风格到底是什么?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496204&idx=2&sn=5aa0cca75c6b59bd5f1b6f9cd1bfe07e&chksm=e88592eedff21bf857a0d53a1c3515e5e4ad32c5ae728218b6dedf63fcd02a9750a9f66387d4&scene=27#wechat_redirect) + +12、[3000 字教你学会最地道的 Python 编程风格](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494858&idx=1&sn=9386de992ce9f9b08d2d6d2721584a39&chksm=e8858c28dff2053ec9ca59d1d4244c119ca4363b6914e87b3ef250a54bb8a82c611ab437763e&scene=27#wechat_redirect) + +13、[再来 6 个例子教你重构 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494838&idx=2&sn=eeaf56eeaf9cc72f1340233bcd88b5a7&chksm=e8858c54dff20542f4712be4bd185f2e27e78e09bb77acb97e2be2d4c1329553be4abe25a3e2&scene=27#wechat_redirect) + ## 02. 进阶系列 ### 2.1 进阶必学 @@ -220,6 +236,14 @@ 43、[为什么继承 Python 内置类型会出问题?!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494268&idx=2&sn=67ea6f78e7add1d43174f601626d1cb5&chksm=e8858a9edff20388c08f6e3f9c8ce9db6969174fc3674bf04d65cf0fbcda48c08217de5d622b&scene=27#wechat_redirect) +44、[Python最会变魔术的魔术方法,竟能"大变活人"](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496126&idx=2&sn=083eafff3facad62a0660061990c7387&chksm=e885915cdff2184aa79584c1e46d3b40bc928eeb732a856e9ff26e7864a3fb1fb81749d73364&scene=27#wechat_redirect) + +45、[说说 Python 的内置电池,你学过吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495322&idx=2&sn=7271490d820c6a48b9ee7d090c1c88bd&chksm=e8858e78dff2076e4b5651811652d2da47d2a1db07eda9362120b51f83da7b65aca58d1ad3a6&scene=27#wechat_redirect) + +46、[恶补了 Python 装饰器的六种写法,你随便问~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494951&idx=1&sn=43b65fade87940be918ff1ff066b9ca8&chksm=e8858dc5dff204d3571414164e63a1de715499418805477d012104fa12e6a2577ef5fa58e6a8&scene=27#wechat_redirect) + +47、[Python 从业十年的程序员,写的万字经验分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494942&idx=1&sn=84efbced575838b9b8e169e8c59383b7&chksm=e8858dfcdff204eaf7d251ddf1ea69655bd5eb69da596a4970c845f912c2ac9e3f2f18e5e7b5&scene=27#wechat_redirect) + ### 2.2 包的管理 @@ -243,6 +267,20 @@ 10、[非常干货:Python 探针实现原理](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493427&idx=1&sn=d9e0c646a98cf2b54b5724ebc3262255&chksm=e88587d1dff20ec71ed33c5165bbdec377eea89307da0f924784cff0f4c01c05a5f3247de50e&token=58703854&lang=zh_CN#rd) +11、[如何编码检查依赖关系是否有循环依赖](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496866&idx=2&sn=a25b46ef4aa67a007612ef53bc832811&chksm=e8859440dff21d5647027fdcdad642c3f9781f140f7ecb47f975678eb74b8321d4b65ec076cc&scene=27#wechat_redirect) + +12、[关于包导入,这三个知识点太多人不知道了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496204&idx=1&sn=cf788dfc35b7377ed70369aafba19c47&chksm=e88592eedff21bf872d8b6c5ded642ea5e2f2f55dcd31331da9bcd13f265cde7fee2f65ca8f5&scene=27#wechat_redirect) + +13、[手把手教你发布 Python 项目开源包](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494964&idx=2&sn=cac94af61d53857c2fa7362f2b4f8564&chksm=e8858dd6dff204c02b5235eb0a705e9193d3c5107369a87d35753bcb2e7f80020ceaccfbb9e9&scene=27#wechat_redirect) + +14、[有人在代码里下毒!慎用 pip install 命令](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494847&idx=1&sn=822e027acb42d6ab882687fc515f14ae&chksm=e8858c5ddff2054b80c6eda85f27331472ad78bcc11ca980cf03cae0824255666b0518afb2cc&scene=27#wechat_redirect) + +15、[简化 Python 函数调用的 3 种技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494847&idx=2&sn=bec329103690975870e79fc00abdeba7&chksm=e8858c5ddff2054bdbddfb145caf763aab5130c3939d39a737cb3bd42934f7a74b8d3fb0e06d&scene=27#wechat_redirect) + +16、[如何 Import 自定义的 Python 模块?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494821&idx=2&sn=646396ea0a67ab3eed7b0f0652e647dd&chksm=e8858c47dff2055109d74b13de70efe7a5083fd1a99a1323043e932694a572291d00359bec15&scene=27#wechat_redirect) + +17、[解锁装包新姿势,这个场景下 pip 真的难用~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494820&idx=1&sn=24ceaa11376cbfa5a4d48adc076603db&chksm=e8858c46dff20550d0124ef1bf1e481e14ffcf485e8c49aa77f6f9541f6163850538e4af05f9&scene=27#wechat_redirect) + ### 2.3 性能优化 1、[Python高效代码实践:性能、内存和可用性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485161&idx=1&sn=07625f06db330897bc8bfee880ceac18&scene=21#wechat_redirect) @@ -267,6 +305,10 @@ 11、[超干分享!如何提高Python的运行速度?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=2&sn=00c8aa00a85f8710fada38d35b2c8388&chksm=e8858b3fdff2022991a0024757c64faaed81da830cbbf1fa62b6b218d7271b0837b11922c1f0&scene=27#wechat_redirect) +12、[程序运行慢?你怕是写的假 Python](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495235&idx=1&sn=816c6de9e1d23d65fe8c1db458426f68&chksm=e8858ea1dff207b749b64461bb06bf8818883644a4450b6400efda4d7e54d4832162448e9f68&scene=27#wechat_redirect) + +13、[超干分享!如何提高Python的运行速度?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=2&sn=00c8aa00a85f8710fada38d35b2c8388&chksm=e8858b3fdff2022991a0024757c64faaed81da830cbbf1fa62b6b218d7271b0837b11922c1f0&scene=27#wechat_redirect) + ### 2.4 并发编程 1、[并发编程01|从性能角度来初探并发编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485074&idx=1&sn=a859c6ab1d9b95c30c9f8b06f9489887&scene=21#wechat_redirect) @@ -303,7 +345,7 @@ 17、[为什么说线程是CPU调度的基本单位?](https://mp.weixin.qq.com/s/c3aZ-6UzZVD3_PvVhiDPWA) -18、[非常适合小白的 Asyncio 教程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485842&idx=1&sn=ab85dab95cf47046ddcc35b60e633c40&chksm=e8866970dff1e066381c0ba53fe09e34b8f1ce52bbbfe568c1ee66b9cbad87f43b442895c770&token=2013245174&lang=zh_CN#rd) +18、[非常适合小白的 Asyncio 教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495734&idx=1&sn=2fb31e6fd306f19ba03b368f5924b7e8&chksm=e88590d4dff219c252b4e1126f196e0a0c395f4f8d22d58cc5c531d9dd2acad6f6e295a1fee8&scene=27#wechat_redirect) 19、[说说 Python 里关于线程安全的那些事儿](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486279&idx=1&sn=2b6acf717b7f5cc6a2b72c62266dbe01&chksm=e8866ba5dff1e2b318c679413fd7d0ac3a69db7cab1ceda03eca26fb71f6d2837af80e2143d0&scene=27#wechat_redirect) @@ -311,6 +353,10 @@ 21、[为什么 Python 多线程无法利用多核?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494567&idx=2&sn=653795446bacff2d445cabd417748756&chksm=e8858b45dff202530afc05a00469b20a0a95f53bcaefc49472240e1c905459ecfb74edbe0461&scene=27#wechat_redirect) +22、[Flask 之父:我不觉得有异步压力](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496214&idx=2&sn=cd813e57b7e03aff0300f1f65cc583f0&chksm=e88592f4dff21be2738eaf2745cf828841fea934e986f9a4ee7d13eb9fe09701d1c13993435c&scene=27#wechat_redirect) + +23、[Python 协程的本质?原来也不过如此](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495553&idx=1&sn=d00916691bf2cddc576f1346672ef603&chksm=e8858f63dff20675238e5eee747645d022aa316bd5417004c296157aadb4de6134d6118e66e7&scene=27#wechat_redirect) + ### 2.5 实战练习 1、[适合 Python 新手练习的绝佳项目](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485898&idx=2&sn=36a1b1a94dece0a290652da23679cff0&chksm=e8866928dff1e03e58b95b3cd24dbc23e368b6eb2c0599b2efe814924e21ec5c01935b2e0455&token=2013245174&lang=zh_CN#rd) @@ -323,7 +369,7 @@ 2、[或许,这是最强大的一款Python GUI工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492571&idx=2&sn=6ac2c0c19d86d653acd82d5b4ef86126&chksm=e8858339dff20a2f595159499e8eb8e072b52e61521e4a1322337f174bff942134a25b3b6b9b&scene=27#wechat_redirect) -### 2.7 自动化办公 +### 2.7 自动化 1、[付费?是不可能的!20行Python代码实现一款永久免费PDF编辑工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492085&idx=1&sn=785968af86bf998ecad9b11583a23ad6&chksm=e8858117dff2080119f596c6447d9a0c7b73c6bced8306883900f2f4d9582929f9d5c26ac45d&scene=27#wechat_redirect) @@ -335,6 +381,12 @@ 5、[Python吊打Excel?屁!那是你不会用!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247487151&idx=2&sn=9c4a697758af01c086cced44b98a3380&chksm=e8866e4ddff1e75bd2f19e036c3e338edee50871364d2f185afa51cb361c83f3bbda8ce2d92f&scene=27#wechat_redirect) +6、[收藏|Python办公自动化不得不会的十大文件操作](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495776&idx=2&sn=50e900caa96346371045145170dc9073&chksm=e8859082dff219940436120f8fa9ae329dc655e69335f967e2f926786420d9d97b1b11711100&scene=27#wechat_redirect) + +7、[再见 VBA!神器工具统一 Excel 和 Python](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495801&idx=1&sn=becae030a09a689e1f231a5c3c1ef7bd&chksm=e885909bdff2198d1d2aafaf0077a2763d796e435817b70b612095ae944489581f3bbffe0a69&scene=27#wechat_redirect) + +8、[微软最强 Python 自动化工具开源了!不用写一行代码!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=1&sn=61d9f8b7557cb2ff517fde1a414b219d&chksm=e8858b3fdff2022954d383579b6392362e99e99af4a6309028dfa6ddd7bfc90a02c85d599b50&scene=27#wechat_redirect) + ### 2.8 网络爬虫 1、[想逆向我的 js 代码?先过了我的反 debug 再说吧!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484876&idx=1&sn=ed81400f77deb2af18311f8e42e9b11b&scene=21#wechat_redirect) @@ -355,6 +407,10 @@ 9、[不懂爬虫也能轻易爬取数据的 6 大工具](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486085&idx=1&sn=51c740e1d848f1624a5a8e582434f661&chksm=e8866a67dff1e371de89a64503591ae0c42b67cd43434fa394125f09a2f65bc23817a266ac13&scene=27#wechat_redirect) +10、[效率提高十倍,Puppeteer 如何启动交互模式?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496566&idx=2&sn=efc444487afd08ec055ceaee414857d6&chksm=e8859394dff21a824ccc8e2679f985e94c60d6b680f6856aa7a7a9ed608c9dc1ad552f0f2b40&scene=27#wechat_redirect) + +11、[别去送死了,你这样写爬虫,早晚得进去~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495216&idx=2&sn=648a296205fb3310287d95907d5126ef&chksm=e8858ed2dff207c4c42eb8b224bfe4f68186108642afd8ad7dfddef2628ff2c8f93170dbd948&scene=27#wechat_redirect) + ### 2.9 实用系列 1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect) @@ -393,6 +449,16 @@ 18、[10 个“疯狂”的 Python 项目创意](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247493963&idx=2&sn=e4ff38bc55f4ad4d990191e81a316421&chksm=e88589a9dff200bff3e65c19744e59dd3407a48ff5b104bc7b71771762b331daca5c7772892a#rd) +19、[使用 Python 自动化清理微信僵尸好友](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496922&idx=2&sn=3b2233563ed7a02df94ab2600fe737f3&chksm=e8859438dff21d2eb4874859a839f6c03b74cbd217a2e92eb6ece714051af11fdcb81c76c4d7&scene=27#wechat_redirect) + +20、[别再问我怎么Python打包成exe了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496522&idx=2&sn=5579b947755bcc4aa2d2dd098d4396bc&chksm=e88593a8dff21abe09ab37a9c6ee855bab17342a7397b946514acb9db281c629502709adc6ae&scene=27#wechat_redirect) + +21、[还没抢到票?试下这个用 Python 写的最新抢票神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496260&idx=2&sn=6f0b4ea3960b25640eb2936f65ac9e0a&chksm=e88592a6dff21bb0140bb319486321f16a41679a53f68ea8cdbe0cad3fa9fbb6996fcc2019ec&scene=27#wechat_redirect) + +22、[使用 Python 制作按键触发Windows通知的脚本](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494892&idx=2&sn=6c633c829e9e996bf6c3109911d2e20e&chksm=e8858c0edff205181ebc2eb5e907ea38b0dc0fa581a243fe57269968dc1aab147d3230c8a8cf&scene=27#wechat_redirect) + +23、[纯Python方案实现中英文全文搜索](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494874&idx=2&sn=f515724a52613a875869e6aa31ae3e78&chksm=e8858c38dff2052ede9f42592fb59446b68bb3fff84870601c36ee64b864527d25cb9619ccae&scene=27#wechat_redirect) + ## 03. 数据分析 @@ -404,6 +470,8 @@ 3、[50题 讲透 matplotlib :从入门到精通](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486026&idx=1&sn=7b333759709c30fe442732b7a107e3c1&chksm=e8866aa8dff1e3be37f5169ac53bbcf71a76514e99fbbcbe458ec15aa1204d988ad44d689eb3&scene=27#wechat_redirect) +4、[实用的 Pandas 技巧,估计 80% 的人不知道](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496209&idx=2&sn=8f8db418261326c981cd27f57cc3fc5b&chksm=e88592f3dff21be543cc302a715ea39ae8a1adacb7342f45d5bcf2eca9c2b398b05955408597&scene=27#wechat_redirect) + ### 3.2 数据可视化 1、[可视化01|一图带你入门matplotlib](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485022&idx=1&sn=76d4270a15c430217588bd9f8be8303b&chksm=e88666bcdff1efaa35c6d685f19b04e62f58e768dc9d1819bcb9f8211f65d3fd83296259d923&token=1148998814&lang=zh_CN#rd) @@ -448,6 +516,10 @@ 21、[牛批了,1行python代码就可实现炫酷可视化](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494580&idx=2&sn=bf87885645377449f3a94d6d8122e1f6&chksm=e8858b56dff2024046c48ccf0ff087c42fa2608c0d0b6183ea61ffb85278a9c52138cdfbef13&scene=27#wechat_redirect) +22、[珍藏版 | 这 30 个细节,决定了数据可视化的质量](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496866&idx=1&sn=f1ba05ed6a4db3e507f0cf0d4ce3ac59&chksm=e8859440dff21d56256e0ad9a9c739dca37fae6c40843fe98033a2e77a412a667407f36a7113&scene=27#wechat_redirect) + +23、[吹爆了这个可视化神器,上手后直接开大~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495182&idx=1&sn=d8e913a808796e6fa793d58df26d209f&chksm=e8858eecdff207fac878d4b27894b5a2c69812d6803262e430d2244443a2c23e38d91a697556&scene=27#wechat_redirect) + ### 3.3 工具使用 1、[ 整理了 50个 IPython 的实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485613&idx=1&sn=70f89faf2573b3025edc73f525f22a0a&chksm=e886684fdff1e159509d30fd0a24854da39fb5a6ae55e0ed6e40b7971d3219968006f80aad7b#rd) @@ -468,6 +540,8 @@ 9、[8 个 Jupyter Notebook Tips,隐藏得太深了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494062&idx=2&sn=860539d7eae82c53791cd7a2eb005536&chksm=e885894cdff2005ae9169c7dfabe3760c19eded392ec18805f1d763ad8dce77c21150698178a&scene=27#wechat_redirect) +10、[PyCaret:几行代码轻松搞定从数据处理到模型部署](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495402&idx=2&sn=bb0664c6ac7995dfad4f4a1268e2e374&chksm=e8858e08dff2071e121d81fa564671ba4425814687fd2df3aa32898ffb3bdbe72d303bc9d66a&scene=27#wechat_redirect) + ## 04. 开发工具 @@ -531,6 +605,10 @@ 11、[用动画展示 Pycharm 十大实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486118&idx=1&sn=990c35adb86919bda2f2cb82e7c5fb9e&chksm=e8866a44dff1e35200244e3d369ad6c0f736d3511dcfb086b7641339e20ae62e35b8fd60bdd9&scene=27#wechat_redirect) +12、[太棒了!Jupyter 与PyCharm 完美融合,Jupytext 来啦!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496487&idx=1&sn=344204a2893c7bb54e9977f274c29fef&chksm=e88593c5dff21ad366d0fa3c2a1af3760f8f67b167ee8b29e016fe31adc66bb84c3e018e25ad&scene=27#wechat_redirect) + +13、[装上后这 14 个插件后,PyCharm 真的是无敌的存在](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495720&idx=1&sn=3233659fa9c975df9e95402a8c68b405&chksm=e88590cadff219dcd82f22d1d4684dda09a2db3469a0826ffe7605388fc37fb3a2c7cca49e0f&scene=27#wechat_redirect) + ### 4.2 VSCode 1、[这 21 个VSCode 快捷键,能让你的代码飞起来](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484893&idx=1&sn=421b544115efad388314ccd027761b40&scene=21#wechat_redirect) @@ -551,6 +629,10 @@ 9、[用 VS Code 写 Python,这8个扩展装上后无敌了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492505&idx=1&sn=ba354ab86500280d46038d2fa2f3572b&chksm=e885837bdff20a6dd2d4411719e44a3b88371b5006e9dc2b2580e6862f3161e70cb339240512&scene=27#wechat_redirect) +10、[有了这个VSCode神器,从此爱上调试代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497025&idx=2&sn=91734e86eb03b0ad8b299fa05fb89896&chksm=e88595a3dff21cb5638a874e6ea75aefa8539eb832e239d8103fd66584d1276edbe1d616c4fb&scene=27#wechat_redirect) + +11、[用 VS Code 写 Python,这几个插件是必装的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496552&idx=1&sn=ab85fc16d53f5d6bbcb0c030cae51402&chksm=e885938adff21a9ca75d485aab0980cb5cb7363d4eadaf0e1867dbb955c7296d1e16f7c7c367&scene=27#wechat_redirect) + ### 4.3 好用的库 1、[使用 Python 远程登陆服务器的最佳实践](https://mp.weixin.qq.com/s/aRXYAP9D9rgil-0_Etb0SQ) @@ -615,6 +697,14 @@ 31、[微软最强 Python 自动化工具开源了!不用写一行代码!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=1&sn=61d9f8b7557cb2ff517fde1a414b219d&chksm=e8858b3fdff2022954d383579b6392362e99e99af4a6309028dfa6ddd7bfc90a02c85d599b50&scene=27#wechat_redirect) +32、[Python 中 Mock 到底该怎么玩?一篇文章告诉你(超全)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496880&idx=2&sn=88899b73e7ca5f261be2b0ab245703ac&chksm=e8859452dff21d4473f5a73b7d87a800ce177581d94028b7911c0187b3f92bd2762d03195126&scene=27#wechat_redirect) + +33、[这款 Python 版终端资源监控器,火了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496153&idx=2&sn=8b53b63e04b2eff07a16bb8ef641aaca&chksm=e885913bdff2182dd176a5f6b1e324ed18f788830037c649915dc24df289139fd402e97c91b5&scene=27#wechat_redirect) + +34、[谁是 2020 年最强 Python 库?年度 Top 10 出炉!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496075&idx=1&sn=f05417a3c7adb638d394741a0b47c21e&chksm=e8859169dff2187ff80b3fd056160ae9d25009f7b373f82a5bc2c7d6f104a5f12ee52a623a3a&scene=27#wechat_redirect) + +35、[Python 算法模板库,Pythonista 找工作利器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495227&idx=2&sn=c7899ab854da814888ef853023d8b74c&chksm=e8858ed9dff207cf571d1819770952180f9c882b63e62025e819fe25538fe4f3a3148d73c228&scene=27#wechat_redirect) + ## 05. 网络基础 1、[点亮你的 HTTPS?原来这么简单!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=1&sn=46b095516bf3c0ebfc367ab64f8fd42c&chksm=e885824bdff20b5db2861a411a07653135ffbafaa1e772588bcf86cf9ad54433a0ff492355ad&scene=27#wechat_redirect) @@ -722,6 +812,8 @@ 5、[原来,我一直都不会用Windows](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494412&idx=2&sn=044b47f18f70e5179c5e5c84cb6f4ec4&chksm=e8858beedff202f8728666709b385c476fe41c7d41ed1c63e40afd198db5fe27fbeed2c1639b&scene=27#wechat_redirect) +6、[Chrome 和 Edge最大的威胁来了....](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495227&idx=1&sn=0175723a1ee9b54d6d40010aa28447b3&chksm=e8858ed9dff207cfc51eef087c07d4f6ee1bd341937c6228605fe9281d5c80ab7e02a7d617f4&scene=27#wechat_redirect) + ### 6.6 其他工具 1、[整理了 11 个好用的代码质量审核和管理工具](https://mp.weixin.qq.com/s/DH_TOA3Cr3y37yZ5F4bU5A) @@ -903,6 +995,12 @@ 11、[涨见识了,在终端执行 Python 代码的 6 种方式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247488283&idx=1&sn=24a78834ec3434a90ca7c3e6e972495c&chksm=e88673f9dff1faef908c43a41133f65695a68579e0edb2d7b15f131d9be26bcf919d66a7d9e3&scene=27#wechat_redirect) +12、[Python 炫技操作:推导式的五种写法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496358&idx=2&sn=1d4c4463663f546b5768945ce31598f7&chksm=e8859244dff21b52cd0dce2ca366782ea14bf194f66498b07b6125426cc324c21a13be5abd3a&scene=27#wechat_redirect) + +13、[Python 炫技操作:安装包的八种方法](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495287&idx=1&sn=1eb6f14782f17a552320b3d6af5e17f3&chksm=e8858e95dff207836d575464ccc2f1be06b8fa824dcd1589613fe8d6ca48ebb4578f57378858&scene=27#wechat_redirect) + + + ## 9. 云计算 @@ -942,6 +1040,10 @@ 4、[深扒微信多开的秘密后,我竟然发现了个 bug](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489704&idx=1&sn=a33559f22517c744367a660f78dbb25a&chksm=e886784adff1f15c1524f90487d6957c86dee0f5c499528630f43b62320b28e66977afe4d884&scene=27#wechat_redirect) +5、[让许多 Python “老玩家”心寒的 10 大槽点](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497032&idx=1&sn=0347edefbb9fc47d13c58379130aa327&chksm=e88595aadff21cbc3a6c8ad6752a75ce708244ed517fd9a3230efa2eb783927033977ba26b82&scene=27#wechat_redirect) + +6、[别瞎学了,这几门语言要被淘汰了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496522&idx=1&sn=1aaa60b8295378cfcbeaaaf528b13df0&chksm=e88593a8dff21abe290ee23821ed9cebb2aa68148548852319363b4de4663d0a5ab646451e5e&scene=27#wechat_redirect) + ## 12. 明哥的作品 1、[太赞了!《Python 黑魔法指南》终于面世了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486834&idx=1&sn=f5b94c3a520624786162f78246e60246&chksm=e8866d90dff1e486a3dae97aaf347e83834c8cf204849e5f9ae23dc9fdf0c470f97e0298cf74&scene=27#wechat_redirect) From 94653d3a2c6966759ee304b34d6205c80da5cb75 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Sun, 21 Mar 2021 19:41:32 +0800 Subject: [PATCH 139/147] update --- README.md | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 34e6517..8f1fdc2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ -目前目录更新内容至 2021/1/26 发的文章。 +目前目录更新内容至 2021/3/20 发的文章。 ## 01. 基础系列 @@ -118,6 +118,18 @@ 12、[一学就会的 Python 时间转化总结(超全)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496446&idx=1&sn=98c71ceefb10fb049ec33c3c1aa2bc6c&chksm=e885921cdff21b0adf359a9b75e5a44033a6cfa86a05cb6ed9e19f07c6b73b6cf97c4e3cd3c1&scene=27#wechat_redirect) +13、[适合新手的 SQLAlchemy 上手教程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498470&idx=2&sn=d0ee4a5367ad5195bcb8377ea6fc6101&chksm=e8859a04dff21312e8172de1e1986d41b4cf60d653d923954f48e111258a0e6019fc1bb50c1a&scene=27#wechat_redirect) + +14、[不服不行,Python 操作 JSON 的门道也这么多~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498220&idx=1&sn=249796b68dadafc9f85600fb03f8b0ae&chksm=e885990edff21018ad853a24abcd4a7c6780cc1740daddcd622bb233cdaa6d4834468284ac96&scene=27#wechat_redirect) + +15、[没想到吧?这货比 open 更适合读取文件](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498035&idx=1&sn=66e076de5448b41f252127b1f12a6d7e&chksm=e88599d1dff210c7f6f470b7917cf6b099febc93af25740562b2e2e0ec1ea42968128d26186b&scene=27#wechat_redirect) + +16、[Python输出简洁美观的文本化表格](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497768&idx=2&sn=2c49bcdf0264481f75658db0c0dbc54c&chksm=e88598cadff211dc7f140bc8c0d59d88169fde365567c82bcd8f7a8dccb4081072da83d88df0&scene=27#wechat_redirect) + +17、[使用 Python 操作 MySQL,这篇文章别错过~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497264&idx=2&sn=b11ff1be91e3297575d9eb9de3465257&chksm=e88596d2dff21fc4e404e0df21d096426df4eae33d6143bd851244cbf00adc3dc9072e9ac1a6&scene=27#wechat_redirect) + +18、[一篇文章教你如何用 Python 记录日志](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497049&idx=2&sn=0bb3de6705dd1dcef5352c2ff37991d5&chksm=e88595bbdff21cad17c69ee31955572ff64fed9ea38a8ef0a63472e89c97bc027d89101b8b57&scene=27#wechat_redirect) + ### 1.3 代码案例 1、[15个Pythonic的代码示例](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485036&idx=1&sn=24de1996a63bf25b0c0deec782f688cf&scene=21#wechat_redirect) @@ -146,6 +158,12 @@ 13、[再来 6 个例子教你重构 Python 代码](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494838&idx=2&sn=eeaf56eeaf9cc72f1340233bcd88b5a7&chksm=e8858c54dff20542f4712be4bd185f2e27e78e09bb77acb97e2be2d4c1329553be4abe25a3e2&scene=27#wechat_redirect) +14、[如何在Python里面实现链式调用?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498592&idx=2&sn=19b2f757b9887a67b0d2cfb8b0cb924e&chksm=e8859b82dff21294793e2b9a4eac9c375509796044d964aeb24c0448f9ec16b4cf3cad0102a9&scene=27#wechat_redirect) + +15、[Python 怎么捕获警告?(注意:不是捕获异常)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498517&idx=1&sn=2540f928893ffecc88a7aa86c330de91&chksm=e8859bf7dff212e1d8425ccff1c2dd043b5d611b291c4a5db729d7416189571d99e2c8d4fdee&scene=27#wechat_redirect) + +16、[a is b 为 True,a == b 一定为 True 吗?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497362&idx=1&sn=993ad8a3f86d467db967ea13d52175cc&chksm=e8859670dff21f66c76d2c4e0c61db9adda56824eba878be40e8b4ed8409c6922c8526f818fc&scene=27#wechat_redirect) + ## 02. 进阶系列 ### 2.1 进阶必学 @@ -160,7 +178,7 @@ 5、[几个使用装饰器的小技巧](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484930&idx=1&sn=ed731e32b6e95e7d83d74a544f97142a&scene=21#wechat_redirect) -6、[围观大神是如何用 Python 处理文件的?](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484905&idx=1&sn=7de0eaab0a4c9f8b44cba01d28ad7254&scene=21#wechat_redirect) +6、[围观大神是如何用 Python 处理文件的?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497532&idx=1&sn=bed756631d9c94c2eb2d57e0b29f6ab6&chksm=e88597dedff21ec899e90126cbe219be5fc738229bf69a02c490435c4ed57cbe32636198bb44&scene=27#wechat_redirect) 7、[Python进阶开发|元类编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485080&idx=1&sn=45e6d9995e4469d8b7bce2f800ac0f9b&scene=21#wechat_redirect) @@ -244,12 +262,14 @@ 47、[Python 从业十年的程序员,写的万字经验分享](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494942&idx=1&sn=84efbced575838b9b8e169e8c59383b7&chksm=e8858dfcdff204eaf7d251ddf1ea69655bd5eb69da596a4970c845f912c2ac9e3f2f18e5e7b5&scene=27#wechat_redirect) +48、[Python 优化机制 "常量折叠" 是究竟是怎么回事?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497951&idx=2&sn=e54c8412c056e98a34a4ce622915221d&chksm=e885983ddff2112b359c3e2a3856357a3fcccc24e5825fa0a6b972d0f5c64662df363c8d705e&scene=27#wechat_redirect) + ### 2.2 包的管理 1、[最全的 pip 使用指南,50% 你可能没用过](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484827&idx=1&sn=df0923856c820e10baca20c9873b336b&scene=21#wechat_redirect) -2、[全面学习 Python 包:包的构建与分发](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485349&idx=1&sn=8d6da555a2852a14506a491c4cf9a234&scene=21#wechat_redirect) +2、[花了两天,终于把 Python 的 setup.py 给整明白了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497217&idx=1&sn=acce69f1f39f2688056fd72a9d7044bf&chksm=e88596e3dff21ff5bf1c0e6f8a26e218458aa079d987810811f2d1ebd3704cdb1f35881229c8&scene=27#wechat_redirect) 3、[深入探讨 Python 的import机制:实现远程导入模块](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484838&idx=1&sn=1e6fbf5d7546902c6965c60383f7b639&scene=21#wechat_redirect) @@ -281,6 +301,8 @@ 17、[解锁装包新姿势,这个场景下 pip 真的难用~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494820&idx=1&sn=24ceaa11376cbfa5a4d48adc076603db&chksm=e8858c46dff20550d0124ef1bf1e481e14ffcf485e8c49aa77f6f9541f6163850538e4af05f9&scene=27#wechat_redirect) +18、[Python 的 import 居然这么有料](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497490&idx=1&sn=8667466d2dcaee300e990b0d4462e485&chksm=e88597f0dff21ee6787df0506a0c4b354df6b960c2c8dbf349ead6e43557230ae81fa0c661d7&scene=27#wechat_redirect) + ### 2.3 性能优化 1、[Python高效代码实践:性能、内存和可用性](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485161&idx=1&sn=07625f06db330897bc8bfee880ceac18&scene=21#wechat_redirect) @@ -309,6 +331,8 @@ 13、[超干分享!如何提高Python的运行速度?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494621&idx=2&sn=00c8aa00a85f8710fada38d35b2c8388&chksm=e8858b3fdff2022991a0024757c64faaed81da830cbbf1fa62b6b218d7271b0837b11922c1f0&scene=27#wechat_redirect) +14、[快亦有道!让 Python 变快的 5个方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497409&idx=2&sn=12667ea2e03ecb9db03478b17778ffea&chksm=e8859623dff21f358e56375becfa2ea8873235c2f25a216c36ffbd7a76ce2f13c7cbc269d58d&scene=27#wechat_redirect) + ### 2.4 并发编程 1、[并发编程01|从性能角度来初探并发编程](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485074&idx=1&sn=a859c6ab1d9b95c30c9f8b06f9489887&scene=21#wechat_redirect) @@ -411,6 +435,8 @@ 11、[别去送死了,你这样写爬虫,早晚得进去~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495216&idx=2&sn=648a296205fb3310287d95907d5126ef&chksm=e8858ed2dff207c4c42eb8b224bfe4f68186108642afd8ad7dfddef2628ff2c8f93170dbd948&scene=27#wechat_redirect) +12、[如何在 APP 上爬取数据?多图教程带你实操](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498688&idx=2&sn=4125d37e4f552bb0fd186fd066c07eb6&chksm=e8859b22dff21234d15301eeeaef6f61acce0f9c6020285029e61448357b6703a53a51984d12&scene=27#wechat_redirect) + ### 2.9 实用系列 1、[你抢不到的火车票,我帮你!](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484972&idx=1&sn=19ac660b61f046c5d419acdae7f394a6&scene=21#wechat_redirect) @@ -459,6 +485,20 @@ 23、[纯Python方案实现中英文全文搜索](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494874&idx=2&sn=f515724a52613a875869e6aa31ae3e78&chksm=e8858c38dff2052ede9f42592fb59446b68bb3fff84870601c36ee64b864527d25cb9619ccae&scene=27#wechat_redirect) +24、[两行 Python 代码,精准识别一张图片的格式](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497854&idx=2&sn=ca9066d3d7e4f3a0e8dfa18429683b30&chksm=e885989cdff2118a81ecd769cf1443e252d5347a7c76e7d5b2d9c494d0afa2dbda5fbe467897&scene=27#wechat_redirect) + +25、[明哥放大招! 这下看你们还怎么搬运我的文章 ~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498344&idx=1&sn=c3707e3058f444fc42e373155f82c514&chksm=e8859a8adff2139c790e5240dd4339b5cf7493e959eba0ddff5738a2432ff287ba6488b59b18#rd) + +26、[如何在手机上配置 Python 环境](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497813&idx=2&sn=b4b09859f7b175e0a60ca7e61131d475&chksm=e88598b7dff211a124f85f82db20a3c7742dce30f7218dcd00ac393cc49ad7754b2401f8e1bd&scene=27#wechat_redirect) + +27、[在手机上运行 Python,这款工具比 QPython 还好用~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497684&idx=2&sn=62756dddcd6d8a8e29326d33b1d9e70e&chksm=e8859736dff21e20cf67f8c29e87cdcd2849f04bb9567dc2f5c2dcc6a00cbd8aeeecaace335f&scene=27#wechat_redirect) + +28、[情人节表白神器,v2.0 版本](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497554&idx=1&sn=032200c99fcb0841246cb5eed74dc73b&chksm=e88597b0dff21ea6b20f8301a3c2e2dfdf914d49f23155f81113c90b7b6135802f252481a318&scene=27#wechat_redirect) + +29、[5个无聊透顶的 Python 程序](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497441&idx=1&sn=a6e7b24b13cee4c7f580587f0303a579&chksm=e8859603dff21f158b9560b6b397b13a26d71ef6c72d47d220c65812f5c0f9d7f15a3c209fe5&scene=27#wechat_redirect) + +30、[怎样用Python制作好玩的GIF动图?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497164&idx=2&sn=cfc237768c53716f4554ea89bbca3294&chksm=e885952edff21c381eb898a203bed86a1b96eba8e1cabecabf0c896489c5fc3a0022b0be8527&scene=27#wechat_redirect) + ## 03. 数据分析 @@ -472,6 +512,14 @@ 4、[实用的 Pandas 技巧,估计 80% 的人不知道](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496209&idx=2&sn=8f8db418261326c981cd27f57cc3fc5b&chksm=e88592f3dff21be543cc302a715ea39ae8a1adacb7342f45d5bcf2eca9c2b398b05955408597&scene=27#wechat_redirect) +5、[用Python 操作 Excel,这篇文章别错过了!(超全总结)](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498065&idx=1&sn=5f2bb43d9d960c2a7330353c4ac926a8&chksm=e88599b3dff210a528c3e6aac2ea1708b7aa5e3d58c49d12d63492271396ccebee8f0a4a6eae&scene=27#wechat_redirect) + +6、[别找了,这是 Pandas 最详细教程了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497846&idx=2&sn=9483146777ac86567e3933e6d5475e5f&chksm=e8859894dff2118208b517d0d9e753fbf8b1bb83995d36cf04694825972b6a82e195d706852d&scene=27#wechat_redirect) + +7、[再见 for 循环!pandas 提速 315 倍~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497472&idx=2&sn=df1089640d2beb3607518d0ea9f3e709&chksm=e88597e2dff21ef45a7c3b25e102bc665368f628d1b76decfbeeb18a4cd1a2605737c4c8fb21&scene=27#wechat_redirect) + +8、[Python 操作 Excel 库 xlwings 常用操作详解](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497164&idx=1&sn=281a25369e47ecba1951d4e92f01d5d0&chksm=e885952edff21c38f633910f3c2aac54f8e379707e70d9a70a1cdde5bfd4b7fde145315ea54f&scene=27#wechat_redirect) + ### 3.2 数据可视化 1、[可视化01|一图带你入门matplotlib](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485022&idx=1&sn=76d4270a15c430217588bd9f8be8303b&chksm=e88666bcdff1efaa35c6d685f19b04e62f58e768dc9d1819bcb9f8211f65d3fd83296259d923&token=1148998814&lang=zh_CN#rd) @@ -520,6 +568,10 @@ 23、[吹爆了这个可视化神器,上手后直接开大~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495182&idx=1&sn=d8e913a808796e6fa793d58df26d209f&chksm=e8858eecdff207fac878d4b27894b5a2c69812d6803262e430d2244443a2c23e38d91a697556&scene=27#wechat_redirect) +24、[Pygal,可导出矢量图的Python可视化利器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497951&idx=1&sn=7cce71d5c34fdeb89509e97817a5f0af&chksm=e885983ddff2112bc76bd9a4dbe14f407c5fe8b2ea0010b353e760b59c4476603f1d82a26eac&scene=27#wechat_redirect) + +25、[这可能是 Python里最强的绘制地图神器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497813&idx=1&sn=c5034f6ecf03fe3ccb6771b39faa8230&chksm=e88598b7dff211a11145c44db19f50df244ab3c2b740e1d51ead51b25d7fa9643660ca5cc29a&scene=27#wechat_redirect) + ### 3.3 工具使用 1、[ 整理了 50个 IPython 的实用技巧](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485613&idx=1&sn=70f89faf2573b3025edc73f525f22a0a&chksm=e886684fdff1e159509d30fd0a24854da39fb5a6ae55e0ed6e40b7971d3219968006f80aad7b#rd) @@ -542,6 +594,8 @@ 10、[PyCaret:几行代码轻松搞定从数据处理到模型部署](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495402&idx=2&sn=bb0664c6ac7995dfad4f4a1268e2e374&chksm=e8858e08dff2071e121d81fa564671ba4425814687fd2df3aa32898ffb3bdbe72d303bc9d66a&scene=27#wechat_redirect) +11、[这个 Jupyter 插件,用起来就像 Excel 一样简单](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498500&idx=2&sn=672a1da1a9957c84a19802842df5b5d9&chksm=e8859be6dff212f0754dd0a3820241be3af34d72e6dc32e7d83d1f87198bd549ccfd66c55029&scene=27#wechat_redirect) + ## 04. 开发工具 @@ -579,7 +633,9 @@ 17、[15 款Python编辑器的优缺点,别再问我“选什么编辑器”啦!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492759&idx=1&sn=6c6e6606fd9da9d2b951bda9e8a1b223&chksm=e8858475dff20d638f4b731a44dd48fe01a68f16480bcaed2516fc7d3d56c61cc6c62afccb9d&scene=27#wechat_redirect) +18、[用了三年的 pdb,没想到还能这么调试](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498550&idx=1&sn=d2d0ba8987509a67ba40c19f7dcfd719&chksm=e8859bd4dff212c2f4d6f07c42ae81af046aeb8611c3c869143ed7e4ebac03fd011ddc8b88e6&scene=27#wechat_redirect) +19、[调试 Python 代码,可别再用 Print了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497083&idx=1&sn=47fa23a2104c2e75ad71c56b6d89b84d&chksm=e8859599dff21c8f0f832e83f3e5220bef96fbba7514863e4e8fcd178818258f024744902806&scene=27#wechat_redirect) ### 4.1 PyCharm @@ -609,6 +665,8 @@ 13、[装上后这 14 个插件后,PyCharm 真的是无敌的存在](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495720&idx=1&sn=3233659fa9c975df9e95402a8c68b405&chksm=e88590cadff219dcd82f22d1d4684dda09a2db3469a0826ffe7605388fc37fb3a2c7cca49e0f&scene=27#wechat_redirect) +14、[卸载 PyCharm!这才是 Python 小白的最理想的 IDE](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497739&idx=1&sn=2e4795f4ddc784cfa796fb59b3d0e707&chksm=e88598e9dff211ffbe2cea5f574c8ee6117f4e7cd5d4869a804422139360421e4803df270f19&scene=27#wechat_redirect) + ### 4.2 VSCode 1、[这 21 个VSCode 快捷键,能让你的代码飞起来](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247484893&idx=1&sn=421b544115efad388314ccd027761b40&scene=21#wechat_redirect) @@ -633,6 +691,10 @@ 11、[用 VS Code 写 Python,这几个插件是必装的](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496552&idx=1&sn=ab85fc16d53f5d6bbcb0c030cae51402&chksm=e885938adff21a9ca75d485aab0980cb5cb7363d4eadaf0e1867dbb955c7296d1e16f7c7c367&scene=27#wechat_redirect) +12、[出炉了! 2021 年将火爆的 10款 VSCode 扩展插件](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497928&idx=1&sn=0ec6994930f1cf8bd3a1ed8d8404eb18&chksm=e885982adff2113c94ba7c0ddc989c174daeedd159359d9289b48006e0c6b1d0aa8b87980ef1&scene=27#wechat_redirect) + +13、[神器 VS Code,超详细Python配置使用指南](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497249&idx=1&sn=00a13cb6099f487ce3f88f27923d6a3b&chksm=e88596c3dff21fd504d3e469a0eddc00f8febdeb95b7b8c7483dfad7ef84382d2cc1f3b16f2c&scene=27#wechat_redirect) + ### 4.3 好用的库 1、[使用 Python 远程登陆服务器的最佳实践](https://mp.weixin.qq.com/s/aRXYAP9D9rgil-0_Etb0SQ) @@ -705,6 +767,10 @@ 35、[Python 算法模板库,Pythonista 找工作利器](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247495227&idx=2&sn=c7899ab854da814888ef853023d8b74c&chksm=e8858ed9dff207cf571d1819770952180f9c882b63e62025e819fe25538fe4f3a3148d73c228&scene=27#wechat_redirect) +36、[终于把所有的Python库,都整理出来啦!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498689&idx=1&sn=66743481223ff48bcb5c8ed257b95754&chksm=e8859b23dff212350492116b1cdc3527f969fbb62cac8309ef91932e02842f35b5f7f4d08661&scene=27#wechat_redirect) + +37、[Python 超级强大的模式匹配工具—Pampy](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498439&idx=1&sn=1613fd0fb2154bb3a9b5caefb1225b41&chksm=e8859a25dff21333272d75f14badb8491295351b0b3542db7cc1506a7462359ed297ce228710&scene=27#wechat_redirect) + ## 05. 网络基础 1、[点亮你的 HTTPS?原来这么简单!!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247492265&idx=1&sn=46b095516bf3c0ebfc367ab64f8fd42c&chksm=e885824bdff20b5db2861a411a07653135ffbafaa1e772588bcf86cf9ad54433a0ff492355ad&scene=27#wechat_redirect) @@ -753,6 +819,8 @@ 8、[不想装系统?这8个网站让你在线体验 Linux](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486303&idx=1&sn=fc6c9b73fcb12c5c0c8257e223c8f657&chksm=e8866bbddff1e2ab5edf8b3d4783bcc65975c0385c140d1c954e7d4949e334b9d6ed92ae98a2&scene=27#wechat_redirect) +9、[比虚拟机更轻量,比 Docker 和 WSL 更简单的 Linux 环境](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498054&idx=2&sn=1aa6a118f214f3488c1188cdb2471834&chksm=e88599a4dff210b273db8dffda010bca3f1eb15219b7c268ea941bf1d036cec3fed8fac9f3db&scene=27#wechat_redirect) + ### 6.2 版本管理 1、[关于 Git 和 GitHub,你所不知道的十件事](https://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485232&idx=2&sn=c3314e79af9029bdd76ae187f7b4eb19&scene=21#wechat_redirect) @@ -779,6 +847,8 @@ 12、[5 个 Git 工作流,改善你的开发流程](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494114&idx=2&sn=f75412a52b9c36e268decf013fffa6de&chksm=e8858900dff2001686dc51e3227a9565f7e97033539a1f2af3d9f38aad87b55a8b5d2ea6d405&scene=27#wechat_redirect) +13、[提高国内访问 GitHub 的速度的 9 种方案](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498432&idx=2&sn=d7f398a5598e9dd6636961387a64860f&chksm=e8859a22dff21334695d1495ead5adaf9a8102d1ba85bdfe0cd8b51fc6a8db08742bec95819e&scene=27#wechat_redirect) + ### 6.3 数据库 @@ -836,6 +906,10 @@ 10、[用 Python 使用 Google Colab?岂止是炫酷](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247494533&idx=2&sn=58e2fd5fc63936d89853c5d550293d00&chksm=e8858b67dff2027179fd492d796d537bad57caf830e0aa3fbd51061cd91bd7c7f3f2ce56eb80&scene=27#wechat_redirect) +11、[自从用了这个神器,我再也不想写代码了...](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498473&idx=1&sn=448af801d90b29a69cb79b4089868f97&chksm=e8859a0bdff2131dd9fa0d6923fb8ae1de9e7311691a7558499cf995c0e4be8fd036031ee1c7&scene=27#wechat_redirect) + +12、[牛逼至极!用这个神器看代码太舒服了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497577&idx=1&sn=d0061deb47fa2c6441eb7dd11272af5d&chksm=e885978bdff21e9d4bc0a8747d9b2f6b04288095c7a019cfe1214a4ae382de657076313387bf&scene=27#wechat_redirect) + ## 07. 代码优化 ### 7.1 算法讲解 @@ -1030,6 +1104,8 @@ 9、[Python这么慢,为啥大公司还在用?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247489132&idx=1&sn=55dda8a6a10429b3a7016393bc058493&chksm=e886768edff1ff981d67b2ecfe52ebf5b8dbc0085289e6bd3f8a6c6631ee6fc4a633fb6bc4b8&scene=27#wechat_redirect) +10、[一个中科大差生的 8年 程序员工作总结](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497523&idx=1&sn=725c1167873927aea519f690f3c106ec&chksm=e88597d1dff21ec709eeba54a9fb35318afcac3cb3b235e0ab831fd40f9917dc2acb40d1bbb3&scene=27#wechat_redirect) + ## 11. 通用文章 1、[假如有人把支付宝存储服务器炸了,你的存款会怎样?](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247485171&idx=1&sn=d6e2e24f68f3c6eb35e880f32e12d4ab&chksm=e8866611dff1ef0791cf5aae05697592cb804010aecd783a8592f7a90a387645fe4b47a376ea&scene=27#wechat_redirect) @@ -1044,6 +1120,10 @@ 6、[别瞎学了,这几门语言要被淘汰了!](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247496522&idx=1&sn=1aaa60b8295378cfcbeaaaf528b13df0&chksm=e88593a8dff21abe290ee23821ed9cebb2aa68148548852319363b4de4663d0a5ab646451e5e&scene=27#wechat_redirect) +7、[是的,Python是慢,但我不在乎](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247497752&idx=1&sn=0d207936a803b414b04d13bb167124ba&chksm=e88598fadff211ecd77676506b5f039c0fbd8bd32eb5f68df2c41a277526304dc89f3d6f7dad&scene=27#wechat_redirect) + +8、[当今全球最厉害的14位程序员,说没听过简直离谱~](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247498702&idx=1&sn=8b5a405de0649de1bf52235956840af7&chksm=e8859b2cdff2123a2f6168fb846781f2ba678ac4b70fd01513c2547b14b4d1a053ca0b67e5f6&scene=27#wechat_redirect) + ## 12. 明哥的作品 1、[太赞了!《Python 黑魔法指南》终于面世了](http://mp.weixin.qq.com/s?__biz=MzIzMzMzOTI3Nw==&mid=2247486834&idx=1&sn=f5b94c3a520624786162f78246e60246&chksm=e8866d90dff1e486a3dae97aaf347e83834c8cf204849e5f9ae23dc9fdf0c470f97e0298cf74&scene=27#wechat_redirect) From d72520292dc0456e8f52a25e4d93c8c6cf1213b8 Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 7 Jul 2021 12:59:05 +0800 Subject: [PATCH 140/147] add sitemap and robots.txt --- requirements.txt | 1 + source/conf.py | 2 ++ source/robots.txt | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 source/robots.txt diff --git a/requirements.txt b/requirements.txt index f0b035b..2058b9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,3 +38,4 @@ sphinxcontrib-htmlhelp==1.0.2 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.2 sphinxcontrib-serializinghtml==1.1.3 +sphinx-sitemap==2.2.0 diff --git a/source/conf.py b/source/conf.py index 6fb6b49..284d9d4 100755 --- a/source/conf.py +++ b/source/conf.py @@ -150,3 +150,5 @@ # Options for extensions. disqus_shortname = 'iswbm' # Add this line to conf.py. +html_baseurl = 'http://pythontime.iswbm.com' +html_extra_path = ["robots.txt"] diff --git a/source/robots.txt b/source/robots.txt new file mode 100644 index 0000000..b10196d --- /dev/null +++ b/source/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Sitemap: http://pythontime.iswbm.com/sitemap.xml From 91e76b1d5626dc665c101b1da1f187c602ff32df Mon Sep 17 00:00:00 2001 From: BingmingWong Date: Wed, 7 Jul 2021 21:31:59 +0800 Subject: [PATCH 141/147] update --- source/c09/c09_07.md | 3 ++- source/conf.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/c09/c09_07.md b/source/c09/c09_07.md index 1d82779..14668f8 100644 --- a/source/c09/c09_07.md +++ b/source/c09/c09_07.md @@ -16,4 +16,5 @@ document.body.contentEditable='true' ``` -在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 Ctrl+Z 后退 。 \ No newline at end of file +在知乎上的回答设置了禁止转载后,就算你执行了上面的命令,也是无法复制,但是可以剪切,剪切完后你再 Ctrl+Z 后退 。 + diff --git a/source/conf.py b/source/conf.py index 284d9d4..fdcb6f7 100755 --- a/source/conf.py +++ b/source/conf.py @@ -28,7 +28,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['chinese_search','sphinx.ext.mathjax'] +extensions = ['chinese_search','sphinx.ext.mathjax', 'sphinx_sitemap'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From f1180a257195abd895615f1c1426e61bde06f5b3 Mon Sep 17 00:00:00 2001 From: iswbm Date: Wed, 29 Sep 2021 14:28:12 +0800 Subject: [PATCH 142/147] update --- source/c02/c02_04.rst | 3 ++- source/c08/c08_03.md | 3 ++- source/c08/c08_03.rst | 3 ++- source/c08/c08_16.md | 2 -- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source/c02/c02_04.rst b/source/c02/c02_04.rst index 38e5da5..408c43a 100755 --- a/source/c02/c02_04.rst +++ b/source/c02/c02_04.rst @@ -339,7 +339,8 @@ Queue.task_done(),说明队列这个任务已经结束了。 当队列内部的任务计数器归于零时,调用 Queue.join() 就不会再阻塞了。 -要理解这个过程,请参考 http://pythontime.iswbm.com/en/latest/c02/c02_06.html +要理解这个过程,请参考 +http://pythontime.iswbm.com/en/latest/c02/c02_06.html 里自定义线程池的的例子。 4. 消息队列的先进先出 diff --git a/source/c08/c08_03.md b/source/c08/c08_03.md index 18a1d18..ef1e329 100644 --- a/source/c08/c08_03.md +++ b/source/c08/c08_03.md @@ -87,7 +87,8 @@ virt-install --name ubuntu-16.04 \ - listen='0.0.0.0'> + + diff --git a/source/c08/c08_03.rst b/source/c08/c08_03.rst index 758198f..f456eb5 100755 --- a/source/c08/c08_03.rst +++ b/source/c08/c08_03.rst @@ -99,7 +99,8 @@ - listen='0.0.0.0'> + + diff --git a/source/c08/c08_16.md b/source/c08/c08_16.md index ad70dbe..eb9283b 100644 --- a/source/c08/c08_16.md +++ b/source/c08/c08_16.md @@ -246,8 +246,6 @@ $ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_avera $ nova meta b1575f81-0a33-4872-995e-2c2dd48d52c8 set __system__vif_inbound_average=12500 __system__vif_inbound_burst=10000 __system__vif_outbound_average=12500 __system__vif_outbound_burst=10000 ``` - - ## 3. 带宽单位换算 带宽,英文名 Bandwidth,在不同领域的含义各不相同,而在网络服务中,带宽是指单位时间内的流经数据量。 From 22a61b65d7d9f10304731e6c55afa701959662f6 Mon Sep 17 00:00:00 2001 From: iswbm Date: Sat, 11 Dec 2021 20:43:39 +0800 Subject: [PATCH 143/147] update --- source/.DS_Store | Bin 12292 -> 0 bytes source/aboutme.rst | 0 source/c01/c01_01.rst | 0 source/c01/c01_02.rst | 0 source/c01/c01_03.rst | 0 source/c01/c01_04.rst | 0 source/c01/c01_05.rst | 0 source/c01/c01_06.rst | 0 source/c01/c01_07.rst | 0 source/c01/c01_08.rst | 0 source/c01/c01_09.rst | 0 source/c01/c01_10.rst | 0 source/c01/c01_11.rst | 0 source/c01/c01_12.rst | 0 source/c01/c01_13.rst | 0 source/c02/c02_01.rst | 0 source/c02/c02_02.rst | 0 source/c02/c02_03.rst | 0 source/c02/c02_04.rst | 0 source/c02/c02_05.rst | 0 source/c02/c02_06.rst | 0 source/c02/c02_07.rst | 0 source/c02/c02_08.rst | 0 source/c02/c02_09.rst | 0 source/c02/c02_10.rst | 0 source/c02/c02_11.rst | 0 source/c03/c03_01.rst | 0 source/c03/c03_02.rst | 0 source/c03/c03_03.rst | 0 source/c03/c03_04.rst | 0 source/c04/c04_01.rst | 0 source/c04/c04_02.rst | 0 source/c04/c04_03.rst | 0 source/c04/c04_04.rst | 0 source/c04/c04_05.rst | 0 source/c04/c04_06.rst | 0 source/c04/c04_07.rst | 0 source/c04/c04_08.rst | 0 source/c04/c04_09.rst | 0 source/c04/c04_10.rst | 0 source/c05/c05_01.rst | 0 source/c05/c05_02.rst | 0 source/c05/c05_03.rst | 0 source/c06/c06_01.rst | 0 source/c06/c06_02.rst | 0 source/c06/c06_03.rst | 0 source/c06/c06_04.rst | 0 source/c06/c06_05.rst | 0 source/c06/c06_06.rst | 0 source/c07/c07_01.rst | 0 source/c07/c07_02.rst | 0 source/c07/c07_03.rst | 0 source/c07/c07_04.rst | 0 source/c07/c07_05.rst | 0 source/c07/c07_06.rst | 0 source/c07/c07_07.rst | 0 source/c08/c08_01.rst | 0 source/c08/c08_02.rst | 0 source/c08/c08_03.rst | 0 source/chapters/p01.rst | 0 source/chapters/p02.rst | 0 source/chapters/p03.rst | 0 source/chapters/p04.rst | 0 source/chapters/p05.rst | 0 source/chapters/p06.rst | 0 source/chapters/p07.rst | 0 source/chapters/p08.rst | 0 source/chapters/p10.rst | 0 source/conf.py | 0 source/index.rst | 0 source/lc01/1-10.rst | 0 source/leetcode/leetcode.rst | 0 source/preface.rst | 0 source/roadmap.rst | 0 74 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 source/.DS_Store mode change 100755 => 100644 source/aboutme.rst mode change 100755 => 100644 source/c01/c01_01.rst mode change 100755 => 100644 source/c01/c01_02.rst mode change 100755 => 100644 source/c01/c01_03.rst mode change 100755 => 100644 source/c01/c01_04.rst mode change 100755 => 100644 source/c01/c01_05.rst mode change 100755 => 100644 source/c01/c01_06.rst mode change 100755 => 100644 source/c01/c01_07.rst mode change 100755 => 100644 source/c01/c01_08.rst mode change 100755 => 100644 source/c01/c01_09.rst mode change 100755 => 100644 source/c01/c01_10.rst mode change 100755 => 100644 source/c01/c01_11.rst mode change 100755 => 100644 source/c01/c01_12.rst mode change 100755 => 100644 source/c01/c01_13.rst mode change 100755 => 100644 source/c02/c02_01.rst mode change 100755 => 100644 source/c02/c02_02.rst mode change 100755 => 100644 source/c02/c02_03.rst mode change 100755 => 100644 source/c02/c02_04.rst mode change 100755 => 100644 source/c02/c02_05.rst mode change 100755 => 100644 source/c02/c02_06.rst mode change 100755 => 100644 source/c02/c02_07.rst mode change 100755 => 100644 source/c02/c02_08.rst mode change 100755 => 100644 source/c02/c02_09.rst mode change 100755 => 100644 source/c02/c02_10.rst mode change 100755 => 100644 source/c02/c02_11.rst mode change 100755 => 100644 source/c03/c03_01.rst mode change 100755 => 100644 source/c03/c03_02.rst mode change 100755 => 100644 source/c03/c03_03.rst mode change 100755 => 100644 source/c03/c03_04.rst mode change 100755 => 100644 source/c04/c04_01.rst mode change 100755 => 100644 source/c04/c04_02.rst mode change 100755 => 100644 source/c04/c04_03.rst mode change 100755 => 100644 source/c04/c04_04.rst mode change 100755 => 100644 source/c04/c04_05.rst mode change 100755 => 100644 source/c04/c04_06.rst mode change 100755 => 100644 source/c04/c04_07.rst mode change 100755 => 100644 source/c04/c04_08.rst mode change 100755 => 100644 source/c04/c04_09.rst mode change 100755 => 100644 source/c04/c04_10.rst mode change 100755 => 100644 source/c05/c05_01.rst mode change 100755 => 100644 source/c05/c05_02.rst mode change 100755 => 100644 source/c05/c05_03.rst mode change 100755 => 100644 source/c06/c06_01.rst mode change 100755 => 100644 source/c06/c06_02.rst mode change 100755 => 100644 source/c06/c06_03.rst mode change 100755 => 100644 source/c06/c06_04.rst mode change 100755 => 100644 source/c06/c06_05.rst mode change 100755 => 100644 source/c06/c06_06.rst mode change 100755 => 100644 source/c07/c07_01.rst mode change 100755 => 100644 source/c07/c07_02.rst mode change 100755 => 100644 source/c07/c07_03.rst mode change 100755 => 100644 source/c07/c07_04.rst mode change 100755 => 100644 source/c07/c07_05.rst mode change 100755 => 100644 source/c07/c07_06.rst mode change 100755 => 100644 source/c07/c07_07.rst mode change 100755 => 100644 source/c08/c08_01.rst mode change 100755 => 100644 source/c08/c08_02.rst mode change 100755 => 100644 source/c08/c08_03.rst mode change 100755 => 100644 source/chapters/p01.rst mode change 100755 => 100644 source/chapters/p02.rst mode change 100755 => 100644 source/chapters/p03.rst mode change 100755 => 100644 source/chapters/p04.rst mode change 100755 => 100644 source/chapters/p05.rst mode change 100755 => 100644 source/chapters/p06.rst mode change 100755 => 100644 source/chapters/p07.rst mode change 100755 => 100644 source/chapters/p08.rst mode change 100755 => 100644 source/chapters/p10.rst mode change 100755 => 100644 source/conf.py mode change 100755 => 100644 source/index.rst mode change 100755 => 100644 source/lc01/1-10.rst mode change 100755 => 100644 source/leetcode/leetcode.rst mode change 100755 => 100644 source/preface.rst mode change 100755 => 100644 source/roadmap.rst diff --git a/source/.DS_Store b/source/.DS_Store deleted file mode 100644 index 30e178227a51cc5fee5acff79a4774f68ec2d071..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12292 zcmeHNYj7J^6+Xvy;@x!~Yvah0VkK%EH;yYOwk1ban;7{SyJ<+kPVA&<6g}3l23c~o zlAXk);}Qa8rY)~RN&_9B%rG;+Ao+w*<*+ywv}$i}(=+5kYJjpgbZDy9>69i0>qE4~?*U(lCQRpn`|6`H4L|o5wcK#Bps%uuQu9YN3 zQr0TBjnAYO#xvneRv!qbZ)Il(!5 zZFp14MN>4U7$}-kxtp0&9yIbLk&EVlqB)>(Wj1kP3HjA2chd(;AEgVH!TAJ^lNgm!2l9?Q%e3F}cU z8H*=R9;I1yG83PUYiYS2L-Is2JgZ$%*R5Y`YqHy?vXS`h*?8u(+9FB3bYrt!IToW! z?oY*7tC;$TB=Z_~v)z6xs~Jpdw2v7Lwzk@p37z#UY#45DSCol&(sW+cWzlrUPQ`vG z5sqjHH71Meczud}a$by>y3Z)=4Jh`hXlizrx~$iu$0**@XSW|`SH2~zXAaD1N$S<( z#^nd7S4UV~e=NrOB(((F#FLdcBs>K_g=VD?9X*xw0WwOG&LwGo?f6K*4jwc`!gfxFO;0fN~e4iU`u;z4{nj^hL# z#*hIu4U?F{+cAw9Jd1bW65fgD@NT>pKZGA9=-r2(#0Lec@?aY^P-bNKxdPbc$J5C~ zDtVGh7&HY0ePfAK)CglTJ#iCl_rJmu9p$a2V|p|~;l4sqkE!A3sj9r;u2L&(s%Yge z|NGwZqCio^^~v1ZQixcRcUUX^?<1BnOCi;{xY*_G@_F1HJZ9O|?c3#Xclo-?qn0cj zNom=rsxsD^FsarZ#PM$R+M3Phc&Z9gbq(Lkz+knB3-Q zXP$t&B2m&IlW)*v?vZ$jqg&?gL3k=rI%%Ndy+)1lEr?8x%H<$M81pTc*jY%#l~oIfb3ayYT(w zPc9OLF#`J{K8oML@8b9H`}kx089vA13waN-U!3Rm_Pc4gSs+R0i^tMftgq(xZChM0>)-o+7B_T%te2VPp z-yu`lg7GIxMf_O<3BKIcWwIl>EU}ZwPNQFj_lK@fYR`zX#J-Mtn6?=np zx29HUy4x__!7Lx6%B|?GOInGCVePFE_`eYv2x2 zl)WS(4MFK+>)~CUJ>`P(0r3=;6_iYHn(mX-dm<;_O;tq#vtlU3Hd6n(35C=*C`wVt zV>y!LBo)g5uKu>R+KNIUOk0+QLRblf zLTp8$5LQH?5L-?~a)M@s6biBB^+zETV(YU7+5Fa|NciOknr*hqN^*svkT1a_@GVl1 zPr*-!%~>eqIb!q|cqrsWcnMzSp^!RUbCscx^TgqIT}3FQ9FI8-X@x}02rmtZFx9x( z?R2UPGm=nxTb&A@60y3rtyEVtwhY^!82&MrQrQIe?%d~j; z(0EN&OoQjGu4?{If3S)+N^ML73@`uvA2+gg)(BW5@WvbgtQi^~8lZd2EzX#4ldZM8 zX*)q1OU!PG9Z;fUz7$V6Uy7%kFU3=yTZ>{gLBn=Q Date: Wed, 19 Jun 2024 21:51:01 +0800 Subject: [PATCH 144/147] update requirements.txt to python3.10.12 --- requirements.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2058b9e..08d22be 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,22 +1,21 @@ alabaster==0.7.12 argh==0.26.2 -Babel==2.7.0 +Babel==2.9.1 certifi==2019.6.16 chardet==3.0.4 docutils==0.14 -idna==2.8 imagesize==1.1.0 -Jinja2==2.10.1 +Jinja2==2.11.3 livereload==2.6.1 MarkupSafe==1.1.1 packaging==19.0 pathtools==0.1.2 port-for==0.3.1 -Pygments==2.4.2 +Pygments==2.7.4 pyparsing==2.4.0 pytz==2019.1 -PyYAML==5.1.1 -requests==2.22.0 +PyYAML==6.0.1 +requests==2.32.3 six==1.12.0 snowballstemmer==1.9.0 Sphinx==2.1.2 @@ -29,7 +28,7 @@ sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.2 sphinxcontrib-serializinghtml==1.1.3 tornado==6.0.3 -urllib3==1.25.3 +urllib3==2.2.2 watchdog==0.9.0 sphinxcontrib-disqus==1.1.0 sphinxcontrib-applehelp==1.0.1 @@ -39,3 +38,4 @@ sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.2 sphinxcontrib-serializinghtml==1.1.3 sphinx-sitemap==2.2.0 +sphinx-multiversion==0.2.4 From 3ad7885224e45768e06a8d6f5ec34f71710ac374 Mon Sep 17 00:00:00 2001 From: iswbm Date: Wed, 19 Jun 2024 22:26:03 +0800 Subject: [PATCH 145/147] support sphinx-multiversion --- rebuild.sh | 13 +++++++++++++ source/_templates/versions.html | 27 +++++++++++++++++++++++++++ source/conf.py | 27 ++++++++++++--------------- 3 files changed, 52 insertions(+), 15 deletions(-) create mode 100755 rebuild.sh create mode 100644 source/_templates/versions.html diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 0000000..3ceedd8 --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,13 @@ +cat << EOF >/usr/local/lib/python3.10/site-packages/sphinx_rtd_theme/comments.html + + + +EOF + +rm -rf build/ && sphinx-multiversion source build/html && cp -rf build/html/master/* build/html/ diff --git a/source/_templates/versions.html b/source/_templates/versions.html new file mode 100644 index 0000000..31a1257 --- /dev/null +++ b/source/_templates/versions.html @@ -0,0 +1,27 @@ +{%- if current_version %} +
    + + Other Versions + v: {{ current_version.name }} + + +
    + {%- if versions.tags %} +
    +
    Tags
    + {%- for item in versions.tags %} +
    {{ item.name }}
    + {%- endfor %} +
    + {%- endif %} + {%- if versions.branches %} +
    +
    Branches
    + {%- for item in versions.branches %} +
    {{ item.name }}
    + {%- endfor %} +
    + {%- endif %} +
    +
    +{%- endif %} diff --git a/source/conf.py b/source/conf.py index fdcb6f7..66e5159 100644 --- a/source/conf.py +++ b/source/conf.py @@ -28,7 +28,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['chinese_search','sphinx.ext.mathjax', 'sphinx_sitemap'] +extensions = ['chinese_search','sphinx.ext.mathjax', 'sphinx_sitemap', 'sphinx_multiversion'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -127,28 +127,25 @@ sys.path.append(os.path.abspath(_exts)) html_js_files = [ - 'js/readmore.js', + 'js/readmore.js', 'js/baidutongji.js', ] -# General configuration. -with open("/home/docs/checkouts/readthedocs.org/user_builds/pythoncodingtime/envs/latest/lib/python3.7/site-packages/sphinxcontrib/disqus.py", "r") as file: - content = file.read() - content=content.replace("sphinx.application", "sphinx.errors") - -with open("/home/docs/checkouts/readthedocs.org/user_builds/pythoncodingtime/envs/latest/lib/python3.7/site-packages/sphinxcontrib/disqus.py", "w") as file: - file.write(content) author = '王炳明' -copyright = '2020, Python编程时光' +copyright = '2020-2024, Python编程时光' exclude_patterns = ['_build'] -extensions = ['sphinxcontrib.disqus'] # Add to this list. master_doc = 'index' project = 'Python编程时光' -release = '1.0' -version = '1.0' # Options for extensions. -disqus_shortname = 'iswbm' # Add this line to conf.py. -html_baseurl = 'http://pythontime.iswbm.com' +html_baseurl = 'https://magic.iswbm.com' html_extra_path = ["robots.txt"] + +html_sidebars = { + '**': [ + 'versioning.html', + ], +} +smv_latest_version = 'master' +sitemap_url_scheme = "{link}" From 07ecd0ea6bc99c5d821c28fc560f551c0848d711 Mon Sep 17 00:00:00 2001 From: iswbm Date: Mon, 26 Jan 2026 22:12:53 +0800 Subject: [PATCH 146/147] update requirements.txt to python3.6.8 --- rebuild.sh | 2 +- requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebuild.sh b/rebuild.sh index 3ceedd8..319b303 100755 --- a/rebuild.sh +++ b/rebuild.sh @@ -1,4 +1,4 @@ -cat << EOF >/usr/local/lib/python3.10/site-packages/sphinx_rtd_theme/comments.html +cat << EOF >/usr/local/lib/python3.6/site-packages/sphinx_rtd_theme/comments.html