Python 中变量作用域是如何工作的?
这让我想更深入地了解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 个回答
在一个函数里面给变量赋值,就会把这个变量变成这个函数的局部变量。像import
、def
和class
这些操作,在这个方面和赋值是一样的。
所以,当编译器处理你的文件时,它会创建一个局部变量os
,这个局部变量和全局的os
是分开的。
想了解更多信息,可以查看Python的教程,特别是第9.2节,链接在这里:http://docs.python.org/tutorial/classes.html
词法作用域是一种常见的概念,大多数设计良好的编程语言,无论是解释型还是编译型,都会使用它。
我有一段时间没有尝试这个了,但注意到下面这个很酷的“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版本开始)就有了。那时候只有两种作用域,局部和全局,任何中间的作用域都是无法访问的。
当你调用 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