go语言之函数
函数的定义:
函数就是一段执行特殊任务的代码块
函数定义规范如下:
func 函数名(参数1 参数类型,参数2 参数类型)返回值类型{
代码块1
return xxx
}
和python类似的是,函数可以有一个或多个返回值。不同于Python,有多少个返回值就需要定义多少个变量来接收它们,而Python中返回多个返回值其实本质上是返回一个tuple(元组),只需要定义一个变量来接收。
//在Python中
def test1(name, age):
name = '_'.join([name, 'test'])
age += 1
return name, age
if __name__ == '__main__':
t1 = test1("surpass",18)
print(type(t1)) //<class 'tuple'>
其实绝大数情况下,你是通过解压赋值的情况下来接收返回值,同时可以通过占位符来接收多余的变量。
name, _ = test1("surpass", 18)
print(name) // surpass_test
print(_) // 19
//在go中
package main
import "fmt"
func testA(name string,age int)(string,int){
names := []rune(name) // 通过name得到一个rune切片
for _,value:=range "_test"{
names = append(names,value)
}
age += 1
return string(names),age
}
func main() {
name,age := testA("surpass",18)
fmt.Printf("name is %s\nage is %d\n",name,age)
}
/
name is surpass_test
age is 19
/
_空白符的作用
不同于Python,在go中使用_来忽略多余的变量。而在python 中是用占位符_接收多余的变量,它是可以打印出来的。
package main
import "fmt"
const pi = 3.1415926
func circle(radius float64)(float64,float64){
area := pi * radius * radius
perimeter := 2 * pi * radius
return area,perimeter
}
func main() {
var r float64 = 5
area,_:=circle(r)
fmt.Printf("半径r为%0.0f的圆:\n面积s为%0.1f",r,area)
}
不需要明确的返回值
函数的返回值可以在声明阶段指定返回值类型和变量,因此在return处不需要明确的返回值。
package main
import "fmt"
const pi = 3.1415926
func Calculations(radius float64)(area,perimeter float64){
area = pi * radius * radius
perimeter = 2 * pi * radius
return
}
func main() {
var radius float64 = 5
area,perimeter := Calculations(radius)
fmt.Printf("半径为%0.0f的圆,周长为%0.2f,面积为%0.2f",radius,perimeter,area)
}
在go语言中,函数是一等公民,它可以赋值给变量,可以作为数组,切片,字典的元素,可以作为函数的参数、返回值。
函数的返回值是一个函数
package main
import "fmt"
const pi = 3.1415926
func CalcA(radius float64)(area,perimeter float64,f func(area, perimeter float64)float64){
area = pi * radius *radius
perimeter = 2 * pi * radius
f = func(area, perimeter float64) float64 {
if area > perimeter{
return area
}else{
return perimeter
}
}
return
}
func main() {
var radius float64 = 5
area,perimeter,f := CalcA(radius)
maxN := f(area,perimeter)
fmt.Printf("半径为%0.0f的圆,周长和面积中最大的是%0.2f",radius,maxN)
}
函数参数为函数类型,返回值为带参数,带返回值的函数类型
package main
import (
"fmt"
"time"
)
func testc(name string)string{
fmt.Printf("I am testc:%s\n",name)
time.Sleep(time.Duration(4)*time.Second)
names := []rune(name)
for _,value:=range "_test"{
names = append(names,value)
}
return string(names)
}
func decoratorA(f func(name string)string) func(name string)string{
return func(name string) string{
t1 := time.Now().Unix()
newName := f(name)
t2 := time.Now().Unix()
t3 := int(t2-t1)
fmt.Printf("程序运行一共花费了%d秒\n",t3)
return newName
}
}
func main() {
testc := decoratorA(testc)
name := testc("surpass")
fmt.Printf("new name is %s\n",name)
}
匿名函数
匿名函数适用于临时使用的场景,在Python中使用lambda关键字来定义匿名函数
# 匿名函数适用于临时使用的场景,常配合Python中的内置函数一起使用,如sorted,filter等。
sum1 = (lambda x,y:x+y)(3,4) # 7
sorted(lst,key=lambda obj:obj.age,reverse=True)
在go语言中,匿名函数具有以下特性:
(1)定义在有名函数内部;
(2)不能是有名函数;
示范例子:
package main
import "fmt"
func testB(name string)func()string{
return func()string {
names := []rune(name)
for _,value := range "_look"{
names = append(names,value)
}
return string(names)
}
}
func main() {
f := testB("dolphin")
name2 := f()
fmt.Printf("new name is %s\n",name2)
}
闭包函数
闭包函数的定义:内嵌函数(在go中,是匿名函数)对外部作用域有引用。闭包函数提供一种给函数传参的方式。
闭包函数的经典应用就是装饰器,装饰器是一种设计模式,它的本质是:
在不修改被装饰对象源代码和调用方式的本质下,给被装饰对象添加额外的功能。
在Python中,通过语法糖@来实现装饰器,如一个简单的程序运行统计时间装饰器如下:
import time
def decorator(func):
def wrapper(*args, **kwargs):
time1 = time.time()
func(*args, **kwargs)
time2 = time.time()
print('运行程序一共经过了%d秒\n' % (int(time2 - time1)))
return wrapper
@decorator
def test1(name):
print('I am test1:%(name)s' % {'name': name})
time.sleep(3)
if __name__ == '__main__':
test1("kevin")
得益于Python的启示,go语言中的装饰器可以这样来实现
package main
import (
"fmt"
"time"
)
//装饰器的实现
func testC(name string)string{
names := []rune(name)
for _,value := range "_perfect"{
names = append(names,value)
}
time.Sleep(time.Duration(3)*time.Second)
return string(names)
}
func decoratorN(f func(name string)string)(res func(name string)string){
res = func(name string) string {
t1 := time.Now().Second()
newName := f(name)
t2 := int(time.Now().Second()-t1)
fmt.Printf("程序运行一共经历了%d秒\n",t2)
return newName
}
return
}
func main() {
testC := decoratorN(testC)
fmt.Printf("new name is %s\n",testC("kevin"))
}
/*
装饰器的精髓在两个函数:
(1)将被装饰函数作为参数传入;
(2)将装饰过的函数作为返回值返回。
*/
如果我们希望这个装饰器可以装饰不同类型的函数了,即被装饰器对象可以是任意函数,应该怎么做了?
答:采用空的interface,空interface是泛型的基础。
package main
import (
"fmt"
"reflect"
"strconv"
"time"
)
func testD(name string)string{
names := []rune(name)
for _,value:=range "_perfect"{
names = append(names,value)
}
time.Sleep(time.Duration(3)*time.Second)
return string(names)
}
func testE(name string,age int)(info map[string]string){
info = map[string]string{
"name":name,
"age":strconv.Itoa(age),
}
time.Sleep(time.Duration(4)*time.Second)
return
}
func decoratedA(decoF,Fn interface{}){
var decoratorFunc,targetFunc reflect.Value
decoratorFunc = reflect.ValueOf(decoF).Elem() //作为输出装饰完毕之后的函数,这里的函数是通过反射动态生成的
targetFunc = reflect.ValueOf(Fn) //动态的获取被装饰的函数(也即目标函数)
v := reflect.MakeFunc(targetFunc.Type(), func(args []reflect.Value) (results []reflect.Value) {
t1 := time.Now().Unix()
results = targetFunc.Call(args)
t2 := int(time.Now().Unix()-t1)
fmt.Printf("程序运行一共经历了%d秒\n",t2)
return
}) //通过反射生成一个新的函数
decoratorFunc.Set(v) //将装饰过的函数存储到输出函数中
return
}
func main() {
testD := testD
testE := testE
decoratedA(&testD,testD) //需要输出的函数需要以指针的方式进行传入
decoratedA(&testE,testE)
newName := testD("dolphin")
fmt.Printf("the new name is %s\n",newName)
info := testE("surpass",18)
for key,value:=range info{
fmt.Printf("%s[%s]\n",key,value)
}
}
参考链接:
变量作用域以及范围
我们知道,在同一包下,函数名不能重名。定义在函数内部的变量是局部变量,它的作用域是函数内部。定义在全局的变量,全局有效,只要修改了,会影响全局。
package main
import "fmt"
var a int //在全局声明一个变量a,未对其初始化,自动初始化其类型的默认值0
func testX(){
fmt.Printf("a的值为%d\n",a) //先从testX()内部找,没有再去全局找
}
func main() {
var a int
fmt.Printf("a的值为%d\n",a) // 0
a = 19
testX() // 0
fmt.Printf("a的值为%d\n",a) // 19
}
/*
a的值为0
a的值为0
a的值为19
*/
go语言之包管理
go语言的包,就是类似Python的模块。
到目前为止,我们看到的 Go 程序都只有一个文件,文件里包含一个 main 函数和几个其他的函数。在实际中,这种把所有源代码编写在一个文件的方法并不好用。以这种方式编写,代码的重用和维护都会很困难。而包(Package)解决了这样的问题。
包的作用:
包的作用是用于组织go的源代码,提高了代码的可读性和复用性
自定义包:
-go语言的代码必须放在gopath的src路径之下
-包的导入是从gopath的src路径之下开始检索的
-除了main包之外,建议包名就叫文件夹名,一个文件夹下的包名必须一致
-同一包下,变量、函数只能定义一次
-同一包下的变量和函数,可以直接使用
-包内的函数或变量,想让外部包使用的话,必须首字母大写
-以后下载的第三方包都是放在gopath的src路径下
特殊函数init函数
init函数不需要调用就会执行,可以定义多个init函数
包导入的几种方式
(1)直接导入
import "dolphin/surpass"
(2)给包重命名
import surpass "dolphin/surpass"
名字,变量/函数
(3)包只导入,不使用
import _ "dolphin/surpass"
go语言没有一个统一包管理地址,大家都放到github之上
go mode模式
go mode有两种创建方式
命令行方式和go modules
命令行方式
在命令行下输入:
go mod init 项目名
在当前路径下创建出go.mod(该项目依赖go的版本,第三方包版本)
-项目路径的cmd窗口,go get第三方包,就会在go.mod中加入依赖
-以后把项目copy给别人,直接go install
-自定义的包,存放在项目的路径下
-使用代理方式:手动写或者在goland中配置
go modules
在goland中创建项目的时候,可以直接使用go modules,配置环境变量(加代理)