一文看懂global, nonlocal, local变量
Python中,变量是根据程序运行顺序进行的,比如函数外的变量,在函数内是可见的,但是可用,不可赋值。那么如何实现赋值呢?
利用global关键字进行。
在函数内,如果出现和函数外的变量同名变量,而且对这个变量进行了赋值操作,这个变量是局部变量,只对函数内可见。如果没有赋值操作,函数访问的是函数外的变量,此时如果你修改该变量将会提示报错,提示你在用这个变量前先进行赋值:UnboundLocalError: local variable 'x' referenced before assignment
也就是说,变量如果在函数内赋值,则这个变量是局部变量。而如果没有赋值操作,又没有加global关键字,使用的是外部的变量。而如果想对来自函数外部的变量进行操作,那么需要加global关键字。
在python内部,解释器引用变量的顺序是:1. 函数内的变量,2. 包含函数的区域 3.包含这个函数模块 4. 系统常用的比如len, str
# In[] ''' global nonlocal #local ''' x=10 def fun(): print(x) fun() # In[] ''' result: 10000 10 here x is a local variable, it is visible just in the function of fun(). It has no relationship with the x variable outside. ''' x=10 def fun(): x=10000 print(x) fun() print(x) # In[] ''' result: UnboundLocalError: local variable 'x' referenced before assignment you can do assignment to the variable x, and it will be a local variable. here the sentence 'x=x+1' is assignment but the x has not initialization. ''' x=10 def fun(): x=x+1 print(x) fun()
此时如果你的本意是对外部的变量进行修改操作,你只需要将这个变量设为global即可。
x=10 def fun(): global x x=x+1 print(x) fun() print(x)
另外,在函数嵌套中,也存在类似的问题。函数嵌套,一个函数内有另外一个函数。外部函数内的变量,对内部的函数是可见的。
def fun(x): def inner(y): print(x+y) return inner f = fun(10) f(1)
但是内部函数对外部函数内的变量是否可以修改呢?下面的代码会返回错误信息:UnboundLocalError: local variable 'x' referenced before assignment
def fun(x): def inner(y): # nonlocal x x=x+10 print(x+y) return inner f = fun(10) print(f) f(1)
但是当你把nonlocal这行注释取消了,就没有任何问题了。此时代码运行结果为21,10,说明在内部函数inner实现了变量x的增值x=x+10操作。这里变量x使用的是外部函数传入的参数,可以从程序里看到,此时外部的变量x在执行外函数调用后,并没有发生变化。外面打印x时,依然是10,而只有函数内部发生了变化。在函数fun中,x变量就是一个传入的参数,和外部的全局变量不一样的。
''' You can not do assignment with the parameter x above. But you can do this below.And it is not the same with global because the variable x outside do not change. The change just appear in the function. ''' x=10 def fun(x): def inner(y): nonlocal x x=x+10 print(x+y) return inner f = fun(x) f(1) print(x)
如果我用这个代码来写,可能会更加清晰了。就是说这里的nonlocal其实有点类似于前面的global的作用,只是范围不一样,但是达到的目的类似,就是使得更内一层的函数可以改变。
x=10 def fun(a): def inner(y): nonlocal a a=a+10 print(a+y) return inner f = fun(x) f(1) print(x)
nonlocal这个关键字出现在python3.x中,在python2是没有的。在python2中,可以通过可变对象类型比如list, dict来实现,内部函数对外部函数的变量进行改变。
''' But nonlocal appears in python 3.5 later. Without nonlocal you can only use list or dict, these variables is not like integer or str which can not vary. Here you are not doing assignment actually. ''' def fun(lst): def inner(y): lst.append(y) print(len(lst)) return inner lst = [] f = fun(lst) f(1) f(2) f(3) f(4)
这种做法,和以下做法一样。
def fun(): lst = [] def inner(y): lst.append(y) print(len(lst)) return inner f = fun() f(1) f(2) f(3) f(4)
但是如果是不可变对象,比如integer和str等就不可以了。python3引入nonlocal后,灵活多了。尤其是在闭包和装饰器中,使用非常灵活和方便。那么接下来的博客我将展开讲解闭包和装饰器。
好了,最后 思考题,通过这个题目,好好体会一下global和nonlocal的神奇。
x=10 def fun(a): global x x = 2 def inner(y): # global x nonlocal a a=a + 10 x=a + y print(x) return inner f = fun(10) f(1) print(x)
此时,inner函数内部,注释掉一句后,输出结果是:
21 2
但是当你把注释拿掉,结果是:
21 21
这就是global的神奇之处,它对一层函数有效,在内部的函数,如果你不加global,x=a+y时x就是一个函数内的局部变量。那么如果函数嵌套有几层,内层是不是还需要再加一层global呢,试试吧?