go语言当中的包

早过忘川 提交于 2020-03-12 14:38:36

说明

在go语言当中,所有的go语言程序都会组织成若干组文件,每组文件都被称为是一个包。通过这样的设置,每个包的代码都可以用来当做复用单元,从而被其他的项目所使用。

例如,我们可以查看一下go当中内置的net包,大概如下:

在这些目录当中包括一系列的以.go为扩展名的相关文件。其中的每一个包都可以单独的导入和引用,从而让开发者可以根据自己的需要导入特定的功能。

go语言当中所谓的包其实就是我们计算机里的文件夹,一个包就是一个文件夹。

所有的.go文件,除了空行和注释,都应该在第一行声明自己所属的包。每个包都在一个单独的目录里。不能把多个包放到同一个目录中,也不能把同一个包的文件分拆到多个不同目录中。这意味着,同一个目录下的所有.go文件必须声明同一个包名。

包的命名

go语言的包的命名,遵循简洁、小写、和go文件所在目录同名的原则,这样就便于我们引用,书写以及快速定位查找(当然包的名字可以和目录名不同,但是不建议这样做)。

并不需要所有包的名字都与别的包不同,因为导入包时是使用全路径的,所以可以区分同名的不同包。一般情况下,包被导入后会使用你的包名作为默认的名字,不过这个导入后的名字可以修改。这个特性在需要导入不同目录的同名包时很有用。

对于自己或者公司开发的程序而言,我们一般采用域名作为顶级包名的方式,这样就不用担心和其他开发者包名重复的问题了。

例如域名为github.com,那么在开发的时候,程序都已github.com作为地址的最顶层部分就可以了。

假如要引入一个工具包,可以如下:

package main 

import "github.com/tools"

当然,如果你使用的ide,那么很多ide在你使用某个包的时候,就会自动的帮助你把包给引入进来。

main包

在go语言当中,main是一个具有特殊意义。Go语言的编译程序会试图把这种名字的包编译为二进制可执行文件。所有用Go语言编译的可执行程序都必须有一个名叫main的包。

而在main包中,一定要包含main函数,否则程序将不会创建可执行文件。main函数是程序的入口,所以如果程序当中没有main函数,那么程序将无法执行。程序编译时,会使用声明main包的代码所在的目录的目录名作为二进制可执行文件的文件名。

我们来创建一个hello的包,来演示内容:

package main 

import "fmt"

func main() {
	fmt.Println("hello,world")
}

我们通过终端切入到hello的目录下,然后执行go build,就会生成可执行文件。

在windows当中会生成.exe可执行文件,在unix或者linxu中会生成二进制可执行文件。

导入包

要想使用一个包,必须先导入它才可以使用,Go语言提供了import关键字来导入一个包,这个关键字告诉Go编译器到磁盘的哪里去找要想导入的包,所以导入的包必须是一个全路径的包,也就是包所在的位置。

import "fmt"

这就表示我们导入了fmt包,也就等于告诉go编译器,我们要使用这个包下面的代码。如果要导入多个包怎么办呢?Go语言还为我们提供的导入块。

import (
	"net/http"
	"fmt"
)

使用一对括号包含的导入块,每个包独占一行。

对于多于一个路径的包名,在代码中引用的时候,使用全路径最后一个包名作为引用的包名,比如net/http,我们在代码使用的是http,而不是net。

远程包的导入

在go开发的过程当中,很多包都被放置在github当中,go语言天生就支持这种远程导入包的情况。

例如:

import "github.com/spf13/cobra"

这种导入,前提必须是该包托管在一个分布式的版本控制系统上,比如Github、Bitbucket等,并且是Public的权限,可以让我们直接访问它们。

当程序进行编译的时候,会先在本地环境里面去找相应的包,如果没有,就会通过go get工具从版本控制系统(GitHub)获取,下载到指定的位置上供编译使用。

go get工具可以递归获取依赖包,如果github.com/spf13/cobra也引用了其他的远程包,该工具可以一并下载下来。

命名导入

我们知道,在使用import关键字导入包之后,我们就可以在代码中通过包名使用该包下相应的函数、接口等。如果我们导入的包名正好有重复的怎么办呢?针对这种情况,Go语言可以让我们对导入的包重新命名,这就是命名导入。

package main

import (
	"fmt"
	myfmt "mylib/fmt"
)

func main() {
	fmt.Println()
	myfmt.Println()
}

如果没有重新命名,那么对于编译器来说,这两个fmt它是区分不清楚的。重命名也很简单,在我们导入的时候,在包名的左侧,起一个新的包名就可以了。

Go语言规定,导入的包必须要使用,否则会包编译错误,这是一个非常好的规则,因为这样可以避免我们引用很多无用的代码而导致的代码臃肿和程序的庞大,因为很多时候,我们都不知道哪些包是否使用,这在C和Java上会经常遇到,有时候我们不得不借助工具来查找我们没有使用的文件、类型、方法和变量等,把它们清理掉。

但是有时候,我们需要导入一个包,但是又不使用它,按照规则,这是不行的,为此Go语言给我们提供了一个空白标志符_,只需要我们使用_重命名我们导入的包就可以了。

package main

import (
	_ "mylib/fmt"
)

函数init函数

每个包都可以有任意多个init函数,这些init函数都会在main函数之前执行。init函数通常用来做初始化变量、设置包或者其他需要在程序执行前的引导工作。比如上面我们讲的需要使用_空标志符来导入一个包的目的,就是想执行这个包里的init函数。

我们以数据库的驱动为例,Go语言为了统一关于数据库的访问,使用databases/sql抽象了一层数据库的操作,可以满足我们操作MYSQL、Postgre等数据库,这样不管我们使用这些数据库的哪个驱动,编码操作都是一样的,想换驱动的时候,就可以直接换掉,而不用修改具体的代码。

这些数据库驱动的实现,就是具体的,可以由任何人实现的,它的原理就是定义了init函数,在程序运行之前,把实现好的驱动注册到sql包里,这样我们就使用使用它操作数据库了。

package mysql

import (
	"database/sql"
)

func init() {
	sql.Register("mysql", &MySQLDriver{})
}

因为我们只是想执行这个mysql包的init方法,并不想使用这个包,所以我们在导入这个包的时候,需要使用_重命名包名,避免编译错误。

import "database/sql"
import _ "github.com/go-sql-driver/mysql"

db, err := sql.Open("mysql", "user:password@/dbname")
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!