什么是装饰器?
在说装饰器之前啊. 我们先说一个软件设计的原则: 开闭原则, 又被成为开放封闭原则,你的代码对功能的扩展是开放的你的程序对修改源代码是封闭的. 这样的软件设计思路可以更好的维护和开发。
开放:对功能扩展开放
封闭:对修改代码封闭
谈装饰器前,还要先要明白一件事,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如:
def foo(): print("foo") def bar(func): func() bar(foo)
装饰器的目的: 在不改变原来代码的基础上. 给代码添加新功能。
装饰器形成过程
简单装饰器
def func1(): print("欢迎访问主页") # 添加一个登录验证的功能 def wrapper(fn): def inner(): print("请先登录....") fn() return inner func1 = wrapper(func1) func1() # 装饰器的基本雏形 # def wrapper(fn): # fn:目标函数. # def inner(): # '''执行函数之前''' # fn() # 执行被装饰的函数 # '''执行函数之后''' # return inner
来看一下,执行流程:
语法糖
但是如果有多个函数,都需要添加登录验证的功能,每次都得func1 = timer(func1)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方法,python给你提供了,那就是语法糖。 @装饰器
def wrapper(fn): def inner(): print("请先登录....") fn() return inner @wrapper # 相当于 => func1 = wrapper(func1) def func1(): print("欢迎访问主页") func1()
有个小问题,就是查看func1函数名和函数注释都变成inner了。
print(func1.__doc__) # 查看函数注释信息 print(func1.__name__) # 查看函数名 # 我是内部函数 # inner 如何解决??? from functools import wraps def wrapper(fn): @wraps(fn) # 改变inner的名字 def inner(): """我是内部函数""" print("请先登录....") fn() return inner @wrapper # 相当于 => func1 = wrapper(func1) def func1(): """ 主页函数 """ print("欢迎访问主页") func1() print(func1.__doc__) print(func1.__name__) # 主页函数 # func1
被装饰函数带参数和返回值
from functools import wraps def wrapper(fn): # fn: 目标函数, 被装饰的函数 @wraps(fn) # 改变inner的名字 def inner(*args,**kwargs): # 万能参数 *args, **kwargs: 接收参数 """我是内部函数""" print("请先登录....") ret = fn(*args,**kwargs) # 调用目标函数. """被装饰函数执行之后""" return ret # 返回结果 return inner @wrapper # 相当于 => func1 = wrapper(func1) def func1(usernamee): """ 主页函数 """ print("欢迎 %s 访问主页" % usernamee) return True @wrapper # 相当于 => func2 = wrapper(func2) def func2(name,age): print("名字:%s 年龄:%s" %(name,age)) return True ret = func1("lishichao") # 实际执行的是inner 参数传给了inner函数 print(ret) func2("lishichao","19")
带有参数的装饰器
给装饰器传参,可以控制装饰器是否使用。
def wrapper(flag): # 装饰器参数 def inner1(fn): # fn: 目标函数, 被装饰的函数 def inner2(*args,**kwargs): # 万能参数 *args, **kwargs: 接收参数 """我是内部函数""" if flag: # 判断传入的参数 True就执行装饰器 False不执行装饰器 print("请先登录....") ret = fn(*args,**kwargs) # 调用目标函数. """""" return ret # 返回结果 return inner2 return inner1 @wrapper(False) # 执行流程: 先执行wrapper(False) 返回装饰器 再和 @ 拼接起来 @inner1 def func1(usernamee): """ 主页函数 """ print("欢迎 %s 访问主页" % usernamee) return True ret = func1("lishichao") # 实际执行的是inner 参数传给了inner函数 print(ret)
执行流程:
多个装饰器装饰一个函数
def wrapper1(fn): def inner(*args, **kwargs): print("第一个装饰器开始") ret = fn(*args, **kwargs) print("第一个装饰器结束") return ret return inner def wrapper2(fn): def inner(*args, **kwargs): print("第二个装饰器开始") ret = fn(*args, **kwargs) print("第二个装饰器结束") return ret return inner def wrapper3(fn): def inner(*args, **kwargs): print("第三个装饰器开始") ret = fn(*args, **kwargs) print("第三个装饰器结束") return ret return inner @wrapper1 @wrapper2 @wrapper3 def func(): print("瞬间来三") func() # 执行结果 # 第一个装饰器开始 # 第二个装饰器开始 # 第三个装饰器开始 # 瞬间来三 # 第三个装饰器结束 # 第二个装饰器结束 # 第一个装饰器结束
执行流程:
wrapper1
wrapper2
wrapper3 # 调用函数执行之前
func
wrapper3 # 调用函数执行之后
wrapper2
wrapper1
练习题
1、装饰器的固定格式
from functools import wraps def wrapper(flag): # 接收装饰器的参数 def inner1(fn): # 接收的是被装饰函数的函数名 @wraps(fn) # 改变inner2函数的名字为被装饰函数的名字 def inner2(*args,**kwargs): # 无敌传参,接收参数 if flag: # 判断是否执行装饰器 print("被装饰函数执行之前") ret = fn(*args,**kwargs) #被装饰函数, 调用参数 print("被装饰函数执行之后") return ret # 返回值 return inner2 return inner1 @wrapper(True) def func1(): print("我是被装饰函数") return True func1() print(func1.__name__)
2、编写装饰器,在每次执行被装饰函数之前打印一句’每次执行被装饰函数之前都得先经过这里,这里根据需求添加代码’
def wrapper(fn): def inner(*args,**kwargs): print("每次执行被装饰函数之前都得先经过这里,这里根据需求添加代码") ret = fn(*args,**kwargs) #调用被装饰函数,ret接收被装饰函数的返回值 return ret return inner @wrapper def func(): print("我是被装饰函数") return "被装饰函数的返回值" ret = func() print(ret)
3、编写装饰器,在每次执行被装饰函数之后打印一句’每次执行完被装饰函数之后都得先经过这里,这里根据需求添加代码’
def wrapper(fn): def inner(*args,**kwargs): ret = fn(*args,**kwargs) print("每次执行完被装饰函数之后都得先经过这里,这里根据需求添加代码") return ret return inner @wrapper def func(): print("我是被装饰函数") func()
4、编写装饰器,在每次执行被装饰函数之前让用户输入用户名,密码,给用户三次机会,登录成功之后,才能访问该函数.
flag = False def login(): num = 1 while num <= 3: username = input("请输入用户名:").strip().lower() password = input("请输入密码:").strip().lower() if username == "lishichao" and password == "123": global flag flag = True break else: num += 1 print("输入错误,请重新输入") continue else: print("输入超过三次,已锁定") def login_wrapper(fn): def inner(): login() if flag: fn() else: print("登录失败") return inner @login_wrapper def func1(): print("欢迎访问文章页面")
5、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件,只支持单用户的账号密码,给用户三次机会)要求登录成功一次,后续的函数都无需再输入用户名和密码
import sys flag = False def sign_up(): with open("user_info",mode="w",encoding="utf-8") as f: username = input("请输入注册账号:").strip() password = input("请输入注册密码:").strip() f.write("%s|%s " %(username,password)) print("注册成功,请登录") def login(): num = 1 while num <= 3: username = input("请输入账号:").strip() if username == "": print("账号不能为空") continue password = input("请输入密码:").strip("|") if password == "": print("密码不能为空,请重新输入") continue num += 1 with open("user_info", mode="r", encoding="utf-8") as f: for i in f: user_list = i.strip().split("|") if username == user_list[0] and password == user_list[1]: print("登录成功") global flag flag = True return else: print("登录失败,账号或密码不正确.") else: print("===========================================") print("登录超过三次,账号已锁定") print("===========================================") sys.exit() def wrapper(fn): def inner(*args,**kwargs): if flag: ret = fn(*args,**kwargs) return ret else: login() return inner @wrapper def func1(): print("欢迎访问文章页面") @wrapper def func2(): print("欢迎访问管理页面") @wrapper def func3(): print("欢迎访问评论页面") dic = { "1":sign_up, "2":login, "3":func1, "4":func2, "5":func3 } def main(): while 1: print(""" 请选择: 1.注册 2.登录 3.访问文章页面 4.访问管理页面 5.访问评论页面 [q] 退出 """) n = input(">>>").strip().lower() if n == "q": break else: dic[n]() main()
6、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件,可支持多账号密码)要求登录成功一次(给三次机会),后续的函数都无需再输入用户名和密码。
import sys
flag = False
def sign_up():
with open("user_info",mode="a",encoding="utf-8") as f:
username = input("请输入注册账号:").strip()
password = input("请输入注册密码:").strip()
f.write("%s|%s
" %(username,password))
print("注册成功,请登录")
def login():
num = 1
while num <= 3:
username = input("请输入账号:").strip()
if username == "":
print("账号不能为空")
continue
password = input("请输入密码:").strip("|")
if password == "":
print("密码不能为空,请重新输入")
continue
num += 1
with open("user_info", mode="r", encoding="utf-8") as f:
for i in f:
user_list = i.strip().split("|")
if username == user_list[0] and password == user_list[1]:
print("登录成功")
global flag
flag = True
return
else:
print("登录失败,账号或密码不正确.")
else:
print("===========================================")
print("登录超过三次,账号已锁定")
print("===========================================")
sys.exit()
def wrapper(fn):
def inner(*args,**kwargs):
if flag:
ret = fn(*args,**kwargs)
return ret
else:
login()
return inner
@wrapper
def func1():
print("欢迎访问文章页面")
@wrapper
def func2():
print("欢迎访问管理页面")
@wrapper
def func3():
print("欢迎访问评论页面")
dic = {
"1":sign_up,
"2":login,
"3":func1,
"4":func2,
"5":func3
}
def main():
while 1:
print("""
请选择:
1.注册
2.登录
3.访问文章页面
4.访问管理页面
5.访问评论页面
[q] 退出
""")
n = input(">>>").strip().lower()
if n == "q":
break
else:
dic[n]()
main()
7、给每个函数写一个记录日志的功能
功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
所需模块:
import time
struct_time = time.localtime()
print(time.strftime("%Y‐%m‐%d %H:%M:%S",struct_time))
def wrapper_log(flag): def wrapper(fn): def innrt(*args,**kwargs): if flag: import time struct_time = time.localtime() Time = time.strftime("%Y-%m-%d %H:%M:%S",struct_time) func_name = fn.__name__ with open("func_accecc.log",mode="a",encoding="utf-8") as f: f.write("在 %s 执行函数: %s " %(Time,func_name)) ret = fn(*args,**kwargs) return ret return innrt return wrapper @wrapper_log(True) def func1(): print("欢迎访问文章页面") @wrapper_log(True) def func2(): print("欢迎访问评论页面") @wrapper_log(True) def func3(): print("欢迎访问管理页面") func1() func2() func3()