python-变量作用域
变量作用域
局部变量
python不要求声明变量,但是在函数体内赋值的变量,都会被当作局部变量。
name = 'global' def fun(): name = 'fun' print(name) fun() #fun
但是如果我把赋值语句写在print语句之后呢?
name = 'global' def fun(): print(name) name = 'fun' fun() #UnboundLocalError: local variable 'name' referenced before assignment
为什么会报错呢?
其实在python编译函数fun的时,已经把函数体内的name当作局部变量,
但是在执行函数体内的打印语句执行时,name此时还未被赋值,所以报错UnboundLocalError。
对于这样的情况,我们最好将函数的局部变量赋值放在第一次使用它的前面。
global
当然,在函数体内我们也可以直接访问全局变量name
name = 'global' def fun(): print(name) fun() #global
但是,如果我们想在函数内更改全局变量的值,该怎么办呢?
我们可以使用global声明函数内的变量为全局变量
name = 'global' def fun(): global name name = 'fun' print(name) fun() #fun print(name) #fun
闭包
闭包是函数和它能够访问的函数体外的变量(自由变量)组成的一个记录。
自由变量,指未在当前作用域绑定的变量。
def make_averager(): series = [] def averager(new_value): series.append(new_value) total_value = sum(series) total_count = len(series) return total_value / total_count return averager avg = make_averager() print(avg(3)) #3.0 print(avg(5)) #4.0
说明:当调用函数make_averager时,会产生一个闭包,这个闭包不仅包含函数averager,而且还包含自由变量series
即使定义作用域不可用了,但是闭包仍然保存对其自由变量的绑定
nonlocal
但是这种做法效率并不高,因为我们每次都要对自由变量求和、计算series中元素的个数,
其实我们只需存储目前的总值和元素个数即可,如下
def make_averager(): total_value = 0 total_count = 0 def averager(new_value): total_value += new_value total_count += 1 return total_value / total_count return averager avg = make_averager() print(avg(3))
但是代码会报错:UnboundLocalError: local variable 'total_value' referenced before assignmen
这是为什么呢?
total_value += new_value等价于total_value = total_value + new_value,这也就是说python编译器把total_value当作函数体内的局部变量。
对于数字、字符串、元组等不可变类型来说,只能读取,不能更改,如果尝试重新绑定,如total_value = total_value + new_value,会隐式的创建局部变量total_value,
这样total_value绑定的就不是自由变量total_value了,因此不会保存在闭包中。
那我们有方法使python把函数内部的变量total_value当作自由变量吗?
当然有,我们可以使用nonlocal,它的作用就是把变量标识为自由变量
def make_averager(): total_count = 0 total_value = 0 def averager(new_value): nonlocal total_value, total_count total_value += new_value total_count += 1 return total_value / total_count return averager avg = make_averager() print(avg(3)) #3.0 print(avg(5)) #4.0
参考资料:《流畅的python》