第11课有三个部分A、B、C,学习顺序不分先后。
在这节课中我们会解释一个叫做变量作用域的概念。大意就是使两个不同的变量,
在其作用域不同时,可以使用同一个名字。这将使编写和修改程序更加简单,尤其是那些庞大的程序。同时,它也是我们以后会学到的内容(递归)的一个重要组成部分。
变量作用域的示例
在这个示例中,,我们程序的一部分会有下面这个函数:
def square(x): value = x * x return value这是一个函数用来计算一个数
x
的平方,例如 square(5)
会返还25
。现在假设我们从程序的另一个部分调用square
函数,这个部分的程序为了另一个目的也使用了value 这个变量:
# 函数体从这里开始 value = 17 fivesquared = square(5) print(fivesquared, value)那么问题来了,在这种情况下程序会打印出什么?假设我们不知道Python是怎样工作的,那么有两种可能:
- 一种可能是Python觉得两个
value
变量应该被“分别保存”。那么,调用square
会返还25,但是value
在main主体中的值会保留在17,同时我们会看见输出25
17
。 - 另一种可能是当
square
被执行后,它将现有的value
的值改写为25, 所以我们会看到输出25
25
。
让我们看看到底发生了什么!
Python的运行实际上符合第一种。事实上, 每当你调用一个函数,你无法改变任何在函数外定义的变量。给变量赋值的任何语言只影响一个调用函数中的“局部”变量,它只调用函数的一个“内部”变量。Python和大多数其他语言这么做的原因是这样可以在编写大型程序、面对成千上百的函数时,让工作变得更加简单方便。具体来说,当我们定义一个新的函数时,我们可以在函数中使用任何变量名(包括常见的名称 result,
temp
和 i
) 即使他们在其他地方被用于不同的目的。
详细来看。在代码展示台的第六步中,你会注意到这里有两个不同的value
变量: 一个在square
函数中, 另一个在函数外面(在全局中)。
x = "外部" def xReplace(value): x = value xReplace("内部") print(x)
x = value
只会影响函数中局部类型的变量x
。全局类型的 x
不会改变(所以,函数 xReplace
并没有任何用处,也不会造成任何影响)。另一个和作用域类似的概念是命名空间;命名空间就像是一个包含作用域的包裹。所以即使你import
一个包裹 (例如 math
),在一些地方用到变量名x
,它也不会与你的程序使用的变量x
重叠, 因为它们在不同的命名空间里。
作用域规则:向外展望
有些情况下,我们想混合局部和全局的变量。一个常见的例子是在程序开始时有一些常见的初始化变量,但你也希望你的函数能够读取这些全局变量。
在这里, 局部作用域并不含有一个叫作favouriteTopping
的变量,但是这并不是造成错误的原因。Python评估变量的规则是,如果一个不在局部作用域里的变量需要被评估,那么它会在全局作用域中寻找此变量。在我们的案例中,它确实在全局中找到了favouriteTopping
(在通常情况下,用一个函数体调用另一个函数你会碰到三层作用域;Python总是先检查“最局部的”,接着朝全局作用域“一步一次”的前进,直到发现这个变量。)。
上面的两个例子,orderPizzas 和xReplace 在语法上基本相同。为什么Python在xReplace 中创建了一个新的局部变量x ,但没有在 orderPizzas 中创建新的favouriteTopping ?Python的规则如下:如果你只读取这个变量,那么没有新的局部变量会被创建;但是你哪怕只是对这个变量写入了一次,这个变量也会在函数体中被当成局部变量对待(这是造成下面这种情况的最常见原因 UnboundLocalError : 请看Python官方文档中的这两个问题。[1, 2]) |
函数实参forever会被当作新的局部变量,下面的代码片段说明了这个规则(它在尝试重置变量g
至0
,但失败了)。
global
改变
和其他很多事一样, 上面描述的在99%的情况下都是正确的。但是在剩下的1%情况中,你可能需要尝试在一个函数中改变一个全球变量。你可以使用Python中的global
语句来完成这项任务。
这是一个对早期的xReplace
示例的修改;在一个函数中,声明一个变量为global
可以让你从函数内部更改全局值。
我们之前提到过, 读取一个全局变量不需要使用到global
语句; 只有在写入时需要用到。这就是这节课的内容!