golang教程之包
包
什么是包,为什么使用它们?
到目前为止,我们已经看到go程序只有一个文件,其中包含一个主函数和其他几个函数。在实际场景中,将所有源代码写入单个文件的方法不起作用。重用和维护以这种方式编写的代码变得不可能。这是包保存的地方。
包用于组织go源代码以获得更好的可重用性和可读性。软件包提供了代码的分隔,因此可以轻松维护应用程序。
例如,假设我们正在创建一个go图像处理应用程序,它提供了图像裁剪,锐化,模糊和颜色增强等功能。组织此应用程序的一种方法是将与功能相关的所有代码分组到自己的包中。例如,裁剪可以是单个包装,锐化可以是另一个包装。这样做的好处是,颜色增强功能可能需要一些锐化功能。颜色增强代码可以简单地导入(我们将在一分钟内讨论导入)锐化包并开始使用其功能。这样代码就变得易于重用。
我们将逐步创建一个计算矩形区域和对角线的应用程序。
我们将通过此应用程序更好地了解包。
主要功能和主要包
每个可执行的go应用程序都必须包含main函数。此函数是执行的入口点。 main函数应该驻留在主包中。
指定特定源文件属于包的代码行是包packagename。这应该是每个go源文件的第一行。
让我们开始为我们的应用程序创建主要功能和主要包。在go工作区的src文件夹中创建一个文件夹,并将其命名为geometry。在几何文件夹中创建文件geometry.go。
在geometry.go中编写以下代码
//geometry.go
package main
import "fmt"
func main() {
fmt.Println("Geometrical shape properties")
}
代码package main
指定此文件属于主包。 import "packagename"
语句用于导入现有包。 在这种情况下,我们导入包含Println
方法的fmt
包。 然后有一个主要功能,可以打印几何形状属性
通过输入go install geometry
来编译上面的程序。 此命令在几何文件夹中搜索具有main函数的文件。 在这种情况下,它找到geometry.go。 然后它编译它并在工作区的bin文件夹内生成一个名为geometry的二进制文件(在windows的情况下为geometry.exe)。 现在工作区结构将是
src
geometry
gemometry.go
bin
geometry
让我们输入workspacepath/bin/geometry
来运行程序。 将workspacepath替换为go工作区的路径。 此命令执行bin文件夹内的几何二进制。 您应该将几何形状属性作为输出。
创建自定义包
我们将以这样的方式构造代码,使得与矩形相关的所有功能都在rectangle
包中。
让我们创建一个自定义rectangle
包,它具有确定矩形区域和对角线的功能。
属于包的源文件应放在它们自己的单独文件夹中。 Go中的一个约定是使用与包名称相同的名称命名此文件夹。
因此,我们在几何文件夹中创建一个名为rectangle
的文件夹。 矩形文件夹中的所有文件都应以行包矩形开头,因为它们都属于矩形包。
在我们刚刚创建的矩形文件夹中创建一个文件rectprops.go并添加以下代码。
//rectprops.go
package rectangle
import "math"
func Area(len, wid float64) float64 {
area := len * wid
return area
}
func Diagonal(len, wid float64) float64 {
diagonal := math.Sqrt((len * len) + (wid * wid))
return diagonal
}
在上面的代码中,我们创建了两个计算Area和Diagonal的函数。 矩形区域是长度和宽度的乘积。 矩形的对角线是长度和宽度的平方和的平方根。 数学包中的Sqrt函数用于计算平方根。
请注意,函数名称Area和Diagonal以caps开头。 这是必不可少的,我们将很快解释为什么需要这样做。
导入自定义包
要使用自定义包,我们必须先导入它。 导入路径是导入自定义包的语法。 我们必须指定相对于工作空间内的src文件夹的自定义包的路径。 我们当前的文件夹结构是
src
geometry
geometry.go
rectangle
rectprops.go
行导入“geometry/rectangle”将导入矩形包。
将以下代码添加到geometry.go
//geometry.go
package main
import (
"fmt"
"geometry/rectangle" //importing custom package
)
func main() {
var rectLen, rectWidth float64 = 6, 7
fmt.Println("Geometrical shape properties")
/*Area function of rectangle package used
*/
fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))
/*Diagonal function of rectangle package used
*/
fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}
上面的代码导入了矩形包,并使用它的Area和Diagonal函数来查找矩形的面积和对角线。 Printf中的%.2f格式说明符是将浮点数截断为两位小数。 应用程序的输出是
eometrical shape properties
area of rectangle 42.00
diagonal of the rectangle 9.22
导出的名称
我们将矩形包中的函数Area和Diagonal大写。这在Go中有特殊意义。任何以大写字母开头的变量或函数都是go中的导出名称。只能从其他包访问导出的函数和变量。在这种情况下,我们需要从主包访问区域和对角线功能。因此他们资本化。
如果函数名在rectprops.go中从Area(len,wid float64)
更改为area(len,wid float64)
,并在geometry.go中从rectangle.Area(rectLen,rectWidth)
更改为rectangle.area(rectLen,rectWidth)
。如果程序运行,编译器将抛出error geometry.go:11:不能引用未导出的名称rectangle.area。因此,如果要访问包外的函数,则应将其大写。
init函数
每个包都可以包含一个init函数。 init函数不应该有任何返回类型,不应该有任何参数。在我们的源代码中无法显式调用init函数。 init函数如下所示
func init() {
}
init函数可用于执行初始化任务,也可用于在执行开始之前验证程序的正确性。
包的初始化顺序如下
- 首先初始化包级别变量
- 接下来调用init函数。 一个包可以有多个init函数(在单个文件中或分布在多个文件中),并按照它们呈现给编译器的顺序调用它们。
如果包导入其他包,则首先初始化导入的包。
即使从多个包导入包,也只会初始化一次包。
让我们对我们的应用程序进行一些修改以理解init函数。
首先,我们将一个init函数添加到rectprops.go文件中。
//rectprops.go
package rectangle
import "math"
import "fmt"
/*
* init function added
*/
func init() {
fmt.Println("rectangle package initialized")
}
func Area(len, wid float64) float64 {
area := len * wid
return area
}
func Diagonal(len, wid float64) float64 {
diagonal := math.Sqrt((len * len) + (wid * wid))
return diagonal
}
我们添加了一个简单的init函数,它只打印rectangle package initialised
现在让我们修改主包。 我们知道矩形的长度和宽度应大于零。 我们将使用geometry.go文件中的init函数和包级别变量来定义此检查。
修改geometry.go文件,如下所示,
//geometry.go
package main
import (
"fmt"
"geometry/rectangle" //importing custom package
"log"
)
/*
* 1. package variables
*/
var rectLen, rectWidth float64 = 6, 7
/*
*2. init function to check if length and width are greater than zero
*/
func init() {
println("main package initialized")
if rectLen < 0 {
log.Fatal("length is less than zero")
}
if rectWidth < 0 {
log.Fatal("width is less than zero")
}
}
func main() {
fmt.Println("Geometrical shape properties")
fmt.Printf("area of rectangle %.2f\n", rectangle.Area(rectLen, rectWidth))
fmt.Printf("diagonal of the rectangle %.2f ",rectangle.Diagonal(rectLen, rectWidth))
}
以下是对geometry.go所做的更改
- rectLen和rectWidth变量从主函数级别移动到包级别。
- 添加了一个init函数。 如果rectLen或rectWidth通过使用log.Fatal函数小于零,则init函数打印日志并终止程序执行。
主程序包的初始化顺序是
- 首先初始化导入的包。 因此geometry包首先被初始化。
- 接下来初始化包级别变量rectLen和rectWidth。
- 调用init函数。
- 最后调用main函数
如果您运行该程序,您将获得以下输出。
rectangle package initialized
main package initialized
Geometrical shape properties
area of rectangle 42.00
diagonal of the rectangle 9.22
正如所料,首先调用矩形包的init函数,然后初始化包级别变量rectLen和rectWidth。 接下来调用主包的init函数。 它检查rectLen和rectWidth是否小于零,如果条件为真则终止。 我们将在单独的教程中详细了解if语句。 现在你可以假设if rectLen < 0
将检查rectLen是否小于0,如果是,则程序将被终止。 我们为rectWidth写了一个类似的条件。 在这种情况下,两个条件都是假的并且程序继续执行。 最后调用main函数。
让我们稍微修改一下该程序,以了解init函数的用法。
var rectLen,rectWidth float64 = 6,7
更改为var rectLen,rectWidth float64 = -6,7
。我们已将rectLen
初始化为负数。
现在,如果您运行该应用程序,您将看到
rectangle package initialized
main package initialized
2017/04/04 00:28:20 length is less than zero
像往常一样,初始化矩形包,然后是主包中的包级别变量rectLen和rectWidth。 rectLen是否定的。 因此,当init函数接下来运行时,程序在打印长度小于零后终止。
该代码可从github下载。
使用空白标识符
在Go中导入包并且不在代码中的任何地方使用它是非法的。 如果你这样做,编译器会抱怨。 这样做的原因是为了避免未使用的包膨胀,这将显着增加编译时间。 用以下代码替换geometry.go中的代码,
//geometry.go
package main
import (
"geometry/rectangle" //importing custom package
)
func main() {
}
上面的程序将抛出 错误geometry.go:6: imported and not used:geometry/rectangle"
但是,当应用程序处于活动开发状态时导入包很常见,如果不是现在则在代码中的某处使用它们。 _
blank标识符可以在这些情况下保存我们。
以下代码可以使上述程序中的错误无效,
package main
import (
"geometry/rectangle"
)
var _ = rectangle.Area //error silencer
func main() {
}
var _ = rectangle.Area
使错误无效。 如果不使用包,我们应该跟踪这些错误静默器,并在应用程序开发结束时删除它们,包括导入的包。 因此,建议在import语句之后在包级别中编写错误静默器。
有时我们需要导入一个包只是为了确保初始化发生,即使我们不需要使用包中的任何函数或变量。 例如,我们可能需要确保调用矩形包的init函数,即使我们在代码中的任何位置都不使用该包。 在这种情况下也可以使用_
blank标识符,如下所示。
package main
import (
_ "geometry/rectangle"
)
func main() {
}
运行上面的程序将输出矩形包初始化。 我们已经成功初始化了包,即使它没有在代码中的任何地方使用。
上一篇: 解决vue单页面应用打包后相对路径、绝对路径相关问题
下一篇: 到这最多40