包和模块
在前面课程中,都是以单个函数为例来进行讲解的。然而在真正的项目需求中,函数的数量会非常多。因此,如何管理这些函数便成为一个问题。
Go 提供了包和模块的管理方式来帮助开发者组织和管理代码。
包
每个 Go 程序都是由包组成的。名为“main”的包在 main() 函数处有一个入口点,一个 main 包被编译成一个可执行程序。任何其他名称的包都是库包,没有入口,只是导出可供其他包使用的功能。
一个 Go 代码目录最多只能有一个包,单个目录中的所有 .go 文件必须都属于同一个包。如果不这样做,编译器将抛出一个错误,这对于 main 包和库包都是一样的。
Go 中的包可分三种:
- 系统内置包:Go 提供的内置包,引入后可以直接使用,如 fmt、time、os 等
- 自定义包:开发者自己写的包
- 第三方包:属于自定义包的一种,需要下载安装到本地后才能使用
导包
在 Go 中,导包有以下几种方式:
单个导入:
import "fmt"
。这是最常见的导入方式,用于导入单个包。在代码中使用这种方式导入后,就可以使用该包中的函数、变量等。多个导入:这种方式可以一次性导入多个包,将它们放在一对圆括号中,并使用换行符分隔,各占一行。
import (
"fmt"
"os"
)
2
3
4
- 别名导入:
import f "fmt"
。可以使用别名来导入包,在使用时就可以用别名代替包名。这种方式有时可以解决不同包之间的命名冲突。 - 匿名导入:
import _ "net/http/pprof"
。使用下划线作为别名,这就意味着无法使用了,这种情况下,只能执行导入包内的 init 函数了,主要作用是做包的初始化用。 - 点导入:go-staticcheck 对于点导入会有警告:should not use dot imports (ST1001) go-staticcheck。点导入很少使用,这种方式很有可能导致导入的标识符发生冲突。
import . "fmt"
相当于把 fmt 包直接合并到当前程序中,在使用 fmt 包内的方法时可以不用加前缀 fmt,直接使用即可。
包的绝对路径是由 GOROOT/src/ 或 GOPATH/src/ 后面跟着包的存放路径组成。
init 函数
在 Go 语言中,init 函数是一种特殊的函数,用来执行包的初始化操作,每个包可以包含一个或多个 init 函数。 init 函数不能有任何返回类型,也不能有任何参数,更不能在源代码中显式调用,它将在包初始化时自动调用。在 main 函数被执行前,所有依赖包的 init 函数都会自动被调用,但是它们是在包被导入时按照导入顺序执行的。
func init() {
fmt.Println("初始化函数 init() 1")
}
func init() {
fmt.Println("初始化函数 init() 2")
}
func main() {
fmt.Println("主函数")
}
2
3
4
5
6
7
8
9
10
包初始化顺序
- 首先初始化导入的包,并调用它的 init 方法
- 接下来初始化包级变量
- 在主包中调用 init 函数
- 最后执行 main 函数
模块
模块是包的集合。所有 Go 项目都有一个 go.mod 文件,通过该文件管理项目的依赖关系。
Go 模块管理步骤:
- go mod init:初始化项目
- go get:给项目添加依赖
- go mod tidy:自动更新当前项目依赖信息
- go list -m all:查看当前项目所有依赖信息
go mod init
要开始使用 Go 模块,首先需要在项目根目录下初始化一个新模块。执行go mod init hello
命令,会在项目根目录下生成一个 go.mod 文件,将当前项目变为一个 Go 模块。
go.mod 文件内容包含模块名、Go 版本、依赖库等信息。
go.mod 文件所在顶层目录被称为 module 根目录,module 根目录以及子目录下的所有 Go 包均归属于这个 module(main module)。项目根目录下的 go.mod 文件管理整个模块的依赖关系,无论应用程序的内部目录结构如何,都可以直接构建和打包应用程序。
go get
下载安装解决 float 精度损失的第三方包 decimal:go get github.com/shopspring/decimal
。
go mod tidy
go mod tidy
命令会自动更新当前模块依赖信息,增加需要的依赖,删除不需要的依赖。项目根目录下还多了一个名为 go.sum 的文件,这个文件记录了项目直接和间接依赖包相关版本的 hash 值,用来校验本地包的真实性。
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// 使用 Decimal 类型进行精确计算
x := decimal.NewFromFloat(0.1)
y := decimal.NewFromFloat(0.2)
sum := x.Add(y)
fmt.Println("Sum:", sum)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16