Go基础11-理解Go语言的包导入
Go语言是使用包(package)作为基本单元来组织源码的,可以说一个Go程序就是由一些包链接在一起构建而成的。虽然与Java、Python等语言相比这算不上什么创新,但与祖辈C语言的头文件包含机制相比则是“先进”了许多。
编译速度快是这种“先进性”的一个突出表现,即便每次编译都是从零开始。Go语言的这种以包为基本构建单元的构建模型使依赖分析变得十分简单,避免了C语言那种通过头文件分析依赖的巨大开销。
Go编译速度快的原因具体体现在以下三方面。
● Go要求每个源文件在开头处显式地列出所有依赖的包导入,这样Go编译器不必读取和处理整个文件就可以确定其依赖的包列表。
● Go要求包之间不能存在循环依赖,这样一个包的依赖关系便形成了一张有向无环图。由于无环,包可以被单独编译,也可以并行编译。
● 已编译的Go包对应的目标文件(file_name.o或package_name.a)中不仅记录了该包本身的导出符号信息,还记录了其所依赖包的导出符号信息。
这样,Go编译器在编译某包P时,针对P依赖的每个包导入(比如导入包Q),只需读取一个目标文件即可(比如:Q包编译成的目标文件中已经包含Q包的依赖包的导出信息),而无须再读取其他文件中的信息。
Go语言中包的定义和使用十分简单。通过package关键字声明Go源文件所属的包:
// xx.go
package a
...
上述源码表示文件xx.go是包a的一部分。
使用import关键字导入依赖的标准库包或第三方包:
package mainimport ("fmt" // 标准库包导入"a/b/c" // 第三方包导入
)
func main() {
c.Func1()
fmt.Println("Hello, Go!")
}
很多Gopher看到上面的代码会想当然地将import后面的“c”“fmt”与c.Func1()和fmt.Println()中的c和fmt认作同一个语法元素:包名。但在深入学习Go语言后,大家会发现事实并非如此。
比如在使用实时分布式消息框架nsq提供的官方client包时,我们的包导入是写成这样的:
import "github.com/nsqio/go-nsq"
但在使用该包提供的导出函数时,我们使用的不是go-nsq.xx而是
nsq.xxx:
q, _ := nsq.NewConsumer("write_test", "ch", config)
很多Gopher在学习Go包导入时或多或少有些疑惑:import后面路径中的最后一个分段到底代表的是什么?是包名还是一个路径?
在本条中我就和大家一起来深入探究和理解一下Go语言的包导入。
Go程序构建过程
我们先来简单了解一下Go程序的构建过程,作为后续理解Go包导入的前导知识。和主流静态编译型语言一样,Go程序的构建简单来讲也是由**编译(compile)和链接(link)**两个阶段组成的。
一个非main包在编译后会对应生成一个.a文件,该文件可以理解为Go包的目标文件,
该目标文件实际上是通过pack工具( G O R O O T / p k g / t o o l / d a r w i n a m d 64 / p a c k )对 . o 文件打包后形成的。默认情况下,在编译过程中 . a 文件生成在临时目录下,除非使用 g o i n s t a l l 安装到 GOROOT/pkg/tool/darwin_amd64/pack)对.o文件打 包后形成的。默认情况下,在编译过程中.a文件生成在临时目录下,除非使用go install 安装到 GOROOT/pkg/tool/darwinamd64/pack)对.o文件打包后形成的。默认情况下,在编译过程中.a文件生成在临时目录下,除非使用goinstall安装到GOPATH/pkg下(Go 1.11版本之前),否则你看不到.a文件。如果是构建可执行程序,那么.a文件会在构建可执行程序的链接阶段起使用。
标准库包的源码文件在 G O R O O T / s r c 下面,而对应的 . a 文件存放在 GOROOT/src下面,而对应的.a文件存放在 GOROOT/src下面,而对应的.a文件存放在GOROOT/pkg/
darwin_amd64下(以macOS为例;如果是Linux系统,则是linux_amd64):
// Go 1.16
$tree -FL 1 $GOROOT/pkg/darwin_amd64
├── archive/
├── bufio.a
├── bytes.a
├── compress/
├── container/
├── context.a
├── crypto/
├── crypto.a
...
“求甚解”的读者可能会提出这样一个问题:构建Go程序时,编译器会重新编译依赖包的源文件还是直接链接包的.a文件呢?下面通过一个实验来给大家答案。
Go 1.10版本引 入了build cache,为了避免build cache给实验过程和分析带来的复杂性,我们使用Go1.9.7版本来进行这个实验。
我们建立实验环境的目录结构如下:
chapter3-demo1
├── cmd/
│ └── app1/
│ └── main.go
└── pkg/
└── pkg1/
└── pkg1.go
由于仅是为了演示,pkg1.go和main.go的源码都很简单:
// cmd/app1/main.go
package main
import (
"github.com/bigwhite/effective-go-book/chapter3-demo1/pkg/pkg1"
)
func main() {
pkg1.Func1()
}
// pkg/pkg1/pkg1.go
package pkg1
import "fmt"
func Func1() {
fmt.Println("pkg1.Func1 invoked")
}
进入chapter3-demo1,执行下面的命令:
$go install github.com/bigwhite/effective-go-book/chapter3-demo1/pkg/pkg1
之后,我们就可以在
$GOPATH/pkg/darwin_amd64/github.com/bigwhite/effective-
go-book/chapter3-demo1/pkg下看到pkg1包对应的目标文件pkg1.a:
$ls $GOPATH/pkg/darwin_amd64/github.com/bigwhite/effective-go-book/chapter3-demo1/pkg
pkg1.a
我们继续在chapter3-demo1路径下编译可执行程序app1:
$go build github.com/bigwhite/effective-go-book/chapter3-demo1/cmd/app1
执行完上述命令后,我们会在chapter3-demo1下看到一个可执行文件app1,执行该文件:
$ls
app1* cmd/ pkg/
$./app1
pkg1.Func1 invoked
这符合我们的预期,但现在我们仍无法知道编译app1使用的到底是pkg1包的源码还是目标文件pkg1.a,因为目前它们的输出都是一致的。
修改一下pkg1.go的代码:
// pkg/pkg1/pkg1.go
package pkg1
import "fmt"
func Func1() {
fmt.Println("pkg1.Func1 invoked - Again")
}
重新编译执行app1,得到如下结果:
$go build github.com/bigwhite/effective-go-book/chapter3-demo1/cmd/app1
$./app1
pkg1.Func1 invoked - Again
究竟是路径名还是包名
通过前面的实验,我们了解到编译器在编译过程中必然要使用的是编译单元(一个包)所依赖的包的源码。
而编译器要找到依赖包的源码文件,就需要知道依赖包的源码路
径。这个路径由两部分组成:基础搜索路径和包导入路径。
基础搜索路径是一个全局的设置,下面是其规则描述。
1)所有包(无论是标准库包还是第三方包)的源码基础搜索路径都包括 G O R O O T / s r c 。 2 )在上述基础搜索路径的基础上,不同版本的 G o 包含的其他基础搜索路径有不同。● G o 1.11 版本之前,包的源码基础搜索路径还包括 GOROOT/ src。 2)在上述基础搜索路径的基础上,不同版本的Go包含的其他基础搜索路径有不同。 ● Go 1.11版本之前,包的源码基础搜索路径还包括 GOROOT/src。2)在上述基础搜索路径的基础上,不同版本的Go包含的其他基础搜索路径有不同。●Go1.11版本之前,包的源码基础搜索路径还包括GOPATH/src。
● Go 1.11~Go 1.12版本,包的源码基础搜索路径有三种模式:
● 经典gopath模式下(GO111MODULE=off): G O P A T H / s r c 。● m o d u l e − a w a r e 模式下( G O 111 M O D U L E = o n ): GOPATH/src。 ● module-aware模式下(GO111MODULE=on): GOPATH/src。●module−aware模式下(GO111MODULE=on):GOPATH/pkg/mod。
● auto模式下(GO111MODULE=auto):在 G O P A T H / s r c 路径下,与 g o p a t h 模式相同;在 GOPATH/src路径下,与gopath模式 相同;在 GOPATH/src路径下,与gopath模式相同;在GOPATH/src路径外且包含go.mod,与module-aware模式相同。
● Go 1.13版本,包的源码基础搜索路径有两种模式:
● 经典gopath模式下(GO111MODULE=off): G O P A T H / s r c 。● m o d u l e − a w a r e 模式下( G O 111 M O D U L E = o n / a u t o ): GOPATH/src。 ● module-aware模式下(GO111MODULE=on/auto): GOPATH/src。●module−aware模式下(GO111MODULE=on/auto):GOPATH/pkg/mod。
● 未来的Go版本将只有module-aware模式,即只在module缓存的目录下搜索包的源码。
而搜索路径的第二部分就是位于每个包源码文件头部的包导入路径。基础搜索路径与
包导入路径结合在一起,Go编译器便可确定一个包的所有依赖包的源码路径的集合,这个
集合构成了Go编译器的源码搜索路径空间。看下面这个例子:
// p1.go
package p1
import (
"fmt"
"time"
"github.com/bigwhite/effective-go-book"
"golang.org/x/text"
"a/b/c"
"./e/f/g"
)
...
包名冲突问题
同一个包名在不同的项目、不同的仓库下可能都会存在。同一个源码文件在其包导入
路径构成源码搜索路径空间下很可能存在同名包。比如:我们有另一个chapter3-demo2,
其下也有名为pkg1的包,导入路径为github.com/bigwhite/effective-go-book/chapter3-
demo2/pkg/pkg1。如果cmd/app3同时导入了chapter3-demo1和chapter3-demo2的pkg1包,
会发生什么呢?
// cmd/app3
package main
import (
"github.com/bigwhite/effective-go-book/chapter3-demo1/pkg/pkg1"
"github.com/bigwhite/effective-go-book/chapter3-demo2/pkg/pkg1"
)
func main() {
pkg1.Func1()
}
编译一下cmd/app3:
$go build github.com/bigwhite/effective-go-book/chapter3-demo1/cmd/app3
# github.com/bigwhite/effective-go-book/chapter3-demo1/cmd/app3
./main.go:5:2: pkg1 redeclared as imported package name
previous declaration at ./main.go:4:2
我们看到的确出现了包名冲突的问题。怎么解决这个问题呢?还是用为包导入路径下
的包显式指定包名的方法:
package main
import (
pkg1 "github.com/bigwhite/effective-go-book/chapter3-demo1/pkg/pkg1"
mypkg1 "github.com/bigwhite/effective-go-book/chapter3-demo2/pkg/pkg1"
)
func main() {
pkg1.Func1()
mypkg1.Func1()
}
上面的pkg1指代的就是chapter3-demo1/pkg/pkg1下面的包,mypkg1则指代的是chapter3-demo1/pkg/pkg1下面的包。就此,包名冲突问题就轻松解决掉了。
我们通过实验进一步理解了Go语言的包导入,Gopher应牢记以下几个结论:
● Go编译器在编译过程中必然要使用的是编译单元(一个包)所依赖的包的源码;
● Go源码文件头部的包导入语句中import后面的部分是一个路径,路径的最后一个分段是目录名,而不是包名;
● Go编译器的包源码搜索路径由基本搜索路径和包导入路径组成,两者结合在一起后,编译器便可确定一个包的所有依赖包的源码路径的集合,这个集合构成了Go编译器的源码搜索路径空间;
● 同一源码文件的依赖包在同一源码搜索路径空间下的包名冲突问题可以由显式指定包名的方式解决。
相关文章:
Go基础11-理解Go语言的包导入
Go语言是使用包(package)作为基本单元来组织源码的,可以说一个Go程序就是由一些包链接在一起构建而成的。虽然与Java、Python等语言相比这算不上什么创新,但与祖辈C语言的头文件包含机制相比则是“先进”了许多。 编译速度快是这种…...

【MySQL数据库原理】在MySQL Workbench界面运行SQL代码——学生管理系统
在 MySQL Workbench 8.0 中,你可以使用以下步骤新建内容并运行 MySQL 语言代码: 1、打开 MySQL Workbench 并连接到你的 MySQL 数据库服务器。 2、在左侧的导航栏中,展开你的连接以查看数据库。选择你要在其中运行 SQL 代码的数据库。 3…...

高分三号1米分辨率飞机检测识别数据集
二、背景介绍 合成孔径雷达(Synthetic Aperture Radar, SAR) 是一种主动式的微波成像系统,它不受光照、云雾 和气候等自然条件影响,具备全天时、全天候对地 观测的能力,已成为遥感领域重要的信息获取平 台。近年来,随着遥感成像技…...

Unity 之Material 类型和 MeshRenderer 组件中的 Materials 之间有一些重要的区别
文章目录 区别代码例子 区别 在Unity中,Material 类型和 MeshRenderer 组件中的 Materials 之间有一些重要的区别。 Material 类型: Material 是 Unity 中用来定义渲染属性的资源。它包含了一系列定义了如何绘制一个对象的属性,比如颜色、纹…...

【LeetCode-简单题】977. 有序数组的平方
文章目录 题目方法一:双指针方法二: 题目 方法一:双指针 class Solution { // 方法一 :双指针public int[] sortedSquares(int[] nums) {int left 0;int right nums.length -1 ;int[] res new int[nums.length];//结果集新数组…...

【笔试强训选择题】Day39.习题(错题)解析
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!ÿ…...

Prometheus-Alertmanager 警报管理器-部署和设置
文章目录 一、介绍二、核心概念1 Grouping 分组2 Inhibition 抑制3 Silences 静默(静音)5 High Availability 高可用性 三、部署1 二进制方式下载配置 systemd 2 docker-compose 方式 四、配置1 配置文件介绍1.1 全局配置1.2 receiver 接收器标准接收器相…...

恒运资本:小盘股的优点?投资小盘股要注意哪些方面?
股市是一个充溢时机和危险的当地,不同出资者有不同的偏好,有的人喜爱追逐大盘蓝筹股,有的人则钟情于小盘股。那么小盘股的长处?出资小盘股要注意哪些方面?恒运资本也为我们准备了相关内容,以供参考。 小盘股…...

LeetCode:2. 两数之和
这个解题思路来自代码随想录:代码随想录 (programmercarl.com) class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {std::unordered_map <int,int> map;for(int i 0; i < nums.size(); i) {// 遍历当前元素&am…...

OpenCV(二十四):可分离滤波
目录 1.可分离滤波的原理 2.可分离滤波函数sepFilter2D() 3.示例代码 1.可分离滤波的原理 可分离滤波的原理基于滤波器的可分离性。对于一个二维滤波器,如果它可以表示为水平方向和垂直方向两个一维滤波器的卷积,那么它就是可分离的。也就是说&#x…...

【JS进阶】防抖与节流
防抖与节流 1.防抖 1.1 为什么要防抖? 在项目中,有的操作是高频触发的,但是其实触发一次就好了,比如我们短时间内多次缩放页面,那么我们不应该每次缩放都去执行操作,应该只做一次就好。再比如说监听输入…...

【css】linear-gradient()的用法
linear-gradient() CSS函数创建一个由两种或多种颜色沿一条直线进行线性过渡的图像,其结果是<gradient>数据类型的对象,此对象是一种特殊的<image> 数据类型。 语法 /* 渐变轴为 45 度,从蓝色渐变到红色 */ linear-gradient(45deg, blue, red);/* 从右…...

java: 读取snakeyaml-1.26.jar各种jar包时出错; error in opening zip file
可能的问题 jar有问题idea没有权限等等其他问题。但执行后报错就是读取不了,还报error in opening zip file这个错。 解决问题 我的错就是jar包有问题。我先后进行了很多次把jar包位置里的东西全部删除,然后重新maven下载但是不管用。最后从网站上下载…...

医疗知识图谱 neo4j
开源项目: https://github.com/liuhuanyong/QASystemOnMedicalKG 一.效果 二.需要安装: pip install pyahocorasick pip install py2neo 三.需要修改: 需要改的点: 1.改连接的方式 2.改读文件的方式 MedicalGraph 运行&am…...

【LeetCode-简单题】367. 有效的完全平方数
文章目录 题目方法一:二分查找 题目 方法一:二分查找 找 1 - num 之间的 mid, 开方是整数 就找得到 mid, 不是整数自然找不到mid class Solution { // 二分查找 ;找 1 - num 之间的mid 开方是整数 就找得到 不是…...
vben-admin中渲染table表格时怎么处理不同的数据结构
最近在用vben admin开发后台管理系统,vben admin这个后管端框架封装的非常细,颗粒度非常细,如果了解里面的组件或者api用法,那开发起来非常快。如果不了解,那就非常痛苦了,目前关于vben admin这块的开发问题…...

从零开始在树莓派上搭建WordPress博客网站并实现公网访问
文章目录 序幕概述1. 安装 PHP2. 安装MySQL数据库3. 安装 Wordpress4. 设置您的 WordPress 数据库设置 MySQL/MariaDB创建 WordPress 数据库 5. WordPress configuration6. 将WordPress站点发布到公网安装相对URL插件修改config.php配置 7. 支持好友链接样式8. 定制主题 序幕 …...
Go基础18-理解方法的本质以选择正确的receiver类型
Go语言虽然不支持经典的面向对象语法元素,比如类、对象、继承等,但Go语言也有方法。和函数相比,Go语言中的方法在声明形式上仅仅多了一个参数,Go称之为receiver参数。receiver参数是方法与类型之间的纽带。 Go方法的一般声明形式…...
Go基础12-理解Go语言表达式的求值顺序
Go语言在变量声明、初始化以及赋值语句上相比其先祖C语言做了一些改进,诸如: ● 支持在同一行声明和初始化多个变量(不同类型也可以) var a, b, c 5, "hello", 3.45 a, b, c : 5, "hello", 3.45 // 短变量…...

OJ练习第165题——修车的最少时间
修车的最少时间 力扣链接:2594. 修车的最少时间 题目描述 给你一个整数数组 ranks ,表示一些机械工的 能力值 。ranksi 是第 i 位机械工的能力值。能力值为 r 的机械工可以在 r * n2 分钟内修好 n 辆车。 同时给你一个整数 cars ,表示总…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...