3.3.3 变量作用域

时间:2022-12-12 16:17:55

  变量起作用的代码范围称为变量的作用域,不同作用域内同名变量之间互不影响,就想不同文件夹的同名文件之间互不影响一样。一个变量在函数外部定义和在函数内部定义,其作用域是不同的,函数内部定义的变量一般为局部变量,在函数外部定义的变量为全局变量。

  在函数内部定义的普通变量只在该函数内起作用,当函数运行结束后,在其内部定义的局部变量将被自动删除而不可访问。在函数内部定义的全局变量当函数结束以后仍然存在并且可以访问。

  如果想要在函数内部修改一个定义在函数外的变量值,那么这个变量就不能是局部的,其作用域必须是全局的。可以在函数内部通过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是用于父函数和模块之间传递变量值的。这一点需要知道。