一、开放封闭原则
- 对扩展是开放的
对任何一个程序,我们不可能在程序设计之初时就已经想好了所有要实现的功能,并且将来也不需要做任何更新和修改。所以我们必修允许代码拓展、添加新的功能。 - 对修改是封闭的
比如我们写一个函数,这个函数已经交付给其他人使用了,如果我们对该函数进行修改,那么很可能就会影响正在使用这个函数的用户。
二、装饰器的形成过程
需求: 在不改用函数的调用方式下,给一个已经写好的函数加上打印该函数运行时间的功能。
import time
def get_run_time(my_func):
def inner():
start = time.time()
my_func()
end = time.time()
print("time:%.3f" % (end - start))
return inner
def func():
print("hello , everybody~")
print("我是一个已经写好的函数")
func = get_run_time(func) # 此处func = inner
func() # inner()
运行顺序分析
每次调用都都要来一次赋值,如果有一百个函数,那就得进行一百次赋值,这时python开发者们就提出了用一句语法糖来解决这个问题!
import time
def get_run_time(my_func):
def inner():
start = time.time()
my_func()
end = time.time()
print("time:%.3f" % (end - start))
return inner
# 语法糖 @装饰器的函数名
@ get_run_time # ------> func = get_run_time(func)
def func(): # 被装饰的函数
print("hello , everybody~")
print("我是一个已经写好的函数")
func() # inner()
小结:
装饰器的本质:一个闭包函数。
装饰器的功能:在不修改原函数及其调用方式的情况下,对原函数功能进行拓展。
之前两个都是不带参数的装饰器,如果需要加上一个参数,怎么办 ? 很简单,在我们加上装饰器的时候,执行原函数相当于执行闭包函数inner(),那么如果原函数本来就带一个参数,那我们可以在inner( )里也放一个参数即可。
import time
def get_run_time(my_func):
def inner(name):
start = time.time()
my_func(name)
end = time.time()
print("time:%.3f" % (end - start))
return inner
@ get_run_time # ------> func = get_run_time(func)
def func(name):
print("hello , everybody~")
print("我是一个已经写好的函数", name)
func("老哥~~") # inner("老哥")
when 我需要传入多个参数时,how?? 囊括所有参数的*args,**kwargs…
def get_run_time(my_func):
def inner(*args, **kwargs):
start = time.time()
my_func(*args, **kwargs)
end = time.time()
print("time:%.3f" % (end - start))
return inner
@ get_run_time # ------> func = get_run_time(func)
def func(name, little_name):
print("hello , everybody~")
print("我是一个已经写好的函数", name, little_name)
func("老哥~~", "little_prince") # inner("老哥~~", "little_prince")
what if 我原函数式有返回值的呢?
import time
def get_run_time(my_func):
def inner(*args, **kwargs):
start = time.time()
to_return = my_func(*args, **kwargs)
end = time.time()
print("time:%.3f" % (end - start))
return to_return
return inner
@ get_run_time # ------> func = get_run_time(func)
def func(name, little_name):
print("hello , everybody~")
print("我是一个已经写好的函数", name, little_name)
return little_name
return_thing = func("老哥~~", "little_prince") # inner("老哥~~", "little_prince")
print(return_thing)
#
# hello , everybody~
# 我是一个已经写好的函数 老哥~~ little_prince
# time:0.000
# little_prince
刚刚那个装饰器已经近乎完美了,但是正常情况下我们查看函数的信息,在这里都会失效。
import time
def get_run_time(my_func):
def inner(*args, **kwargs):
start = time.time()
to_return = my_func(*args, **kwargs)
end = time.time()
print("time:%.3f" % (end - start))
return to_return
return inner
@ get_run_time # ------> func = get_run_time(func)
def func(name, little_name):
"""我就是函数文档啦---"""
print("hello , everybody~")
print("我是一个已经写好的函数", name, little_name)
return little_name
print(func.__doc__) # None (查看函数文档)
print(func.__name__) # inner (查看函数名)
为了不失效,还要在装饰器上面再加上一点东西来完善它,look this
import time
from functools import wraps # 导入functools 里面的wraps,它能保留原有函数的名称和docstring。
def get_run_time(my_func):
@wraps(my_func) # 加在最内层函数的正上方
def inner(*args, **kwargs):
start = time.time()
to_return = my_func(*args, **kwargs)
end = time.time()
print("time:%.3f" % (end - start))
return to_return
return inner
@ get_run_time # ------> func = get_run_time(func)
def func(name, little_name):
"""我就是函数文档啦---"""
print("hello , everybody~")
print("我是一个已经写好的函数", name, little_name)
return little_name
print(func.__doc__) # 我就是函数文档啦
print(func.__name__) # func
装饰器的主要功能和装饰器的固定结构
- 装饰器的主要功能:
在不改变函数调用方式的基础上在函数的前后添加功能。 - 装饰器的固定格式
from functools import wraps
def get_run_time(my_func):
@wraps(my_func) # 有时候这里不用
def inner(*args, **kwargs):
"""执行函数之前要做的"""
to_return = my_func(*args, **kwargs)
"""执行函数之后要做的"""
return to_return
return inner
- 带参数的装饰器(理解成先调用,再装饰原先的无参数的)
假设你有很多一个函数都是使用了一个装饰器,现在你要将他们全部取消,,how … 后面你发现取消之后不行,还是得用回去 … what_fu…
这告诉我们给装饰器带上参数,用来选择函数是否需要装饰内容,这是很重要的。
from functools import wraps
def outer(flag):
def get_run_time(my_func):
@wraps(my_func)
def inner(*args, **kwargs):
if flag:
print("""执行函数之前要做的""")
to_return = my_func(*args, **kwargs)
if flag:
print("""执行函数之后要做的""")
return to_return
return inner
return get_run_time
# ------> func = outer(False)(func(a,b)) = get_run_time(func(a,b)) = inner(func(a,b))
@ outer(False) # 不使用装饰的功能 == 1]after_outer = outer(False) 2]@after_outer
def func(name, little_name):
"""我就是函数文档啦---"""
print("hello , everybody~")
print("我是一个已经写好的函数", name, little_name)
return little_name
func("老哥", "little_prince")
# hello , everybody~
# 我是一个已经写好的函数 老哥 little_prince
- 多个装饰器装饰同一个函数
from functools import wraps
def outer(flag):
def get_run_time(my_func):
@wraps(my_func)
def inner(*args, **kwargs):
if flag:
print("""执行函数之前要做的 in outer""")
to_return = my_func(*args, **kwargs)
if flag:
print("""执行函数之后要做的 in outer""")
return to_return
return inner
return get_run_time
def outer_print(flag):
def print_sb(my_func):
@wraps(my_func)
def inner(*args, **kwargs):
if flag:
print("sb ")
to_return = my_func(*args, **kwargs)
if flag:
print("sb over")
return to_return
return inner
return print_sb
@outer(True) # ------> func = outer(True)(func(a,b)) = get_run_time(func(a,b)) = inner(func(a,b))
@outer_print(True)
def func(name, little_name):
"""我就是函数文档啦---"""
print("hello , everybody~")
print("我是一个已经写好的函数", name, little_name)
return little_name
func("老哥", "little_prince")
# 执行函数之前要做的 in outer
# sb
# hello , everybody~
# 我是一个已经写好的函数 老哥 little_prince
# sb over
# 执行函数之后要做的 in outer
自行忽略图片中out_print错误,应该为outer_print
作业
1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件)要求登录成功一次, 后续的函数都无需再输入用户名和密码。
flag = True
user_info_dict = {}
def get_user_info():
""""获取用户账户密码信息"""
with open("user_info", "r+", encoding='utf-8') as f:
for each_line in f: # f内容: jacky 001 \n weige 001
__user_name, __user_passwd = each_line.replace('\n', "").split(" ")
user_info_dict[__user_name] = __user_passwd
print(user_info_dict)
def user_log(my_func):
"""装饰器,用于加登录功能"""
def inner(*args, **kwargs):
"""log in"""
global flag
if flag:
usr_name = input("请输入您的用户名:")
usr_passwd = input("请输入您的密码:")
if usr_name in user_info_dict and usr_passwd == user_info_dict[usr_name]: # 用户名密码正确
print('登录成功!')
flag = False
to_return = my_func(*args, **kwargs)
return to_return
else: # 密码错误
print('密码错误!')
return
else:
to_return = my_func(*args, **kwargs)
return to_return
return inner
@user_log
def f1():
print("1")
@user_log
def f2():
print("2")
get_user_info()
f1()
f2()
f2()
2.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数写入文件.
from functools import wraps
import datetime
def use_info(my_func):
"""装饰器,添加调用记录"""
@wraps(my_func)
def inner(*args, **kwargs):
write__info(my_func) # 将函数名写入文件
to_return = my_func(*args, **kwargs)
return to_return
return inner
def write__info(func_name):
with open("use_info", "a+", encoding="utf-8") as f:
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 将调用的函数名和调用时间写入文件
f.write(func_name.__name__ + " " + now_time + "\n")
@use_info
def f1():
print("1")
@use_info
def f2():
print("2")
f1()
f2()