Golang 的init函数和其他函数或方法有诸多不同. 它是 Golang package 初始化中使用的重要角色, 可以说是语法糖. 当对于 Golang 这样一门工程化编程语言来说,init函数有着很多巧妙的使用. 本文从init函数的的一些特性开始, 并附加部分标准库中的例子, 来谈谈init函数的使用方法.

1. 不唯一性

init函数和其他函数最大的区别之一就是, 同一个 package 或源文件中, 可以有很多个. 我们看下面的例子:

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

运行结果:

init 2
init 1
main

如果同一个 Package 下的多个源文件中都有init函数, 也是没有问题的.

2. 生命周期

init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized. Link

init函数在一个 package 中的所有全局变量都初始化完成之后, 才开始运行. 这一点非常方便代码的组织. 例如当一个package 中有非常多的方法或函数, 这些方法逻辑上都处于同一个级别, 进一步拆分 package 并不合理. 这时候我们就可以将这些方法或函数放在多个源文件中. 而对这些方法或函数初始化的init函数可以和对应的逻辑放在一起, 这也能体现代码设计上的 Cohesion.

例如在一个名为api的 package 中:

./api/
├── account.go
├── profile.go
├── resource.go
└── user.go

API 对应的初始化部分都可以单独地写在每个源文件中, 引用这个 package 的开发者并不需要显示地调用初始化函数就能完成整个 package 的初始化.

其次 init 函数只会运行一次, 即使被 import 了很多次.

Package initialization is done only once even if package is imported many times.

这对很多需要维护全局唯一的一些特性非常有用, 例如在流行的日志工具logrus的实现中, 为了提升获取时间的性能, logrus 在初始化的时候获取了系统的基准时间, 而这个时间需要全局唯一, 并且只需要获取一次. 代码如下:

// Link https://github.com/sirupsen/logrus/blob/d5d4df1108f606433e95b17c8fbc110916779780/text_formatter.go#L26package logrusimport ("time"
)var baseTimestamp time.Timefunc init() {baseTimestamp = time.Now()
}

3. 没有输入输出的参数

init function is niladic. Link

如果我们给init函数写上输入参数或输出参数会怎么样呢?init函数会不会变成一个普通的函数? 答案是:

func init must have no arguments and no return values.

Compiler 会告诉我们, 这样写是语法错误的, 这也说明了init函数在 Golang 语法体系中的特殊性.

4. 运行顺序

  1. 同一个源文件中, 写在更靠近文件上面的 init 函数更早运行
  2. 同一个 package 中, 文件名排序靠前的文件中的 init 函数更早运行

第一条没有什么疑问, 对于第二条, 可以简单地参考字符串比较, 例如:

  • a.go > b.go
  • a1.go > a2.go

5. 用作 side effect

标准库中的 MySQL Driver 就是通过导入一个匿名的 package 来实现 side effect. 例如:

import "database/sql"
import _ "github.com/go-sql-driver/mysql"db, err := sql.Open("mysql", "user:password@/dbname")

在上面的代码中, 我们导入了 MySQL 的 Driver, 却没有显示地使用它, 那这行导入实际上发生了什么呢, 如果我们去看 go-sql-driver/mysql 这个 package 的实现, 就会发现:

// Link: https://github.com/go-sql-driver/mysql/blob/578c4c8066964679ef44f45de2b6c7e811cc665e/driver.go#L83func init() {sql.Register("mysql", &MySQLDriver{})
}

这里在init函数中调用 sql 的标准接口, 注册了 MySQL 的 Driver. 这样做的好处是开发者并不需要显示地调用初始化函数.

最后, 标题就算小结了. 再提供一个 Tips:init函数并不一定需要写在源文件的最上面, 从语法层面说, 写在任何地方都可以. 标准库里有很多实践的例子. 但从实用和工程化的考量, 可以总结为已下思路:

  1. 可以将 init 函数写在和 init 函数初始化的内容相关的函数上面(特别是有多个 init 函数的情况下)
  2. 如果没有特别想关的内容, init 函数就放在源文件的最上面或最下面(方便被看到)
  3. 如果一个 package 只有一个init函数, 那尽量放在和 package 同名的源文件里, 例如 go-sql-driver/mysql 就放在mysql.go这个文件中

c语言清屏函数怎么用_怎么用好 Golang 的 init 函数相关推荐

  1. GoLang之init函数

    文章目录 GoLang之init函数 1.init函数特性 2.init函数执行顺序 3.init函数使用场景 GoLang之init函数 注:本文基于Go SDK v1.8进行讲解 1.init函数 ...

  2. c语言清屏小程序,写了个小程序,一直会闪屏,用的gotoxy函数,求大神教

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #include #include // 全局变量 int position_x,position_y; // 飞机位 ...

  3. Matlab学习第一天(注释、输入输出、清空工作区、清屏、向量矩阵表示和运算、字符串、求和求积函数、等差数列、特征值、判断语句、循环语句)

    % 1.分号的使用:每一行的语句最后可以选择性地加上分号,加上分号后不显示运行结果(因此大部分语句加上分号),否则显示运行结果a=3;b=5% 2.注释的使用:用百分号表示注释,多行注释快捷键为Ctr ...

  4. c语言清屏运行慢,关于c清屏的问题

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 } for(j=x1;j goto_xy(i,y1); putchar(' '); goto_xy(j,y2); putchar(' '); delay( ...

  5. c语言清屏不清菜单,关于c清屏的问题

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 } for(j=x1;j goto_xy(i,y1); putchar(' '); goto_xy(j,y2); putchar(' '); delay( ...

  6. c语言清屏、等待、随机函数

    清屏函数 #include<conio.h> system("CLS");或system(cls); 等待函数 #include<windows.h>  S ...

  7. python在匿名函数作和_跟光磊学Python开发-匿名函数函数和高阶函数

    跟光磊学Python开发-匿名函数函数和高阶函数 跟光磊学Python开发-匿名函数函数和高阶函数跟光磊学Python开发 匿名函数 匿名函数就是函数定义时没有名字的函数,也称为匿名表达式. 普通函数 ...

  8. python可变类型与不可变类型作为函数参数区别_不要用可变类型对象做函数默认参数...

    不要用可变类型对象做函数默认参数 1. 可变对象做默认参数 内置数据类型int,float,bool,str,tuple 是不可变对象, 字典,集合,列表是可变对象. 在定义python函数时,千万不 ...

  9. sql自定义函数学习思路_学习SQL:用户定义的函数

    sql自定义函数学习思路 You can create several user-defined objects in a database. One of these is definitely u ...

  10. python实验指导书答案函数代码复用_#Python学习笔记(5)函数和代码重用,复用

    七段数码管绘制 绘制单段数码管 def drawLine(draw): turtle.pendown() if draw else turtle.penup() #将绘制一条线定义为一个基本函数, 参 ...

最新文章

  1. VMware虚拟机提示在该系统上全局禁用了虚拟机打印功能
  2. OD命令大全(OllyDbg命令)
  3. 关于CMMI级别阶梯式前进路线图的对话
  4. opencv学 之图像傅里叶变换dft
  5. 实现一个基于动态代理的 AOP
  6. css特性:空白外边距互相叠加
  7. PriorityQueue 优先队列
  8. 40岁才博士毕业,新任安徽省长的他写了篇句句戳心的博士论文后记
  9. pdf复制去掉换行符
  10. 锁屏后,360浏览器总是自动打开
  11. 同步发电机励磁调节实验原理_发电机组自动控制系统工作原理
  12. 计算机配色在纺织中的应用,计算机配色在印染行业的应用
  13. 全国计算机教学图片,全国“xx杯”计算机类说课大赛课件一等奖作品:图层蒙版教学课件.pptx...
  14. 物联卡Iccid号码编码规则
  15. vscode报错http://127.0.0.1:5500/11.html 找不到应用程序
  16. Linux常用命令英文全称与中文解释 (pwd、su、df、du等)
  17. Java设计模式(03) -- 里氏替换原则
  18. TOEFL wordlist 6
  19. 统一配置中心对比介绍
  20. 30个顶级Python库 | 用于深度学习、自然语言处理和计算机视觉

热门文章

  1. WIN7下odbc数据源配置问题
  2. 小甲鱼 python11讲作业
  3. 历年计算机二级Java考试试题及答案(完整版)
  4. C++转Java快速入门
  5. 计算机组成原理白中英课后习题题答案
  6. 红米AirDots无线蓝牙耳机连接win10笔记本
  7. 吴恩达机器学习作业2
  8. IDEA 删除SVN文件
  9. Java核心技术11 | Java IO
  10. 怎么下c语言程序,最新VC++(c语言程序下载安装)下载地址电脑版-CC软件