Golang中的init函数

举报
张俭 发表于 2023/12/30 08:56:52 2023/12/30
【摘要】 翻译自https://medium.com/golangspec/init-functions-in-go-eac191b3860a 正文init函数在包中定义,通常用来比较复杂的初始化流程,不能通过表达式初始化的变量的初始化检查、修复程序的状态注册执行一次性的计算等等除了下面讨论的一些差异之外,init函数中可以放任何常规函数可以放的东西 包初始化要使用导入的包,需要先将包初始化,初始化...

翻译自

正文

init函数在包中定义,通常用来

  • 比较复杂的初始化流程,不能通过表达式初始化的变量的初始化
  • 检查、修复程序的状态
  • 注册
  • 执行一次性的计算
  • 等等

除了下面讨论的一些差异之外,init函数中可以放任何常规函数可以放的东西

包初始化

要使用导入的包,需要先将包初始化,初始化及顺序问题由Golang的running system完成。

  • 导入的包的初始化(递归定义)
  • 为包中声明的变量计算和分配初始值
  • 执行包中的init函数
即使包被导入多次,包的初始化也只能执行一次

顺序

Go里面的一个包可以包含多个文件。分布在众多包中的众多文件,变量和init函数执行的顺序应该是什么样子呢?之前的文章提到了变量的初始化顺序。完成此操作后,需要决定文件 a.goz.go 中的变量初始化谁更应该早执行。 这取决于传递给编译器的文件顺序。 如果 z.go 首先由构建系统传递,则z.go先执行,然后在 a.go 的再执行。 这同样适用于init 函数的触发。 语言规范建议始终使用相同的顺序并按字典顺序从包中传递文件名:

为了确保可重现的初始化行为,鼓励构建系统把属于同一包的多个文件以词法文件名顺序传递给编译器。

不过依赖文件名初始化顺序的程序十分罕见,让我们来看这样子的例子

sandbox.go

package main
import "fmt"
var _ int64 = s()
func init() {
    fmt.Println("init in sandbox.go")
}
func s() int64 {
    fmt.Println("calling s() in sandbox.go")
    return 1
}
func main() {
    fmt.Println("main")
}

a.go

package main
import "fmt"
var _ int64 = a()
func init() {
    fmt.Println("init in a.go")
}
func a() int64 {
    fmt.Println("calling a() in a.go")
    return 2
}

.go

package main
import "fmt"
var _ int64 = z()
func init() {
    fmt.Println("init in z.go")
}
func z() int64 {
    fmt.Println("calling z() in z.go")
    return 3
}

运行后,程序会这样输出

calling a() in a.go
calling s() in sandbox.go
calling z() in z.go
init in a.go
init in sandbox.go
init in z.go
main

属性

init函数不需要参数也不返回任何值。和main不同,标识符init未声明,所以不能被引用

package main
import "fmt"
func init() {
    fmt.Println("init")
}
func main() {
    init()
}

在编译时,程序返回

undefined: init

同一个包或文件中可以有很多个init函数,在不同文件中定义的init函数如下按照字母顺序执行,同一个文件按声明顺序执行,举例

sandbox.go

package main
import "fmt"
func init() {
    fmt.Println("init 1")
}
func init() {
    fmt.Println("init 2")
}
func main() {
    fmt.Println("main")
}

utils.go

package main
import "fmt"
func init() {
    fmt.Println("init 3")
}

输出

init 1
init 2
init 3
main

init函数的最常见用途,就是用来给那些不能通过表达式初始化的变量初始化,如:

var precomputed = [20]float64{}
func init() {
    var current float64 = 1
    precomputed[0] = current
    for i := 1; i < len(precomputed); i++ {
        precomputed[i] = precomputed[i-1] * 1.2
    }
}

表达式中可不能使用for循环,所以通过init函数来解决这个问题

为了副作用来导入包

Go对于未使用的引用非常严格。有些场景,你导入一个包,只为了执行其中的init函数(如mysql的driver)。

import _ "image/png"
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。