详解Go语言中关于包导入必学的 8 个知识点
作者:写代码的明哥 发布时间:2023-07-09 05:38:11
1. 单行导入与多行导入
在 Go 语言中,一个包可包含多个 .go
文件(这些文件必须得在同一级文件夹中),只要这些 .go
文件的头部都使用 package
关键字声明了同一个包。
导入包主要可分为两种方式:
单行导入
import "fmt"
import "sync"
多行导入
import(
"fmt"
"sync"
)
如你所见,Go 语言中 导入的包,必须得用双引号包含,在这里吐槽一下。
2. 使用别名
在一些场景下,我们可能需要对导入的包进行重新命名,比如
我们导入了两个具有同一包名的包时产生冲突,此时这里为其中一个包定义别名
import (
"crypto/rand"
mrand "math/rand" // 将名称替换为mrand避免冲突
)
我们导入了一个名字很长的包,为了避免后面都写这么长串的包名,可以这样定义别名
import hw "helloworldtestmodule"
防止导入的包名和本地的变量发生冲突,比如 path 这个很常用的变量名和导入的标准包冲突。
import pathpkg "path"
3. 使用点操作
如里在我们程序内部里频繁使用了一个工具包,比如 fmt,那每次使用它的打印函数打印时,都要 包名+方法名。
对于这种使用高频的包,可以在导入的时,就把它定义会 "自己人
"(方法是使用一个 .
),自己人的话,不分彼此,它的方法,就是我们的方法。
从此,我们打印再也不用加 fmt 了。
import . "fmt"
func main() {
Println("hello, world")
}
但这种用法,会有一定的隐患,就是导入的包里可能有函数,会和我们自己的函数发生冲突。
4. 包的初始化
每个包都允许有一个 init
函数,当这个包被导入时,会执行该包的这个 init
函数,做一些初始化任务。
对于 init
函数的执行有两点需要注意
init
函数优先于 main
函数执行
在一个包引用链中,包的初始化是深度优先的。比如,有这样一个包引用关系:main→A→B→C,那么初始化顺序为
C.init→B.init→A.init→main
5. 包的匿名导入
当我们导入一个包时,如果这个包没有被使用到,在编译时,是会报错的。
但是有些情况下,我们导入一个包,只想执行包里的 init
函数,来运行一些初始化任务,此时怎么办呢?
可以使用匿名导入,用法如下,其中下划线为空白标识符,并不能被访问
// 注册一个PNG decoder
import _ "image/png"
由于导入时,会执行 init 函数,所以编译时,仍然会将这个包编译到可执行文件中。
6. 导入的是路径还是包?
当我们使用 import 导入 testmodule/foo
时,初学者,经常会问,这个 foo
到底是一个包呢,还是只是包所在目录名?
import "testmodule/foo"
为了得出这个结论,专门做了个试验(请看「第七点里的代码示例」),最后得出的结论是:
导入时,是按照目录导入。导入目录后,可以使用这个目录下的所有包。
出于习惯,包名和目录名通常会设置成一样,所以会让你有一种你导入的是包的错觉。
7. 相对导入和绝对导入
据我了解在 Go 1.10 之前,好像是不支持相对导入的,在 Go 1.10 之后才可以。
绝对导入:从 $GOPATH/src
或 $GOROOT
或者 $GOPATH/pkg/mod
目录下搜索包并导入
相对导入:从当前目录中搜索包并开始导入。就像下面这样
import (
"./module1"
"../module2"
"../../module3"
"../module4/module5"
)
分别举个例子吧
一、使用绝对导入
有如下这样的目录结构(注意确保当前目录在 GOPATH 下)
其中 main.go 是这样的
package main
import (
"app/utilset" // 这种使用的就是绝对路径导入
)
func main() {
utils.PrintHello()
}
而在 main.go 的同级目录下,还有另外一个文件夹 utilset
,为了让你理解 「第六点:import 导入的是路径而不是包」,我在 utilset 目录下定义了一个 hello.go
文件,这个go文件定义所属包为 utils
。
package utils
import "fmt"
func PrintHello(){
fmt.Println("Hello, 我在 utilset 目录下的 utils 包里")
}
运行结果如下
二、使用相对导入
还是上面的代码,将绝对导入改为相对导入后
将 GOPATH 路径设置回去(请对比上面使用绝对路径的 GOPATH)
然后再次运行
总结一下,使用相对导入,有两点需要注意
项目不要放在 $GOPATH/src
下,否则会报错(比如我修改当前项目目录为GOPATH后,运行就会报错)
Go Modules 不支持相对导入,在你开启 GO111MODULE 后,无法使用相对导入。
最后,不得不说的是:使用相对导入的方式,项目可读性会大打折扣,不利用开发者理清整个引用关系。
所以一般更推荐使用绝对引用的方式。使用绝对引用的话,又要谈及优先级了
8. 包导入路径优先级
前面一节,介绍了三种不同的包依赖管理方案,不同的管理模式,存放包的路径可能都不一样,有的可以将包放在 GOPATH 下,有的可以将包放在 vendor 下,还有些包是内置包放在 GOROOT 下。
那么问题就来了,如果在这三个不同的路径下,有一个相同包名但是版本不同的包,我们导入的时候,是选择哪个进行导入呢?
这就需要我们搞懂,在 Golang 中包搜索路径优先级是怎样的?
这时候就需要区分,是使用哪种模式进行包的管理的。
如果使用 govendor
当我们导入一个包时,它会:
先从项目根目录的 vendor 目录中查找
最后从 $GOROOT/src 目录下查找
然后从 $GOPATH/src 目录下查找
都找不到的话,就报错。
为了验证这个过程,我在创建中创建一个 vendor 目录后,就开启了 vendor 模式了,我在 main.go 中随便导入一个包 pkg,由于这个包是我随便指定的,当然会找不到,找不到就会报错, Golang 会在报错信息中打印中搜索的过程,从这个信息中,就可以看到 Golang 的包查找优先级了。
如果使用 go modules
你导入的包如果有域名,都会先在 $GOPATH/pkg/mod
下查找,找不到就连网去该网站上寻找,找不到或者找到的不是一个包,则报错。
而如果你导入的包没有域名(比如 "fmt"这种),就只会到 $GOROOT
里查找。
还有一点很重要,当你的项目下有 vendor 目录时,不管你的包有没有域名,都只会在 vendor 目录中想找。
通常vendor
目录是通过 go mod vendor
命令生成的,这个命令会将项目依赖全部打包到你的项目目录下的 verdor 文件夹中。
延伸阅读如何使用go module导入本地包
来源:https://blog.csdn.net/weixin_36338224/article/details/106314521


猜你喜欢
- 一、python开发环境安装与配置1.python的下载从https://www.python.org/downloads/进行下载。2.安
- python-pymysql获取字段名称-获取内容获取字段名称-获取内容import pymysql# 连接数据库db = pymysql.
- 张量范数:torch.norm(input, p=2) → float返回输入张量 input 的 p 范数举个例子:>>>
- unsigned 既为非负数,用此类型可以增加数据长度! 例如如果 tinyint最大是127,那 tinyint unsigned 最大
- Jquery中的一些东西学习一下子,补充完善一下,毕竟有些时候没有使用到这个方式很有用,在使用bootstrap table的时候,选择当前
- 因为有时直接使用pip install在线安装 Python 库下载速度非常慢,所以这里介绍使用 Anaconda 离线安装 Python
- zip文件格式是通用的文档压缩标准,在ziplib模块中,使用ZipFile类来操作zip文件,下面具体介绍一下:class zipfile
- 最近在使用layui前端框架,在使用单选按钮、下拉菜单select、checkbox等控件的时候 往往遇到一些初始化的东西。有时候会发现,自
- 本文实例讲述了Python爬虫PyQuery库基本用法。分享给大家供大家参考,具体如下:PyQuery库也是一个非常强大又灵活的网页解析库,
- 如下所示:# coding=utf-8import pandas as pd# 读取csv文件 3列取名为 name,sex,births,
- forma格式化的用法format函数可以接受不限个参数,位置可以不按顺序。基本语法是通过{ }和:来代替c语言的%。>>>
- 有些项目可能涉及到使用多个数据库的情况,方法很简单。1.在settings中设定DATABASE比如要使用两个数据库:DATABASES =
- 前言在日常开发编程中,我们有时从用户那里得到一些输入信息,对于特定应用,部分信息不允许包含中文字符,那如何检测信息字符串中是否包含中文字符呢
- python strip() 函数和 split() 函数的详解及实例一直以来都分不清楚strip和split的功能,实际上st
- Function ChkInvaildWord(Words) Const InvaildWords=&quo
- 进程Process是对各种资源管理的集合,包含对各种资源的调用、内存的管理、网络接口的调用;一个进程Process可以包含多个子进程,启动一
- 本文给大家介绍有关数据库SQL递归查询在不同数据库中的实现方法,具体内容请看下文。比如表结构数据如下:Table:TreeID Name P
- 这篇文章主要介绍了Python远程开发环境部署与调试过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 这篇论坛文章主要介绍了Oracle数据库到SQL Server数据库主键的迁移过程,具体内容请参考下文。由于项目需要要将以前Oracle的数
- 一、数组的创建方式一var a = new Array(); a[0]="wo"