import 后面的是 package name? 那是 java。
专栏的介绍可以参考 《GotchaGolang专栏》,代码可以看《宝库-Gotcha》。
这里主要有两个坑:
- 一个文件夹下的所有文件必须属于同一个 package (同级目录只有一个包名)
- 一个好的规则是 package 和文件夹名字相同
- import语句所指定的是寻找 package 的 path,而不是 package 的名字
案例
具体的语法如下:
1 | import [packagename] importpath |
package name 可以省略,但是路径名字不能。
先看常见的 fmt 的情况:
1 | import ( |
fmt 是 Golang 的标准库,他其实是去 GOROOT 下去加载该模块(路径名是在 GOROOT 基础上查找的)。
一般可以使用绝对路径或者相对路径来加载自己的模块(省略包的别名)
1 | 相对路径 import "./model" //当前文件同一目录的model目录,但是不建议这种方式来import |
还是最好用 GOPATH/src
这种方式比较好,而不是依赖基于 src 的相对目录。
详细讲解每种方式:
1 | Import declaration Local name of Sin |
- 第一种情况: 我们一般使用第一种方式
import "lib/math"
,然后用math.Sin
来使用具体包内的内容。 - 第二种情况: 给包起一个别名, 例如 import( f “fmt” ) 别名操作调用包函数时前缀变成了重命名的前缀,即f.Println(“hello world”)
- 第三种情况: 使用这个包,可以省略 package 名字,当做本地函数使用。
一般用第一种形式,引入路径,但是在具体的文件中,还是按照 pkgname.xxx
来调用。
导入过程
程序的初始化和执行都起始于 main 包。如果 main 包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。
下图详细地解释了整个执行过程:
init函数
引用但是不使用某个包,使用
_
1 | import ( |
这种做法可以保证 “github.com/xxx” 这个 package 的初始化操作完成(这个package中所有init函数都执行),即使在当前文件中没有显式地使用到这个package。有些时候并非真的需要使用这些包,仅仅是希望它的init()函数被执行而已。这个时候就可以使用操作引用该包了。即使用操作引用包是无法通过包名来调用包中的导出函数,而是 只是为了简单的调用其init函数()
。
通过上面的介绍我们了解了import的时候其实是执行了该包里面的init函数,初始化了里面的变量,_操作只是说该包引入了,只初始化里面的init函数和一些变量,不能通过包名来调用其它的函数,这有什么用呢?往往这些init函数里面是注册自己包里面的引擎,让外部可以方便的使用,就很多实现database/sql的引用,在init函数里面都是调用了sql.Register(name string, driver driver.Driver)注册自己,然后外部就可以使用了。
总结
- 一个包可以有多个 init (最好只有一个);
- 优先导入其他包(初始化变量,调用init),然后才是本包。
- 初始化本包的变量,然后调用本包的 init 函数。
外部包不能和主模块放到一起,可能会编译不过的。
Merlin 2018.3 import 的坑