Python自学成才之路 彻底搞懂python变量作用域

时间:2022-07-23
本文章向大家介绍Python自学成才之路 彻底搞懂python变量作用域,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

python 作用域分成四种 L(Local):最内层,包含局部变量,比如一个方法内部。 E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。 G(Global):当前脚本的最外层,比如当前模块的全局变量。 B(Built-in):包含了内建的变量/关键字等。,最后被搜索

看个案例:

# 全局变量
num = 30

def outer():
    # 嵌套变量
    num = 20

    def inner():
        # 局部变量
        num = 10
        print(num)
    inner()

outer()

这个案例里面,第一个num是全局变量,第二个是局部变量,第三个是嵌套变量。内建变量这里没有,内建变量都是python系统内部定义好的变量。

变量的访问顺序遵循LEGB原则 L(局部)> E(内嵌)> G(全局)> B(内建)。这里的变量类型是一个相对的概念,第三个num相对于inner这个函数是局部变量,第二个num相对于inner是局部变量。所以是否是局部变量是相对于当前的作用域而言的。而能创建作用域的只有模块,类和函数。其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,比如下面的例子:

if True:
    num = 10
num+=1
print(num)

虽然变量有四种类型,但是访问一个变量可以简单的分为两种类型,局部变量和非局部变量。如果是局部变量,则在当前作用域访问的就是该局部变量,如果是非局部变量访问的就是离改作用域最近的局部变量,这个最近遵循LEGB原则。

看下面这个案例:

arg1 = 100
arg2 = 15

def funtion1():
    arg1 = 90
    arg2 = 10
    def inner_function1():
        print(arg1)
        arg2 = 99
        print(arg2)
        arg3 = arg1 + arg2
        print(arg3)
    inner_function1()

funtion1()

输出:
90
99
189

这个案例里面输出的arg1是非局部变量,根据LEGB原则arg1应该是90。arg2是局部变量,输出99。

再看下面这个案例:

# 全局变量
num = 30

def outer():
    # 局部变量
    num = 20

    def inner():
        # 嵌套变量
        num = num + 1
        print(num)

    inner()

outer()

这个案例会输出什么?什么都不会输出,直接报错,语法都过不了。如果改成下面这个样子就不会报错。

# 全局变量
num = 30

def outer():
    # 局部变量
    num = 20

    def inner():
        # 嵌套变量
        num = 1
        print(num)

    inner()

outer()

原因就在num = num + 1这里,如果是num = 1 ,那num是局部变量。同样num = num + 1这里num也是局部变量,但是局部变量还没任何值,然后又做加1操作,语法上就是错误的。那如何让num = num + 1有效呢?可以使用nonlocal。nonlocal的使用后面会介绍。

上面介绍的都是脚本里面的局部变量,类里面的变量和脚本不一样。看下面这个案例:

# 全局变量
num = 10

class GlobalDemo(object):

    num = 15

    def __init__(self, num):
        self.num = num

    def add(self):
        global num
        num += 1

    def getTotal(self):
        return num

globalDemo = GlobalDemo(1)
globalDemo.add()
print(globalDemo.getTotal())

输出:
11

上面这个案例最后输出结果是11,因为返回的num是全局变量。如果getTotal方法改成下面这种形式:

    def getTotal(self):
        return self.num

输出结果是1,这个时候访问的是示例属性,再改成下面这种形式:

    def getTotal(self):
        return GlobalDemo.num

输出结果是15,这个时候访问的是类属性。

类里面访问的变量判断方法其实和脚本是一样的,但是首先要把实例属性和类属性区分出来,有self的是实例属性,带‘类名.’的是类属性(可以看我前面的文章),如果既不是实例属性也不是类属性,那访问判断方式和脚本是一样的。

global和nonlocal关键字 如果想要指定访问的变量是全局变量可以使用global关键字

num = 10

def myFunction():
    
    global num
    num = num + 1

myFunction()
print(num)

输出:
11

如果是在函数嵌套里面想要使用外层函数的变量,则要用nonlocal关键字。

# 全局变量
num = 30

def outer():
    # 局部变量
    num = 20

    def inner():
        # 嵌套变量
        nonlocal num
        num = num + 1
        print(num)

    inner()

outer()

输出:
21

这里如果把nonlocal改为global输出的结果就是31。所以global和nonlocal的区别就是,global是争对全局变量,nonlocal是争对嵌套函数里面的上级变量。

总结一下:访问一个变量前需要先知道这个变量是什么变了,局部还是非局部,如果是非局部,则根据LEGB原则访问的就是最近的非局部变量。如果想指定访问的是哪个变量,全局可以使用global关键字,嵌套函数上级变量可以使用nonlocal关键字。