变量的作用域
参考:https://blog.csdn.net/bylhjcsmmd/article/details/76372110
python的作用域并不是哪里都能访问的,分全局和局部的。
python变量的作用域大致分为已下四类:
- L (local) 局部作用域
- E (Enclosing) 闭包函数外的函数中
- G (Global) 全局作用域
- B (Built-in) 内建作用域
遵循LEGB原则
L->E->G->B
按照这样的顺序进行查找
局部变量不能修改全局变量
>>> a = 1
>>> def func1():
a +=1
>>> func1()
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
func1()
File "<pyshell#7>", line 2, in func1
a +=1
UnboundLocalError: local variable 'a' referenced before assignment
引入global,nonlocal
>>> a = 1
>>> def func2():
global a # 声明全局变量
a +=1
>>> func2()
>>> print(a)
2
>>> def wrapper():
x = 1
def inner():
x +=1
inner()
print(x)
>>> wrapper()
Traceback (most recent call last):
File "<pyshell#22>", line 1, in <module>
wrapper()
File "<pyshell#21>", line 5, in wrapper
inner()
File "<pyshell#21>", line 4, in inner
x +=1
UnboundLocalError: local variable 'x' referenced before assignment
>>> def wrapper():
x = 1
def inner():
nonlocal x # 声明非局部变量
x +=1
inner()
print(x)
>>> wrapper()
2
在python中,模块(module),类(class),函数(def,lambda)会产生新的作用域;
条件,控制语句,比如: (if...else) (for x in range(num)) (try...except...)
并不会改变变量的作用域。
重点理解
def add_end(L=[]):
L.append('END')
return L
函数的参数:
⑴如果变量L的指向对象变了,L才变
⑵如果变量L的指向对象没变,L不变
(就算指向对象自身的值发生了改变L也不变)
情况一:
>>> add_end([1,2,3])
[1, 2, 3, 'END']
>>> add_end(['a','b','c'])
['a', 'b', 'c', 'END']
情况二:
>>> def add_end(L=[]):
L.append('END')
return L
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
>>> x=y=[]
>>> x.append('x')
>>> x
['x']
>>> y # 也许你会想,y输出应该是[],but,you are wrong
['x']
# 为什么呢?
# 同样的代码,根据上下文的情景不同,会有不同的理解
# 我理解的是,这里调用的是x.append(),也就是x所指向的[]这个列表的方法
# 那么,实际发生修改的就是这个列表,
# so, y也指向这个列表,那么y=['x']
对比
>>> x=y=[]
>>> x=1
>>> x
1
>>> y
[]
# 对比这里y 输出 []
# 这里,就是上下文的情景不同,x=1,
# 直接修改的是x这个变量,直接给x赋值=1
# 并没有影响到变量x原来指向的[]这个空列表
# so, y=[]
# y变量指向的[]列表没有发生任何变化
升级
函数版
>>> def main():
lis = []
func1(lis)
# 通过传参,把非局部变量传给嵌套函数,
# so 内层函数就不用nonlocal声明,就可以修改
print(lis) # 查看修改后的lis
>>> def func1(lis):
lis.append('a')
>>> main()
['a']
类版
>>> def main():
lis = []
a = A(lis)
print(lis) # 最后打印,查看一下修改后的lis
>>> class A(object):
def __init__(self, lis):
self.lis = lis
self.lis.append('a')
# 经典之处在这,self.lis.append()
# 其实呢,self.lis = lis,
# 与我们最开始的 x=y=[] 十分相似
# 根据上下文的不同情景,同样的代码与不同的意义
# 其实修改的值lis这个列表,或者说lis指向的内存地址
>>> main()
['a']