闭包函数

  1. # 一:大前提:
  2. # 闭包函数=名称空间与作用域+函数嵌套+函数对象
  3. # 核心点:名字的查找关系是以函数定义阶段为准
  4. # 二:什么是闭包函数
  5. # "闭"函数指的该函数是内嵌函数
  6. # "包"函数指的该函数包含对外层函数作用域名字的引用(不是对全局作用域)
  7. # 闭包函数:名称空间与作用域的应用+函数嵌套
  8. # def f1():
  9. # x = 33333333333333333333
  10. # def f2():
  11. # print(x)
  12. # f2()
  13. # 闭包函数:函数对象
  14. # def f1():
  15. # x = 33333333333333333333
  16. # def f2():
  17. # print('函数f2:',x)
  18. # return f2
  19. # f=f1()
  20. # # print(f)

闭包应用

  1. import requests
  2. # 传参的方案一:
  3. # def get(url):
  4. # response=requests.get(url)
  5. # print(len(response.text))
  6. #
  7. # get('https://www.baidu.com')
  8. # get('https://www.cnblogs.com/linhaifeng')
  9. # get('https://zhuanlan.zhihu.com/p/109056932')
  10. # 传参的方案二:
  11. # 一次传参,多次调用不用再次传参
  12. def outter(url):
  13. # url='https://www.baidu.com'
  14. def get():
  15. response=requests.get(url)
  16. print(len(response.text))
  17. return get
  18. baidu=outter('https://www.baidu.com')
  19. baidu()
  20. cnblogs=outter('https://www.cnblogs.com/linhaifeng')
  21. cnblogs()
  22. zhihu=outter('https://zhuanlan.zhihu.com/p/109056932')
  23. zhihu()

无参装饰器

装饰器准备知识

  1. # 一:储备知识
  2. #1、 *args, **kwargs
  3. def index(x,y):
  4. print(x,y)
  5. def wrapper(*args,**kwargs):
  6. index(*args,**kwargs) #
  7. # index(y=222,x=111)
  8. wrapper(y=222,x=111)
  9. # 2、名称空间与作用域:名称空间的的"嵌套"关系是在函数定义阶段,即检测语法的时候确定的
  10. # 3、函数对象:
  11. # 可以把函数当做参数传入
  12. # 可以把函数当做返回值返回
  13. def index():
  14. return 123
  15. def foo(func):
  16. return func
  17. foo(index)
  18. #4、函数的嵌套定义:
  19. def outter(func):
  20. def wrapper():
  21. pass
  22. return wrapper
  23. # 闭包函数
  24. def outter():
  25. x=111
  26. def wrapper():
  27. x
  28. return wrapper
  29. f=outter()
  30. # 传参的方式一:通过参数的形式为函数体传值
  31. def wrapper(x):
  32. print(1)
  33. print(2)
  34. print(3)
  35. x
  36. wrapper(1)
  37. wrapper(2)
  38. wrapper(3)
  39. #传参的方式二:通过闭包的方式为函数体传值
  40. def outter(x):
  41. # x=1
  42. def wrapper():
  43. print(1)
  44. print(2)
  45. print(3)
  46. x
  47. return wrapper # return outter内的wrapper那个函数的内地址
  48. # f1=outter(1)
  49. # f2=outter(2)
  50. # f3=outter(3)
  51. wrapper=outter(1)

装饰器理论

  1. """
  2. 1、什么是装饰器
  3. 器指的是工具,可以定义成成函数
  4. 装饰指的是为其他事物添加额外的东西点缀
  5. 合到一起的解释:
  6. 装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
  7. 2、为何要用装饰器
  8. 开放封闭原则
  9. 开放:指的是对拓展功能是开放的
  10. 封闭:指的是对修改源代码是封闭的
  11. 装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能

装饰器创造过程✨

无参装饰器 @outter

  1. 装饰器

    1. 将一个函数作为参数传递给第一个装饰函数outter—->第一层作用域—->返回第二个装饰函数wrapper
    2. 在第一个装饰器函数outter内部定义第二个装饰函数wrapper,接收任意参数—->第二层作用域
      1. 在wrapper中调用被装饰函数,并传递任意参数
      2. 存储被装饰函数返回值
      3. 将返回值返回
    3. 装饰过的函数和原函数属性不一致
      1. 原函数和wrapper函数的name,doc函数不一致
      2. 使用funtools的wraps装饰下wrapper函数 @wraps(func)
  2. 装饰器语法糖

    1. 定义好装饰器函数
    2. @装饰器函数 在被装饰函数上方
    3. 多个装饰器叠加,执行顺序是从上到下

有参装饰器 @tool(db_type=”pg”)

  1. 在outter装饰函数外面再套一层作用域,传递参数,这个参数一般给wrapper用,返回outter地址,outerr返回wrapper地址
  2. wrapper的参数给被装饰函数用
  3. 原始写法

    1. deco=auth(db_type=’ldap’)

      1. @deco # 账号密码的来源是ldap

类装饰器

:::info

思考题(选做),叠加多个装饰器,加载顺序与运行顺序。——》加载顺序是从上到下
# @deco1 # index=deco1(deco2.wrapper的内存地址)
# @deco2 # deco2.wrapper的内存地址=deco2(deco3.wrapper的内存地址)
# @deco3 # deco3.wrapper的内存地址=deco3(index)

:::

  1. 3、如何用
  2. """
  3. # 需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
  4. def index(x,y):
  5. time.sleep(3)
  6. print('index %s %s' %(x,y))
  7. index(111,222)
  8. # index(y=111,x=222)
  9. # index(111,y=222)
  10. #解决方案一:失败
  11. #问题:没有修改被装饰对象的调用方式,但是修改了其源代码
  12. import time
  13. def index(x,y):
  14. start=time.time()
  15. time.sleep(3)
  16. print('index %s %s' %(x,y))
  17. stop = time.time()
  18. print(stop - start)
  19. index(111,222)
  20. # 解决方案二:失败
  21. # 问题:没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能
  22. # 但是代码冗余
  23. import time
  24. def index(x,y):
  25. time.sleep(3)
  26. print('index %s %s' %(x,y))
  27. start=time.time()
  28. index(111,222)
  29. stop=time.time()
  30. print(stop - start)
  31. # 解决方案三:失败
  32. # 问题:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了
  33. import time
  34. def index(x,y):
  35. time.sleep(3)
  36. print('index %s %s' %(x,y))
  37. def wrapper():
  38. start=time.time()
  39. index(111,222)
  40. stop=time.time()
  41. print(stop - start)
  42. wrapper()
  43. # 方案三的优化一:将index的参数写活了
  44. import time
  45. def index(x,y,z):
  46. time.sleep(3)
  47. print('index %s %s %s' %(x,y,z))
  48. def wrapper(*args,**kwargs):
  49. start=time.time()
  50. index(*args,**kwargs) # index(3333,z=5555,y=44444)
  51. stop=time.time()
  52. print(stop - start)
  53. # wrapper(3333,4444,5555)
  54. # 方案三的优化二:在优化一的基础上把被装饰对象写活了,原来只能装饰index(增加了装饰任意函数)
  55. import time
  56. def index(x,y,z):
  57. time.sleep(3)
  58. print('index %s %s %s' %(x,y,z))
  59. def home(name):
  60. time.sleep(2)
  61. print('welcome %s to home page' %name)
  62. def outter(func): # func = index的内存地址
  63. def wrapper(*args,**kwargs):
  64. start=time.time()
  65. func(*args,**kwargs) # index的内存地址()
  66. stop=time.time()
  67. print(stop - start)
  68. return wrapper #增加了这一句,能够根据返回值函数来调用
  69. index=outter(index) # index=wrapper的内存地址
  70. home=outter(home) # home=wrapper的内存地址
  71. home('egon')
  72. # home(name='egon')
  73. # 方案三的优化三:将wrapper做的跟被装饰对象一模一样,以假乱真
  74. import time
  75. def index(x,y,z):
  76. time.sleep(3)
  77. print('index %s %s %s' %(x,y,z))
  78. def home(name):
  79. time.sleep(2)
  80. print('welcome %s to home page' %name)
  81. def outter(func):
  82. def wrapper(*args,**kwargs):
  83. start=time.time()
  84. res=func(*args,**kwargs) #res=接受了被装饰函数的返回值
  85. stop=time.time()
  86. print(stop - start)
  87. return res #return res,将接受的返回值通过wrapper函数返回去
  88. return wrapper
  89. # # 偷梁换柱:home这个名字指向的wrapper函数的内存地址
  90. home=outter(home)
  91. res=home('egon') # res=wrapper('egon')
  92. print('返回值--》',res)
  93. #大方向:如何在方案三的基础上不改变函数的调用方式

装饰器语法糖

  1. # 语法糖:让你开心的语法
  2. import time
  3. # 装饰器先定义好
  4. def timmer(func):
  5. def wrapper(*args,**kwargs):
  6. start=time.time()
  7. res=func(*args,**kwargs)
  8. stop=time.time()
  9. print(stop - start)
  10. return res
  11. return wrapper
  12. # 在被装饰对象正上方的单独一行写@装饰器名字
  13. # @timmer # index=timmer(index)
  14. def index(x,y,z):
  15. time.sleep(3)
  16. print('index %s %s %s' %(x,y,z))
  17. # @timmer # home=timmer(ome)
  18. def home(name):
  19. time.sleep(2)
  20. print('welcome %s to home page' %name)
  21. # index(x=1,y=2,z=3)
  22. # home('egon')
  23. # 思考题(选做),叠加多个装饰器,加载顺序与运行顺序。----》加载顺序是从上到下
  24. # @deco1 # index=deco1(deco2.wrapper的内存地址)
  25. # @deco2 # deco2.wrapper的内存地址=deco2(deco3.wrapper的内存地址)
  26. # @deco3 # deco3.wrapper的内存地址=deco3(index)
  27. # def index():
  28. # pass
  29. # 总结无参装饰器模板
  30. def outter(func):
  31. def wrapper(*args,**kwargs):
  32. # 1、调用原函数
  33. # 2、为其增加新功能
  34. res=func(*args,**kwargs)
  35. return res
  36. return wrapper

装饰器应用

  1. #为其他函数添加输入密码验证功能
  2. def auth(func):
  3. def wrapper(*args,**kwargs):
  4. # 1、调用原函数
  5. # 2、为其增加新功能
  6. name=input('your name>>: ').strip()
  7. pwd=input('your password>>: ').strip()
  8. if name == 'egon' and pwd == '123':
  9. res=func(*args,**kwargs)
  10. return res
  11. else:
  12. print('账号密码错误')
  13. return wrapper
  14. @auth
  15. def index():
  16. print('from index')
  17. index()

装饰器wraps模块

  1. #偷梁换柱,即将原函数名指向的内存地址偷梁换柱成wrapper函数
  2. # 所以应该将wrapper做的跟原函数一样才行
  3. from functools import wraps
  4. def outter(func):
  5. @wraps(func) #打印后面index的摘要或者函数名称,将打印的是index的摘要或者函数名称,不是wrapper的摘要、名称
  6. def wrapper(*args, **kwargs):
  7. """这个是主页功能"""
  8. res = func(*args, **kwargs) # res=index(1,2)
  9. return res
  10. # 手动将原函数的属性赋值给wrapper函数
  11. # 1、函数wrapper.__name__ = 原函数.__name__
  12. # 2、函数wrapper.__doc__ = 原函数.__doc__
  13. # wrapper.__name__ = func.__name__
  14. # wrapper.__doc__ = func.__doc__
  15. return wrapper
  16. @outter # index=outter(index)
  17. def index(x,y):
  18. """这个是主页功能"""
  19. print(x,y)
  20. print(index.__name__)
  21. print(index.__doc__) #help(index)

有参装饰器

知识准备

  1. # 一:知识储备
  2. # 由于语法糖@的限制,outter函数只能有一个参数,并且该才是只用来接收
  3. # 被装饰对象的内存地址
  4. def outter(func):
  5. # func = 函数的内存地址
  6. def wrapper(*args,**kwargs):
  7. res=func(*args,**kwargs)
  8. return res
  9. return wrapper
  10. # @outter # index=outter(index) # index=>wrapper
  11. @outter # outter(index)
  12. def index(x,y):
  13. print(x,y)
  14. # 偷梁换柱之后
  15. # index的参数什么样子,wrapper的参数就应该什么样子
  16. # index的返回值什么样子,wrapper的返回值就应该什么样子
  17. # index的属性什么样子,wrapper的属性就应该什么样子==》from functools import wraps

有参装饰器创造过程

  1. # 山炮玩法:
  2. def auth(func,db_type):
  3. def wrapper(*args, **kwargs):
  4. name=input('your name>>>: ').strip()
  5. pwd=input('your password>>>: ').strip()
  6. if db_type == 'file':
  7. print('基于文件的验证')
  8. if name == 'egon' and pwd == '123':
  9. res = func(*args, **kwargs)
  10. return res
  11. else:
  12. print('user or password error')
  13. elif db_type == 'mysql':
  14. print('基于mysql的验证')
  15. elif db_type == 'ldap':
  16. print('基于ldap的验证')
  17. else:
  18. print('不支持该db_type')
  19. return wrapper
  20. #
  21. # @auth # 账号密码的来源是文件
  22. def index(x,y):
  23. print('index->>%s:%s' %(x,y))
  24. # @auth # 账号密码的来源是数据库
  25. def home(name):
  26. print('home->>%s' %name)
  27. # @auth # 账号密码的来源是ldap
  28. def transfer():
  29. print('transfer')
  30. index=auth(index,'file') #给装饰器函数添加了第二个参数
  31. home=auth(home,'mysql')
  32. transfer=auth(transfer,'ldap')
  33. # index(1,2)
  34. # home('egon')
  35. # transfer()
  36. #======================================================
  37. # 山炮二
  38. def auth(db_type):
  39. def deco(func):
  40. def wrapper(*args, **kwargs):
  41. name=input('your name>>>: ').strip()
  42. pwd=input('your password>>>: ').strip()
  43. if db_type == 'file':
  44. print('基于文件的验证')
  45. if name == 'egon' and pwd == '123':
  46. res = func(*args, **kwargs)
  47. return res
  48. else:
  49. print('user or password error')
  50. elif db_type == 'mysql':
  51. print('基于mysql的验证')
  52. elif db_type == 'ldap':
  53. print('基于ldap的验证')
  54. else:
  55. print('不支持该db_type')
  56. return wrapper
  57. return deco
  58. deco=auth(db_type='file')
  59. @deco # 账号密码的来源是文件
  60. def index(x,y):
  61. print('index->>%s:%s' %(x,y))
  62. deco=auth(db_type='mysql')
  63. @deco # 账号密码的来源是数据库
  64. def home(name):
  65. print('home->>%s' %name)
  66. deco=auth(db_type='ldap')
  67. @deco # 账号密码的来源是ldap
  68. def transfer():
  69. print('transfer')
  70. index(1,2)
  71. home('egon')
  72. transfer()

有参装饰器语法糖

  1. # 语法糖
  2. def auth(db_type):
  3. def deco(func):
  4. def wrapper(*args, **kwargs):
  5. name = input('your name>>>: ').strip()
  6. pwd = input('your password>>>: ').strip()
  7. if db_type == 'file':
  8. print('基于文件的验证')
  9. if name == 'egon' and pwd == '123':
  10. res = func(*args, **kwargs) # index(1,2)
  11. return res
  12. else:
  13. print('user or password error')
  14. elif db_type == 'mysql':
  15. print('基于mysql的验证')
  16. elif db_type == 'ldap':
  17. print('基于ldap的验证')
  18. else:
  19. print('不支持该db_type')
  20. return wrapper
  21. return deco
  22. @auth(db_type='file') # @deco # index=deco(index) # index=wrapper
  23. def index(x, y):
  24. print('index->>%s:%s' % (x, y))
  25. @auth(db_type='mysql') # @deco # home=deco(home) # home=wrapper
  26. def home(name):
  27. print('home->>%s' % name)
  28. @auth(db_type='ldap') # 账号密码的来源是ldap
  29. def transfer():
  30. print('transfer')
  31. # index(1, 2)
  32. # home('egon')
  33. # transfer()

有参装饰器模板

  1. # 有参装饰器模板
  2. def 有参装饰器(x,y,z):
  3. def outter(func):
  4. def wrapper(*args, **kwargs):
  5. res = func(*args, **kwargs)
  6. return res
  7. return wrapper
  8. return outter
  9. @有参装饰器(1,y=2,z=3)
  10. def 被装饰对象():
  11. pass

叠加多个装饰器分析

  1. # 一、叠加多个装饰器的加载、运行分析(了解***)
  2. def deco1(func1): # func1 = wrapper2的内存地址
  3. def wrapper1(*args,**kwargs):
  4. print('正在运行===>deco1.wrapper1')
  5. res1=func1(*args,**kwargs)
  6. return res1
  7. return wrapper1
  8. def deco2(func2): # func2 = wrapper3的内存地址
  9. def wrapper2(*args,**kwargs):
  10. print('正在运行===>deco2.wrapper2')
  11. res2=func2(*args,**kwargs)
  12. return res2
  13. return wrapper2
  14. def deco3(x):
  15. def outter3(func3): # func3=被装饰对象index函数的内存地址
  16. def wrapper3(*args,**kwargs):
  17. print('正在运行===>deco3.outter3.wrapper3')
  18. res3=func3(*args,**kwargs)
  19. return res3
  20. return wrapper3
  21. return outter3
  22. # 加载顺序自下而上(了解)
  23. @deco1 # index=deco1(wrapper2的内存地址) ===> index=wrapper1的内存地址
  24. @deco2 # index=deco2(wrapper3的内存地址) ===> index=wrapper2的内存地址
  25. @deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址
  26. def index(x,y):
  27. print('from index %s:%s' %(x,y))
  28. # 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3
  29. index(1,2) # wrapper1(1,2)

装饰器详解 - 图1