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
0312birdzhang 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
infun 2020-08-04 09:40:11 +08:00
我记得之前查过说 因为 for 循环不是函数体,所以这里的变量与 for 是同级的,所以注意命名吧
|
7
youthfire 2020-08-04 09:40:23 +08:00 via iPhone
没有学过其他语言,所以我的作法是一段代码封装一个函数就隔离了
|
8
DOLLOR 2020-08-04 09:42:16 +08:00 1
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 Android
其实就是语言没设计好。。
|
12
Vegetable 2020-08-04 10:12:39 +08:00 4
设计就是设计,这个设计并没有带来什么真正的问题,不像 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 1
这个问题我找到[一篇博客]( 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 1
循环都是单字母变量。如果你污染外边的「作用域」了说明你单字母变量是不是太多了? 23333333333
|
19
vagrantear 2020-08-04 11:47:35 +08:00 1
为什么别人设计的语言一定要遵循你的想法呢,你还能指望每个语言都一模一样?
|
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 2
换个场景,现在有个 for 循环,需要知道 break 之前运行了多少次,该怎么写?
外面先 int counter = 0;,然后里面 counter++,还是直接看运行完之后 i 是多少更方便? i 想污染外面变量的前提是外面有变量叫 i 能污染,那能不能规避不就是写代码的你自己的问题了吗? |
32
GTim 2020-08-04 15:59:41 +08:00
|
33
midtin 2020-08-04 17:29:19 +08:00 1
应该是因为 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 1
尝试解释一下,
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 关键字,作用域缩小到代码块,这样闭包就不会有非预期行为了。
|