C 家族的编程语言,基本都会为 for 和 while 循环引入新的作用域,以保证循环里定义的局部变量不会污染外部作用域。
for (int i = 0; i < 10; i++) { printf("%d\n", i); } // 无法再访问 i
但 Python 不是这样,for 和 while 循环内的局部变量会在循环后继续保留,依然可以被访问。也就是说,for 和 while 循环没有引入新的作用域。
for i in range(10): print(i) print(i) # 可以访问,i=9
很明显,这样的做法会造成很多不必要的错误,比如循环内的变量遮盖了外部作用域的变量,循环结束后使用了被循环污染的变量等。
Python2 有这样的问题可以归结为历史原因,Python3 为什么也继承了这个问题?
![]() | 1 1daydayde 2020-08-04 09:34:14 +08:00 这就是 Python.jpg |
2 domosekai 2020-08-04 09:34:23 +08:00 via Android 解释语言的锅? |
![]() | 3 zachlhb 2020-08-04 09:36:52 +08:00 via Android 变量名不要起成一样的 |
![]() | 4 sixway 2020-08-04 09:37:16 +08:00 |
5 optional 2020-08-04 09:39:48 +08:00 via iPhone 函数作用域是很多语言的特性啊,比如 c89 es5 |
![]() | 6 GlobalNPC 2020-08-04 09:40:11 +08:00 我记得之前查过说 因为 for 循环不是函数体,所以这里的变量与 for 是同级的,所以注意命名吧 |
![]() | 7 youthfire 2020-08-04 09:40:23 +08:00 via iPhone 没有学过其他语言,所以我的作法是一段代码封装一个函数就隔离了 |
![]() | 8 AV1 2020-08-04 09:42:16 +08:00 ![]() js 的 var 声明也是这样的,是函数级作用域,会提升到函数开始处,不过后来有了 let 和 const 这两,用来声明块级作用域,不再有这种问题了。 |
![]() | 9 magiclx 2020-08-04 09:56:55 +08:00 其实 for 循环结束后,如果你想知道 i 最后的值,能访问到 i 没什么不好的。 其实是需要养成一个习惯,如果后面新使用 i 时,必须赋初始值。 |
10 xiaolinjia 2020-08-04 10:01:00 +08:00 你这个问题确实是这样的,所以我一般用列表推导,就没这个问题( Py3 限定) [i for i in range(10)] print(i) NameError: name 'i' is not defined |
11 laike9m 2020-08-04 10:08:03 +08:00 via Androd 其实就是语言没设计好。。 |
![]() | 12 Vegetable 2020-08-04 10:12:39 +08:00 ![]() 设计就是设计,这个设计并没有带来什么真正的问题,不像 js 的 var 一样反认知。 |
13 qdzzyb 2020-08-04 10:16:45 +08:00 这个还好吧 也没规定 for 一定要开启一个作用域吧 |
![]() | 14 msg7086 2020-08-04 10:17:10 +08:00 因为他是 Python,他不是其它语言。 而且 C++之前的 for 循环声明的变量也是会算在外作用域的,后来规范里明确要求放进内部作用域,才改成了现在这样。 |
![]() | 15 whoami9894 2020-08-04 10:22:59 +08:00 Python 只有 def, class, lambda, [i for i ...]会引入新作用域 |
17 yzqtdu 2020-08-04 10:26:39 +08:00 ![]() 这个问题我找到[一篇博客]( https://eli.thegreenplace.net/2015/the-scope-of-index-variables-in-pythons-for-loops/) 这应该是语言的设计问题,前阵子看的 plp 刚好讲了 for 循环的语义复杂性,不光有 index 的访问问题,还有循环体内修改 index 和结束标记的问题 |
![]() | 18 est 2020-08-04 10:31:33 +08:00 ![]() 循环都是单字母变量。如果你污染外边的「作用域」了说明你单字母变量是不是太多了? 23333333333 |
![]() | 19 vagrantear 2020-08-04 11:47:35 +08:00 ![]() 为什么别人设计的语言一定要遵循你的想法呢,你还能指望每个语言都一模一样? |
![]() | 20 Ehend 2020-08-04 11:49:57 +08:00 via Android 这就是我放弃 Python 的原因,但也方便不少,不喜欢就换 Java 或者 c++吧。ps:已经换 c++。 |
![]() | 21 wuwukai007 2020-08-04 11:50:00 +08:00 via Android for 后面可以写 else,拿到最后一个值做处理 |
![]() | 22 nnqijiu 2020-08-04 11:52:46 +08:00 为什么这么设计,你就得去问 python 开发者了 |
![]() | 23 Nich0la5 2020-08-04 12:08:31 +08:00 via Android 这叫语言特性 feature |
24 lolizeppelin 2020-08-04 14:11:08 +08:00 @est 你这样黑人太皮了呀 |
25 oooooooooooo 2020-08-04 14:33:00 +08:00 PHP 是不是 C 家族的? js 、php 、ruby .... 这些解释型语言,哪个有块级作用域???怎么就变成 Python 的 feature 了? |
![]() | 26 threebr 2020-08-04 14:41:35 +08:00 via Android 我还挺喜欢这个特性的,实现一些科学计算中的算法很方便 |
27 SergeGao 2020-08-04 14:51:20 +08:00 @oooooooooooo 杠一下,es6 的 let,const 引入了块级作用域.. |
![]() | 28 misaka19000 2020-08-04 14:56:42 +08:00 我喜欢这个特性 |
![]() | 29 lovecy 2020-08-04 15:16:07 +08:00 每个语言都有自己的变量作用域,你习惯性认为 for 和 while 里面是独立的块级作用域,就想让其他语言都这么做。。。 @oooooooooooo 对啊,C 家族挺多无块级作用域的 |
![]() | 30 0x4C 2020-08-04 15:22:44 +08:00 Python 的 for for <variable> in <sequence>: <statements> else: <statements> C++的 for for ( init; condition; increment ) { statement(s); } 具体不用说什么了吧 |
31 krixaar 2020-08-04 15:43:29 +08:00 ![]() 换个场景,现在有个 for 循环,需要知道 break 之前运行了多少次,该怎么写? 外面先 int counter = 0;,然后里面 counter++,还是直接看运行完之后 i 是多少更方便? i 想污染外面变量的前提是外面有变量叫 i 能污染,那能不能规避不就是写代码的你自己的问题了吗? |
32 yemoluo 2020-08-04 15:59:41 +08:00 |
33 midtin 2020-08-04 17:29:19 +08:00 ![]() 应该是因为 C 语言定义了 for 或 while 里面是一个代码块,所以有独立的作用域,而 Python 并没有把 for 和 while 视为一个代码块,没有给予独立的作用域。 实际上这个是语言特性,并不是什么设计缺陷, 譬如当业务需要在循环外检查中断原因时有很棒的作用。 而变量污染更多应该是靠人来规避的,别都甩锅给语言 |
![]() | 34 crella 2020-08-04 18:45:10 +08:00 via Android 我记得 ruby 的循环是相对独立的,循环中会改变上文的变量,但是下文无法识别 do;end 循环中的变量;下文可以识别 for in;end 循环中的变量。 真的很讨厌:‘’解释型语言的什么问题‘’一竿子打翻一船人的行为。 另外 ruby 的 for while until 循环可以不用打 do 字符。 |
![]() | 35 crella 2020-08-04 18:48:27 +08:00 via Android ruby 里好像很多语法都有相似而备用的用法,比如 define_method 用来穿透作用域,lambda {}’里支持显式 return 来支持复杂循环退出,等等。不过无法跨线程 catch 和 throw 让我不爽。 |
![]() | 36 northisland 2020-08-04 20:55:10 +08:00 ![]() 尝试解释一下, c++,很多情况下,变量名就是数据。 python 的变量名就是个名字,需要和数据绑定。 循环变量 i 的数据没有回收, 变量名没有回收, 绑定关系也没有回收, 当然可以访问 i 了。 看开点,语法不是重点~ |
![]() | 37 northisland 2020-08-04 20:57:30 +08:00 python 这语言浪的很 |
![]() | 38 fasionchan 2020-08-05 08:44:19 +08:00 @sixway 闭包不是问题的根源,相反它是受害者。循环没有独立作用域,会导致很多非预期的行为,在循环中定义的闭包函数就是典型的一例。Python 中变量的行为跟 Javascript 中 var 定义的变量一样,都是函数作用域。但 Javascript 后来引入 let 和 const 关键字,作用域缩小到代码块,这样闭包就不会有非预期行为了。 |