>백엔드 개발 >Golang >Go 언어 기본 패키지

Go 언어 기본 패키지

Go语言进阶学习
Go语言进阶学习앞으로
2023-07-21 11:56:461211검색

가방이란 무엇이며 왜 사용하나요?

지금까지 우리가 본 Go 프로그램에는 기본 기능과 몇 가지 다른 기능이 포함된 파일이 하나만 있습니다. 실제 비즈니스 개발에서 모든 소스 코드를 단일 파일에 작성하는 이 방법은 확장성이 없습니다. 이러한 종류의 코드 재사용성과 유지 관리성은 불가능해지며, 여기서 패키지가 유용할 수 있습니다.

패키지는 동일한 디렉터리에 있는 .go로 끝나는 파일 모음으로, 더 나은 재사용성과 가독성을 위해 소스 코드를 구성하는 데 사용됩니다. 패키지는 Go 프로젝트를 더 쉽게 유지 관리할 수 있도록 코드 분할을 제공합니다.

예를 들어, 단리 계산, 복리 계산 등의 기능을 포함하는 금융 시스템을 Go로 작성한다고 가정해 보겠습니다. 이 시스템을 구성하는 간단한 방법은 기능에 따라 나누는 것입니다. 단순이자, 복리이자 및 대출의 세 가지 패키지를 만들 수 있습니다. 대출 패키지가 simpleinterest 패키지의 기능을 사용해야 하는 경우 simpleinterest 패키지만 가져오기만 하면 코드를 재사용할 수 있습니다.

원금, 이자율, 시간을 주어 이자가 얼마인지 계산하는 간단한 애플리케이션을 만들어 패키지에 대해 알아봅니다.

메인 함수 및 메인 패키지

모든 실행 가능한 Go 애플리케이션에는 메인 함수가 포함되어 있어야 합니다. 이 함수는 프로그램 실행을 위한 진입점이며 주요 기능은 기본 패키지에 있어야 합니다.

package packagename

위 구문은 소스 파일이 일반적으로 파일의 첫 번째 줄에 나타나는 packagename 패키지에 속함을 지정하는 데 사용됩니다.

애플리케이션의 메인 기능과 메인 패키지 생성을 시작해 보겠습니다.

다음 명령을 실행하여 현재 사용자의 Documents 디렉터리에 learnpackage라는 디렉터리를 만듭니다.

mkdir ~/Documents/learnpackage/

learnpackage 디렉토리에 다음 코드가 포함된 main.go라는 파일을 생성합니다.

package main

import "fmt"

func main() {
    fmt.Println("Simple interest calculation")
}

package 메인 코드는 파일이 메인 패키지에 속함을 지정하는 데 사용됩니다.

import "packagename" 문은 다음과 같이 사용됩니다. 기존 패키지를 가져옵니다.

packagename.FunctionName()은 패키지에서 함수를 호출하는 것입니다.

上面代码的第 3 行,我们导入 fmt 包以便能调用包里的 Println() 函数,fmt 是 Go 语言标准库中内置的一个包。主函数会打印 Simple interest calculation。

cd 到 learnpackage 目录并且编译上面的代码

cd ~/Documents/learnpackage/
go install

如果一切顺利,二进制文件将被编译并可以执行。在终端中键入命令 learnpackage,将会看到如下输出:

Simple interest calculation

Go Module

我们将会这样构建代码,与计算利息相关的功能函数都放在 simpleinterest 包里。为此,我们需要创建一个自定义包 simpleinterest,其中包含计算利息的函数。在创建自定义包之前,我们需要先了解 Go Modules,创建自定义包需要 Go Modules。

简单来说,Go Module 只不过是 Go 包的集合。你可能会有疑问,为什么我们需要 Go 模块来创建自定义包呢?答案是我们创建的自定义包的导入路径来源于 go 模块的名称。除此之外,我们的应用程序使用的所有其他第三方包(例如来自 github 的源代码)将与版本一起出现在 go.mod 文件中。当我们创建一个新模块时,会创建出一个 go.mod 文件,下一小节会讲到这点。

可能你又会疑惑了:为什么到现在我们还没有创建 Go 模块程序也能执行成功?答案是,本系列教程到目前为止,我们从未创建过自定义包,因此不需要 Go 模块。

理论知识学完了,让我们来创建自己的 Go 模块和自定义包。

创建 Go module

执行下面命令确保在 learnpackage 目录下,

cd ~/Documents/learnpackage/

在该目录下输入如下命令创建名为 learnpackage 的 go 模块。

go mod init learnpackage

上面的命令将创建一个名为 go.mod 的文件,下面是该文件的内容:

module learnpackage

go 1.13

代码行 module learnpackage 指定模块的名字为 learnpackage。正如之前提到的,learnpackage 是模块的基础路径,想要导入模块的任何一个包必须基于此路径。最后一行指定此模块中的文件使用 1.13 的 go 版本。

创建一个自定义包:计算利息

属于一个包的源文件应该放在它们自己的单独文件夹中。Go 中的约定是文件夹名称与包名相同。

让我们在 learnpackage 文件夹中创建一个名为 simpleinterest 的文件夹。mkdir simpleinterest 将会创建该文件夹。

simpleinterest 文件夹中的所有文件都应该以 package simpleinterest 开头,因为它们都属于 simpleinterest 包。

在 simpleinterest 文件夹中创建一个文件 simpleinterest.go。

下面是程序的结构目录:

├── learnpackage
│   ├── go.mod
│   ├── main.go
│   └── simpleinterest
│       └── simpleinterest.go

将以下代码添加到 simpleinterest.go 文件中。

package simpleinterest

//Calculate calculates and returns the simple interest for a principal p, rate of interest r for time duration t years
func Calculate(p float64, r float64, t float64) float64 {
    interest := p * (r / 100) * t
    return interest
}

在上面的代码中,我们创建了一个计算并返回利息的函数 Calculate()。

导入自定义包

要使用自定义包,我们必须先导入它。导入路径是模块名加上包的子目录和包名。在我们的例子中,模块名称是 learnpackage,包 simpleinterest 位于 learnpackage 下的 simpleinterest 文件夹中。

├── learnpackage
│   └── simpleinterest

所以 import "learnpackage/simpleinterest" 代码将导入 simpleinterest 包。

如果我们的目录结构是这样的:

learnpackage
│   └── finance
│       └── simpleinterest

那么导入包的语句将会是 mport "learnpackage/finance/simpleinterest"。

将下面的代码加入 main.go

package main

import (
    "fmt"
    "learnpackage/simpleinterest"
)

func main() {
    fmt.Println("Simple interest calculation")
    p := 5000.0
    r := 10.0
    t := 1.0
    si := simpleinterest.Calculate(p, r, t)
    fmt.Println("Simple interest is", si)
}

上面的代码导入了 simpleinterest 包并使用了 Calculate() 函数来计算利息。标准库中的包不需要模块名称前缀,比如直接导入 fmt 包也是可以的。执行代码输出:

Simple interest calculation
Simple interest is 500

关于 go install 更多的知识

现在我们了解了包的工作原理,是时候来讨论关于 go install 的用法了。像 go install 这样的 Go 工具在当前目录的上下文中工作。我们来理解是什么意思,到目前为止,我们一直在目录 ~/Documents/learnpackage/ 中运行 go install。如果我们尝试在其他任何目录执行这个命令,将会报错。

尝试执行命令 cd ~/Documents/ 然后运行 go install learnpackage,它将失败并出现以下错误:

can't load package: package learnpackage: cannot find package "learnpackage" in any of:
    /usr/local/Cellar/go/1.13.7/libexec/src/learnpackage (from $GOROOT)
    /Users/nramanathan/go/src/learnpackage (from $GOPATH)

我们一起来分析下错误的原因,go install 需要一个可选的包名作为参数(在我们的例子中包名是 learnpackage),如果执行命令的当前目录或者其父级目录下存在这个包,它会尝试编译 main 函数。

我们在 Documents 目录中没有 go.mod 文件,因此 go install 会提示找不到包 learnpackage。

当我们 cd 到 ~/Documents/learnpackage/ 目录时,该目录下存在 go.mod 文件,该文件中指定了模块名称 learnpackage。

所以 go install learnpackage 将在 ~/Documents/learnpackage/ 目录中工作。

但是到目前为止,我们只是在使用 go install 并且没有指定包名。如果没有指定包名,go install 将默认为当前工作目录中的模块名,这就是我们没有指定包名但仍然能执行成功的原因。因此,在 ~/Documents/learnpackage/ 目录下执行下面 3 个命令是等效的:

go install

go install .

go install learnpackage

上面我们提到 go install 能够递归地在父目录中搜索 go.mod 文件,让我们检查一下这点是否正确。

cd ~/Documents/learnpackage/simpleinterest/

执行上面的命令 cd 到 simpleinterest 目录,在该目录下执行下面的命令:

go install learnpackage

Go install 将能在父目录 learnpackage 中找到一个 go.mod 文件,该文件定义了模块 learnpackage,因此它可以工作。

可导出

simpleinterest 包里的 Calculate() 函数开头字母是大写的。这在 Go 中有特别的定义,任何以大写字母开头的变量或函数都是可导出。只能访问其他包中可导出的变量或者函数,在我们这个例子中,我们能在 main 包里访问 Calculate() 函数,因为它是可导出的。

如果将 Calculate() 函数开头字母小写 calculate(),再次尝试从 main 包访问,将会报错:

# learnpackage
./main.go:13:8: cannot refer to unexported name simpleinterest.calculate
./main.go:13:8: undefined: simpleinterest.calculate

因此,如果你想从其他包能访问包内的函数,则应将其首字母大写。

init 函数

Go 里每个包都可以包含一个 init() 函数。init() 函数不能有任何返回值,也不能有任何参数。在代码中不能显式地调用 init() 函数,包初始化时会自动调用 init() 函数。init() 函数语法如下:

func init() {
}

init() 函数可以用于执行初始化任务,也可用于程序运行前一些参数的验证。

包的初始化顺序如下:

1.首先初始化包级别的变量;2.接着会调用 init() 函数,一个包可以有多个 init() 函数(在单个文件中或分布在多个文件中),它们按照呈现给编译器的顺序被调用;

如果一个包导入其他包,则首先初始化导入的包。

一个包即使被多个包导入,也只会被初始化一次。

我们对之前的程序做一些修改,以便能更好地学习了解 init() 函数。

首先,让我们将 init 函数添加到 simpleinterest.go 文件中。

package simpleinterest

import "fmt"

/*
 * init function added
 */
func init() {
    fmt.Println("Simple interest package initialized")
}
//Calculate calculates and returns the simple interest for principal p, rate of interest r for time duration t years
func Calculate(p float64, r float64, t float64) float64 {
    interest := p * (r / 100) * t
    return interest
}

上面的代码添加了一个简单的 init 函数,它只负责打印 Simple interest package initialized。

现在,我们来修改下 main 包,我们都知道计算利息时、利率和时间都应该大于零,我们将在 main.go 文件里定义包级别的变量并且在 init() 函数校验这些变量。

main.go 文件如下:

package main

import (
    "fmt"
    "learnpackage/simpleinterest" //importing custom package
    "log"
)
var p, r, t = 5000.0, 10.0, 1.0

/*
* init function to check if p, r and t are greater than zero
 */
func init() {
    println("Main package initialized")
    if p < 0 {
        log.Fatal("Principal is less than zero")
    }
    if r < 0 {
        log.Fatal("Rate of interest is less than zero")
    }
    if t < 0 {
        log.Fatal("Duration is less than zero")
    }
}

func main() {
    fmt.Println("Simple interest calculation")
    si := simpleinterest.Calculate(p, r, t)
    fmt.Println("Simple interest is", si)
}

main.go 修改如下:

1.p、r 和 t 变量从主函数级别移至包级别。2.添加了一个 init() 函数,如果本 金、利率或时间有一个小于 0,log.Fatal() 会打印日志并终止程序。

初始化顺序如下:

1.导入的包首先被初始化,因此 simpleinterest 包首先被初始化并且它的 init 方法被调用。2.接下来初始化包级变量 p、r 和 t。3.接着调用 main.go 里的 init() 函数。4.最后调用 main() 函数。

执行程序将会输出:

Simple interest package initialized
Main package initialized
Simple interest calculation
Simple interest is 500

正如我们预期的那样,simpleinterest 包的 init 函数首先被调用,然后是包级变量 p、r 和 t 的初始化,接下来调用 main 包的 init() 函数,它检查 p、r 和 t 是否小于零,如果 if 语句为真则会终止程序。关于 if 语句的使用我们将在另外章节介绍。在我们的程序里,所有的 if 条件都为 false,程序继续执行,最后调用 main() 函数。

我们来简单修改下代码,

将 main.go 文件的这行代码:

var p, r, t = 5000.0, 10.0, 1.0

修改成:

var p, r, t = -5000.0, 10.0, 1.0

我们已将 p 初始化为负数。

现在如果执行程序就会输出:

Simple interest package initialized
Main package initialized
2020/02/15 21:25:12 Principal is less than zero

因为 p 是负数,所有程序在输出 Principal is less than zero 之后便终止了。

空白符 _

源代码里导入包如果不适用是非法的,编译器编译时会提示,这样做的原因是为了避免未使用的包太多,这将显着增加编译时间。将 main.go 中的代码替换为以下内容:

package main

import (
        "learnpackage/simpleinterest"
)

func main() {

}

执行上面的代码将会报错:

# learnpackage
./main.go:4:2: imported and not used: "learnpackage/simpleinterest"

但是在平时开发时,导入一个包可能现在不用,在之后代码的某个地方使用是很常见的,这时候该怎么办?

这时候空白符 _ 就能派上用场了,上面代码的报错可以使用空白符消除。

package main

import (
        "learnpackage/simpleinterest"
)

var _ = simpleinterest.Calculate

func main() {

}

上面的代码行 var _ = simpleinterest.Calculate 虽然能消除错误,但是不建议这种方式,我们应该特别留意这类代码,并在程序开发完成之后删除,包括导入但未使用的包。因此,建议在使用 import 导入包时使用 _ 消除此类错误。

有时我们导入一个包只是为了确保初始化,即使我们不需要使用包中的任何函数或变量。例如,我们可能需要确保调用 simpleinterest 包的 init 函数,即使我们不需要用到包里的任何函数或者变量。这种情况下也可以使用 _ 空白标识符,如下所示:

package main

import (
    _ "learnpackage/simpleinterest"
)

func main() {

}

执行上面的代码会输出 Simple Interest package initialized,我们已经成功地初始化了 simpleinterest 包,即使它没有在代码中的任何地方被使用。

위 내용은 Go 언어 기본 패키지의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Go语言进阶学习에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제