今日学习目标:
- 闭包函数
- 装饰器
今日学习内容:
内容简要:
- 闭包函数(重要)
- 装饰器(重点+难点)
内容详细:
闭包函数(重要)
闭包函数(重要)
-
什么闭包
如果在一个函数的内部定义了另一个函数,外部的函数叫它外函数,内部的函数叫它内函数。
-
闭包条件
1 内函数里运用了外函数的临时变量。 2 并且外函数的返回值是内函数的引用。 3 在一个外函数中定义了一个内函数。
-
闭包函数的两大特征
1.闭:定义在函数内部的函数
2.包:内部函数使用了外层函数名称空间中的名字特征
举例:
def outer():
x = 999
def inner():
print('from outer>>>inner',x)
return inner
x = 666
res = outer()
res()
'''
#执行结果
from outer>>>inner 999
'''
-
闭包函数的实际应用
闭包函数是给函数体传参的另外一种方式举例:
def outer(username): def index(): print(username) # 永远使用的都是jason return index res = outer('kevin') # 形参username与值kevin临时绑定 >>>:outer局部名称空间中 res() res1 = outer('jason') # 形参username与值jason临时绑定 >>>:outer局部名称空间中 res1() ''' #执行结果 kevin jason '''
装饰器
-
装饰器简介(重点+难点)
python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
以上就是官方一点的说法,装饰器通俗易懂的说,可以类似与你买了一部手机以后,想让它外观看起来更好看一点,就又给它装了个手机壳。装饰器就可以类比手机壳。
装饰器的原则
(开放封闭原则):1.对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。 2.对修改封闭,意味着类一旦设计完成,就可以独立其工作,而不要对类尽任何修改。
举例
需求:统计函数的执行时间
扩展:
import time # 模块调用
print(time.time()) # 1647568920.168808
'''上述的数字是时间戳:1970年1月1日0时0分0秒距离刚刚代码运行间隔的秒数'''
time.sleep(3) # 让程序原地等待三秒
print('hello world')
实现
import time
def test():
time.sleep(3)
print('test is running!')
'''给test函数增加了一个统计执行时间的功能'''
start = time.time() # 函数执行之前获取一个时间戳
test()
stop = time.time() # 函数执行之后获取一个时间戳
print(stop - start) # 两个时间戳的差值就是函数的执行时间
'''
#执行结果
test is running!
3.000020980834961
'''
-
简易版本装饰器
思考
:上述功能是实现了,但是它好像只能满足一个函数去统计运行时间。这样的话就会显得这个功能实现的很鸡肋,如果这个功能可以用在其他函数上的话它的价值就会提高
提高
:
利用之前函数的知识就会想到将这个功能封装成一个函数,然后向里面传参数import time def get_time(func): start_time = time.time() # 函数执行之前获取一个时间戳 func() end_time = time.time() # 函数执行之后获取一个时间戳 print(end_time - start_time) # 两个时间戳的差值就是函数的执行时间 def home(): time.sleep(3) print('home is running!') get_time(home) ''' #执行结果 home is running! 3.0141494274139404 '''
思考
现在这个功能的实现就和装饰器的定义很像了,但是还有一点条件没满足:
封装成函数之后调用方式改变了
提升
对于这个问题利用之前函数名的使用方式,所以采用函数体传参的方式
import time
def home():
time.sleep(3)
print('home is running!')
print(home)
def outer(func): # 真正的index被outer局部名称空间存储了
def get_time():
start_time = time.time() # 函数执行之前获取一个时间戳
func() # 调用了真正的index函数
end_time = time.time() # 函数执行之后获取一个时间戳
print(end_time - start_time) # 两个时间戳的差值就是函数的执行时间
return get_time
home = outer(home) # 狸猫换太子,看似调用的home其实调用的是get_time
print(home)
home()
'''
#执行结果
<function home at 0x0000027480CEF820>
<function outer.<locals>.get_time at 0x0000027480CEF5E0>
home is running!
3.0091540813446045
'''
个人总结:
这样就基本上实现了装饰器的功能,从这些思考过程可以看出装饰器的实现完全是之前:名称空间 函数名 闭包函数这些知识的整合。所以想要真正理解装饰器的话,将上述这些知识点搞懂就应该差不多了。
- 完整版本装饰器
上一版本的装饰器,不难看出对于对于内层函数的话,还是有一点可以改进的地方:参数问题,返回值问题,所以利用之前参数和函数返回值的知识进一步改进
def outer(func_name):
def get_time(*args, **kwargs):
start_time = time.time()
res = func_name(*args, **kwargs) # 执行真正的index函数
end_time = time.time()
print(end_time - start_time)
# return '不要急躁' # 如何在此处返回真正index函数的返回值
return res
return get_time
- 装饰器模板(拷贝使用即可)
最后利用之前的所有过程,就可以总结出一套万能模板
'''编写装饰器其实有一套固定的代码 不需要做任何理解'''
def outer(func_name): # func_name用于接收被装饰的对象(函数)
def inner(*args, **kwargs):
print('执行被装饰函数之前 可以做的额外操作')
res = func_name(*args, **kwargs) # 执行真正的被装饰函数
print('执行被装饰函数之后 可以做的额外操作')
return res # 返回真正函数的返回值
return inner
- 装饰器语法糖(@装饰器名)
装饰器语法糖:其实就是python帮助我们去进一步优化装饰器功能的写法,
让它更加简洁的被使用
def outer(func_name):
def inner(*args, **kwargs):
print('执行函数之前的操作')
res = func_name(*args, **kwargs)
# 额外操作
return res
return inner
@outer # 等价于 home = outer(home)
def home(*args,**kwargs):
print('home is running!')
补充
语法糖内部原理:
1.使用的时候最好紧跟在被装饰对象的上方
2.语法糖会自动将下面紧挨着的函数名传给@后面的函数调用
- 装饰器修复技术(扩展知识)
对于一些追求完美的程序员来说,这个版本的装饰器从底层原理来说还是不能完全符合装饰器的定义,因为对于原函数的调用只是表面上看是调用了原函数,但是从底层第调用地址来说还是用的装饰器函数的地址,所以说它还是有瑕疵。下面就来解决这个问题:
from functools import wraps
def outer(func_name):
@wraps(func_name)
def inner(*args, **kwargs):
print('执行被装饰对象之前可以做的操作')
res = func_name(*args, **kwargs)
return res
return inner
@outer
def home():
'''这是home函数的注释'''
print('from home')
help(home) # help可以查看指定函数的注释信息
print(home)
print("*****************************")
home()
print(home)
'''
#执行结果
Help on function home in module __main__:
home()
这是home函数的注释
<function home at 0x00000200075FF700>
*****************************
执行被装饰对象之前可以做的操作
from home
<function home at 0x00000200075FF700>
'''
今日学习时间:
这里统计计划学习的时间
1、 上午8:30~12:30
2、 下午2:30~5:30
3、 晚上6:30~9:30
今日学习产出:
这里统计学习计划的总量
- 1、 技术笔记 1遍
- 2、CSDN 技术博客 1篇
- 3、每日录音,预习明日内容
- 4、作业
编写一个用户认证装饰器
基本要求
执行每个函数的时候必须先校验身份 eg: jason 123
拔高练习(有点难度)
执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
register login transfer withdraw
提示:全局变量 记录当前用户是否认证
# 验证是否是管理员
def outer(func):
def inner(*args, **kwargs):
# 判断是否已经验证过了
if count:
emp_name = input('请输入用户名>>>:').strip()
emp_pwd = input('请输入密码>>>:').strip()
# 判断管理员用户名和密码是否正确
if emp_name == user_data_dict['0']['emp_name'] and emp_pwd == user_data_dict['0']['emp_pwd']:
res = func(*args, **kwargs)
return res
else:
return
else:
res = func(*args, **kwargs)
return res
return inner
# 添加员工信息
@outer
def add():
# 1.先获取员工编号
emp_id = input('请输入新员工的编号>>>:').strip()
# 2.判断员工编号是否已存在 如果存在则提示重复 不存在则正常往下执行
if emp_id in user_data_dict:
print('员工编号已存在 无法添加')
return # 3.存在 则直接结束该功能
# 4.正常获取员工的其他数据
emp_name = input('请输入员工的姓名>>>:').strip()
emp_age = input('请输入员工的年龄>>>:').strip()
emp_salary = input('请输入员工的薪资>>>:').strip()
# 5.临时构建一个用户小字典
user_dict = {'emp_id': emp_id, 'emp_name': emp_name, 'emp_age': emp_age, 'emp_salary': emp_salary}
# 6.将数据添加到大字典中
user_data_dict[emp_id] = user_dict # 键不存在则新增键值对
# 7.打印提示信息
print(f'员工{emp_name}添加成功')
# 查询特定员工
@outer
def query_one():
# 1.获取员工编号
target_id = input('请输入您想要查看的员工编号>>>:').strip()
# 2.判断当前编号是否存在
if target_id not in user_data_dict:
print('员工编号不存在 无法查看')
return # 3.存在 则直接结束该功能
# 4.直接获取对应的员工数据字典
user_dict = user_data_dict.get(target_id)
# 5.打印员工数据
print("""
------------emp info------------
员工编号:%s
员工姓名:%s
员工年龄:%s
员工薪资:%s
---------------end--------------
""" % tuple(user_dict.values())) # %(1,2,3,4) 简便写法
# 修改员工薪资
@outer
def revise():
# 1.先获取想要修改薪资的员工编号
target_id = input('请输入您想要修改的员工编号>>>:').strip()
# 2.判断当前编号是否存在
if target_id not in user_data_dict:
print('员工编号不存在 无法修改')
return # 3.存在 则直接结束该功能
# 4.获取对应员工的字典
user_dict = user_data_dict.get(target_id)
# 5.获取新的薪资
new_salary = input('请输入该员工新的薪资待遇>>>:').strip()
# 6.修改员工字典数据
user_dict['emp_salary'] = new_salary
# 7.修改大字典数据
user_data_dict[target_id] = user_dict
# 8.提示信息
print(f'编号为{target_id}的员工薪资成功修改为{new_salary}')
# 查询所有员工
@outer
def query():
for user_dict in user_data_dict.values(): # {} {} {}
print("""
------------emp info------------
员工编号:%s
员工姓名:%s
员工年龄:%s
员工薪资:%s
---------------end--------------
""" % tuple(user_dict.values())) # %(1,2,3,4) 简便写法
# 删除特定员工
@outer
def delete_one():
# 1.获取想要删除的员工编号
delete_id = input('请输入您想要删除的员工编号>>>:').strip()
# 2.判断当前编号是否存在
if delete_id not in user_data_dict:
print('员工编号不存在 无法删除')
return # 3.存在 则直接结束该功能
# 3.根据字典的key删除数据
res = user_data_dict.pop(delete_id)
# 4.提示
print(f'员工信息删除完毕', res)
# 退出员工系统
def secede():
print('退出员工系统,欢迎下次光临!')
# 定义一个功能存放区
func_dict = {
'1': add,
'2': query_one,
'3': revise,
'4': query,
'5': delete_one,
'6': secede,
}
user_data_dict = {'0': {'emp_name': 'jason', 'emp_pwd': '123'}, }
count = True
while True:
print("""
1.添加员工信息
2.查询特定员工
3.修改员工薪资
4.查询所有员工
5.删除特定员工
6.退出员工系统
""")
choice = input('请输入您想要执行的功能编号>>>:').strip()
if choice in func_dict:
# 获取键对应的值(函数名)
func_name = func_dict.get(choice)
# 执行函数
func_name()
count = False
if choice == '6':
break
else:
print('功能编号不存在')