一个包由住在同一个目录中的Go文件组成,并在开始时具有相同的Package声明。您可以从包中添加额外的功能,使您的程序更加复杂。一些包通过Go标准库可用,因此与您的Go安装一起安装。其他包可以用Go的Go get
命令安装。您也可以通过使用所需的包声明在同一个目录中创建Go文件来创建自己的Go包。
本教程将指导您通过编写Go包用于其他编程文件中。
前提条件
- 创建一个 Go 编程环境,按照 如何安装和设置 Go 本地编程环境系列的教程之一。 在本地编程环境教程中按照步骤 5 创建您的 Go 工作空间。 若要遵循本文中的示例和命名惯例,请阅读第一节 编写和导入包
- 要深入了解 GOPATH,请阅读我们的文章 理解 GOPATH。
编写和导入包
写包就像写任何其他 Go 文件一样,包可以包含函数的定义, 类型和 变量,然后可以在其他 Go 程序中使用。
在我们创建新包之前,我们需要在我们的Go工作空间中。这通常是在我们的gopath
下。例如,在本教程中,我们将该包称为greet
。为了做到这一点,我们在我们的gopath
下创建了一个名为greet
的目录。如果我们的组织是gopherguides
,我们希望在使用Github作为我们的代码存储库时在组织下创建greet
的目录,那么我们的目录将看起来像这样:
1└── $GOPATH
2 └── src
3 └── github.com
4 └── gopherguides
问候
目录位于gopherguides
目录中:
1└── $GOPATH
2 └── src
3 └── github.com
4 └── gopherguides
5 └── greet
最后,我们可以将第一个文件添加到我们的目录中. 通常认为,包中的主要
或入口点
文件以目录的名称命名。 在这种情况下,我们会在欢迎
目录中创建一个名为greet.go
的文件:
1└── $GOPATH
2 └── src
3 └── github.com
4 └── gopherguides
5 └── greet
6 └── greet.go
随着创建的文件,我们可以开始编写我们想要重复使用或共享的代码,在这种情况下,我们将创建一个名为Hello
的函数,打印出Hello World
。
在文本编辑器中打开您的greet.go
文件,并添加以下代码:
1[label greet.go]
2package greet
3
4import "fmt"
5
6func Hello() {
7 fmt.Println("Hello, World!")
8}
让我们把这个第一个文件打破下来. 每个文件的第一行需要你正在工作的包
的名称. 因为你在欢迎
包中,你使用了包
的关键字,然后是包的名称:
1package greet
这将告诉编译器将文件中的所有内容视为问候
包的一部分。
接下来,您声明您需要使用的任何其他包与进口
声明. 您只使用此文件中的一个包,即fmt
包:
1import "fmt"
最后,您将创建函数Hello
,它将使用fmt
包来打印Hello, World!
:
1func Hello() {
2 fmt.Println("Hello, World!")
3}
现在你已经写了问候
包,你可以在你创建的任何其他包中使用它. 让我们创建一个新的包,在其中你将使用你的问候
包。
您将创建一个名为示例
的包,这意味着您需要一个名为示例
的目录。
1└── $GOPATH
2 └── src
3 └── github.com
4 └── gopherguides
5 └── example
现在你有你的目录为你的新包,你可以创建入口点文件. 因为这将是一个可执行的程序,它被认为是最佳的做法,以名称入口点文件 main.go
:
1└── $GOPATH
2 └── src
3 └── github.com
4 └── gopherguides
5 └── example
6 └── main.go
在文本编辑器中,打开main.go
并添加以下代码来调用greet
包:
1[label main.go]
2package main
3
4import "github.com/gopherguides/greet"
5
6func main() {
7 greet.Hello()
8}
因为你正在导入一个包,你需要通过引用点符号中的包名称来调用该函数。 Dot notation 是将一个期限放在你正在使用的包名称和你想要使用的包内的资源之间。
现在,您可以打开终端并在命令行上运行该程序:
1go run main.go
当你这样做时,你会收到以下输出:
1[secondary_label Output]
2Hello, World!
要了解如何在一个包中使用变量,让我们在您的greet.go
文件中添加变量定义:
1[label greet.go]
2package greet
3
4import "fmt"
5
6var Shark = "Sammy"
7
8func Hello() {
9 fmt.Println("Hello, World!")
10}
接下来,打开您的 main.go 文件,并添加以下突出的行,以便在 fmt.Println() 函数中调用greet.go
中的变量:
1[label main.go]
2package main
3
4import (
5 "fmt"
6
7 "github.com/gopherguides/greet"
8)
9
10func main() {
11 greet.Hello()
12
13 fmt.Println(greet.Shark)
14}
当您重新启动该程序时:
1go run main.go
您将收到以下输出:
1[secondary_label Output]
2Hello, World!
3Sammy
最后,让我们在greet.go
文件中定义一个类型,您将创建类型Octopus
与名称
和颜色
字段,以及一个函数,在呼叫时打印这些字段:
1[label greet.go]
2package greet
3
4import "fmt"
5
6var Shark = "Sammy"
7
8type Octopus struct {
9 Name string
10 Color string
11}
12
13func (o Octopus) String() string {
14 return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
15}
16
17func Hello() {
18 fmt.Println("Hello, World!")
19}
打开main.go 以在文件末尾创建此类型的实例:
1[label main.go]
2package main
3
4import (
5 "fmt"
6
7 "github.com/gopherguides/greet"
8)
9
10func main() {
11 greet.Hello()
12
13 fmt.Println(greet.Shark)
14
15 oct := greet.Octopus{
16 Name: "Jesse",
17 Color: "orange",
18 }
19
20 fmt.Println(oct.String())
21}
一旦你创建了Octopus
类型的实例,使用oct := greet.Octopus
,你可以访问该类型的函数和字段在main.go
文件的名称空间中。这允许你在最后一行上写oct.String()
,而不召唤greet
。
Octopus
类型的String
方法使用fmt.Sprintf
函数来创建一个句子,并返回
结果,一个字符串,给呼叫者(在这种情况下,你的主要程序)。
当您运行该程序时,您将收到以下输出:
1go run main.go
1[secondary_label Output]
2Hello, World!
3Sammy
4The octopus's name is "Jesse" and is the color orange.
通过在Octopus
上创建String
方法,您现在有了可重复使用的方式来打印有关自定义类型的信息。
出口代码
您可能已经注意到,您所呼叫的greet.go
文件中的所有声明都是资本化的。Go 没有像其他语言一样公共
、私人
或受保护
修改的概念。外部可见性是由资本化控制的。
如果您将一个名为Reset
的新方法添加到Octopus
,您可以从greet
包中调用它,但不能从main.go
文件中调用它,它位于greet
包外:
1[label greet.go]
2package greet
3
4import "fmt"
5
6var Shark = "Sammy"
7
8type Octopus struct {
9 Name string
10 Color string
11}
12
13func (o Octopus) String() string {
14 return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
15}
16
17func (o *Octopus) reset() {
18 o.Name = ""
19 o.Color = ""
20}
21
22func Hello() {
23 fmt.Println("Hello, World!")
24}
如果您尝试从 main.go 文件中调用重置
:
1[label main.go]
2package main
3
4import (
5 "fmt"
6
7 "github.com/gopherguides/greet"
8)
9
10func main() {
11 greet.Hello()
12
13 fmt.Println(greet.Shark)
14
15 oct := greet.Octopus{
16 Name: "Jesse",
17 Color: "orange",
18 }
19
20 fmt.Println(oct.String())
21
22 oct.reset()
23}
您将收到以下编译错误:
1[secondary_label Output]
2oct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)
要导出
从Octopus
的重置
功能,请将R
在重置
中的R
资本化:
1[label greet.go]
2package greet
3
4import "fmt"
5
6var Shark = "Sammy"
7
8type Octopus struct {
9 Name string
10 Color string
11}
12
13func (o Octopus) String() string {
14 return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
15}
16
17func (o *Octopus) Reset() {
18 o.Name = ""
19 o.Color = ""
20}
21
22func Hello() {
23 fmt.Println("Hello, World!")
24}
因此,您可以从您的其他包呼叫重置
,而不会出现错误:
1[label main.go]
2package main
3
4import (
5 "fmt"
6
7 "github.com/gopherguides/greet"
8)
9
10func main() {
11 greet.Hello()
12
13 fmt.Println(greet.Shark)
14
15 oct := greet.Octopus{
16 Name: "Jesse",
17 Color: "orange",
18 }
19
20 fmt.Println(oct.String())
21
22 oct.Reset()
23
24 fmt.Println(oct.String())
25}
如果您正在运行该程序:
1go run main.go
您将获得以下输出:
1[secondary_label Output]
2Hello, World!
3Sammy
4The octopus's name is "Jesse" and is the color orange
5The octopus's name is "" and is the color .
通过调用Reset
,您清除了Name
和Color
字段中的所有信息,当您调用String
方法时,它将不会在Name
和Color
字段正常出现的地方打印任何信息,因为这些字段现在是空的。
结论
写 Go 包与写任何其他 Go 文件相同,但将其放置到另一个目录中允许您将代码隔离到其他地方进行重复使用。本教程涵盖了如何在一个包中写定义,展示了如何在另一个 Go 编程文件中使用这些定义,并解释了存储该包以便访问其中的选项。