开放封闭原则:

软件实体应该是可拓展,而不可修改的。也就是说,对拓展是开放的,而对修改是封闭的

装饰器原理阐述:
将被装饰的函数当做一个参数传到装饰器中,并且让被装饰的函数名指向装饰器内部的函数,在装饰器的内部函数中用接收到的参数再调用被装饰的函数

装饰器作用:

在不更改原功能函数内部代码,并且不改变调用方法的情况下为原代码添加新的功能。

小案例:

def decorator(func):
def wrapper():
print("---开机,打开软件--")
func()
print("---关机,底薪到手---")

return wrapper


@decorator # 作用 @decorator 等同于 ===> work1 = decorator(work1)
def work1():
print("----写代码---")


work1()

练习:

需求:实现一个可以统计任意时间的装饰器

import time


def decorator(func):
def wrapper():
st = time.time()
func()
et = time.time()
ut = et - st
print(f'函数执行时间为:"{ut:.2f}"')

return wrapper


@decorator
def func():
time.sleep(2)


func()
装饰器优化(实现传参,返回值)
  • 装饰器内部嵌套函数接收参数
  • 调用函数时,传入参数
  • 调用功能函数,接收功能函数返回的结果,装饰器执行完返回结果
def count_time(func):
def wrapper(a, b):
strat_time = time.time()
# 调用功能函数,接收功能函数返回的结果
res = func(a, b)
end_time = time.time()
t_time = end_time - strat_time
print('运行总时间%:', t_time)
# 返回结果
return res

return wrapper


@count_time
def work(a, b):
res = a + b
return res


res = work(5, 6)
print(res)

通用装饰器: 

如果同一个装饰器既要装饰有参数的函数,又要装饰无参数的函数,那么我们在传参的时候就设置成不定长参数,这样不管被装饰的函数有没有参数都能用。
应用案例:
def count_time(func):
def wrapper(*args, **kwargs):
strat_time = time.time()
# 调用功能函数,接收功能函数返回的结果
res = func(*args, **kwargs)
end_time = time.time()
t_time = end_time - strat_time
print('运行总时间:', t_time)
# 返回结果
return res

return wrapper


@count_time
def work1(a, b):
print("------work1-----")
time.sleep(1)
res = a + b
return res


@count_time
def work2(a, b, c):
print("------work2-----")
time.sleep(2)
res = a + b + c
return res


res1 = work1(1, 2)
res2 = work2(3, 4, 5)
print(res1)
print(res2)
装饰器装饰类:使用类装饰器的时候,记得要返回被装饰的类调用的结果
def decorator(cls):
def wrapper(*args, **kwargs):
print("----装饰器扩展代码1------")
# 通过类实例化对象
res = cls()
print("----装饰器扩展代码2------")
return res

return wrapper


@decorator # MyClass =decorator(MyClass)
class MyClass:
pass


res = MyClass()
print(res)
装饰器传参数:
def musen(name, age):
# name,age为装饰器的参数
def decoreter(func):
# func:被装饰的函数
def wrapper(*args, **kwargs):
# *args, **kwargs被装饰器的函数调用是传递的参数
# 在装饰器内部的嵌套函数中使用装饰器传递的参数:
print(name, age)
func(*args, **kwargs)

return wrapper

return decoreter

"""
最外层参数,接收的是装饰器的参数
第二层参数,接收是被装饰的函数
第三层参数,接收的是被装饰函数的参数
"""
通过装饰器修改属性:
def mark(func):
func.name = 'name002'
return func

# 通过装饰器给原函数动态添加属性
@mark # work = mark(work)
def work():
print("----work----")

# work.name = 'name001'
# work()
print(work.name)
装饰器的副作用:函数/类在被装饰器装饰了之后,会改变原函数名的指向,无法在通过原函数名去获取函数原有的属性
消除装饰器的副作用:functools.wraps
from functools import wraps


def decorator(func):
@wraps(func) # 消除装饰器的副作用
def wrapper(*args, **kwargs):
"""装饰器内部wrapper的注释"""
res = func(*args, **kwargs)
return res

return wrapper


@decorator
def work(a, b):
"""
实现两个对象相加的方法
:param a: 数字1
:param b: 数字2
:return: 两个数相加的结果
"""
res = a + b
print('a+b的结果为:', res)


# 获取函数的文档字符串
print("函数的文档字符串注释:", work.__doc__)
# 获取函数的name属性
print('函数名:', work.__name__)
 通过类实现装饰器:
class Decorator:
def __init__(self, func):
self.func = func

def __call__(self, *args, **kwargs):
print("装饰器扩展代码1111111111111")
res = self.func()
print("装饰器扩展代码2222222222")
return res


# 通过类作为装饰器
@Decorator # work = Decorator(work)
def work():
print("----work----------")


work()
PS:个人感悟啊,举个栗子,加深印象,便于李姐
被装饰函数或类:丈夫
装饰器外层函数:老婆
装饰器内层函数:孩子
在是否有参数的各个场景下,丈夫(被装饰函数或类)既要满足老婆(decorator)的需要,又要满足孩子(wrapper)的需要,不然就会报错