Go 是一门鼓励良好软件工程实践的语言。高质量软件的一个重要部分是代码复用,遵循 DRY 原则——不要重复自己。Go 包(package)正是实现代码可复用性的一个方法。
什么是包?
简单来说,Go 包就是项目中的一个目录,它包含一个或多个 Go 源文件,甚至还可以包含其他嵌套的 Go 包。包的主要目的是帮助你将相关的源文件组织到一个单元中,使它们更易于复用和维护。
每个 Go 源文件都属于一个包;这就是为什么每个 Go 文件都以包声明开头:
package packagename
源文件中的任何变量、类型或函数都属于其声明的包,同一个包内的其他源文件都可以访问。
存在于同一目录中的 Go 源文件必须属于同一个包。尽管不强制要求包的名称与其所在目录同名,但遵循这个约定是一个好习惯。
导入包
最常用的内置 Go 包之一是提供 I/O 功能的 fmt 包。要在代码中使用这个包,你可以在包声明下方导入它:
package packagename
import "fmt"
导入 fmt 包后,你就可以访问该包导出的函数。你可以使用点 . 操作符来访问这些函数,例如 fmt.Println() 或 fmt.Scanf()。
通常,你的程序中会需要使用多个包。你可以使用以下语法导入多个包:
import (
"fmt"
"math"
"strings"
)
在少数情况下,例如调试未完成的代码时,代码中可能会有一些未使用的包。为了避免编译器报错,你可以在包名前使用空白标识符 _。例如:
import (
"fmt"
"math"
_ "os"
"strings"
)
_的另一个作用是触发某些包中的初始化 init() 函数。例如,在使用数据库驱动时,你可以使用空白导入来触发 init() 函数,为该包提供操作数据库所需的数据。请看下面使用 Go 连接 PostgreSQL 数据库的例子:
import (
"database/sql" // 导入 Go 的 database/sql 包
_ "github.com/lib/pq" // 导入 database/sql 包所需的 PostgreSQL sql 驱动
)
在 github.com/lib/pq 包中,conn.go 文件包含了 init() 函数。简单来说,这将把必要的数据传递给主包中的 database/sql 包,使其能够与 PostgreSQL 数据库协同工作:
func init() {
// 当使用 "_" 导入初始化时,init() 函数会将必要数据
// 传递给主包中的 database/sql 包,使其能够与 PostgreDB 协同工作
sql.Register("postgres", &Driver{})
}
另一种特殊的导入方式是点 . 导入。它将包导入到当前包相同的命名空间中,因此无需使用导入包的名字来访问其函数。例如:
import (
. "fmt"
. "math"
)
func main() {
// 向控制台输出 5,你可以直接使用 Println 而无需加 fmt. 前缀,Abs 也无需加 math. 前缀
Println(Abs(-5))
}
你也可以导入嵌套包,例如:
import "math/rand"
这里 math 是主包,rand 是嵌套包。在这种情况下,你只能使用 rand 包内的函数,math 包的其他任何功能都不可用,除非你同时导入了 math 和 math/rand 包。
作为一个良好的编码实践,请记住只导入你将在代码中使用的包!否则,你会得到 imported and not used(已导入但未使用)的编译错误。
包的类型
Go 中有两种类型的包:可执行包和工具包。可执行包包含一个可由 Go 编译运行的可执行程序。而工具包本身不可执行;它只包含可在可执行包中使用的工具函数。fmt、os、math 等就是工具包的例子。
一个特殊的可执行包是 main 包。要在 Go 中创建可执行包,有两个要求:
-
包的名称必须是
main。 -
它应该包含一个名为
main()的函数,这将是程序的起点。
下面你可以看到位于 main 包中的源文件 main.go 的内容:
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
外部包
你也可以使用 go get 命令将外部包安装到你的项目中。例如,让我们尝试安装托管在 GitHub 上的 fatih/color 包:
$ go get github.com/fatih/color
从 Go 1.16 版本开始,模块感知模式默认启用。因此,如果你使用 Go 1.16 或更高版本,并想安装外部包,需要执行以下步骤:
-
在终端中执行
go mod init module-name命令在项目中初始化 Go 模块,例如go mod init example。 -
然后执行
go get package-name命令来安装外部包,例如go get github.com/fatih/color。 -
最后,执行
go mod tidy命令来添加缺失的模块并移除项目中未使用的模块。
现在你可以在 main.go 文件中导入 fatih/color 包了:
package main
import "github.com/fatih/color"
func main() {
color.Red("Hello World!")
}
执行 go run main.go 后,你将在终端中看到以下红色输出:
Hello World!
内部包
最后但同样重要的是,你也可以创建内部包。内部包很特殊,因为它们必须位于项目内名为 internal 的目录中。
Go 工具能识别这个 internal 目录,并防止它包含的包被任何不共享同一父目录或不是该父目录子目录的包导入。
例如,让我们看一下包含嵌套内部包的 example 目录结构:
├── calculator
│ ├── calculator.go
│ └── internal
│ └── modulo.go
└── main
└── main.go
example/calculator/internal 这个内部包可以被 calculator.go 或 example/calculator 目录及其子目录内的任何其他 Go 源文件导入,因为它们共享共同的父目录 example/calculator。
然而,如果你尝试从位于 example/main 目录的 main 包导入 example/calculator/internal 包,将会得到以下编译错误:
use of internal package example/calculator/internal not allowed
这是因为 example/main 目录和 example/calculator/internal 目录不共享共同的父目录 example/calculator。
总结
-
Go 包就是一个包含相关 Go 源文件的目录;
-
Go 中有两种类型的包:工具包和可执行包。工具包不可执行;它们只包含可供可执行的
main包使用的函数,main包是程序的入口点; -
你可以通过
go get命令将外部包安装到你的项目中; -
内部包是一种特殊类型的包,必须始终位于名为
internal的目录内。请记住,内部包只能由位于与该内部包相同父目录或其子目录内的包导入。
Go包的基础与使用详解
876

被折叠的 条评论
为什么被折叠?



