10分钟让你熟练Python闭包

初识闭包

什么是闭包

闭包的本质是函数

如何创建闭包

  1. 嵌套函数定义(外部函数,内部函数)
  2. 内部函数使用外部函数中定义的变量
  3. 外部函数一定要有返回值,返回内部函数名

【实例】定义一个闭包:

def Out(a):
	def In(b):
        return a+b
    return In

如何使用闭包

【实例】使用闭包计算两个数字的和:

# 计算两个数字的和
def funcOut(num1):
    def funcIn(num2):
        return num2+num1
    return funcIn # 返回内部函数

a = 10
b = 20
funcIn = funcOut(a) # 此时变量funcIn是函数类型,可以调用
result = funcIn(b) # 调用的是funcOut()的内部函数funcIn()
print(result)

执行结果:

30

【注意】在上述例子中,执行funcOut(a)以及funcIn(b)后,funcOut(a)里面的内容并不会因为funcIn(b)执行完而被释放。当在再次执行funcIn (100) 时结果为110。在闭包中,可以使用外部函数定义的变量,当直接改变外部函数变量的时候,内部函数会认为这是一个新定义的变量,从而导致报错。如果需要改变外部函数变量,需要使用nonlocal进行声明。

【实例】修改外部函数变量值

def funcOut(num1):
    def funcIn(num2):
        nonlocal num1 # 只有声明了外部函数变量再修改才是合法的
        num += 1
        return num2+num1
    return funcIn

闭包应用

【实例】求(10, 10), (20, 20)距离原点(0, 0)的距离

# 使用普通函数实现
from math import sqrt

def getDis(x1, y1, x2, y2):
    return sqrt((x1-x2)**2 + (y1-y2)**2)

# 使用闭包实现
def getDisOut(x1, y1):
    def getDisIn(x2, y2):
        return sqrt((x1-x2)**2 + (y1-y2)**2)
    return getDisIn

# 普通函数
dis = getDis(0, 0, 10, 10)
print("(10, 10)距离远点的距离:{}".format(dis))
dis = getDis(0, 0, 20, 20)
print("(20, 20)距离远点的距离:{}".format(dis))

# 闭包
disIn = getDisOut(0, 0)
dis = disIn(10, 10)
print("(10, 10)距离远点的距离:{}".format(dis))
dis = disIn(20, 20)
print("(20, 20)距离远点的距离:{}".format(dis))

在使用闭包后,由于闭包特殊的性质,即调用Out函数和In函数后,原先Out函数变量里面的值并不会被释放,在处理上述类型问题的时候可以减少代码量。

闭包的特殊用途

可以在不修改现有功能源码的前提下,增加新的功能。

比如:日志功能(统计访问时间,访问功能,写道日志文件中),权限验证(下载之前,验证是否为会员)

【实例】不用闭包的情况下添加日志功能

# 实现日志功能
import time


def write_log(func):
    try:
        file = open('log.txt', 'a', encoding='utf-8')
        # 写入相关信息(访问的函数名称,访问的时间)
        file.write(func.__name__)
        file.write('\t')
        # 写入访问时间
        file.write(time.asctime())
        file.write('\n')

    except Exception as e:
        print(e.args)
    finally:
        # 关闭文件
        file.close()


def func1():
    write_log(func1)
    print('我是功能1')


def func2():
    write_log(func2)
    print('我是功能2')


if __name__ == "__main__":
    func1()
    func2()

运行结果:

func1 Mon Oct 31 21:51:34 2022
func2 Mon Oct 31 21:51:34 2022

当不使用闭包添加功能的时候,大概率要在源代码上面进行修改,这就违反了程序开发的开闭原则:对添加功能开放,对修改源代码关闭。当代码量少的时候还好,一旦添加的代码涉及修改元逻辑就有可能导致新的问题出现。

【实例】使用闭包添加日志功能

# 闭包的应用:添加日志功能
import time


def write_log(func):
    try:
        file = open('log.txt', 'a', encoding='utf-8')
        # 写入相关信息(访问的函数名称,访问的时间)
        file.write(func.__name__)
        file.write('\t')
        # 写入访问时间
        file.write(time.asctime())
        file.write('\n')

    except Exception as e:
        print(e.args)
    finally:
        # 关闭文件
        file.close()


def func_out(func):
    def func_in():
        write_log(func)
        func()
    return func_in


def func1():
    print('我是功能1')


def func2():
    print('我是功能2')


if __name__ == "__main__":
    # 闭包的调用
    func1 = func_out(func1) 
    func2 = func_out(func2)
    func1()
    func2()

这里使用了闭包给原函数添加功能,首先当执行func1 = func_out(func1)时,func1被赋值为闭包外部函数变量值为func1()的内部函数func_in(), 所以当后面执行func1()的时候,其实执行的是闭包里面func_in()的代码。由于传实参的时候,func1()被当成对象传递进闭包中,按照func_in()执行步骤,先执行了以func1()为实参的write_log(func1)再执行从闭包外部函数传递进来的func()(此时func()就等于func1())。通过这样的方式实现了不修改原函数源代码,为原函数添加了日志功能。

这里可能有点绕,但是闭包的逻辑本身就是很绕,希望小伙伴们自己多写一下这里的代码进行体会。

闭包在实际开发中用处非常广泛,希望同学们好好体会。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nine_mink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值