装饰器的概念
- 装饰器的实现是函数里面嵌套函数;
- 装饰器的本质是一个函数, 它可以让其他函数在不需要做任何代码改动的前提下增加额外的功能;
- 装饰器需要传递一个函数, 返回值也是一个函数对象.
装饰器的应用场景
- 计时器
def timeit(fun): # fun_list
def wrapper(*args, **kwargs): # 接收可变参数和关键字参数
# args: 元组 kwargs: 字典 args=(5,)
# 函数执行之前
start_time = time.time()
# 执行函数
fun(*args, **kwargs) # args解包, 5,| 对于元组解包, 对于字典解包
# 执行函数之后
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return wrapper
- 记录日志
- 用户登录验证
- 函数参数验证
#高阶函数
- 主要理解执行的过程及分析----高阶函数,返回值是一个函数(定义函数并不会执行函数内容, 调用时才会执行.)
def compare1(base): # base=10
def compare2(y): # y=3
return base > y # 10>3 True
return compare2 # compare2===函数名
compare_base_10 = compare1(10)
print(compare_base_10) # 函数名
res = compare_base_10(3)
print(res)
print(abs)
def add(x,y):
return x+y
print(add)
如何使用装饰器
- ATM
- 用户使用ATM的时候, 执行程序之前祝福语==“五一快乐”,
- 执行程序之后打个广告===="欢迎再次光临.".
- 1. 解决问题: 在函数执行之前和执行之后添加功能, 调用函数的方式改变了.
2. 不改变原有函数的调用方法: 函数里面潜逃函数, 并且返回嵌套的函数login = desc(login)
def desc(fun): # fun = login #1). 需要传递一个函数, 要装饰的函数
def add_info(): # 2). 装饰器函数里面嵌套函数
print("五一快乐")
fun() # login()
print("欢迎再次光")
return add_info #3). 返回值是嵌套的函数对象
@desc # login = desc(login) # 4). 如何调用装饰器(两种方式)
def login():
# print("国庆节快乐")
print("login......")
# print("欢迎再次光临")
login = desc(login) # 返回值是一个函数
login()
装饰器实现一个计时器
-
装饰器需求: 获取每个函数的执行时间
1).函数执行之前计算时间;
2).函数执行之后计算时间 -
实验1:看字符串拼接的效率
1). “hello” + “world”
2). " ".join([‘hello’, “world”]) -
实验2: 检测列表生成式和map的效率高低, n为函数传入的参数===两者实力相当
1). [n2 for i in range(n)]
2). map(lambda x:x2, range(n))
import random
import string
import time
li = [ random.choice(string.ascii_letters) for i in range(100)]
def timeit(fun): # fun_list
def wrapper(*args, **kwargs): # 接收可变参数和关键字参数
# args: 元组 kwargs: 字典 args=(5,)
# 函数执行之前
start_time = time.time()
# 执行函数
fun(*args, **kwargs) # args解包, 5,| 对于元组解包, 对于字典解包
# 执行函数之后
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return wrapper
@timeit
def con_add():
s = ''
for i in li:
s += (i+",")
print(s)
@timeit
def join_add():
print(",".join(li))
@timeit # fun_list=timeit(fun_list) # fun_list = wrapper
def fun_list(n):
print([2*i for i in range(n)])
@timeit
def fun_map(n):
print(list(map(lambda x:x*2, range(n))))
con_add()
join_add()
fun_list(500000) #
fun_map(500000)
装饰器函数属性变化解决方案
- 问题1: 装饰的函数有返回值的解决方法:
res = 函数()
return res - 问题2:如何保留被装饰函数的函数名和帮助文档信息.
import functools
@functools.wraps()
import random
import string
import time
import functools
# 高阶函数的一些方法
from functools import reduce
@functools.wraps(fun)
def timeit(fun): # fun_list
"""这是一个装饰器timeit"""
@functools.wraps(fun)
# 可以保留被装饰函数的函数名和帮助文档信息.
def wrapper(*args, **kwargs): # 接收可变参数和关键字参数 # 100
"""这是一个wrpper函数"""
# args: 元组 kwargs: 字典 args=(100,)
# 函数执行之前
start_time = time.time()
# 执行函数
res = fun(*args, **kwargs) # args解包, 100,| 对于元组解包, 对于字典解包
# fun_list(100)
# 执行函数之后
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return res
return wrapper
@timeit # fun_list=timeit(fun_list) # fun_list = wrapper
def fun_list(n):
"""这是fun_list函数, 被timeit装饰"""
return [2*i for i in range(n)]
@timeit
def fun_map(n):
"""这是fun_map函数"""
return map(lambda x:x*2, range(n))
@timeit
def fun():
print("hello")
# print(fun_list(100)) # wrapper(100)
# print(fun_map(100))
# fun()
print(fun_list.__name__)
print(fun_list.__doc__)
#练习 添加日志
- 创建装饰器, 要求如下:
- 创建add_log装饰器, 被装饰的函数打印日志信息;
- 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx
import functools
import time
# format
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
# run_time = time.ctime()
# fun_name = fun.__name__
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print("[%s] 函数名: %s, 运行时间:%.5f, 运行返回值结果:%d"
%(time.ctime(), fun.__name__, end_time-start_time, res )
)
return res
return wrapper
@add_log
def add(x,y):
time.sleep(0.1)
return x+y
print(add(1,2))
#装饰器的第二种方式
- 需求: 用户登陆验证的装饰器is_login
1). 如果用户登陆成功, 则执行被装饰的函数;
2). 如果用户登陆不成功, 则执行登陆函数
import functools
login_users = ['admin', 'root']
def is_login(fun): # fun: writeBlog
@functools.wraps(fun)
def wrapper(*args, **kwargs): # name="admin" # kwargs={"name":"admin"}
# 判断写博客的这个用户是否登陆成功;
if kwargs.get("name") in login_users:
res = fun(*args, **kwargs)
return res
else:
res=login()
return res
return wrapper
# 必须登陆成功
@is_login # writeBlog = is_login(writeBlog)
def writeBlog(name):
return "编写博客"
def login():
return "登陆。。。。"
# 是否登陆成功都可以执行代码
def news():
print("新闻......")
print(writeBlog(name="admin1"))
#判断变量的数据类型
-
- 基础版(无参数的装饰器)
编写装饰器required_ints, 条件如下:
1). 确保函数接收到的每一个参数都是整数;
2). 如果参数不是整形数, 打印 TypeError:参数必须为整形
- 基础版(无参数的装饰器)
import functools
def required_ints(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs): # args=(1,2)
for i in args: # i=1
# if isinstance(i, int):
# pass
# else:
# print("函数所有参数并非为整形")
# break
if not isinstance(i , int):
print("函数所有参数并非为整形")
break
else:
res = fun(*args, **kwargs)
return res
return wrapper
@required_ints
def add(a, b):
return a + b
@required_ints
def myMax(a, b, c, d):
return max(a, b, c, d)
print(add(1,2.0))
#带有多个装饰器的函数
- 需求: 用户登陆验证的装饰器is_login
1). 如果用户登陆成功, 则执行被装饰的函数;
2). 如果用户登陆不成功, 则执行登陆函数
- 需求: 判断登陆用户是否未管理员is_admin(此处管理员只有一个为:admin用户)
1).如果用户为管理员, 则执行被装饰的函数;
2).如果用户不是管理员, 则报错;
# import functools
# login_users = ['admin', 'root']
#
#
#
# def is_admin(fun):
# @functools.wraps(fun)
# def wrapper(*args, **kwargs):
# #
# if kwargs.get("name") == 'admin':
# res = fun(*args, **kwargs)
# return res
# else:
# return "Error: 您没有权限访问该网站"
# return wrapper
#
# def is_login(fun): # fun: writeBlog
# @functools.wraps(fun)
# def wrapper(*args, **kwargs): # name="admin" # kwargs={"name":"admin"}
# # 判断写博客的这个用户是否登陆成功;
# if kwargs.get("name") in login_users:
# res = fun(*args, **kwargs)
# return res
# else:
# res=login()
# return res
# return wrapper
#
# # 必须登陆成功
# @is_login # writeBlog = is_login(writeBlog)
# @is_admin # 先判断是否为admin用户
# def writeBlog(name):
# return "编写博客"
#
# def login():
# return "登陆。。。。"
#
# # 是否登陆成功都可以执行代码
# def news():
# print("新闻......")
#
# print(writeBlog(name="root"))
def makebold(fun):
print("bold1")
def wrapper1(*args, **kwargs):
print("bold2")
return fun(*args, **kwargs) # wrapper
return wrapper1
def makei(fun): # fun=login
print("i1")
def wrapper(*args, **kwargs):
print("i2")
return fun(*args, **kwargs)
return wrapper
# 当有多个装饰器时, 从下到上调用装饰器,
# 真实wrapper内容执行是从上到下执行.
@makebold # login = makebold(login) # login为wrapper1
@makei # login = makei(login) # login为wrapper
def login():
return "登陆"
print(login())
#带有参数的装饰器
- 创建装饰器, 要求如下:
-
- 创建add_log装饰器, 被装饰的函数打印日志信息;
- 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx
import functools
import time
# format
def log(kind): # kind="debug"
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
# run_time = time.ctime()
# fun_name = fun.__name__
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print("<%s> [%s] 函数名: %s, 运行时间:%.5f, 运行返回值结果:%d"
%(kind, time.ctime(), fun.__name__, end_time-start_time, res )
)
return res
return wrapper
return add_log
@log("debug")
# log("debug")==> 返回值是add_log
# add=add_log(add)
def add(x,y):
time.sleep(0.1)
return x+y
print(add(1,2))
# wrapper(1,2)
#有参数的装饰器
-
- 升级版(有参数的装饰器)
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型
- 升级版(有参数的装饰器)
import functools
def required_types(*kinds): # kinds
def required(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs): # args=(1,2)
for i in args: # i=1
# if isinstance(i, int):
# pass
# else:
# print("函数所有参数并非为整形")
# break
if not isinstance(i , kinds):
print("函数所有参数并非", kinds)
break
else:
res = fun(*args, **kwargs)
return res
return wrapper
return required
@required_types(int, float)
def add(a, b):
return a + b
@required_types(int)
def myMax(a, b, c, d):
return max(a, b, c, d)
print(add(1,2.0))