Python中闭包的概念及使用方法

1.闭包理解

  1. 闭包定义:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包

2.必报的构成条件:

  • 在函数嵌套(函数里面在定义函数)的前提下

  • 内部函数使用了外部函数的变量(还包括外部函数的参数)

  • 外部函数返回了内部函数

  • 3.闭包书写步骤:

  • 定义外部函数

  • 定义外部函数,在内部函数中使用外部函数的变量

  • 外部函数返回内部函数的地址

  • 代码需求:
    一个初始值 num
    对 num 的基础上进行 加法运算
    # 定义内部函数:
    def outer(num):
        # 2.定义内部函数,在内部函数中使用外部函数的变量
        def inner(num1):
            print(num + num1)
    
        # 3.外部函数返回内部函数的地址
        return inner  # 函数名字就是函数地址
    
    
    if __name__ == '__main__':
        # 创建闭包实例
        func = outer(100)  # 此时func相当于是inner
        # 调用闭包
        func(10)  # 此时调用inner函数,保存的num值为100
    # 可以创建多个闭包实例,不同的闭包实例之间,不会相互影响

    2.闭包的使用

    1. 案例:根据配置信息使用闭包实现不同人的对话信息,例如对话:

    张三:到北京了吗?

    李四:已经到了,放心吧。

    1. 实现步骤说明

  • 定义外部函数接受不同的配置信息参数,参数是人名

  • 定义内部函数接受对话信息参数

  • 在内部函数里面把配置信息和对话信息进行拼接输出

  • def outer(name):
        # 定义内部函数,参数是 说话的信息
        print(f'{name}:到北京了吗?')
    
        def inner(name1):
            # 内部函数中,将name和info进行拼接输出
            print(f'{name1}:已经到了,放心吧。')
    
        # 外部函数返回内部函数的地址
        return inner
    
    
    # 创建闭包实例
    if __name__ == '__main__':
        func = outer('张三')
        func('李四')

    注意点:由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。

    3.闭包修改外部函数变量(理解)

    函数内部想要修改全局变量,使用global关键字

    在闭包函数内部,想要修改外部函数的局部变量,需要使用nonlocal关键字

    def outer():
        num = 10
    
        def inner():
            # num = 100  # 不是修改外部变量的值,重新定义的局部变量
            nonlocal num  # 声明使用外部变量 num 不重新定义
            num = 100
    
        print(f'调用inner之前:{num}')
        inner()
        print(f'调用inner之后:{num}')
        return inner
    
    
    func = outer()
    func()

    4.装饰器(理解)

    装饰器的功能:就是给已有函数添加新的功能
               1.不修改已有函数的1源代码
               2.不修改已有函数的调用方式
               3.给函数添加功能
     
    装饰器本质就是一个闭包函数,只不过比较特殊
    1.定义外层函数(要求参数只有一个,类型是函数类型,调用时传参传递的是原函数)
    2.定义内层函数,在内层函数中,书写新的功能,并在合适的时机调用原函数
    3.返回内部函数的地址
     
    装饰器的书写方法: 新的功能和原函数进行对比,查看需要的新的功能,将新功能书写在inner的内层函数中
    案例需求:
    原函数: comment()  一个评论功能
    新的需求:在评论之前,检查是否登录
    
    # -------------------装饰器-----------------
    def login_check(fn):
        def inner():
            # 书写新的功能,使用print进行模拟
            print('登录验证......')
            # 新的功能书写完成之后,调用原函数
            fn()
    
        return inner
    
    
    # 装饰器的语法糖格式,
    @login_check  # comment =login_check(comment)
    def comment():
        # 使用print输出模拟评论功能
        print('进行评论检查......')
    
    
    # 在调用之前创建闭包实例
    # func = login_check(comment)
    comment = login_check(comment)
    if __name__ == '__main__':
        comment()

    5.装饰器语法糖格式替换时机

    def func():
        pass
     
     
    func()
    需求:统计函数执行所消耗的时间(新)
    1.执行函数前记录当前时间
    2.执行函数
    3.函数执行结束之后,再次记录当前时间(新)
    两次的时间差就是就是函数执行的时间
    -----装饰器语法糖格式替换时机-----
    
    import time
    
    
    # 定义装饰器函数:
    def calc_time(fn):
        def inner():
            # 记录当前时间
            start = time.time()  # 记录当前时间的秒数
            fn()
            end = time.time()
            print(f'函数耗时{end - start}')
    
        return inner
    
    
    # 定义原函数
    @calc_time
    def func():
        for i in range(10):
            print(i)
            time.sleep(0.3)
        print('函数执行')
    
    
    if __name__ == '__main__':
        func()

    6.装饰器输出日志信息

    需求:
       def func1():
           pass
       def func2():
           pass
    在每次进入函数时:输出 func1 enter......
    函数执行结束之后,输出 func leave.....
    
    分析步骤:
    1.函数执行前,输出func1 enter....
    2.执行函数
    3.执行结束, 输出 func1 leave.....
    # 定义原函数:
    def logger(fn):
        def inner(*args, **kwargs):
            print(f'{fn.__name__} enter....')
            fn(*args, **kwargs)
            print(f'{fn.__name__} leave....')
    
        return inner
    
    
    @logger
    def comment1():
        print('评论功能:')
    
    
    @logger
    def func(num):
        print(num)
    
    
    if __name__ == '__main__':
        comment1()
        func(10)
    

    11.装饰带返回值的函数

    不管原函数有没有返回值,装饰器的内部函数都应该将fn函数的返回值进行返回

    1. 如果原函数有返回值,返回的就是原函数的返回值

    1. 如果没有返回的是None

    #     func(10)
    # ----装饰带返回值的函数---
    def logger(fn):
        def inner(*args, **kwargs):
            print(f'{fn.__name__} enter....')
            result=fn(*args, **kwargs)
            print(f'{fn.__name__} leave....')
            # 函数没有书写返回值,默认返回None
            return result
    
        return inner
    
    
    @logger
    def func(into):
        return into
    
    
    @logger
    def my_sum(a, b):
        return a + b
    
    
    if __name__ == '__main__':
        print(func('hello'))
        print(func((3, 6)))

    12.通用装饰器

    def decorate(fn):
        def inner(*args,**kwargs):
              ...
            result=fn(*args,**kwargs)
            return result
         return inner

    13.多个装饰器装饰同一个函数

    def comment(info):
       return info
     
     1.书写一个装饰器 给原函数的返回值加上p 标签
    <p>返回值</p>
    2.书写一个装饰器,给 原函数加上 div标签
    <div> 返回值 </div>
    # ---多个装饰器装饰同一个函数----
    def make_p(fn):
        def inner(info):
            result = fn(info)
            return '<p>' + result + '</p>'
        return inner
    #
    #
    # @make_p
    # def comment(info):
    #     return info
    #
    #
    # if __name__ == '__main__':
    #     print(comment('hello world'))
    
    def make_div(fn):
        def inner(info):
            result = fn(info)
            return '<div>' + result + '</div>'
    
        return inner
    # 多个装饰器装饰同一个函数,装饰顺序是就近原则,谁离原函数近,就先装饰谁
    # 1.comment =make_p(comment)
    # 2.comment =make_div(comment)
    @make_div
    @make_p
    def comment(info):
        return info
    
    
    if __name__ == '__main__':
        print(comment('hello world!'))

    14.带参数的装饰器

    # 装饰器  make_tag
    
    
    @make_tag('div')
    @make_tag('p')
    def comment():
        pass
    
    1.当代码执行到第4行,@make_tag('div')
     
     
    def make_tag(tag):
       返回一个装饰器
    def make_tag(tag):
        # 返回一个装饰器
        def decorate(fn):
            def inner(*args, **kwargs):
                result = fn(*args, **kwargs)
                return f'<> {tag}>{result}</{tag}'
    
            return inner
    
        return decorate  # 返回装饰器
    
    # 1.先进行函数调用,返回装饰器
    # 2.对原函数进行装饰
    @make_tag('aini')
    @make_tag('div')
    @make_tag('p')
    def comment(info):
        return info
    
    
    if __name__ == '__main__':
        print(comment('hello world'))

    15.题目训练加强

    1.闭包的特点是什么?

    1.外部函数中定义了一个内部函数

    2.外部函数总是返回内部函数的引用

    3.内部函数可以使用外部函数提供的环境变量

    2.创建一个闭包,实现统计函数执行的次数功能

    有如下调用闭包函数的代码

    f=func_count()
    f()
    f()
    f()
    def func_count():
        num = 0
    
        def inner():
            nonlocal num
            num += 1
            print('hello world')
            print(f'共执行次数{num}')
    
        return inner
    
    
    if __name__ == '__main__':
        f = func_count()
        # 调用闭包
        f()
        f()
        f()

    3.实现装饰器,实现对函数执行时间进行计算的能力

    import time
    
    
    def outer(fn):
        def inner():
            start = time.time()
            fn()
            end = time.time()
            print(f'{end - start}')
    
        return inner
    
    
    @outer
    def func():
        for i in range(10):
            print(i)
            time.sleep(0.3)
    
    
    if __name__ == '__main__':
        func()
    

    4.一个函数,返回一个字符串,使用装饰器实现对这个字符串添加后缀.txt

    def func(fn):
        def inner(info):
            result = fn(info)
            return result + '.txt'
    
        return inner
    
    
    @func
    def comment(into):
        return into
    
    
    if __name__ == '__main__':
        print(comment('hello world'))
    

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python中闭包的概念及使用方法

    发表回复