目录
一、什么是函数装饰器
二、函数装饰器的执行时机
三、变量作用域
四、闭包
五、保留函数的元数据
七、使用lru_cache缓存函数执行结果
八、使用singledispatch实现泛型函数
九、通过参数控制函数装饰器的行为
一、什么是函数装饰器
1.函数装饰器是Python提供的一种增强函数功能的标记函数;

2.装饰器是可调用的函数对象,其参数是另一个函数(被装饰的函数);

我们可以使用修饰器来封装某个函数,从而让程序在执行这个函数之前与执行完这个函数之后,分别运行某些代码。这意味着,调用者传给函数的参数值、函数返回给调用者的值,以及函数抛出的异常,都可以由修饰器访问并修改。这是个很有用的机制,能够确保用户以正确的方式使用函数,也能够用来调试程序或实现函数注册功能,此外还有许多用途。

二、函数装饰器的执行时机
函数装饰器在被装饰函数编译解析之后直接执行,装饰器是按照从上到下执行的;

函数装饰器内部定义的返回函数依附在装饰器的执行环境中;

函数装饰器每次执行都会生成新的返回函数;

  1. import sys
  2. def dec1(m):
  3. print(f'{sys._getframe().f_code.co_name} is execute, arg {m.__name__}')
  4. def newm1():
  5. print(f'{sys._getframe().f_code.co_name}')
  6. return newm1;
  7. @dec1
  8. def m1():
  9. print(f'{sys._getframe().f_code.co_name}')
  10. @dec1
  11. def m11():
  12. print(f'{sys._getframe().f_code.co_name}')
  13. if __name__ == '__main__':
  14. print(m1)
  15. print(m11)
  16. print(f'm1==m11:{m1==m11}')
  17. # dec1 is execute, arg m1
  18. # dec1 is execute, arg m11
  19. # <function dec1.<locals>.newm1 at 0x7fdfa97d9160>
  20. # <function dec1.<locals>.newm1 at 0x7fdfa97d91f0>
  21. # m1==m11:False

三、变量作用域
Python中将变量声明和赋值操作合一,很容易导致函数局部变量覆盖函数外的变量

  1. b=6
  2. def f():
  3. print(b)
  4. f()
  5. # 6
  1. b=6
  2. def f():
  3. print(b)
  4. b = 9
  5. f()
  6. # UnboundLocalError: local variable 'b' referenced before assignment

通过生成的字节码可以看到两者对变量b的处理的差异,前者直接LOAD_GLOBAL,后者是LOAD_FAST,但是给b赋值却在print之后导致报错;

  1. from dis import dis
  2. b=6
  3. def f():
  4. print(b)
  5. # b = 9
  6. dis(f)
  7. # 5 0 LOAD_GLOBAL 0 (print)
  8. # 2 LOAD_GLOBAL 1 (b)
  9. # 4 CALL_FUNCTION 1
  10. # 6 POP_TOP
  11. # 8 LOAD_CONST 0 (None)
  12. # 10 RETURN_VALUE
  1. from dis import dis
  2. b=6
  3. def f():
  4. print(b)
  5. b = 9
  6. # 5 0 LOAD_GLOBAL 0 (print)
  7. # 2 LOAD_FAST 0 (b)
  8. # 4 CALL_FUNCTION 1
  9. # 6 POP_TOP
  10. # 6 8 LOAD_CONST 1 (9)
  11. # 10 STORE_FAST 0 (b)
  12. # 12 LOAD_CONST 0 (None)
  13. # 14 RETURN_VALUE

可以使用global来强制声明b是全局变量,然后就可以重新赋值了;

  1. b=6
  2. def f():
  3. global b
  4. print(b)
  5. b = 9
  6. f()
  7. # 6

四、闭包
闭包是是指可以访问非在函数体内定义的非全局变量的函数;

通过函数的codeclosure可以看到局部变量和自由变量及闭包的情况;

  1. def makesum():
  2. sum = [0]
  3. def s(val):
  4. sum[0] += val
  5. return sum[0]
  6. return s
  7. s = makesum()
  8. print(s(1))
  9. print(s(2))
  10. print(s.__code__.co_varnames)
  11. print(s.__code__.co_freevars)
  12. print(s.__closure__)
  13. print(s.__closure__[0].cell_contents)
  14. # 1
  15. # 3
  16. # ('val',)
  17. # ('sum',)
  18. # (<cell at 0x7f63321f1b20: list object at 0x7f63321e8a00>,)
  19. # [3]

基于三中Python变量作用域的缘故,上边的sum只能使用列表对象,python提供的nonlocal关键字可以直接使用int类型的变量;

  1. def makesum():
  2. sum = 0
  3. def s(val):
  4. nonlocal sum
  5. sum += val
  6. return sum
  7. return s
  8. s = makesum()
  9. print(s(1))
  10. print(s(2))
  11. print(s.__code__.co_varnames)
  12. print(s.__code__.co_freevars)
  13. print(s.__closure__)
  14. print(s.__closure__[0].cell_contents)
  15. # 1
  16. # 3
  17. # ('val',)
  18. # ('sum',)
  19. # (<cell at 0x7f73e6a4ab20: int object at 0x7f73e6b47970>,)
  20. # 3

五、保留函数的元数据
函数装饰器默认会使用返回的函数完全取代被装饰的函数,这样可能会导致序列化或者开发工具智能提示的问题;可以使用functools.wraps来保留原始函数的标准属性(name、module、annotations等);

  1. import functools
  2. def dec(func):
  3. def real():
  4. '''this is real function'''
  5. pass
  6. return real
  7. def wrapsdec(func):
  8. @functools.wraps(func)
  9. def real():
  10. '''this is real function'''
  11. pass
  12. return real
  13. @dec
  14. def max(nums):
  15. '''this is max function'''
  16. pass
  17. @wrapsdec
  18. def sum(nums):
  19. '''this is sum function'''
  20. print(max)
  21. print(max.__name__)
  22. print(max.__doc__)
  23. print(help(max))
  24. print()
  25. print(sum)
  26. print(sum.__name__)
  27. print(sum.__doc__)
  28. print(help(sum))
  29. # <function dec.<locals>.real at 0x7f1bfd4241f0>
  30. # real
  31. # this is real function
  32. # Help on function real in module __main__:
  33. #
  34. # real()
  35. # this is real function
  36. #
  37. # None
  38. #
  39. # <function sum at 0x7f1bfd424280>
  40. # sum
  41. # this is sum function
  42. # Help on function sum in module __main__:
  43. #
  44. # sum(nums)
  45. # this is sum function
  46. #
  47. # None

六、支持关键字参数、位置参数

  1. def dec(func):
  2. def real(*args, **kwargs):
  3. result = func(*args, **kwargs)
  4. return result
  5. return real
  6. @dec
  7. def sum(*nums, **kwargs):
  8. s = 0
  9. for n in nums:
  10. s = s + n
  11. for a in kwargs.values():
  12. s = s + a
  13. return s
  14. print(sum(1,2,3,first=1))

七、使用lru_cache缓存函数执行结果
lru_cache内部使用函数的参数作为key,使用dict进行缓存执行结果,减少重复计算;

默认支持缓存128条记录,超过后采用LRU方式丢弃多余记录;

需要注意执行中不要改变参数,否则会影响字典key的有效性;

  1. from functools import lru_cache
  2. @lru_cache()
  3. def fibonacci(n):
  4. print(f'fibonacci({n})')
  5. if n<2:
  6. return n
  7. return fibonacci(n-1) + fibonacci(n-2)
  8. print(fibonacci(6))
  9. # fibonacci(6)
  10. # fibonacci(5)
  11. # fibonacci(4)
  12. # fibonacci(3)
  13. # fibonacci(2)
  14. # fibonacci(1)
  15. # fibonacci(0)
  16. # 8

八、使用singledispatch实现泛型函数
python不支持方法或者函数的重载,我们无法单独定义不同参数类型的同名函数;

singledispatch提供了这样一种能力,其通过注册具体的参数类型和关联的函数;

我们可以在自己的模块定义自己的类型,并实现自己的自定义函数;

  1. import math
  2. import numbers
  3. from functools import singledispatch
  4. @singledispatch
  5. def absall(obj):
  6. return abs(obj)
  7. @absall.register(numbers.Number)
  8. def numabs(num):
  9. return abs(num)
  10. @absall.register(numbers.Complex)
  11. def cabs(c):
  12. return math.sqrt(c.real*c.real + c.imag* c.imag)
  13. class Line:
  14. def __init__(self, startx, starty, endx, endy):
  15. self.startx = startx
  16. self.starty = starty
  17. self.endx = endx
  18. self.endy = endy
  19. @absall.register(Line)
  20. def lineabs(line):
  21. y =line.endy - line.starty
  22. x = line.endx - line.startx
  23. return math.sqrt(x*x + y*y)
  24. print(absall(-1.1))
  25. print(absall(3+4j))
  26. l = Line(1,2,4,6)
  27. print(absall(l))
  28. # 1.1
  29. # 5.0
  30. # 5.0

九、通过参数控制函数装饰器的行为
函数装饰器本身不支持传递参数,解析源代码的时候,python会将被装饰的函数对象作为第一个参数传递给装饰器;

我们可以通过在函数装饰器外再嵌套一个函数工厂来承载装饰特定函数的时候设置的参数;

  1. def accesscontrol(checkuser=True, updatesession=True):
  2. def dec(func):
  3. def checkuserf():
  4. print('check user')
  5. return True
  6. def updatesessionf():
  7. print('update session')
  8. return True
  9. def access():
  10. if checkuser:
  11. checkuserf()
  12. if updatesession:
  13. updatesessionf()
  14. func()
  15. return access
  16. return dec
  17. @accesscontrol()
  18. def pushitem():
  19. print('pushitem')
  20. return True
  21. @accesscontrol(checkuser=False)
  22. def getitem():
  23. print('getitem')
  24. return True
  25. # pushitem()
  26. # print()
  27. # getitem()
  28. #
  29. # check user
  30. # update session
  31. # pushitem
  32. #
  33. # update session
  34. # getitem

更多相关文章

  1. ES6 基础语法总结
  2. md5加密与数组函数
  3. 五.Python面向对象
  4. PHP字符运算、类型转换和系统函数
  5. ES6基本语法,Axios异步网络请求介绍
  6. Python的装饰器原来是这么用的
  7. 循环语句 超级全局变量 及 cURL函数
  8. ES6 和异步网络请求
  9. udp数据报从网卡驱动到用户空间流程总结

随机推荐

  1. Android中横竖屏切换的问题
  2. Android 应用程序之间数据共享—ContentP
  3. Android GWES之Android消息系统
  4. 相对布局各种属性说明
  5. Android(安卓)录音实现(AudioRecord)
  6. 解决NestedScrollView中嵌套ViewPager,Vie
  7. ListView去掉分割线
  8. 【Android】gravity、layout_gravity 以
  9. 手把手教学OpenCV-android-sdk 配置以及
  10. Android架构师之路