云原生探索系列(一):Go基础语法 与 Python 对比(控制结构)
前言
在写了两篇云原生系列之docker相关的文章后,决定还是先把技术栈Go先学习一遍。因为像Docker、K8s等涉及云原生的基本是使用Go语言来实现的,自己有一些 python语言基础,通过类比学习方式,应该很快可以入门Go。这篇内容将从输出HelloWorld入手,然后介绍条件语句以及循环语句。
Go语言优势
Go 语言是一个可以编译高效,支持高并发的,面向垃圾回收的全新语言。
- 秒级完成大型程序的单节点编译。
- 依赖管理清晰。
- 不支持继承,程序员无需花费精力定义不同类型之间的关系。
- 支持垃圾回收,支持并发执行,支持多线程通讯。
- 对多核计算机支持友好。
Go 语言不支持的特性
- 不支持函数重载和操作符重载
- 为了避免在C/C++开发中的一些Bug和混乱,不支持隐式转换
- 支持接口抽象,不支持继承
- 不支持动态加载代码
- 不支持动态链接库
- 通过recover和panic来替代异常机制
- 不支持断言
- 不支持静态变量
HelloWorld
Go实现
哈哈,学习编程的第一段代码都是从打印HelloWorld开始,我们也从打印入手,不过加入的解析命令行参数,先看go语言实现代码 main.go
:
package main
import (
"flag"
"fmt"
"os"
)
func main() {
name := flag.String("name", "world", "specify the name you want to say hi")
flag.Parse()
fmt.Println("os args is:", os.Args)
fmt.Println("input parameter is:", *name)
fullString := fmt.Sprintf("Hello %s from Go\n", *name)
fmt.Println(fullString)
}
先不管代码逻辑,先编译运行看看输出结果,执行如下命令进行编译:
go build -o ~/GolandProjects/bin/helloworld main.go
-o: 编译后的可执行文件输出到~/GolandProjects/bin目录下,并命名为helloworld。 进入二进制文件目录,执行如下命令:
./helloworld
可以看到输出如下内容:
os args is: [./helloworld]
input parameter is: world
Hello world from Go
执行如下命令:
./helloworld -h
可以看到输出如下内容:
Usage of ./helloworld:
-name string
specify the name you want to say hi (default "world")
从帮助中,我们可以看到,可以指定name 执行如下命令:
./helloworld -name leilei
可以看到输出如下内容:
os args is: [./helloworld -name leilei]
input parameter is: leilei
Hello leilei from Go
解释一下这个简单的Go语言程序,它包含以下几个部分:
- 导入包:
fmt
和flag
是Go语言的标准库包,用于打印输出和处理命令行参数。 func main()
:这是Go语言程序的入口函数,程序从这里开始执行。- 定义命令行参数:使用
flag.String
函数定义了一个名为name
的命令行参数,指定了默认值为"world"
,并提供了参数说明。 - 解析命令行参数:使用
flag.Parse()
函数解析命令行参数,并将结果保存在相应的变量中。 - 打印命令行参数和输入参数:使用
fmt.Println
函数打印输出了命令行参数os.Args
和输入参数*name
。 - 格式化字符串:使用
fmt.Sprintf
函数生成一个格式化的字符串fullString
,其中包含了输入参数*name
。 - 打印输出:使用
fmt.Println
函数将格式化的字符串fullString
打印输出。
python实现
笔者之前工作中使用python语言居多,就将GO与python做比较,加深记忆。
下面是python实现类似功能, main.py
import sys
if __name__ == '__main__':
name = "world"
print(f"sys args is {sys.argv}")
if len(sys.argv) > 1:
name = sys.argv[1]
print(f"input parameter is:{name}}")
full_string = f"Hello {name} from Python"
print(full_string)
在Python中,我们使用sys.argv
来获取命令行参数, sys.argv[0]
表示脚本文件名,后续的参数通过sys.argv[1:]
进行访问。在上述代码中,我们检查sys.argv
的长度,如果长度大于1,则将第一个参数赋值给name
变量。 执行如下命令,
python main.py leilei
输出内容如下:
sys args is ['main.py', 'leilei']
input parameter is: leilei
Hello leilei from Python
当然,我们看到go实现代码中,使用flag处理命令行参数,python也有提供内置的模块argparse可以用于处理命令行参数。优化后的代码如下:
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-n", "--name", default="world", help="specify the name you want to say hi")
args = parser.parse_args()
print(f"input parameter is:{args.name}")
full_string = f"Hello {args.name} from Python"
print(full_string)
首先创建了一个ArgumentParser对象,然后使用add_argument方法定义了一个名为--name(或者简写为-n)的命令行参数,并指定了默认值和帮助信息。接下来,通过调用parse_args方法解析命令行参数,并将结果保存在args变量中。
可以看到,使用argparse模块相较于之前的实现更灵活和易用。
IF
基本用法
与python类似,直接上代码,一看就明白 go实现:
package main
import "fmt"
func main() {
age := 25
if age < 18 {
fmt.Println("You are a minor")
} else if age >= 18 && age < 60 {
fmt.Println("You are an adult")
} else {
fmt.Println("You are a senior")
}
}
python实现:
if __name__ == '__main__':
age = 25
if age < 18:
print("You are a minor")
elif age >= 18 and age < 60:
print("You are an adult")
else:
print("You are a senior")
基本用法很好理解,不多解释了。
条件判断中声明变量
go实现:
func main() {
if y := 20 - 6; y < 15 {
fmt.Println("y is less than 15")
} else {
fmt.Println("y is greater than or equal to 15")
}
}
python是不支持这种写法的。
switch
基本使用
小案例:当天是周六或者周日,输出是周末,否则输出是工作日
go实现:
package main
import (
"fmt"
"time"
)
func main() {
day := time.Now().Weekday()
switch day {
case time.Saturday, time.Sunday:
fmt.Println("It's the weekend")
default:
fmt.Println("It's a weekday")
}
该示例展示了基本的switch语句,根据条件判断输出不同的结果。
注意:可以同时测试多个可能符合条件的值,使用逗号分割它们,向上面这样case time.Saturday, time.Sunday:
python实现: python3.10后引入类似swicth的语法。之前版本没有。
import datetime
if __name__ == '__main__':
day = datetime.date.today().weekday()
match day:
case 5 | 6:
print("It's the weekend")
case _:
print("It's a weekday")
这里使用 "|" 进行模式组合。
使用表达式
小案例:判断当前小时,小于12输出早上好,小于18输出下午好,否则输出晚上好 go实现:
func main() {
hour := time.Now().Hour()
switch {
case hour < 12:
fmt.Println("Good morning!")
case hour < 18:
fmt.Println("Good afternoon!")
default:
fmt.Println("Good evening!")
}
}
此代码示例展示了在switch中使用表达式的用法,根据表达式的结果判断输出不同的结果。 python实现:
if __name__ == '__main__':
hour = datetime.datetime.now().hour
match hour:
case hour if hour < 12:
print("Good morning!")
case hour if hour < 18:
print("Good afternoon!")
case _:
print("Good evening!")
强制执行后面的 case 语句
go语言使用 fallthrough 会强制执行后面的 case 语句。 go实现:
func main() {
value := 2
switch value {
case 1:
fmt.Println("Value is 1")
fallthrough
case 2:
fmt.Println("Value is 2 or fell through from 1")
fallthrough
case 3:
fmt.Println("Value is 3 or fell through from 2")
case 4:
fmt.Println("Value is 3")
fallthrough
default:
fmt.Println("Unknown value")
}
}
编译执行上面代码,会输出如下内容:
Value is 2 or fell through from 1
Value is 3 or fell through from 2
从以上代码输出的结果可以看出:switch 从第一个判断表达式为 true 的 case 开始执行,如果 case 带有 fallthrough,程序会继续执行下一条 case,且它不会去判断下一个 case 的表达式是否为 true。 python match语法不支持类似fallthrough行为,强制执行后面的case
For循环
Go 只有一种循环结构:for 循环。
基本使用
小案例:使用基本for循环打印1到5的整数 go实现:
func main() {
for i := 1; i <= 5; i++ {
fmt.Println(i)
}
}
python实现:
if __name__ == '__main__':
for i in range(1, 6):
print(i)
初始化语句和后置语句是可选的,此场景与 while 等价
小案例: sum 小于 10 的时候计算 sum 自相加后的值. go实现:
func main() {
sum := 1
for sum < 10 {
sum += sum
}
fmt.Println(sum)
}
python实现:
if __name__ == '__main__':
sum_ = 1
while sum_ < 10:
sum_ += sum_
print(sum_)
python中是有while循环的。
无限循环
go实现:
func main() {
for {
fmt.Println("loop")
}
}
python实现:
if __name__ == '__main__':
while True:
print("loop")
for-range
遍历数组,切片,字符串,Map 等. go实现:
func main() {
// 遍历数组
slice := []int{1, 2, 3, 4, 5}
for index, value := range slice {
fmt.Printf("索引:%d,值:%d\n", index, value)
}
// 遍历字符串
fullString := "hello world"
for index, char := range fullString {
fmt.Println(index, string(char))
}
// 遍历Map
m := map[string]int{"one": 1, "two": 2, "three": 3}
for k, v := range m {
fmt.Println(k, v)
}
arr := []*int{new(int), new(int), new(int)}
// 遍历指针数组
for _, ptr := range arr {
fmt.Println(ptr) // 这里输出的是指针的拷贝地址
}
}
着重说一下遍历指针数组,在每次迭代中,取出的指针地址实际上是原指针地址的一个拷贝。这意味着,你在循环中对取出的指针地址进行操作时,实际上是在操作拷贝后的指针地址,而不是原始的指针地址。 因此,如果你想要修改原始指针数组中的内容,你需要使用索引来直接访问原始数组。
python实现:
for index, value in enumerate([1, 2, 3, 4, 5]):
print(f"索引:{index},值:{value}")
for index, char in enumerate("hello world"):
print(index, char)
for k, v in {"one": 1, "two": 2, "three": 3}.items():
print(f"{k}: {v}")
enumerate函数用于将一个可迭代对象(如列表、元组或字符串)转换为一个枚举对象,该对象可以生成一个由计数(从start开始,默认为0)和可迭代对象中的元素组成的元组序列。
最后
通过类比的方式,快速掌握了条件语句以及循环语句,下一篇将掌握常用数据结构。
转载自:https://juejin.cn/post/7371687296599277619