1.概念
器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器 对象添加额外功能的工具/函数。
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代 码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
2.为什么使用装饰器
如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。
- 装饰器的实现必须遵循两大原则:
1.封闭: 对已经实现的功能代码块封闭。 不修改被装饰对象的源代码
2.开放: 对扩展开发
装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。 - 装饰器的使用方法:
@装饰器的使用名称
3.练习
- 因为装饰器装饰的是函数/类,所以需要传递的参数是函数名或者类名。
【1】举例
函数名() 调用函数
#装饰器:用来装饰函数或类的工具,在不改变源代码的情况下给函数/类添加额外功能。
#装饰器装饰的函数/类,所以需要传递的参数是函数名或类名
import time #时间处理模块
#HTTP请求库,多用于网络爬网,需要pip install下载
import requests
# 1.如何去创建装饰器
#需求:添加功能-计算被装饰函数运行的时间的工具
def timeit(f):
"""
添加被装饰函数执行的时间
"""
def wrapper():
#函数执行之前计算当前的时间戳
start = time.time()
#调用被装饰的函数,并保存函数的返回值
result = f()
#函数执行后计算当前的时间戳
end = time.time()
print("函数%s执行使用的时间是%.3fs"%(f.__name__,end-start))
return result
return wrapper
#2.如何去使用装饰器
#@timeit工作原理:download_music=timeit(download_music),timeit为装饰器,将函数名download_music作为参数传过去,最终将返回值赋值给函数downliad_music。
#执行的过程:
# 1) timeit(download_music)函数的返回值时wraper函数名。
# 2) download_music=timeit(download_music),让download_music指向wrapper函数
# 3) 最后一行downliad_music(),实质上是执行的函数wrapper()
# 4)执行wrapper函数时,f()实质上执行的函数download_music()
@timeit
def download_music():
url="http://m10.music.126.net/20200719111612/e36c0e235dbad219e9d8f0e65fa62007/ymusic/0201/7233/bea2/2cb43c8bcaa7797d32e5ca9b831350d8.mp3"
#模拟浏览器访问mp3的网址,获取服务器端给我们的响应(response)
response = requests.get(url)
#获取mp3音乐的内容
#music_content = response.content
#打开文件,存储音乐内容到文件中
with open("再见.mp3", "wb") as f:
f.write(response.content)
print("再见.mp3下载完成......")
download_music()
时间处理模块:
【2】加参数
#装饰器:用来装饰函数或类的工具,在不改变源代码的情况下给函数/类添加额外功能。
#装饰器装饰的函数/类,所以需要传递的参数是函数名或类名
import time #时间处理模块
#HTTP请求库,多用于网络爬网,需要pip install下载
import requests
#1)如何去创建装饰器
#需求:添加功能-计算被装饰函数运行的时间的工具
def timeit(f):
"""
添加被装饰函数执行的时间
"""
#*wargs和**kwargs是形参还是实参?形参
#*wargs:可变参数,可以接受多个参数信息,一般存储到元祖中
#kwargs:关键字参数,可以接受多个键值对信息,一般存储到字典中
#wrapper(10, 20)接收时用远祖来存储(10, 20)
def wrapper(*args, **kwargs):
#函数执行之前计算当前的时间戳
start = time.time()
#调用被装饰的函数,并保存函数的返回值
#*wargs和**kwargs是形参还是实参?实参
#注意:这里*wargs不是可变参数的意思,是解包的意思,args=(10, 20),*args=10,20 即把 #一个元祖变成两个数值
#f(10, 20)==add(10, 20)
result = f(*args, **kwargs)
#函数执行后计算当前的时间戳
end = time.time()
print("函数%s执行使用的时间是%.3fs"%(f.__name__,end-start))
return result
return wrapper
#2.如何去使用装饰器
@timeit
def download_music():
url = "http://m10.music.126.net/20200719111612/e36c0e235dbad219e9d8f0e65fa62007/ymusic/0201/7233/bea2/2cb43c8bcaa7797d32e5ca9b831350d8.mp3"
#模拟浏览器访问mp3的网址,获取服务器端给我们的响应(response)
response = requests.get(url)
#获取mp3音乐的内容
#music_content = response.content
#打开文件,存储音乐内容到文件中
with open("再见.mp3", "wb") as f:
f.write(response.content)
print("再见.mp3下载完成......")
#download_music()
@timeit
def add(num1, num2):
time.sleep(0.2)
return num1 + num2
"""
分析add函数的执行过程:
1.add(10, 20)调用函数
2.发现add函数被装饰器timeit装饰了,@timeit==> add=timeit(add)
3.timeit(add)函数的返回值是wrapper函数,add=timeit(add)这里add=wrapper
4.终于知道add函数是什么了,add(20, 20)==>wrapper(10, 20)
5.在wrapper函数中有f()==>add(10, 20)
6.返回f()函数的返回值30
"""
print (add(10, 20))
【3】模板总结
#编写装饰器模板
# 1.先实现闭包
# 2.要让这个装饰器可以装饰所有函数/类(代表汉和函数可以接受有参数/无参数)
import time
def logger(f):
#这里是可变参数和关键字参数
def wrapper(*args, **kwargs):
start = time.time()
#这里是解包
result = f(*args, **kwargs)
end = time.time()
print("Logger: %s %s run %.3f s" %(time.ctime(), f.__name__, end-start))
return result
return wrapper
@logger
def add(num1, num2):
time.sleep(0.1)
return num1 + num2
result = add(10, 20)
print(result)
【4】如何让被装饰的函数保持原有的帮助信息呢?
#编写装饰器模板
# 1.先实现闭包
# 2.要让这个装饰器可以装饰所有函数/类(代表汉和函数可以接受有参数/无参数)
# 3.如何让被装饰的函数报出自己原有的帮助信息呢?
import time
from functools import wraps
def logger(f):
#这里是可变参数和关键字参数
#wraps装饰器用来保留f函数原有的属性,包括他的帮助信息
@wraps(f)
def wrapper(*args, **kwargs):
"""
wrapper function
"""
start = time.time()
#这里是解包
result = f(*args, **kwargs)
end = time.time()
print("Logger: %s %s run %.3f s" %(time.ctime(), f.__name__, end-start))
return result
return wrapper
@logger #add = loggger(add)==> add=wrapper
def add(num1, num2):
"""
add function
"""
time.sleep(0.1)
return num1 + num2
#result = add(10, 20)
#print(result)
print(help(add))
【5】有多个装饰器
多个装饰器装饰的时候,从下向上装饰,执行的时候是从上到下进行执行。
#编写装饰器模板
# 1.先实现闭包
# 2.要让这个装饰器可以装饰所有函数/类(代表汉和函数可以接受有参数/无参数)
# 3.如何让被装饰的函数报出自己原有的帮助信息呢?
import time
from functools import wraps
def welcome(f):
@wraps(f)
def wrapper(*args, **kwargs):
print("welcome......")
result = f(*args, **kwargs)
return result
return wrapper
def logger(f):
#这里是可变参数和关键字参数
#wraps装饰器用来保留f函数原有的属性,包括他的帮助信息
@wraps(f)
def wrapper(*args, **kwargs):
"""
wrapper function
"""
start = time.time()
#这里是解包
result = f(*args, **kwargs)
end = time.time()
print("Logger: %s %s run %.3f s" %(time.ctime(), f.__name__, end-start))
return result
return wrapper
# 多个装饰器装饰的时候,从下向上装饰,执行的时候是从上到下进行执行。
@welcome
@logger #add = loggger(add)==> add=wrapper
def add(num1, num2):
"""
add function
"""
time.sleep(0.1)
return num1 + num2
result = add(10, 20)
print(result)
#print(help(add))