go与beego
beego go
go
一个例子
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
基本语法
注意:在go中声明的变量必须使用,不然会报错。当某些参数不想使用时,使用 _ 代替
注意:字符串类型在go中是一个结构,包含指向底层数组的指针和长度,这两个部分都是8个字节,所以一共为16个字节
注意:在定义常量组时,如果不提供初值,则表示使用上一行的表达式
注意:iota只在同一个const常量组中递增,当有新的const关键字时,会重新从0计数
var age int //变量定义
i := 1 //更简洁
i, j = j, i //交换变量
s1 + s2 //字符串直接相加
func main() {
_,numb,strs := numbers() //只获取函数返回值的后两个
fmt.Println(numb,strs)
}
//一个可以返回多个值的函数
func numbers()(int,int,string){
a , b , c := 1 , 2 , "str"
return a,b,c
}
const a string = "hello"
b := len(a) //求元素数
c := unsafe.Sizeof(b) //求字节数, c=16
fmt.Println(a, b, c)
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1 使用上一行的表达式
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
注意:只有a++,没有++a;而且a++不能直接输出
a := 10
a++
fmt.Println(a)
a := 10
ptr := &a //指针操作
*ptr++
fmt.Println(a, *ptr)
func main() {
a, b := 1, -1
swap(&a, &b)
fmt.Println(a, b)
}
func swap(x *int, y *int) { //指针交换
temp := *x
*x = *y
*y = temp
}
注意:switch-case默认带有break,如要继续执行,要用fallthrough
注意:if语句的格式
注意:select会循环检测条件,如果有满足则执行并退出
a := 10
if a < 20 {
fmt.Println("less than 20")
} else {
fmt.Println("bigger than 20")
}
注意:go中循环语句只有for,没有while;另外有break, continue
注意:go中的range可以遍历
a := 3
for i := 0; i < a; i++ { //等价于C中的for
fmt.Println(i)
}
j := 0
for j < a { //等价于C中的while
fmt.Println(j)
j++
}
strings := []string{"google", "runoob"}
for i, s := range strings { //range遍历
fmt.Println(i, s)
}
注意:go中函数默认使用值传递(形参)
函数高级用法
-
函数作为实参:可以实现回调
-
闭包:允许匿名函数
-
方法:go中没有面向对象的类,通过为结构体提供方法实现
func main() {
getSquare := func(x float64) float64 {
return math.Sqrt(x)
}
fmt.Println(getSquare(9)) //函数作为实参,类python
}
//回调函数
func main() {
testCallBack(1, callback)
testCallBack(2, func(x int) int {
fmt.Println("callback:", x)
return x
})
}
func testCallBack(x int, f func(int) int) int {
return f(x)
}
func callback(x int) int {
fmt.Println("callback:", x)
return x
}
func main() {
add_func := add(1, 2)
fmt.Println(add_func())
fmt.Println(add_func())
fmt.Println(add_func())
}
// 闭包使用方法
func add(x1, x2 int) func() (int, int) {
i := 0
return func() (int, int) {
i++
return i, x1 + x2
}
}
//Circle is struct
type Circle struct {
radius float64
}
func main() {
var c1 Circle
c1.radius = 10.0
fmt.Println(c1.getArea())
c1.changeRadius(100)
fmt.Println(c1.getArea())
}
//该method属于Circle类型对象的方法
func (c Circle) getArea() float64 {
return 3.14 * c.radius * c.radius
}
//当需要改变结构体属性时,需要传结构体的指针
func (c *Circle) changeRadius(radius float64) {
c.radius = radius
}
注意:全局变量与C++类似,相同名字时局部变量会代替全局变量
注意:局部变量和全局变量的默认值为0 (int), 0 (float),nil (pointer), false (bool)
数组
多维数组
数组作为函数的参数:是值传递,不会改变数组的值;但如果是切片的话,是指针引用,可以同步修改
func main() {
var v1 [3]int //数组定义
var v2 = [5]int{1, 2, 3}
fmt.Println(v1, v2)
}
func main() {
var v1 [3][4]int //3行4列的二维数组
var v2 = [3][5]int{{1, 2, 3, 4, 5}, {0, 9, 8, 7, 6}, {3, 4, 5, 6, 7}}
fmt.Println(v1, v2)
}
func main() {
b := [...]int{2, 3, 4, 5} //数组
boo(b)
fmt.Println(b)
p := []int{2, 3, 4, 5} //切片
poo(p)
fmt.Println(p)
}
iang
func boo(num [4]int) {
num[0], num[len(num)-1] = num[len(num)-1], num[0] //不对原数组产生影响
}
func poo(num []int) {
num[0], num[len(num)-1] = num[len(num)-1], num[0] //会对原数组产生影响
}
指针
空指针为nil,等价于nullptr
指针数组与数组指针
指向指针的指针
func main() {
var ptr *int
fmt.Println(ptr == nil)
}
func main() {
a := [max]int{1, 2, 3}
var ptr [3]*int //指针数组
for i := 0; i < 3; i++ {
ptr[i] = &a[i]
fmt.Println("address:", ptr[i], "value:", *ptr[i])
}
}
func main() {
a := [max]int{1, 2, 3}
var ptr *[3]int = &a //数组指针
for i := 0; i < max; i++ {
fmt.Println(&a[i], &(*ptr)[i])
}
}
func main() {
a := 100
var ptr *int = &a
var pptr **int = &ptr //指向指针的指针
fmt.Println(a, *ptr, **pptr)
}
结构体
结构体指针
//Complex is struct
type Complex struct {
real float64
imag float64
}
func main() {
var c1 Complex
c1.real = 1
c1.imag = -1
c2 := Complex{real: 1, imag: -2}
fmt.Println(c1, c2)
ptr := &c1 //结构体指针
fmt.Println(*ptr)
}
切片
-
一种动态数组,像vector,长度可变,可以追加元素; 非常python风格
-
作为函数参数时,切片按引用传递,数组按值传递
-
当把 slice 作为参数,本身传递的是值,但其内容就 byte* array,实际传递的是引用,所以可以在函数内部修改,但如果对 slice 本身做 append,而且导致 slice 进行了扩容,实际扩容的是函数内复制的一份切片,对于函数外面的切片没有变化。
-
底层数组容量是 k 的切片 slice[i:j] 来说,其长度为 j - i ;容量为 k - i
//第一个字段表示 array 的指针,是真实数据的指针第二个是表示 slice 的长度,第三个是表示 slice 的容量。
//unsafe.Sizeof(切片)永远都是 24
struct Slice //切片的结构
{
byte* array; // actual data
uintgo len; // number of elements
uintgo cap; // allocated number of elements
};
func main() {
s1 := []int{1, 2, 3} //直接初始化
printSlice(s1)
s2 := make([]int, 3, 5)
printSlice(s2)
s3 := s1[:] //s3为s1的引用,会影响s1
s3[0] = -1
printSlice(s1)
s4 := s1[0:1] //切片为原数组的引用,两者会互相影响;不包含右边界
s4[0] = -2
printSlice(s1)
printSlice(s4)
s4 = append(s4, 0) //append的时候没有超过cap,还是会影响原数组
s4[0] = 0
printSlice(s1)
printSlice(s4)
s4 = append(s4, 7, 8, 9, 10) //append的时候超过cap,会发生重新分配,与原数组无关了
s4[0] = -111
printSlice(s1)
printSlice(s4)
s5 := make([]int, len(s1)) //copy只是值复制,不会互相影响
copy(s5, s1)
s5[0] = -3
printSlice(s1)
printSlice(s5)
}
func printSlice(s []int) {
fmt.Println(s, len(s), cap(s)) //切片元素数量和容量
}
range
同python中的range,遍历容器,一个kye-valu对
func main() {
fmt.Println(os.Args)
for _, arg := range os.Args { //获取参数列表
fmt.Println(arg)
}
}
map集合
func main() {
var m1 map[string]int
m1 = make(map[string]int)
m1["first"] = 1
m1["second"] = 2
fmt.Println(m1)
m2 := make(map[string]string)
m2["china"] = "beijign"
fmt.Println(m2)
for key, value := range m1 {
fmt.Println(key, value)
}
delete(m1, "first") //删除
fmt.Println(m1)
capital, ok := m2["American"] //查找
if ok {
fmt.Println(capital)
} else {
fmt.Println("Not exist")
}
}
类型转化
func main() {
var a int = 3
var b float32 = 9.0
mean := b / float32(a)
fmt.Println(mean)
}
接口
把具有共性的方法定义在一起,任何其他类型直言实现了这些方法就是实现了这些接口
type Phone interface { //接口
call()
}
type Nokia struct {
}
func (nikia Nokia) call() {
fmt.Println("I am Nokia, I can call you!")
}
type Iphone struct {
}
func (iphone Iphone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(Nokia) //实例化接口
phone.call()
phone = new(Iphone) //实例化接口
phone.call()
}
错误处理
//内置的错误接口
type error interface {
Error() string
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, errors.New("square root of negative number")
} else {
result := math.Sqrt(x)
return result, nil
}
}
func main() {
result, err := Sqrt(-1)
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
}
并发
go开启一个 goroutine线程
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() { //输出没有固定顺序
go say("hi")
say("hello")
}
channel通道
用于在两个goroutine之间传递值来同步运行和通讯,<-用于通道方向,发送和接收
通道默认存消息和取消息都是阻塞的,只能发送一个数据,只要这个数据未被接收,则所有的发送阻塞
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
go func(c chan int) { //读写均可的channel c } (a)
go func(c <- chan int) { //只读的Channel } (a)
go func(c chan <- int) { //只写的Channel } (a)
func sum(num []int, c chan int) {
sum := 0
for _, n := range num {
sum += n
}
c <- sum
}
func main() {
s := []int{1, 2, 4, -4, 2, 3}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x := <-c
y := <-c
fmt.Println(x, y)
}
通道缓冲区
允许发送数据和接收数据处于异步状态,发送端的发送数据可以放在缓冲区里,接收端空闲可以去接收
但如果缓冲区满了,发送端就无法发送数据了
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞
ch := make(chan int, 100)
func main() {
c := make(chan int, 2)
c <- 1
c <- 2
fmt.Println(<-c) //先入先出
fmt.Println(<-c)
}
遍历通道
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c) // 关闭通道并不会丢失里面的数据,只是让读取通道数据的时候不会读完之后一直阻塞等待新数据写入
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
// 会结束,从而在接收第 11 个数据的时候就阻塞了。
for i := range c {
fmt.Println(i)
}
}
beego框架
beego支持MVC设计模式,将程序分成model, view, controller三个部分,视图层面向用户,模型层是核心的数据层,控制层根据视图层的指令,选取合适的数据进行操作,得到最终结果。MVC使得软件得以模块化。
支持热部署:代码更改后,不需要停止程序,直接可以看到更改
安装beego后需要配置GOPATH,之后通过bee new创建的文件都保存在GOPATH下的src下。
注意:在import包时,包前有 _ 表示先执行包内的init方法
- router:解析路由请求的,将请求转发给响应的controller