跳转到主要内容

介绍

一个包由位于同一目录中的 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
最后,我们可以在我们的目录中添加第一个文件。通常的做法是,包中的主文件或入口点文件以目录名称命名。在这种情况下,我们将在 greet 目录中创建一个名为 greet.go 的文件:

└── $GOPATH
    └── src
        └── github.com
            └── gopherguides
                └── greet
                    └── greet.go
创建文件后,我们可以开始编写我们想要在项目之间重用或共享的代码。在这种情况下,我们将创建一个名为 Hello 的函数来打印 Hello World。

在文本编辑器中打开 greet.go 文件并添加以下代码:

greet.go

package greet

import "fmt"

func Hello() {
    fmt.Println("Hello, World!")
}

让我们分解第一个文件。每个文件的第一行需要您正在使用的包的名称。由于您在 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()
}

因为您正在导入一个包,所以您需要通过以点表示法引用包名称来调用该函数。点符号是放置句号的做法。在您正在使用的包的名称和您要使用的包中的资源之间。例如,在你的 greet 包中,你有 Hello 函数作为资源。如果要调用该资源,请使用 greet.Hello() 的点表示法。

现在,您可以打开终端并在命令行上运行程序:

go run main.go


完成后,您将收到以下输出:

Output
Hello, World!

要了解如何在包中使用变量,让我们在 greet.go 文件中添加一个变量定义:

greet.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


您将收到以下输出:

Output
Hello, World!
Sammy


最后,让我们在 greet.go 文件中定义一个类型。您将创建带有名称和颜色字段的 Octopus 类型,以及一个在调用时将打印出字段的函数:

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 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。例如,您还可以调用类型字段之一,例如 oct.Color 而不引用 greet 包的名称。

Octopus 类型的 String 方法使用 fmt.Sprintf 函数创建一个句子,并将结果以字符串形式返回给调用者(在本例中为您的主程序)。

运行程序时,您将收到以下输出:

go run main.go

Output
Hello, World!
Sammy
The octopus's name is "Jesse" and is the color orange.


通过在 Octopus 上创建 String 方法,您现在有了一种可重用的方式来打印有关您的自定义类型的信息。如果你想在以后改变这个方法的行为,你只需要编辑这个方法。

导出的代码


您可能已经注意到,您调用的 greet.go 文件中的所有声明都是大写的。 Go 不像其他语言那样具有公共、私有或受保护修饰符的概念。外部可见性由大小写控制。以大写字母开头的类型、变量、函数等在当前包之外是公开可用的。在其包外可见的符号被视为已导出。

如果你向 Octopus 添加一个名为 reset 的新方法,你可以在 greet 包中调用它,但不能从在 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()
}

您将收到以下编译错误:

Output
oct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)


要从 Octopus 导出重置功能,请将重置中的 R 大写:

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!")
}

因此,您可以从其他包中调用 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


您将收到以下输出:

Output
Hello, World!
Sammy
The octopus's name is "Jesse" and is the color orange
The octopus's name is "" and is the color .


通过调用重置,您清除了名称和颜色字段中的所有信息。当您调用 String 方法时,它不会在 Name 和 Color 通常出现的地方打印任何内容,因为这些字段现在是空的。

结论


编写 Go 包与编写任何其他 Go 文件相同,但是将其放在另一个目录中可以隔离代码以在其他地方重用。本教程介绍了如何在包中编写定义,演示了如何在另一个 Go 编程文件中使用这些定义,并解释了将包保存在何处以便访问它的选项。

文章链接