包语句与.go文件目录之间的关系

时间:2017-04-24 04:29:37

标签: go import package

参见此实验。

~/go/src$ tree -F
.
├── 1-foodir/
│   └── 2-foofile.go
└── demo.go

1 directory, 2 files

~/go/src$ cat demo.go 
package main

import (
    "fmt"
    "1-foodir"
)

func main() {
    fmt.Println(foopkg.FooFunc())
}

~/go/src$ cat 1-foodir/2-foofile.go 
package foopkg

func FooFunc() string {
    return "FooFunc"
}

~/go/src$ GOPATH=~/go go run demo.go 
FooFunc

我以为我们总是导入一个包名。但是上面的例子 表明我们实际导入了一个包目录名("1-foodir") 但是在调用该包中的导出名称时,我们使用了 在Go文件(foopkg.FooFunc)中声明的包名称。

这对像我这样来自Java和Python世界的初学者来说很困惑, 目录名称本身就是用于限定的包名称 包中定义的模块/类。

为什么我们使用import语句的方式有所不同 参考Go中包中定义的名称?你能解释一下规则吗? 关于Go的这些事情背后?

3 个答案:

答案 0 :(得分:4)

如果你说的是真的,那么你的函数调用实际上是1 - foodir.FooFunc()而不是foopkg.FooFunc()。相反,go在2-foofile.go中看到包名称并将其导入为foopkg,因为在包中,包名称正好是.go文件顶部的单词package之后的名称,只要它是有效的标识符。

目录的唯一用途是收集共享相同包名的一组文件。这在规范中重申了

  

一组共享相同PackageName的文件形成包的实现。实现可能要求包的所有源文件都位于同一目录中。

在go中,通常目录与包名匹配,但这不是必须的,并且通常不包含第三方包。 stdlib确实很好地坚持了这个惯例。

目前的目录是导入路径。您可以拥有两个名为' foo'在你的单个二进制文件中,只要它们有不同的导入路径,即

/some/path/1/foo/some/path/2/foo

我们可以变得非常时髦,并将进口别名变为我们想要的任何东西,例如我可以做到

import (
    bar "/some/path/1/foo"
    baz "/some/path/2/foo"
)

同样有效的原因不是因为包名必须是唯一的,而且包导入路径必须是唯一的。

从这个陈述中收集的另一个洞察力是 - 在一个目录中,你不能有两个包名。 go编译器会抛出一个错误,说明它cannot load package并且它found packages foo (foo.go) and bar (bar.go)

有关详细信息,请参阅https://golang.org/doc/code.html#PackageNames

答案 1 :(得分:3)

大致为什么:

  1. 套餐有一个"名称"这是由package子句设置的,源代码开头是package thepackagename

  2. 导入包的过程几乎是不透明的字符串:导入声明中的导入路径。

  3. 第一个是名称,第二个是如何找到该名称。第一个是程序员,第二个是编译器/工具链。 声明(编译器和程序员)非常方便

      

    请导入"/some/hierarchical/location"

    中找到的包

    然后在

    等语句中通过它的简单名称robot引用该包
    robot.MoveTo(3,7)
    

    请注意使用此包

    /some/hierarchical/location.MoveTo(3.7)
    

    不是合法代码,既不可读也不清晰也不方便。 但是对于编译器/工具链,如果导入路径具有结构并允许表示任意包位置,即不仅是文件系统中的位置,而且例如,在档案馆内或远程机器上,或或。或。

    同样重要的是要注意:Go编译器和go工具。 Go编译器和go工具是不同的东西,go工具对你的代码,工作空间和包的布局方式施加了比Go编译器和语言规范所要求的更多限制。 (例如,Go编译器允许将来自不同目录的文件编译成一个包而没有任何问题。)

    go工具强制要求所有(有特殊情况,我知道)包的源文件驻留在一个文件系统目录中,并且常识要求此目录应该被命名为包"

答案 2 :(得分:1)

首先,包子句和导入路径是不同的东西。

package clause 声明 PackageName

PackageClause  = "package" PackageName .
PackageName    = identifier .

包子句的目的是对文件进行分组:

<块引用>

一组共享相同 PackageName 的文件构成了一个包的实现。

按照惯例ImportPath(见下文)的路径基名(目录名)与 PackageName 相同。为方便起见,建议您无需考虑要使用的 PackageName

但是,它们可以不同。

basename 只影响 ImportPath,检查 spec for import declartions

ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec       = [ "." | PackageName ] ImportPath .
ImportPath       = string_lit .
<块引用>

如果省略 PackageName,则默认为导入包的 package 子句中指定的标识符。

例如,如果您有一个目录 foo,但您声明 package bar 位于其中的源文件中,那么当您 import <prefix>/foo 时,您将使用 bar 作为前缀以引用从该包中导出的任何符号。

darehas' answer 提出了一个很好的观点:您不能在同一个基本名称下声明多个包。但是,根据 package clause,您可以将相同的包分布在不同的基名上:

<块引用>

实现可能要求包的所有源文件都位于同一目录中。