Golang: import和package坑(4)

import 后面的是 package name? 那是 java。

专栏的介绍可以参考 《GotchaGolang专栏》,代码可以看《宝库-Gotcha》

这里主要有两个坑:

  • 一个文件夹下的所有文件必须属于同一个 package (同级目录只有一个包名)
    • 一个好的规则是 package 和文件夹名字相同
  • import语句所指定的是寻找 package 的 path,而不是 package 的名字

案例

具体的语法如下:

1
import [packagename] importpath

package name 可以省略,但是路径名字不能。

先看常见的 fmt 的情况:

1
2
3
import (
"fmt"
)

fmt 是 Golang 的标准库,他其实是去 GOROOT 下去加载该模块(路径名是在 GOROOT 基础上查找的)。

一般可以使用绝对路径或者相对路径来加载自己的模块(省略包的别名)

1
2
相对路径     import   "./model"             //当前文件同一目录的model目录,但是不建议这种方式来import
绝对路径 import "shorturl/model" //加载GOPATH/src/shorturl/model模块

还是最好用 GOPATH/src 这种方式比较好,而不是依赖基于 src 的相对目录。

详细讲解每种方式:

1
2
3
4
5
Import declaration          Local name of Sin

import "lib/math" math.Sin
import m "lib/math" m.Sin
import . "lib/math" 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
2
3
import (
_ "github.com/xxx"
)

这种做法可以保证 “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 的坑

文章目录
  1. 1. 案例
  2. 2. 导入过程
  3. 3. init函数
  4. 4. 总结
|