- 原文地址:Part 7: Packages
- 原文做者:Naveen R
- 译者:咔叽咔叽 转载请注明出处。
到目前为止,咱们看到的 go 代码只有一个文件,其中有一个 main 函数和其余几个函数。在实际场景中,将全部源代码写入单个文件不是一个好方法。复用和维护这种代码将变得很是艰难,包就是用来解决这些问题的。golang
包可使代码更好的复用和可读,也可使代码解耦,所以使得应用程序很容易维护。windows
例如,假设咱们正在建立一个 go 图像处理的应用程序,它提供了图像裁剪,锐化,模糊和色彩加强等功能。组织此应用程序的一种方法是将与功能相关的全部代码分组到本身的包中。例如,裁剪能够是单个包,锐化能够是另外一个包。这样作的优势是,色彩加强功能可能须要一些锐化功能。色彩加强代码能够简单地导入(咱们将立刻讨论包导入)锐化包并使用其功能。这样代码就变得易于复用。less
咱们将逐步建立一个计算矩形区域和对角线的应用程序,借此应用程序让咱们更好地了解包的做用。函数
每一个可执行的 go 应用程序都必须包含 main 函数。此函数是执行的入口。 main 函数应该放在 main 包中。ui
每一个 go 源代码文件的第一行是package packagename
,该语法用来指定该文件属于哪一个包。spa
让咱们开始为咱们的应用程序建立 main 函数和 main 包。在 go 工做区的 src 文件夹中建立一个文件夹,并将其命名为 geometry。在 geometry 文件夹中建立文件 geometry.go。日志
geometry.go 的代码以下,code
//geometry.go
package main
import "fmt"
func main() {
fmt.Println("Geometrical shape properties")
}
复制代码
代码package main
指定此文件属于 main 包。import "packagename"
语句用于导入现有的包。在该例子中,咱们导入了包含Println
方法的 fmt 包。而后有一个 main 函数,打印了Geometrical shape properties
,教程
经过输入go install geometry
来编译上面的程序。此命令在 geometry 文件夹中搜索具备 main 函数的文件。在该例子中,它会找到 geometry.go。而后编译它并在工做区的bin
文件夹内生成一个名为 geometry 的二进制文件(在 windows 的状况下为 geometry.exe)。如今工做区结构将是开发
src
geometry
gemometry.go
bin
geometry
复制代码
咱们输入workspacepath/bin/geometry
来运行程序。将 workspacepath 替换为 go 工做区的路径。此命令执行 bin 文件夹中的 geometry 二进制文件,你能够看到输出为Geometrical shape properties
。
咱们以这样的方式来组织代码,让与矩形相关的全部功能都在 rectangle 包中。
咱们来建立一个自定义包rectangle
,它拥有计算矩形区域和对角线长度的两个函数。
包的源文件应放在本身的单独文件夹中,Go 中的一个约定是包的名称和此文件夹的名称相同。
所以,咱们在 geometry 文件夹中建立一个名为 rectangle 的文件夹。rectangle 文件夹中的全部文件都应以package rectangle
开头,由于它们都属于rectangle
包。
在 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
函数。矩形的面积是长度和宽度的乘积,矩形的对角线长度是长度和宽度的平方和的平方根。math
包中的Sqrt
函数用于计算平方根。
注意,函数名称Area
和Diagonal
都是大写字母开头,这是必要的,咱们待会解释。
要使用自定义包,必须先导入它。import path
是导入自定义包的语法。咱们必须指定自定义包相对于工做空间内的 src 文件夹的路径。咱们当前的文件夹结构是
src
geometry
geometry.go
rectangle
rectprops.go
复制代码
import "geometry/rectangle"
是导入 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))
}
复制代码
上面的代码导入了 rectangle 包,并使用它的Area
和Diagonal
函数来计算矩形面积和对角线长度。Printf
中的%.2f
格式化符号是将浮点数截取为两位小数。该程序的输出是
Geometrical shape properties
area of rectangle 42.00
diagonal of the rectangle 9.22
复制代码
将 rectangle 包中的函数Area
和Diagonal
首字母大写,这在 Go 中有特殊意义。任何以大写字母开头的变量或函数都是 go 中的可导出命名,用这种命名方式的函数和变量可以被其余包访问。在该例子中,咱们经过这种方式在 main 包中了访问Area
和Diagonal
行数。
若是把 rectprops.go 的函数名由Area(len, wid float64)
更改成area(len, wid float64)
,并在 geometry.go 中由rectangle.Area(rectLen, rectWidth)
更改成rectangle.area(rectLen, rectWidth)
。若是程序运行,编译器将抛出错误geometry.go:11: cannot refer to unexported name rectangle.area
。所以,若是要访问其余包的函数,则应使用可导出的命名方式。
每一个包均可以包含 init 函数。init 函数不该该有任何返回类型,也不该该有任何参数,在咱们的源代码中也没法显式调用 init 函数。 init 函数以下所示
func 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
如今来修改一下 main 包,众所周知矩形的长度和宽度应该大于零。咱们可使用 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
变量从 main 函数级别移动到包级别。rectLen
或rectWidth
小于零则使用log.Fatal
函数打印日志并终止。main 包的初始化顺序是
rectLen
和rectWidth
若是运行程序,将输出
rectangle package initialized
main package initialized
Geometrical shape properties
area of rectangle 42.00
diagonal of the rectangle 9.22
复制代码
正如所料,首先调用 rectangle 包的 init 函数,而后初始化包级变量rectLen
和rectWidth
,接下来调用 main 包的 init 函数,它检查rectLen
和rectWidth
是否小于零,若是小于零则打印日志并终止。咱们会在单独的教程中详细了解 if 语句。如今你能够假设若是rectLen < 0
就是判断rectLen
是否小于 0,若是是,则程序将被终止。咱们为rectWidth
也写了同样的条件。在该例子中,两个条件都为false
,程序继续执行。最后调用 main 函数。
再稍微修改一下该程序,以了解 init 函数的用法。
将 geometry.go 的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
复制代码
正常状况下,先初始化 rectangle 包,而后是 main 包中的包级别变量rectLen
和rectWidth
。rectLen
是负数。所以,当 init 函数运行时,程序在打印length is less than zero
后终止。
_
符号Go 中导入包而且不在代码中的任何位置使用它是非法的。若是你这样作,编译器会报错。这样作的缘由是为了不大量未使用的包显著增长编译时间。用如下代码替换 geometry.go 中的代码,
//geometry.go
package main
import (
"geometry/rectangle" //importing custom package
)
func main() {
}
复制代码
上面的程序将抛出错误geometry.go:6: imported and not used: "geometry/rectangle"
可是,当应用程序处于开发状态时导入各类包很常见,可能接下来不必定要使用这个包。 _
符号能够用于这些场景。
如下代码可使上述程序中正常运行,
package main
import (
"geometry/rectangle"
)
var _ = rectangle.Area //error silencer
func main() {
}
复制代码
该行var _ = rectangle.Area
会使程序不会报错。若是咱们不使用这个包,咱们应该关注这些代码,并在应用程序开发结束时删除它们。所以,在程序开发阶段,能够在 import 语句以后用该方式避免报错。(译者注:通常用_
存储不使用的变量等等,毕竟声明了若是不使用编译器会报错的)
有时咱们导入一个包只是为了使用初始化功能,除此以外咱们不须要使用包中的任何函数或变量。例如,咱们可能须要调用 rectangle 包的 init 函数,即便在代码中的任何位置都不使用该包。在这种状况下也可使用_
符号,以下所示。
package main
import (
_ "geometry/rectangle"
)
func main() {
}
复制代码
运行上面的程序将输出rectangle package initialized
。咱们已经成功初始化了包,即便它没有在代码中的任何地方被使用。(译者注:包导入还有.
操做能够省略前缀包名直接调用该包的函数或者变量。别名操做也比较好理解,就是把前缀包名重命名,语法是 import alias package
)