Python作用域
Python的作用域
参考骏马金龙https://www.cnblogs.com/f-ck-need-u/p/9925021.html#blogaaa2
4大作用域
内置作用域dir(builtins)->全局作用域dir()->外部函数的本地作用域->嵌套函数内部的本地作用域
x=1
def f(i):
x=3
g()
def r():
x=2
print("r:",x)
print("f:",x) # 3
r()
def g():
print("g:",x) # 1
f(5)
print("main:",x)
g: 1
f: 3
r: 2
main: 1
作用域分析
处于全局作用域(模块级别)范围的变量有:x=1,f,g
处于f本地作用域的变量有:i,x=3,r
处于嵌套函数r的本地作用域的变量有:x=2
全局变量
关于python中的全局变量:
- 每个py文件(模块)都有一个自己的全局范围
- 文件内部顶层的,不在def区块内部的变量,都是全局变量
- def内部声明(赋值)的变量默认是本地变量,要想让其变成全局变量,需要使用global关键字声明
- def内部如果没有声明(赋值)某变量,则引用的这个变量是全局变量
global关键字
正常写法
相当于在zx中声明一个局部变量x=2,所以没改变全局变量
x = 3
def zx():
x = 2
zx()
print(x)
3
global
相当于在zx中把x声明为全局变量,所以可以直接在zx中操作改变全局变量的值
global就是声明变量的名称空间,告诉zx这个x是全局的,不是你的
x = 3
def zx():
global x
x = 2
zx()
print(x)
2
错误
这句话是错误的,而且是语法错误!
def f():
y=2
global y
全局变量是不安全的
当使用多线程的时候,全局变量是共享的,每个线程都能操作这个数据,所以是不安全的,需要自己加锁
模块全局变量
主要思路就是,模块导入只会执行一次,其他都是从内存中拿的,所以后面两次都是+2,都是拿的同一个模块对象
b.py
x=3
def f():
global x
x += 2
def f1():
x=4 # 本地变量
def f2():
x=4 # 本地变量
import b
b.x += 2 # 全局变量
def f3():
x=4 # 本地变量
import sys
glob = sys.modules['b']
glob.x += 2 # 全局变量
def test():
print("aaa",x) # 输出3
f();f1();f2();f3()
print("bbb",x)
a.py
import b
b.test()
aaa 3
bbb 9
超级错误案例解析
例1
x=1
def g():
print(x)
x=3
g()
print(x)
UnboundLocalError: local variable 'x' referenced before assignment
错误分析
python不是读一行运行一行的吗?这里为啥会报错?不是应该去打印全局作用域的x吗?
相信你也会有这些问题吧!首先我们来看看打印的错误。赋值前引用了局部变量x,说明并没有去全局找x,而是在本地拿了值,但是这个变量还没有赋值才出现的错误
原因:
函数的运行并不是我们想的那样的,每个函数属于一个区块,这个区块是一次性解释的,不是读就执行一行的,而是一直读完整个区块,再去执。当读完整个区块,记住了x=3这句话,将重新定义本地变量x,当执行print(x)的时候,区块告诉它我有x值,不用出去找了,于是print(x)信了,打印的时候发现没有值啊,区块骗我!
例2
x=3
def f1():
x += 3
print(x)
f1()
x += 3
UnboundLocalError: local variable 'x' referenced before assignment
原因:
这个错误和上面其实是一样的,把x+=3当成是x=x+3,继续上面思路,当读到x+=3的时候,它把它当做了一个普通的变量声明和赋值,然后进行区块执行的时候是先右边开始运行所以先运行x+1,这个时候x并没有值,所以出错了。
局部作用域
nonlocal关键字
nonlocal关键字类似global,但是它是局部名称空间相对于,局部名称空间中的名称空间
x=3
def f1():
x=4 # f1的本地变量
def f2():
x=5 # f2的本地变量
def f3():
nonlocal x # f2的本地变量
print("f3:",x) # 输出5
x=6
f3()
print("f2:",x) # 被修改,输出6
f2()
f1()
f3: 5
f2: 6
但是局部嵌套有多层的特点,他会一直向上找,但是不会找到全局作用域范围
x=3
def f1():
x=4
def f2():
def f3():
nonlocal x # f1()的本地
print("f3:",x) # 输出4
x=6 # 修改f1()的本地
f3()
print("f2:",x) # 输出6
f2()
print("f1:",x) # 输出6
f1()
f3: 4
f2: 6
f1: 6
在没有nonlocal的时候是怎么解决问题的
x=3
def f1():
x=4
def f2(x=x):
x += 3
print("f2:",x)
x=5
f2()
print("f1:",x)
f1()
f2: 7
f1: 5
来说一下fe()函数执行的过程,因为是函数,所以是个区块,先读完整个区块的代码,读到x=x就相当于,知道区块了一个 x变量,然后开始运行,给x变量赋值为4,f2中是有个 局部变量的
避免函数嵌套
嵌套函数
def f1():
x=3
def f2():
nonlocal x
print(x)
f2()
f1()
3
尽量写成
def f1():
x=3
f2(x)
def f2(x):
print(x)
f1()
3
循环内部函数
运行f1,f1中i=4,有一个n函数对象,当真正执行n函数,首先读一遍块区,知道n里面有一个i变量,然后执行print(i),才会去找i的值。当循环结束,n中的i指向i的最后一个元素i的地址
def f1():
for i in range(5):
def n():
print(i)
return n
f1()()
4
所以这里5个函数中的i都是指向最后一个元素i的地址
def f1():
list1 = []
for i in range(5):
def n(x):
return i+x
list1.append(n)
return list1
mylist = f1()
for i in mylist: print(i)
print(mylist[0](2))
print(mylist[2](2))
<function f1.<locals>.n at 0x02F93660>
<function f1.<locals>.n at 0x02F934B0>
<function f1.<locals>.n at 0x02F936A8>
<function f1.<locals>.n at 0x02F93738>
<function f1.<locals>.n at 0x02F93780>
6
6
如果我们就想要这种效果呢?那么就使用传参的方法
def f1():
list1 = []
for i in range(5):
def n(x,i=i):
return i+x
list1.append(n)
return list1
for i in f1():
print(i(3))
3
4
5
6
7