Python的变量作用域中分为四个级别,简称为:BGEL,作用域的级别依次升高,级别最高的是Local,如果该变量在Local中已经声明并赋值,将优先使用Local中的变量对应的值
Python变量作用域的四种情况:
B:build-in 系统固定模块里面的变量,也叫系统变量,比如int
G:global 全局变量,也就是模块级别定义的变量
E:enclosing 嵌套的父级函数的局部作用域,就是包含此函数的上层函数的局部作用域
L:local 局部作用域,即为函数中定义的变量
E和L是相对的,E中的变量相对上层来说也是L
Python的变量作用域可以用下图表示:
一个变量使用哪个域中定义的值,取决于它的相对位置
从上图中看,如果从Local的位置向上看,最先看到的是Local中的变量,其次是Enclosing中的变量,再次是Global中的变量,最后才是Build-in变量
变量的取值取决于你在哪个位置,比如你在E和L中间,那同名的这个变量肯定是会向前看,取E中的值
Python作用域的产生 在Python中,没有块级作用域。只有模块(module),类(class)以及函数(def,lambda)才会引入新的作用域
1 2 3 4 if True : name = "PolarSnow" print(name)
以上代码是可以正常运行的,但是在Java(有块级作用域)中运行就会报,变量未定义的错误
1 2 3 4 5 def func () : name = "Polarsnow" func() print(name)
以上代码执行就会报错,name变量未定义
Python变量作用域 L(Local) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int = 7 def func2 () : int = 6 def func () : int = 5 print(int) return func f = func2() f() ------------ 5
LEG都对int变量自定义了值,但是最后取得是L中的值
E(Enclosing) 1 2 3 4 5 6 7 8 9 10 11 12 13 int = 7 def func2 () : int = 6 def func () : print(int) return func f = func2() f() ------------ 6
在local中取值,但是local中没有,就会去E里面找
G(Global) 1 2 3 4 5 6 7 8 9 10 11 12 int = 7 def func2 () : def func () : print(int) return func f = func2() f() ------------ 7
L和E中都没有找到int的值,继续向上找,在G中找到了int的值
B(Build-in) 1 2 3 4 5 6 7 8 9 10 def func2 () : def func () : print(int) return func f = func2() f() ------------ <class 'int '>
int变量的值在LEG中都没有找到,但是int是内建变量,在系统中已经对其有定义,最后在B中,找到了int的值
如果一个变量在local中查找,查找到B中还没有找到,就会报变量未定义的错误
global & nonlocal 关键字 global 函数内部可以访问全局变量中的值,但是不能修改全局变量的值。如果需要修改,要加上global关键字
1 2 3 4 5 6 7 8 9 name = "ps" def readonly () : print("inner --->" , name) readonly() ------------ ps
函数内部是可以访问全局变量的值的,原则 –> GBEL
1 2 3 4 5 6 7 8 9 10 11 name = "ps" def readonly () : global name name = "PolarSnow" readonly() print(name) ------------ PolarSnow
加上了global关键字,就可以修改全局变量的值
nonlocal global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量就需要nonlocal关键字了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def outer () : count = 10 def inner () : nonlocal count count = 20 print("inner ---> " , count) inner() print("outer --->" , count) outer() ------------ 20 20
变量作用域中的疑难杂症 1 2 3 4 5 6 7 8 9 10 name = "123" def func1 () : print(name) def func2 () : name = "456" func1() func2()
最后会输出什么结果?
结果是: 123
1 2 3 4 5 6 7 8 9 10 11 name = "123" def func1 () : print(name) def func2 () : name = "456" return func1 ret = func2() ret()
结果是: 123
上面两段代码的结果都是全局变量中的值123
,这里需要特别注意的是,变量作用域在函数执行之前就已经确定了!
Python作为解释型的语言,代码从上至下执行,遇到def时,就将其函数体保存在内存的堆区,把对应的函数名保存在栈区并指向堆区的函数体,在函数执行之前,这个函数中的变量作用域就已经被记录到内存中了,不会因为后期的调用或嵌套而更改
1 2 3 4 5 6 7 8 9 10 11 l = [x + 1 for x in range(10 ) if x > 5 ] print("display list --->" , l) print("first element --->" , l[0 ]) print("---" * 5 ) ll = [lambda :x for x in range(10 ) if x > 5 ] print("display list --->" , ll) ret = ll[0 ]() print("first element --->" , ret)
结果是:
1 2 3 4 5 display list ---> [7, 8, 9, 10] first element ---> 7 --------------- display list ---> [<function <listcomp>.<lambda> at 0x101b7b730>, <function <listcomp>.<lambda> at 0x101b7b7b8>, <function <listcomp>.<lambda> at 0x101b7b840>, <function <listcomp>.<lambda> at 0x101b7b8c8>] first element ---> 9
问题来了,问什么使用了lambda之后,第一个元素变成9了呢?
我们把这个问题拆解一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 lll = [] for x in range(10 ): if x > 5 : lll.append(x + 1 ) print(lll) print(lll[0 ]) llll = [] for x in range(10 ): if x > 5 : def func () : return x llll.append(func) print(llll) print(llll[0 ]()) ------------ [7 , 8 , 9 , 10 ] 7 [<function func at 0x10137b620 >, <function func at 0x10137b6a8 >, <function func at 0x10137b730 >, <function func at 0x10137b7b8 >] 9
为什么 x+1 时第一个元素 = 7, 而 lambda :x 时第一个元素 = 9了呢
原因就是Python程序在解释道lambda的时候,并没有执行里面的代码,而是直接放到的了内存中,随着外侧循环的结束,x的值已经变成了9,此时再把内存里保存的lambda函数拿出来执行,x变量获取到的就是当前已经变成9的这个值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 lllll = [] for x in range(10 ): if x > 5 : def func (i = x) : return i lllll.append(func) print(lllll) print(lllll[0 ]()) print(lllll[1 ]()) print(lllll[2 ]()) print(lllll[3 ]()) ------------ [<function func at 0x101b7b620 >, <function func at 0x101b7b6a8 >, <function func at 0x101b7b730 >, <function func at 0x101b7b7b8 >] 6 7 8 9
上面的代码仅仅小修改了一下,在func中执行一条赋值语句,结果就立刻不一样了,这次列表的第一个值变成了6
之前不是说代码运行到def就直接保存到内存,不执行了吗?没错,函数体是没有被执行,但是函数的参数是个赋值表达式,被Python解释器执行了,获取到了每个循环的x的值,并赋值给函数自己内部的变量(local)
当程序执行完毕,运行列表中第一个函数取值的时候,该函数取到的是函数内部的变量i,所以最后的结果和上面是截然不同的