Go-Package

本文描述创建&使用package的一些内容。

要点:

  • GOPATH环境变量
  • package目录结构
  • 首字母大小写约定

GOPATH

要创建自定义的package,需要首先定义GOPATH环境变量。

这个GOPATH代表的目录是任意的,比如,D:\the_go_path

D:\examples>echo %GOPATH%
D:\examples\the_go_path

D:\examples>

如果没有定义这个环境变量,会有异常提示:

D:\examples\shape>go install
go install: no install location for directory D:\examples\shape outside GOPATH
        For more details see: go help gopath

D:\examples\shape>

目录结构

在Introducing Go这本书中,没有明确写出package的目录结构。为此,请执行go help gopath命令获取详细内容。

尽管占用大量篇幅,但作者还是决定拷贝到这里:

D:\examples>go help gopath
The Go path is used to resolve import statements.
It is implemented by and documented in the go/build package.

The GOPATH environment variable lists places to look for Go code.
On Unix, the value is a colon-separated string.
On Windows, the value is a semicolon-separated string.
On Plan 9, the value is a list.

GOPATH must be set to get, build and install packages outside the
standard Go tree.

Each directory listed in GOPATH must have a prescribed structure:

The src directory holds source code.  The path below src
determines the import path or executable name.

The pkg directory holds installed package objects.
As in the Go tree, each target operating system and
architecture pair has its own subdirectory of pkg
(pkg/GOOS_GOARCH).

If DIR is a directory listed in the GOPATH, a package with
source in DIR/src/foo/bar can be imported as "foo/bar" and
has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".

The bin directory holds compiled commands.
Each command is named for its source directory, but only
the final element, not the entire path.  That is, the
command with source in DIR/src/foo/quux is installed into
DIR/bin/quux, not DIR/bin/foo/quux.  The "foo/" prefix is stripped
so that you can add DIR/bin to your PATH to get at the
installed commands.  If the GOBIN environment variable is
set, commands are installed to the directory it names instead
of DIR/bin. GOBIN must be an absolute path.

Here's an example directory layout:

    GOPATH=/home/user/gocode

    /home/user/gocode/
        src/
            foo/
                bar/               (go code in package bar)
                    x.go
                quux/              (go code in package main)
                    y.go
        bin/
            quux                   (installed command)
        pkg/
            linux_amd64/
                foo/
                    bar.a          (installed package object)

Go searches each directory listed in GOPATH to find source code,
but new packages are always downloaded into the first directory
in the list.

See https://golang.org/doc/code.html for an example.

Internal Directories

Code in or below a directory named "internal" is importable only
by code in the directory tree rooted at the parent of "internal".
Here's an extended version of the directory layout above:

    /home/user/gocode/
        src/
            crash/
                bang/              (go code in package bang)
                    b.go
            foo/                   (go code in package foo)
                f.go
                bar/               (go code in package bar)
                    x.go
                internal/
                    baz/           (go code in package baz)
                        z.go
                quux/              (go code in package main)
                    y.go


The code in z.go is imported as "foo/internal/baz", but that
import statement can only appear in source files in the subtree
rooted at foo. The source files foo/f.go, foo/bar/x.go, and
foo/quux/y.go can all import "foo/internal/baz", but the source file
crash/bang/b.go cannot.

See https://golang.org/s/go14internal for details.

Vendor Directories

Go 1.6 includes support for using local copies of external dependencies
to satisfy imports of those dependencies, often referred to as vendoring.

Code below a directory named "vendor" is importable only
by code in the directory tree rooted at the parent of "vendor",
and only using an import path that omits the prefix up to and
including the vendor element.

Here's the example from the previous section,
but with the "internal" directory renamed to "vendor"
and a new foo/vendor/crash/bang directory added:

    /home/user/gocode/
        src/
            crash/
                bang/              (go code in package bang)
                    b.go
            foo/                   (go code in package foo)
                f.go
                bar/               (go code in package bar)
                    x.go
                vendor/
                    crash/
                        bang/      (go code in package bang)
                            b.go
                    baz/           (go code in package baz)
                        z.go
                quux/              (go code in package main)
                    y.go

The same visibility rules apply as for internal, but the code
in z.go is imported as "baz", not as "foo/vendor/baz".

Code in vendor directories deeper in the source tree shadows
code in higher directories. Within the subtree rooted at foo, an import
of "crash/bang" resolves to "foo/vendor/crash/bang", not the
top-level "crash/bang".

Code in vendor directories is not subject to import path
checking (see 'go help importpath').

When 'go get' checks out or updates a git repository, it now also
updates submodules.

Vendor directories do not affect the placement of new repositories
being checked out for the first time by 'go get': those are always
placed in the main GOPATH, never in a vendor subtree.

See https://golang.org/s/go15vendor for details.

D:\examples>

要点:
- GOPATH下面有个src目录和pkg目录
- src放源码(.go文件),而go install会把生成的.a文件放在pkg目录下
- src下面又是一个个的目录,每个对应一个package
- 到src下面的对应package下执行 go install 命令,会自动安装package
- go install之后,就如同通常的情形来import。

示例

以前面Go-Interface的第一个例子说明package的使用方法。

创建目录结构

D:\examples\the_go_path>tree /F .
文件夹 PATH 列表
卷序列号为 5645-602F
D:\EXAMPLES\THE_GO_PATH
├─pkg
└─src
    └─shape
            the_shape.go


D:\examples\the_go_path>

这里取package的名字为shape。而package(shape)文件夹下面的go文件名没有什么要求,为此特意取名the_shape.go。

the_shape.go

package shape 

import "math"

type Shape interface {
    area() float64 
}

type Circle struct {
    r float64 
}

func (c *Circle) area() float64 {
    return math.Pi * c.r * c.r
}

type Rectangle struct {
    length, width float64 
}

func (r *Rectangle) area() float64 {
    return r.length * r.width;
}

func totalArea(shapes ... Shape) (total float64) {
    total = 0 
    for _, shape := range shapes {
        total += shape.area()
    }

    return 
}

go install

在package的目录下执行go install命令,这时会自动编译&链接,生成.a,并放在pkg目录下。如果go有编译错误,会有对应提示。

D:\examples\the_go_path\src\shape>go install

D:\examples\the_go_path\src\shape>cd ../..

D:\examples\the_go_path>tree /F .
文件夹 PATH 列表
卷序列号为 5645-602F
D:\EXAMPLES\THE_GO_PATH
├─pkg
│  └─windows_amd64
│          shape.a
│
└─src
    └─shape
            the_shape.go


D:\examples\the_go_path>

调用新创建的package

package main
import "fmt"
import "shape"

/*
D:\examples>go run helloworld.go
# command-line-arguments
.\helloworld.go:17: implicit assignment of unexported field 'r' in shape.Circle literal
.\helloworld.go:18: implicit assignment of unexported field 'length' in shape.Rectangle literal
.\helloworld.go:18: implicit assignment of unexported field 'width' in shape.Rectangle literal
.\helloworld.go:20: cannot refer to unexported name shape.totalArea
.\helloworld.go:20: undefined: shape.totalArea

D:\examples>
*/
func main() {
    circle := shape.Circle{5} // L17
    ractangle := shape.Rectangle{3, 4} // L18
                                       // L19
    fmt.Println(shape.totalArea(&circle, &ractangle)) // L20
}

通过编译错误信息,知道import “shape”已经生效了,即可以找到取名go install的shape包。但有新的问题。

为此,注意到Introducing Go Page81的如下说明:

In Go, if something starts with a capital letter, that means other packages (and programs) are able to see it. If we had named the function average instead of Average, our main program would not have been able to see it.

It’s a good practice to only expose the parts of our package that we want other packages using and hide everything else. This allows us to freely change those parts later without having to worry about breaking other programs, and it makes our package easier to use.

要点:

  • package中大写打头的是可以export的,小写打头的外面则不能访问
  • 通过这种首字母大小写可以起到public&private的作用。

修改后的代码

为了对比,这里新创建一个shape2文件,即新建一个名为shape2的包。然后修改这个目录下的the_shape.go文件:

  • package的名字改成shape2
  • 里边要导出的变量或函数改成首字母大写

    D:\examples\the_go_path>tree /F .
    文件夹 PATH 列表
    卷序列号为 5645-602F
    D:\EXAMPLES\THE_GO_PATH
    ├─pkg
    │ └─windows_amd64
    │ shape.a

    └─src
    ├─shape
    │ the_shape.go

    └─shape2
    the_shape.go

    D:\examples\the_go_path>

shape2/the_shape.go

package shape2

import "math"

type Shape interface {
    Area() float64 
}

type Circle struct {
    R float64 
}

func (c *Circle) Area() float64 {
    return math.Pi * c.R * c.R
}

type Rectangle struct {
    Length, Width float64 
}

func (r *Rectangle) Area() float64 {
    return r.Length * r.Width;
}

func TotalArea(shapes ... Shape) (total float64) {
    total = 0 
    for _, shape := range shapes {
        total += shape.Area()
    }

    return 
}

go install

D:\examples\the_go_path>cd src/shape2

D:\examples\the_go_path\src\shape2>go install

D:\examples\the_go_path\src\shape2>cd ../..

D:\examples\the_go_path>tree /F .
文件夹 PATH 列表
卷序列号为 5645-602F
D:\EXAMPLES\THE_GO_PATH
├─pkg
│  └─windows_amd64
│          shape.a
│          shape2.a
│
└─src
    ├─shape
    │      the_shape.go
    │
    └─shape2
            the_shape.go


D:\examples\the_go_path>

修改调用函数

package main
import "fmt"
import "shape2"

/*
D:\examples\the_go_path>cd ..

D:\examples>go run helloworld.go
90.53981633974483

D:\examples>
*/
func main() {
    circle := shape2.Circle{5}
    ractangle := shape2.Rectangle{3, 4}

    fmt.Println(shape2.TotalArea(&circle, &ractangle))
}

Document

对于自己创建的package,也可以用godoc命令

注:为了显示下面的内容,已在源码上增加了一些注释信息。

D:\examples\the_go_path\src\shape2>godoc shape2
use 'godoc cmd/shape2' for documentation on the shape2 command

PACKAGE DOCUMENTATION

package shape2
    import "shape2"


FUNCTIONS

func TotalArea(shapes ...Shape) (total float64)

TYPES

type Circle struct {
    R float64
}

func (c *Circle) Area() float64
    Area of a cirle.

type Rectangle struct {
    Length, Width float64
}

func (r *Rectangle) Area() float64

type Shape interface {
    Area() float64
}
    Area of the shape. User must implement it.



D:\examples\the_go_path\src\shape2>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值