Python之装饰器(基础)
1.定义一个简单的装饰器
装饰器:
把一个函数当作参数,返回一个替代版的函数
本质上就是一个返回函数的函数
“在不改变原函数的基础上,给函数增加功能”
例1:在不更改函数fun1的前提下,在输出 hello python 之前先输出 *****************
# 1.定义装饰器
def decorator(fun):
def prints():
print('*****************')
fun()
return prints
# 2.添加装饰器
@decorator
def say_hello():
print('hello python')
# 3.调用函数
say_hello()
2.调用函数的两种方式
例2:在不更改函数fun1的前提下,在输出This is a function之前先输出系统时间
# 导入时间模块
import time
# 定义装饰器(它的形参是一个函数)
def decorator(func):
def wrapper():
# 打印时间
print(time.time())
# 调用接收的函数
func()
# 必须返回wrapper函数,否则将无输出信息
return wrapper
# 方式1:
def fun1():
print('This is a function')
# 1.无需要更改函数,但需要更改调用方式
fun1 = decorator(fun1)
fun1()
# 导入时间模块
import time
# 定义装饰器(它的形参是一个函数)
def decorator(func):
def wrapper():
# 打印时间
print(time.time())
# 调用接收的函数
func()
# 必须返回wrapper函数,否则将无输出信息
return wrapper
# 方式2:
@decorator
def fun1():
print('This is a function')
# 无需要更改函数,也无需要更改调用方式
fun1()
3.定义一个有参数的函数
# 导入时间模块
import time
# 定义装饰器(它的形参是一个函数)
def decorator(func):
# *args:可变参数(可以接收任意多个参数),因为fun1,fun2函数里有不同个数的参数;
# **kwargs:关键字参数(用来接收字典),因为fun3函数里有关键字参数
def wrapper(*args,**kwargs):
# 打印时间
print(time.time())
# 调用接收的函数
func(*args,**kwargs)
# 必须返回wrapper函数,否则将无输出信息
return wrapper
# (1) 定义有一个参数的函数
@decorator
def fun1(func_name):
# + 表示连接
print('This is a function ' + func_name)
fun1('test')
# (2) 定义有两个参数的函数
@decorator
def fun2(func_name1,func_name2):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)
fun2('test1','test2')
# (3) 定义有关键字参数的函数
@decorator
# **kwargs:关键字参数,用来接收字典
def fun3(func_name1,func_name2,**kwargs):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)
print(kwargs)
fun3('tes1','test2',a='1',b='2',c='westos')
4.装饰器实现一个函数计时器
解决:
问题1: 如何保留被装饰函数的函数名和帮助信息文档
在装饰器中添加: @functools.wraps(fun)
问题2: 如果被装饰函数有返回值,该如何定义装饰器
res = fun(*args,**kwargs)
return res
import time
import random
import string
import functools
# 使用序列生成式的方式:随机生成100个字母,将其定义为一个列表
li = [random.choice(string.ascii_letters) for i in range(100)]
# print(li)
# 定义装饰器,来比较这两种方式的运行效率
def timeit(fun):
# 解决问题1:
# 保留被装饰函数的函数名和帮助信息文档
@functools.wraps(fun)
# @1.一般都写上可变参数和关键字参数,
# 无论需要装饰的函数是否有参数,有就使用,没有就不使用
def wrapper(*args,**kwargs):
"""这是wrapper函数"""
start_time = time.time()
print(start_time)
#1. fun(*args, **kwargs)
# @2.一般都将其赋给变量,便于返回函数返回值,
# 无论被装饰函数是否有return,有则使用,没有则不使用
res = fun(*args,**kwargs)
end_time = time.time()
print(end_time)
# 多保留几位小数,才能看到时间差
print('运行时间为:%.6f' %(end_time - start_time))
# 解决问题2:
return res
return wrapper
# 用逗号来连接列表中的所有元素并输出
# 方式1:(自定义方法)
@timeit
def con_add():
"""这是con_add函数"""
s = ''
for i in li:
s += (i + ',')
print(s)
con_add()
# 方式2:(系统内置方法:join方法)
@timeit
def join_add():
"""这是join方法"""
print(','.join(li))
join_add()
## 发现内置函数比自定义函数运行效率要高
# # 如果装饰器内没有 @functools.wraps(fun), 则返回装饰函数的函数名和帮助信息文档
# # 如果装饰函数中也没有注释,则返回 None
#
# # 1.查看被装饰函数的帮助文档
# print(con_add.__doc__)
# print(join_add.__doc__)
#
# # 2.查看被装饰函数的函数名
# print(con_add.__name__)
# print(join_add.__name__)
import time
import random
import string
import functools
# 使用序列生成式的方式:随机生成100个字母,将其定义为一个列表
li = [random.choice(string.ascii_letters) for i in range(100)]
# print(li)
# 定义装饰器,来比较这两种方式的运行效率
def timeit(fun):
# 解决问题1:
# 保留被装饰函数的函数名和帮助信息文档
@functools.wraps(fun)
# @1.一般都写上可变参数和关键字参数,
# 无论需要装饰的函数是否有参数,有就使用,没有就不使用
def wrapper(*args,**kwargs):
"""这是wrapper函数"""
start_time = time.time()
print(start_time)
#1. fun(*args, **kwargs)
# @2.一般都将其赋给变量,便于返回函数返回值,
# 无论被装饰函数是否有return,有则使用,没有则不使用
res = fun(*args,**kwargs)
end_time = time.time()
print(end_time)
# 多保留几位小数,才能看到时间差
print('运行时间为:%.6f' %(end_time - start_time))
# 解决问题2:
return res
return wrapper
## 定义一个有返回值的函数
# 方式1:(自定义方法)
@timeit
def fun_list(n):
# 列表生成式,计算n以内的各个元素的平方并将其存入列表中
return [i ** 2 for i in range(n)]
# 调用时需要print打印
print(fun_list(5))
# 方式2:(系统内置方法:map方法)
@timeit
# 定义一个有返回值的函数
def fun_map(n):
# map方法,计算n以内的各个元素的平方并将其存入列表中(lambda为匿名函数)
return list(map(lambda x:x * 2,range(n)))
# 调用时需要print打印
print(fun_map(5))
## 由于内置方法:map 里使用到了匿名函数:lambda 所以它的运行效率不如自定义方法快
5.定义含参装饰器
import functools
# 定义带有参数的装饰器
# 1.第一个函数用来接收参数
# *kinds和*args一样,都是指可变参数,真正起作用的是*,后边跟任何字符均可,一般用args
# 由于后边已经使用了args所以换了一个字符串表示
def required_types(*kinds):
# 2.第二个函数用来接收函数
def required_type(fun):
@functools.wraps(fun)
# 3.第三个函数用来实现功能
def wrapper(*args,**kwargs):
# 遍历参数
for i in args:
# 参数是否符合要求
if not isinstance(i,kinds):
# raise TypeError('参数必须为%s' %kinds)
print('参数必须为',kinds)
break
else:
# 执行函数,并返回函数返回值
res = fun(*args,**kwargs)
return res
return wrapper
return required_type
## 添加装饰(带有参数)
# 只能是整型
# @required_types(int)
# 浮点型或者整型,无先后顺序之分
@required_types(float,int)
def add(a,b):
return a + b
# print(add(1,3))
print(add('westos',3.0))
6.多个装饰器的执行
import functools
# 定义装饰器1
def decorator_a(fun):
print('Get in decorator_a')
@functools.wraps(fun)
def inner_a(*args,**kwargs):
print('Get in inner_a')
# 调用函数
res = fun(*args,**kwargs)
return res
return inner_a
# 定义装饰器2
def decorator_b(fun):
print('Get in decorator_b')
@functools.wraps(fun)
def inner_b(*args,**kwargs):
print('Get in inner_b')
# 调用函数
res = fun(*args,**kwargs)
return res
return inner_b
# 添加装饰
@decorator_b
@decorator_a
# 定义被装饰函数
def f(x):
print('Get in f')
return x ** 2
print(f(3))
# 外层:先执行装饰器decorator_a,再执行decorator_b(从下到上)
# 内层:先执行inner_b,再执行inner_a(先执行后返回的)
# 仅与添加装饰的顺序有关,与定义装饰器的顺序无关
# 外层函数从下到上依次执行,内层函数先执行后返回的