如何在Go中编写包
一个包由位于同一目录中的 Go 文件组成,并且在开头具有相同的包语句。 您可以从包中包含其他功能,以使您的程序更加复杂。 一些包可通过 Go 标准库获得,因此随 Go 安装一起安装。 其他可以使用 Go 的 go get
命令安装。 您还可以通过使用必要的包语句在要共享代码的同一目录中创建 Go 文件来构建自己的 Go 包。
本教程将指导您编写 Go 包以在其他编程文件中使用。
先决条件
- 按照 如何为 Go 系列安装和设置本地编程环境的教程之一设置 Go 编程环境。 按照本地编程环境教程中的第 5 步创建您的 Go 工作区。 要遵循本文中的示例和命名约定,请阅读第一部分编写和导入包。
- 要加深您对 GOPATH 的了解,请阅读我们的文章 了解 GOPATH。
编写和导入包
编写一个包就像编写任何其他 Go 文件一样。 包可以包含函数、类型和变量的定义,然后可以在其他Go程序中使用。
在我们创建一个新包之前,我们需要进入我们的 Go 工作区。 这通常在我们的 gopath
下。 例如,在本教程中,我们将调用包 greet
。 为此,我们在项目空间下的 gopath
中创建了一个名为 greet
的目录。 如果我们的组织是 gopherguides
,并且我们想在组织下创建 greet
包,同时使用 Github 作为我们的代码存储库,那么我们的目录将如下所示:
└── $GOPATH └── src └── github.com └── gopherguides
greet
目录位于 gopherguides
目录中:
└── $GOPATH └── src └── github.com └── gopherguides └── greet
最后,我们可以在我们的目录中添加第一个文件。 通常认为包中的 primary
或 entry point
文件以目录名称命名。 在这种情况下,我们将在 greet
目录中创建一个名为 greet.go
的文件:
└── $GOPATH └── src └── github.com └── gopherguides └── greet └── greet.go
创建文件后,我们可以开始编写我们想要在项目之间重用或共享的代码。 在这种情况下,我们将创建一个名为 Hello
的函数,它会打印出 Hello World
。
在文本编辑器中打开 greet.go
文件并添加以下代码:
问候.go
package greet import "fmt" func Hello() { fmt.Println("Hello, World!") }
让我们分解第一个文件。 每个文件的第一行需要您正在使用的 package
的名称。 由于您在 greet
包中,因此您使用 package
关键字后跟包的名称:
package greet
这将告诉编译器将文件中的所有内容视为 greet
包的一部分。
接下来,您声明需要与 import
语句一起使用的任何其他包。 您只在此文件中使用了一个 - fmt
包:
import "fmt"
最后,创建函数 Hello
。 它将使用 fmt
包打印出 Hello, World!
:
func Hello() { fmt.Println("Hello, World!") }
现在您已经编写了 greet
包,您可以在您创建的任何其他包中使用它。 让我们创建一个新包,您将在其中使用 greet
包。
您将创建一个名为 example
的包,这意味着您需要一个名为 example
的目录。 在您的 gopherguides
组织中创建此包,因此目录结构如下所示:
└── $GOPATH └── src └── github.com └── gopherguides └── example
现在您已经有了新包的目录,您可以创建入口点文件。 因为这将是一个可执行程序,所以将入口点文件命名为 main.go
被认为是最佳实践:
└── $GOPATH └── src └── github.com └── gopherguides └── example └── main.go
在您的文本编辑器中,打开 main.go
并添加以下代码以调用 greet
包:
main.go
package main import "github.com/gopherguides/greet" func main() { greet.Hello() }
因为您正在导入一个包,所以您需要通过以点表示法引用包名称来调用该函数。 Dot notation 是在你正在使用的包的名称和你想要使用的包中的资源之间放置一个句点 .
的做法。 例如,在您的 greet
包中,您将 Hello
函数作为资源。 如果要调用该资源,请使用 greet.Hello()
的点表示法。
现在,您可以打开终端并在命令行上运行程序:
go run main.go
当您这样做时,您将收到以下输出:
OutputHello, World!
要了解如何在包中使用变量,让我们在 greet.go
文件中添加变量定义:
问候.go
package greet import "fmt" var Shark = "Sammy" func Hello() { fmt.Println("Hello, World!") }
接下来,打开您的 main.go
文件并添加以下突出显示的行以在 fmt.Println()
函数中从 greet.go
调用变量:
main.go
package main import ( "fmt" "github.com/gopherguides/greet" ) func main() { greet.Hello() fmt.Println(greet.Shark) }
再次运行程序后:
go run main.go
您将收到以下输出:
OutputHello, World! Sammy
最后,我们还要在 greet.go
文件中定义一个类型。 您将使用 name
和 color
字段创建类型 Octopus
,以及在调用时打印出字段的函数:
问候.go
package greet import "fmt" var Shark = "Sammy" type Octopus struct { Name string Color string } func (o Octopus) String() string { return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color) } func Hello() { fmt.Println("Hello, World!") }
打开 main.go
以在文件末尾创建该类型的实例:
main.go
package main import ( "fmt" "github.com/gopherguides/greet" ) func main() { greet.Hello() fmt.Println(greet.Shark) oct := greet.Octopus{ Name: "Jesse", Color: "orange", } fmt.Println(oct.String()) }
一旦你用 oct := greet.Octopus
创建了一个 Octopus
类型的实例,你就可以在 main.go
文件的命名空间中访问该类型的函数和字段。 这使您可以在最后一行写入 oct.String()
而无需调用 greet
。 例如,您还可以在不引用 greet
包的名称的情况下调用 oct.Color
等类型字段之一。
Octopus
类型上的 String
方法使用 fmt.Sprintf
函数创建一个句子,并将 returns
结果,一个字符串,给调用者(在这个案例,你的主程序)。
运行程序时,您将收到以下输出:
go run main.go
OutputHello, World! Sammy The octopus's name is "Jesse" and is the color orange.
通过在 Octopus
上创建 String
方法,您现在有一种可重用的方式来打印有关您的自定义类型的信息。 如果你想在以后改变这个方法的行为,你只需要编辑这个方法。
导出的代码
您可能已经注意到,您调用的 greet.go
文件中的所有声明都是大写的。 Go 不像其他语言那样具有 public
、private
或 protected
修饰符的概念。 外部可见性由大小写控制。 以大写字母开头的类型、变量、函数等在当前包之外是公开可用的。 在其包装外可见的符号被认为是 exported
。
如果向 Octopus
添加一个名为 reset
的新方法,则可以从 greet
包中调用它,但不能从 main.go
文件中调用它,即greet
包外:
问候.go
package greet import "fmt" var Shark = "Sammy" type Octopus struct { Name string Color string } func (o Octopus) String() string { return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color) } func (o *Octopus) reset() { o.Name = "" o.Color = "" } func Hello() { fmt.Println("Hello, World!") }
如果您尝试从 main.go
文件中调用 reset
:
main.go
package main import ( "fmt" "github.com/gopherguides/greet" ) func main() { greet.Hello() fmt.Println(greet.Shark) oct := greet.Octopus{ Name: "Jesse", Color: "orange", } fmt.Println(oct.String()) oct.reset() }
您将收到以下编译错误:
Outputoct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)
要 export
Octopus
中的 reset
功能,请将 reset
中的 R
大写:
问候.go
package greet import "fmt" var Shark = "Sammy" type Octopus struct { Name string Color string } func (o Octopus) String() string { return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color) } func (o *Octopus) Reset() { o.Name = "" o.Color = "" } func Hello() { fmt.Println("Hello, World!") }
因此,您可以从其他包中调用 Reset
而不会出现错误:
main.go
package main import ( "fmt" "github.com/gopherguides/greet" ) func main() { greet.Hello() fmt.Println(greet.Shark) oct := greet.Octopus{ Name: "Jesse", Color: "orange", } fmt.Println(oct.String()) oct.Reset() fmt.Println(oct.String()) }
现在如果你运行程序:
go run main.go
您将收到以下输出:
OutputHello, World! Sammy The octopus's name is "Jesse" and is the color orange The octopus's name is "" and is the color .
通过调用 Reset
,您清除了 Name
和 Color
字段中的所有信息。 当您调用 String
方法时,它不会在 Name
和 Color
通常出现的地方打印任何内容,因为这些字段现在是空的。
结论
编写 Go 包与编写任何其他 Go 文件相同,但是将其放在另一个目录中可以隔离代码以在其他地方重用。 本教程介绍了如何在包中编写定义,演示了如何在另一个 Go 编程文件中使用这些定义,并解释了将包保存在何处以便访问它的选项。