demo_wraps.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. '''
  2. 装饰器的本质 = 一个"接收函数、返回函数"的函数
  3. 前提:函数也是"对象",可以被传来传去
  4. 这是装饰器的地基。函数名后面不加括号,指的是函数本身,可以赋值、传递:
  5. # '''
  6. # def hello():
  7. # '''打招呼'''
  8. # print("hi")
  9. # print(hello.__name__)
  10. # print(hello.__doc__)
  11. #
  12. # f = hello # 把函数本身赋给 f(注意没有括号!)
  13. # f() # 输出 hi —— f 现在就是 hello
  14. # # ##todo: 第1级:装饰器的本质 = 一个"接收函数、返回函数"的函数
  15. # def my_decorator(func): # 接收一个函数 func
  16. # def wrapper(): # 定义一个新函数,在原函数前后加点料
  17. # print("【前】")
  18. # func() # 调用原函数
  19. # print("【后】")
  20. # return wrapper # 返回这个新函数
  21. # #
  22. # # def say_hi():
  23. # # print("hi")
  24. #
  25. # # say_hi = my_decorator(say_hi) # 手动"装饰":用包装版替换掉原来的 say_hi
  26. # # say_hi()
  27. # # 输出: 【前】 / hi / 【后】
  28. # # #todo: 第2级:@ 只是上面那行的简写(语法糖)
  29. # @my_decorator # 这一行,完全等价于 say_hi = my_decorator(say_hi)
  30. # def say_hi():
  31. # print("hi")
  32. #
  33. # say_hi() # 同样输出: 【前】 / hi / 【后】
  34. # #todo:第3级:让 wrapper 能接收任意参数 —— *args, **kwargs
  35. # def my_decorator(func):
  36. # def wrapper(*args, **kwargs): # 接住所有参数
  37. # print("【前】")
  38. # result = func(*args, **kwargs) # 原样转交参数,并接住返回值
  39. # print("【后】")
  40. # return result # 把原函数的结果返回出去(别漏!)
  41. # return wrapper
  42. #
  43. # @my_decorator
  44. # def add(a, b):
  45. # '''这是加法运算'''
  46. # return a + b
  47. # #
  48. # # print(add(3, 5)) # 输出: 【前】 / 【后】 / 8
  49. # # print(add.__name__) # wrapper ← 名字竟然变成了 wrapper!
  50. # # print(add.__doc__) # None ← 那句文档没了!
  51. # # #
  52. # # #
  53. # @my_decorator
  54. # def login(): pass
  55. #
  56. # @my_decorator
  57. # def logout(): pass
  58. #
  59. # print(login.__name__, logout.__name__)
  60. # # 不加 wraps → wrapper wrapper ← 两个函数名字一样,日志/报错里根本分不清谁是谁!
  61. # # 加了 wraps → login logout ← 各叫各的名,清清楚楚
  62. #
  63. # #todo:第4级:加 @wraps(func) 保住身份(刚学过)
  64. # # '''
  65. # # @wraps(func) 把"被装饰函数的身份信息"(名字、文档等)复制到 wrapper 上,
  66. # # 让装饰之后的函数对外"看起来还是原来那个函数"。
  67. # # '''
  68. # from functools import wraps
  69. # def my_decorator(func):
  70. # @wraps(func)
  71. # def wrapper(*args, **kwargs):
  72. # return func(*args, **kwargs)
  73. # return wrapper
  74. #
  75. # @my_decorator
  76. # def say_hello():
  77. # """打个招呼"""
  78. # print("hello")
  79. # #
  80. # print(say_hello.__name__) # 输出: wrapper ← 名字被冒名顶替了!
  81. # print(say_hello.__doc__) # 输出: None ← 原来的文档丢了!
  82. from functools import wraps
  83. # # # todo:第5级:带参数的装饰器
  84. def repeat(times): # 最外层:接收【装饰器的参数】 times
  85. def decorator(func): # 中间层:接收【被装饰的函数】
  86. @wraps(func)
  87. def wrapper(*args, **kwargs): # 最内层:实际执行
  88. for _ in range(times):
  89. result = func(*args, **kwargs)
  90. return wrapper
  91. return decorator
  92. #
  93. @repeat(times=3) # 重复执行3次 # greet = repeat(3)(greet)
  94. def greet(name):
  95. print(f"hi {name}")
  96. greet("Tom")
  97. # # 输出: hi Tom / hi Tom / hi Tom
  98. print(greet.__name__)
  99. #
  100. # # '''
  101. # 装饰器最内层永远是 wrapper(接住"调用时的实参");
  102. # 往外一层接住"被装饰的函数";
  103. # 如果装饰器自己还要参数,就再往外包一层接住"装饰器的参数"。
  104. # '''