python 闭包 装饰器

时光怂恿深爱的人放手 提交于 2019-11-27 15:02:27

闭包

1. 什么是闭包

  • 在 python 中创建一个闭包一般有3个要求:
    • (1)闭包函数必须有内嵌函数
    • (2)内嵌函数必须要引用外层函数的变量
    • (3)闭包函数返回内嵌函数的地址(函数名称)
  • 作用:可以在不修改目标源码的前提下,加功能
  • 注意:闭包函数中的变量的生命周期得到延长

2. 创建一个闭包函数

def funcOut():
    name = 'Jery'
        
    def funcIn():
        # format的简写方式
        print(f"姓名 = {name}")
    return funcIn
    
f = funcOut()
f()

 运行结果:

姓名 = Jery

  

3. 判断是否为闭包函数

  • 闭包函数相对与普通函数会多出一个__closure__的属性,里面定义了一个元组用于存放所有的cell对象,每个cell对象保存了这个闭包中所有的外部变量。
  • funcIn.__closure__:返回None,则不是闭包函数
  • 如:
    def funcOut():
        name = 'Jery'
        def funcIn():
            print(f"姓名 = {name}")
        print(funcIn.__closure__)
        print(funcIn.__closure__[0].cell_contents)  # 第一个外部变量
        return funcIn
    
    f = funcOut()
    f()
    

    运行结果

    (<cell at 0x000000000272F8B8: str object at 0x00000000026A08B8>,)
    Jery
    姓名 = Jery
    

    装饰器

    1. 本质及作用

    • 装饰器的本质
      • 闭包函数
    • 装饰器的作用:
      • 在不修改原函数及其调用方式的情况下对原函数功能进行扩展

    2. 装饰器的使用

  • 需求:为现有功能fun1增加日志功能
    • 传统方案解决 —— 使用闭包
       def writeLog(fn):
            print("记录日志")
            print('访问方法:'+fn.__name__)
      
        def funcOut(func):
            def funcIn():
                writeLog(func)
                func()
            return funcIn
            
        def fun1():
            print("使用功能1")
            
        def fun2():
            print("使用功能2")
        
        fun1 = funcOut(fun1)
        # 装饰器(闭包)
        fun1()
      
    • 运行结果:

      记录日志
      访问方法:fun1
      使用功能1 
    • 使用装饰器(语法糖)解决 

        def writeLog(fn):
            print("记录日志")
            print('访问方法:'+fn.__name__)
        
        def funcOut(func):
            def funcIn():
                writeLog(func)
                func()
            return funcIn
        
        @funcOut
        def fun1():
            print("使用功能1")
        @funcOut
        def fun2():
            print("使用功能2")
        
        fun1()
        fun2()
      

      运行结果: 

      记录日志
      访问方法:fun1
      使用功能1
      记录日志
      访问方法:fun2
      使用功能2 

3. 多个装饰器的使用

    • 如:

    • def war1(func):
          print("war 1")
      
          def inner(*args, **kwargs):
              print("======war1 start=====")
              func(*args, **kwargs)  # inner
              print("======war1 end=====")
              return inner
      
      def war2(func):
          print("war2")
      
          def inner(*args, **kwargs):
              print("======war2 start=====")
              func(*args, **kwargs)
              print("======war2 end=====")
          return inner
      
      @war1
      @war2
      def f():
          print("****self****")
      
      f()
    • 运行结果:
      war2
      war1
      ======war1 start=====
      ======war2 start=====
      ****self****
      ======war2 end=====
      ======war1 end=====  
    • 解释:
      (1)
      @war1
      @war2 之后相当于 --> f = war1(war2(f))
      其中war2(f)是一个函数,作为实参传递
      war2(f):
        print("======war2 start=====")
        print("****self****")
        print("======war2 end=====")
      
      war1(war2(f)):
        print("======war1 start=====")
        war2(f)
        print("======war1 end=====")
      (2)
      f() 相当于执行 --> war1(war2(f))() 

  4. 对有参数的函数进行装饰

    • def funcOut(fn):
          print("funcOut")
          def funcIn(aa, bb):
              print("funcIn1")
              fn(aa,bb)
              print("funcIn2")
          return funcIn
      
      @funcOut
      def test(a, b):
          print("a=%d,b=%d" % (a, b))
      # 装饰器装饰之后,这不是直接调用test方法,而是调用func_in方法
      test(1,2)
      

       结果

    • funcOut
      funcIn1
      a=1,b=2
      funcIn2

  5. 通用装饰器的使用

    • 一个装饰器可以装饰多个不同参数、不同返回值的函数
    • 如:
         def funcOut(fn):
              # 需要有参数,*args,**kwargs
              def funcIn(*args,**kwargs):
                  print("记录日志")
                  print('访问方法:'+fn.__name__)
                  # 需要有参数,*args,**kwargs
                  # 需要有返回值
                  return fn(*args,**kwargs)
              return funcIn
          
          # 待装饰函数1:无参数,无返回值
          @funcOut
          def test1():
              print("test1")
          test1()
          print("---------")
          # 待装饰函数2:无参数,有返回值
          @funcOut
          def test2():
              return "Hello"
          print(test2())
          print("---------")
          # 待装饰函数3:有参数,无返回值
          @funcOut
          def test3(a):
              print('a=%d'%a)
          test3(1)
          print("---------")
          # 待装饰函数3:有键值对参数,无返回值
          @funcOut
          def test4(**kwargs):
              print(kwargs)
          test4(a=1)
    • 结果
      记录日志
      访问方法:test1
      test1
      ---------
      记录日志
      访问方法:test2
      Hello
      ---------
      记录日志
      访问方法:test3
      a=1
      ---------
      记录日志
      访问方法:test3
      {'a': 1}

       

 

 

      

  

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!