闭包

基本概念

  • 定义:在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
  • 简单举例

image.png

  • 在此例子中,函数cal_y 和 变量 k、b构成闭包
    • 闭包的作用:提高代码的可复用性
    • 注意:由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

      外部函数中的变量

  • 情形一:内部函数在修改外部的变量前访问了该变量,则访问前必须加修饰,否则访问时会报错

image.png

  • 情形二:使用 nonlocal 修饰外部函数的局部变量,然后可以访问和修改

image.png

  • 注意:外部函数的形参也是用 nonlocal 修饰

image.png

  • 情形三:使用 global 修饰外部函数外的全局变量,然后可以访问和修改

image.png

装饰器

基本概念

  • 开放封闭原则:已经实现的功能代码不允许被修改,但可以被扩展
    • 开放:对扩展开放
    • 封闭:对已实现的功能代码封闭
  • 简单举例

image.png

  • 上面的@outer_func其实就是将下面的函数 f 塞进函数 outer_func 中,将返回的函数 inner_func 再重新赋值给 f,等价于

image.png

  • 装饰器在调用函数之前,已经被Python解释器执行了,所以在调用函数之前已经修饰好了,只需要调用即可
  • 注意:如果代码在闭包内的外部函数和内部函数之间,则该部分代码在装饰函数时就已经得到了执行,而不是调用的时候

image.png

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

规律:离得近的先装饰,离得远的后装饰,先装饰的后执行

  1. def first_outer_func(func):
  2. print('111')
  3. def first_inner_func(*args, **kwargs):
  4. print('222')
  5. return func(*args, **kwargs)
  6. return first_inner_func
  7. def second_outer_func(func):
  8. print('333')
  9. def second_inner_func(*args, **kwargs):
  10. print('444')
  11. return func(*args, **kwargs)
  12. return second_inner_func
  13. @first_outer_func
  14. @second_outer_func
  15. def test():
  16. print('555')
  17. test()
  18. '''
  19. 执行结果:
  20. 333
  21. 111
  22. 222
  23. 444
  24. 555
  25. '''
  1. def make_bold(fn):
  2. def wrapped():
  3. return "<b>" + fn() + "</b>"
  4. return wrapped
  5. def make_italic(fn):
  6. def wrapped():
  7. return "<i>" + fn() + "</i>"
  8. return wrapped
  9. @make_bold
  10. @make_italic
  11. def test():
  12. return 'hello-world'
  13. print(test())
  14. '''
  15. 执行结果:
  16. <b><i>hello-world</i></b>
  17. '''

装饰器的分类

无参数的装饰器

  1. def outer_func(func):
  2. def inner_func():
  3. print('func_name: %s' % func.__name__)
  4. func()
  5. return inner_func
  6. @outer_func
  7. def f():
  8. print(f.__name__)
  9. f()
  10. '''
  11. 执行结果:
  12. func_name: f
  13. inner_func
  14. '''

有参数的装饰器

  1. def outer_func(func):
  2. def inner_func(a, b):
  3. func(a, b)
  4. return inner_func
  5. @outer_func
  6. def f(a, b):
  7. print(a + b)
  8. f(1, 2) # 3
  9. f(3, 4) # 7

不定长参数的装饰器

采用 args 和 *kwargs 的参数设计,无论是有参数,无参数,还是多个参数,均能实现传递

  1. def outer_func(func):
  2. def inner_func(*args, **kwargs):
  3. func(*args, **kwargs)
  4. return inner_func
  5. @outer_func
  6. def f(num, *args, **kwargs):
  7. print(num)
  8. print(args)
  9. print(kwargs)
  10. print()
  11. f(1)
  12. f(3, 4, 5)
  13. f(7, 8, 9, 10, 11, a=100, b=200)
  14. '''
  15. 执行结果:
  16. 1
  17. ()
  18. {}
  19. 3
  20. (4, 5)
  21. {}
  22. 7
  23. (8, 9, 10, 11)
  24. {'a': 100, 'b': 200}
  25. '''

带有return的装饰器

为了让装饰器更通用,一般情况下加上return

  1. def outer_func(func):
  2. def inner_func():
  3. func()
  4. return inner_func
  5. @outer_func
  6. def f():
  7. return '111'
  8. print(f()) # None
  1. def outer_func(func):
  2. def inner_func():
  3. return func()
  4. return inner_func
  5. @outer_func
  6. def f():
  7. return '111'
  8. print(f()) # 111

带有外部变量的装饰器

  1. def set_level(level_num):
  2. def set_func(func):
  3. def call_func():
  4. if level_num == 1:
  5. print('AAA')
  6. elif level_num == 2:
  7. print('BBB')
  8. return func()
  9. return call_func
  10. return set_func
  11. @set_level(1)
  12. def test1():
  13. print('111')
  14. return 'aaa'
  15. @set_level(2)
  16. def test2():
  17. print('222')
  18. return 'bbb'
  19. print(test1())
  20. print(test2())
  21. '''
  22. 执行结果:
  23. AAA
  24. 111
  25. aaa
  26. BBB
  27. 222
  28. bbb
  29. '''