【go从入门到精通】作用域,包详解

作者简介:

        高科,先后在 IBM PlatformComputing从事网格计算,淘米网,网易从事游戏服务器开发,拥有丰富的C++,go等语言开发经验,mysql,mongo,redis等数据库,设计模式和网络库开发经验,对战棋类,回合制,moba类页游,手游有丰富的架构设计和开发经验。 (谢谢你的关注)

--------------------------------------------------------------------------------------------------------------------------------

      这一节给大家来分享下作用域,包package的作用,用法以及相关代码示例,前面十几篇讲了很基础的go的语法,也用到了一些作用域和包package,但发现自始自终都还没有给大家分享他们的作用,怎么用,那么这一篇文章相信会给大家一个很好的解释和学习机会。

为什么作用域和包很重要?

包和作用域在 Go 中至关重要,原因如下:

  1. 封装:作用域控制变量和函数的可见性,促进封装并防止意外访问。
  2. 模块化:包有助于将代码组织成可管理的单元,从而更轻松地构建和维护项目。
  3. 命名空间:每个包都有自己的命名空间,这意味着您可以在不同的包中使用相同的标识符名称而不会发生冲突。
  4. 可重用性:库包可以在不同的项目中重用,节省时间和精力。

1 作用域

            在 Golang 中,作用域是指可以访问已定义变量或函数的程序区域。它对于控制变量和函数的可见性和生命周期至关重要。在Go语言中,变量的作用域取决于它们的声明位置。根据声明的位置,可以将变量的作用域分为以下几种情况:

  1. 全局作用域:在函数外部声明的变量具有全局作用域,可以在任何函数内部访问。

  2. 函数作用域:在函数内部声明的变量具有函数作用域,只能在该函数内部访问。

  3. 块作用域:在if语句、for循环等块结构内部声明的变量具有块作用域,只能在该块内部访问。

var globalVariable int  // 全局变量func  myFunc () { localVariable := 2  // 局部变量// 使用 localVariable 做一些事情}

在不同作用域内,变量的可见性不同。在内部作用域中,内部变量可以访问外部作用域中的变量。而在外部作用域中,不能直接访问内部作用域中的变量。因此你可以在myFunc里使用globalVariable这个全局变量,但是你不能在全局作用域下使用局部变量localVariable

因此假设你真想这样做,你会收到这样的报错信息:

另外,对于函数参数中声明的变量,它们的作用域也是函数作用域。只能在该函数内部访问。

在代码中直观可见的显式的(explicit)code block,比如:函数的函数体、for循环的循环体等;还有隐式的(implicit)code block。

下面的代码综合了几种作用域的情况,很容易混淆。请各位仔细琢磨弄清楚。

package mainimport "fmt"var (globalV int = 100
) func GetFunc() func() int {if globalV := 55; globalV < 60 {fmt.Println("GetFunc if 中:", globalV)}for globalV := 2; ; {fmt.Println("GetFunc 循环中:", globalV)break}fmt.Println("GetFunc 函数中全局变量:", globalV)return func() int {globalV += 1return globalV}
}func main() {fmt.Println("main函数中全局变量:", globalV)globalV := "string"fmt.Println("main函数中局部变量:", globalV)b := GetFunc()fmt.Println("main函数中:", b(), b(), b(), b()){globalV := 2fmt.Println(globalV){globalV := 3fmt.Println(globalV)}}fmt.Println(globalV)
}

程序输出:

globalV作为全局变量纯在是int类型,值为100;因此在main函数的第一个打印实际上是打印的全局变量值100,接下来globalV通过简式声明 := 操作,是string类型,值为string。在main()中,v很典型地体现了在“{}”花括号中的作用域问题,每一层花括号,都是对上一层的屏蔽。而闭包函数,GetGa()返回的匿名函数,赋值给b,每次执行b(),Ga的值都被记忆在内存中,下次执行b()的时候,取b()上次执行后Ga的值,而不是全局变量Ga的值,这就是闭包函数可以使用包含它的函数内的变量,因为作为代码块一直存在,所以每次执行都是在上次基础上运行。

简单总结如下:

有花括号"{ }"一般都存在作用域的划分; := 简式声明会屏蔽所有上层代码块中的变量(常量),建议使用规则来规范,如对常量使用全部大写,而变量尽量小写; 在if等语句中存在隐式代码块,需要注意; 闭包函数可以理解为一个代码块,并且他可使用包含它的函数内的变量;

注意,简式变量只能在函数内部声明使用,但是它可能会覆盖函数外全局同名变量。而且你不能在一个单独的声明中重复声明一个变量,但在多变量声明中这是允许的,而且其中至少要有一个新的声明变量。重复变量需要在相同的代码块内,否则你将得到一个隐藏变量。

如果你在代码块中犯了这个错误,将不会出现编译错误,但应用运行结果可能不是你所期望。所以尽可能避免和全局变量同名。

思考:

func main() {if a := 1; false {} else if b := 2; false {} else if c := 3; false {} else {println(a, b, c)}
}

这段代码运行结果是什么,你能写出来吗?

包Package

Go语言使用包(package)的概念来组织管理代码,包是结构化代码的一种方式。在Go语言中,每个.go文件都必须归属于某一个包,每个文件都可有init()函数。包名在源文件中第一行通过关键字package指定,包名要小写。如下所示:

package fmt

每个目录下面可以有多个.go文件,这些文件只能属于同一个包,否则编译时会报错。同一个包下的不同.go文件相互之间可以直接引用变量和函数,所以这些文件中定义的全局变量和函数不能重名。

如果你使用某个包中的某个成员,就必须import 包名。有两种方式,单行汇导入和多行导入:

// 单行导入
import "fmt" 
import "math"


// 多行导入
import ( 
    "fmt" 
    "math" 
)

如果在一个包中,想要访问另一个包的成员,就像变数、函式、结构等,就必须导出成员,export 的方法即将成员名称设置为大写。

package mypkg 
var myVar = 100 // 只能在 mypkg 中使用
const MyConst = "hello" // 可以在 mypkg 外部使用

 

Go语言的可执行应用程序必须有main包,而且在main包中必须且只能有一个main()函数,main()函数是应用程序运行开始入口。在main包中也可以使用init()函数。

Go语言不强制要求包的名称和文件所在目录名称相同,但是这两者最好保持相同,否则很容易引起歧义。因为导入包的时候,会使用目录名作为包的路径,而在代码中使用时,却要使用包的名称。

包的导入

一个Go程序通过import关键字将一组包链接在一起。import其实是导入目录,而不是定义的包名称,实际应用中我们一般都会保持一致。

例如标准包中定义的big包:package big,import "math/big" ,源代码其实是在GOROOT下src中的src/math/big目录。在代码中使用big.Int时,big指的才是.go文件中定义的包名称。

当导入多个包时,一般按照字母顺序排列包名称,像LiteIDE会在保存文件时自动完成这个动作。所谓导入包即等同于包含了这个包的所有的代码对象。

为避免名称冲突,同一包中所有对象的标识符必须要求唯一。但是相同的标识符可以在不同的包中使用,因为可以使用包名来区分它们。

import语句一般放在包名定义的下一行,导入包示例如下:

package mainimport  "context"  //加载context包

导入多个包的常见的方式是:

import  (
"fmt"
"net/http")

调用导入的包函数的一般方式:

fmt.Println("Hello World!")

下面介绍三种特殊的import方式。

点操作的含义是某个包导入之后,在调用这个包的函数时,可以省略前缀的包名,如这里可以写成Println("Hello World!"),而不是fmt.Println("Hello World!")。例如:

import( . "fmt" )

别名操作就是可以把包命名成另一个容易记忆的名字。例如:

import(f "fmt"
)

别名操作调用包函数时,前缀变成了别名,即f.Println("Hello World!")。在实际项目中有时这样使用,但请谨慎使用,不要不加节制地采用这种形式。

_ 操作是引入某个包,但不直接使用包里的函数,而是调用该包里面的init函数,比如下面的mysql包的导入。此外在开发中,由于某种原因某个原来导入的包现在不再使用,也可以采用这种方式处理,比如下面fmt的包。代码示例如下:

import (_ "fmt"_ "github.com/go-sql-driver/mysql"
)

标准包

在 Go 的安装文件里包含了一些可以直接使用的标准包。在$GOROOT/src中可以看到源码,也可以根据情况自行重新编译。

完整列表可以访问GoWalker(https://gowalker.org/)查看。

    unsafe: 包含了一些打破 Go 语言“类型安全”的命令,一般的程序中不会被使用,可用在 C/C++ 程序的调用中。
    syscall-os-os/exec:
        os: 提供给我们一个平台无关性的操作系统功能接口,采用类Unix设计,隐藏了不同操作系统间差异,让不同的文件系统和操作系统对象表现一致。
        os/exec: 提供我们运行外部操作系统命令和程序的方式。
        syscall: 底层的外部包,提供了操作系统底层调用的基本接口。
    archive/tar 和 /zip-compress:压缩(解压缩)文件功能。
    fmt-io-bufio-path/filepath-flag:
        fmt: 提供了格式化输入输出功能。
        io: 提供了基本输入输出功能,大多数是围绕系统功能的封装。
        bufio: 缓冲输入输出功能的封装。
        path/filepath: 用来操作在当前系统中的目标文件名路径。
        flag: 对命令行参数的操作。  
    strings-strconv-unicode-regexp-bytes:
        strings: 提供对字符串的操作。
        strconv: 提供将字符串转换为基础类型的功能。
        unicode: 为 unicode 型的字符串提供特殊的功能。
        regexp: 正则表达式功能。
        bytes: 提供对字符型分片的操作。
    math-math/cmath-math/big-math/rand-sort:
        math: 基本的数学函数。
        math/cmath: 对复数的操作。
        math/rand: 伪随机数生成。
        sort: 为数组排序和自定义集合。
        math/big: 大数的实现和计算。   
    container-/list-ring-heap: 实现对集合的操作。
        list: 双链表。
        ring: 环形链表。
   time-log:
        time: 日期和时间的基本操作。
        log: 记录程序运行时产生的日志。
    encoding/Json-encoding/xml-text/template:
        encoding/Json: 读取并解码和写入并编码 Json 数据。
        encoding/xml:简单的 XML1.0 解析器。
        text/template:生成像 HTML 一样的数据与文本混合的数据驱动模板。
    net-net/http-html:
        net: 网络数据的基本操作。
        http: 提供了一个可扩展的 HTTP 服务器和客户端,解析 HTTP 请求和回复。
        html: HTML5 解析器。
    runtime: Go 程序运行时的交互操作,例如垃圾回收和协程创建。
    reflect: 实现通过程序运行时反射,让程序操作任意类型的变量。
 

从 GitHub 安装包

如果有人想安装您的远端项目到本地机器,打开终端并执行(ffhelicopter是我在 GitHub 上的用户名):

go get -u go.mongodb.org/mongo-driver/mongo

这样现在这台机器上的其他 Go 应用程序也可以通过导入路径:"go.mongodb.org/mongo-driver/mongo" 来使用。 

import "go.mongodb.org/mongo-driver/mongo"

Go 对包的版本管理做的不是很友好,不过现在有些第三方项目做的不错,有兴趣的同学可以了解下(glide、godep、govendor)。

导入外部安装包

如果你要在你的应用中使用一个或多个外部包,你可以使用 Go install在你的本地机器上安装它们。Go install 是 Go 中自动包安装工具:如需要将包安装到本地它会从远端仓库下载包:检出、编译和安装一气呵成。

在包安装前的先决条件是要自动处理包自身依赖关系的安装。被依赖的包也会安装到子目录下,但是没有文档和示例:可以到网上浏览。

Go install 使用了 GoPATH 变量

假设你想使用GitHub - gocolly/colly: Elegant Scraper and Crawler Framework for Golang 这种托管在 Google Code、GitHub 和 Launchpad 等代码网站上的包。

你可以通过如下命令安装: Go install github.com/gocolly/colly 将一个名为 github.com/gocolly/colly 安装在$GoPATH/pkg/ 目录下。

Go install/build都是用来编译包和其依赖的包。

区别: Go build只对main包有效,在当前目录编译生成一个可执行的二进制文件(依赖包生成的静态库文件放在$GoPATH/pkg)。

Go install一般生成静态库文件放在$GoPATH/pkg目录下,文件扩展名a。

如果为main包,运行Go buil则会在$GoPATH/bin 生成一个可执行的二进制文件。

包的初始化

可执行应用程序的初始化和执行都起始于main包。如果main包的源代码中没有包含main()函数,则会引发构建错误 undefined: main.main。main()函数既没有参数,也没有返回类型,init()函数和main()函数在这一点上两者一样。

如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时某个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。

当某个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init()函数(如果有的话),依次类推。

等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init()函数,最后执行main()函数。

Go语言中init()函数常用于包的初始化,该函数是Go语言的一个重要特性,有下面的特征:

  • init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等
  • 每个包可以拥有多个init函数
  • 包的每个源文件也可以拥有多个init函数
  • 同一个包中多个init()函数的执行顺序不定
  • 不同包的init()函数按照包导入的依赖关系决定该函数的执行顺序
  • init()函数不能被其他函数调用,其在main函数执行之前,自动被调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/812232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

性能分析-数据库(安装、索引、sql、执行过程)与磁盘知识(读、写、同时读写、内存速度测试)

数据库 数据库&#xff0c;其实是数据库管理系统dbms。 数据库管理系统&#xff0c; 常见&#xff1a; 关系型数据库&#xff1a; mysql、pg、 库的表&#xff0c;表与表之间有关联关系&#xff1b; 表二维表统一标准的SQL&#xff08;不局限于CRUD&#xff09;非关系型数据…

阿里云9元服务器租用收费价格表_免费云服务器领取

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

第P2周:CIFAR10彩色图片识别

第P2周&#xff1a;CIFAR10彩色图片识别 &#x1f368; 本文为&#x1f517;365 天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K 同学啊 &#x1f4cc;第P2周&#xff1a;彩色图片识别&#x1f4cc; 难度&#xff1a;小白入门⭐ 语言&#xff1a;Python…

QSFP-DD 和 QSFP+ / QSFP28 / QSFP56 / OSFP / CFP8 / COBO 之间的区别

&#x1f31f;QSFP-DD 作为400G 光模块的最小外形尺寸&#xff0c;提供业界最高的带宽密度&#xff0c;同时利用对低速 QSFP 可插拔模块和电缆的向后兼容性&#xff0c;使其在光纤制造商中很受欢迎。作为400G高速应用中最新的热门光收发器&#xff0c;QSFP-DD经常被拿来与QSFP5…

计算机视觉——DiffYOLO 改进YOLO与扩散模型的抗噪声目标检测

概述 物体检测技术在图像处理和计算机视觉中发挥着重要作用。其中&#xff0c;YOLO 系列等型号因其高性能和高效率而备受关注。然而&#xff0c;在现实生活中&#xff0c;并非所有数据都是高质量的。在低质量数据集中&#xff0c;更难准确检测物体。为了解决这个问题&#xff…

【报错】AttributeError: ‘NoneType‘ object has no attribute ‘pyplot_show‘(已解决)

【报错】AttributeError: ‘NoneType’ object has no attribute ‘pyplot_show’ 问题描述&#xff1a;python可视化出现下面报错 我的原始代码&#xff1a; import matplotlib.pyplot as pltplt.figure() plt.plot(x, y, bo-) plt.axis(equal) plt.xlabel(X) plt.ylabe…

基于LNMP部署wordpress

目录 一.环境准备 二.配置源并安装 三.配置Nginx 四.配置数据库 五.上传源码并替换 六.打开浏览器&#xff0c;输入虚拟机ip访问安装部署 七.扩展增加主题 一.环境准备 centos7虚拟机 关闭防火墙和seliunx stop firewalld #关闭防火墙 setenforce 0 …

软件设计师-基础知识科目-标准化与软件知识产权基本知识11

十一、标准化与软件知识产权基本知识&#xff1a; 知识产权&#xff1a; 主要包括&#xff1a;著作权及邻接权、专利权、工业品外观设计权、商标权、地理标志权、继承电路布图设计权。邻接权是指与著作权相邻近的权利&#xff0c;是指作品传播者&#xff0c;对其传播作品过程…

Unity TextMeshProUGUI 获取文本尺寸·大小

一般使用ContentSizeFitter组件自动变更大小 API 渲染前 Vector2 GetPreferredValues(string text)Vector2 GetPreferredValues(string text, float width, float height)Vector2 GetPreferredValues(float width, float height) 渲染后 Vector2 GetRenderedValues()Vector…

【安全】挖矿木马自助清理手册

一、什么是挖矿木马 挖矿木马会占用CPU进行超频运算&#xff0c;从而占用主机大量的CPU资源&#xff0c;严重影响服务器上的其他应用的正常运行。黑客为了得到更多的算力资源&#xff0c;一般都会对全网进行无差别扫描&#xff0c;同时利用SSH爆破和漏洞利用等手段攻击主机。 …

JavaEE初阶——多线程(二)

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章延续上一篇文章,与大家分享Thread常见的方法以及线程的状态相关知识 其他内容我们下一篇再见! 如果有错误或不足请您指出!!! 目录 3.Thread类及常见方法3.1Thread常见的构造方法3.2Thread…

STM32H743VIT6使用STM32CubeMX通过I2S驱动WM8978(2)

接前一篇文章&#xff1a;STM32H743VIT6使用STM32CubeMX通过I2S驱动WM8978&#xff08;1&#xff09; 本文参考以下文章及视频&#xff1a; STM32CbueIDE Audio播放音频 WM8978 I2S_stm32 cube配置i2s录音和播放-CSDN博客 STM32第二十二课&#xff08;I2S&#xff0c;HAL&am…

CLIP大模型图文检索——原理解读及代码实现

一. 核心思想 通过自然语言处理获得的监督信号可用于训练迁移效果出色的视觉模型。本论文的作者团队构建了一个庞大的图像文本配对数据集&#xff0c;其中包含400 million个图片文本的配对。利用最大规模的ViT-large模型&#xff0c;他们提出了CLIP&#xff08;Contrastive La…

机器学习和深度学习 -- 李宏毅(笔记与个人理解)Day 13

Day13 Error surface is rugged…… Tips for training :Adaptive Learning Rate critical point is not the difficult Root mean Square --used in Adagrad 这里为啥是前面的g的和而不是直接只除以当前呢? 这种方法的目的是防止学习率在训练过程中快速衰减。如果只用当前的…

自然语言处理NLP关键知识点

大家好&#xff0c;在人工智能出现之前&#xff0c;机器智能处理结构化的数据&#xff0c;例如 Excel 里的数据。但是网络中大部分的数据都是非结构化的&#xff0c;例如文章、图片、音频、视频等。在非结构数据中&#xff0c;文本的数量是最多的&#xff0c;他虽然没有图片和视…

信息系统项目管理师——第27章管理科学基础知识

1 最大流量问题[简单] 百度百科:最大流问题&#xff0c;一种组合最优化问题&#xff0c;就是要讨论如何充分利用装置的能力&#xff0c;使得运输的流量最大&#xff0c;以取得最好的效果。 教材P869:在起点和终点之间可能存在多条运输路径&#xff0c;总的最大流量就是求出各…

智能EDM邮件营销推广工具哪个好?

有效且精准的客户沟通已经成为企业成功的关键要素之一&#xff0c;云衔科技以其尖端的智能EDM邮件营销系统解决方案脱颖而出&#xff0c;为全球各行业的企业提供了一个强有力的竞争优势和业绩增长引擎。 云衔科技深谙市场营销的艺术与科学&#xff0c;凭借多年积累的专业技术研…

SPI 机制

一、简述 本文介绍 SPI 机制。 二、什么是 SPI 机制 SPI&#xff08;Service Provider Interface&#xff09;机制是 Java 编程语言中的一种机制&#xff0c;用于实现组件之间的解耦和扩展。SPI 允许开发者编写服务接口&#xff08;Service Interface&#xff09;&#xff0…

计算机网络 路由器基本配置

一、实验内容 1、按照下表配置好PC机IP地址和路由器端口IP地址 2、配置好路由器特权密文密码“abcd&#xff0b;两位班内序号”和远程登录密码“star” 3、验证测试 a.验证各个接口的IP地址是否正确配置和开启 b.PC1 和 PC2 互ping c.验证PC1通过远程登陆到路由器上&#…

目前深圳嵌入式单片机就业环境如何?

深圳作为中国的科技创新中心之一&#xff0c;嵌入式行业的就业环境相对较好。我这里有一套嵌入式入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习嵌入式&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在…