作用域(概念) |
在一个程序里,有可能在多个地方出现同样名字的变量,例如在所有函数之外用到一个全局变量,其名字是x,而函数f的参数中也有一个的名字是x。实际上这是两个变量,只是它们正好采用了同样的名字。 要准确理解程序的意义,需要确定程序里写出的每个名字实际上指的是那个变量(或其他东西)。而要严格地讨论变量名和变量的关系,就需要理解作用域的概念。 作用域一个变量名的作用域(scope)是指程序代码中的一个部分,在这段代码中这个变量名就表示了某个特定的变量。例如,一般说,函数的参数名的作用域就是这个函数的体,一个全局变量的作用域是整个文件,但有可能需要排除其作用范围内有同名局部变量定义的区域。 在 Python 里,一个变量被赋值,就是它有定义了,也有了名字约束。其第一次赋值的位置决定了它的作用域。函数参数的情况有些特殊,列在参数表里就是有了定义。 如果一个文件被作为主程序执行,其中在函数之外有定义的变量都是具有全局作用域的变量。如果该文件是作为模块被 import,有关情况见关于模块的讨论。 一个函数确定一个作用域,它的参数和所有局部变量以这个函数的体作为作用域。函数作用域嵌套在全局作用域里面。如果一个函数里有嵌套定义的函数,那些函数又确定了进一步嵌套的作用域。函数里还可能有更局部的作用域,下面讨论。 总之,程序里的作用域形成一种嵌套结构。如果程序里出现了一个变量的使用,就从这个使用位置向外逐层查找,确定其定义:它就是在从这个使用位置逐层向外找,最先找到的那个有定义的同名变量。 下面是一个例子: x = 3 z = 2 def f (x) : def g () : y = 5 ...x... ... def h () : def r () : ...y... ... ...z... ... y = 5 ...这里有全局变量的 x 和 z,而函数 f 又以 x 为参数,所以在 f 的体里出现的 x 永远不会是全局的 x(除非有用 global 说明的 x)。在函数 g 里有 y 的赋值,所以 g 的函数体里的 y 都是这个局部的 y,而其中用到的 x(假设没赋值)就是其外围函数 f 的参数 x。注意在 f 的体里有对 y 的赋值,这就定义了一个局部的 y。在 f 里嵌套在内层函数 h 里的函数 r 的体里使用了变量 y,如果它没有局部定义且在 h 里也没有定义,那么使用的就是 f 里定义的那个 y。在 h 的函数体里使用了 z,如果 h 和 f 里都没定义 z,用到就是全局的 z。 注1:函数里的 global 语句用于说明一个或几个变量是全局变量,它强行建立明确的变量使用关系。函数里的 nonlocal 语句说明一个或几个变量不是局部的,要求从当前函数的外围函数开始查找名字的定义(而不是从这个函数本身开始查找)。 注2:虽然按照 Python 的作用域规则,上面这么复杂的嵌套结构中虽然有很多重名情况,每个变量的使用都可以正确确定其定义。但在实际中这样的程序是不好的,因为它的意义很难理解。我们应尽可能减少程序里变量重名的情况,以便不影响程序的可读性。 Python 的作用域概念解释了可能出现的各种复杂情况,理解这些,不是为了让我们(作为程序员)去写包含复杂重名情况的嵌套作用域,而是为了:
其他作用域问题Python 里还有一些结构也引进了变量。包括: for 语句头部使用的循环变量,其作用域是这个 for 语句所在的函数。下面是一个例子:>>> def f () : print(i) for i in range(5) : print(i) print(i) >>> f() Traceback (most recent call last): File "这里出错,是因为系统认为第一个 print 语句使用了还没有赋值的 i。 在表等数据类型的生成式表示中的 for 片段里引进的变量,其作用域是这个生成式的基本表达式和位于这个 for 片段之后的那个部分。看下面两个例子: >>> [i1 + i2 for i1 in range(i2) for i2 in range(4)] Traceback (most recent call last): File "在第一个 for 片段里 i2 没有定义。但下面生成式没问题: >>> [i1 + i2 for i1 in range(4) for i2 in range(i1 + 2)] [0, 1, 1, 2, 3, 2, 3, 4, 5, 3, 4, 5, 6, 7]因为在第二个 for 片段里 i1 已经有定义了。 |
本页及相关页面(除另声明者外)由裘宗燕创建维护,可自由用于各种学习活动。其他使用需得到作者许可。 |