V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dwlovelife
V2EX  ›  程序员

最近学 Python ,关于作用域的问题有点不明白

  •  
  •   dwlovelife · 2021-12-16 14:55:34 +08:00 · 2781 次点击
    这是一个创建于 1077 天前的主题,其中的信息可能已经有所发展或是发生改变。
    def foo():
        c = "hello"
        x = 0
        def bar():
            b = True
            print(a)
            print(b)
            print(c)
            x = a
        bar()
        print(x)
    
    
    if __name__ == "__main__":
        a = 100
        foo()
    

    结果就是: 100 True hello 0

    问题 1: 为什么在 bar 函数内能访问 a 呢 问题 2: 能访问按道理能赋值啊,x 咋还是 0

    22 条回复    2022-03-03 17:21:50 +08:00
    jxxz
        1
    jxxz  
       2021-12-16 15:06:42 +08:00   ❤️ 2
    a 相当于全局变量
    x = a 的作用是创建一个局部变量 x ,这个 x 是 bar 内部的 x 不是 foo 里的 x ,你要用 foo 里的 x 要加上 nonlocal
    rglee
        2
    rglee  
       2021-12-16 15:09:13 +08:00 via Android   ❤️ 3
    此 x 非彼 x ,用 id()看一下?
    ipwx
        3
    ipwx  
       2021-12-16 15:10:58 +08:00   ❤️ 1
    如果你在 foo 里面 a = 200 ,然后在 foo() 之后 print(a),你会发现 foo 里面的 a 和外面的 a 就不是一个 a 了。

    我觉得这是 Python 让人不满意的地方。因为没有声明,赋值即声明,所以会搞不清楚作用域。

    事实上如果你运行如下代码:

    def main():
    ....print(a)
    ....a = 1

    a = 2
    main()

    你会得到一个异常:

    UnboundLocalError: local variable 'a' referenced before assignment

    原因是 a = 1 这句话在 main 函数里面定义了一个变量 a ,因此你在 print(a) 这一行就引用不到全局的 a 了。
    ddmasato
        4
    ddmasato  
       2021-12-16 15:52:37 +08:00   ❤️ 1
    赋值即重新定义, 所以 bar 里面的 x 是在 bar 作用域中的 x,不会影响到 foo 中的 x
    foobear
        5
    foobear  
       2021-12-16 16:05:16 +08:00   ❤️ 5
    https://pythontutor.com/visualize.html 把你的代码复制到上面跑一遍就明白了
    Skiro
        6
    Skiro  
       2021-12-16 17:20:05 +08:00 via Android   ❤️ 1
    问题 1: 为什么在 bar 函数内能访问 a 呢
    回答:因为 a 是全局变量。
    问题 2: 能访问按道理能赋值啊,x 咋还是 0
    回答:因为实际上赋值成功的是 bar()里的 x ,这里 x ==100 ,而你输出的是 foo()里的 x 。
    jaredyam
        7
    jaredyam  
       2021-12-16 17:40:56 +08:00   ❤️ 1
    前几层已经说得很明白的,问题 2 也不是 Python 的问题。
    DOLLOR
        8
    DOLLOR  
       2021-12-16 18:19:46 +08:00 via Android   ❤️ 1
    python 里,等号不仅有赋值的作用,还有声明变量的作用,相当于 JS 的 var 。
    比如 x=a ,其实干了两件事情,一是在当前作用域声明了一个叫 x 的变量,二是赋值为 a 。
    只要声明了变量,当前作用域内无论哪个位置,都再也不能访问外部的同名变量。
    ch2
        9
    ch2  
       2021-12-16 18:30:01 +08:00   ❤️ 1
    bar 可以访问 foo 里的 x,但是 bar 里直接修改 x 不行
    ila
        10
    ila  
       2021-12-16 18:35:35 +08:00 via Android   ❤️ 1
    用类或全局变量
    vance123
        11
    vance123  
       2021-12-16 19:00:15 +08:00 via Android   ❤️ 2
    是有意设计成这样的
    函数引用自己作用域内不存在的变量时,可以报错,也可以顺着词法环境往上搜索,python 选择了后者。
    函数在内部赋值时,可以选择对已有变量赋值,或者重新定义一个变量,python 选择了后者。
    原则是选择最合乎逻辑的那一个方案
    flyhelan
        12
    flyhelan  
       2021-12-16 19:02:57 +08:00   ❤️ 1
    不如 在 x = a 后 再加一行 print(x) 你体会一下。bar 里的 x 和 foo 里的 x 不是同一个 x 。

    def foo():
    c = "hello"
    x = 0
    def bar():
    b = True
    print(a)
    print(b)
    print(c)
    x = a
    print(x)
    bar()
    print(x)


    if __name__ == "__main__":
    a = 100
    foo()
    Buges
        13
    Buges  
       2021-12-16 19:10:11 +08:00 via Android   ❤️ 1
    @ipwx 我觉得这个反而是挺好的设计,explicitly mutate global 。
    kidblg
        14
    kidblg  
       2021-12-16 19:15:47 +08:00   ❤️ 1
    进入 foo 之前,产生的作用域 1 内:a=100
    进入 foo 后,产生的作用域 2 内:c=hello, x=0 ,而且因为函数内可以访问函数外的变量,所以可以访问 a=100
    进入 bar 时,产生的作用域 3 内:b=true, x=a ,但 bar 退出时,就销毁这个新的作用域

    所以 x 是 100
    rationa1cuzz
        15
    rationa1cuzz  
       2021-12-16 19:26:09 +08:00   ❤️ 1
    关键词:作用域,局部变量与全局变量
    拓展:形参 实参
    xiaoxinshiwo
        16
    xiaoxinshiwo  
       2021-12-16 19:26:29 +08:00   ❤️ 1
    @foobear
    powerman
        17
    powerman  
       2021-12-16 19:32:49 +08:00   ❤️ 1
    懒得很,我从来都是把所有语言当成 类 C 语言的风格来写,根本不关心变量作用域
    inframe
        18
    inframe  
       2021-12-16 22:09:57 +08:00
    python 变量搜索顺序是 LEGB,
    https://www.cnblogs.com/xuexianqi/p/13658528.html
    zhaohehedola
        19
    zhaohehedola  
       2021-12-16 22:56:56 +08:00 via iPhone
    外层变量 可读不可写
    echoechoin
        20
    echoechoin  
       2021-12-17 10:04:11 +08:00
    闭包
    NanFengXiangWan
        21
    NanFengXiangWan  
       2021-12-17 12:24:48 +08:00
    因为变量 a 是全局作用域
    ![屏幕截图 2021-12-17 122430.png]( https://nf-1252257903.cos.ap-chengdu.myqcloud.com/2021/12/17/1a7583baaf637.png)
    flyhelan
        22
    flyhelan  
       2022-03-03 17:21:50 +08:00
    <section id="nice" data-tool="mdnice 编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; color: black; padding: 0 10px; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"><pre class="custom" data-tool="mdnice 编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url( https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">def</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">foo</span><span class="hljs-params" style="line-height: 26px;">()</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;c&nbsp;=&nbsp;<span class="hljs-string" style="color: #98c379; line-height: 26px;">"hello"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;x&nbsp;=&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">def</span>&nbsp;<span class="hljs-title" style="color: #61aeee; line-height: 26px;">bar</span><span class="hljs-params" style="line-height: 26px;">()</span>:</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;=&nbsp;<span class="hljs-literal" style="color: #56b6c2; line-height: 26px;">True</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(a)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(b)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(c)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x&nbsp;=&nbsp;a<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(x)&nbsp;<span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">#&nbsp;新增这条</span><br>&nbsp;&nbsp;&nbsp;&nbsp;bar()<br>&nbsp;&nbsp;&nbsp;&nbsp;print(x)<br><br><br><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span>&nbsp;__name__&nbsp;==&nbsp;<span class="hljs-string" style="color: #98c379; line-height: 26px;">"__main__"</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;=&nbsp;<span class="hljs-number" style="color: #d19a66; line-height: 26px;">100</span><br>&nbsp;&nbsp;&nbsp;&nbsp;foo()<br></code></pre>
    </section>
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3449 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 11:20 · PVG 19:20 · LAX 03:20 · JFK 06:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.