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

2021-12-16 14:55:34 +08:00
 dwlovelife
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

2807 次点击
所在节点    程序员
22 条回复
jxxz
2021-12-16 15:06:42 +08:00
a 相当于全局变量
x = a 的作用是创建一个局部变量 x ,这个 x 是 bar 内部的 x 不是 foo 里的 x ,你要用 foo 里的 x 要加上 nonlocal
rglee
2021-12-16 15:09:13 +08:00
此 x 非彼 x ,用 id()看一下?
ipwx
2021-12-16 15:10:58 +08:00
如果你在 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
2021-12-16 15:52:37 +08:00
赋值即重新定义, 所以 bar 里面的 x 是在 bar 作用域中的 x,不会影响到 foo 中的 x
foobear
2021-12-16 16:05:16 +08:00
https://pythontutor.com/visualize.html 把你的代码复制到上面跑一遍就明白了
Skiro
2021-12-16 17:20:05 +08:00
问题 1: 为什么在 bar 函数内能访问 a 呢
回答:因为 a 是全局变量。
问题 2: 能访问按道理能赋值啊,x 咋还是 0
回答:因为实际上赋值成功的是 bar()里的 x ,这里 x ==100 ,而你输出的是 foo()里的 x 。
jaredyam
2021-12-16 17:40:56 +08:00
前几层已经说得很明白的,问题 2 也不是 Python 的问题。
DOLLOR
2021-12-16 18:19:46 +08:00
python 里,等号不仅有赋值的作用,还有声明变量的作用,相当于 JS 的 var 。
比如 x=a ,其实干了两件事情,一是在当前作用域声明了一个叫 x 的变量,二是赋值为 a 。
只要声明了变量,当前作用域内无论哪个位置,都再也不能访问外部的同名变量。
ch2
2021-12-16 18:30:01 +08:00
bar 可以访问 foo 里的 x,但是 bar 里直接修改 x 不行
ila
2021-12-16 18:35:35 +08:00
用类或全局变量
vance123
2021-12-16 19:00:15 +08:00
是有意设计成这样的
函数引用自己作用域内不存在的变量时,可以报错,也可以顺着词法环境往上搜索,python 选择了后者。
函数在内部赋值时,可以选择对已有变量赋值,或者重新定义一个变量,python 选择了后者。
原则是选择最合乎逻辑的那一个方案
flyhelan
2021-12-16 19:02:57 +08:00
不如 在 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
2021-12-16 19:10:11 +08:00
@ipwx 我觉得这个反而是挺好的设计,explicitly mutate global 。
kidblg
2021-12-16 19:15:47 +08:00
进入 foo 之前,产生的作用域 1 内:a=100
进入 foo 后,产生的作用域 2 内:c=hello, x=0 ,而且因为函数内可以访问函数外的变量,所以可以访问 a=100
进入 bar 时,产生的作用域 3 内:b=true, x=a ,但 bar 退出时,就销毁这个新的作用域

所以 x 是 100
rationa1cuzz
2021-12-16 19:26:09 +08:00
关键词:作用域,局部变量与全局变量
拓展:形参 实参
xiaoxinshiwo
2021-12-16 19:26:29 +08:00
@foobear
powerman
2021-12-16 19:32:49 +08:00
懒得很,我从来都是把所有语言当成 类 C 语言的风格来写,根本不关心变量作用域
inframe
2021-12-16 22:09:57 +08:00
python 变量搜索顺序是 LEGB,
https://www.cnblogs.com/xuexianqi/p/13658528.html
zhaohehedola
2021-12-16 22:56:56 +08:00
外层变量 可读不可写
echoechoin
2021-12-17 10:04:11 +08:00
闭包

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/822607

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX