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》

猜你喜欢

转载自www.cnblogs.com/marton/p/10908447.html