Go 高级

date
Apr 13, 2021
slug
28
status
Published
tags
Go
summary
type
Post

结构体

type Profile struct {
    name   string
    age    int
    gender string
    mother *Profile // 指针
    father *Profile // 指针
}
方法
func (person Profile) FmtProfile() {
    fmt.Printf("名字:%s\n", person.name)
    fmt.Printf("年龄:%d\n", person.age)
    fmt.Printf("性别:%s\n", person.gender)
}
把 Profile 称为方法的接收者,而 person 表示实例本身,它相当于 Python 中的 self,在方法内可以使用 person.属性名 的方法来访问实例属性.
完整代码:
package main

import "fmt"

// 定义一个名为Profile 的结构体
type Profile struct {
    name   string
    age    int
    gender string
    mother *Profile // 指针
    father *Profile // 指针
}

// 定义一个与 Profile 的绑定的方法
func (person Profile) FmtProfile() {
    fmt.Printf("名字:%s\n", person.name)
    fmt.Printf("年龄:%d\n", person.age)
    fmt.Printf("性别:%s\n", person.gender)
}

func main() {
    // 实例化
    myself := Profile{name: "小明", age: 24, gender: "male"}
    // 调用函数
    myself.FmtProfile()
}
传参
当你想要在方法内改变实例的属性的时候,必须使用指针做为方法的接收者。建议都使用指针做为接收者
package main

import "fmt"

// 声明一个 Profile 的结构体
type Profile struct {
    name   string
    age    int
    gender string
    mother *Profile// 指针father *Profile// 指针}

// 重点在于这个星号: 
func (person *Profile) increase_age() {
    person.age += 1
}

func main() {
    myself := Profile{name: "小明", age: 24, gender: "male"}
    fmt.Printf("当前年龄:%d\n", myself.age)
    myself.increase_age()
    fmt.Printf("当前年龄:%d", myself.age)
}
指针结构体
p3 := &person{} 
//或者
var p2 = new(person)
// 实例化
// p3.name = "博客"其实在底层是(*p3).name = "博客",这是Go语言帮我们实现的语法糖

结构体的组合

type company struct {
    companyName string
    companyAddr string
}

type staff struct {
    name string
    age int
    gender string
    position string
}

type staff struct {
    name string
    age int
    gender string
    position string
    company   // 匿名匿名匿名匿名匿名字段
}
完整例子
package main

import "fmt"

type company struct {
    companyName string
    companyAddr string
}

type staff struct {
    name string
    age int
    gender string
    position string
    company
}

func main()  {
    myCom := company{
        companyName: "Tencent",
        companyAddr: "深圳市南山区",
    }
    staffInfo := staff{
        name:     "小明",
        age:      28,
        gender:   "男",
        position: "云计算开发工程师",
        company: myCom,
    }

    fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName)
    fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName)
}
 
  • 当方法的首字母为大写时,这个方法对于所有包都是Public,其他可以随意调用
  • 当方法的首字母为小写时,这个方法是Private,其他是无法访问的。

Tag

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    Addr string `json:"addr,omitempty"`
}
//`key01:"value01" key02:"value02" key03:"value03"`
利用反射
// 三种获取 field
field := reflect.TypeOf(obj).FieldByName("Name")
field := reflect.ValueOf(obj).Type().Field(i)  // i 表示第几个字段
field := reflect.ValueOf(&obj).Elem().Type().Field(i)  // i 表示第几个字段

// 获取 Tag
tag := field.Tag

// 获取键值对
labelValue := tag.Get("label")
labelValue,ok := tag.Lookup("label")

结构体匿名内嵌

类型匿名内嵌
package main
import "fmt"
type innerS struct {
    in1 int
    in2 int
}
type outerS struct {
    b int
    c float32
    int // 匿名匿名匿名匿名匿名匿名类型字段
    innerS // 匿名匿名匿名匿名匿名匿名类型字段
}
func main() {
    outer := new(outerS)
    outer.b = 6
    outer.c = 7.5
    outer.int = 60
    outer.in1 = 5
    outer.in2 = 10
    fmt.Printf("outer.b is: %d\n", outer.b)
    fmt.Printf("outer.c is: %f\n", outer.c)
    fmt.Printf("outer.int is: %d\n", outer.int)
    fmt.Printf("outer.in1 is: %d\n", outer.in1)
    fmt.Printf("outer.in2 is: %d\n", outer.in2)
    // 使用结构体字面量
    outer2 := outerS{6, 7.5, 60, innerS{5, 10}}
    fmt.Printf("outer2 is:", outer2)
}
结构体匿名内嵌, 需要再次声明结构才能初始化. 感觉并不便利...
package main
import "fmt"
// 车轮
type Wheel struct {
    Size int
}
// 车
type Car struct {
    Wheel
    // 引擎
    Engine struct { // 结构体匿名内嵌
        Power int    // 功率
        Type  string // 类型
    }
}
func main() {
    c := Car{
        // 初始化轮子
        Wheel: Wheel{
            Size: 18,
        },
        // 初始化引擎
        Engine: struct {
            Power int
            Type  string
        }{
            Type:  "1.4T",
            Power: 143,
        },
    }
    fmt.Printf("%+v\n", c)
}

匿名方法

Manager 直接调用User的方法
package main

import "fmt"

type User struct {
    id   int
    name string
}

type Manager struct {
    User
}

func (self *User) ToString() string { // receiver = &(Manager.User)
    return fmt.Sprintf("User: %p, %v", self, self)
}

func main() {
    m := Manager{User{1, "Tom"}}
    fmt.Printf("Manager: %p\n", &m)
    fmt.Println(m.ToString()) // Manager 直接调用User的方法
}

类型别名和自定义类型

自定义类型是一个新类型, 类型别名在编译之后就不存在了,只是为了人类易读.
type TypeAlias = Type
type byte = uint8
type rune = int32
// 以上是类型别名
// 以下是自定义类型
type MyInt int

方法集

  • 类型 T 方法集包含全部 receiver T 方法。
  • 类型 *T 方法集包含全部 receiver T + *T 方法。
  • 如类型 S 包含匿名字段 T,则 S*S 方法集包含 T 方法。
  • 如类型 S 包含匿名字段 *T,则 S*S 方法集包含 T + *T 方法。
  • 不管嵌入 T*T*S 方法集总是包含 T + *T 方法。

表达式

package main

import "fmt"

type User struct {
    id   int
    name string
}

func (self *User) TestPointer() {
    fmt.Printf("TestPointer: %p, %v\n", self, self)
}

func (self User) TestValue() {
    fmt.Printf("TestValue: %p, %v\n", &self, self)
}

func main() {
    u := User{1, "Tom"}
    fmt.Printf("User: %p, %v\n", &u, u)

    mv := User.TestValue // 这里
    mv(u)

    mp := (*User).TestPointer // 这里
    mp(&u)

    mp2 := (*User).TestValue // 还有这里
    mp2(&u)
}

接口与多态

type Phone interface {
   call()
}
实现
type Nokia struct {
    name string
}

// 接收者为 Nokia
func (phone Nokia) call() {
    fmt.Println("我是 Nokia,是一台电话")
}
接口方法可以带返回值
package main

import (
    "fmt"
    "strconv"
)

// 定义一个接口
type Good interface {
    settleAccount() int
    orderInfo() string
}

type Phone struct {
    name string
    quantity int
    price int
}

func (phone Phone) settleAccount() int {
    return phone.quantity * phone.price
}
func (phone Phone) orderInfo() string{
    return "您要购买" + strconv.Itoa(phone.quantity)+ "个" +
        phone.name + "计:" + strconv.Itoa(phone.settleAccount()) + "元"
}

type FreeGift struct {
    name string
    quantity int
    price int
}

func (gift FreeGift) settleAccount() int {
    return 0
}
func (gift FreeGift) orderInfo() string{
    return "您要购买" + strconv.Itoa(gift.quantity)+ "个" +
        gift.name + "计:" + strconv.Itoa(gift.settleAccount()) + "元"
}

func calculateAllPrice(goods []Good) int {
    var allPrice int
    for _,good := range goods{
        fmt.Println(good.orderInfo())
        allPrice += good.settleAccount()
    }
    return allPrice
}
func main()  {
    iPhone := Phone{
        name:     "iPhone",
        quantity: 1,
        price:    8000,
    }
    earphones := FreeGift{
        name:     "耳机",
        quantity: 1,
        price:    200,
    }

    goods := []Good{iPhone, earphones}
    allPrice := calculateAllPrice(goods)
    fmt.Printf("该订单总共需要支付 %d 元", allPrice)
}

空接口的坑(注意事项)

  • 当空接口承载数组和切片后,该对象无法再进行切片
    • package main
      
      import "fmt"
      
      func main() {
          sli := []int{2, 3, 5, 7, 11, 13}
      
          var i interface{}
          i = sli
      
          g := i[1:3]
          fmt.Println(g)
      }
      //./main.go:11:8: cannot slice i (type interface {})
  • 空接口需要做switch 断言判断
    • package main
      
      import (
          "fmt"
      )
      
      func myfunc(iinterface{})  {
      
      switch i.(type) {
      case int:
              fmt.Println("参数的类型是 int")
      case string:
              fmt.Println("参数的类型是 string")
          }
      }
      
      func main() {
          a := 10
          b := "hello"
          myfunc(a)
          myfunc(b)
      }
  • 接口如法调用非接口方法,哪怕结构体实现了
    • package main
      
      import "fmt"
      
      type Phone interface {
          call()
      }
      
      type iPhone struct {
          name string
      }
      
      func (phone iPhone)call()  {
          fmt.Println("Hello, iPhone.")
      }
      
      func (phone iPhone)send_wechat()  {
          fmt.Println("Hello, Wechat.")
      }
      
      func main() {
          var phone Phone
          phone = iPhone{name:"ming's iphone"}
          phone.call()
          phone.send_wechat() // 报错报错报错报错
      }
  • 调用函数时的隐式转换
    • 传入函数的参数值的类型隐式的转换成 interface{} 类型
      import (
          "fmt"
      )
      
      func printType(i interface{})  {
      
          switch i.(type) {
          case int:
              fmt.Println("参数的类型是 int")
          case string:
              fmt.Println("参数的类型是 string")
          }
      }
      
      func main() {
          a := 10
          printType(a)
      }
  • 强制类型转换
    • package main
      
      import "fmt"
      
      
      func main() {
          a := 10
      
          switch interface{}(a).(type) {
          case int:
              fmt.Println("参数的类型是 int")
          case string:
              fmt.Println("参数的类型是 string")
          }
      }

接口的嵌套

类型断言Type Assertion

t := i.(T)
//或者*T
t := i.(*T) // 这时候t是指针
断言成功, 返回i的值. 断言失败 panic
package main

import "fmt"

func main() {
    var i interface{} = 10
    t1 := i.(int)
    fmt.Println(t1)

    fmt.Println("=====分隔线=====")

    t2 := i.(string)
    fmt.Println(t2)
}
另一种方式, 成功t是值, ok是true. 失败t是零值,ok是false
t, ok:= i.(T)

Type Switch

如果要区分多种类型.
i.(type)
  • 静态类型为空接口(interface{})的对象进行断言
  • 如果你的值是 nil,那么匹配的是 case nil
  • 如果你的值在 switch-case 里并没有匹配对应的类型,那么走的是 default 分支
package main

import "fmt"

func findType(i interface{}) {
    switch x := i.(type) {
    case int:
        fmt.Println(x, "is int")
    case string:
        fmt.Println(x, "is string")
    case nil:
        fmt.Println(x, "is nil")
    default:
        fmt.Println(x, "not type matched")
    }
}

func main() {
    findType(10)      // int
    findType("hello") // string

    var k interface{} // nil
    findType(k)

    findType(10.23) //float64
}
例子
package main

import "fmt"
import "math"

func main() {
    shapes := []Shape{
        Circle{1.0},
        Square{1.772453},
        Rectangle{5, 10},
        Triangle{10, 4, 7},
    }
    for _, v := range shapes {
        fmt.Println(v, "\tArea:", v.Area())
        if t, ok := v.(Triangle); ok {
            fmt.Println("Angles:", t.Angles())
        }
    }
}

type Shape interface {
    Area() float64
}
type Circle struct {
    Radius float64
}
type Triangle struct {
    A, B, C float64 // lengths of the sides of a triangle.
}
type Rectangle struct {
    A, B float64
}
type Square struct {
    A float64
}

func (t Circle) Area() float64 {
    return math.Pi * t.Radius * t.Radius
}

// Heron's Formula for the area of a triangle
func (t Triangle) Area() float64 {
    p := (t.A + t.B + t.C) / 2.0 // perimeter half
    return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
}
func (t Rectangle) Area() float64 {
    return t.A * t.B
}

func (t Square) Area() float64 {
    return t.A * t.A
}

func (t Circle) String() string {
    return fmt.Sprint("Circle (Radius: ", t.Radius, ")")
}
func (t Triangle) String() string {
    return fmt.Sprint("Triangle (Sides: ", t.A, ", ", t.B, ", ", t.C, ")")
}
func (t Rectangle) String() string {
    return fmt.Sprint("Rectangle (Sides: ", t.A, ", ", t.B, ")")
}
func (t Square) String() string {
    return fmt.Sprint("Square (Sides: ", t.A, ")")
}

func (t Triangle) Angles() []float64 {
    return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
}
func angle(a, b, c float64) float64 {
    return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
}

反射

反射可以将接口类型变量 转换为“反射类型对象”

  1. reflect.TypeOf(i) :获得接口值的类型
  1. reflect.ValueOf(i):获得接口值的值

也可以转回来

package main

import (
"fmt"
"reflect"
)

func main() {
    var age interface{} = 25
		var age int = 25 // 这样也没问题

    fmt.Printf("原始接口变量的类型为 %T,值为 %v \n", age, age)

    t := reflect.TypeOf(age)
    v := reflect.ValueOf(age)

    // 从接口变量到反射对象
    fmt.Printf("从接口变量到反射对象:Type对象的类型为 %T \n", t)
    fmt.Printf("从接口变量到反射对象:Value对象的类型为 %T \n", v)

    // 从反射对象到接口变量
    i := v.Interface()
    fmt.Printf("从反射对象到接口变量:新对象的类型为 %T 值为 %v \n", i, i)

}

当你使用 reflect.Typeof 和 reflect.Valueof 的时候,如果传递的不是接口变量的指针,反射世界里的变量值始终将只是真实世界里的一个拷贝,你对该反射对象进行修改,并不能反映到真实世界里

 
  • 不是接收变量指针创建的反射对象,是不具备『可写性』的
  • 是否具备『可写性』,可使用 CanSet() 来获取得知
  • 对不具备『可写性』的对象进行修改,是没有意义的,也认为是不合法的,因此会报错。
  • 要让反射对象具备可写性,需要注意两点, 缺一不可
      1. 创建反射对象时传入变量的指针
      1. 使用 Elem()函数返回指针指向的数据
      package main
      
      import (
          "fmt"
          "reflect"
      )
      func main() {
          var name string = "Go编程"
          v1 := reflect.ValueOf(&name)
          fmt.Println("v1 可写性为:", v1.CanSet())
      
          v2 := v1.Elem() // Elem() 返回 Type 对象所表示的指针
  • 修改真实世界的值
    • notion image
package main

import (
    "fmt"
    "reflect"
)

func main() {
    var name string = "Go编程"
    fmt.Println("真实世界里 name 的原始值为:", name)

    v1 := reflect.ValueOf(&name)
    v2 := v1.Elem()

    v2.SetString("Python编程")
    fmt.Println("通过反射对象进行更新后,真实世界里 name 变为:", name)
}

其他函数

Kind()
package main

import (
    "fmt"
    "reflect"
)

type Profile struct {
    name string
    age int
    gender string
}

func main() {
    m := Profile{}

    t := reflect.TypeOf(m)
    fmt.Println("Type: ",t) // Type:  main.Profile
    fmt.Println("Kind: ",t.Kind()) // Kind:  struct
}
//-----------------------------------------
package main

import (
    "fmt"
    "reflect"
)

type Profile struct {
    name string
    age int
    gender string
}

func main() {
    m := Profile{}

    t := reflect.TypeOf(&m)

    fmt.Println("&m Type: ",t) // &m Type:  *main.Profile
    fmt.Println("&m Kind: ",t.Kind()) // &m Kind:  ptr

    fmt.Println("m Type: ",t.Elem()) // m Type:  main.Profile
    fmt.Println("m Kind: ",t.Elem().Kind()) // m Kind:  struct
}
package main

import (
	"fmt"
	"reflect"
)

func main() {

	var age int = 25

	v1 := reflect.ValueOf(age)
	fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)
	v2 := v1.Int()
	fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)

	// var score float64 = 99.5

	// v1 := reflect.ValueOf(score)
	// fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)
	// v2 := v1.Float()
	// fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)

	// var name string = "Go编程"

	// v1 := reflect.ValueOf(name)
	// fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)
	// v2 := v1.String()
	// fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)

	// var isMale bool = true

	// v1 := reflect.ValueOf(isMale)
	// fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)
	// v2 := v1.Bool()
	// fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)

	// var age int = 25

	// v1 := reflect.ValueOf(&age)
	// fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)
	// v2 := v1.Pointer()
	// fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)

	// var age int = 25

	// v1 := reflect.ValueOf(age)
	// fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)
	// v2 := v1.Interface()
	// fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)
}
Int() :转成 int
Float():转成 float
String():转成 string
Bool():转成布尔值
Pointer():转成指针
Interface():转成接口类型
 
Slice():对切片再切片(两下标)
Slice3():对切片再切片(三下标)
以上返回还是 reflect.Value 反射对象,而不再是我们所想的真实世界里的切片对象
package main

import (
    "fmt"
    "reflect"
)

func main() {

    var numList []int = []int{1,2}

    v1 := reflect.ValueOf(numList)
    fmt.Printf("转换前, type: %T, value: %v \n", v1, v1)

    // Slice 函数接收两个参数
    v2 := v1.Slice(0, 2)
    fmt.Printf("转换后, type: %T, value: %v \n", v2, v2)
}

// 转换前, type: reflect.Value, value: [1 2]
// 转换后, type: reflect.Value, value: [1 2]
Set() 和 Append():更新切片
package main

import (
    "fmt"
    "reflect"
)
// func appendToSlice(arrPtr *[]int) {
func appendToSlice(arrPtr interface{}) {
    valuePtr := reflect.ValueOf(arrPtr)
    value := valuePtr.Elem()

    value.Set(reflect.Append(value, reflect.ValueOf(3)))

    fmt.Println(value)
    fmt.Println(value.Len())
}

func main() {
    arr := []int{1,2}

    appendToSlice(&arr)

    fmt.Println(arr)
}

// [1 2 3]
// 3
// [1 2 3]
NumField() 和 Field()
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    name string
    age int
    gender string
}

func (p Person)SayBye()  {
    fmt.Println("Bye")
}

func (p Person)SayHello()  {
    fmt.Println("Hello")
}



func main() {
    p := Person{"写代码", 27, "male"}

    v := reflect.ValueOf(p)

    fmt.Println("字段数:", v.NumField())
    fmt.Println("第 1 个字段:", v.Field(0))
    fmt.Println("第 2 个字段:", v.Field(1))
    fmt.Println("第 3 个字段:", v.Field(2))

    fmt.Println("==========================")
    // 也可以这样来遍历
    for i:=0;i<v.NumField();i++{
        fmt.Printf("第 %d 个字段:%v \n", i+1, v.Field(i))
    }
}
NumMethod() 和 Method()
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    name string
    age int
    gender string
}

func (p Person)SayBye()  {
    fmt.Println("Bye")
}

func (p Person)SayHello()  {
    fmt.Println("Hello")
}



func main() {
    p := &Person{"写代码", 27, "male"}

    t := reflect.TypeOf(p)

    fmt.Println("方法数(可导出的):", t.NumMethod())
    fmt.Println("第 1 个方法:", t.Method(0).Name)
    fmt.Println("第 2 个方法:", t.Method(1).Name)

    fmt.Println("==========================")
    // 也可以这样来遍历
    for i:=0;i<t.NumMethod();i++{
       fmt.Printf("第 %d 个方法:%v \n", i+1, t.Method(i).Name)
    }
}
Call 动态调动方法
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    name string
    age int
}

func (p Person)SayBye() string {
    return "Bye"
}

func (p Person)SayHello() string {
    return "Hello"
}


func main() {
    p := &Person{"wangbm", 27}

    t := reflect.TypeOf(p)
    v := reflect.ValueOf(p)


    for i:=0;i<v.NumMethod();i++{
       fmt.Printf("调用第 %d 个方法:%v ,调用结果:%v\n",
           i+1,
           t.Method(i).Name,
           v.Elem().Method(i).Call(nil))// 这里
    }
		
		v.MethodByName("SayHello").Call(nil)
    v.MethodByName("SayBye").Call(nil)
}
动态调用且带参数
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
}

func (p Person)SelfIntroduction(name string, age int)  {
    fmt.Printf("Hello, my name is %s and i'm %d years old.", name, age)
}



func main() {
    p := &Person{}

    //t := reflect.TypeOf(p)
    v := reflect.ValueOf(p)
    name := reflect.ValueOf("wangbm")
    age := reflect.ValueOf(27)
    input := []reflect.Value{name, age}
    v.MethodByName("SelfIntroduction").Call(input)
}

静态类型与动态类型

静态类型, 变量声明的时候的类型, 肉眼可见的类型.
var age int   // int 是静态类型
var name string  // string 也是静态类型
动态类型 concrete type, 程序运行时系统才能看见的类型
var i interface{}

i = 18
i = "Go编程"
i的静态类型是 空接口,动态类型可能是int也可能是字符串

所有变量都是接口

package main

import "fmt"

func main() {
	age := int(25)            // 等价
	age2 := (int)(25)         // 等价
	age3 := interface{}(25)   // 等价
	age4 := (interface{})(25) // 等价

	fmt.Printf("type: %T, data: %v \n", age, age)
	fmt.Printf("type: %T, data: %v \n", age2, age2)
	fmt.Printf("type: %T, data: %v \n", age3, age3)
	fmt.Printf("type: %T, data: %v \n", age4, age4)
}

make 和 new 的区别

func new(Type) *Type
  • 分配内存
  • 设置零值
  • 返回指针(重要)
import "fmt"

type Student struct {
   name string
   age int
}

func main() {
    // new 一个内建类型
    num := new(int)
    fmt.Println(*num) //打印零值:0

    // new 一个自定义类型
    s := new(Student)
    s.name = "wangbm"
}
make
func make(t Type, size ...IntegerType) Type
  1. 内建函数 make 用来为 slice,map 或 chan 类型(注意:也只能用在这三种类型上)分配内存和初始化一个对象
  1. make 返回类型的本身而不是指针,而返回值也依赖于具体传入的类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了
  1. make必须初始化, 没有默认零值这一说.
//切片
a := make([]int, 2, 10)

// 字典
b := make(map[string]int)

// 通道
c := make(chan int, 10)

在 Go 语言中,一个包可包含多个 .go 文件(这些文件必须得在同一级文件夹中),只要这些 .go 文件的头部都使用 package 关键字声明了同一个包。
import "fmt"
import "sync"
// 或者
import(
    "fmt"
    "sync"
)

别名, 先写别名

import (
    "crypto/rand"
    mrand "math/rand" // 将名称替换为mrand避免冲突
)

from xxx import *, go 中也有类似解决方案

import . "fmt"

func main() {
    Println("hello, world")
}

init 函数, 每个包可以有很多个, 用于初始化, 深度优先

C.init→B.init→A.init→main
// main→A→B→C

匿名导入, 只调用包的init, 不执行包里面的函数/方法/对象...

import _ "image/png"

导入的实际是目录,不是包, 只不过包名通常和目录名一致

  • 导入时,是按照目录导入。导入目录后,可以使用这个目录下的所有包。
  • 出于习惯,包名和目录名通常会设置成一样,所以会让你有一种你导入的是包的错觉。

相对导入和绝对导入

绝对导入:从 $GOPATH/src 或 $GOROOT 或者 $GOPATH/pkg/mod 目录下搜索包并导入
相对导入:从当前目录中搜索包并开始导入。
推荐绝对导入
import (
    "./module1"
    "../module2"
    "../../module3"
    "../module4/module5"
)

包导入路径优先级

如果使用 govendor — 这个不是指vendor目录
  1. 先从项目根目录的 vendor 目录中查找
  1. 最后从 $GOROOT/src 目录下查找
  1. 然后从 $GOPATH/src 目录下查找
  1. 都找不到的话,就报错。
如果使用 go modules — go mod 也可以有vendor目录 — go mod vendor
  1. 你导入的包如果有域名,都会先在 $GOPATH/pkg/mod 下查找,找不到就连网去该网站上寻找,找不到或者找到的不是一个包,则报错。
  1. 而如果你导入的包没有域名(比如 “fmt”这种),就只会到 $GOROOT 里查找。
  1. 还有一点很重要,当你的项目下有 vendor 目录时,不管你的包有没有域名,都只会在 vendor 目录中想找

© chaleaoch 2021