Python 的 for 和 while 循环为什么没有引入新的作用域? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
CNife
V2EX    Python

Python 的 for 和 while 循环为什么没有引入新的作用域?

  •  
  •   CNife 2020-08-04 09:25:56 +08:00 5699 次点击
    这是一个创建于 1905 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 为什么也继承了这个问题?

    1daydayde
        1
    1daydayde  
       2020-08-04 09:34:14 +08:00
    这就是 Python.jpg
    domosekai
        2
    domosekai  
       2020-08-04 09:34:23 +08:00 via Android
    解释语言的锅?
    zachlhb
        3
    zachlhb  
       2020-08-04 09:36:52 +08:00 via Android
    变量名不要起成一样的
    sixway
        4
    sixway  
       2020-08-04 09:37:16 +08:00
    https://mail.python.org/pipermail/python-ideas/2008-October/002124.html
    这里可以看见,应该是因为闭包的问题
    optional
        5
    optional  
       2020-08-04 09:39:48 +08:00 via iPhone
    函数作用域是很多语言的特性啊,比如 c89 es5
    GlobalNPC
        6
    GlobalNPC  
       2020-08-04 09:40:11 +08:00
    我记得之前查过说 因为 for 循环不是函数体,所以这里的变量与 for 是同级的,所以注意命名吧
    youthfire
        7
    youthfire  
       2020-08-04 09:40:23 +08:00 via iPhone
    没有学过其他语言,所以我的作法是一段代码封装一个函数就隔离了
    AV1
        8
    AV1  
       2020-08-04 09:42:16 +08:00   1
    js 的 var 声明也是这样的,是函数级作用域,会提升到函数开始处,不过后来有了 let 和 const 这两,用来声明块级作用域,不再有这种问题了。
    magiclx
        9
    magiclx  
       2020-08-04 09:56:55 +08:00
    其实 for 循环结束后,如果你想知道 i 最后的值,能访问到 i 没什么不好的。
    其实是需要养成一个习惯,如果后面新使用 i 时,必须赋初始值。
    xiaolinjia
        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
    laike9m
        11
    laike9m  
       2020-08-04 10:08:03 +08:00 via Androd
    其实就是语言没设计好。。
    Vegetable
        12
    Vegetable  
       2020-08-04 10:12:39 +08:00   4
    设计就是设计,这个设计并没有带来什么真正的问题,不像 js 的 var 一样反认知。
    qdzzyb
        13
    qdzzyb  
       2020-08-04 10:16:45 +08:00
    这个还好吧 也没规定 for 一定要开启一个作用域吧
    msg7086
        14
    msg7086  
       2020-08-04 10:17:10 +08:00
    因为他是 Python,他不是其它语言。
    而且 C++之前的 for 循环声明的变量也是会算在外作用域的,后来规范里明确要求放进内部作用域,才改成了现在这样。
    whoami9894
        15
    whoami9894  
       2020-08-04 10:22:59 +08:00
    Python 只有 def, class, lambda, [i for i ...]会引入新作用域
    CNife
        16
    CNife  
    OP
       2020-08-04 10:24:38 +08:00
    @magiclx 是这样的,但就像一个圆桌子上吃饭,别人都是右手,就你 Python 非要拿左手吃饭一样,硌人。
    yzqtdu
        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 和结束标记的问题
    est
        18
    est  
       2020-08-04 10:31:33 +08:00   1
    循环都是单字母变量。如果你污染外边的「作用域」了说明你单字母变量是不是太多了? 23333333333
    vagrantear
        19
    vagrantear  
       2020-08-04 11:47:35 +08:00   1
    为什么别人设计的语言一定要遵循你的想法呢,你还能指望每个语言都一模一样?
    Ehend
        20
    Ehend  
       2020-08-04 11:49:57 +08:00 via Android
    这就是我放弃 Python 的原因,但也方便不少,不喜欢就换 Java 或者 c++吧。ps:已经换 c++。
    wuwukai007
        21
    wuwukai007  
       2020-08-04 11:50:00 +08:00 via Android
    for 后面可以写 else,拿到最后一个值做处理
    nnqijiu
        22
    nnqijiu  
       2020-08-04 11:52:46 +08:00
    为什么这么设计,你就得去问 python 开发者了
    Nich0la5
        23
    Nich0la5  
       2020-08-04 12:08:31 +08:00 via Android
    这叫语言特性 feature
    lolizeppelin
        24
    lolizeppelin  
       2020-08-04 14:11:08 +08:00
    @est
    你这样黑人太皮了呀
    oooooooooooo
        25
    oooooooooooo  
       2020-08-04 14:33:00 +08:00
    PHP 是不是 C 家族的? js 、php 、ruby .... 这些解释型语言,哪个有块级作用域???怎么就变成 Python 的 feature 了?
    threebr
        26
    threebr  
       2020-08-04 14:41:35 +08:00 via Android
    我还挺喜欢这个特性的,实现一些科学计算中的算法很方便
    SergeGao
        27
    SergeGao  
       2020-08-04 14:51:20 +08:00
    @oooooooooooo 杠一下,es6 的 let,const 引入了块级作用域..
    misaka19000
        28
    misaka19000  
       2020-08-04 14:56:42 +08:00
    我喜欢这个特性
    lovecy
        29
    lovecy  
       2020-08-04 15:16:07 +08:00
    每个语言都有自己的变量作用域,你习惯性认为 for 和 while 里面是独立的块级作用域,就想让其他语言都这么做。。。
    @oooooooooooo 对啊,C 家族挺多无块级作用域的
    0x4C
        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);
    }

    具体不用说什么了吧
    krixaar
        31
    krixaar  
       2020-08-04 15:43:29 +08:00   2
    换个场景,现在有个 for 循环,需要知道 break 之前运行了多少次,该怎么写?
    外面先 int counter = 0;,然后里面 counter++,还是直接看运行完之后 i 是多少更方便?
    i 想污染外面变量的前提是外面有变量叫 i 能污染,那能不能规避不就是写代码的你自己的问题了吗?
    yemoluo
        32
    yemoluo  
       2020-08-04 15:59:41 +08:00
    midtin
        33
    midtin  
       2020-08-04 17:29:19 +08:00   1
    应该是因为 C 语言定义了 for 或 while 里面是一个代码块,所以有独立的作用域,而 Python 并没有把 for 和 while 视为一个代码块,没有给予独立的作用域。

    实际上这个是语言特性,并不是什么设计缺陷, 譬如当业务需要在循环外检查中断原因时有很棒的作用。

    而变量污染更多应该是靠人来规避的,别都甩锅给语言
    crella
        34
    crella  
       2020-08-04 18:45:10 +08:00 via Android
    我记得 ruby 的循环是相对独立的,循环中会改变上文的变量,但是下文无法识别 do;end 循环中的变量;下文可以识别 for in;end 循环中的变量。

    真的很讨厌:‘’解释型语言的什么问题‘’一竿子打翻一船人的行为。

    另外 ruby 的 for while until 循环可以不用打 do 字符。
    crella
        35
    crella  
       2020-08-04 18:48:27 +08:00 via Android
    ruby 里好像很多语法都有相似而备用的用法,比如 define_method 用来穿透作用域,lambda {}’里支持显式 return 来支持复杂循环退出,等等。不过无法跨线程 catch 和 throw 让我不爽。
    northisland
        36
    northisland  
       2020-08-04 20:55:10 +08:00   1
    尝试解释一下,

    c++,很多情况下,变量名就是数据。

    python 的变量名就是个名字,需要和数据绑定。
    循环变量 i 的数据没有回收,
    变量名没有回收,
    绑定关系也没有回收,
    当然可以访问 i 了。


    看开点,语法不是重点~
    northisland
        37
    northisland  
       2020-08-04 20:57:30 +08:00
    python 这语言浪的很
    fasionchan
        38
    fasionchan  
       2020-08-05 08:44:19 +08:00
    @sixway 闭包不是问题的根源,相反它是受害者。循环没有独立作用域,会导致很多非预期的行为,在循环中定义的闭包函数就是典型的一例。Python 中变量的行为跟 Javascript 中 var 定义的变量一样,都是函数作用域。但 Javascript 后来引入 let 和 const 关键字,作用域缩小到代码块,这样闭包就不会有非预期行为了。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5305 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 08:15 PVG 16:15 LAX 01:15 JFK 04:15
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86