如何在多个编程语言间切换自如
目录
Attention
要自由切换编程语言,必然要通过实践,编程语言之海浩瀚无边,本文会随着作者的实践而不断更新,敬请期待。
基本思路 ¶
- 熟悉各语言基本语法,怎么定义变量、函数、控制流等,这里有语法简介、数据结构等章节。
- 识别各语言的特性,为什么 Python 有元组等,也就是要知道语言的特性和高级用法等。
在熟悉基本语法时,要能搞清楚各编程语言怎么描述相同的功能的。 在识别语言特性时,要能搞清楚各编程语言处理方式上的设计哲学。
语法简介 ¶
语法 | Python | Go |
---|---|---|
变量声明 | 动态类型,无需声明 | 静态类型,必须声明 |
类型转换 | int(), str(), float(), tuple(), list(), set(), dict() | type_name(exp), strconv.Atoi(), strconv.Itoa(), strconv.ParseFloat(), string(), value.(type), value.(T) |
输入输出 | print(), input() | fmt.Println(), fmt.Scan() |
代码块 | 缩进(空格/制表符) | {} 包裹 |
循环 | for、while | 只有 for |
函数 | def,支持默认参数 | func,无默认参数 |
错误处理 | try-except | 返回 error + if err != nil |
并发 | threading(GIL 限制) | goroutine + channel |
面向对象 | 完整类继承 | struct + interface |
包管理 | pip + import | go mod + import |
内置函数 ¶
Python 的内置函数更丰富,适合快速开发;Go 的内置函数较少,但更专注于底层控制和性能优化。
Go ¶
- 内存分配:new(), make()
- 类型转换:int(), float64(), string()
- 集合操作:len(), cap(), append()
- 并发:go, chan, close()
- 错误处理:panic(), recover()
Python ¶
- 输入输出:print(), input()
- 类型转换:int(), float(), str(), bool()
- 数学运算:abs(), round(), min(), max(), sum()
- 迭代与序列操作:len(), range(), sorted(), enumerate()
- 对象操作:type(), isinstance(), dir()
- 高阶函数:map(), filter(), zip()
控制流 ¶
循环 ¶
Go ¶
/* Go 只有 for 循环 */
for i, val := range arr {
// ...
}
for i := 0; i < len(arr); i++ {
// ...
}
Python ¶
for i in range(10):
pass
for i in range(0, 10, 1):
pass
for val in arr:
pass
for i, val in enumerate(arr):
pass
else
pass
while len(queue) > 0:
pass
else
pass
while flag: print(str(flag))
数据结构 ¶
栈 ¶
Go ¶
stack := make([]string, 0, 10)
// 入栈
stack = append(stack, "A")
// 出栈
v := stack[len(stack)-1]
stack = stack[:len(stack)-1]
// 判断是否为空
isEmpty := len(stack) == 0
Python ¶
L = []
L.append('D') # 入栈
L.pop() # 出栈
L[-1] # peek
队列 ¶
Go ¶
queue := make([]int, 0)
// 入队
queue = append(queue, 10)
// 出队
v := queue[0]
queue = queue[1:]
// 判断是否为空
isEmpty := len(queue) == 0
Python ¶
L = []
L.append('D') # 入队
L.pop(0) # 出队
L[0] # peek
collections.deque
是个高效的双端队列,具有popleft()
append()
方法。
集合 ¶
集合(set)是一个无序的不重复元素序列。
如果从广义上来看的话,数组、字典、元组、栈、队列等都是集合。
Go ¶
无原生 Set,通常用 map[T]bool
或 map[T]struct{}
模拟。一般还会封装成一个结构体。
// 初始化一个 set(用 map[string]bool 模拟)
set := make(map[string]bool)
// 添加元素
set["apple"] = true
set["banana"] = true
// 检查元素是否存在
if set["apple"] {
fmt.Println("apple exists") // 输出: apple exists
}
if _, ok := set["apple"]; ok {
// 当使用 map[string]struct{} 时更省内存(struct{} 是零大小类型)
fmt.Println("apple exists") // 输出: apple exists
}
// 删除元素
delete(set, "banana")
// 遍历集合
for key := range set {
fmt.Println(key) // 输出: apple
}
Python ¶
set1 = {1, 2, 3, 4} # 直接使用大括号创建集合; 创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典
set2 = set([4, 5, 6, 7]) # 使用 set() 函数从列表创建集合
set2.add(8) # 将元素 x 添加到集合 s 中,如果元素已存在,则不进行任何操作
set2.update([9,10]) # 参数可以是列表,元组,字典等
set2.remove(8) # 将元素 x 从集合 s 中移除,如果元素不存在,则会发生错误
set2.discard(8) # 移除集合中的元素,且如果元素不存在,不会发生错误
set2.pop() # 随机删除集合中的一个元素
set2.clear() # 清空集合
x in s # 判断元素 x 是否在集合 s 中,存在返回 True,不存在返回 False
len(s) # 计算集合 s 元素个数
字符串 ¶
串是特殊的线性表,字符串的操作一般有:
- 字符串替换
- 字符串查找
- 字符串连接
- 字符串分割
- 大小写转换
- 去除空格
- 字符串判断
- 字符串格式化
- 字符串构建
两种语言的字符串都是不可变的。需要注意的是:
- Python3 的字符串是 Unicode 码点序列,Go 字符串是 UTF-8 字节序列(rune 才是 Unicode 码点)
- Python 使用对象方法,而 Go 使用 strings 包函数
- Go 需要显式错误处理,Python 使用异常
- 索引访问时,Python 直接返回字符,Go 返回字节
- Python支持负数索引,Go 不支持
- Python没有单独的字符类型,字符串(str)是由单个字符组成的序列,也就是说,一个字符就是一个长度为1的字符串;而在 Go 中处理 ASCII 字符时常用 byte,而处理 Unicode 字符时常用 rune,按字符遍历,用 for range 循环时会将字符串解码为 rune。
Go ¶
// 字符串替换
s := "hello world"
newS := strings.Replace(s, "world", "golang", -1) // "hello golang"
newS2 := strings.Replace(s, "l", "L", 2) // "heLLo world"
// 字符串判断
s := "hello"
starts := strings.HasPrefix(s, "he") // true
ends := strings.HasSuffix(s, "lo") // true
// Go标准库没有直接的isalpha/isdigit,需要自定义或使用unicode包
isAlpha := true
for _, r := range s {
if !unicode.IsLetter(r) {
isAlpha = false
break
}
}
// 字符串分割
strings.Split("a,b,c", ",") // ["a" "b" "c"], Split slices s into all substrings separated by sep and returns a slice of the substrings between those separators.
strings.Split("a man a plan a canal panama", "a ") // "" "man " "plan " "canal panama"]
strings.Split(" xyz ", "") // [" " "x" "y" "z" " "], If sep is empty, Split splits after each UTF-8 sequence.
strings.SplitN("a,b,c,d", ",", 3) // []string{"a", "b", "c,d"}
// 字符串转整数
s := "123"
numInt, err := strconv.Atoi(s) // 123
if err != nil {
fmt.Println("转换错误:", err)
}
// 指定基数的转换
numHex, _ := strconv.ParseInt("ff", 16, 64) // 255 - 十六进制
numBin, _ := strconv.ParseInt("1010", 2, 64) // 10 - 二进制
// 字符串转浮点数
sFloat := "3.14"
numFloat, err := strconv.ParseFloat(sFloat, 64) // 3.14
if err != nil {
fmt.Println("转换错误:", err)
}
// 无符号整数转换
numUint, _ := strconv.ParseUint("123", 10, 64)
// 布尔值转换
boolVal, _ := strconv.ParseBool("true") // true
fmt.Println(numInt, numHex, numBin, numFloat, numUint, boolVal)
// 安全转换函数
func safeAtoi(s string) int {
if num, err := strconv.Atoi(s); err == nil {
return num
}
return 0
}
result := safeAtoi("123") // 123
result2 := safeAtoi("abc") // 0
Python ¶
# 字符串替换
s = "hello world"
new_s = s.replace("world", "golang") # "hello golang"
new_s2 = s.replace("l", "L", 2) # "heLLo world"
# 字符串判断,Python 中蛮多方法的
s = "hello"
starts = s.startswith("he") # True
ends = s.endswith("lo") # True
is_alpha = s.isalpha() # True, Return True if all characters in the string are alphabetic and there is at least one character, False otherwise.
is_digit = s.isdigit() # False, Return True if all characters in the string are digits and there is at least one character, False otherwise. # isdecimal() ⊆ isdigit() ⊆ isnumeric()
# 字符串分割
'1,2,3'.split(',') # ['1', '2', '3'], Return a list of the words in the string, using sep as the delimiter string.
'1,2,3'.split(',', maxsplit=1) # ['1', '2,3']
'1,2,,3,'.split(',') # ['1', '2', '', '3', '']
'1 2 3'.split() # ['1', '2', '3'] If sep is not specified or is None, a different splitting algorithm is applied: runs of consecutive whitespace are regarded as a single separator, and the result will contain no empty strings at the start or end if the string has leading or trailing whitespace.
' 1 2 3 '.split() # ['1', '2', '3']
# 字符串转整数
s = "123"
num_int = int(s) # 123
num_hex = int("ff", 16) # 255 - 十六进制
num_bin = int("1010", 2) # 10 - 二进制
# 字符串转浮点数
s_float = "3.14"
num_float = float(s_float) # 3.14
# 错误处理
try:
num = int("abc")
except ValueError as e:
print(f"转换错误: {e}")
# 安全转换(不会抛出异常)
def safe_int(s, default=0):
try:
return int(s)
except ValueError:
return default
result = safe_int("123") # 123
result2 = safe_int("abc") # 0
语言特性 ¶
类型 ¶
Python ¶
- 在 Python 中,空容器(如空列表、空字符串、空字典等)在布尔上下文中被视为
False
,而非空容器被视为True
,所以可以使用while stack
表示栈不为空时的循环。 - 圆括号 () → 元组,方括号 [] → 列表,花括号 {} → 集合或字典
函数 ¶
Java ¶
- 在 Java 中,如果在函数中修改传递给函数的引用所对应的对象,是值传递,但是传递的是引用的值,会影响原对象。通过引用操作对象,效率高但需要注意副作用。
Go ¶
- 在 Go 中,如果在函数中修改传递给函数的 slice 或 map 里的元素,也是值传递,但由于这个值包含指向底层数组的指针,实际上也是传递的引用,会影响原切片,但使用
append()
可能不会影响原切片。Go 更明确地控制内存分配和修改行为。
Python ¶
- 在 Python 中,如果在函数中修改传递给函数的可变对象(如列表),实际上传递的是对象的引用,跟 Java 类似,会直接影响原对象。通过引用操作对象,效率高但需要注意副作用。
计算 ¶
Java ¶
- Java 中,
/
的行为取决于操作数类型,整数 / 整数 → 整数除法(截断),浮点数 / 整数 → 浮点除法,在设计上更注重类型安全和性能。
Go ¶
- Go 中,
/
的行为取决于操作数类型,整数 / 整数 → 整数除法(截断),浮点数 / 浮点数 → 浮点除法,浮点数 / float64(整数)→ 浮点除法,在设计上更注重类型安全和性能,同时“显式优于隐式”,Go 的类型系统更严格,相比 Java 没做整数到浮点数的自动类型提升。
Python ¶
- Python 中的除法运算符
/
总是返回一个浮点数(即使两个操作数都是整数并且可以整除),如果需要执行整数除法(即向下取整),可以使用//
运算符。Python 的设计哲学强调代码的清晰性和可读性,“让简单的事情简单,复杂的事情可能” ,/
总是进行真除法(true division),返回数学上准确的结果。