python变量(全局、局部):global、nonlocal、locals

全局变量在全局范围内起作用,局部变量仅在函数内部起作用。
python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量.

1. 局部作用域

局部作用域在def定义的函数体内。在函数体内声明的变量,默认都是局部变量,除非有特别说明,如全局变量的声明要用关键字global。

x = 50
def func(x):
    print('local x is ',x)
    x = 2
    print('changed local x to ',x)

func(30)
print('global x is:', x)
local x is  30
changed local x to  2
global x is: 50

上例中,函数内部定义的局部变量x不影响全局变量x

x = 100
def func():
    print(x)
func()
print(x)
100
100

上例中,函数内部仍可调用全局变量x

x = 100
def func():
    print(x)
    x = 2
    print(x)
func()
print(x)
UnboundLocalError: local variable 'x' referenced before assignment

上例报错了,因为函数内部仅可调用全局变量,不可对全局变量做更改。所以会提示局部变量x已经被赋值。

2. 全局变量

第一节最后一个例子报错,因为def内不能对全局变量做更改,那么想要在def内更改全局变量怎么办?可用global。

x = 100
def func():
    global x
    print(x)
    x = 2
    print(x)
func()
print(x)
100
2
2

可以看到,def内首先调用了全局变量x,故print为100,在声明global的前提下,对x进行修改,此时的修改会影响全局变量,故最终print(x)时,全局变量x已经变成了2
此外,全局时没有定义变量,局部时先global也会创建一个全局变量,如下

def fun(x):
    global a
    a = x +2
    return a
fun(3)
print(a)
5

讲到这里,思考以下两个代码:

x = 2
def func1():
    x = 3
    print(x)
x = 2
def func2():
    print(x)    
    x = 3

这两个代码分别有什么结果呢?答案如下:
python变量(全局、局部):global、nonlocal、locals_第1张图片
func1()没有报错,但是func()报错了。func1()先对x赋值,可认为函数体内的x为局部变量,和全局变量没有任何关系(因为此时函数体暂时没有全局变量x的信息),故print(x)时,打印的是局部变量x,函数体执行完毕,全局变量x仍然为2;
func2()先print(x),由于此时函数体内没有x,故向外层寻找,找到全局变量x,说明在函数体内先调用了全局变量,这时函数体内有了全局变量的信息,下面再在函数体遇到x,程序就认为该x是全局变量,因此x=3是对全局变量做了修改,而不是重新定义了一个局部变量并赋值为3。由于全局变量只能调用,不能更改,故程序报错。
回头再看第2个例子:

3. nonlocal

nonlocl用于闭包中,上述讲到在def内怎样对global进行更改,nonlocal就是在闭包中怎样对上一层函数内的变量进行更改。

x = 123
def outer():
    x = 100
    def inter():
        x = 200
    inter()
    print(x)
outer()
print(x)
100
123

可以看到闭包内的x不影响外层def内的x,同样的,闭包内的x和外层def内的x都不影响全局变量x

x = 123
def outer():
    x = 100
    def inter():
        print(x)
    return inter()
outer()

100

可以看到,闭包内没有x会先调用外层def的x,而不是模块中的全局变量x。故结果为100

x = 123
def outer():
    x = 100
    def inter():
        x = x + 1
        print(x)
    return inter()
outer()

UnboundLocalError: local variable 'x' referenced before assignment

类似第一节中最后一个案例,虽然闭包可以调用外层和全局变量,但是不能对其做更改,此时可以使用nonlocal对外层def内的x做更改

x = 123
def outer():
    x = 100
    def inter():
        nonlocal x
        x = x + 1
    print(x)
    return inter()
outer()
print(x)
101
123

可以看到,闭包内使用nonlocal,更改了外层def定义的x,故结果变为101,但是全局变量没有更改,仍为123。

x = 123
def outer():
    x = 100
    def inter():
        global x
        x = x + 1
        print(x)
    print(x)
    return inter()
outer()
print(x)
100
124
124

闭包内也可以对全局变量做更改,使用global即可

4. locals

locals():会以字典类型返回当前位置的全部局部变量

def runoob(arg):    # 两个局部变量:arg、z
    z = 1
    print (locals())    
runoob(4)
{'z': 1, 'arg': 4}

5. 小结

局部变量只能调用全局变量,同样的闭包变量只能调用外层def变量和全局变量,但是都不能对其进行修改,如果需要修改,要加global或者nonlocal,加上该词后,即使在内部修改,也会作用到外部。

6. 深度思考

思考以下代码:

a = []
def func():
    a.append(1)
    print(a)
func()
print(a)
[1]
[1]

上文讲了,函数体内只能调用全局变量,不能修改全局变量,为什么这个代码没有报错呢?以下为个人思考的原因(可能与官方原因不一样,仅是个人猜测):
我们知道python的数据结构分别可变类型和不可变类型。list为可变类型,str为不可变类型。(可变类型具体见博客https://blog.csdn.net/weixin_43178406/article/details/87460278)
对于str:以此为例

x = 100
def func():
    print(x)
    x = 2
    print(x)
func()
print(x)

x = 100这一代码实际是先申请了一块内存(假如叫该内存的名字为tom),值为100,该内存指向了x,故此时变量x为100。当函数体再对x重新赋值为2时(代码x=2),相当于修改了刚才申请的那块内存(tom)的值,我们知道字符串是不可变类型,即不能修改内存的值,故报错。平时我们可以看到代码

x = 2
x = 3

这个过程实际是先申请一块内存,值为2,指向了x,故变量x为2。后来又申请了一块内存,值为3,也指向了x,但是x只能有一个指向,后来的覆盖了前面的,故变量x变成了3。
在函数内调用完全局变量x(字符串)后,再对x做任何修改操作都是修改原来内存的值。而不是新创建一块内存,重新指向x。由于str的不可变性,故会报错。
对于list:a = [],先是申请了一块内存,该内存的值为[],且该内存指向了a,当a.ppend(1)时,是在这块内存上进行了修改,即内存的值由[]变成了[1]。由于a指代的是该块内存,故a变成了[1],即全局变量a也变成了[1]。由于list本身就是可变类型,即可以修改内存的值,所以程序不会报错。
再来看list的一个例子

a = [1, 2, 3, 4]
def func():
    a[0] = 100
func()
print(a)
[100, 2, 3, 4]

同样的原理,list的赋值操作也是修改内存,故不会报错
总结:函数体内对全局变量的修改,是修改全局变量指向的内存的值,而不是重新申请内存。

你可能感兴趣的:(python基础)