闭包、装饰器

闭包、装饰器

闭包

  • 通俗来讲,闭包就相当于函数的嵌套 ,在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包

    # ======================程序====================
    def test(number):
        def test_in(number_in):
            print("in test_in 函数, number_in is %d" % number_in)
            return number+number_in
        # 其实这里返回的就是闭包的结果
        return test_in
    
    # 给test函数赋值,这个20就是给参数number
    ret = test(20)
    # 注意这里的100其实给参数number_in
    print(ret(100))
    #注 意这里的200其实给参数number_in
    print(ret(200))
    
    # ====================运行结果===================
    in test_in 函数, number_in is 100
    120
    in test_in 函数, number_in is 200
    220
    
  • 闭包实际例子

    # ====================程序===============
    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))
    
    # ====================结果===============
    6
    25
    

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

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

  • 在闭包内也可以修改外部函数中的变量

    # ====================程序===============
    def counter(start=0):
        def incr():
            nonlocal start
            start += 1
            return start
        return incr
    
    c1 = counter(5)
    print(c1())
    print(c1())
    
    c2 = counter(50)
    print(c2())
    print(c2())
    
    print(c1())
    print(c1())
    
    print(c2())
    print(c2())
    
    # ====================结果===============
    6
    7
    51
    52
    8
    9
    53
    54
    

    每一次创建闭包实际上是分别分配了一个空间给 c1和c2 ,他们相互独立

装饰器

  • 装饰器本质上是一个 Python 函数,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象

  • 装饰器(decorator)功能:

    • 引入日志
    • 函数执行时间统计
    • 执行函数前预备处理
    • 执行函数后清理功能
    • 权限校验等场景
    • 缓存
  • 先看一段代码

    def t1():
        print('执行前...')
      	print('t1------ 我被执行了。。')
      	print('执行完成')
    
    def t2():
      	print('执行前...')
      	print('t2------ 我被执行了。。')
      	print('执行完成')
      
    def t3():
      	print('执行前...')
      	print('t3------ 我被执行了。。')
      	print('执行完成')
    
  • 在看一段代码

    # ===================程序================
    def set_func(func):
      	def call_func():
        	print('执行前...')
        	func()
        	print('执行完成')
      	return call_func
    
    @set_func
    def t1():
      	print('t1------ 我被执行了。。')
    
    @set_func
    def t2():
      	print('t2------ 我被执行了。。')
    
    @set_func  
    def t3():
      	print('t3------ 我被执行了。。')
    
    t1()
    
    # ===================结果================
    执行前...
    t1------ 我被执行了。。
    执行完成
    

    这两段代码的效果都是一样的,而下面的则使用了 装饰器 ,由此可见,使用装饰器可以在不改变原函数的情况下,对原函数的功能进行扩展!

    @set_func 等价于 t1 = set_func(t1)

    执行过程:当执行 @set_func 时,相当于把 t1 当作实参传给了 set_func() ,这时 形参func 指向了函数 t1 ,然后到执行 return call_func时,就是返回call_func这个引用给 t1(因为是t1调用的set_func),也就是说这时t1 指向的就是 call_func() 函数,然后调用 t1() 时,就相当于调用 call_func() ,执行到func() 时,因为它指向的是 t1 里面的语句,所以执行的是print('t1------ 我被执行了。。') 语句

  • 装饰有参数无返回值的函数

    # ===================程序================
    def set_func(func):
        def call_func(num):
            print("11111111111")
            func(num)
        return call_func
    
    @set_func
    def test1(num):
        print("-----test1-----%d" % num)
    
    test1(100)
    
    # ===================结果================
    11111111111
    -----test1-----100
    

    因为函数带有参数,所以指向test1func 在调用时也需带参数传递,故而call_func也需要有参数传给 func

  • 装饰不定长参数的函数

    # ===================程序================
    def set_func(func):
        def call_func(*args, **kwargs):
            print("11111111111")
            func(*args, **kwargs)
        return call_func
    
    @set_func
    def test1(num, *args, **kwargs):
        print("-----test1-----%d" % num)
        print("-----test1-----", args)
        print("-----test1-----", kwargs)
    
    test1(100)
    test1(100, 200)
    test1(100, 200, 300, mm=100)
    
    # ===================结果================
    11111111111
    -----test1-----100
    -----test1----- ()
    -----test1----- {}
    11111111111
    -----test1-----100
    -----test1----- (200,)
    -----test1----- {}
    11111111111
    -----test1-----100
    -----test1----- (200, 300)
    -----test1----- {'mm': 100}
    
  • 装饰带有返回值的函数

    # ===================程序================
    def set_func(func):
        def call_func(*args, **kwargs):
            print("11111111")
            return func(*args, **kwargs)  # 返回原函数的返回值
        return call_func
    
    @set_func
    def test1(num, *args, **kwargs):
        print("-----test1-----%d" % num)
        print("-----test1-----", args)
        print("-----test1-----", kwargs)
        return "hhhhh"
    
    ret = test1(100)
    print(ret)
    
    # ===================结果================
    11111111
    -----test1-----100
    -----test1----- ()
    -----test1----- {}
    hhhhh
    

    因为原函数有返回值,所以我们需要在装饰器中接收原函数的返回值并返回

  • 同一个装饰器装饰多个函数

    # ===================结果================
    def set_func(func):
        def call_func(num):
            print("11111111111")
            func(num)
        return call_func
    
    @set_func
    def test1(num):
        print("-----test1-----%d" % num)
    
    @set_func
    def test2(num):
        print("-----test1-----%d" % num)
    
    test1(100)
    test2(200)
    
    # ===================结果================
    11111111111
    -----test1-----100
    11111111111
    -----test1-----200
    
    

    用一个函数装饰器可对多个函数进行装饰,每次装饰时,在没调用函数之前就已经装饰了,每次装饰相当一个闭包

  • 多个装饰器装饰一个函数

    # ===================结果================
    def set_func1(func):
        print("---开始装饰权限1的功能---")
        def call_func(*args, **kwargs):
            print("----这是权限1----")
            return func(*args, **kwargs)
        return call_func
    
    def set_func2(func):
        print("----开始装饰权限2的功能----")
        def call_func(*args, **kwargs):
            print("----这是权限2-----")
            return func(*args, **kwargs)
        return call_func
    
    @set_func1
    @set_func2
    def test1(num, *args, **kwargs):
        print("-----test1-----%d" % num)
        print("-----test1-----", args)
        print("-----test1-----", kwargs)
        return "hhhhh"
    
    ret = test1(100)
    print(ret)
    
    # ===================结果================
    ----开始装饰权限2的功能----
    ---开始装饰权限1的功能---
    ----这是权限1----
    ----这是权限2-----
    -----test1-----100
    -----test1----- ()
    -----test1----- {}
    hhhhh
    
    

    装饰时,@set_func1想装饰,结果它发现下面的不是一个函数,然后它就去找,就开始执行@set_func2 ,等@set_func2 装饰好时,相当于一个函数,这时@set_func1 就找到了它要装饰的东西,所以多个装饰器装饰同一个函数时,下面的装饰器先装饰

    • 一个小demo

      # ===================结果================
      def set_func_1(func):
          def call_func():
              return "<h1>" + func() + "</h1>"
          return call_func
      
      def set_func_2(func):
          def call_func():
              return "<a>" + func() + "</a>"
          return call_func
      
      @set_func_1
      @set_func_2
      def get_str():
          return "hhh"
      
      print(get_str())
      # ===================结果================
      <h1><a>hhh</a></h1>
      
      

      装饰器就把它理解成一个包装,一个装饰器相当于一层包装,外面的包装会把里面的那层包装给包住

  • 带有参数的装饰器

    def set_level(level_num):
        def set_func(func):
            def call_func(*args, **kwargs):
                if level_num == 1:
                    print("----权限级别1,验证-----")
                elif level_num == 2:
                    print("----权限级别2,验证-----")
                return func()
            return call_func
        return set_func
    
    @set_level(1)
    def test1():
        print("-----test1----")
        return "ok"
    
    @set_level(2)
    def test2():
        print("-----test2----")
        return "ok"
    
    ret = test1()
    print(ret)
    ret = test2()
    print(ret)
    
    

    这个有点像把一个参数从外一层一层的传递进去,然后把最终结果一层一层的返回到原函数,有那么一点点…绕。。

END…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值