最近初学 Golalng ,学到导包时,发现由于 Golang 特殊的包管理机制,导致和 python 导包有些大的区别,记录一下。主要是 golang 的导包、包名和目录名的关系、init初始化阶段等。

一、go 导入包的搜索路径

在 python 中,同级目录文件可以直接 import。我在golang中做了类似的尝试。

编译阶段报错:
main.go:6:2: cannot find package "lib1" in any of:
G:\Go\src\lib1 (from $GOROOT)
C:\Users\Ob\go\src\lib1 (from $GOPATH)
main.go:4:2: cannot find package "lib2" in any of:
G:\Go\src\lib2 (from $GOROOT)
C:\Users\Ob\go\src\lib2 (from $GOPATH)

通过报错,可以发现编译器会在 go 的安装目录和环境变量进行搜索导入的包,而不会在同级目录进行查找。

也就是说,go总是先从 GOROOT 处先搜索,再从 GOPATH 列出的路径顺序中搜索,只要一搜索到合适的包就理解停止。当搜索完了仍搜索不到包时,将报错。
包导入后,就可以使用这个包中的属性。使用包名.属性的方式即可。

我想,这不是很不方便吗,平常做一个测试,还得把包的文件放在指定位置。百度一堆,发现了解决办法,在 go 中导入同级目录需要加 ./ 。

现在就可以正常运行了。

原文是这样描述的:

Go的import还支持如下两种方式来加载自己写的模块:
相对路径 import "./model" //当前文件同一目录的model目录,但是不建议这种方式import
绝对路径 import "shorturl/model" //加载GOPATH/src/shorturl/model模块

go 的导包还是一些特殊的操作。

  1. 别名

给 fmt 包起一个别名 abc,类似于python 的

2. 点操作

导入 fmt 包中的全部方法,在本包中,fmt 包中的全部方法可以直接使用 API 来调用,不需要 fmt.API 来调用。

类似于python的

3. _操作

匿名导入包,_操作只是导入该包。当导入一个包时,它的init()函数会被执行,但有些时候并非真的需要使用这些包,仅仅是希望它的init()函数被执行而已。这个时候就可以使用操作引入该包了。即:使用_操作导入包时无法通过包名来调用包中的函数的,而仅仅是为了简单地调用其 init() 函数。

二、包的导入过程说明

一个包可以简单理解为一个存放.go文件的文件夹。 该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包。

go 的可执行程序一定是 main 包,main 包中又一定有 main () 函数,如果main 包导入了其他包,编译器会在编译时依次导入。当一个包被导入时,如果其导入其他包,也会依次导入,导入过程会执行 init() 初始化函数。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。

go 导包的 init 函数执行过程和 python 基本一致,下图详细地解释了整个执行过程:

go 中 main函数和 init 函数不能有任何参数和返回值,它们都是在特定的时候自动调用的,无需手动执行。每个包中都可以定义init函数,甚至可以定义多个,但建议每个包只定义一个。每次导入包的时候,在导入完成后,且变量、常量等声明并初始化完成后,将会调用这个包中的init()函数。

所以,init() 经常用来初始化环境、安装包或其他需要在程序启动之前先执行的操作。如果import导入包的时候,发现前面命名为下划线 _ 了,一般就说明所导入的这个包有init()函数,且导入的这个包除了init()函数外,没有其它作用。

Go 语言这个包和 python 的包有点不一样,python的包是在一个文件夹下面,而且包名就是文件夹名。Go语言这个包,包名和文件夹名可以不一样,而且一个文件夹下的所有文件都只能属于一个包,而且python需要import 文件夹名(包名).文件名.变量名,而Go语言的不用加文件名,直接import "包名.变量名"。

三、包名、文件名和目录名的关系

在 IDE(我用的 Goland)中,创建目录后新建 go 文件,包名会自动生成,和目录名一致,当然也可以修改。

在第一部分导入包时,我在同级目录导入的包,和目录名是一致的。

看一下具体代码,也顺便复习下初始化函数。

...(假装分割线)

...

运行结果容易预测

lib1. init() ...
lib2. init() ...
lib1Test()...
lib2Test()...
107 k j

我打印 k 和 j 两个字符,也是想体现下 go 的一个特性,打印字符串的话,需要加 string 函数,不然输出的是字符的 ASCII 码值。

go 的目录名、文件名和包名之间是什么关系呢?

包名不必和目录名一致,但为了更好的维护和更高的可读性,普遍的做法是报名和目录名一致,如若不一致,import的时候要写目录名,引用的时候要写包名。

做了一些测试,代码都贴上来有点繁琐,就直接描述结论了。

发现同一个目录下,所有 go文件 必须属于一个 package,否则编译时会报错!文件名无所谓,没有强制要求。

做一些总结:

  1. go 导入包的搜索路径为安装路径和环境变量路径,我们想要导入同级目录下的包时,可使用相对路径(./);
  2. 一个文件夹下面直接包含的文件只能归属一个package,同样一个package的文件不能在多个文件夹下;
  3. 包名可以和文件夹的名字不同,包名不能包含特殊符号;
  4. 包名为main的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码则不会得到可执行文件;
  5. import 导入的参数是路径,而非包名;
  6. 包名不必和目录名一致,但为了更好的维护和更高的可读性,普遍的做法是报名和目录名一致,如若不一致,import的时候要写目录名,引用的时候要写包名;
  7. 尽管习惯将包名和目录名保证一致,但这不是强制规定;
  8. 在代码中引用包成员时,使用包名而非目录名;
  9. 同一目录下,所有源文件必须使用相同的包名称(因为导入时使用绝对路径,所以在搜索路径下,包必须有唯一路径,但无须是唯一名字);
  10. 对文件名,没有限制(扩展名为.go)。

参考资料链接: