访问函数外的变量
我对Python还比较陌生,但对C++有点经验,所以下面这个代码示例让我有点困惑。
def foo():
y = x
print y
x = 5
foo()
运行这段代码会打印出值5。那么,变量x是怎么在foo()函数里面被知道的呢?在C++中,上面的代码是无法运行的,只有这样做才行:
#include <iostream>
int x = 5;
void foo()
{
std::cout << "x = " << x << std::endl;
}
int main()
{
foo();
return 0;
}
因为在这里,x是一个在全局范围内的变量,它是在foo()之前声明(和定义)的。在Python中,它能正常工作是因为x被添加到了全局符号表里吗?
谢谢大家的帮助!
4 个回答
据我所知,Python(就像Lua一样)把全局变量当作在你定义它们的时候就一直存在的东西。所以,当你定义了一个函数并把x设置为5后,Python认为这两个东西(函数和变量)都存在,并且一直都存在。当你调用foo()
的时候,它会在全局和局部的命名空间里找一个叫'x'的变量,结果会找到一个值为5的变量,然后把这个值打印出来。
希望你觉得这段代码有用。
def foo():
y = x # y becames local while x ..is found as a global
print "globals=", globals()
print "locals=", locals()
print y
x = 5 # here you declare variable x with global scope and with value 5
foo()
--------------------
$ python test.py
globals= {'__builtins__': <module '__builtin__' (built-in)>, '__file__': 'test.py', '__package__': None, 'x': 5, '__name__': '__main__', 'foo': <function foo at 0x7f0d4cbf05f0>, '__doc__': None}
locals= {'y': 5}
5
这两种语言的主要区别在于它们是如何查找变量的。
在C++中,代码是一次性编译的。当编译器读取代码中的foo
时,它看到x
,但还不知道这个标识符是什么。它可能是任何类型的变量,也可能是一个函数,或者只是个拼写错误。如果编译器之前没有看到x
的定义,也就是没有解释x是什么类型的东西(虽然不一定是它的实际值),那么它会立刻报错。
而在Python中,当看到x
时,它知道这就是一个名字,也就是一个标识符,因为它符合相应的标记类型,并且不是语言的关键字。在Python中,一切都是对象,这是真的——这也包括函数。Python在编译时没有类型检查,所以我们不在乎x
是整数、函数还是别的什么——这些都可以用同样的方式处理。(是的,你可以打印函数,但这不会给你任何有用的信息,比如原始代码;它只会给你一个包含类型、名称和对象ID的一些基本信息的占位符)。实际上,Python的变量没有类型(甚至没有像Haskell那样的隐式类型),只有Python的值才有类型。
另外,也没有检查x
是否真的存在的有效性检查,因为这也不可能——在Python中,有办法动态创建名字(一般来说,请不要这样做)。
不过,在编译时可以进行足够的分析来确定x
不是一个局部变量,因此Python会生成代码,指示“查找一个名为x
的全局变量并使用它”。任何由此产生的错误会在函数foo
实际运行时发生,以异常的形式出现。如果x
作为全局变量实际上不存在,它会引发NameError
;如果存在但对应的对象类型错误(通常对于print
来说是不可能的,但对于+
来说是可能的),则会引发TypeError
;如果类型正确但值无效,通常会引发ValueError
或其子类型(例如,试图用无效的数字索引访问list
会引发IndexError
,这是ValueError
的一个子类型)。
在全局范围内的所有东西都可以在函数内部读取。这是必须的:在Python中,指向变量的名字和指向函数的名字没有区别,所以如果这不成立,你甚至无法调用函数。
但是,如果你想修改变量x,就需要使用global这个关键词。
至于为什么在函数定义后变量也能正常工作:Python并不会在编译时就解决这些引用,而是在函数被调用时才会去处理。因为Python是动态的,所以事先无法知道一个变量是否已经定义。