zoukankan      html  css  js  c++  java
  • 闭包&装饰器

    闭包

    1.函数引用

    def test():
        print('--test--')
    
    # 调用函数
    test()
    # 引用函数
    ret = test
    
    print(id(ret))
    print(id(test))
    
    # 通过引用调用函数
    ret()
    
    #输出结果
    --test--
    1718807047704
    1718807047704
    --test--

    2.什么是闭包

    # 定义一个函数
    def test(num):
        # 在函数内部再定义一个函数,并且这个函数用到外部函数的变量,那么将这个函数以及用到的一些变量称之为闭包
        def inner_test(inner_num):
            print('inner_num:%d' %inner_num)
            return num +inner_num
        # 其实这里返回的就是闭包的结果
        return inner_test
    
    # 给test函数赋值,这个20就是给参数num
    ret = test(10)
    # 这里的50其实给参数inner_num
    print(ret(50))
    # 这里的100其实给参数inner_num
    print(ret(100))
    # 输出结果: inner_num:50 60 inner_num:100 110

    3. 一个闭包的实际例子:

    def line_conf(a, b):
        def line(x):
            return a*x + b
        return line
    
    line1 = line_conf(1, 1)
    line2 = line_conf(4, 5)
    print(line1(5))
    print(line2(5))

    这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

    如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。

    注意点:

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

    4. 修改外部函数中的变量

    python3的方法:

    def outer(start=0):
        def inner():
            nonlocal start
            start += 1
            return start
        return inner
    
    o1= outer(5)
    print(o1())  # 6
    print(o1())  # 7
    
    o2 = outer(10)
    print(o2())  # 11
    print(o2())  # 12

    python2的方法:

    def outer(start=0):
        count=[start]
        def inner():
            count[0] += 1
            return count[0]
        return inner
    
    o1 = closeure.outer(5)
    print(o1())  # 6
    print(o1())  # 7
    o2 = closeure.outer(10)
    print(o2())  # 11
    print(o2())  # 12

    装饰器

    1、先明白这段代码

    #### 第一波 ####
    def foo():
        print('foo')
    
    foo  # 表示是函数
    foo()  # 表示执行foo函数
    
    #### 第二波 ####
    def foo():
        print('foo')
    
    foo = lambda x: x + 1
    
    foo()  # 执行lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数

    函数名仅仅是个变量,只不过指向了定义的函数而已,所以才能通过函数名()调用,如果 函数名=xxx被修改了,那么当在执行 函数名()时,调用的就不知之前的那个函数了

    2、需求

    初创公司有N个业务部门,基础平台部门负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:

    ############### 基础平台提供的功能如下 ###############
    
    def f1():
        print('f1')
    
    def f2():
        print('f2')
    
    def f3():
        print('f3')
    
    def f4():
        print('f4')
    
    ############### 业务部门A 调用基础平台提供的功能 ###############
    
    f1()
    f2()
    f3()
    f4()
    
    ############### 业务部门B 调用基础平台提供的功能 ###############
    
    f1()
    f2()
    f3()
    f4()

    目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

    老大把工作交给员工C,他是这么做的:

    ############### 基础平台提供的功能如下 ############### 
    
    def f1():
        # 验证1
        # 验证2
        # 验证3
        print('f1')
    
    def f2():
        # 验证1
        # 验证2
        # 验证3
        print('f2')
    
    def f3():
        # 验证1
        # 验证2
        # 验证3
        print('f3')
    
    def f4():
        # 验证1
        # 验证2
        # 验证3
        print('f4')
    
    ############### 业务部门不变 ############### 
    ### 业务部门A 调用基础平台提供的功能### 
    
    f1()
    f2()
    f3()
    f4()
    
    ### 业务部门B 调用基础平台提供的功能 ### 
    
    f1()
    f2()
    f3()
    f4()

    老大把工作交给员工D,他是这么做的:

    ############### 基础平台提供的功能如下 ############### 
    
    def check_login():
        # 验证1
        # 验证2
        # 验证3
        pass
    
    
    def f1():
    
        check_login()
    
        print('f1')
    
    def f2():
    
        check_login()
    
        print('f2')
    
    def f3():
    
        check_login()
    
        print('f3')
    
    def f4():
    
        check_login()
    
        print('f4')

    老大看了下员工D的实现,嘴角漏出了一丝的欣慰的笑,语重心长的跟员工D聊了个天:

    写代码要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

    • 封闭:已实现的功能代码块
    • 开放:对扩展开发

    如果将开放封闭原则应用在上述需求中,那么就不允许在函数 f1 、f2、f3、f4的内部进行修改代码,老板就给了员工D一个实现方案:

    def w1(func):
        def inner():
            # 验证1
            # 验证2
            # 验证3
            func()
        return inner
    
    @w1
    def f1():
        print('f1')
    @w1
    def f2():
        print('f2')
    @w1
    def f3():
        print('f3')
    @w1
    def f4():
        print('f4')
    # 单独以f1为例:
    
    def w1(func):
        def inner():
            # 验证1
            # 验证2
            # 验证3
            func()
        return inner
    
    @w1
    def f1():
        print('f1')

    python解释器就会从上到下解释代码,步骤如下:

    1. def w1(func): ==>将w1函数加载到内存
    2. @w1

    没错,从表面上看解释器仅仅会解释这两句代码,因为函数在 没有被调用之前其内部代码不会被执行。

    从表面上看解释器着实会执行这两句,但是 @w1 这一句代码里却有大文章, @函数名 是python的一种语法糖。

    上例@w1内部会执行一下操作:

    执行w1函数

    执行w1函数 ,并将 @w1 下面的函数作为w1函数的参数,即:@w1 等价于 w1(f1) 所以,内部就会去执行:

    def inner(): 
        #验证 1 
        #验证 2 
        #验证 3 
        f1() # func是参数,此时 func 等于 f1 
    return inner# 返回的 inner,inner代表的是函数,非执行函数 ,其实就是将原来的 f1 函数塞进另外一个函数中

    w1的返回值

    将执行完的w1函数返回值 赋值 给@w1下面的函数的函数名f1 即将w1的返回值再重新赋值给 f1,即:

    新f1 = def inner(): 
            #验证 1 
            #验证 2 
            #验证 3 
            原来f1() 
     return inner

    所以,以后业务部门想要执行 f1 函数时,就会执行 新f1 函数,在新f1 函数内部先执行验证,再执行原来的f1函数,然后将原来f1 函数的返回值返回给了业务调用者。

    如此一来, 即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用者

    3. 装饰器(decorator)功能

    1. 引入日志

    2. 函数执行时间统计

    3. 执行函数前预备处理

    4. 执行函数后清理功能

    5. 权限校验等场景

    6. 缓存

    4. 装饰器示例

    例1:无参数的函数

    import time
    
    def set_func(foo):
        def call_func():
            start_time = time.time()
            foo()
            end_time = time.time()
            print("运行时间:%f" % (end_time-start_time))
        return call_func
    
    @set_func
    def test():
        print("test")
        for i in range(100000):
            pass
    test()
    
    test = set_fun(foo)
    
    # test先作为参数赋值给foo后,test接收指向set_fun返回的call_func
    # 调用test(),即等价调用call_func()
    # 内部函数call_func被引用,所以外部函数的foo变量(自由变量)并没有释放
    # foo里保存的是原test函数对象
    View Code

    例2:带参数函数的装饰

    def set_func(foo):
        def call_func(num):
            foo(num)
        return call_func
    
    @set_func
    def test(num):
        print(num)
    
    test(11)
    View Code

    例3:对多个函数进行装饰

    def set_func(foo):
        def call_func():
            foo()
        return call_func
    
    @set_func
    def test1():
        print("test1")
    
    @set_func
    def test2():
        print("test2")
    
    test1()
    test2()
    View Code

    例4:在调用函数之前已经开始装饰

    def set_func(foo):
        print("开始装饰")
        def call_func():
            foo()
        return call_func
    
    @set_func
    def test1():
        print("test1")
    View Code

    例5:对不定长参数的函数装饰

    def set_func(foo):
        def call_func(*args, **kwargs):
            foo(*args, **kwargs)
            print("--end--")
        return call_func
    
    @set_func
    def test1(num, *args, **kwargs):
        print("test1",num)
        print("test1",args)
        print("test1",kwargs)
    
    test1(11)
    test1(11,22,33)
    test1(11,22,33,a=55)
    View Code

    例6;对有返回值的函数装饰(通用装饰器)

    def set_func(foo):
        def call_func(*args, **kwargs):
            return foo(*args, **kwargs)
        return call_func
    
    @set_func
    def test1():
        print("test1")
        return "ok"
    ret = test1()
    print(ret)
    View Code

    例7:多个装饰器对一个函数装饰

    def set_func1(foo):
        print("装饰器1开始装饰")
        def call_func(*args, **kwargs):
            print("装饰器1功能")
            return foo(*args, **kwargs)
        return call_func
    
    def set_func2(foo):
        print("装饰器2开始装饰")
        def call_func(*args, **kwargs):
            print("装饰器2功能")
            return foo(*args, **kwargs)
        return call_func
    
    @set_func1
    @set_func2
    def test1():
        print("test1")
    
    test1()
    
    # 输出结果:
    装饰器2开始装饰
    装饰器1开始装饰
    装饰器1功能
    装饰器2功能
    test1
    View Code
    def set_func1(foo):
        print("装饰器1开始装饰")
        def call_func():
            return "<tr>"+foo()+"</tr>"
        return call_func
    
    def set_func2(foo):
        print("装饰器2开始装饰")
        def call_func():
            return "<td>" + foo() + "</td>"
        return call_func
    
    @set_func1
    @set_func2
    def test1():
        return "ok"
    print(test1())
    
    # 输出结果:
    装饰器2开始装饰
    装饰器1开始装饰
    <tr><td>ok</td></tr>
    View Code

    例8:用类对函数进行装饰

    class Test(object):
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args, **kwargs):
            print("装饰器的功能")
            return self.func(*args, **kwargs)
    
    @Test
    def test():
        return "ok"
    
    print(test())
    
    #说明:
    #1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
    #   并且会把test这个函数名当做参数传递到__init__方法中
    #   即在__init__方法中的属性func指向了test指向的函数
    #
    #2. test指向了用Test创建出来的实例对象
    #
    #3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
    #
    #4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
    #   所以才有了self.func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体
    View Code

    例9:装饰器带参数,在原有装饰器的基础上,设置外部变量

    def level(level_num):
        def set_func(func):
            def call_func(*args, **kwargs):
                if level_num == 1:
                    print("极品货")
                elif level_num == 2:
                    print("A货")
                func(*args, **kwargs)
            return call_func
        return set_func
    
    
    @level(1)
    def test1():
        print("ok")
    
    @level(2)
    def test2():
        print("ok")
    
    test1()
    test2()
    # 下面的装饰过程
    # 1. 调用level("1")
    # 2. 将步骤1得到的返回值,即set_func返回, 然后set_func(func)
    # 3. 将set_func(func)的结果返回,即call_func
    # 4. 让test1 = call_func,即test1现在指向call_func
    View Code
  • 相关阅读:
    NOI2015 小园丁和老司机
    退役记
    留言板
    $mathfrak {reputation}$
    计算几何基础
    HNOI2018简要题解
    JXOI2018简要题解
    BJOI2018简要题解
    HAOI2018 简要题解
    CQOI2018简要题解
  • 原文地址:https://www.cnblogs.com/ForT/p/10297972.html
Copyright © 2011-2022 走看看