如何在Go中编写包

Go包是由Go代码组成的目录。本教程将指导您编写Go包以在其他编程文件中使用。

包由Go文件组成,这些文件位于同一目录中,并且在开头具有相同的包语句。 您可以在程序包中包含其他功能,以使程序更加复杂。 某些软件包可通过Go标准库获得,因此随Go安装一起安装。 其他人可以使用Go的go get命令安装。 您还可以通过使用必要的包语句在要共享代码的同一目录中创建Go文件来构建自己的Go包。

本教程将指导您编写Go包以在其他编程文件中使用。

先决条件

  • 按照如何安装和设置本地编程环境for Go系列的教程之一设置Go编程环境。 按照本地编程环境教程中的第5步创建Go Workspace。 要按照本文中的示例和命名约定,请阅读第一节“编写和导入包”。
  • 要加深对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

最后,我们可以在目录中添加第一个文件。 通常的做法是,包中的primaryentry point文件以目录名称命名。 在这种情况下,我们将在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!")
}

让我们打破第一个文件。 每个文件的第一行都需要您正在使用的package的名称。由于您在greet包中,因此使用package关键字,后跟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

当你这样做时,你会收到以下输出:

Hello, World!

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

greet.go
package greet

import "fmt"

var Shark = "Sammy"

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

接下来,打开main.go文件并添加以下突出显示的行, greet.gofmt.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

您将收到以下输出:

Hello, World!
Sammy

最后,让我们在greet.go文件中定义一个类型。 您将使用namecolor字段创建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函数创建一个句子, returns结果(字符串) returns给调用者(在本例中为主程序)。

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

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

通过在Octopus上创建String方法,您现在可以通过可重用的方式打印有关自定义类型的信息。 如果要在将来更改此方法的行为,则只需编辑此方法。

导出的代码

您可能已经注意到,您调用的greet.go文件中的所有声明都是大写的。 Go没有像其他语言那样的publicprivateprotected修饰符的概念。 外部可见性由资本化控制。 以大写字母开头的类型,变量,函数等在当前包之外公开可用。 在其包装外可见的符号被视为已exported

如果你向Octopus添加一个名为reset的新方法,你可以从greet包中调用它,但不能从你的main.go文件中调用它,它位于greet包之外:

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()
}

您将收到以下编译错误:

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

要从Octopus export reset功能,请在resetR大写:

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

您将收到以下输出:

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

通过调用Reset ,您清除了NameColor字段中的所有信息。 当您调用String方法时,它将不会打印NameColor通常出现的任何内容,因为字段现在为空。

结论

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