引子

>>> def func():
...   print("YorkFish")
...
>>> func()
YorkFish
>>> f = func
>>> f()
YorkFish
>>> id(func) - id(f)
0
>>> f.__name__
'func'
>>> 
  • 现在有新的需求
    • func 进行扩展:每次打印 YorkFish 之前打印当前系统时间
    • 但实现这个功能不要改动现有代码
  • 解决办法:使用装饰器

简介

  • 在不改动函数代码的基础上无限制扩展函数功能的一种机制
  • 本质上讲,装饰器是一个 返回函数高阶函数
  • 使用:在每次要扩展的函数定义前使用 @func_name

举例

>>> import time  # 下面的例子就不重复这个导入操作了
  • 先写好函数备用
>>> def print_date(f):
...   def wrapper(*args, **kwargs):
...     print("Date: %s" % time.strftime("%Y-%m-%d", time.localtime()))
...     return f(*args, **kwargs)
...   return wrapper
...
>>> 

例1

  • 对函数进行功能扩展,每次执行函数前,打印当前日期
>>> @print_date
... def func():
...   print("YorkFish")
...
>>> func()
Date: 2019-12-20
YorkFish
>>> 
  • 装饰器的好处
    • 一处定义,多处装饰
    • 一旦被装饰,就能拥有装饰器的功能

例2

  • 不使用 @,手动执行装饰器
>>> def manual():
...   print("manual operation")
...
>>> func = print_date(manual)
>>> func()
Date: 2019-12-20
manual operation
>>> 

例3

  • print_date()def wrapper(*args, **kwargs) 的参数可以自定义
>>> def cal_time(f):
...   def wrapper(x, y):
...     start = time.perf_counter_ns()
...     f(x, y)
...     stop = time.perf_counter_ns()
...     print(f"run time: {stop - start}")
...   return wrapper
...
>>> @cal_time  # 装饰器要写在被装饰的函数的上方
... def add_two_nums(x, y):
...   print(f"{x} + {y} = {x + y}")
...
>>> add_two_nums(10, 20)
10 + 20 = 30
run time: 1606600
>>>

例4

  • 两个装饰器
>>> def deco1(f):
...   print("decorator1")
...   def wrapper():
...     print("decorator1's wrapper")
...     f()
...   return wrapper
...
>>> def deco2(f):
...   print("decorator2")
...   def wrapper():
...     print("decorator2's wrapper")
...     f()
...   return wrapper
...
>>> @deco2
... @deco1
... def dbl_decos():
...     print("double decorators")
...
decorator1
decorator2
>>> dbl_decos()
decorator2's wrapper
decorator1's wrapper
double decorators
>>>

例5

  • 沿用例4的思路,把这些代码写到一个 .py 的文件中
def deco1(f):
    print("先穿衬衫")
    def wrapper():
        print("再脱衬衫")
        f()
    return wrapper


def deco2(f):
    print("再穿西装")
    def wrapper():
        print("先脱西装")
        f()
    return wrapper


@deco2
@deco1
def dbl_decos():
    print("over")


if __name__ == "__main__":
    dbl_decos()

>>>

先穿衬衫
再穿西装
先脱西装
再脱衬衫
over
  • 装饰的过程:先执行 deco1,再执行 deco2
  • 调用的过程:先调用 deco2,再调用 deco1

相关文章: