3.3.3 变量作用域
变量起作用的代码范围称为变量的作用域,不同作用域内同名变量之间互不影响,就想不同文件夹的同名文件之间互不影响一样。一个变量在函数外部定义和在函数内部定义,其作用域是不同的,函数内部定义的变量一般为局部变量,在函数外部定义的变量为全局变量。
在函数内部定义的普通变量只在该函数内起作用,当函数运行结束后,在其内部定义的局部变量将被自动删除而不可访问。在函数内部定义的全局变量当函数结束以后仍然存在并且可以访问。
如果想要在函数内部修改一个定义在函数外的变量值,那么这个变量就不能是局部的,其作用域必须是全局的。可以在函数内部通过global关键字来声明或定义全局变量,这分两种情况:
(1)一个变量已在函数外定义,如果在函数内需要修改这个变量的值,并将这个赋值结果反映到函数之外,可以在函数内部用global明确声明要使用已定义的同名全局变量。
(2)在函数内部直接使用global关键字将一个变量声明为全局变量,如果在函数外没有定义该全局变量,在调用这个函数之后,会自动增加新的全局变量。
或者说,也可以这样理解:
<1>在函数内如果值引用某个变量的值而没有为其赋新值,该变量为(隐式的)全局变量;
<2>如果在函数内任意位置有为变量赋值的操作,该变量即被认为是(隐式的)局部变量,除非在函数内显式地用global进行声明。
下面的代码演示了局部变量和全局变量的用法。
1 >>> def demo():
2 global x #声明或创建全局变量
3 x = 3 #修改全局变量的值
4 y = 4 #局部变量
5 print('全局变量:{} 局部变量:{}'.format(x,y))
6
7
8 >>> x = 5 #在函数外部定义了全局变量 x
9 >>> demo()
10 全局变量:3 局部变量:4
11 >>>
12 >>> x
13 3
14 >>> y #尝试着在函数外部访问一下函数的局部变量
15 Traceback (most recent call last):
16 File "<pyshell#11>", line 1, in <module>
17 y #尝试着在函数外部访问一下函数的局部变量
18 NameError: name 'y' is not defined
19 >>>
20 >>> del x #删除在函数外部定义的全局变量
21 >>>
22 >>> x #尝试访问一下全局变量 x
23 Traceback (most recent call last):
24 File "<pyshell#15>", line 1, in <module>
25 x #尝试访问一下全局变量 x
26 NameError: name 'x' is not defined
27 >>>
28 >>> demo() #再调用一次函数,该函数会声明全局变量哦
29 全局变量:3 局部变量:4
30 >>>
31 >>> x #在函数内部创建了全局变量x
32 3
33 >>>
34 >>> y #局部变量在函数调用结束后自动删除
35 Traceback (most recent call last):
36 File "<pyshell#21>", line 1, in <module>
37 y
38 NameError: name 'y' is not defined
39 >>>
如果局部变量与全局具有相同的名字,那么改局部变量会在自己的作用域内隐藏同名的全局变量,例如下面的代码所演示。
1 >>> def demo():
2 x = 3 #创建了局部变量,并自动隐藏了同名的全局变量
3 print(x)
4
5
6 >>> x = 5 #创建全局变量
7 >>> x
8 5
9 >>>
10 >>> demo()
11 3
12 >>> x #函数调用后,不影响全局变量 x 的值
13 5
14 >>>
15
16 # 个人理解:
17 # 局部变量的作用范围是函数体内,即使和全局变量同名了,在函数体内修改局部变量的值,也不会影响全局变量的值
18
19 # 在函数外修改全局变量的值,不会影响函数体内局部变量的值
最后,如果需要在同一个程序的不同模块之间共享全局变量,可以编写一个专门的模块来实现这一目的。例如,假设在模块A.py中有如下变量定义:
global_variable = 0
而在模块B.py中使用以下语句修改全局变量的值:
import A
A.global_variable = 1
在模块C.py中使用以下语句来访问全局变量的值:
import A
print(A.global_variable )
小提示:
(1)一般而言,局部变量的引用比全局变量速度快,应优先考虑使用;
(2)应尽量避免过多使用全局变量,因为全局变量会增加不同函数之间的隐式耦合度,降低代码可读性,并使得代码测试和纠错变得很困难。
拓展知识:局部变量的空间是在栈上分配的,而栈空间是由操作系统维护的,每当调用一个函数时,操作系统会为其分配一个栈帧,函数调用结束后立刻释放这个栈帧。因此,函数调用结束后,该函数内部所有的局部变量都不再存在。
拓展知识:除了局部变量和全局变量,Python还支持nonlocal关键字定义一种介于两者之间的变量。例如下面代码:
1 def scope_test():
2
3 def do_local():
4 spam = '我是局部变量'
5
6 def do_nonlocal():
7 nonlocal spam #这时要求spam必须是已存在的变量
8 spam='我不是局部变量,也不是全局变量'
9
10 def do_global():
11 global spam #如果全局作用域内没有spam,就自动创建一个
12 spam = '我是全局变量'
13
14 spam = '原来的值'
15 do_local()
16 print('局部变量赋值后:',spam)
17 do_nonlocal()
18 print('nonlocal变量赋值后:',spam)
19 do_global()
20 print('全局变量赋值后',spam)
21
22
23 scope_test()
24 print('全局变量:',spam)
25
26 #局部变量赋值后: 原来的值
27 #nonlocal变量赋值后: 我不是局部变量,也不是全局变量
28 #全局变量赋值后 我不是局部变量,也不是全局变量
29 #全局变量: 我是全局变量
30
31 #哈哈,感觉这个nonlocal就是用在嵌套函数中的,用于在子函数和父函数之间传递变量值的。而local是用于父函数和模块之间传递变量值的。这一点需要知道。