go version查看go版本
go env查看go所有的版本
go env -w GOPROXY=https://goproxy.io,direct修改为国内镜像
go env -w GO111MODULE=on打开 go moudle
go get -u github.com/gin-gonic/gin
go get golang.org/x/tools/cmd/goimports tools安装

go run xx.go直接在命令行执行go
go build xx.go编译成exe文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
go定义变量的方法:
一、单声明变量
1、基础定义,
var i int
i = 20
fmt.Println(i)
//定义并初始化
var i int = 10
fmt.Println(i)

2、根据值自行判断变量类型(类型推断)
var i = 100 //并没有设置类型
fmt.Println(i)

3、省略var(这种快速方式只能用在函数体内)
i := 100
fmt.Println(i)

三种方式:
var a int = 10
var b =10
c := 10
1
2
3
4
5
6
7
8
多变量:
var a,b,c int = 10,20,30
fmt.Println(a,b,c)
//集合类型
var(
a int
b string
)

#go基本数据类型

基本数据类型

bool
数值类型:整数、浮点数、复数、byte字节、run类型
字符串和string

有符号数,不能表示负数
11111111 = 257
01111111 = 127
10000001(负数) = -1

有符号数会拿出第一位表示正/负数 上限就会小

python里查看字节:
import sys
age = 18
sys.getsizeof(age)

字符串基本操作

字符串长度问题

1
2
3
4
5
6
7
8
9
10
//1.求解字符串长度
//返回的是字节的长度
//涉及刀中文就产生了变化
//go采用的是unicode 字符集 存储的时候需要utf8编码规则,utf8是一个动态的编码规则
//utf8 用一个字节表示英文,中文三个字节
var name string = "\t\nlenk测试一下"
fmt.Println(len(name))//18
//类型转换后
name_arr := []rune(name)
fmt.Println(len(name_arr))//10

转义符

1
2
3
4
5
//go中 “”表示字符串‘’表示字符
name := "2020\\01\\01"//转义符
names := `2020\01\01` //这种用法类似py的r''
fmt.Println(name)
fmt.Println(names)

查询、统计、开始结尾、分割合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//是否包含某个子串
var name string = "lenk:\"测试一下\"else"
fmt.Println(strings.Contains(name, "测试"))//true
//字符串的索引位置
fmt.Println(strings.Index(name, "测试"))//6
//统计出现的次数
fmt.Println(strings.Count(name, "l"))//2
//前缀后缀
fmt.Println(strings.HasPrefix(name, "l"))//true
fmt.Println(strings.HasSuffix(name, "s"))//false
//大小写转换
fmt.Println(strings.ToUpper("lenk"))
fmt.Println(strings.ToLower("LENK"))
//字符串的比较
fmt.Println(strings.Compare("a", "b"))//字符串的比较就是ascii的比较
fmt.Println(strings.Compare("b", "a"))//小于返回-1,等于返回0,大于范围1
fmt.Println(strings.Compare("b", "b"))
//多个字符比较是逐个比较
fmt.Println(strings.Compare("ab", "aa"))//1
//去掉空格和指定字符串
fmt.Println(strings.Trim("lenkl", "l"))//enk 去掉左右两边的l
fmt.Println(strings.TrimLeft("lenkl", "l"))//enkl 去掉左边的第一个l
fmt.Println(strings.TrimSpace(" lenks "))//lenks 前后空格都会被去掉

//split方法
fmt.Println(strings.Split("lenk yes", " "))//[lenk yes]
//合并join方法
arrs := strings.Split("lenk yes", " ")
fmt.Println(strings.Join(arrs, "-"))
//字符串替换(源,要替换的,替换的,替换个数)
fmt.Println(strings.Replace("ls:18 tel:181888", "18", "19", 2))//ls:19 tel:191888

Py里面的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
name = 'lenk看看测试一下else'
if "测试" in name:
print("yes")#yes
# 索引位置
print(name.index("s"))#12
# 出现次数
print(name.count("l"))#2
# 开始
print(name.startswith("l"))#True
# 结尾
print(name.endswith("s"))#False

# 大小写转换
print("lenk".upper())#LENK
print("LENK".lower())#lenk

# 分割合并
print("ls as".split())#['ls', 'as']
print(",".join(['a', 'b']))#a,b

格式化输入输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//printf 格式化
name := "bobby"
age := 18
fmt.Println("name:" + name,"age:" + strconv.Itoa(age))
/*
%v(不管是什么类型的,后面是什么就打印什么)
%#v(字符串则把引号打印出来)
%T(类型打印)
%d十进制 %o八进制 %x16进制

%c 字符
%q 有引号字符
*/
fmt.Printf("name:%v age:%v\n", name, age)
fmt.Printf("name:%T age:%-4d,\n", name, age)
fmt.Printf("name:%#v age:%x\n", name, age)

desc := fmt.Sprintf("name:%s, age:%x,\n", name, age)
fmt.Println(desc)

data := 65
fmt.Printf("data:%c\n", data)//data:A
fmt.Printf("data:%q\n", data)//data:'A'

fmt.Printf("%f\n",65.1)
fmt.Printf("%e\n",65.1)

//输入
var names string
var ages int
fmt.Println("请输入你的姓名和年龄:")
fmt.Scanln(&names, &ages)
fmt.Println(names, ages)

//通过scanf输入
fmt.Println("请输入你的姓名和年龄:")
fmt.Scanf("%s %d", &names, &ages)//lenk 19
fmt.Println(names, ages)

Python中:

1
2
3
4
5
6
7
8
9
10
# 格式化
name = 'lenk'
age = 18
print("name:%s, age:%d"%(name,age))
print("name:{}, age:{}".format(name,age))
print(f"name:{name}, age:{age}")

ages = input("请输入年龄:")
print(ages)
print(type(ages))#str(python里面一切处理都是字符串,可在输入外面套个int转换)

条件语句和循环语句

##if条件控制语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//if语句
num := 11
if num % 2 == 0 {
fmt.Println("偶数")
}

score := 45
if score >= 90 {
fmt.Println("优")
}else if score >= 80 {
fmt.Println("良")
}else if score >= 60 {
fmt.Println("一般")
}else {
fmt.Println("不及格")
}

//if statement; condition
if num := 10; num % 2 == 0 {
fmt.Println("偶数")
}

##for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//Go语言的For循环有3种形式,只有其中的一种使用分号。
//1、和C语言的for一样:
//for init; condition; post { }
//init: 一般为赋值表达式,给控制变量赋初值
//condition:关系表达式或逻辑表达式,循环控制条件;
//post: 一般为赋值表达式,给控制变量增量或减量

//2、和 C 的while一样:
//for condition { }

//3、和 C 的for (;;)一样;
//for { }

//1、for init; condition; post { } 计算1-10的和
sum := 0
for i := 1; i <= 10; i++ {
sum += i
}
fmt.Println(sum)

//2、for condition { }
i := 0
for i < 10 {
fmt.Println("lenk")
i++
}

//3、死循环
for {
fmt.Println("死循环")
}

//循环字符串
name := "abcd测试ef"
for index, value := range name {
//返回的是索引位置和ascii码
fmt.Println(index, value)
}
//打印字符串可通过Printf方式
for _, item := range name {
fmt.Printf("%c\n", item)
}

//1.name是一个字符串 2.字符串是字符串的数组
//在做中文遍历时 因为字节len的问题 要转成rune
//因为中文是3个字节 而只取出一个是乱码
name_arr := []rune(name)
for j := 0; j<len(name_arr); j++ {
fmt.Printf("%c\n", name_arr[j])//abcd测试ef
}

##goto语句
goto 语句可以无条件地转移到过程中指定的行。
goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
但是goto语句能不用则不用,goto过多,label过多,整个程序后期维护就会麻烦。
最容易理解的代码逐行执行,哪怕多一个函数的调用对于我们都是理解上的负担。

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 定义局部变量 */
var a int = 10

/* 循环 */
LOOP: for a < 20 {
if a == 15 {
/* 跳过迭代 */
a = a + 1
goto LOOP
}
fmt.Printf("a的值为 : %d\n", a)
a++
}

使用goto退出多层循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var breakAgain bool
// 外循环
for x := 0; x < 10; x++ {
// 内循环
for y := 0; y < 10; y++ {
// 满足某个条件时, 退出循环
if y == 2 {
// 设置退出标记
breakAgain = true
// 退出本次循环
break
}
}
// 根据标记, 还需要退出一次循环
if breakAgain {
break
}
}
fmt.Println("done")

优化后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	for x := 0; x < 10; x++ {
for y := 0; y < 10; y++ {
if y == 2 {
// 跳转到标签
goto breakHere
}
fmt.Println(y)
}
}
// 手动返回, 避免执行进入标签
return
// 标签放在循环之外,跳转到这就直接结束
breakHere:
fmt.Println("done")

使用goto集中处理错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
err := firstCheckError()
if err != nil {
fmt.Println(err)
exitProcess()
return
}
err = secondCheckError()
if err != nil {
fmt.Println(err)
exitProcess()
return
}
fmt.Println("done")

上述代码修改

1
2
3
4
5
6
7
8
9
10
11
12
13
	err := firstCheckError()
if err != nil {
goto onExit
}
err = secondCheckError()
if err != nil {
goto onExit
}
fmt.Println("done")
return
onExit:
fmt.Println(err)
exitProcess()

##switch语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
>90 A
80-89 B
70-79 C
60-69 D
<60 E
*/
score := 90
grade := "a"

//go里面的switch默认加了break,如果要取消加fallthrough
switch score {
case 90:
grade = "a"
fallthrough
case 85:
grade = "b"
case 80:
grade = "c"
default:
grade = "e"
}
fmt.Println(grade)

//另一种多条件判断写法
switch {
case score >= 90:
grade = "A"
case score >= 80 && score <= 89:
grade = "B"
case score >= 70 && score <= 79:
grade = "C"
case score >= 60 && score <= 69:
grade = "D"
default:
grade = "E"
}
fmt.Println(grade)

//另一种写法
var a = "daddy"
switch a{
//一分支多值
//或条件判断 满足一个即可
case "mum", "daddy":
fmt.Println("family")
}
//另一种写法
switch {
case a=="res" || a=="daddy":
fmt.Println("case1")

}

Python中的switch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 采用dict映射
# 简单需求直接通过lambda实现
socres = 80
switchs = {
90:lambda : print("A"),
80:lambda : print("B"),
70:lambda : print("C"),
60:lambda : print("D"),
}
switchs[socres]()

# 另一种方法:
def print_A():
print("A")

def print_B():
print("B")

def print_C():
print("C")

def print_D():
print("D")

socre = 90
switch = {
90:print_A,
80:print_B,
70:print_C,
60:print_D,
}
switch[socre]()

常用的复杂数据类型-map、数组、切片

数组多种初始化方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//go语言中的数组和python的list可以对应起来理解,slice和python的list更像
//静态语言中的数组:1、大小确定 2、类型一致
//数组的声明
//var courses [10] string
//var courses = [5] string{"1","2","3"}
courses := [5]string{"111","222","333"}
//静态语言要求严格, 动态语言是一门动态类型的

//1.修改值,取值:删除值,添加某一个值,数组一开始就要指定大小
//取值,修改值
fmt.Println(courses[0])//111
courses[0] = "123"
fmt.Println(courses)//[123 222 333 ]

//数组的另一种创建方式
//var a [4] float32
var a = [4] float32{1.0}
fmt.Println(a)//[1 0 0 0]
var b = [5] int{'A','B'}
fmt.Println(b)//[65 66 0 0 0]

//省略号的意思是 里面是数组 长度根据初始化值定的
d := [...] int {1,2,3,4,5}
fmt.Println(d)//[1 2 3 4 5]
//指明 第四个下标是100
e := [5] int {4:100}
fmt.Println(e)//[0 0 0 0 100]
f := [...] int {0:1, 5:10, 10:100}
fmt.Println(f)//[1 0 0 0 0 10 0 0 0 0 100]

##for range对数组元素求和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//数组操作第一种场景:求长度
fmt.Println(len(f))//11

//数组操作第二种场景:遍历数组
for i, value := range f {
fmt.Println(i, value)
}

//使用for range求和
sum := 0
for _, value := range f {
sum += value
}
fmt.Println(sum)

//使用for语句也可以遍历数组
sums := 0
for i := 0; i<len(courses); i++ {
sums += f[i]
}
fmt.Println(sums)

##go语言中数组是值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func printArray(toPrint [5]string){
//[]string 不指定长度就都传不了 这是切片类型
toPrint[0] = "lenky"
fmt.Println(toPrint)//[lenky scrapy1 tornado1 python+go asyncio]
}

//数组是值类型
courseA := [3] string {"django", "scrapy", "tornado"}
courseB := [...] string {"django1", "scrapy1", "tornado1", "python+go", "asyncio"}
//courseA和courseB应该是同一种类型, 都是数组类型
//在go语言中,courseA和courseB都是数组,但是不是同一种类型
fmt.Printf("%T\n", courseA)
fmt.Printf("%T\n", courseB)
//如果courseA和courseB是一种类型的话 为什么前面要加一个数组 长度不一样的数组类型是不一样
//正是基于这些,在go语言中函数传递参数的时候,如果传递数组会把值copy一份,也就是数组作为参数,实际调用是值传递
printArray(courseB)
fmt.Println(courseB)//[django1 scrapy1 tornado1 python+go asyncio]
1
2
3
4
5
6
7
8
9
10
11
12
13
from typing import List
from copy import deepcopy

def print_list(course: List[str]):
course[0] = "lenk"
print(course)

# 引用传递(外部也跟着改变)
courses = ['django', "scrapy", "tornado"]
# print_list(courses)
# 值传递通过deepcopy一份
print_list(deepcopy(courses))//['lenk', 'scrapy', 'tornado']
print(courses)//['django', 'scrapy', 'tornado']

##slice的基本操作和原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
func replace(mySlice []string) {
mySlice[0] = "lenky"
}

func main(){
//什么是切片
//数组有一个很大的问题:大小确定,不能修改 - 切片 - 动态数组
//var identifier []type
//第一种定义切片方法:var courses = []string //定义了一个切片

//var courses = []string{"django", "scrapy", "tornado"}
//fmt.Printf("%T", courses)

//第二种:切片的初始化方法 make
//切片不是没有长度限制
//为什么使用make初始化 需要我们传递一个长度, 那我传递了长度之后是不是意味着就像数组一样长度不能变了?
//slice底层依赖的是数组
//courses := make([]string, 5)
//fmt.Printf("%T\n", courses)//[]string
//fmt.Println(len(courses))//5
//slice对标的就是python中list

//第三种方法:通过数组变成一个切片
//var courses = [5]string{"django", "scrapy", "tornado", "python", "asyn"}
//subCourse := courses[1:4]
//subCourse[1] = "lenk"
//fmt.Println(subCourse)//[scrapy lenk python]
//replace(subCourse)
//fmt.Println(subCourse)

//第四种方式: new
//subCourse2 := *new([]int)
//fmt.Println(subCourse2)

//数组的传递是值传递 切片不是值传递 是引用传递

//slice是动态数组,所以我们需要动态添加值
//subCourse2 := subCourse[1:3]
//fmt.Printf("%T, %v\n", subCourse2, subCourse2)//[]string, [tornado python]

//append 可以向切片追加元素,
//appendCourse := []string{"imooc", "imooc2", "imooc3"}
//subCourse2 = append(subCourse2, appendCourse...)//函数的传递规则...
//fmt.Println(subCourse2)//[tornado python golang]

//subCourse3 := make([]string, len(subCourse2))
//fmt.Println(len(subCourse3))
//copy(subCourse3, subCourse2)
//拷贝的时候 目标对象长度需要设置好
//fmt.Println(subCourse3)

//append函数追加多个元素
//切片删除
deleteCourses := [5]string{"django", "scrapy", "tornado", "python", "asyncio"}
courseSlice := deleteCourses[:]
courseSlice = append(courseSlice[:1], courseSlice[2:]...)
fmt.Println(courseSlice)

//如何判断某个元素是否在切片中
//go需要自己去实现,python里通过in

//python和go的slice区别 go的slice更像是python的list go语言的底层是基于数组实现的
//slice进行的操作都会影响原来的数组 slice更像一个指针 本身不存值

//slice的原理
//1. 第一个现象
//不会去主动扩展空间 如果0个则就0
//如果想要copy则下面长度要设置和目标一样
a := make([]int, 0)
b := []int{1,2,3}
fmt.Println(copy(a, b))//0
fmt.Println(a)//[]
//2. 第二个现象
c := b[:]
//c[0] = 8
fmt.Println(b)//[8 2 3]
fmt.Println(c)//[8 2 3]
//3.第三个现象
c = append(c, 9)
fmt.Println(b)//append函数没有影响到原来的数组
fmt.Println(c)//[1 2 3 9]
c[0] = 8
fmt.Println(b)//[1 2 3]
fmt.Println(c)//[8 2 3 9]
//4.第四个现象
fmt.Println(len(c))//4
fmt.Println(cap(c))//6 //cap指的是容量 长度和容量是两个概念

//假设有一个值 实际上申请数组的时候可能是两个 如果后续要增加数据 那么就直接添加到数据的结尾, 这个时候我不要额外重新申请
//切片有不同的初始化方式
//1. 使用make方法初始化 len和cap是多少
d := make([]int, 5)
fmt.Printf("len=%d, cap=%d\n", len(d), cap(d))

//2.通过数组取切片
data := [10]int{0,1,2,3,4,5,6,7,8,9}
slice := data[2:4]
newSlice := data[3:6]
for index, value := range slice{
fmt.Println(index, value)
}
fmt.Printf("len=%d, cap=%d\n",len(slice), cap(slice))
fmt.Printf("len=%d, cap=%d\n",len(newSlice), cap(newSlice))

//切片扩容问题,扩容阶段会影响速度,python的list中低层实际上也是数组,也会面临动态扩容的问题,py的list中数据类型可以不一致
oldSlice := make([]int, 0)
fmt.Printf("len=%d, cap=%d\n",len(oldSlice), cap(oldSlice))
oldSlice = append(oldSlice, 1)
fmt.Printf("len=%d, cap=%d\n",len(oldSlice), cap(oldSlice))

//如果小于1024 扩容的速度是2倍 如果大于了1024 扩容的速度就是1.25

//切片来说1. 低层是数组,如果是基于数组产生的 会有一个问题就是会影响原来的数组。
//2. 切片的扩容机制
//3. 切片的传递是引用传递

oldArr := [3]int{1,2,3}
newArr := oldArr
newArr[0] = 5
fmt.Println(newArr, oldArr)

oldSlice = []int {1,2,3}
newSlice = oldSlice
newSlice[0] = 5
fmt.Println(oldSlice)
//当make遇到了append容易出现坑
s1 := make([]int, 5)
s1 = append(s1, 6)
fmt.Println(s1)
//打印结果是[0 0 0 0 0 6] 前面默认0
//append 是在5之后插入的

}

##map的定义和基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
func main(){
//go中的map ——> python中的dict
//go语言中的map的key和value类型申明的就要指明
//1,字面值
m1 := map[string]string{
"m1": "v1",
}
fmt.Printf("%v\n",m1)

//2.make函数 make函数可以穿件slice 可以创建map
m2 := make(map[string]string)//创建时,里面不添加元素
m2["m2"] = "v2"
fmt.Printf("%v\n",m2)

//3.定义一个空的map
m3 := map[string]string{}
fmt.Printf("%v\n", m3)

//map中的key 不是所有的类型都支持,改类型需要支持 == 或者 != 操作
//int rune()
//a := []int{1,2,3}
//b := []int{1,2,3}
//var m1 map[[]int]string

//a := [3]int{1,2,3}
//b := [3]int{1,2,3}
//if a == b{
//
//}

//map的基本
m := map[string]string{
"a": "va",
"b": "vb",
}

//1.进行增加,修改
m["c"] = "vc"
m["d"] = "vd"
fmt.Printf("%v\n", m)

//查询 两个参数ok是找到了就true,否false
v, ok := m["d"]
fmt.Printf("%v %v\n", v, ok)

//删除
delete(m, "a")
//删除不存在的也不会异常
delete(m, "t")
fmt.Printf("%v\n",m)

//遍历
for k, v := range m{
fmt.Println(k ,v)
}

//go语言中也有一个list 就是数据结构中提到的链表

}

指针

什么是指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
func swap(a *int, b *int){
//用于交换a和b
c := *a
*a = *b
*b = c
}

func main(){
//什么是指针
a := 10
b := 20
//go 也能像py这样用
//a, b = b, a
//swap(a, b)
fmt.Println(a, b)

//指针-对于内存来说,每一个字节其实都是有地址-通过16进制打印出来
fmt.Printf("%p\n", &a)//取变量地址
//现在有一种特殊的变量类型,这个变量只能保存地址值
var ip *int //这个变量里面只能保存地址类型这种值
ip = &a

//如果要修改指针指向的变量的值,用法也比较特殊
*ip = 30
fmt.Println(a)
//如果修改指针变量指向的内存中的值。通过指针去取值的时候不知道应该取多大的连续内存空间
fmt.Printf("空间地址是:%p, 内存地址是:%d\n",ip, *ip)
swap(&a, &b)
fmt.Println(a,b)

//go中数组是值传递 数组中有100万个值 这种一般采用切片来传递
//在python中list和dict这种传递都是值传递

//指针还可以指向数组 指向数组的指针 数组是值类型
arr := [3]int{1,2,3}
var ip *[3]int = &arr
//指针数组
var ptrs [3]*int//创建能够存放三个指针变量的数组
//在go中 指针很多时候都是函数参数的时候指明的类型
//指针的默认值是nil
if ip != nil {

}

}

##go指针和c指针有什么区别

1
2
3
4
5
//像python和java这种语言都在极力的屏蔽指针,c/c++ 都提供了指针 指针本身很强大
//c和c++中指针的功能都很强大 指针的转换 指针的偏移 指针的运算
//go语言没有屏蔽指针,但是go在指针上做了大量的限制,安全性高很多, 相比较c/c++灵活性就降低了
//指针变量中涉及到两个符号 & 和 *

##go中nil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
func main(){
//make, new, nil
//make和new函数
//new函数用法
//var p *int //申明了一个变量p 但是变量没有初始值 没有内存
//*p = 10
//默认值 int byte rune float bool string 这些类型都有默认值
//指针, 切片 map,接口这些默认值是nil 理解为none
var a int
a = 10
fmt.Println(a)

//对于指针来说或者其它默认值是0的情况来说 如何一开始申明的时候就分配内存
var p *int = new(int) //go编译器就知道先申请一个内存空间,这里的内存空间的值全部设置为0
*p = 10

//int使用make就不行
//除了new函数可以申请内存以外 还有一个函数就是make 更加常用的是make函数 make函数一般用于切片map
var info map[string] string = make(map[string]string)
info["c"] = "lenk"
//new函数返回的是这个值的地址 指针 make函数返回的是指定类型的实例
//遇到new首先调用操作系统的分配地址的接口,操作系统先把内存分配好并返回给go设置到变量中

//nil的一些细节
var info2 map[string] string
if info2 == nil {
fmt.Println("map 默认值是 nil")
}

var slice []string
if slice == nil {
fmt.Println("map 默认值是 nil")
}

var err error
if err == nil{
fmt.Println("error默认值是Nil")
}

//python中None和go语言中的nil类型不一样,None是全局唯一的
//go语言中 nil是唯一可以用来表示部分类型的零值的标识符,它可以代表许多不同内存布局的值
fmt.Println(unsafe.Sizeof(slice), unsafe.Sizeof(info2))

}

#go语言的函数

函数的各种定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//相比其他静态语言 go语言的函数有很多特点
//函数的几个要素: 1.函数名 2.参数 3.返回值
//函数的第一种定义方法
func add(a, b int) int {
var sum int
sum = a + b
return sum
}

//函数定义的第二种方法
func add2(a, b int) (sum int){
sum = a + b
return sum
}

//函数定义的第三种方法
func add3(a, b int) (sum int){
sum = a + b
return //不填 默认上面的sum
}

//函数定义的第四种方法
//被除数等于0, 要返回多个值 - 一个非常有用的特性
//也可以返回多个值
func div(a, b int) (result, a1 int, err error){
//var result int //也可省略在上面直接写
//var err error
if b == 0{
err = errors.New("被除数不能为0")
}else{
result = a / b
}
return
}

func main(){

result, a1, err := div(12,5)
if err != nil {
fmt.Println(err)
}else {
fmt.Println(result)
fmt.Println(a1)
}

}

通过省略号设置参数个数不定长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//省略号方式
func add(params ...int) (sum int){
for _, v := range params {
sum += v
}
return
}

//slice方式
func add_slice(params []int) (sum int){
for _, v := range params {
sum += v
}
params[0] = 11
return
}


func main(){
//通过省略号去动态设置多个参数值
slice := []int{1,2,3,56,6,7}
//通过... 拆解成int
fmt.Println(add(slice...))
fmt.Println(add(1,23,4,6,7,7,8))

//区别,slice是一种类型 还是引用传递 可以改里面的值
fmt.Println(add_slice(slice))

//省略号的用途 1.函数参数不定长 2.将slice打散
arr := [...]int{1,2,3}
fmt.Printf("%T",arr)//[3]int 数组类型

}

##go语言中函数是一等公民

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
type sub func(a, b int) int //sub就等同于int map
func subImpl(a, b int) int {
return a - b
}

func filter(score []int, f func(int) bool) []int{
reSlice := make([]int, 0)
for _, v := range score {
if f(v) {
reSlice = append(reSlice, v)
}
}
return reSlice
}

func main(){
//匿名函数
results := func (a, b int) int{
return a+b
}(1,2)
fmt.Println(results)
//还可以省略:
fmt.Println(func (a, b int) int{
return a+b
}(1,2))

//go中非常重要的特性 函数 一些trick 一些语法糖 函数的一等公民特性 - 可以作为参数 返回值 复制给另一个变量
//语法也可以当做参数

var mySubs sub = subImpl
fmt.Println(mySubs(8,2))
//只要符合上面type的格式 直接写匿名函数也可以
var mySub sub = func(c, d int) int {
return c + d
}
fmt.Println(mySub(2,2))

//将函数作为另一个函数的参数
//写一个函数用于过滤一部分数据
score := []int{10,90,88,85,75,43,35,11}
//写一个函数过滤掉不合格成绩
//fmt.Println(filter(score))

//还可以通过添加匿名函数判断条件的方式
fmt.Println(filter(score, func(a int) bool{
if a >= 90{
return true
}else {
return false
}
}))

}

##defer的作用和细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
func f1() int{
x := 10
defer func() {
x++
}()
tmp := x
return tmp
}

func f2() *int {
a := 10
b := &a
defer func() {
*b++
}()
temp_data := b
return temp_data
}


func main() {
//fmt.Println("test1")
//defer 语句是go的一种用于注册延迟调用的机制,它可以让当前函数执行完毕后执行
//defer 之后只能是函数调用 不能是表达式 比如a++
//多个它采用的是栈的方式第一个进压在最下面 也就是先入后出的 倒序的方式
//defer fmt.Println("test")
//defer fmt.Println("test3")

//先执行下面的tst3然后tst1,拷贝机制 copy了一份执行
//tst := func() {
// fmt.Println("tst1")
//}
//defer tst()
//tst = func() {
// fmt.Println("tst2")
//}
//fmt.Println("tst3")

//这一块用指针是向内存区域取值的,内存已经被改成11
//x := 10
//defer func( a *int) {
// fmt.Println(*a)
//}(&x)
//x++

//这一块的结果是11 它用的是闭包的特性
//x := 10
//defer func() {
// fmt.Println(x)
//}()
//x++

fmt.Println(f1())
fmt.Println(*f2())
//defer本质上是注册了一个延迟函数,defer函数的执行顺序已经确定了
//defer 没有嵌套 defer的机制是要取代try except finally
}

##recover和异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func div(a, b int) (int, error){
if b == 0 {
panic("不能为0")
}
return a/b, nil
}

func main(){
//错误就是遇到可能出现的情况,这些情况会导致你的代码出问题, 参数检查 数据库访问不了
a := 12
b := 0
defer func() {
err := recover()
if err != nil {
fmt.Println("异常被捕获到")
}
fmt.Println("lenk")
}()
fmt.Println(div(a, b))


//datas := strconv.Itoa(data)
//fmt.Println(datas)
//fmt.Printf("%T",datas)

}

#go语言中的结构体
##type的5种应用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func main() {
//go语言中的关键词 type
//1.给一个类型定义别名,实际上为什么会有byte,就是为了强调我们现在处理的对象是字节类型
//2.这种别名实际上还是为了代码的可读性 这个本质上仍然是uint8 无非就是在代码编码阶段可读性强而已
type myByte = byte
var b myByte
fmt.Printf("%T\n",b)//uint8

//2.第二种 就是基于一个已有的类型定义一个新的类型
type myInt int
var c myInt
fmt.Printf("%T\n",c)//main.myInt

//3.定义结构体
type Course struct{
name string
age int
}

//4.定义接口
type Callable interface {}

//5.定义函数别名
type handle func(str string)
}

##python中的class封装性和namedtuple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# 封装的问题
# 栈:后进先出 先进后出

# 封装重要的特性:变量和方法

class Stack:
def __init__(self):
self.items = []

def isEmpty(self):
return self.items == []

def push(self, item):
self.items.append(item)

def pop(self):
return self.items.pop()

def size(self):
return len(self.items)


# 另外一个场景,我们现在有一组课程的信息要保存:课程名、课程url、价格

class Course:
def __init__(self, name, price, url):
self.name = name
self.price = price
self.url = url

courses = []
courses1 = Course("django", 100, "https://imooc.com")
courses.append(courses1)

courses2 = Course("Scrapy", 200, "http://baiodu.com")
courses.append(courses2)

# 如果只关心数据,tuple更合适, tuple更省内存 性能更高
courses2 = []
new_course1 = ("django", 100, "https://imooc.com")
courses2.append(new_course1)


from collections import namedtuple
#nametuple 很像只能封装数据的类,但是nametuple性能比class高,内存会比class小
NewCouse = namedtuple('Course','name price url')
a = NewCouse(name="django", price=100, url="https://baidu.com")
print(a.price)
courses2.append(a)
print(courses2)

#go语言的struct更像nametuple

结构体的定义、实例化以及绑定方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
type Course struct {
Name string
Price int
Url string
}

func main() {
//go语言不支持面向对象
//面向对象的三个基本特征:1.封装 2.继承 3.多态
//还有些其他的4.方法重载 4.抽象基类
//定义struct go语言没有class概念 所以对于很多人来说会少理解很多面向对象的概念

//实例化
var c Course = Course{
Name: "django",
Price: 100,
Url: "http://baidu.com",
}

//访问
fmt.Println(c.Name, c.Url, c.Price)

//大小写在go语言中的可重要性 可见性
//一个包中的变量或者结构体如果首字母是小写 那么对于另一个包不可见
//结构体定义的名称和属性首字母 都要采用 大写 才能访问
//2.第二种实例化方式 - 顺序形式
c2 := Course{"Scrapy", 110 ,"http://www.haha.com"}
fmt.Println(c2.Name, c2.Url, c2.Price)

//3.如果一个指向结构体的指针, 通过结构体指针获取对象的值
c3 := &Course{"tornado", 100, "https://baidu.com"}
//fmt.Printf("%T\n",c3)//*main.Course
//通过这种方式直接访问也可以,这是一个go语言的语法糖
//但这种必须访问结构体才行,如果指向int对象那是不行的
//go语言是借鉴了动态语言的特性-很多地方不管如何写都是正确的
//另一个根本原因-go语言的指针是受限的
fmt.Println(c3.Name, c3.Url, c3.Price)
//fmt.Println((*c3).Name, (*c3).Url, (*c3).Price)

//4.零值 如果不给结构体赋值,go语言会默认给每个字段采用默认值
c4 := Course{}
fmt.Println(c4.Name)

//5.多种方式零值初始结构体
var c5 Course = Course{}
var c6 Course
var c7 *Course = new(Course)
//var c8 *Course
//为什么C6和c8表现出来的结构不一样 指针如果只申明不赋值 默认值是nil c6不是指针 是结构体类型
//slice map 默认值都是nil 这种默认值都要采用new或者make方式来初始化它
fmt.Println(c5.Price)
fmt.Println(c6.Price)
fmt.Println(c7.Price)
//fmt.Println(c8.Price)//抛错

//6.结构体是值类型
c8 := Course{"fast", 110, "http:fast.baidu.com"}
c9 := c8
fmt.Println(c8)
fmt.Println(c9)
c8.Price = 200
fmt.Println(c8)
fmt.Println(c9)

//go语言中struct无处不在
//7.结构体的大小 占用内存的大小 可以使用sizeof来查看对象占用的类型
fmt.Println(unsafe.Sizeof(1))
//go语言string的本质 其实string是一个结构体
fmt.Println(unsafe.Sizeof(""))
fmt.Println(unsafe.Sizeof(c8))//40 字符串占16个字节 int占8个

//8.slice的大小
type slice struct {
array unsafe.Pointer //底层数组的地址
len int
cap int
}
s1 := []string{"a1","a2","a3","a4","a5","a6"}
fmt.Println("切片占用的内存:",unsafe.Sizeof(s1))

m1 := map[string]string{
"lenk1": "l1",
"lenk2": "l2",
"lenk3": "l3",
"lenk4": "l4",
}
fmt.Println(unsafe.Sizeof(m1))
}

通过内嵌结构实现继承效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
type Teacher struct {
Name string
Age int
Title string
}

func (t Teacher) teacherInfo() {
fmt.Printf("姓名:%s, 年龄:%d, 职称:%s", t.Name, t.Age, t.Title)
}

type Course struct {
//这里可以不写变量
Teacher //如果讲师信息比较多,可以引入另一个结构体变量进来
Name string
Price int
Url string
}

//匿名继承,这种做法 其实就是语法糖 还不算是继承

func (c Course) courseInfo() {//方法绑定
//这里不可以不指定Teacher,但重名 就必须指定了
fmt.Printf("课程名:%s, 价格:%d, 信息:%s,%d,%s\n", c.Name, c.Price, c.Teacher.Name, c.Teacher.Age, c.Teacher.Title, )
}

func main() {
//go语言的继承:组合
t := Teacher{
Name: "lenk",
Age: 18,
Title: "程序员",
}
c := Course{
Teacher: t,
Name: "django",
Price: 100,
Url: "baidu",
}
c.courseInfo()
t.teacherInfo()
}

Python中的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 继承
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print("姓名:{},年龄:{}".format(self.name, self.age))


class Student(Person):
def __init__(self, name, age, school):
super().__init__(name, age)
self.school = school

def detail(self):
print("姓名:{},年龄:{}, 学校:{}".format(self.name, self.age,self.school))


s = Student("lenk",18,"hhh")
s.speak()
s.detail()

结构体标签的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import (
"encoding/json"
"fmt"
"reflect"
)

//type Info struct {//能表述的信息是有限的
// Name string`json:"name"`
// Age int `json:"age,omitempty"`
// Gender string
//}

type Info struct {//能表述的信息是有限的
Name string`orm:"name, max_length=17, min_length=5"`
Age int `json:"-"`//则不会把结果序列出来
Gender string `orm:"gender, required"`
}
//反射包

func main() {
//结构体标签
/*
结构体的字段除了名字和类型外,还可以有一个可选的标签(tag);
它是一个附属于字段的字符串,可以是文档或其他的重要标记。
比如在我们解析json或者生成json文件时,常用到encoding/json包,
它提供一些默认标签,例如:omitempty标签可以在序列化的时候忽略0值或者空值。
而-标签的作用是不进行序列化,其效果和直接将结构体中字段写成小写的效果一样
*/
info := Info{Name: "lenk", Gender: "mal"}
re, _ := json.Marshal(info)
fmt.Println(string(re))//{"name":"lenk","Gender":"男"}

//通过反射包去识别这些tag java的spring底层都是反射
t := reflect.TypeOf(info)
fmt.Println("Type:",t.Name())//Type: Info
fmt.Println("Kind:",t.Kind())//Kind: struct

for i := 0; i<t.NumField(); i++ {
field := t.Field(i)//获取结构体的每一个字段
tag := field.Tag.Get("orm")
fmt.Printf("%d. %v (%v), tag:'%v'\n", i+1, field.Name, field.Type.Name(), tag)
}
//大部分情况下 是不需要使用反射的
}