Python 中变量作用域是如何工作的?

4 投票
3 回答
1647 浏览
提问于 2025-04-16 00:31

这让我想更深入地了解Python的源代码,不过在StackOverflow上已经有很多人研究过这个,我很想听听他们的建议。

>>> import os
>>> def scope():
...     print os
...     import os
... 
>>> scope()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in scope
UnboundLocalError: local variable 'os' referenced before assignment

我觉得,当解析器读取文件时,它会自动为函数创建一个局部作用域,这样就让os和全局作用域“脱离”了关系。

这是真的吗?有没有人能告诉我在哪里可以找到更多关于作用域实现的资料?

补充:另外,这不仅仅是导入的特殊情况,普通变量也是这样。

3 个回答

0

在一个函数里面给变量赋值,就会把这个变量变成这个函数的局部变量。像importdefclass这些操作,在这个方面和赋值是一样的。

所以,当编译器处理你的文件时,它会创建一个局部变量os,这个局部变量和全局的os是分开的。

想了解更多信息,可以查看Python的教程,特别是第9.2节,链接在这里:http://docs.python.org/tutorial/classes.html

1

词法作用域是一种常见的概念,大多数设计良好的编程语言,无论是解释型还是编译型,都会使用它。

我有一段时间没有尝试这个了,但注意到下面这个很酷的“global”关键字:

o = 1
def foo():
    global o
    o = 2
foo()
print o

如果没有“global”这一行,对变量o的修改是局部的,这时“print o”会输出1。如果加上“global o”这一行,它会输出2。我们称这种方式为我的函数(没有global o)会有自己的变量。而上面的global就是一种特别请求,允许我们打破正常的词法作用域规则。

真正的词法作用域是Python 1.0所缺乏的,而Python从很早以前(如果我没记错的话,从1.6版本开始)就有了。那时候只有两种作用域,局部和全局,任何中间的作用域都是无法访问的。

5

当你调用 scope() 时,Python 发现你在这个方法里有一个叫 os 的局部变量(这是因为在 scope 里有个 import),所以这个局部的 os 会遮盖掉全局的 os。但是,当你说 print os 的时候,你还没有执行到那一行,也就是说局部的导入还没有发生,所以你会看到一个关于“在赋值之前引用”的错误。这里有几个其他的例子可能会对你有帮助:

>>> x = 3
>>> def printx():
...     print x # will print the global x
...
>>> def printx2():
...     print x # will try to print the local x
...     x = 4
...
>>> printx()
3
>>> printx2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in printx2
UnboundLocalError: local variable 'x' referenced before assignment

再回到你的 os 的例子。对 os 的任何赋值都会产生同样的效果:

>>> os
<module 'os' from 'C:\CDL_INSTALL\install\Python26\lib\os.pyc'>
>>> def bad_os():
...     print os
...     os = "assigning a string to local os"
...
>>> bad_os()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in bad_os
UnboundLocalError: local variable 'os' referenced before assignment

最后,比较这两个例子:

>>> def example1():
...     print never_used # will be interpreted as a global
...
>>> def example2():
...     print used_later # will be interpreted as the local assigned later
...     used_later = 42
...
>>> example1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in example1
NameError: global name 'never_used' is not defined
>>> example2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in example2
UnboundLocalError: local variable 'used_later' referenced before assignment

撰写回答