Python基础学习(14)装饰器
一、今日内容大纲
- 装饰器 decorator
- 装饰器的应用
二、装饰器
-
开放封闭原则
开放:对代码的拓展开放
封闭:对源码的修改封闭
-
装饰器 decorator
完全遵循开放封闭原则,是一个函数,本质上属于闭包 closure 的应用;在不改变原函数代码及调用方式的前提下,为其增加新的功能。
现在,就一个测试函数执行效率的问题,我们对其进行讨论:
-
version 1
: 写一些代码测试index()
函数的执行效率。import time def index(): time.sleep(2) # 模拟网络延迟或代码效率 print('welcome cnblogs.com') # print(time.time()) # 格林威治时间 Greenwich Mean Time 返回1970纪元后的浮点秒数 start_time = time.time() index() end_time = time.time() print(end_time - start_time) # summary: 如果要测试其他函数的代码效率,必须重新编写代码,效率低下。
-
version 2
: 利用函数解决代码重复使用的问题。import time def index1(): time.sleep(2) # 模拟网络延迟或代码效率 print('welcome cnblogs.com') def index2(): time.sleep(3) # 模拟网络延迟或代码效率 print('enter the diary page') def timer(func): start_time = time.time() func() end_time = time.time() print(end_time - start_time) timer(index1) # 修改了调用方式 timer(index2) # summary:原来index函数源码没有变化,给原函数添加了一个测试执行效率的功能;但是不满足开放封闭原则,因为版本二修改了调用方式。
-
version 3
: 着手调用方式问题,使装饰后的函数调用方式和原函数调用更加相似。import time def index1(): time.sleep(2) # 模拟网络延迟或代码效率 print('welcome cnblogs.com') def index2(): time.sleep(3) # 模拟网络延迟或代码效率 print('enter the diary page') def timer(func): def inner(): start_time = time.time() func() end_time = time.time() print(end_time - start_time) return inner index1 = timer(index1) # 多余项目 index2 = timer(index2) # 多余项目 index1() index2() # summary:这就是最基础的装饰器,但是多了一项赋值运算,还需要优化
-
version 4
:Python
做了一个优化,提出了语法糖(Syntactic sugar)的概念,用来替代那项多余的赋值运算。import time # timer装饰器 def timer(func): def inner(): start_time = time.time() func() end_time = time.time() print(end_time - start_time) return inner @timer # 等价于index1 = timer(index1) 读了两行 def index1(): time.sleep(2) # 模拟网络延迟或代码效率 print('welcome cnblogs.com') @timer # 等价于index2 = timer(index2) 读了两行 def index2(): time.sleep(3) # 模拟网络延迟或代码效率 print('enter the diary page') index1() index2() # summary:在无传参的情况下,基本实现了开放封闭原则,但是如果被装饰函数有参数传输,又会出现问题。
-
version 5
: 被装饰函数拥有返回值时的情况。import time # timer装饰器 def timer(func): def inner(): start_time = time.time() r = func() # 实际的返回值在这里 end_time = time.time() print(end_time - start_time) return r # 将index1()返回值通过inner()返回 return inner @timer # 等价于index1 = timer(index1) 读了两行 def index1(): time.sleep(2) # 模拟网络延迟或代码效率 print('welcome cnblogs.com') return 666 @timer # 等价于index2 = timer(index2) 读了两行 def index2(): time.sleep(3) # 模拟网络延迟或代码效率 print('enter the diary page') return 777 ret1 = index1() # inner()的返回值要变成index1()的返回值 ret2 = index2() print(ret1, ret2) # summary:通过inner()将index()返回值返回
-
version 6
: 被装饰函数有参数传入时的情况。import time # 标准timer装饰器******************* def timer(func): def inner(*args, **kwargs): # *代表聚合 start_time = time.time() r = func(*args, **kwargs) # *代表打散,相当于func(*(),**{}) end_time = time.time() print(end_time - start_time) return r # 将index1()返回值通过inner()返回 return inner @timer # 等价于index1 = timer(index1) 读了两行 def index1(name): time.sleep(2) # 模拟网络延迟或代码效率 print(f"welcome 'cnblogs.com', {name}.") return 666 @timer # 等价于index2 = timer(index2) 读了两行 def index2(name, title): time.sleep(3) # 模拟网络延迟或代码效率 print(f'{title}{name} enter the diary page') return 777 ret1 = index1('太白') # inner()的返回值要变成index1()的返回值 ret2 = index2('纳钦', 'Dr.') print(ret1, ret2)
-
-
装饰器的标准形式
# 标准版的装饰器 def wrapper(f): def inner(*args, **kwargs): """添加额外功能:f()之前的装饰""" ret = f(*args, **kwargs) """添加额外功能:f()之后的装饰""" return ret return inner
三、装饰器的应用:以博客园的登录为例
# 装饰器的应用:登录认证
# 这周的周末作业:模拟博客园登录,需要利用装饰器的认证功能
login_status = dict(
status=False,
username=None
)
def get_users_info():
users_info = {}
with open(r'02 装饰器的应用users_info', encoding='utf-8') as file_handler:
for line in file_handler:
li = line.split('|')
users_info.setdefault(li[0].strip(), li[1].strip())
return users_info
def login():
users_info = get_users_info()
# print(users_info)
for i in range(3):
username = input('username:')
password = input('password:')
if username in users_info and users_info[username] == password:
login_status['status'] = True
login_status['username'] = username
return True
else:
print('The account password you entered is not correct.')
return False
def auth(func):
def inner():
if not login_status['status']:
login()
if login_status['status']:
func()
return
return inner
@auth
def article():
print('欢迎访问文章页面')
@auth
def comment():
print('欢迎访问评论页面')
@auth
def dairy():
print('欢迎访问评论页面')