1.1函数
1.go函数不支持嵌套、重载和默认参数;
2.但支持以下特性:
无需声明原型、不定长度变参、多返回值、命名返回值参数、匿名函数、闭包;
3.定义函数使用关键字func,且左大括号不能另起一行;
4.函数也可以作为一种类型使用;
当有输入值和输出值时,必须对类型进行限制,如下
1.传入参数当类型都一样时,也可以用a,b int来对参数进行限制;
2.返回参数可以用int, string限制类型,当类型一样时,也可以用a,b int限制,当用a,b int这种时,因为相当于在程序开始就定义了a,b两个变量,所以第一次使用a和b时直接就可以用=而不是:=
func A(a int, b int) (int, int,int){
}
func A(a, b int) (a,b,c int){
}
不定长参数,当传入多个相同类型的变量时,可以用一个变量 ...接类型进行表示,如a ...int(相当于传入了一个slice);
不定长参数必须要放在参数最后
func A(a ...int) (a,b,c int){
}
当给函数传入一个切片,在函数内部修改切片中的值会影响到函数外部;
当给函数直接传入值时,在函数内部修改值不会影响外部,而当给函数传入地址时,在函数内部修改则会影响外部
// 切片为引用地址,故会改变值
func main(){
s1 := []int{1,2}
A(s1)
fmt.Println(s1)
}
func A(s [] int){
s[0] = 5
s[1] = 6
fmt.Println(s)
}
// 外部a不会改变
func main(){
a :=1
A(a)
fmt.Println(a)
}
func A(a int){
a = 5
fmt.Println(a)
}
// 由于传入的是指针型变量,外部a会改变
func main(){
a :=1
A(&a)
fmt.Println(a)
}
func A(a *int){
*a = 5
fmt.Println(*a)
}
1.2匿名函数
将函数类型赋值给一个变量,如下将一个函数赋值给a
func main(){
a :=func() {
fmt.Println("Function")
}
a()
}
func closure(x int) func(int) int {
return func(y int) int{
return x+y
}
}
1.3闭包
闭包定义:定义在一个函数内部的函数,静态保存所有了父级作用域的内部函数。
闭包作用:1.将函数作为参数传入另外一个函数;2.实现封装,管理私有变量和私有方法,将变量(状态)的变化封装在安全的环境中。3.这些变量的值始终保持在内存中,不会发生改变。
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包。
1.3.1python中的闭包
闭包就是外部函数传入使用的环境(即配置环境,下面中的a和b),内部函数使用外部函数传入的环境进行计算;传入的环境可以多次使用
def func(a, b):
def line(x):
return a * x - b
return line
line = func(2, 3)
# 输出7
print(line(5))
1.3.2go中的闭包
以闭包closure为例,传入一个环境x,类型为int,返回一个函数类型,函数类型需要传入一个int类型的参数,并且换回一个int型的值,使用时f := closure(10),相当于初始化闭包,f(1)利用闭包进行计算
闭包中环境的地址始终一样,如下面x中的地址不会发生改变。
func main(){
f := closure(10)
fmt.Println(f(1))
}
func closure(x int) func(int) int {
return func(y int) int {
return x+y
}
}
2defer
1.defer的执行方式类似其他语言中的析构函数,在函数体执行结束后按照调用顺序的相反顺序逐个执行;
2.即使函数发生严重错误也会执行;
3.支持匿名函数的调用;
4.常用于资源清理、文件关闭、解锁以及记录时间等操作;
5.通过与匿名函数配合可在return之后修改函数计算结果;
6.如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时即已经获得了拷贝,否则则是引用某个变量的地址;
7.go中没有异常机制,但有panic/recover模式来处理错误;
8.panic可以在任何地方引发,但recover只有在defer调用的函数中有效。
func B(){
// 打印顺序a,c,b
fmt.Println("a")
defer fmt.Println("b")
defer fmt.Println("c")
}
因为输入的值,故输入的是0,1,2
func B(){
// 打印顺序a,2,1,0
for i:=0;i<3;i++{
defer fmt.Println(i)
}
fmt.Println("a")
}
defer中使用匿名函数,匿名函数后大括号后必须接()表示调用,如下。
理解:如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时即已经获得了拷贝,否则则是引用某个变量的地址;
如下B函数因为没有传递参数,所以传入的是变量i的地址,因为最后i=3不满足条件退出,所以i为3,最后都打印3。
C函数因为传入了参数i,所以获得了拷贝,最后输出2,1,0
func B(){
// 打印顺序a,3,3,3
for i:=0;i<3;i++{
defer func(){
fmt.Println(i)
}()
}
fmt.Println("a")
}
func C(){
// 打印顺序a,2,1,0
for i:=0;i<3;i++{
defer func(i int){
fmt.Println(i)
}(i)
}
fmt.Println("a")
}
3panic及recover的使用
defer需要在panic之前使用;panic相当于python中的抛出错误raise用法;抛出错误后,panic之后的代码不会被执行,但之前的defer仍然会被执行;
recover恢复运行,相当于捕获异常,必须放在defer中使用,相当于python中的exception
简单来讲:go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
func C() {
fmt.Println("func A")
}
func CC() {
defer func() {
if err:=recover();err!=nil{
fmt.Println("Recover in CC")
}
}()
panic("panic in CC")
}
func CCC() {
fmt.Println("panic in CCC")
}
C()
CC()
CCC()
4练习
在for循环下定义函数,凡是不传递参数的,最后使用的都是一个地址,故使用的都是打破循环那个值,例子如下。
func F(){
var fs = [4] func(){}
for i:=0;i<4;i++{
defer fmt.Println("defer i=", i)
defer func() {fmt.Println("defer_closure i=", i)}()
fs[i] = func() {fmt.Println("closure i=", i)}
}
for _,f:= range fs {
f()
}
}
输出结果:
closure i= 4
closure i= 4
closure i= 4
closure i= 4
defer_closure i= 4
defer i= 3
defer_closure i= 4
defer i= 2
defer_closure i= 4
defer i= 1
defer_closure i= 4
defer i= 0
网友评论