引子
>>> 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