装饰器
1. 装饰器的基本知识
< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >
1.1. 装饰器的概念
装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:
- 不能修改被装饰的函数的源代码
- 不能修改被装饰的函数的调用方式
- 满足1、2的情况下给程序增添功能
1.2. 装饰器的作用
- 日志
- 检查(文件是否存在,自动命名)
- 认证
- 计时
- 路由
- 发邮件
1.3. 装饰器的类型
- 单重
- 多重--多重的装饰器一般用不到
- 装饰器带参数(多一层封装,传入参数)
- 类的装饰器
- 官方工具:wraps:保留原函数的名字和说明
1.4. 装饰器的执行次序
装饰器在运行时导入,正常函数执行时从上到下,相互调用。
在其他文件中引用这个文件,会自动执行装饰器。
2. 装饰器学习
2.1. 无参数版本装饰器
def deco_iron(func):
def inner(): # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
print('开始准备变身')
func()
print('变身结束')
return inner
@deco_iron # 其实就是 tony = deco_iron(tony)
def tony():
print('toney运行函数:', tony.__name__)
# tony = deco_iron(tony) # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个
def main():
tony()
if __name__ == '__main__':
main()
2.2. 被装饰函数带参数版本
- 先装饰函数名
- 为嵌套函数inner传递参数-因为在最终调用的tony其本质是inner,所以在inner接受函数
# coding=utf-8
def deco_iron(func):
def inner(*args, **kwargs): # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
print('开始准备变身', func.__name__)
print(args, kwargs)
func(*args, **kwargs)
print('变身结束', func.__name__)
return inner # 被装饰的函数,其实是变成了这个,所以传递参数是传给inner
@deco_iron # 其实就是 tony = deco_iron(tony)
def tony(name, age, dic):
print('toney运行函数:', tony.__name__) # 这里的name发生了变化,因为这个tony被装饰后,变成了inner,后面需要解决这个问题
# tony = deco_iron(tony) # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个
def main():
tony('hui', 12, {'city': 'hz', 'name': 'hui'} ) # 这里的tony 其实变成了inner
if __name__ == '__main__':
main()
2.3. 装饰器带参数版本
装饰器带参数需要在上面的版本的外面再且套一层函数
# coding=utf-8
def outer(name):
def deco_iron(func):
def inner(*args, **kwargs): # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
print('开始准备变身', func.__name__)
print(args, kwargs)
func(*args, **kwargs)
print(name)
print('变身结束', func.__name__)
return inner # 被装饰的函数,其实是变成了这个,所以传递参数是传给inner
return deco_iron
@outer('小辣椒') # 其实就是 tony = deco_iron(tony)
def tony(name, age, dic):
print('toney运行函数:', tony.__name__) # 这里的name发生了变化,因为这个tony被装饰后,变成了inner
# tony = deco_iron(tony) # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个
def main():
tony('hui', 12, {'city': 'hz', 'name': 'hui'} ) # 这里的tony 其实变成了inner
if __name__ == '__main__':
main()
2.4. 解决传递函数变化
主要使用python中的内置函数wraps。
其本质是 func.name = tony.name 其他的类似
# coding=utf-8
from functools import wraps #
def outer(name):
def deco_iron(func):
@wraps(func) #解决传递函数,函数名变化等问题'''
def inner(*args, **kwargs): # 使用且套函数,并返回是为了解决被装饰函数运行两次的问题
print('开始准备变身', func.__name__)
print(args, kwargs)
func(*args, **kwargs)
print(name)
print('变身结束', func.__name__)
return inner # 被装饰的函数,其实是变成了这个,所以传递参数是传给inner
return deco_iron
@outer('小辣椒') # 其实就是 tony = deco_iron(tony)
def tony(name, age, dic):
'''被修饰的函数'''
print('toney运行函数:', tony.__name__) # 这里的name发生了变化,因为这个tony被装饰后,变成了inner
print('toney函数的doc', tony.__doc__)
# tony = deco_iron(tony) # 这句其实就是装饰器 语法糖, 在其他代码中引用,会先运行这个
def main():
tony('hui', 12, {'city': 'hz', 'name': 'hui'} ) # 这里的tony 其实变成了inner
if __name__ == '__main__':
main()
2.5. 封装成类
使用技术:
类() 调用初始化__init__
对象() 调用__call__