Python的变量作用域中分为四个级别,简称为:BGEL,作用域的级别依次升高,级别最高的是Local,如果该变量在Local中已经声明并赋值,将优先使用Local中的变量对应的值

Python变量作用域的四种情况:

B:build-in 系统固定模块里面的变量,也叫系统变量,比如int

G:global 全局变量,也就是模块级别定义的变量

E:enclosing 嵌套的父级函数的局部作用域,就是包含此函数的上层函数的局部作用域

L:local 局部作用域,即为函数中定义的变量

E和L是相对的,E中的变量相对上层来说也是L

Python的变量作用域可以用下图表示:

python system使用变量 python的系统变量_Python

一个变量使用哪个域中定义的值,取决于它的相对位置

从上图中看,如果从Local的位置向上看,最先看到的是Local中的变量,其次是Enclosing中的变量,再次是Global中的变量,最后才是Build-in变量

变量的取值取决于你在哪个位置,比如你在E和L中间,那同名的这个变量肯定是会向前看,取E中的值

Python作用域的产生

在Python中,没有块级作用域。只有模块(module),类(class)以及函数(def,lambda)才会引入新的作用域

1

2

3

4if True:

name = "PolarSnow"

print(name)

以上代码是可以正常运行的,但是在Java(有块级作用域)中运行就会报,变量未定义的错误

1

2

3

4

5def func():

name = "Polarsnow"

func()

print(name)

以上代码执行就会报错,name变量未定义

Python变量作用域

L(Local)1

2

3

4

5

6

7

8

9

10

11

12

13

14int = 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

13int = 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

12int = 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

10def func2():

def func():

print(int)

return func

f = func2()

f()

------------

int变量的值在LEG中都没有找到,但是int是内建变量,在系统中已经对其有定义,最后在B中,找到了int的值

如果一个变量在local中查找,查找到B中还没有找到,就会报变量未定义的错误

global & nonlocal 关键字

global

函数内部可以访问全局变量中的值,但是不能修改全局变量的值。如果需要修改,要加上global关键字

1

2

3

4

5

6

7

8

9name = "ps"

def readonly():

print("inner --->", name)

readonly()

------------

ps

函数内部是可以访问全局变量的值的,原则 –> GBEL

1

2

3

4

5

6

7

8

9

10

11name = "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

14def 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

10name = "123"

def func1():

print(name)

def func2():

name = "456"

func1()

func2()

最后会输出什么结果?

结果是: 123

1

2

3

4

5

6

7

8

9

10

11name = "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

11l = [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

5display list ---> [7, 8, 9, 10]

first element ---> 7

---------------

display list ---> [. at 0x101b7b730>, . at 0x101b7b7b8>, . at 0x101b7b840>, . 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

21lll = []

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

[, , , ]

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

18lllll = []

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]())

------------

[, , , ]

6

7

8

9

上面的代码仅仅小修改了一下,在func中执行一条赋值语句,结果就立刻不一样了,这次列表的第一个值变成了6

之前不是说代码运行到def就直接保存到内存,不执行了吗?没错,函数体是没有被执行,但是函数的参数是个赋值表达式,被Python解释器执行了,获取到了每个循环的x的值,并赋值给函数自己内部的变量(local)

当程序执行完毕,运行列表中第一个函数取值的时候,该函数取到的是函数内部的变量i,所以最后的结果和上面是截然不同的