19.go语言基础学习(上)——2019年12月16日
Posted oneapple
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了19.go语言基础学习(上)——2019年12月16日相关的知识,希望对你有一定的参考价值。
2019年12月16日16:57:04
5.接口
2019年11月01日15:56:09
5.1 duck typing
1.
2.
接口
3.介绍
Go 语言的接口设计是非侵入式的,接口编写者无须知道接口被哪些类型实现。
而接口实现者只需知道实现的是什么样子的接口,但无须指明实现哪一个接口。
编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。
每个接口类型由数个方法组成。接口的形式代码如下:
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
对各个部分的说明:
- 接口类型名:使用 type 将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加 er,如有写操作的接口叫 Writer,有字符串功能的接口叫 Stringer,有关闭功能的接口叫 Closer 等。
- 方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略,例如:
type writer interface{
Write([]byte) error
}
3.接口由使用者定义
4.
接口的值类型
5.type assertion 类型断言
判断r的真实类型,是不是mock.Retriever类型实现的。
r.(*mock.Retriever)
r.(type)
// Type assertion
if mockRetriever, ok := r.(*mock.Retriever); ok {
fmt.Println(mockRetriever.Contents)
} else {
fmt.Println("r is not a mock retriever")
}
fmt.Println(
"Try a session with mockRetriever")
fmt.Println(session(&mockRetriever))
}
6.interface里一般有类型和值
fmt.println("%T %v ",r,r)
func inspect(r Retriever) {
fmt.Println("Inspecting", r)
fmt.Printf(" > Type:%T Value:%v
", r, r)
fmt.Print(" > Type switch: ")
switch v := r.(type) {
case *mock.Retriever:
fmt.Println("Contents:", v.Contents)
case *real.Retriever:
fmt.Println("UserAgent:", v.UserAgent)
}
fmt.Println()
}
7.接口变量里有什么
8.接口变量自带指针
9.接口变量同样采用值传递,几乎不需要使用接口的指针
10.指针接受者实现只能以指针方式使用;值接受者都可以
var r Retriever
mockRetriever := mock.Retriever{
Contents: "this is a fake imooc.com"}
r = &mockRetriever
inspect(r)
r = &real.Retriever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
11.查看接口类型
interface{}
支持任何类型
type Queue []interface{}
func (q *Queue) Push(v interface{}) {
*q=append(*q,v)
}
Type Assertion
Type Switch
2019年11月03日
5.2 接口的组合
2019年11月03日13:33:08
1.新定义一个接口——为了组合
type Poster interface {
Post(url string,
form map[string]string) string
}
func post(poster Poster) {
poster.Post(url,
map[string]string{
"name": "ccmouse",
"course": "golang",
})
}
2.定义一个组合接口
type RetrieverPoster interface {
Retriever
Poster
}
3.mockRetriever接口实现了Post方法
//mockretriever.go
func (r *Retriever) Post(url string,
form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
//main.go
func session(s RetrieverPoster) string {
s.Post(url, map[string]string{
"contents": "another faked imooc.com",
})
return s.Get(url)
}
func main() {
var r Retriever
mockRetriever := mock.Retriever{
Contents: "this is a fake imooc.com"}
r = &mockRetriever
inspect(r)
r = &real.Retriever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
inspect(r)
fmt.Println(
"Try a session with mockRetriever")
fmt.Println(session(&mockRetriever))
}
6.函数式编程
2019年11月03日14:53:11
1.
#### 2.闭包
<img src="https://tva1.sinaimg.cn/large/006y8mN6ly1g8kwses02tj313u0u0h1k.jpg" alt="image-20191103150946598" style="zoom:25%;" />
#### 3.匿名函数
Go语言支持匿名函数,即在需要使用函数时再定义函数,匿名函数没有函数名只有函数体,函数可以作为一种类型被赋值给函数类型的变量,匿名函数也往往以变量方式传递,这与C语言的回调函数比较类似,不同的是,Go语言支持随时在代码里定义匿名函数。
**定义一个匿名函数**
匿名函数的定义格式如下:
func(参数列表)(返回参数列表){
函数体
}
```
匿名函数的定义就是没有名字的普通函数定义。
在定义时调用匿名函数
匿名函数可以在声明后调用,例如:
func(data int) {
fmt.Println(
"hello", data)
}(100)
注意第3行} (100)
将匿名函数赋值给变量
匿名函数可以被赋值,例如:
// 将匿名函数体保存到f()中
f := func(data int) {
fmt.Println("hello", data)
}
// 使用f()调用
f(100)
匿名函数的用途非常广泛,它本身就是一种值,可以方便地保存在各种容器中实现回调函数和操作封装。
4.闭包的记忆效应
被捕获到闭包中的变量让闭包本身拥有了记忆效应,闭包中的逻辑可以修改闭包捕获的变量,变量会跟随闭包生命期一直存在,闭包本身就如同变量一样拥有了记忆效应。
累加器的实现:
package main
import (
"fmt"
)
// 提供一个值, 每次调用函数会指定对值进行累加
func Accumulate(value int) func() int {
// 返回一个闭包
return func() int {
// 累加
value++
// 返回一个累加值
return value
}
}
func main() {
// 创建一个累加器, 初始值为1
accumulator := Accumulate(1)
// 累加1并打印
fmt.Println(accumulator())
fmt.Println(accumulator())
// 打印累加器的函数地址
fmt.Printf("%p
", accumulator)
// 创建一个累加器, 初始值为1
accumulator2 := Accumulate(10)
// 累加1并打印
fmt.Println(accumulator2())
// 打印累加器的函数地址
fmt.Printf("%p
", accumulator2)
}
代码说明如下:
- 第 8 行,累加器生成函数,这个函数输出一个初始值,调用时返回一个为初始值创建的闭包函数。
- 第 11 行,返回一个闭包函数,每次返回会创建一个新的函数实例。
- 第 14 行,对引用的 Accumulate 参数变量进行累加,注意 value 不是第 11 行匿名函数定义的,但是被这个匿名函数引用,所以形成闭包。
- 第 17 行,将修改后的值通过闭包的返回值返回。
- 第 24 行,创建一个累加器,初始值为 1,返回的 accumulator 是类型为 func()int 的函数变量。
- 第 27 行,调用 accumulator() 时,代码从 11 行开始执行匿名函数逻辑,直到第 17 行返回。
- 第 32 行,打印累加器的函数地址。
对比输出的日志发现 accumulator 与 accumulator2 输出的函数地址不同,因此它们是两个不同的闭包实例。
7.错误处理和资源管理
7.1 defer调用
2019年11月03日16:00:50
1.defer调用来实现资源管理
2.确保调用在函数结束时发生
3.参数在defer语句时计算
4.defer列表为后进先出
5.何时使用defer调用
Open/Close
Lock/unlock
PrintHeader/PrintFooter
6.源码
package main
import (
"fmt"
"os"
"u2pppw/src/functional/fib"
"bufio"
)
func tryDefer() {
for i := 0; i < 100; i++ {
defer fmt.Println(i)
if i == 30 {
// Uncomment panic to see
// how it works with defer
// panic("printed too many")
}
}
}
func writeFile(filename string) {
file, err := os.OpenFile(filename,
os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
if pathError, ok := err.(*os.PathError); !ok {
panic(err)
} else {
fmt.Printf("%s, %s, %s
",
pathError.Op,
pathError.Path,
pathError.Err)
}
return
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
f := fib.Fibonacci()
for i := 0; i < 20; i++ {
fmt.Fprintln(writer, f())
}
}
func main() {
tryDefer()
writeFile("fib.txt")
}
7.2 错误处理概念
2019年11月03日16:57:12
1.为了防止程序挂掉——错误处理
if err != nil {
if pathError, ok := err.(*os.PathError); !ok {
panic(err)
} else {
fmt.Printf("%s, %s, %s
",
pathError.Op,
pathError.Path,
pathError.Err)
}
return
}
服务器统一出错处理
1.如何实现统一的错误处理逻辑
2.起一个服务端读取内容
读取方法
const prefix = "/list/"
func HandleFileList(writer http.ResponseWriter,
request *http.Request) error {
fmt.Println()
if strings.Index(
request.URL.Path, prefix) != 0 {
return userError(
fmt.Sprintf("path %s must start "+
"with %s",
request.URL.Path, prefix))
}
path := request.URL.Path[len(prefix):]
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
处理错误的包装方法——使用函数式编程
type appHandler func(writer http.ResponseWriter,
request *http.Request) error
func errWrapper(
handler appHandler) func(
http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter,
request *http.Request) {
// panic
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)
if err != nil {
log.Printf("Error occurred "+
"handling request: %s",
err.Error())
// user error
if userErr, ok := err.(userError); ok {
http.Error(writer,
userErr.Message(),
http.StatusBadRequest)
return
}
// system error
code := http.StatusOK
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer,
http.StatusText(code), code)
}
}
}
main方法
func main() {
http.HandleFunc("/",
errWrapper(filelisting.HandleFileList))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
3.源码
main.go
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"os"
"u2pppw/src/errhandling/filelistingserver/filelisting"
)
type appHandler func(writer http.ResponseWriter,
request *http.Request) error
func errWrapper(
handler appHandler) func(
http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter,
request *http.Request) {
// panic
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)
if err != nil {
log.Printf("Error occurred "+
"handling request: %s",
err.Error())
// user error
if userErr, ok := err.(userError); ok {
http.Error(writer,
userErr.Message(),
http.StatusBadRequest)
return
}
// system error
code := http.StatusOK
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer,
http.StatusText(code), code)
}
}
}
type userError interface {
error
Message() string
}
func main() {
http.HandleFunc("/",
errWrapper(filelisting.HandleFileList))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
handle.go
package filelisting
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
)
const prefix = "/list/"
type userError string
func (e userError) Error() string {
return e.Message()
}
func (e userError) Message() string {
return string(e)
}
func HandleFileList(writer http.ResponseWriter,
request *http.Request) error {
fmt.Println()
if strings.Index(
request.URL.Path, prefix) != 0 {
return userError(
fmt.Sprintf("path %s must start "+
"with %s",
request.URL.Path, prefix))
}
path := request.URL.Path[len(prefix):]
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
7.3panic和recover
2019年11月04日14:00:22
1. panic
1.停止当前函数执行
2.一直向上返回,执行每一层的defer
3.如果没有遇见recover,程序退出
2. recover
1.仅在defer调用中使用
defer func() {
err:=recover()
}()
2.获取panic的值
如果无法处理,就可以重新panic
package main
import (
"fmt"
)
func tryRecover() {
defer func() {
r := recover()
if r == nil {
fmt.Println("Nothing to recover. " +
"Please try uncomment errors " +
"below.")
return
}
if err, ok := r.(error); ok {
fmt.Println("Error occurred:", err)
} else {
panic(fmt.Sprintf(
"I don't know what to do: %v", r))
}
}()
// Uncomment each block to see different panic
// scenarios.
// Normal error
//panic(errors.New("this is an error"))
// Division by zero
//b := 0
//a := 5 / b
//fmt.Println(a)
// Causes re-panic
panic(123)
}
func main() {
tryRecover()
}
8.goroutine
2019年11月04日14:49:18
1.并发编程
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 1000; i++ {
go func(i int) {
for {
fmt.Printf("Hello from "+
"goroutine %d
", i)
}
}(i)
}
time.Sleep(time.Minute)
}
2.协程 Coroutine
轻量级线程
非抢占式多任务处理,由协程主动交出控制权
多个协程可能在一个或多个线程上运行
func main() {
var a [10]int
for i:=0;i<10;i++{
go func(i int){
for{
//fmt.Println("hello %d
",i)
a[i]++
runtime.Gosched()
}
}(i)
}
time.Sleep(time.Millisecond)
fmt.Println(a)
}
3.任何函数只需加上go就能送给调度器运行
4.不需要在定义时区分时都是异步函数
5.调度器在合适的点进行切换
6.使用-race来检测数据访问冲突
7.goroutine可能的切换点
I/O,select
channel
等待锁
函数调用
runtime,Gosched()
9.channel
2019年11月04日15:30:28
1.介绍
Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。
通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。
2.声明通道类型
通道本身需要一个类型进行修饰,就像切片类型需要标识元素类型。通道的元素类型就是在其内部传输的数据类型,声明如下:
var 通道变量 chan 通道类型
- 通道类型:通道内的数据类型。
- 通道变量:保存通道的变量。
chan 类型的空值是 nil,声明后需要配合 make 后才能使用。
3.创建通道
通道是引用类型,需要使用 make 进行创建,格式如下:
通道实例 := make(chan 数据类型)
- 数据类型:通道内传输的元素类型。
- 通道实例:通过make创建的通道句柄。
请看下面的例子:
ch1 := make(chan int) // 创建一个整型类型的通道
ch2 := make(chan interface{}) // 创建一个空接口类型的通道, 可以存放任意格式
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip) // 创建Equip指针类型的通道, 可以存放*Equip
4.发送数据
// 创建一个空接口通道
ch := make(chan interface{})
// 将0放入通道中
ch <- 0
// 将hello字符串放入通道中
ch <- "hello"
10.beego介绍及搭建
2019年12月06日13:28:50
1.介绍
- Beego是一个能够快速开发Go应用程序的HTTP框架, 它可以用来迅速的开发API, 网络APP(网站)和后端服务
- Beego是一个MVC的框架
beego有八个模块,分别是
- cache
- config:
- context:
- httplibs: curl函数
- logs:
- orm
- session
- toolbox
2.优势
beego是一个类似tornado的Go应用框架,采用了RESTFul的方式来实现应用框架,是一个超轻量级的框架,主要有如下的特点:
- 支持MVC的方式,用户只需要关注逻辑,实现对应method的方法即可
- 支持websocket,通过自定义Handler实现集成sockjs等方式实现
- 支持自定义路由,支持各种方式的路由,正则、语意均支持,类似sinatra
- session集成,支持memory、file、redis、mysql等存储
- 表单处理自动化解析,用户可以很方便的获取数据
- 日志分级系统,用户可以很方便的调试和应用日志记录
- 自定义配置文件,支持ini格式的文本配置,可以方便的在系统中调参数
- 采用了Go内置的模板,集成实现了很多Web开发中常用的函数
3.环境搭建
beego 的安装是典型的 Go 安装包的形式:
go get github.com/astaxie/beego
Go 升级,通过该方式用户可以升级 beego 框架,强烈推荐该方式:
go get -u github.com/astaxie/beego
源码下载升级,用户访问 https://github.com/astaxie/beego
,下载源码,然后覆盖到 $GOPATH/src/github.com/astaxie/beego
目录,然后通过本地执行安装就可以升级了:
go install github.com/astaxie/beego
bee 工具简介
bee 工具是一个为了协助快速开发 beego 项目而创建的项目,通过 bee 您可以很容易的进行 beego 项目的创建、热编译、开发、测试、和部署。
bee 工具的安装
您可以通过如下的方式安装 bee 工具:
go get github.com/beego/bee
安装完之后,bee
可执行文件默认存放在 $GOPATH/bin
里面,所以您需要把 $GOPATH/bin
添加到您的环境变量中,才可以进行下一步。
new
命令
new
命令是新建一个 Web 项目,我们在命令行下执行 bee new <项目名>
就可以创建一个新的项目。但是注意该命令必须在 $GOPATH/src
下执行。最后会在 $GOPATH/src
相应目录下生成如下目录结构的项目:
bee new myproject
[INFO] Creating application...
/gopath/src/myproject/
/gopath/src/myproject/conf/
/gopath/src/myproject/controllers/
/gopath/src/myproject/models/
/gopath/src/myproject/static/
/gopath/src/myproject/static/js/
/gopath/src/myproject/static/css/
/gopath/src/myproject/static/img/
/gopath/src/myproject/views/
/gopath/src/myproject/conf/app.conf
/gopath/src/myproject/controllers/default.go
/gopath/src/myproject/views/index.tpl
/gopath/src/myproject/main.go
13-11-25 09:50:39 [SUCC] New application successfully created!
myproject
├── conf
│ └── app.conf
├── controllers
│ └── default.go
├── main.go
├── models
├── routers
│ └── router.go
├── static
│ ├── css
│ ├── img
│ └── js
├── tests
│ └── default_test.go
└── views
└── index.tpl
8 directories, 4 files
api
命令
上面的 new
命令是用来新建 Web 项目,不过很多用户使用 beego 来开发 API 应用。所以这个 api
命令就是用来创建 API 应用的,执行命令之后如下所示:
bee api apiproject
create app folder: /gopath/src/apiproject
create conf: /gopath/src/apiproject/conf
create controllers: /gopath/src/apiproject/controllers
create models: /gopath/src/apiproject/models
create tests: /gopath/src/apiproject/tests
create conf app.conf: /gopath/src/apiproject/conf/app.conf
create controllers default.go: /gopath/src/apiproject/controllers/default.go
create tests default.go: /gopath/src/apiproject/tests/default_test.go
create models object.go: /gopath/src/apiproject/models/object.go
create main.go: /gopath/src/apiproject/main.go
这个项目的目录结构如下:
apiproject
├── conf
│ └── app.conf
├── controllers
│ └── object.go
│ └── user.go
├── docs
│ └── doc.go
├── main.go
├── models
│ └── object.go
│ └── user.go
├── routers
│ └── router.go
└── tests
└── default_test.go
从上面的目录我们可以看到和 Web 项目相比,少了 static 和 views 目录,多了一个 test 模块,用来做单元测试的。
同时,该命令还支持一些自定义参数自动连接数据库创建相关 model 和 controller:
bee api [appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
如果 conn 参数为空则创建一个示例项目,否则将基于链接信息链接数据库创建项目。
run
命令
我们在开发 Go 项目的时候最大的问题是经常需要自己手动去编译再运行,bee run
命令是监控 beego 的项目,通过 fsnotify监控文件系统。但是注意该命令必须在 $GOPATH/src/appname
下执行。
这样我们在开发过程中就可以实时的看到项目修改之后的效果:
bee run
13-11-25 09:53:04 [INFO] Uses 'myproject' as 'appname'
13-11-25 09:53:04 [INFO] Initializing watcher...
13-11-25 09:53:04 [TRAC] Directory(/gopath/src/myproject/controllers)
13-11-25 09:53:04 [TRAC] Directory(/gopath/src/myproject/models)
13-11-25 09:53:04 [TRAC] Directory(/gopath/src/myproject)
13-11-25 09:53:04 [INFO] Start building...
13-11-25 09:53:16 [SUCC] Build was successful
13-11-25 09:53:16 [INFO] Restarting myproject ...
13-11-25 09:53:16 [INFO] ./myproject is running...
我们打开浏览器就可以看到效果 http://localhost:8080/
以上是关于19.go语言基础学习(上)——2019年12月16日的主要内容,如果未能解决你的问题,请参考以下文章