• Python 中闭包函数和装饰器


    一、闭包函数的概述

      闭包 closure, 是一种代码的组织结构。当一个内嵌函数引用其外部作用域的变量,就形成一个闭包函数。闭包函数有一个内嵌函数,内嵌函数引用外部函数中的变量,外部函数的返回值是内嵌函数。

      闭包是函数和所引用环境变量的组合体。闭包让函数持有状态,延长环境变量的生命周期。类似类里的类变量,可以用函数或类的__call__方法实现装饰器。闭包函数只是绑定自由变量,并不会立即执行。只有当闭包函数执行时,才会访问所引用的目标对象。

      闭包函数加载的时候会访问闭包函数外部的代码,再次调用闭包函数的返回时执行闭包函数内部的代码。比如闭包函数的延迟绑定,闭包函数嵌套的匿名函数在外部函数访问时不会触发,会当作整体传递出去,只有再次调用才会触发函数体的执行。

      优点是具备封装性,可实现隐式上下文管理,并减少参数,在设计上可替代部分全局变量,或将环境与调用接口分离。

      缺点是对自由变量隐式依赖,会提升代码的复杂度。自由变量的生命周期变长,会提升内存的占用。

     

    二、装饰器的概述

    我们需要遵守开放封闭原则。

      开放封闭原则:程序上线以后就应该对修改封闭,对扩展开放

      修改封闭:不能修改功能性函数的源代码,不能修改功能性函数的调用方式

      扩展开放:可以为原有的功能添加新功能

     

    三、使用闭包函数完成装饰器的逐步实现

    首先,我们定义一个函数

    import time, random
    def msg():
      print('I have something to tell you, please wait a moment.')
      time.sleep(random.randint(1, 5))
      print('I like you.')
    msg()

    该函数会随机休眠1-5秒钟。

    现在,我们想要给该函数增加一个计时的功能,看看函数具体运行了多久。

    import time, random
    def msg():
        print('I have something to tell you, please wait a moment.')
        time.sleep(random.randint(1, 5))
        print('I like you.')
    start_time = time.time()
    msg()
    stop_time = time.time()
    print('Run time is %s' % (stop_time - start_time))

    并且如果要重复使用计时功能,就要写重复代码,所以我们想到了将计时功能封装成一个函数

    def wrapper():
        start = time.time()
        msg()
        stop = time.time()
        print('Run time is %s' % (stop - start))
        wrapper()

    我们将wrapper函数写死了,只能对内部调用的msg函数进行计时,使用闭包函数

    def timmer(func):  
        def wrapper():  
            start = time.time()
            func()
            stop = time.time()
            print('Run time is %s' % (stop - start))
            return wrapper  # 将wrapper函数的内存地址作为返回值返回。
        msg = timmer(msg)  # 作为参数的msg是我们定义的msg函数的内存地址
    
    # 作为变量名的msg是指向wrapper函数的内存地址
    msg() 此时我们这样写看似在调用msg函数,但实际上是调用了wrapper函数
    这样子我们就实现了偷梁换柱,在外观上没有更改msg函数的调用方式。

    当传入的函数有参数或者有返回值时

    def timmer(func):
        def wrapper(*args, **kwargs):
            strat_time = time.time()
            res = func(*args, **kwargs)
            stop_time = time.time()
            return res
        return wrapper

    装饰器的使用方法:语法糖

    import time, random
    def timmer(func):
        def wrapper(*args, **kwargs):
            strat_time = time.time()
            res = func(*args, **kwargs)
            stop_time = time.time()
            return res
        return wrapper
    @timmer
    def msg():
        print('I have something to tell you, please wait a moment.')
        time.sleep(random.randint(1, 5))
        print('I like you.')
    msg()

    含参装饰器,含参装饰器是一个三层的闭包函数

    import time
    def timemer(partment):
        def func(func_1):
            def wrapper(*args,**wkargs):
                if partment=='task1':
                    start = time.time()
                    func_1(*args, **wkargs)
                    stop = time.time()
                    print("the task1 run time is :", stop - start)
                elif partment=='task2':
                    start = time.time()
                    func_1(*args, **wkargs)
                    stop = time.time()
                    print("the task2 run time is :", stop - start)
            return wrapper
        return func
    @timemer(partment='task1')
    def task1():
        time.sleep(2)
        print("in the task1")
    @timemer(partment='task2')
    def task2():
        time.sleep(2)
        print("in the task2")
    task1()
    task2()

    类实现的含参装饰器

    
    
    from functools import wraps
    class decorate:
        def __init__(self, name):
            self.name = name    # 装饰器的参数
    def __call__(self, func): @wraps(func) def deco(*args, **kwargs): if self.name == 'Admin': passelif self.name == 'Student': passelif self.name == 'Teacher': passreturn func(*args, **kwargs) return deco

    @decarate('Admin')
  • 相关阅读:
    【洛谷P2660烤鸡】
    cogs448
    排队打水
    洛谷U36590搬书
    NOIP2012借教室
    归并排序模版
    NOIP2015神奇的幻方
    NOIP2006能量项链
    NOIP2003加分二叉树
    NOI1995石子合并&多种石子合并
  • 原文地址:https://www.cnblogs.com/wang-kai-1994/p/10222069.html
Copyright © 2020-2023  润新知