Python 闭包和nonlocal声明

本文介绍了Python中闭包的概念,即函数可以保存上层函数作用域中的变量,以及nonlocal声明如何允许函数访问和修改局部作用域以外的变量。通过实例说明了两者在变量共享和函数装饰器中的应用。
摘要由CSDN通过智能技术生成

闭包是针对嵌套函数环境的概念,它的作用是延伸函数的作用域。简单来说,闭包就是一个函数,但它可以保存着上层函数作用域中的变量,使得这些变量可以在函数中使用。而nonlocal声明的作用就是允许函数重新绑定局部作用域以外且非全局作用域当中的变量。

一、变量作用域

Python中的变量根据作用域可以分为全局变量和局部变量,而变量在代码中赋值的位置决定了其作用域。

1.1 局部变量

默认情况下,在函数中被赋值的对象,是这个函数的局部变量,且仅在函数运行期间存在,当函数返回时,变量就消失了。

示例:变量b是全局变量,变量a是函数f的局部变量,函数f(1)运行输出1(局部变量a)和2(全局变量b)。函数运行完a就消失了,再次输入a会告诉你变量未定义:

b = 2
def f(a):
    print(a)    # 打印局部变量a
    print(b)    # 没有局部变量b,向上搜索,找到全局变量b
f(1)

在这里插入图片描述

1.2 全局变量

上面的例子,我们在print(b)语句之后增加1个赋值语句b=3,其他保持不变,再次运行。print(a)成功打印出1,但print(b)出错,提示本地变量b未赋值。这里出错的原因就是后面新增了b=3赋值语句,赋值操作导致在编译时将b转换成了局部变量(变量在代码中赋值的位置决定了其作用域)。

b = 2
def f(a):
    print(a)    # 打印局部变量a
    print(b)    # 运行报错,提示b未赋值
    b = 3      # 赋值语句将b变为局部变量
f(1)

在这里插入图片描述

如果要使用全局变量b,需要用global关键字进行声明。global b意味引用全局变量中的b:

b = 2
def f(a):
    global b    # 声明引用全局变量b
    print(a)   
    print(b)    # 打印全局变量b
    b = 3      # 将全局变量赋值为3
f(1)
b

在这里插入图片描述

二、闭包的概念

闭包是针对嵌套函数环境的概念,它的作用是延伸函数的作用域。简单来说,闭包就是一个函数,但它可以保存着上层函数作用域中的变量,使得这些变量可以在函数中使用。

我们通过一个示例来演示,现在定义一个函数,计算所有累计输入数字的平均值,函数avger中定义一个变量numbers,嵌套函数clac向这个变量内累加值后并求平均值:

def avger():
    numbers = []
    def calc(n):
        numbers.append(n)
        return sum(numbers)/len(numbers)
    return calc

avg = avger()
avg(1)    # 1/1
avg(2)    # (1+2)/2
avg(3)    # (1+2+3)/3 每次函数调用都会在历史计算结果上累加

在这里插入图片描述

仔细思考一下,上面代码中,numbers是函数avger()的本地变量而不是calc的本地变量,在函数avg = avger()调用后就消失了,我们可以尝试调用一下numbers,可以发现其未定义,但为什么avg可以一直使用?

numbers    # 未定义,因为在avger()函数返回后就消失了
avg(4)    # (1+2+3+4)/4 依然可以使用numbers变量

在这里插入图片描述

这里的概念就是闭包,闭包指延伸了作用域的函数,它包含了不在函数体内的非全局变量,这种变量叫自由变量。即它(calc)能访问定义体之外定义的非全局变量(numbers),用一个示意图解释如下:

  • 橙色方框是calc本地作用域
  • 绿色方框是闭包,相当于延伸了作用域的calc函数
  • 红色是全局作用域
    在这里插入图片描述

三、nonlocal声明

我们把上面的例子再修改一下,用数字count, total分别记录数量和总和,累加后求平均数。但换了变量后发现闭包失效了,count无法引用:

def avger():
    count = 0
    total = 0
    def calc(n):
        count += 1
        total += n
        return total/count
    return calc
avg = avger()
avg(1)    # 报错,变量count未赋值

在这里插入图片描述

出错的原因就出在count的数据类型上。count是数字,而python中数字类型是不可以变的。count +=1 实际上是count = count+1,python隐式创建了一个新对象然后赋值给了count,而不是原地改变。正是这个赋值操作将count变成了本地变量(和上面1.2全局变量演示的错误原因相同)。

这时就轮到nonlocal声明了,它的作用是将变量标记为自由变量,即使重新赋值也不会影响。下面将count, total用nonlocal声明后,代码即可顺利运行:

def avger():
    count = 0
    total = 0
    def calc(n):
        nonlocal count, total
        count += 1
        total += n
        return total/count
    return calc
avg = avger()
avg(1)
avg(2)

在这里插入图片描述

以上即是Python中闭包和nonlocal的概念,其经常用在变量共享及函数装饰器场景。

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值