打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
python基础-闭包函数和装饰器

闭包函数和装饰器

闭包函数

  • 概念:在函数中(嵌套)定义另一个函数时,内部函数引用了外层函数的名字。

  • 特性

    • 闭包函数必须在函数内部定义

    • 闭包函数可引用外层函数的名字

闭包函数是函数嵌套、函数对象、名称空间与作用域结合体

闭包中被内部函数引用的变量,不会因为外部函数结束而被释放掉,而是一直存在内存中,直到内部函数被调用结束

# 闭包函数的定义def func(y):    x = 100    def inner():		# 此处的inner 就是闭包函数        print(x)        print(y)    return inner# 闭包函数的调用inner = func(1000)inner()
  • 应用:为了给装饰器的使用做准备


装饰器

  • 概念:在不修改被装饰对象源代码与调用方式的前提下,添加新的功能。简单来说,就是给其他函数增加额外功能的函数

  • 装饰器必须遵循“开放封闭”原则

    • 开放:对函数功能的添加是开放的

    • 封闭:对函数功能的修改是封闭的

  • 必备条件

    • 不能改变被装饰对象源代码

    • 为被装饰对象添加新的功能

    • 不能改变被装饰对象调用方式

  • 使用装饰器,可以解决代码冗余问题,提高代码的可扩展性

  • 应用:统计时间、登录认证

说了这多理论,上代码。

需求:统计下载媒体文件的时间(简易版)

# 需求: 统计下载电影的时间。# 一般我们实现这个功能的方式如下import timedef download_movie():    """    下载电影功能    :return: None    """    print("电影下载开始了……")    time.sleep(2)  # 模拟电影下载2 秒    print("下载完成")start_time = time.time()  # 获取当前时间戳download_movie()  # 下载电影end_time = time.time()  # 获取当前时间戳print(f"下载时间:{end_time-start_time}")

以上代码确实实现了统计下载时间的功能,问题来了,如果我有多个类型文件下载函数,都需要统计时间呢?难道展现我们的“CV大法”?估计隔天就要被扫地出门了……

就没有什么办法可以解决了吗?当然有啦!先看代码

# 定义一个装饰器def time_record(func):    """    新增统计时间功能    :param func: 使用该功能的函数对象    :return: inner 函数对象    """    def inner():        # 统计开始        start_time = time.time()        func()  # func()  ----→ download_movie() or download_music()        end_time = time.time()        # 统计结束,打印统计时间        print(f"下载时间:{end_time-start_time}")    return innerdef download_movie():    """    模拟下载电影    :return: None    """    print("电影下载开始了……")    time.sleep(2)    print("下载完成!")def download_music():    """    模拟下载音乐    :return: None    """    print("歌曲下载开始了……")    time.sleep(2)    print("下载完成!")# time_record(download_movie) 返回一个inner,将其赋值给download_movieinner = time_record(download_movie)inner()  # inner()  ----→  download_movie()# time_record(download_music) 返回一个inner,将其赋值给download_musicinner1 = time_record2(download_music)inner1()

以上代码就是对装饰器的引入,其中 time_record 就是装饰器,download_movie 和 download_music 就是被装饰的函数对象。

通过观察🕵,以上被装饰的函数对象是没有返回值,且没有参数的。问题来了,假如 download_movie 等被装饰对象是有返回值和参数,那该咋整?

不说废话🙊,继续看代码

# 定义一个装饰器def time_record(func):    """    新增统计时间功能    :param func: 使用该功能的函数对象    :return: inner 函数对象    """    def inner(*args, **kwargs):        # 统计开始        start_time = time.time()        # func()  ----→ download_movie();定义变量接收download_movie() 的返回值        # 定义可变长位置参数,用于接收任意类型的位置参数,甚至无参都可        # 同理,定义可变长关键字参数,用于接收任意类型的关键字参数,甚至是无参都行        res = func(*args, **kwargs)        end_time = time.time()        # 统计结束,打印统计时间        print(f"下载时间:{end_time-start_time}")        return res  # 将download_movie() 的返回值返回    return innerdef download_movie(url):    """    模拟下载电影    :return: None    """    print(f"{url}的电影下载开始了……")    time.sleep(2)    print("下载完成!")    return "海贼王.mp4"def download_music(url, name):    """    模拟下载音乐    :return: None    """    print(f"{url}中的歌曲下载开始了……")    time.sleep(2)    print(f"{name}.mp3 下载完成!")download_movie = time_record(download_movie)# 接收download_movie()的返回值res = download_movie("https://www.cnblogs.com/xiaoyuanqujing/p/11636160.html")print(res) # 打印返回值download_music = time_record(download_music)download_music("https://music.163.com/", name="烟火里的尘埃")  # 传入一个位置参数和一个关键字参数

细心如你,装饰器定义好了,但是每次调用都特别麻烦,有没有一种比较便捷的调用方式呢?

当当当当!当然有,那就是装饰器的语法糖,听起来就感觉使用非常甜,咋用呢?继续看代码(由于之前代码已经实现了装饰器,这里就不重复展示了,只实现语法糖的代码

@time_record  # 这就是装饰器time_record 的语法糖def download_movie(url):    """    模拟下载电影    :return: None    """    print(f"{url}的电影下载开始了……")    time.sleep(2)    print("下载完成!")    return "海贼王.mp4"@time_record  # 这就是装饰器time_record 的语法糖def download_music(url, name):    """    模拟下载音乐    :return: None    """    print(f"{url}中的歌曲下载开始了……")    time.sleep(2)    print(f"{name}.mp3 下载完成!")res = download_movie("https://www.cnblogs.com/xiaoyuanqujing/p/11636160.html")print(res)download_music("https://music.163.com/", name="烟火里的尘埃")  # 传入一个位置参数和一个关键字参数
  • 装饰器语法糖

    注意:在使用时,装饰器必须在被装饰对象之前定义

    • 顾名思义,这个关键字只属于装饰器

    • 关键字@

    • 使用方法:@ 装饰器名称

  • 装饰器终极模板

    # func 表示需要装饰的函数对象def wrapper(func):    def inner(*args, **kwargs):  # 接收不固定参数        # 调用func 之前增加的新功能        res = func(*args, **kwargs)  # 调用被装饰对象,得到返回值        # 调用func 之后增加的新功能        return res  # 将返回值返回    return inner@wrapperdef func1():    pass# 使用装饰器语法糖后,直接调用函数func1()
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
一文读懂Python装饰器由来(一)
Python装饰器
Python学习—装饰器
python笔记36-装饰器之wraps
从零开始学Python:21课-函数的高级应用
Python | 如何实现一个装饰器
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服