Python 这个 scope/block leak 我是越来越膈硬

2022-08-07 11:44:21 +08:00
 haoliang

这个 scope/block leak 我最初是在学 nim 时看到的: https://github.com/nim-lang/Nim/wiki/Nim-for-Python-Programmers#scoping

最开始我还不是很在意,直到之前接手老项目代码,我都快疯了:if..elif..else 里面藏着 n 个变量,然后在后面使用变量的代码是否能正常运行完全靠运气(当然有一些业务逻辑保证,但业务逻辑一直在变啊,能从静态分析阶段排除的问题非要等到运行时再确认,费时费力) 我之前一直用嵌套 function 的方式来创造 scope ,以达到隔离变量名、最小化变量生命周期的目的 (我经常纠结命名:xx_a 、xx_b, 有了 scope 我全叫它们 xx )

我觉得有两方面的缺失在 python 中

  1. for 、while 、with 、if 这类应该是隐式 scope ,却不是 (当然 with 列在这里不太合适,但有必要)
  2. 没有提供显式 scope 块,除非 def

针对第 2 点,我所了解的其他大部分语言都有对应的设计

针对第 1 点,javascript 有 let ,lua 有 local ,上面列出的其他语言没这毛病; python 的 nonlocal 也不是针对这种情况的。

很少见网上有过相关讨论,所以好奇问下大家难道不在意吗?

-- 倒是找到个相关库: https://github.com/l74d/scoping

2423 次点击
所在节点    Python
9 条回复
imycc
2022-08-07 12:04:04 +08:00
是有点在意的。块级作用域缺失,出现的 bug 靠编辑器发现不了。比如一个函数写得比较长,存在几个循环,后面的循环用错了变量之类的。
只能靠代码规范来规避了,比如控制函数长度,变量命名表意明确,临时变量的初始化、使用和销毁放到一块,写好注释之类的。
humiaozuzu
2022-08-07 12:07:44 +08:00
一方面是靠编码风格来减少这些 scope 在其他地方被引用。另外不知道 flake8 有没有插件可以发现这类用法, 提示出来
wxf666
2022-08-07 12:07:57 +08:00
这……是一个函数内揉了太多内容,还是不同含义的变量都用同一个命名表示了?
hezhiming1993
2022-08-07 14:46:56 +08:00
在意啊
所以转 Go 了,

现在小工具都用静态语言写
chenxytw
2022-08-07 18:39:28 +08:00
这点确实是 Why Python sucks 的重要组成部分。而且嵌套函数定义也确确实实就是唯一解决方案。
但其实 Python 作用域的坑远比你想象的深且大(
比如这个: https://blog.kevmod.com/2014/06/26/what-does-this-print-1/
junkun
2022-08-07 23:17:11 +08:00
我觉得 leak 这个名词不合适,python 它就是只有两种作用域,模块全局和函数内,并没有 leak 。global 和 nonlocal 其实只是变量查找的规则。

并且个人认为,python 其实设计上就不提倡嵌套函数(除非你要返回这个函数),过多的嵌套函数实际上就像共享全局变量一样,甚至 context 更复杂,也不好扩展。我更提倡在外面建一个新的函数然后把相关局部变量用参数传递进去,要修改 nonlocal 变量再返回出来。
jfcherng
2022-08-08 00:51:00 +08:00
@chenxytw #5 請問這個是什麼原理呢?
haoliang
2022-08-08 12:10:49 +08:00
谢谢各位回复。
@chenxytw 哈哈,这个例子太冷门了,我目前没遇到过。不过帖子描述中的 nim 链接中确实有列出它。
@junkun 这 nonlocal 操作有点繁琐啊,看起来不贴近实际场景,我大概率会继续用 closure ,除非像 zig 这样不支持 nested function 更没有 closure 的。(为了区分这两者,就用 closure 、function 代替了)

我有些惊讶,lua 5.1 这个 10 来年语法没改变的语言在这点上都比 python 想得周到,而 python 这边竟然连个 pep 都没有。(lua 5.1 发布于 2006 ,python 3.0 于 2008 )
chenxytw
2022-08-08 13:57:36 +08:00
@jfcherng 用 dis 包看一下这个函数编译出来的 opcode 。x 和 y 的处理完全不一样 0 0
至于为什么不一样,原因之一 是后面的赋值语句,会改变 x 的属性 /类别。
原因之二是 class 的特殊处理。同样的顺序放到 function 中,只会得到 UnboundLocalError: local variable referenced before assignment

当然,整件事上我个人认为是设计缺陷....

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

https://www.v2ex.com/t/871213

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

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

© 2021 V2EX