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
GeekHub
CNife
V2EX  ›  Python

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

  •  
  •   CNife · 50 天前 · 2667 次点击
    这是一个创建于 50 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    38 条回复    2020-08-05 08:44:19 +08:00
    0312birdzhang
        1
    0312birdzhang   50 天前
    这就是 Python.jpg
    domosekai
        2
    domosekai   50 天前 via Android
    解释语言的锅?
    zachlhb
        3
    zachlhb   50 天前 via Android
    变量名不要起成一样的
    sixway
        4
    sixway   50 天前
    https://mail.python.org/pipermail/python-ideas/2008-October/002124.html
    这里可以看见,应该是因为闭包的问题
    optional
        5
    optional   50 天前 via iPhone
    函数作用域是很多语言的特性啊,比如 c89 es5
    infun
        6
    infun   50 天前
    我记得之前查过说 因为 for 循环不是函数体,所以这里的变量与 for 是同级的,所以注意命名吧
    youthfire
        7
    youthfire   50 天前 via iPhone
    没有学过其他语言,所以我的作法是一段代码封装一个函数就隔离了
    DOLLOR
        8
    DOLLOR   50 天前   ❤️ 1
    js 的 var 声明也是这样的,是函数级作用域,会提升到函数开始处,不过后来有了 let 和 const 这两,用来声明块级作用域,不再有这种问题了。
    magiclx
        9
    magiclx   50 天前
    其实 for 循环结束后,如果你想知道 i 最后的值,能访问到 i 没什么不好的。
    其实是需要养成一个习惯,如果后面新使用 i 时,必须赋初始值。
    xiaolinjia
        10
    xiaolinjia   50 天前
    你这个问题确实是这样的,所以我一般用列表推导,就没这个问题( Py3 限定)
    [i for i in range(10)]
    print(i)
    NameError: name 'i' is not defined
    laike9m
        11
    laike9m   50 天前 via Android
    其实就是语言没设计好。。
    Vegetable
        12
    Vegetable   50 天前   ❤️ 4
    设计就是设计,这个设计并没有带来什么真正的问题,不像 js 的 var 一样反认知。
    qdzzyb
        13
    qdzzyb   50 天前
    这个还好吧 也没规定 for 一定要开启一个作用域吧
    msg7086
        14
    msg7086   50 天前
    因为他是 Python,他不是其它语言。
    而且 C++之前的 for 循环声明的变量也是会算在外作用域的,后来规范里明确要求放进内部作用域,才改成了现在这样。
    whoami9894
        15
    whoami9894   50 天前
    Python 只有 def, class, lambda, [i for i ...]会引入新作用域
    CNife
        16
    CNife   50 天前
    @magiclx 是这样的,但就像一个圆桌子上吃饭,别人都是右手,就你 Python 非要拿左手吃饭一样,硌人。
    yzqtdu
        17
    yzqtdu   50 天前   ❤️ 1
    这个问题我找到[一篇博客]( https://eli.thegreenplace.net/2015/the-scope-of-index-variables-in-pythons-for-loops/)
    这应该是语言的设计问题,前阵子看的 plp 刚好讲了 for 循环的语义复杂性,不光有 index 的访问问题,还有循环体内修改 index 和结束标记的问题
    est
        18
    est   50 天前   ❤️ 1
    循环都是单字母变量。如果你污染外边的「作用域」了说明你单字母变量是不是太多了? 23333333333
    vagrantear
        19
    vagrantear   50 天前   ❤️ 1
    为什么别人设计的语言一定要遵循你的想法呢,你还能指望每个语言都一模一样?
    Ehend
        20
    Ehend   50 天前 via Android
    这就是我放弃 Python 的原因,但也方便不少,不喜欢就换 Java 或者 c++吧。ps:已经换 c++。
    wuwukai007
        21
    wuwukai007   50 天前 via Android
    for 后面可以写 else,拿到最后一个值做处理
    nnqijiu
        22
    nnqijiu   50 天前
    为什么这么设计,你就得去问 python 开发者了
    Nich0la5
        23
    Nich0la5   50 天前 via Android
    这叫语言特性 feature
    lolizeppelin
        24
    lolizeppelin   50 天前
    @est
    你这样黑人太皮了呀
    oooooooooooo
        25
    oooooooooooo   50 天前
    PHP 是不是 C 家族的? js 、php 、ruby .... 这些解释型语言,哪个有块级作用域???怎么就变成 Python 的 feature 了?
    threebr
        26
    threebr   50 天前 via Android
    我还挺喜欢这个特性的,实现一些科学计算中的算法很方便
    SergeGao
        27
    SergeGao   50 天前
    @oooooooooooo 杠一下,es6 的 let,const 引入了块级作用域..
    misaka19000
        28
    misaka19000   50 天前
    我喜欢这个特性
    lovecy
        29
    lovecy   50 天前
    每个语言都有自己的变量作用域,你习惯性认为 for 和 while 里面是独立的块级作用域,就想让其他语言都这么做。。。
    @oooooooooooo 对啊,C 家族挺多无块级作用域的
    0x4C
        30
    0x4C   50 天前
    Python 的 for
    for <variable> in <sequence>:
    <statements>
    else:
    <statements>


    C++的 for
    for ( init; condition; increment )
    {
    statement(s);
    }

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

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

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

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

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

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

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


    看开点,语法不是重点~
    northisland
        37
    northisland   50 天前
    python 这语言浪的很
    fasionchan
        38
    fasionchan   49 天前
    @sixway 闭包不是问题的根源,相反它是受害者。循环没有独立作用域,会导致很多非预期的行为,在循环中定义的闭包函数就是典型的一例。Python 中变量的行为跟 JavaScript 中 var 定义的变量一样,都是函数作用域。但 JavaScript 后来引入 let 和 const 关键字,作用域缩小到代码块,这样闭包就不会有非预期行为了。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2626 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 15:24 · PVG 23:24 · LAX 08:24 · JFK 11:24
    ♥ Do have faith in what you're doing.