先生,Go语言里有个包,进来了解一下吧。
包是结构化代码的一种方式:每个程序都由包(通常简称为 pkg)的概念组成,可以使用自身的包或者从其它包中导入内容。
如同其它一些编程语言中的类库或命名空间的概念,每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go
为扩展名的源文件组成,因此文件名和包名一般来说都是不相同的。
我正在学习酷酷的 Golang,可点此查看帖子Golang学习笔记汇总。
你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main
。package main
表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main
的包。
一个应用程序可以包含不同的包,而且即使你只使用 main 包也不必把所有的代码都写在一个巨大的文件里:你可以用一些较小的文件,并且在每个文件非注释的第一行都使用 package main
来指明这些文件都属于 main 包。如果你打算编译包名不是为 main 的源文件,如 pack1
,编译后产生的对象文件将会是 pack1.a
而不是可执行程序。另外要注意的是,所有的包名都应该使用小写字母。
一个 Go 程序是通过 import
关键字将一组包链接在一起。
import "fmt"
告诉 Go 编译器这个程序需要使用 fmt
包(的函数,或其他元素),fmt
包实现了格式化 IO(输入/输出)的函数。包名被封闭在半角双引号 ""
中。如果你打算从已编译的包中导入并加载公开声明的方法,不需要插入已编译包的源代码。
如果需要多个包,它们可以被分别导入:
import "fmt"
import "os"
或:
import "fmt"; import "os"
但是还有更短且更优雅的方法(被称为因式分解关键字,该方法同样适用于 const、var 和 type 的声明或定义):
import (
"fmt"
"os"
)
它甚至还可以更短的形式,但使用 gofmt 后将会被强制换行:
import ("fmt"; "os")
当你导入多个包时,最好按照字母顺序排列包名,这样做更加清晰易读。
如果包名不是以 .
或 /
开头,如 "fmt"
或者 "container/list"
,则 Go 会在全局文件进行查找;如果包名以 ./
开头,则 Go 会在相对目录中查找;如果包名以 /
开头(在 Windows 下也可以这样使用),则会在系统的绝对路径中查找。
导入包即等同于包含了这个包的所有的代码对象。
除了符号 _
,包中所有代码对象的标识符必须是唯一的,以避免名称冲突。但是相同的标识符可以在不同的包中使用,因为可以使用包名来区分它们。
包通过下面这个被编译器强制执行的可见性规则来决定是否将自身的代码对象暴露给外部文件:
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。
(大写字母可以使用任何 Unicode 编码的字符,比如希腊文,不仅仅是 ASCII 码中的大写字母)。
因此,在导入一个外部包后,能够且只能够访问该包中导出的对象。
假设在包 pack1 中我们有一个变量或函数叫做 Thing(以 T 开头,所以它能够被导出),那么在当前包中导入 pack1 包,Thing 就可以像面向对象语言那样使用点标记来调用:pack1.Thing
(pack1 在这里是不可以省略的)。
因此包也可以作为命名空间使用,帮助避免命名冲突(名称冲突):两个包中的同名变量的区别在于他们的包名,例如 pack1.Thing
和 pack2.Thing
。
你可以通过使用包的别名来解决包名之间的名称冲突,或者说根据你的个人喜好对包名进行重新设置,如:import fm "fmt"
。
拿个LoRaServer实际项目分析下:
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/codegangsta/cli"
"github.com/pkg/errors"
migrate "github.com/rubenv/sql-migrate"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
}
“crypto/tls”、”fmt”、”os”、”time”等都是标准库,在全局文件进行查找。
“github.com/codegangsta/cli” 这是网络远程库,直接从github上拉取。
migrate “github.com/rubenv/sql-migrate”,也是github上拉取的网络远程库,利用包别名,为了方便接下来的代码编写,有点宏的感觉。
在这个文件里的函数发现了这个包的调用。
func runDatabaseMigrations(c *cli.Context) error {
if c.Bool("db-automigrate") {
log.Info("applying database migrations")
m := &migrate.AssetMigrationSource{
Asset: migrations.Asset,
AssetDir: migrations.AssetDir,
Dir: "",
}
n, err := migrate.Exec(common.DB.DB, "postgres", m, migrate.Up)
if err != nil {
return errors.Wrap(err, "applying migrations failed")
}
log.WithField("count", n).Info("migrations applied")
}
return nil
}
migrate.AssetMigrationSource和migrate.Exec是migrate的对外函数,migrate.Up是migrate的对外变量。他们全都支持可见性规则,以大写子母来标识。
包是Go语言中结构化代码的方式,每个程序都由包组成,可以使用自身的包或者从其它包中导入内容。同其它编程语言中的类库或命名空间的概念。
这篇笔记学习了包的基础知识,包名、包的导入、包的可见性规则,通过一个项目实例来加深了知识点印象。