作用域和闭包
作用域
python作用域(重要概念)
python中的作用域分为全局作用域和局部作用域
当我们定义一个函数时,函数之内的函数就是局部作用域
局部作用域中的变量成为局部变量,函数之外就是全局作用域
全局作用域中的变量就是全局变量
作用域的规则:
1、全局作用域的变量,在程序被销毁的时候,全局变狼就会被丢弃
2、局部作用域的变量,在局部运行完后,局部变量也就会被丢弃。
3、全局作用域代码不能访问局部变量,而局部作用域中的代码可以访问全局变量
4、不同局部作用域中的代码不能使用其他局部作用域中的变量
5、不同作用域中,可以使用相同的变量名来表示不同得到变量。
隐式创建局部变量
示例:
b = 10
def f2(a):
print(a)
print(b)
b = 6
f2(3)
输出 :3
b会报错。会被判断成局部变量。因为变量b在函数体内进行了赋值操作
主要是因为Python不要求使用特定的语法进行变量声明,所有它假定在函数定义体中的赋值的变量是局部变量
怎样判断以上所说的内容是正确的呢?
引出反汇编Python函数字节码
示例:from dis import dis
dis(f2)
得出两个关键词 :
LOAD_FAST(局部变量)
LOAD_GLOBAL(全局变量)
可变对象与不可变对象:
示例:
b = []
def f3(a):
print(a)
print(b)
b.append(1)
f3(3)
s输出:3
以上不会报错
解释:此时的变量b指向的是一个列表,利用了列表是可变对象这个特性,而python只会将局部作用域中的赋值操作认为是创建局部变量。我们需要改变不可变对象,除了为变量赋新值,别无他法。
global关键字
如果在函数中赋值时,想让python解释器把变量认为是全局变量,则使用global进行声明。
示例:
b = 10
def f(a):
global b
print(a)
print(b)
b = 6
f3(3)
输出:3
10
闭包
闭包概念
(当涉及到函数定义的嵌套时,闭包就出现了)
所谓闭包延伸了作用域的函数,其中包含了函数定义体中引用但不在函数定义体中的非全局变量
!注意,闭包是一个函数,这个函数的一些特性让其变为了闭包,一个函数是不是闭包,关键在于他是否能访问定义体之外定义的非全局变量
示例:
def f():
a =1
def f2():
print(a)
return f2
func = f()
func()
以上8,9,10称之为闭包
在函数f2中,使用了函数定义体外的变量a,此时变量a对于函数f2而言就是一个自由变量,而它们一同构成的结构就是闭包
利用闭包解决问题
示例:
#定义函数体
In [1]: def make_avg():
#定义一个自由变量
...: values = []
#定义新的函数体avg,传入参数new_value
...: def avg(new_value):
...: values.append(new_value)
...: return sum(values) / len(values)
...: return avg
...:
In [2]: avg = make_avg()
In [3]: avg(5)
Out[3]: 5.0
In [4]: avg(6)
Out[4]: 5.5
In [5]: avg(7)
Out[5]: 6.0
In [6]: avg(8)
Out[6]: 6.5
进一步了解闭包:
什么叫做编译后的函数体
avg.__code__编译后的函数定义体
avg.__code__.co_varnames表示局部变量
('new_value',)
avg.__code__.co_freevars表示自由变量
('values',)
avg.__closure__中的元素对应这co_freevars中的名称
('values',):(<cell at 0x000001BF674745B8: list object at 0x000001BF675F4A48>,)
#__closure__元素中有cell_contents属性拥有存放自由变量具体的值
avg.__closure__[0].cell_contents
[5, 6, 7, 8]
nnonlocal关键字:
问题示例:
In [9]: def make_avg():
...: count = 0 #count = count + 1
...: total = 0 #total = total + 1
...: def avg(new_value):
...: count += 1
...: total += new_value
...: return total / count
...: return avg
...:
In [10]: avg = make_avg()
In [11]: avg(5)
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-11-d6bf7ac334bd> in <module>
----> 1 avg(5)
<ipython-input-9-62b76712d46e> in avg(new_value)
3 total = 0
4 def avg(new_value):
----> 5 count += 1
6 total += new_value
7 return total / count
UnboundLocalError: local variable 'count' referenced before assignment
错误因为:
1、赋值操作会把count变为局部变量,totaly也是一样的
2、python将count变量隐式创建为局部变量,让错误发生
解决问题:
添加nonlocal关键字。该关键字的作用是吧变量标记为自由变量,就算在函数中为变量赋了新的值,也会变为自由变量
正确示例:
In [9]: def make_avg():
...: count = 0 #count = count + 1
...: total = 0 #total = total + 1
...: def avg(new_value):
添加: nonlocal count,total
...: count += 1
...: total += new_value
...: return total / count
...: return avg
...:
In [10]: avg = make_avg()
In [11]: avg(5)