• python闭包与装饰器


    一、闭包

    闭包定义:在函数嵌套(函数内再定义函数)的前提下,内部函数使用了外部函数变量或参数,且外部函数返回了内部函数,则将这个使用了外部函数变量的内部函数称为闭包

    闭包作用特点

    • 可以保存外部函数内的变量,不会随外部函数调用完而销毁
    • 闭包可以提高代码的可重用性,无需再手动定义额外的功能函数
    • 由于闭包引用了外部函数变量,外部函数变量没有及时释放,消耗内存
    1. def outFunc(a): # 定义外部函数,a=5
    2. b = 2
    3. def inFunc(c): # 定义内部函数 c=1和3
    4. result = a + b + c # 内部函数使用了外部函数参数a和变量b
    5. print('结果为:', result)
    6. return inFunc # 外部函数返回内部函数,该内部函数即为闭包,是一个引用,该引用存入了out对象中,故执行out()时相当于运行了inFunc()函数
    7. out = outFunc(5) # 创建闭包实例,执行outFunc函数,传参给了a
    8. out(1) # 执行闭包inFunc函数,传参给了c
    9. out(3)
    10. 输出: # 由结果可知闭包保存了外部函数的参数a和变量b,每次执行闭包都是在a=5,b=2基础上计算
    11. 结果为: 8
    12. 结果为: 10

    1.1闭包的使用

    根据配置使用闭包实现多人之间的对话

    1. def configName(name):
    2. def sayInfo(info):
    3. print(name + ": " + info)
    4. return sayInfo
    5. xy = configName("XY")
    6. xy("你好,在不")
    7. xy("你好!在不在?")
    8. yx = configName("YX")
    9. yx("不在。")
    10. 输出:
    11. XY: 你好,在不
    12. XY: 你好!在不在?
    13. YX: 不在。!

    1.2修改闭包内使用的外部变量

    使用nonlocal关键字完成,具体举例如下

    1. def outFunc(a): # a=1
    2. b = 2
    3. def inFunc(c): # c=8和3
    4. # a = 5 # 错误修改,本意要修改外部参数a的值,实际在内部函数定义了一个局部变量a
    5. # 正确修改
    6. nonlocal a # 告诉解释器,此处使用的是外部变量a
    7. a = 5 # 修改
    8. result = a + b + c
    9. print('结果为:', result)
    10. print('修改前的a值:', a) # a=1
    11. inFunc(8)
    12. print('修改后的a值:', a) # a=5
    13. return inFunc # 外部函数返回内部函数,该内部函数即闭包
    14. out = outFunc(1) # 创建闭包实例,执行outFunc函数,a=1
    15. out(3) # 执行闭包函数inFunc,c=3
    16. 输出:
    17. 修改前的a值: 1
    18. 结果为: 15
    19. 修改后的a值: 5
    20. 结果为: 10

    二、装饰器

    装饰器定义:指为已有函数增加额外功能的函数,本质上就是一个闭包函数,闭包函数有且只有一个参数并为函数类型,规定已实现的功能代码不允许被修改,但可以被扩展

    装饰器功能特点

    • 不修改已有函数的源代码
    • 不修改已有函数的调用方式
    • 为已有函数增加额外的调用功能
    1. def outFunc(out): # 定义装饰器,out为被装饰的目标函数,只有一个参数,且为函数类型(下面调用了)
    2. def inFunc(): # 在内部函数中对已有函数进行装饰
    3. print('额外功能……')
    4. out() # 执行被装饰的目标函数,即执行调用ordinaryFunc函数
    5. return inFunc
    6. # def ordinaryFunc():
    7. # print('已有普通函数……')
    8. # ordinaryFunc = outFunc(ordinaryFunc) # 调用装饰器来装饰已有的ordinaryFunc函数,ordinaryFunc=inFunc
    9. # ordinaryFunc() # 执行inFunc函数
    10. # 语法糖写法,书写格式为:@装饰器名字,通过语法糖可以完成对已有函数的装饰
    11. @outFunc # 等价于ordinary = outFunc(ordinaryFunc)
    12. def ordinaryFunc(): # 已有函数不变
    13. print('已有普通函数……')
    14. ordinaryFunc() # 执行inFunc函数,调用方式不变
    15. 输出:
    16. 额外功能……
    17. 已有普通函数……

    2.1装饰器使用

    实例:装饰器实现统计已有函数执行时间,可知装饰器能够在不改变已有函数源代码及调用方式的前提下,对已有函数进行功能的扩展

    1. import time
    2. def getTime(func):
    3. def inFunc():
    4. begin = time.time()
    5. func()
    6. end = time.time()
    7. print('func()函数的执行时间为:%.5f' % (end-begin))
    8. return inFunc
    9. @getTime
    10. def ordinaryFunc():
    11. for i in range(100000):
    12. print('这是第%d条数据!' % i)
    13. ordinaryFunc()
    14. 输出:
    15. ……
    16. ……
    17. 这是第99998条数据!
    18. 这是第99999条数据!
    19. func()函数执行时间为:1.77821

    2.2通用装饰器的使用

    1.装饰器带有参数的函数

    1. def outFunc(func):
    2. def inFunc(a, b): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
    3. func(a, b)
    4. return inFunc
    5. @outFunc # addSum = outFunc(addSum),即addSum=inFunc
    6. def addSum(c, d):
    7. sum = c + d
    8. print('结果为:', sum)
    9. addSum(10, 3) # 调用执行inFunc
    10. 输出:
    11. 结果为: 13

    2.装饰器带返回值的函数

    1. # 装饰器带返回值的函数
    2. def outFunc(func):
    3. def inFunc(a, b):
    4. result = func(a, b) # 调用执行sum()
    5. return result
    6. return inFunc
    7. @outFunc
    8. def sum(c, d):
    9. sum = c + d
    10. return sum
    11. sum2 = sum(10, 3) # 调用执行inFunc
    12. print('结果为:', sum2)
    13. 输出:
    14. 结果为: 13

    3.装饰带不定长参数的函数

    1. # 装饰带不定长参数和返回值的函数
    2. def outFunc(func):
    3. def inFunc(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
    4. func(*args, **kwargs) # 执行addSum
    5. return inFunc
    6. @outFunc # addSum = outFunc(addSum),即addSum=inFunc
    7. def addSum(*args, **kwargs):
    8. sum = 0
    9. for v in args:
    10. sum += v
    11. for v in kwargs.values():
    12. sum += v
    13. print('结果为:', sum)
    14. addSum(1, 3, a=5, b=2, c=2) # 调用执行inFunc
    15. 输出:
    16. 结果为: 13

    4.通用装饰器

    1. # 通用装饰器
    2. def outFunc(func):
    3. def inFunc(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
    4. result = func(*args, **kwargs) # 执行addSum
    5. return result
    6. return inFunc
    7. @outFunc # addSum = outFunc(addSum),即addSum=inFunc
    8. def addSum(*args, **kwargs):
    9. sum = 0
    10. for v in args:
    11. sum += v
    12. for v in kwargs.values():
    13. sum += v
    14. return sum
    15. @outFunc
    16. def subtraction(a,b):
    17. result1 = a - b
    18. print('a - b结果为:', result1)
    19. result2 = addSum(1, 3, a=5) # 调用执行inFunc,1、3为args元组,a=5为kwargs字典
    20. print('结果为:', result2)
    21. subtraction(9, 4) # 调用执行inFunc,9、4为args元组
    22. 输出:
    23. 结果为: 9
    24. a - b结果为: 5

    2.3多个装饰器的使用

    装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,即由内到外的装饰

    1. def outFunc(func):
    2. def inFunc(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
    3. return "
      " + func() + "
      "
    4. return inFunc
    5. def outFunc2(func):
    6. def inFunc2(*args, **kwargs): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
    7. return "

      " + func() + "

      "
    8. return inFunc2
    9. @outFunc
    10. @outFunc2
    11. def info():
    12. return "Hello World!"
    13. result = info() # 首先执行inFunc
    14. print('结果为:', result)
    15. 输出:
    16. 结果为:

      Hello World!

    单步调试执行过程如下

    2.4带参数的装饰器

    使用带参数的装饰器,需在装饰器外再包裹一个函数,该函数用来接收参数,返回是装饰器,因为 @符号需要配合装饰器实例使用

    1. def out(flag): # 在装饰器外包裹一个out函数,用来接收参数,返回装饰器,因为@后必须为装饰器实例
    2. def outFunc(func): # 装饰器只能接收一个参数,且只能是函数类型,故需在外部再加一个out函数
    3. def inFunc(c, d): # 使用装饰器装饰已有函数时,内部函数类型和已有函数保持一致
    4. if flag=="+":
    5. print("正在进行加法运算")
    6. elif flag=="-":
    7. print('正在进行减法运算')
    8. result = func(c, d)
    9. return result
    10. return inFunc
    11. return outFunc # 返回装饰器
    12. @out("+") # 使用装饰器装饰函数
    13. def add(a, b):
    14. sum = a + b
    15. return sum
    16. @out("-")
    17. def sub(a, b):
    18. result = a - b
    19. return result
    20. result1 = add(3, 5) # 执行inFunc,先进行加减判定,再执行func(),根据判定确定执行add()或sub()函数
    21. result2 = sub(5, 2)
    22. print('加法运算result1结果为:%d,减法运算result2结果为:%d' % (result1, result2))
    23. 输出:
    24. 正在进行加法运算
    25. 正在进行减法运算
    26. 加法运算result1结果为:8,减法运算result2结果为:3

    2.5类装饰器

    • 要想类的实例对象能够像函数一样调用,需在类中使用call方法,把类的实例变成可调用对象(callable),即能像调用函数一样进行调用
    • call方法里进行对func函数的装饰,可以添加额外的功能
    1. class decorate(object): # 类装饰器
    2. def __init__(self, func):
    3. self.func = func
    4. def __call__(self, *args, **kwargs): # 实现该方法,表示对象是一个可调用对象,能像调用函数一样进行调用
    5. print('添加装饰功能')
    6. self.func() # 执行info()方法
    7. @decorate # 等价于info = decorate(info),故需提供一个init方法,并多增加一个func参数
    8. def info():
    9. print('已有普通函数')
    10. info() # 执行__call__()方法
    11. 输出:
    12. 添加装饰功能
    13. 已有普通函数

     学习导航:http://xqnav.top/

  • 相关阅读:
    使用reposync同步远程yum源镜像仓库到本地和制作本地yum源(以同步EPEL为例)
    在 msys2@mingw 下编译 BVLC/Caffe
    【免费送书】机器学习和数据分析的关系是怎么样的,要学习的话哪者为先?
    第 40 章 呼吸灯与 SPWM 波
    【统计学概念】初学者指南:了解置信区间
    javaEE初阶---linux
    SNMP 网络协议介绍
    图片识别文字其实很简单,快来看看这几招
    Seata之@GlobalTransactional验证
    Linux:最全的开发常用命令
  • 原文地址:https://blog.csdn.net/qq_43874317/article/details/127783479