1.示例
在对变量作用域进行讲解之前,我们先看一个简单的例子:
# 例1b = 6def f1(a): print(a) print(b)print(f1(3))输出:36None
# 例2b = 6def f1(a): print(a) print(b) b = 8print(f1(3))输出:3Traceback (most recent call last):UnboundLocalError: local variable "b" referenced before assignment
我们观察到例1的代码没有问题,但是例2中的代码print(b)却直接报错,报错提示:局部变量b在对其赋值前被引用了。
我们先给出原因分析:
Python编译函数的定义体时,它判断b是局部变量,因为在函数中给b赋值了。生成的字节码证实了这种判断,Python会尝试从本地环境中获取b。后面调用f2(3)时,f2的定义体会获取并打印局部变量a的值,但是尝试获取局部变量b的值时,发现b没有绑定值,即b没有赋值。
这不是缺陷,是设计选择:Python不要求声明变量,但是会假定在函数定义体中赋值的变量是局部变量。
看到这里,我们肯定已经想到了变量的作用域可以分为局部和全局,对应的变量我们可以称之为局部变量和全局变量。
在讲解具体的全局变量与局部变量相互转换的方法前(或者说如何让我们写出正确的代码),请允许我先对局部变量和全局变量进行介绍。
2.变量作用域
上一节中我们通过一个例子,引出了局部变量以及全局变量,那么什么是局部变量,什么是全局变量?
局部变量:在某个函数中声明的,只能在该函数中调用它。如果试图在超出范围的地方调用,则会造成程序报错。
全局变量:在整个py文件中声明的,全局范围内都可以访问。
# 全局变量b,局部变量ab= 10def test(): a = 1 print("test") print("a: ", a) print("b: ", b)print(test())print("b: ", b)print(a)输出:testa: 1b: 10Noneb: 10NameError: name "a"isnot defined
3.如何正确运用变量作用域
-
对1.示例中的程序进行修正:
# 如果在函数中赋值的时候想让解释器把b当成全局变量,要使用global声明。b = 6def f1(a): global b print(a) print(b) b = 8print(f1(3))print(b)输出:36None8
在上例中,我们在f1函数的定义体中加上了global b代码,通过关键字global,将局部变量变成全局变量。此时代码运行到print(b)时,会从整个py文件中往前寻找变量b的值,直到找到b。从这里就可以看到,即使我们添加了global b代码,我们也不能不在print(b)前面定义变量b。
def f1(a): global b print(a) print(b) b = 8print(f1(3))输出:NameError: name "b"isnot defined
-
在函数定义体中修改变量的值会被Python认为其为局部变量
# 示例1g_count = 0def global_test(): g_count = 1 print(g_count)global_test()输出:1# 示例2g_count = 0def global_test(): print(g_count)global_test()输出:0# 示例3# 直接报错,在global_test定义体中g_count提示unresolved referenceg_count = 0def global_test(): g_count += 1 print(g_count)global_test()输出:UnboundLocalError: local variable "g_count" referenced before assignment
在上例中,我们发现示例3直接报错,提示局部变量g_count在分配前被引用。为什么会造成这个错误?因为我们没有在global_test函数定义体中对局部变量g_count赋值,导致g_count += 1报错。需记住:"如果内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改。那么Python会认为它是一个局部变量"。我们可以通过两种方式将上面的示例3中的代码进行修改:
# 方式1:在函数定义体中对局部变量g_count进行赋值g_count = 0def global_test(): g_count = 10 g_count += 1 print(g_count)# 方式2:将局部变量变成全部变量,同时需要注意的是,该方式同时也对全局变量g_count进行了修改,其值变为1。而方式1不会对全局变量g_count进行修改g_count = 0def global_test(): global g_count g_count += 1 print(g_count)
-
nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量
def make_counter(): count = 0 def counter(): nonlocal count count += 1 return count return counterdef make_counter_test(): mc = make_counter() print(mc()) print(mc()) print(mc())make_counter_test()输出:123
其实在上例中,体现了闭包的概念。这里我们不做展开,后续再对闭包进行详细介绍。
-
函数定义体中的局部、全局变量
def scope_test(): def do_local(): spam = "local spam"# 此函数定义了另外的一个spam字符串变量,并且生命周期只在此函数内。此处的spam和外层的spam是两个变量,如果写出spam = spam + "local spam" 会报错 def do_nonlocal(): nonlocal spam # 使用外层的spam变量 spam = "nonlocal spam" def do_global(): global spam spam = "global spam" spam = "test spam" do_local() print("After local assignmane:", spam) do_nonlocal() print("After nonlocal assignment:", spam) do_global() print("After global assignment:", spam)scope_test()print("In global scope:", spam)输出:After local assignmane: test spamAfter nonlocal assignment: nonlocal spamAfter global assignment: nonlocal spamIn global scope: global spam
上例中需要的是do_global()的作用,注意下面的输出。
-
函数定义体中全局变量的修改
# 示例1def add_b(): global b b = 42 def do_global(): global b b = b + 10 print(b) do_global() print(b)add_b()print(b)输出:525252
上例中,在函数add_b内global定义的变量b,只能在函数do_global内引用,如果要在do_global内修改,必须在do_global函数里面声明global b,表明是修改外面的全局变量 b。
"在函数add_b内global定义的变量b,只能在函数do_global内引用"怎么理解?请看下例:
# 示例2def add_b(): global b b = 42 print(b)输出:NameError: name "b"isnot defined
那我们如何对上例进行修改?其实很简单,我们只需要在调用b之前,调用一下add_b即可。
# 示例3def add_b(): global b b = 42 add_b()print(b)输出:42
我们对上面的示例3进行修改,注释掉do_global函数定义体中的global b语句。发现程序又报错了。
# 示例4def add_b(): global b b = 42 def do_global(): #global b b = b + 10 print(b) do_global() print(b) add_b()输出:UnboundLocalError: local variable "b" referenced before assignment
具体原因还是和上面一样,do_global中定义的b又被当前的局部变量,而我们有没有对局部变量b进行赋值,所以报错。
-
嵌套函数中的global和nonlocal关键字
# 示例1def add_b(): b = 42 def do_global(): global b b = 10 print(b) do_global() print(b)add_b()print(b)输出:104210
# 示例2def add_b(): b = 42 def do_global(): nonlocal b b = 10 print(b) do_global() print(b)add_b()# print(b) 直接报错:NameError: name "b" is not defined输出:1010
从上面的示例1和示例2可以看出,嵌套函数中的global和nonlocal关键字的不同作用。示例1中global则会将b变成全局变量,从而在外面可以被调用。而示例2中的nonlocal只会让b在函数定义体中调用。
# 示例3def add_b(): # b = 42 def do_global(): nonlocal b b = 10 print(b) do_global() # print(b)add_b()输出:SyntaxError: no binding fornonlocal"b" found
即示例3中nonlocal b要绑定一个局部变量。即需要在add_b中以及do_global外定义b并进行赋值。
# 示例4def add_b(): # b = 42 def do_global(): global b b = 10 print(b) do_global() print(b)add_b()print(b)输出:101010
# 示例5def add_b(): #global b #b = 42 def do_global(): global b b = 10 print(b) do_global() b = b + 20# 这一行报错 print(b)add_b()输出:Traceback (most recent call last): b = b + 20UnboundLocalError: local variable "b" referenced before assignment
# 示例6def add_b(): #global b b = 42 def do_global(): global b b = 10 print(b) do_global() b = b + 20 print(b)add_b()输出:1062
4.总结
-
注意变量的作用域包括全局以及局部 -
Python引用变量的顺序:当前作用域局部变量>>外层作用域变量>>当前模块中的全局变量>>Python内置变量 -
注意嵌套函数中的global和nonlocal关键字
End.
作者:极值数据
本文为转载分享,如果涉及作品、版权和其他问题,请联系我们第一时间删除(微信号:lovedata0520)
- 我的微信公众号
- 微信扫一扫
- 我的微信公众号
- 微信扫一扫
评论