百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

Go 语法速览与实践清单(V0.5)

bigegpt 2024-08-24 23:21 2 浏览

Go 语法速览与实践清单(V0.5)

Go CheatSheet 是对于 Go 学习/实践过程中的语法与技巧进行盘点,其属于 Awesome CheatSheet 系列,致力于提升学习速度与研发效能,即可以将其当做速查手册,也可以作为轻量级的入门学习资料。 本文参考了许多优秀的文章与代码示范,统一声明在了 Go Links;如果希望深入了解某方面的内容,可以继续阅读 Go 开发:语法基础与工程实践,或者前往 coding-snippets/go 查看使用 Go 解决常见的数据结构与算法、设计模式、业务功能方面的代码实现。

环境配置与语法基础

可以前往这里下载 Go SDK 安装包,或者使用 brew 等包管理器安装。go 命令依赖于 $GOPATH 环境变量进行代码组织,多项目情况下也可以使用 ln 进行目录映射以方便进行项目管理。GOPATH 允许设置多个目录,每个目录都会包含三个子目录:src 用于存放源代码,pkg 用于存放编译后生成的文件,bin 用于存放编译后生成的可执行文件。

环境配置完毕后,可以使用 go get 获取依赖,go run 运行程序,go build 来编译项目生成与包名(文件夹名)一致的可执行文件。Golang 1.8 之后支持 dep 依赖管理工具,对于空的项目使用 dep init 初始化依赖配置,其会生成 Gopkg.toml Gopkg.lock vendor/ 这三个文件(夹)。

我们可以使用 dep ensure -add github.com/pkg/errors 添加依赖,运行之后,其会在 toml 文件中添加如下锁:

[[constraint]] name = "github.com/pkg/errors" version = "0.8.0"

简单的 Go 中 Hello World 代码如下:

package main import "fmt" func main() { fmt.Println("hello world") }

也可以使用 Beego 实现简单的 HTTP 服务器:

package main import "github.com/astaxie/beego" func main() { beego.Run() }

Go 并没有相对路径引入,而是以文件夹为单位定义模块,譬如我们新建名为 math 的文件夹,然后使用 package math 来声明该文件中函数所属的模块。

import ( mongo "mywebapp/libs/mongodb/db" // 对引入的模块重命名 _ "mywebapp/libs/mysql/db" // 使用空白下划线表示仅调用其初始化函数 )

外部引用该模块是需要使用工作区间或者 vendor 相对目录,其目录索引情况如下:

cannot find package "sub/math" in any of: ${PROJECTROOT}/vendor/sub/math (vendor tree) /usr/local/Cellar/go/1.10/libexec/src/sub/math (from $GOROOT) ${GOPATH}/src/sub/math (from $GOPATH)

Go 规定每个源文件的首部需要进行包声明,可执行文件默认放在 main 包中;而各个包中默认首字母大写的函数作为其他包可见的导出函数,而小写函数则默认外部不可见的私有函数。

表达式与控制流

变量声明与赋值

作为强类型静态语言,Go 允许我们在变量之后标识数据类型,也为我们提供了自动类型推导的功能。

// 声明三个变量,皆为 bool 类型 var c, python, java bool // 声明不同类型的变量,并且赋值 var i bool, j int = true, 2 // 复杂变量声明 var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) ) // 短声明变量 c, python, java := true, false, "no!" // 声明常量 const constant = "This is a constant"

在 Go 中,如果我们需要比较两个复杂对象的相似性,可以使用 reflect.DeepEqual 方法:

m1 := map[string]int{ "a":1, "b":2, } m2 := map[string]int{ "a":1, "b":2, } fmt.Println(reflect.DeepEqual(m1, m2))

条件判断

Go 提供了增强型的 if 语句进行条件判断:

// 基础形式 if x > 0 { return x } else { return -x } // 条件判断之前添加自定义语句 if a := b + c; a < 42 { return a } else { return a - 42 } // 常用的类型判断 var val interface{} val = "foo" if str, ok := val.(string); ok { fmt.Println(str) }

Go 也支持使用 Switch 语句:

// 基础格式 switch operatingSystem { case "darwin": fmt.Println("Mac OS Hipster") // 默认 break,不需要显式声明 case "linux": fmt.Println("Linux Geek") default: // Windows, BSD, ... fmt.Println("Other") } // 类似于 if,可以在条件之前添加自定义语句 switch os := runtime.GOOS; os { case "darwin": ... } // 使用 switch 语句进行类型判断: switch v := anything.(type) { case string: fmt.Println(v) case int32, int64: ... default: fmt.Println("unknown") }

Switch 中也支持进行比较:

number := 42 switch { case number < 42: fmt.Println("Smaller") case number == 42: fmt.Println("Equal") case number > 42: fmt.Println("Greater") }

或者进行多条件匹配:

var char byte = '?' switch char { case ' ', '?', '&', '=', '#', '+', '%': fmt.Println("Should escape") }

循环

Go 支持使用 for 语句进行循环,不存在 while 或者 until:

for i := 1; i < 10; i++ { } // while - loop for ; i < 10; { } // 单条件情况下可以忽略分号 for i < 10 { } // ~ while (true) for { }

我们也可以使用 range 函数,对于 Arrays 与 Slices 进行遍历:

// loop over an array/a slice for i, e := range a { // i 表示下标,e 表示元素 } // 仅需要元素 for _, e := range a { // e is the element } // 或者仅需要下标 for i := range a { } // 定时执行 for range time.Tick(time.Second) { // do it once a sec }

Function: 函数

定义,参数与返回值

// 简单函数定义 func functionName() {} // 含参函数定义 func functionName(param1 string, param2 int) {} // 多个相同类型参数的函数定义 func functionName(param1, param2 int) {} // 函数表达式定义 add := func(a, b int) int { return a + b }

Go 支持函数的最后一个参数使用 ... 设置为不定参数,即可以传入一个或多个参数值:

func adder(args ...int) int { total := 0 for _, v := range args { // Iterates over the arguments whatever the number. total += v } return total } adder(1, 2, 3) // 6 adder(9, 9) // 18 nums := []int{10, 20, 30} adder(nums...) // 60

我们也可以使用 Function Stub 作为函数参数传入,以实现回调函数的功能:

func Filter(s []int, fn func(int) bool) []int { var p []int // == nil for _, v := range s { if fn(v) { p = append(p, v) } } return p }

虽然 Go 不是函数式语言,但是也可以用其实现柯里函数(Currying Function):

func add(x, y int) int { return x+ y } func adder(x int) (func(int) int) { return func(y int) int { return add(x, y) } } func main() { add3 := adder(3) fmt.Println(add3(4)) // 7 }

Go 支持多个返回值:

// 返回单个值 func functionName() int { return 42 } // 返回多个值 func returnMulti() (int, string) { return 42, "foobar" } var x, str = returnMulti() // 命名返回多个值 func returnMulti2() (n int, s string) { n = 42 s = "foobar" // n and s will be returned return } var x, str = returnMulti2()

闭包: Closure

Go 同样支持词法作用域与变量保留,因此我们可以使用闭包来访问函数定义处外层的变量:

func scope() func() int{ outer_var := 2 foo := func() int { return outer_var} return foo }

闭包中并不能够直接修改外层变量,而是会自动重定义新的变量值:

func outer() (func() int, int) { outer_var := 2 inner := func() int { outer_var += 99 return outer_var // => 101 (but outer_var is a newly redefined } return inner, outer_var // => 101, 2 (outer_var is still 2, not mutated by inner!) }

函数执行

Go 中提供了 defer 关键字,允许将某个语句的执行推迟到函数返回语句之前:

func read(...) (...) { f, err := os.Open(file) ... defer f.Close() ... return .. // f will be closed

异常处理

Go 语言中并不存在 try-catch 等异常处理的关键字,对于那些可能返回异常的函数,只需要在函数返回值中添加额外的 Error 类型的返回值:

type error interface { Error() string }

某个可能返回异常的函数调用方式如下:

import ( "fmt" "errors" ) func main() { result, err:= Divide(2,0) if err != nil { fmt.Println(err) }else { fmt.Println(result) } } func Divide(value1 int,value2 int)(int, error) { if(value2 == 0){ return 0, errors.New("value2 mustn't be zero") } return value1/value2 , nil }

Go 还为我们提供了 panic 函数,所谓 panic,即是未获得预期结果,常用于抛出异常结果。譬如当我们获得了某个函数返回的异常,却不知道如何处理或者不需要处理时,可以直接通过 panic 函数中断当前运行,打印出错误信息、Goroutine 追踪信息,并且返回非零的状态码:

_, err := os.Create("/tmp/file") if err != nil { panic(err) }

数据类型与结构

类型绑定与初始化

Go 中的 type 关键字能够对某个类型进行重命名:

// IntSlice 并不等价于 []int,但是可以利用类型转换进行转换 type IntSlice []int a := IntSlice{1, 2}

可以使用 T(v) 或者 obj.(T) 进行类型转换,obj.(T) 仅针对 interface{} 类型起作用:

t := obj.(T) // if obj is not T, error t, ok := obj.(T) // if obj is not T, ok = false // 类型转换与判断 str, ok := val.(string);

基本数据类型

interface {} // ~ java Object bool // true/false string int8 int16 int32 int64 int // =int32 on 32-bit, =int64 if 64-bit OS uint8 uint16 uint32 uint64 uintptr uint byte // alias for uint8 rune // alias for int32, represents a Unicode code point float32 float64

字符串

// 多行字符串声明 hellomsg := ` "Hello" in Chinese is 你好 ('Ni Hao') "Hello" in Hindi is ?????? ('Namaste') `

格式化字符串:

fmt.Println("Hello, 你好, ??????, Привет, ???") // basic print, plus newline p := struct { X, Y int }{ 17, 2 } fmt.Println( "My point:", p, "x coord=", p.X ) // print structs, ints, etc s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // print to string variable fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e",17,17,17,17.0,17.0) // c-ish format s2 := fmt.Sprintf( "%d %f", 17, 17.0 ) // formatted print to string variable

序列类型

Array 与 Slice 都可以用来表示序列数据,二者也有着一定的关联。

Array

其中 Array 用于表示固定长度的,相同类型的序列对象,可以使用如下形式创建:

[N]Type [N]Type{value1, value2, ..., valueN} // 由编译器自动计算数目 [...]Type{value1, value2, ..., valueN}

其具体使用方式为:

// 数组声明 var a [10]int // 赋值 a[3] = 42 // 读取 i := a[3] // 声明与初始化 var a = [2]int{1, 2} a := [2]int{1, 2} a := [...]int{1, 2}

Go 内置了 len 与 cap 函数,用于获取数组的尺寸与容量:

var arr = [3]int{1, 2, 3} arr := [...]int{1, 2, 3} len(arr) // 3 cap(arr) // 3

不同于 C/C++ 中的指针(Pointer)或者 Java 中的对象引用(Object Reference),Go 中的 Array 只是值(Value)。这也就意味着,当进行数组拷贝,或者函数调用中的参数传值时,会复制所有的元素副本,而非仅仅传递指针或者引用。显而易见,这种复制的代价会较为昂贵。

Slice

Slice 为我们提供了更为灵活且轻量级地序列类型操作,可以使用如下方式创建 Slice:

// 使用内置函数创建 make([]Type, length, capacity) make([]Type, length) // 声明为不定长度数组 []Type{} []Type{value1, value2, ..., valueN} // 对现有数组进行切片转换 array[:] array[:2] array[2:] array[2:3]

不同于 Array,Slice 可以看做更为灵活的引用类型(Reference Type),它并不真实地存放数组值,而是包含数组指针(ptr),len,cap 三个属性的结构体。换言之,Slice 可以看做对于数组中某个段的描述,包含了指向数组的指针,段长度,以及段的最大潜在长度,其结构如下图所示:

// 创建 len 为 5,cap 为 5 的 Slice s := make([]byte, 5) // 对 Slice 进行二次切片,此时 len 为 2,cap 为 3 s = s[2:4] // 恢复 Slice 的长度 s = s[:cap(s)]

需要注意的是, 切片操作并不会真实地复制 Slice 中值,只是会创建新的指向原数组的指针,这就保证了切片操作和操作数组下标有着相同的高效率。不过如果我们修改 Slice 中的值,那么其会 真实修改底层数组中的值,也就会体现到原有的数组中:

d := []byte{'r', 'o', 'a', 'd'} e := d[2:] // e == []byte{'a', 'd'} e[1] = 'm' // e == []byte{'a', 'm'} // d == []byte{'r', 'o', 'a', 'm'}

Go 提供了内置的 append 函数,来动态为 Slice 添加数据,该函数会返回新的切片对象,包含了原始的 Slice 中值以及新增的值。如果原有的 Slice 的容量不足以存放新增的序列,那么会自动分配新的内存:

// len=0 cap=0 [] var s []int // len=1 cap=2 [0] s = append(s, 0) // len=2 cap=2 [0 1] s = append(s, 1) // len=5 cap=8 [0 1 2 3 4] s = append(s, 2, 3, 4) // 使用 ... 来自动展开数组 a := []string{"John", "Paul"} b := []string{"George", "Ringo", "Pete"} a = append(a, b...) // equivalent to "append(a, b[0], b[1], b[2])" // a == []string{"John", "Paul", "George", "Ringo", "Pete"}

我们也可以使用内置的 copy 函数,进行 Slice 的复制,该函数支持对于不同长度的 Slice 进行复制,其会自动使用最小的元素数目。同时,copy 函数还能够自动处理使用了相同的底层数组之间的 Slice 复制,以避免额外的空间浪费。

func copy(dst, src []T) int // 申请较大的空间容量 t := make([]byte, len(s), (cap(s)+1)*2) copy(t, s) s = t

映射类型

var m map[string]int m = make(map[string]int) m["key"] = 42 // 删除某个键 delete(m, "key") // 测试该键对应的值是否存在 elem, has_value := m["key"] // map literal var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, }

Struct & Interface: 结构体与接口

Struct: 结构体

Go 语言中并不存在类的概念,只有结构体,结构体可以看做属性的集合,同时可以为其定义方法。

// 声明结构体 type Vertex struct { // 结构体的属性,同样遵循大写导出,小写私有的原则 X, Y int z bool } // 也可以声明隐式结构体 point := struct { X, Y int }{1, 2} // 创建结构体实例 var v = Vertex{1, 2} // 读取或者设置属性 v.X = 4; // 显示声明键 var v = Vertex{X: 1, Y: 2} // 声明数组 var v = []Vertex{{1,2},{5,2},{5,5}}

方法的声明也非常简洁,只需要在 func 关键字与函数名之间声明结构体指针即可,该结构体会在不同的方法间进行复制:

func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } // Call method v.Abs()

对于那些需要修改当前结构体对象的方法,则需要传入指针:

func (v *Vertex) add(n float64) { v.X += n v.Y += n }

var p *Person = new(Person) // pointer of type Person

Pointer: 指针

// p 是 Vertex 类型 p := Vertex{1, 2} // q 是指向 Vertex 的指针 q := &p // r 同样是指向 Vertex 对象的指针 r := &Vertex{1, 2} // 指向 Vertex 结构体对象的指针类型为 *Vertex var s *Vertex = new(Vertex)

Interface: 接口

Go 允许我们通过定义接口的方式来实现多态性:

// 接口声明 type Awesomizer interface { Awesomize() string } // 结构体并不需要显式实现接口 type Foo struct {} // 而是通过实现所有接口规定的方法的方式,来实现接口 func (foo Foo) Awesomize() string { return "Awesome!" }

type Shape interface { area() float64 } func getArea(shape Shape) float64 { return shape.area() } type Circle struct { x,y,radius float64 } type Rectangle struct { width, height float64 } func(circle Circle) area() float64 { return math.Pi * circle.radius * circle.radius } func(rect Rectangle) area() float64 { return rect.width * rect.height } func main() { circle := Circle{x:0,y:0,radius:5} rectangle := Rectangle {width:10, height:5} fmt.Printf("Circle area: %f\n",getArea(circle)) fmt.Printf("Rectangle area: %f\n",getArea(rectangle)) } //Circle area: 78.539816 //Rectangle area: 50.000000

惯用的思路是先定义接口,再定义实现,最后定义使用的方法:

package animals type Animal interface { Speaks() string } // implementation of Animal type Dog struct{} func (a Dog) Speaks() string { return "woof" } /** 在需要的地方直接引用 **/ package circus import "animals" func Perform(a animal.Animal) { return a.Speaks() }

Go 也为我们提供了另一种接口的实现方案,我们可以不在具体的实现处定义接口,而是在需要用到该接口的地方,该模式为:

func funcName(a INTERFACETYPE) CONCRETETYPE

定义接口:

package animals type Dog struct{} func (a Dog) Speaks() string { return "woof" } /** 在需要使用实现的地方定义接口 **/ package circus type Speaker interface { Speaks() string } func Perform(a Speaker) { return a.Speaks() }

Embedding

Go 语言中并没有子类继承这样的概念,而是通过嵌入(Embedding)的方式来实现类或者接口的组合。

// ReadWriter 的实现需要同时满足 Reader 与 Writer type ReadWriter interface { Reader Writer } // Server 暴露了所有 Logger 结构体的方法 type Server struct { Host string Port int *log.Logger } // 初始化方式并未受影响 server := &Server{"localhost", 80, log.New(...)} // 却可以直接调用内嵌结构体的方法,等价于 server.Logger.Log(...) server.Log(...) // 内嵌结构体的名词即是类型名 var logger *log.Logger = server.Logger

并发编程

Goroutines

Goroutines 是轻量级的线程,可以参考并发编程导论一文中的进程、线程与协程的讨论;Go 为我们提供了非常便捷的 Goroutines 语法:

// 普通函数 func doStuff(s string) { } func main() { // 使用命名函数创建 Goroutine go doStuff("foobar") // 使用匿名内部函数创建 Goroutine go func (x int) { // function body goes here }(42) }

Channels

信道(Channel)是带有类型的管道,可以用于在不同的 Goroutine 之间传递消息,其基础操作如下:

// 创建类型为 int 的信道 ch := make(chan int) // 向信道中发送值 ch <- 42 // 从信道中获取值 v := <-ch // 读取,并且判断其是否关闭 v, ok := <-ch // 读取信道,直至其关闭 for i := range ch { fmt.Println(i) }

譬如我们可以在主线程中等待来自 Goroutine 的消息,并且输出:

// 创建信道 messages := make(chan string) // 执行 Goroutine go func() { messages <- "ping" }() // 阻塞,并且等待消息 msg := <-messages // 使用信道进行并发地计算,并且阻塞等待结果 c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // 从 c 中接收

如上创建的是无缓冲型信道(Non-buffered Channels),其是阻塞型信道;当没有值时读取方会持续阻塞,而写入方则是在无读取时阻塞。我们可以创建缓冲型信道(Buffered Channel),其读取方在信道被写满前都不会被阻塞:

ch := make(chan int, 100) // 发送方也可以主动关闭信道 close(ch)

Channel 同样可以作为函数参数,并且我们可以显式声明其是用于发送信息还是接收信息,从而增加程序的类型安全度:

// ping 函数用于发送信息 func ping(pings chan<- string, msg string) { pings <- msg } // pong 函数用于从某个信道中接收信息,然后发送到另一个信道中 func pong(pings <-chan string, pongs chan<- string) { msg := <-pings pongs <- msg } func main() { pings := make(chan string, 1) pongs := make(chan string, 1) ping(pings, "passed message") pong(pings, pongs) fmt.Println(<-pongs) }

同步

同步,是并发编程中的常见需求,这里我们可以使用 Channel 的阻塞特性来实现 Goroutine 之间的同步:

func worker(done chan bool) { time.Sleep(time.Second) done <- true } func main() { done := make(chan bool, 1) go worker(done) // 阻塞直到接收到消息 <-done }

Go 还为我们提供了 select 关键字,用于等待多个信道的执行结果:

// 创建两个信道 c1 := make(chan string) c2 := make(chan string) // 每个信道会以不同时延输出不同值 go func() { time.Sleep(1 * time.Second) c1 <- "one" }() go func() { time.Sleep(2 * time.Second) c2 <- "two" }() // 使用 select 来同时等待两个信道的执行结果 for i := 0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("received", msg1) case msg2 := <-c2: fmt.Println("received", msg2) } }

Web 编程

HTTP Server

package main import ( "fmt" "net/http" ) // define a type for the response type Hello struct{} // let that type implement the ServeHTTP method (defined in interface http.Handler) func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello!") } func main() { var h Hello http.ListenAndServe("localhost:4000", h) } // Here's the method signature of http.ServeHTTP: // type Handler interface { // ServeHTTP(w http.ResponseWriter, r *http.Request) // }

Beego

利用 Beego 官方推荐的 bee 命令行工具,我们可以快速创建 Beego 项目,其目录组织方式如下:

quickstart ├── conf │ └── app.conf ├── controllers │ └── default.go ├── main.go ├── models ├── routers │ └── router.go ├── static │ ├── css │ ├── img │ └── js ├── tests │ └── default_test.go └── views └── index.tpl

在 main.go 文件中,我们可以启动 Beego 实例,并且调用路由的初始化配置文件:

package main import ( _ "quickstart/routers" "github.com/astaxie/beego" ) func main() { beego.Run() }

而在路由的初始化函数中,我们会声明各个路由与控制器之间的映射关系:

package routers import ( "quickstart/controllers" "github.com/astaxie/beego" ) func init() { beego.Router("/", &controllers.MainController{}) }

也可以手动指定 Beego 项目中的静态资源映射:

beego.SetStaticPath("/down1", "download1") beego.SetStaticPath("/down2", "download2")

在具体的控制器中,可以设置返回数据,或者关联的模板名:

package controllers import ( "github.com/astaxie/beego" ) type MainController struct { beego.Controller } func (this *MainController) Get() { this.Data["Website"] = "beego.me" this.Data["Email"] = "astaxie@gmail.com" this.TplNames = "index.tpl" // version 1.6 use this.TplName = "index.tpl" }

DevPractics: 开发实践

文件读写

import ( "io/ioutil" ) ... datFile1, errFile1 := ioutil.ReadFile("file1") if errFile1 != nil { panic(errFile1) } ...

测试

VSCode 可以为函数自动生成基础测试用例,并且提供了方便的用例执行与调试的功能。

  • /** 交换函数 */ func swap(x *int, y *int) { x, y = y, x } /** 自动生成的测试函数 */ func Test_swap(t *testing.T) { type args struct { x *int y *int } tests := []struct { name string args args }{ // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { swap(tt.args.x, tt.args.y) }) } }

相关推荐

AI「自我复制」能力曝光,RepliBench警示:大模型正在学会伪造身份

科幻中AI自我复制失控场景,正成为现实世界严肃的研究课题。英国AISI推出RepliBench基准,分解并评估AI自主复制所需的四大核心能力。测试显示,当前AI尚不具备完全自主复制能力,但在获取资源...

【Python第三方库安装】介绍8种情况,这里最全看这里就够了!

**本图文作品主要解决CMD或pycharm终端下载安装第三方库可能出错的问题**本作品介绍了8种安装方法,这里最全的python第三方库安装教程,简单易上手,满满干货!希望大家能愉快地写代码,而不要...

pyvips,一个神奇的 Python 库!(pythonvip视频)

大家好,今天为大家分享一个神奇的Python库-pyvips。在图像处理领域,高效和快速的图像处理工具对于开发者来说至关重要。pyvips是一个强大的Python库,基于libvips...

mac 安装tesseract、pytesseract以及简单使用

一.tesseract-OCR的介绍1.tesseract-OCR是一个开源的OCR引擎,能识别100多种语言,专门用于对图片文字进行识别,并获取文本。但是它的缺点是对手写的识别能力比较差。2.用te...

实测o3/o4-mini:3分钟解决欧拉问题,OpenAI最强模型名副其实!

号称“OpenAI迄今为止最强模型”,o3/o4-mini真实能力究竟如何?就在发布后的几小时内,网友们的第一波实测已新鲜出炉。最强推理模型o3,即使遇上首位全职提示词工程师RileyGoodsid...

使用Python将图片转换为字符画并保存到文件

字符画(ASCIIArt)是将图片转换为由字符组成的艺术作品。利用Python,我们可以轻松实现图片转字符画的功能。本教程将带你一步步实现这个功能,并详细解释每一步的代码和实现原理。环境准备首先,你...

5分钟-python包管理器pip安装(python pip安装包)

pip是一个现代的,通用、普遍的Python包管理工具。提供了对Python包的查找、下载、安装、卸载的功能,是Python开发的基础。第一步:PC端打开网址:选择gz后缀的文件下载第二步:...

网络问题快速排查,你也能当好自己家的网络攻城狮

前面写了一篇关于网络基础和常见故障排查的,只列举了工具。没具体排查方式。这篇重点把几个常用工具的组合讲解一下。先有请今天的主角:nslookup及dig,traceroute,httping,teln...

终于把TCP/IP 协议讲的明明白白了,再也不怕被问三次握手了

文:涤生_Woo下周就开始和大家成体系的讲hadoop了,里面的每一个模块的技术细节我都会涉及到,希望大家会喜欢。当然了你也可以评论或者留言自己喜欢的技术,还是那句话,希望咱们一起进步。今天周五,讲讲...

记一次工控触摸屏故障的处理(工控触摸屏维修)

先说明一下,虽然我是自动化专业毕业,但已经很多年不从事现场一线的工控工作了。但自己在单位做的工作也牵涉到信息化与自动化的整合,所以平时也略有关注。上一周一个朋友接到一个活,一家光伏企业用于启动机组的触...

19、90秒快速“读懂”路由、交换命令行基础

命令行视图VRP分层的命令结构定义了很多命令行视图,每条命令只能在特定的视图中执行。本例介绍了常见的命令行视图。每个命令都注册在一个或多个命令视图下,用户只有先进入这个命令所在的视图,才能运行相应的命...

摄像头没图像的几个检查方法(摄像头没图像怎么修复)

背景描述:安防监控项目上,用户的摄像头运行了一段时间有部分摄像头不能进行预览,需要针对不能预览的摄像头进行排查,下面列出几个常见的排查方法。问题解决:一般情况为网络、供电、设备配置等情况。一,网络检查...

小谈:必需脂肪酸(必需脂肪酸主要包括)

必需脂肪酸是指机体生命活动必不可少,但机体自身又不能合成,必需由食物供给的多不饱和脂肪酸(PUFA)。必需脂肪酸主要包括两种,一种是ω-3系列的α-亚麻酸(18:3),一种是ω-6系列的亚油酸(18:...

期刊推荐:15本sci四区易发表的机械类期刊

  虽然,Sci四区期刊相比收录在sci一区、二区、三区的期刊来说要求不是那么高,投稿起来也相对容易一些。但,sci四区所收录的期刊中每本期刊的投稿难易程度也是不一样的。为方便大家投稿,本文给大家推荐...

be sick of 用法考察(be in lack of的用法)

besick表示病了,做谓语.本身是形容词,有多种意思.最通常的是:生病,恶心,呕吐,不适,晕,厌烦,无法忍受asickchild生病的孩子Hermother'sverysi...