if __name__ == '__main__':
ss = 'terry0609_08\x04\x04\x04\x04'
print(type(ss))
print(ss[-1])
print(ss[-2])
python3中bytes可以是任何二进制数据,文本/图片/视频/音频等等。str就是文本数据。
python2中把b'abcd'视为str,对str的切片是把\x04当作1位,数字8被视为unicode即char,输出的就是'8'。
python3中对bytes的切片是把\x04当作1位,数字8被视为utf-8的int,输出的都是数值56。
出错的代码
def unpadding(self, s):
return s[0:-ord(s[-1])]
出错代码原因分析:
没有注明类型,不能进行类型的判读,代码的写法比较trick。
python3中bytes和str的转换
b = b"example" # bytes object
s = "example" # str object
s2b = bytes(s, encoding = "utf8") # str to bytes
s2b = str.encode(s) # str to bytes
s2b = s.encode("utf8") # str to bytes
b2s = str(b, encoding = "utf8") # bytes to str
b2s = bytes.decode(b) # bytes to str
b2s = b.decode("utf8") # bytes to str
python3.5 以后的新特性
typing模块的作用:
类型检查,防止运行时传入参数不符合。
作为开发文档附加说明,方便使用者调用传入和返回类型。
如果传入参数类型不匹配会报错【IDE层面的报错】。
def func(name: str) -> str:
return "Hello" + name
if __name__ == '__main__':
print(func(123))
使用标注前首先需要导入typing标注库, 常用的标注
from typing import List, Dict, Tuple
List[str]
Dict[str, str]
Dict[List[str], str]
Tuple[str, str]
注意:解释器不会对返回类型做检查,比如实际返回是一个dict,但是标注的是str,程序可以正常运行。
中文字符在unicode下占2个字节,在utf-8编码下占3个字节,而golang默认编码正好是utf-8。
s := "hello 中国"
for c, v := range s {
fmt.Printf("%d %c\n", c, v)
}
输出结果
可以发现输出的序号跟预期不太符合
0 h
1 e
2 l
3 l
4 o
5
6 中
9 国
package main
import "fmt"
func main() {
s := "hello 中国"
a := []byte(s)
for c, v := range a {
fmt.Printf("%d %d\n", c, v)
}
}
byte中的汉字被解码为3个字节
0 104
1 101
2 108
3 108
4 111
5 32
6 228
7 184
8 173
9 229
10 155
11 189
如果想要输出可以识别的字符,通常使用string转换一下
rune可变,按照unicode输出
rune=int32 四个字节
byte=int8 1个字节
package main
import "fmt"
func main() {
s := "hello 中国"
a := []rune(s)
for c, v := range a {
fmt.Printf("%d %s\n", c, string(v))
}
}
输出结果
0 h
1 e
2 l
3 l
4 o
5
6 中
7 国
package main
import (
"encoding/json"
"fmt"
)
func main() {
//创建一个map
m := make(map[string]interface{}, 4)
m["company"] = "byte"
m["subjects"] = []string{"GO", "C++", "Java"}
m["ok"] = true
m["price"] = 666.666
//编码成json
//result, err := json.Marshal(m)
result, err := json.MarshalIndent(m, "", " ")
if err != nil {
fmt.Printf("error: %s", err)
return
}
fmt.Printf("result = %v", string(result))
}
因为map是无序的,所以不能保证每次输出的顺序相同。
json转map
package main
import (
"encoding/json"
"fmt"
)
func main() {
//创建一个json
result := `{
"company": "bytedance",
"isok": true,
"price": 666.666,
"subjects": ["GO","C++","Java"]
}`
//创建一个用于接收的map
mp := make(map[string]interface{}, 99)
json.Unmarshal([]byte(result), &mp)
fmt.Println(mp)
}
输出的结果
map[company:bytedance isok:true price:666.666 subjects:[GO C++ Java]]
json转结构体
package main
import (
"encoding/json"
"fmt"
)
//变量名必须大写
type IT struct {
Company string `json:"company"`
Subject []string `json:"subjects"`
Isok bool `json:"-"` //不输出
Price float64 `json:",string"`
}
func main() {
//定义一个结构体变量 同时初始化
s := IT{"bytedance", []string{"GO", "C++", "Python", "Java"}, true, 666.66}
//编码 根据内容生成Json文本
//buf, err := json.Marshal(s)
buf, err := json.MarshalIndent(s, "", " ")
if err != nil {
fmt.Printf("error %s", err)
}
fmt.Printf("%+v", string(buf))
}
结构体转json
package main
import (
"encoding/json"
"fmt"
)
//变量名必须大写
type IT struct {
Company string
Subject []string
Price float64
}
func main() {
result := `{
"Company": "bytedance",
"Subject": ["GO","C++","Python","Java"],
"Price": 666.66
}`
/*创建一个结构体变量*/
var it IT
err := json.Unmarshal([]byte(result), &it)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\n", it)
}
main.IT{Company:“bytedance”, Subject:[]string{“GO”, “C++”, “Python”, “Java”}, Price:666.66}
golang内置了一些基础数据类型的排序,如int,string,float等,其他类型的结构体需要自己实现less, map,len三个函数。
内置排序
package main
import (
"fmt"
"sort"
)
func main() {
// int排序
ints := []int{1, 8, 4, 5}
sort.Ints(ints)
fmt.Println(ints)
// string排序
names := []string{"hello", "world", "yao", "jun"}
sort.Strings(names)
fmt.Println(names)
}
自定义排序
package main
import (
"fmt"
"sort"
)
type Student struct {
name string
age int
}
type stuSlice []Student
func (s stuSlice) Len() int {
return len(s)
}
func (s stuSlice) Less(i, j int) bool {
return s[i].age < s[j].age
}
func (s stuSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func main() {
students := []Student{{"mike", 16}, {"jane", 22}, {"Ben", 19}}
sort.Sort(stuSlice(students))
for k, v := range students {
fmt.Println(k, v)
}
}
输出结果
0 {mike 16}
1 {Ben 19}
2 {jane 22}
t *testing.T 普通测试函数
b *testing.B benchMark测试
普通测试函数
通常test文件的命名规范是xxx_test.go
test case的函数命名为TestXxx形式 否则go test会跳过该test不执行
func F(a,b int)int{
return a+b
}
测试代码
package main
import "testing"
func TestF(t *testing.T) {
res := F(3, 4)
if res != 7 {
t.Errorf("add wrong!")
}
}
Go test不保证多个test按照顺序执行,但是我们通常会要求按照顺序执行。
使用t.Run来执行subtests可以做到控制test执行的顺序
package main
import (
"fmt"
"testing"
)
func TestF(t *testing.T) {
t.Run("a1", func(t *testing.T) { fmt.Println("a1") })
t.Run("a2", func(t *testing.T) { fmt.Println("a2") })
t.Run("a3", func(t *testing.T) { fmt.Println("a3") })
}
使用TestMain作为初始化test,并且使用m.Run()来调用其他tests可以完成一些需要初始化操作的testing,比如数据库连接,文件打开,rest服务登录等。如果没有在TestMain中调用m.Run()则除了TestMain以外的其他tests都不会被执行。
func TestMain(m *testing.M){
fmt.Println("test main first")
m.Run()
}
benchmark测试
benchmark函数一般以BenchMark开头
benchmark函数的case一般会跑b.N次,而且每次执行都会如此
在执行过程中会根据实际case的执行时间是否稳定会增加b.N的次数以达到稳态
go test -bench =. //命令行
func BenchmarkF(b *testing.B) {
for i := 0; i < b.N; i++{
F(100, 10)
}
}
func F(a, b int)int{
return a+b
}
如果benchmark测试的函数执行时间始终无法趋于稳态 则永远无法执行完
func unstable(n int){
for n > 0{
n--
}
}
func BenchmarkF(b *testing.B)
for i := 0; i < b.N; i++{
unstable(i) //如果benchmark测试的函数执行时间始终无法趋于稳态 则永远无法执行完
}
}
slice传参数时新增失败
package main
import "fmt"
func modifySlice(s []string) { //切片新增无效
s[0] = "modify 123"
s = append(s, "456")
}
func main() {
s := []string{"123"}
modifySlice(s)
fmt.Println(s) //返回结果 [modify 123] 新增的数据没有生效
s = modifySliceNew(s)
fmt.Printf("%v", s) //必须重新接收返回值 此时生效 因为修改的指针返回了
}
正确使用方式
原因是传入的参数是指针的拷贝 但是新增数据是修改的指针
func modifySliceNew(s []string)[]string{ //切片新增生效
s[0] = "modify 123"
s = append(s, "456")
return s
}
- 方法1: 删除某个元素后,剩余的元素往前移动
- 方法2: 重新开一个切片,把满足条件的往新切片中放入
- 方法3: 索引累计,如果是删除的元素索引就不加1,最后根据累计的索引进行切片
package main
import (
"fmt"
)
func main() {
//创建一个slice
sliceU := []int{1, 0, 1, 1, 0, 1, 0, 1, 1}
// 要求:把切片中所有的0删除掉
//slice的删除方式1
//删除剩余的元素往前移动
for i, v := range sliceU {
if v == 0 {
sliceU = append(sliceU[:i], sliceU[i+1:]...)
}
}
fmt.Println(sliceU)
//slice的删除方式2
//重新开一个数组,把满足条件的往新数组中写入
var newSliceU []int
for _, v := range sliceU {
if v != 0 {
newSliceU = append(newSliceU, v)
}
}
fmt.Println(newSliceU)
// slice的删除方式3
// 索引累计 最后切片
index := 0
for _, v := range sliceU {
if v != 0 {
sliceU[index] = v
index++
}
}
fmt.Println(newSliceU[:index])
}
从上面的结果可以看出,数据范围较小时,方法2和方法3性能差不多,数据量大以后,方法1总体上要差于其他两个策略。
方法1
时间复杂度:O(n^2)
空间复杂度:O(n)
方法2
时间复杂度:O(n)
空间复杂度:O(n)
方法3
时间复杂度:O(n)
空间复杂度:O(1)
package main
import "fmt"
func f(arr [10]int){ //不仅是数组还必须长度一致
arr[1] = 100
fmt.Println(arr)
}
func main(){
/*
数组:类型和长度确定。
值类型:传递参数时,进行值拷贝。
*/
var arr [10]int
arr[1] = 2
arr[2] = 3
f(arr)
fmt.Println(arr)
arr2 := arr
arr2[1] = 100 //拷贝以后改变 没影响
fmt.Println(arr2)
fmt.Println(arr)
}
package main
import "fmt"
func f(arr [10]int){ //不仅是数组还必须长度一致
arr[1] = 100
fmt.Println(arr)
}
func main(){
/*
切片:slice是对底层数组一段内容的引用,slice是一个fat pointer存储的是实际数组的地址,切片的长度,最大容量
注意:应该避免在两个变量中修改同一个底层数组
data[low:high:max]high和max都是不包含当前下标的。
len()
cap()
append()
copy() 可以实现对切片的深拷贝 切片间的直接赋值是浅拷贝,只是赋值了共同的底层数组地址
对同一个数组进行的不同切片,对某个切片进行操作,对其他切片是没有感知的,但是如果是对底层数组修改,两者都有感知。
引用类型都是浅拷贝
操作切片也是操作底层数组,slice采用两倍扩容机制,一旦扩容就重新指向一个新的底层数组。
cap是切片开始位置到数组末尾的长度
*/
var arr [10]int
arr[0] = 100
arr[1] = 200
arr[2] = 300
slice1 := arr[0:2]
slice2 := arr[1:5]
fmt.Println(cap(slice1))
fmt.Println(cap(slice2))
slice3 := slice2
arr[1] = 20000
fmt.Println("_______")
fmt.Println(slice1)
fmt.Println(slice2)
fmt.Println(slice3)
}
val, typeErr := strconv.Atoi(myStr2)
b2, errs:= strconv.ParseInt(s2, 2, 64) //把字符串二进制转换为十进制整数
value := strconv.Itoa(myInt)
ss2 := strconv.FormatInt(b2, 2) //把整数以二进制转换为字符串
myByte := []byte(myStr3)
一个英文字符对应一个byte,一个中文字符对应三个byte。
一个rune对应一个UTF-8字符,所以一个中文字符对应一个rune。%c输出对应的字符和汉字。
rune相当于go的char。
byte=uint8
rune=uint32
t:= time.Now() //当前时间
bf := bufio.NewReader(os.Stdin)
ss, errs := bf.ReadString('\n')
lst := string.Field(ss) //按照空格切分字符串
cities := [...]string{"北京", "上海","深圳"}
cities := []string{"上海", "成都"}
for index,value := range cities{
fmt.Println(index, value)
}