Пакет состоит из файлов Go, которые находятся в одном и том же каталоге и имеют одинаковый оператор пакета в начале. Вы можете включить дополнительные функции из пакетов, чтобы сделать ваши программы более сложными. Некоторые пакеты доступны через стандартную библиотеку Go и поэтому устанавливаются вместе с установкой Go. Остальные можно установить с помощью команды Gogo get
. Вы также можете создавать свои собственные пакеты Go, создавая файлы Go в том же каталоге, в котором вы хотите поделиться кодом, используя необходимую инструкцию пакета.
Этот учебник поможет вам в написании пакетов Go для использования в других программных файлах.
Предпосылки
-
Настройте среду программирования Go, следуя одному из руководств из серииHow To Install and Set Up a Local Programming Environment for Go. Создайте свое рабочее пространство Go, выполнив шаг 5 в учебном руководстве по локальной среде программирования Чтобы следовать примеру и соглашениям об именах в этой статье, прочитайте первый раздел Написание и импорт пакетов.
-
Чтобы углубить свои знания о GOPATH, прочтите нашу статьюUnderstanding the GOPATH.
Написание и импорт пакетов
Написание пакета аналогично написанию любого другого файла Go. Пакеты могут содержать определения функцийtypes иvariables, которые затем можно использовать в других программах Go.
Прежде чем мы создадим новый пакет, мы должны быть в нашей рабочей области Go. Обычно это под нашимgopath
. Например, в этом руководстве мы назовем пакетgreet
. Для этого мы создали каталог с именемgreet
в нашемgopath
в пространстве нашего проекта. Если бы наша организация былаgopherguides
, и мы хотели бы создать пакетgreet
в этой организации, используя Github в качестве репозитория кода, то наш каталог выглядел бы так:
└── $GOPATH
└── src
└── github.com
└── gopherguides
Каталогgreet
находится внутри каталогаgopherguides
:
└── $GOPATH
└── src
└── github.com
└── gopherguides
└── greet
Наконец, мы можем добавить первый файл в наш каталог. Принято считать, что файлprimary
илиentry point
в пакете именуется по имени каталога. В этом случае мы бы создали файл с именемgreet.go
внутри каталогаgreet
:
└── $GOPATH
└── src
└── github.com
└── gopherguides
└── greet
└── greet.go
Создав файл, мы можем начать писать наш код, который мы хотим использовать повторно или делиться между проектами. В этом случае мы создадим функцию с именемHello
, которая выводитHello World
.
Откройте файлgreet.go
в текстовом редакторе и добавьте следующий код:
greet.go
package greet
import "fmt"
func Hello() {
fmt.Println("Hello, World!")
}
Давайте разберем этот первый файл. В первой строке каждого файла необходимо указать имяpackage
, с которым вы работаете. Поскольку вы находитесь в пакетеgreet
, вы используете ключевое словоpackage
, за которым следует имя пакета:
package greet
Это укажет компилятору рассматривать все в файле как часть пакетаgreet
.
Затем вы объявляете любые другие пакеты, которые вам нужно использовать, с помощью оператораimport
. В этом файле вы используете только один - пакетfmt
:
import "fmt"
Наконец, вы создаете функциюHello
. Он будет использовать пакетfmt
для выводаHello, World!
:
func Hello() {
fmt.Println("Hello, World!")
}
Теперь, когда вы написали пакетgreet
, вы можете использовать его в любом другом создаваемом вами пакете. Давайте создадим новый пакет, в котором вы будете использовать свой пакетgreet
.
Вы собираетесь создать пакет с именемexample
, а это значит, что вам нужен каталог с именемexample
. Создайте этот пакет в своей организацииgopherguides
, чтобы структура каталогов выглядела так:
└── $GOPATH
└── src
└── github.com
└── gopherguides
└── example
Теперь, когда у вас есть каталог для нового пакета, вы можете создать файл точки входа. Поскольку это будет исполняемая программа, рекомендуется назвать файл точки входаmain.go
:
└── $GOPATH
└── src
└── github.com
└── gopherguides
└── example
└── main.go
В текстовом редакторе откройтеmain.go
и добавьте следующий код для вызова пакетаgreet
:
main.go
package main
import "github.com/gopherguides/greet"
func main() {
greet.Hello()
}
Поскольку вы импортируете пакет, вам нужно вызвать функцию, указав имя пакета в точечной нотации. Dot notation - это практика помещения точки.
между именем используемого пакета и ресурсом в этом пакете, который вы хотите использовать. Например, в вашем пакетеgreet
у вас есть функцияHello
как ресурс. Если вы хотите вызвать этот ресурс, используйте точечную записьgreet.Hello()
.
Теперь вы можете открыть свой терминал и запустить программу из командной строки:
go run main.go
Когда вы это сделаете, вы получите следующий вывод:
OutputHello, World!
Чтобы увидеть, как можно использовать переменные в пакете, давайте добавим определение переменной в ваш файлgreet.go
:
greet.go
package greet
import "fmt"
var Shark = "Sammy"
func Hello() {
fmt.Println("Hello, World!")
}
Затем откройте файлmain.go
и добавьте следующую выделенную строку для вызова переменной изgreet.go
в функцииfmt.Println()
:
main.go
package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
}
После того, как вы снова запустите программу:
go run main.go
Вы получите следующий вывод:
OutputHello, World!
Sammy
Наконец, давайте также определим тип в файлеgreet.go
. Вы создадите типOctopus
с полямиname
иcolor
, а также функцию, которая будет распечатывать поля при вызове:
greet.go
package greet
import "fmt"
var Shark = "Sammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}
func Hello() {
fmt.Println("Hello, World!")
}
Откройтеmain.go
, чтобы создать экземпляр этого типа в конце файла:
main.go
package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
oct := greet.Octopus{
Name: "Jesse",
Color: "orange",
}
fmt.Println(oct.String())
}
После того как вы создали экземпляр типаOctopus
сoct := greet.Octopus
, вы можете получить доступ к функциям и полям типа в пространстве имен файлаmain.go
. Это позволяет вам писатьoct.String()
в последней строке без вызоваgreet
. Вы также можете, например, вызвать одно из полей типов, такое какoct.Color
, не ссылаясь на имя пакетаgreet
.
МетодString
для типаOctopus
использует функциюfmt.Sprintf
для создания предложения, аreturns
- результат, строку, для вызывающего (в данном случае ваш основной программа).
Когда вы запустите программу, вы получите следующий вывод:
go run main.go
OutputHello, World!
Sammy
The octopus's name is "Jesse" and is the color orange.
Создав методString
дляOctopus
, теперь у вас есть многоразовый способ распечатать информацию о вашем настраиваемом типе. Если вы хотите изменить поведение этого метода в будущем, вам нужно только отредактировать этот метод.
Экспортированный код
Вы могли заметить, что все объявления в вызванном вами файлеgreet.go
были написаны с заглавной буквы. В Go нет концепции модификаторовpublic
,private
илиprotected
, как в других языках. Внешняя видимость контролируется заглавными буквами. Типы, переменные, функции и т. Д., Начинающиеся с заглавной буквы, доступны публично вне текущего пакета. Символ, видимый вне упаковки, считаетсяexported
.
Если вы добавляете вOctopus
новый метод с именемreset
, вы можете вызывать его из пакетаgreet
, но не из вашего файлаmain.go
, который находится за пределамиgreet
пакет:
greet.go
package greet
import "fmt"
var Shark = "Sammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}
func (o *Octopus) reset() {
o.Name = ""
o.Color = ""
}
func Hello() {
fmt.Println("Hello, World!")
}
Если вы попытаетесь вызватьreset
из файлаmain.go
:
main.go
package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
oct := greet.Octopus{
Name: "Jesse",
Color: "orange",
}
fmt.Println(oct.String())
oct.reset()
}
Вы получите следующую ошибку компиляции:
Outputoct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)
Чтобыexport
функциональностьreset
изOctopus
, используйтеR
вreset
с заглавной буквы:
greet.go
package greet
import "fmt"
var Shark = "Sammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}
func (o *Octopus) Reset() {
o.Name = ""
o.Color = ""
}
func Hello() {
fmt.Println("Hello, World!")
}
В результате вы можете вызватьReset
из другого пакета без получения ошибки:
main.go
package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
oct := greet.Octopus{
Name: "Jesse",
Color: "orange",
}
fmt.Println(oct.String())
oct.Reset()
fmt.Println(oct.String())
}
Теперь, если вы запустите программу:
go run main.go
Вы получите следующий вывод:
OutputHello, World!
Sammy
The octopus's name is "Jesse" and is the color orange
The octopus's name is "" and is the color .
ВызвавReset
, вы очистили всю информацию в поляхName
иColor
. Когда вы вызываете методString
, он ничего не печатает там, где обычно появляютсяName
иColor
, потому что поля теперь пусты.
Заключение
Написание пакета Go аналогично написанию любого другого файла Go, но размещение его в другом каталоге позволяет изолировать код для повторного использования в другом месте. В этом руководстве рассказывалось о том, как писать определения в пакете, демонстрировалось, как использовать эти определения в другом программном файле Go, и объяснялись варианты того, где хранить пакет, чтобы получить к нему доступ.