go 转 Python 的心智负担增加

10 天前
 yujianwjj

之前写 go ,go 调用函数的时候,有问题就是通过 返回值 有没有 err 来判断。心智负担很小,直接撸就行。

一个简单的案例,打开文件,然后 json 解析。 go 版本是

func TestA(t *testing.T) {
	fd, err := os.OpenFile("a.txt", os.O_RDWR, 0666)
	if err != nil {
		fmt.Println("打开文件失败", err)
	}
	defer fd.Close()

	var data map[string]string
	err = json.NewDecoder(fd).Decode(&data)
	if err != nil {
		fmt.Println("解析文件失败", err)
	}
	fmt.Println(data)
}

但是到 python 这边

f = open("a.json")
data = json.load(f)
f.close()

但是吧

  1. 如果文件不存在需要处理
  2. 文件存在,open() 执行异常了要处理
  3. json.load() 会不会异常?我跳转源码看 josn.load()源码也没看到他会返回哪些异常信息。

所以我写出这种代码

try:
	with open("a.json", "r") as f:
		data = json.load(f)
except Exception as e:
	print(e)

但是这样把多种异常合到一个 Exception 了,如果出现异常,你不知道是哪个函数调用出现的异常。所以就需要这样。

try:
	with open("a.json", "r") as f:
    	try:
			data = json.load(f)
    	except Exception as e:
			print(e)        
except Exception as e:
	print(e)

然后我发现,最大的问题就是,我每次调用一个外部函数的时候,TMD 根本不知道这个函数会不会异常,总不能每个函数调用都用 try/except 处理一下?

try:
	f1()
except Exception as e:
	print(e)
    
try:
	f2()
except Exception as e:
	print(e)
    
try:
	f3()
except Exception as e:
	print(e)

写 python 给我的感受就是,想写好一个健壮的程序,有很大的心智负担,我总是要考虑我调用的函数会不会有异常。

8070 次点击
所在节点    Python
96 条回复
Leviathann
10 天前
@w568w 已经确认在 2.4 加上 union error 了吗
w568w
10 天前
@Leviathann KotlinConf 2025 说的,到 2.4 应该还是 experimental 。

X 帖子: https://x.com/joreilly/status/1925451874116817181
Leviathann
10 天前
@chenqh checked exception 本意是好的,只是编译器写的太低级没做到自动推导
wulili
10 天前
还是写 C#最省心,每个函数会不会抛出 Exception ,还有会抛出什么类型的 Exception 都给你列的明明白白的,很直观。
lance07
10 天前
go 不就是"调一个函数处理一次 "
hwdq0012
10 天前
python 的异常可以把 stacktrace 打印出来看看,就知道哪里异常了,用返回值确实挺好,c++很多人宁愿用返回值不用 exception
johnnyyeen
10 天前
python 的思维逻辑很可以“曾智慧” doge
yiwayhb
10 天前
@wulili 实际上,只有 MS 官方的库有这种良好的文档注释,更多的第三方库都不太完善。
常用的做法仍然是「 let is crash 」,只处理能解决的异常,最外层统一处理,给出用户交互
bronyakaka
10 天前
你都文件不存在了还 return 啥,打印啥,有意义吗?这时候是要主动抛出问题

python 直接不处理让他自己报错就行了,报错自带堆栈和 return ,一眼就知道问题了

go 不得不写 err ,是因为 go 没有异常传播机制,只能手打 err ,不然会继续往下走,非常蠢
newtype0092
10 天前
不是精细到每一个异常都处理才是好的设计,不然为什么系统要分那么多层级。

try catch 是在理清楚哪些错误需要处理,哪些错误需要屏蔽的基础上再进行 fallback 处理的,目的是保证程序能按预期执行下去。

一般好的设计会把所有的下层异常都包装成自己本层可解释的异常,声明在方法上。
当然也有很多没有这么做的,就会冒出没有声明的异常,这种情况如果没有特殊的原因的话做统一处理就好了,实在需要某些信息的时候就往下追追代码。

对于你说的不知道有哪些异常的问题,先学会看文档吧,一开始就追代码容易追不对地方,基础库的文档还是比较全的,熟练了想再追代码的事。
rockdodos
10 天前
python EAFP ,外层 try 就完事了,我觉得 go 才罗里吧嗦的,一直 if err != nil
xman99
10 天前
php 可记录调用栈信息的,会有报错行数
不知道 python 有没有对应处理方法
DOLLOR
10 天前
自己封装一个嘛,反正 python 那么灵活:

from typing import Callable, Tuple, TypeVar

T = TypeVar('T')
def go_try(fn: Callable[[], T]) -> Tuple[T, Exception]:
▓▓"""go 式异常"""
▓▓try:
▓▓▓▓return (fn(), None) # type: ignore
▓▓except Exception as error:
▓▓▓▓return (None, error) # type: ignore

# 回来了,一切都回来了🐶
myfile, err = go_try(lambda: open('a.json'))

# 就是这个味🐶
if err is not None:
▓▓print('open 异常', err)
superrichman
10 天前
@qiuhang 异常可远不止这些,光是文件读取相关的异常就能列出一长串:

文件不存在异常
传入路径是文件夹而非文件
没有读取权限
读取模式参数错误
使用错误的模式操作文件,比如以读模式尝试写入
各种 OS 级异常,比如磁盘损坏、挂载分区断开等
文件编码错误,比如用 UTF-8 解码 GBK 文件
等等等等,遇到得多了你会发现奇葩异常真不少。

如果每个异常都要手动处理一遍,代码会变得又臭又长,简直跟 Go 一样,心智负担爆表。

一个 try-catch 包住,已经能兜住大部分问题。只有那些频繁出现且确实值得特殊处理的异常,才需要单独写逻辑去应对。否则就是浪费时间,拉低可维护性。
UN2758
10 天前
@DonaldVVV 你这只打印了异常类型,没输出异常的代码行数,还不如 traceback.print_exc() 呢
iyaozhen
10 天前
@superrichman 是的,异常可多了。之前写 Python 就是,压根不告诉你会返回哪些异常
XueXianqi
10 天前
@DOLLOR 嘿,你别说,你还真别说,就是这个味儿!
ychost
10 天前
写 python 就不需要每个函数都处理 err 了,像 java 一样,需要处理的地方才 catch
wx497657341
10 天前
go 没有资格拿异常处理来碰其他语言
openmynet
10 天前
python: 默认一切都安全的
rust: 默认一切都是不安全的

使用 python 就是要放下一切,能跑就行。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1140619

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX