Go基础教程系列之import导入包(远程包)和变量初始化详解

import导入包

搜索路径

import用于导入包:

import (
    "fmt"
    "net/http"
    "mypkg"
)

编译器会根据上面指定的相对路径去搜索包然后导入,这个相对路径是从GOROOT或GOPATH(workspace)下的src下开始搜索的。

假如go的安装目录为/usr/local/go,也就是说GOROOT=/usr/local/go,而GOPATH环境变量GOPATH=~/mycode:~/mylib,那么要搜索net/http包的时候,将按照如下顺序进行搜索:

/usr/local/go/srcnet/http
~/mycode/src/net/http
~/mylib/src/net/http

以下是go install搜索不到mypkg包时的一个报错信息:

can't load package: package mypkg: cannot find package "mypkg" in any of:
        /usr/lib/go-1.6/src/mypkg (from $GOROOT)
        /golang/src/mypkg (from $GOPATH)

也就是说,go总是先从GOROOT出先搜索,再从GOPATH列出的路径顺序中搜索,只要一搜索到合适的包就理解停止。当搜索完了仍搜索不到包时,将报错。

包导入后,就可以使用这个包中的属性。使用包名.属性的方式即可。例如,调用fmt包中的Println函数fmt.Println

包导入的过程

首先从main包开始,如果main包中有import语句,则会导入这些包,如果要导入的这些包又有要导入的包,则继续先导入所依赖的包。重复的包只会导入一次,就像很多包都要导入fmt包一样,但它只会导入一次。

每个被导入的包在导入之后,都会先将包的可导出函数(大写字母开头)、包变量、包常量等声明并初始化完成,然后如果这个包中定义了init()函数,则自动调用init()函数。init()函数调用完成后,才回到导入者所在的包。同理,这个导入者所在包也一样的处理逻辑,声明并初始化包变量、包常量等,再调用init()函数(如果有的话),依次类推,直到回到main包,main包也将初始化包常量、包变量、函数,然后调用init()函数,调用完init()后,调用main函数,于是开始进入主程序的执行逻辑。

别名导入和特殊的导入方法

当要导入的包重名时会如何?例如network/convert包用于转换从网络上读取的数据,file/convert包用于转换从文件中读取的数据,如果要同时导入它们,当引用的时候指定convert.FUNC(),这个convert到底是哪个包?

可以为导入的包添加一个名称属性,为包设置一个别名。例如,除了导入标准库的fmt包外,自己还定义了一个mypkg/fmt包,那么可以如下导入:

package main

import (
    "fmt"
    myfmt "mypkg/fmt"
)

func main() {
    fmt.Println()
    myfmt.myfunc()   // 使用别名进行访问
}

如果不想在访问包属性的时候加上包名,则import导入的时候,可以为其设置特殊的别名:点(.)。

import (
    . "fmt"
)

func main() {
    Println()    // 无需包名,直接访问Println
}

这时要访问fmt中的属性,必须不能使用包名fmt。

go要求import导入的包必须在后续中使用,否则会报错。如果想要避免这个错误,可以在包的前面加上下划线:

import (
    "fmt"
    _ "net/http"
    "mypkg"
)

这样在当前包中就无需使用net/http包。其实这也是为包进行命名,只不过命名为"_",而这个符号又正好表示丢弃赋值结果,使得这成为一个匿名包。

下划线(_)在go中,下划线出现的频率非常高,它被称为blank identifier,可以用于赋值时丢弃值,可以用于保留import时的包,还可以用于丢弃函数的返回值。详细内容可参见官方手册:https://golang.org/doc/effective_go.html#blank

导入而不使用看上去有点多此一举,但并非如此。因为导入匿名包仅仅表示无法再访问其内的属性。但导入这个匿名包的时候,会进行一些初始化操作(例如init()函数),如果这个初始化操作会影响当前包,那么这个匿名导入就是有意义的。

远程包

现在通过分布式版本控制系统进行代码共享是一种大趋势。go集成了从gti上获取远程代码的能力。

例如:

$ go get github.com/golang/example

在import语句中也可以使用,首先从GOPATH中搜索路径,显然这是一个URL路径,于是调用go get进行fetch,然后导入。

import (
    "fmt"
    "github.com/golang/example"
)

当需要从git上获取代码的时候,将调用go get工具自动进行fetch、build、install。如果workspace中已经有这个包,那么将只进行最后的install阶段,如果没有这个包,将保存到GOPATH的第一个路径中,并build、install。

go get是递归的,所以可以直接fetch整个代码树。

常量和变量的初始化

Go中的常量在编译期间就会创建好,即使是那些定义为函数的本地常量也如此。常量只允许是数值、字符(runes)、字符串或布尔值。

由于编译期间的限制,定义它们的表达式必须是编译器可评估的常量表达式(constant expression)。例如,1<<3是一个常量表达式,而math.Sin(math.Pi/4)则不是常量表达式,因为涉及了函数math.Sin()的调用过程,而函数调用是在运行期间进行的。

变量的初始化和常量的初始化差不多,但初始化的变量允许是"需要在执行期间计算的一般表达式"。例如:

var (
    home   = os.Getenv("HOME")
    user   = os.Getenv("USER")
    gopath = os.Getenv("GOPATH")
)

init()函数

Go中除了保留了main()函数,还保留了一个init()函数,这两个函数都不能有任何参数和返回值。它们都是在特定的时候自动调用的,无需我们手动去执行。

还是这张图:

每个包中都可以定义init函数,甚至可以定义多个,但建议每个包只定义一个。每次导入包的时候,在导入完成后,且变量、常量等声明并初始化完成后,将会调用这个包中的init()函数。

对于main包,如果main包也定义了init(),那么它会在main()函数之前执行。当main包中的init()执行完之后,就会立即执行main()函数,然后进入主程序。

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

更多关于Go语言中 import导入包(远程包)和变量初始化技术文章请查看下面的相关链接

(0)

相关推荐

  • 如何解决django配置settings时遇到Could not import settings 'conf.local'

    举个例子吧 Django最佳实践与部署:Nginx + Gunicorn + Supervisor(Ubuntu和CentOS) http://sfdye.com/articles/django-best-practice-and-deployment-with-nginx-gunicorn-and-supervisor/ 结果出现runserver --settings=...的时候出现报错Could not import settings 'conf.local' (Is it on sys

  • golang import自定义包方式

    1.初探 刚开始接触go时,以为import自定义包与Java工程类似,在非GOPATH的路径中新建了一个go项目HelloGo,如下: 接下来,利用beego的bee工具新建了另一个项目helloapi,如下: 现在,想在helloapi项目中调用HelloGo项目的函数,通过import引入"HelloGo",编辑器中一直提示无法引用,即使把HelloGo添加到左下角的GOPATH[helloapi]中,也没有效果. 具体如下: 问题出在HelloGo项目的路径上,因为其不在系统定

  • 解决goland 导入项目后import里的包报红问题

    解决办法: 1.Goland--->Preferences...--->Go--->GOPATH--->Project GOPATH下添加命令行go env下的GOPATH值,自行修改自己的Project GOPATH 2.正常情况下我们不需要自己去处理外部包的代理,但是也有可能就是你不经意之间做了修改,这边也是通过setting进行配置: 补充:GOLAND 导入项目后import里的包报红,以及$GOPATH/go.mod exists but should not 首先 im

  • 详解golang避免循环import问题(“import cycle not allowed”)

    前言 golang不允许循环import package ,如果检测到 import cycle ,会在编译时报错,通常import cycle是因为设计错误或包的规划问题. 以下面的例子为例,package a依赖package b,同事package b依赖package a package a import ( "fmt" "github.com/mantishK/dep/b" ) type A struct { } func (a A) PrintA() {

  • 对Golang import 导入包语法详解

    package 的导入语法 写 Go 代码的时经常用到 import 这个命令用来导入包,参考如下: import( "fmt" ) 然后在代码里面可以通过如下的方式调用: fmt.Println( "我爱北京天安门" ) fmt 是 Go 的标准库,它其实是去 GOROOT 下去加载该模块,当然 Go 的 import 还支持如下两种方式来加载自己写的模块: 相对路径 import "./model" // 当前文件同一目录的 model 目录

  • 解决golang在import自己的包报错的问题

    原因: 使用git clone项目后,项目根路径是小写英文名称,比如cmdbapi,但是项目里面的import导入自己的相关包时,红色报错 解决: 把项目名称改写成import导入包的名称,即cmdbApi.当然也可以将import导入包改写成小写. 补充:golang之import导入包的一些特殊用法 我们学习了golang之后,晓得关键字import是go的包导入语法,而且我们的第一个程序通常都需要输出hello world. 示例如下: import( "fmt" ) fmt.P

  • Golang import 导入包语法及一些特殊用法详解

    package 的导入语法 写 Go 代码的时经常用到 import 这个命令用来导入包,参考如下: import( "fmt" ) 然后在代码里面可以通过如下的方式调用: fmt.Println( "我爱我们" ) fmt 是 Go 的标准库,它其实是去 GOROOT 下去加载该模块,当然 Go 的 import 还支持如下两种方式来加载自己写的模块: 相对路径 import "./model" // 当前文件同一目录的 model 目录,但是

  • go各种import的使用方法讲解

    go的各种import 最简单的: package main import "fmt" func main() { fmt.Println("hehe") // ok } 不用说. 导入的特殊用法 那么, 带一个点, 是什么意思呢? 看看: package main import . "fmt" func main() { Println("hehe") // ok } 可见, 带点后, 在调用时可以省略包名. 继续看, 下划

  • golang中import cycle not allowed解决的一种思路

    发现问题 项目中碰到了一些问题,使用了指针函数的思路来解决相应问题 在实际项目中,因为两个项目互相引了对方的一些方法,导致了循环引用的错误,原本可以使用http的请求来解决,经过其他人指导,发现了可以使用指针函数来解决 这里通过一个简单的例子来说明,首先是例子的结构,我们仅仅用到了这两个文件:ciconnector.go和rockettest.go 例子结构 原本设定的方法是这两个包里的函数互相调用,那么我们通过代码来看下怎么解决相应的问题. 首先是rockettest.go,这里调用了cico

  • Golang中 import cycle not allowed 问题的解决方法

    目录 1.问题现象 2.问题分析 3.解决问题思路 4.如何破除依赖 5.问题回顾 1.问题现象 go编译的时候报错import cycle not allowed cycle意思很简单就是循环的意思.代表的就是一个包被循环的导入. 2.问题分析 这个错是在container_cloud/pkg/service这里出现的,说明这个包被循环导入. 可是service这个包文件太多怎么检查呢? 可以看到这个service包下面的文件特别多. 3.解决问题思路 虽然文件很多,但是看自己最近修改过的文件

  • Golang import本地包和导入问题相关详解

    1 本地包声明 包是Go程序的基本单位,所以每个Go程序源代码的开始都是一个包声明: package pkgName 这就是包声明,pkgName 告诉编译器,当前文件属于哪个包.一个包可以对应多个*.go源文件,标记它们属于同一包的唯一依据就是这个package声明,也就是说:无论多少个源文件,只要它们开头的package包相同,那么它们就属于同一个包,在编译后就只会生成一个.a文件,并且存放在$GOPATH/pkg文件夹下. 示例: (1) 我们在$GOPATH/目录下,创建如下结构的文件夹

  • Golang报“import cycle not allowed”错误的2种解决方法

    前言 相信不少 Gopher 在写 Golang 程序都遇到过 import cycle not allowed 问题,本人最近研读 go-ethereum 源码时,发现定义 interface 也能解决此问题, 还能解决连分包都不能解决的情况, 并且比分包更加简单快捷.下面逐个讲解 分包 和 定义接口 这两种方法. 1. 应用场景 假设有如下使用场景: A 是应用程序的框架级结构体,在 A 包含子模块 B 和 C 的指针: B 为了方便的使用应用的其他子模块(比如 C )功能,所以在其结构体包

  • golang 之import和package的使用

    golang 使用包 package 来管理定义模块,可以使用 import 关键字来导入使用. 如果导入的是 go 自带的包,则会去安装目录 $GOROOT/src 按包路径加载,如 fmt 包 如果是我们 go get 安装或自定义的包,则会去 $GOPATH/src 下加载 package 的定义 package 的存放位置是以 $GOPATH/src 作为根目录,然后灵活的按照目录去组织,且包名需与最后一级目录名一致. 例如我们自定义 baz 包,包模块的存放位置则为 $GOPATH/s

随机推荐