当前位置: 首页 > news >正文

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/src2)在上述基础搜索路径的基础上,不同版本的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/srcmoduleaware模式下(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/srcmoduleaware模式下(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 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!&#xff…...

Prometheus-Alertmanager 警报管理器-部署和设置

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

恒运资本:小盘股的优点?投资小盘股要注意哪些方面?

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

LeetCode:2. 两数之和

这个解题思路来自代码随想录&#xff1a;代码随想录 (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.可分离滤波的原理 可分离滤波的原理基于滤波器的可分离性。对于一个二维滤波器&#xff0c;如果它可以表示为水平方向和垂直方向两个一维滤波器的卷积&#xff0c;那么它就是可分离的。也就是说&#x…...

【JS进阶】防抖与节流

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

【css】linear-gradient()的用法

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

java: 读取snakeyaml-1.26.jar各种jar包时出错; error in opening zip file

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

医疗知识图谱 neo4j

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

【LeetCode-简单题】367. 有效的完全平方数

文章目录 题目方法一&#xff1a;二分查找 题目 方法一&#xff1a;二分查找 找 1 - num 之间的 mid&#xff0c; 开方是整数 就找得到 mid&#xff0c; 不是整数自然找不到mid class Solution { // 二分查找 &#xff1b;找 1 - num 之间的mid 开方是整数 就找得到 不是…...

vben-admin中渲染table表格时怎么处理不同的数据结构

最近在用vben admin开发后台管理系统&#xff0c;vben admin这个后管端框架封装的非常细&#xff0c;颗粒度非常细&#xff0c;如果了解里面的组件或者api用法&#xff0c;那开发起来非常快。如果不了解&#xff0c;那就非常痛苦了&#xff0c;目前关于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语言虽然不支持经典的面向对象语法元素&#xff0c;比如类、对象、继承等&#xff0c;但Go语言也有方法。和函数相比&#xff0c;Go语言中的方法在声明形式上仅仅多了一个参数&#xff0c;Go称之为receiver参数。receiver参数是方法与类型之间的纽带。 Go方法的一般声明形式…...

Go基础12-理解Go语言表达式的求值顺序

Go语言在变量声明、初始化以及赋值语句上相比其先祖C语言做了一些改进&#xff0c;诸如&#xff1a; ● 支持在同一行声明和初始化多个变量&#xff08;不同类型也可以&#xff09; var a, b, c 5, "hello", 3.45 a, b, c : 5, "hello", 3.45 // 短变量…...

OJ练习第165题——修车的最少时间

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

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...