Python变量作用域详解

数据分析学习社
数据分析学习社
数据分析学习社
348
文章
0
评论
2021-06-2210:56:59 评论 38 4664字
摘要

在编程语言中,变量的作用域是一个非常需要注意的知识点。当我们在定义函数的时候,不可避免的需要在函数中定义相应的变量以使用。而当这些在函数中定义的变量名与函数外定义的变量名重名时,我们这些变量在使用的过程中,可能会对函数的输出造成相应的影响。同时,我们又如何对函数中定义的变量进行相应的修改与使用?本文将通过大量示例,对全局变量和局部变量进行详细的解释。

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

更多文章前往首页浏览http://www.itongji.cn/

  • 我的微信公众号
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: