golang之pkg(包)

1、概述

  Golang拥有超过100个标准包(可用go list std |wc -l查看)html

  任何包系统设计的目的都是简化大型程序的设计和维护工做,经过将一组相关的特性放进一个独立的模块以便于理解和更新,在每一个模块更新的同时保持和程序中其余模块的相对独立,这种模块化的特性容许每一个包能被其余的不一样项目共享和重用,在项目范围内、全局范围内的复用;mysql

  每一个包通常都定义了一个不一样的名字空间用于它内部的每一个标识符的访问。 每一个名字空间关联到一个特定的包, 让咱们给类型、 函数等选择简短明了的名字, 这样能够避免在咱们使用它们的时候减小和其它部分名字的冲突。git

  每一个包还经过控制包内名字的可见性和是否导出来实现封装特性。 经过限制包成员的可见性并隐藏包API的具体实现, 将容许包的维护者在不影响外部包用户的前提下调整包的内部实现。 经过限制包内变量的可见性, 还能够强制用户经过某些特定函数来访问和更新内部变量, 这样能够保证github

内部变量的一致性和并发时的互斥约束。golang

2、包的使用之导入路径

  每一个包是由一个全局惟一的字符串所标识的导入路径定位。 出如今import语句中的导入路径也是字符串。sql

import (
    "fmt"
    "math/rand"
    "encoding/json"
    "golang.org/x/net/html"
    "github.com/go-sql-driver/mysql"
)

3、包声明:

  在每一个Go源文件的开头都必须有包声明语句。 包声明语句的主要目的是肯定当前包被其它包导入时默认的标识符(也称为包名)json

  例如, math/rand包的每一个源文件的开头都包含 package rand 包声明语句, 因此当你导入这个包, 你就能够用rand.Int、 rand.Float64相似的方式访问包的成员。并发

  关于默认包名通常采用导入路径名的最后一段的约定也有三种例外状况。模块化

    第一个例外, 包对应一个可执行程序, 也就是main包, 这时候main包自己的导入路径是可有可无的。 名字为main的包是给go build( §10.7.3) 构建命令一个信息, 这个包编译完以后必须调用链接器生成一个可执行程序。函数

    第二个例外, 包所在的目录中可能有一些文件名是以test.go为后缀的Go源文件( 译注:前面必须有其它的字符, 由于以``前缀的源文件是被忽略的) , 而且这些源文件声明的包名也是以_test为后缀名的。 这种目录能够包含两种包:一种普通包, 加一种则是测试的外部扩展包。

  全部以_test为后缀包名的测试外部扩展包都由go test命令独立编译, 普通包和测试的外部扩展包是相互独立的。 测试的外部扩展包通常用来避免测试代码中的循环导入依赖。

    第三个例外, 一些依赖版本号的管理工具会在导入路径后追加版本号信息, 例如"gopkg.in/yaml.v2"。 这种状况下包的名字并不包含版本号后缀, 而是yaml。  

    warning:若是咱们想同时导入两个有着名字相同的包, 例如math/rand包和crypto/rand包, 那么导入声明必须至少为一个同名包指定一个新的包名以免冲突。 这叫作导入包的重命名。

    import (
      "crypto/rand"
      mrand "math/rand"   // alternative name mrand avoids conflict
    )

  warning:匿名导入:

    import _ "image/png" // register PNG decoder

 1 // The jpeg command reads a PNG image from the standard input
 2 // and writes it as a JPEG image to the standard output.
 3 package main
 4 import (
 5   "fmt"
 6   "image"
 7   "image/jpeg"
 8   _ "image/png" // register PNG decoder
 9   "io"
10   "os"
11 ) 
12 func main() { 13   if err := toJPEG(os.Stdin, os.Stdout); err != nil { 14     fmt.Fprintf(os.Stderr, "jpeg: %v\n", err) 15     os.Exit(1) 16   } 17 } 18 func toJPEG(in io.Reader, out io.Writer) error { 19   img, kind, err := image.Decode(in) //这里可能须要匿名导入 20   if err != nil { 21     return err 22   } 23   fmt.Fprintln(os.Stderr, "Input format =", kind) 24   return jpeg.Encode(out, img, &jpeg.Options{Quality: 95}) 25 }

  下面说说工做机制。 标准库还提供了GIF、 PNG和JPEG等格式图像的解码器,用户也能够提供本身的解码器, 可是为了保持程序体积较小, 不少解码器并无被所有包含, 除非是明确须要支持的格式。 image.Decode函数在解码时会依次查询支持的格式列表。

  每一个格式驱动列表的每一个入口指定了四件事情:格式的名称;一个用于描述这种图像数据开头部分模式的字符串, 用于解码器检测识别;一个Decode函数用于完成解码图像工做;一个DecodeConfig函数用于解码图像的大小和颜色空间的信息。 每一个驱动入口是经过调用image.RegisterFormat函数注册, 通常是在每一个格式包的init初始化函数中调用, 例如image/png包是这样注册的:

1 package png // image/png
2 func Decode(r io.Reader) (image.Image, error)
3 func DecodeConfig(r io.Reader) (image.Config, error)
4 func init() {
5   const pngHeader = "\x89PNG\r\n\x1a\n"
6   image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
7 }

  最终的效果是, 主程序只须要匿名导入特定图像驱动包就能够用image.Decode解码对应格式的图像!

 4、自定义本身的包:

  包是函数和数据的集合。用 package 关键字定义一个包。文件名不须要与包名一致,可是你的文件夹必需要与包名字一致,这个很重要,因此为了简单起见,名字都是同样的。包名的约定是使用小写字符。Go 包能够由多个文件组成,可是使用相同的;
  package 这一行。让咱们在文件 test1.go 中定义一个叫作 pkg的包。

1 ├── pkg
2 │   └── myprint.go
3 
4 //myprint.go
5 package pkg
6 import "fmt"
7 func MyPrintf(in string) {
8     fmt.Printf("%s", in)
9 }

  在main包中测试:

 1 package main 
 2 import  (
 3      "fmt"
 4      "./pkg"     //带上路径
 5 )
 6 
 7 func main() {
 8     pkg.MyPrinf("my pkg test")
 9     fmt.Printf("test ok")
10 }
11 
12 //测试ok
13 稍做修改:
14 package main 
15 import  (
16      "fmt"
17      "pkg"     //不带上路径
18 )
19 
20 func main() {
21     pkg.MyPrinf("my pkg test")
22     fmt.Printf("test ok")
23 }
24 
25 //closure.go:6:2: cannot find package "pkg" in any of:
26     /root/golang/go/src/pkg (from $GOROOT)
27     /root/src/pkg (from $GOPATH)
28 
29 # 解决办法:将pkg包加个软链接到$GOROOT 或$GOPATH
相关文章
相关标签/搜索