异常捕获、yield其他用法、生成器对象
异常捕获
1.如和理解异常
程序在运行的时候出现了异常导致整个程序结束,也称之为‘bug’
2.异常的结构
如何理解错误提示
关键字line所在行:精准提示哪一行的代码出错
最后一行冒号左侧:错误的类型
最后一行冒号右侧:错误的具体原因(改错误的关键)
3.异常的类型
NameError:名字有错误
IndexError:索引有错误
KeyError:键有错误
SyntaxError:语法错误
TypeError:类型错误
4.异常的分类
1.语法错误
不允许出现的 一旦出现请立刻修改!!!
2.逻辑错误
允许出现的 允许出错之后修改即可!!!
异常捕获实参演练
1.什么时候会需要自己写代码处理异常
当代码不确定什么时候会报错的情况下,就是自己也不知道什么时候会报错,不会确定什么时候发生,但是会有发生的概率
eg:编写网络爬虫访问网址数据并处理
1.向某个网页发送请求 获取该网页的数据(可能会出现错误)
2.处理网页数据
有可能会出现断网数据没有处理不了
2.异常捕获的使用相当于是提前预测可能会出现的问题,然后提前给予一些处理的措施
3.异常捕获的具体使用
3.1.基本语法机构:
try:
可能会出错的代码(被try监控)
except 错误类型1 as e: # e就是具体错误的原因
对应错误类型1的解决措施
except 错误类型2 as e: # e就是具体错误的原因
对应错误类型2的解决措施
except 错误类型3 as e: # e就是具体错误的原因
对应错误类型3的解决措施
except 错误类型4 as e: # e就是具体错误的原因
对应错误类型4的解决措施
try:
name
except NameError:
print('name没有被定义')
'''
IndexError: list index out of range
争对不同的错误类型要做出不同的区分,使用正确的错误类型关键字
'''
try:
l1 = [1, 2, 3, 4]
l1[10]
except NameError:
print('name没有被定义')
except IndexError:
print('索引出错了')
3.2.万能异常(笼统的处理方式,在你不知道什么错误的情况下,就是统一的处理方式)
try:
# name
# d = {'name':'jason'}
# d['pwd']
123 + 'hello'
except Exception as e: # 万能异常方式1
print(e)
except BaseException as e: # 万能异常方式2
print(e)
4.异常捕获其他操作补充
4.1.else与finally
try:
name = 123
except Exception as e:
print('你出错了 你个小垃圾')
else:
print('try监测的代码没有出错的情况下正常运行结束 则会执行else子代码')
finally:
print('try监测的代码无论有没有出错 最后都会执行finally子代码')
4.2.断言
name = 'jason'
# 通过一系列的手段获取来的数据
assert isinstance(name, list)
# 断言数据属于什么类型 如果不对则直接报错 对则正常执行下面的代码
print('针对name数据使用列表相关的操作')
4.3.主动抛出异常
name = input('username>>>:').strip()
if name == 'jason':
# raise NameError('jason来了 快跑!!!')
raise Exception('反正就是不能过')
else:
print('不是jason 那没事了')
'''可以用来处理一些紧急情况'''
5.强调
5.1.异常捕获能尽量少用就尽量少用,因为在检测一个语法的时候它在底层会消耗许多资源,代码跑起来就会消耗许多资源
5.2.被try监测的代码能尽量少就尽量少
异常捕获练习
1.for循环内部的本质
l1 = [11, 22, 33, 44, 55, 66, 77, 88]
# 变为迭代器对象
res = l1.__iter__()
while True:
try:
# 当出现异常的时候,捕获错误
print(res.__next__())
except Exception as e:
break
2.实际项目练习
'''
1.先看具体的报错信息
2.再看具体的定位信息
由下往上查看
3.尽量将错误缩小到某个具体的变量
4.注意力就关注在出现这个变量的代码身上即可
'''
user_data = {
'1': {'name': 'jason', 'pwd': '123', 'access': ['1', '2', '3']},
'2': {'name': 'kevin', 'pwd': '321', 'access': ['1', '2']},
'3': {'name': 'oscar', 'pwd': '222', 'access': ['1']}
}
# 8定义全局变量
is_login = {
'is_login': False,
'access_list': []
}
def login_auth(func_id):
def outer(func):
def inner(*args, **kwargs):
# 10 先判断用户是否为登陆状态
if is_login.get('is_login'):
# 11 获取当前用户执行权
access = is_login.get('access')
# !2 判断当前函数编号是否在用户权限内
if func_id in access:
res = func(*args, **kwargs)
return res
else:
print('你当前没有编号为%s的函数执行权' % func_id)
else:
# 1.获取用户编号
user_id = input('请输入用户编号>>>').strip()
# 2.判断用户编号是否存在
if user_id in user_data:
# 3.获取用户名和密码
username = input('请输入用户名>>>').strip()
pwd = input('请输入密码>>>').strip()
# 4.比对信息
real_dict = user_data.get(user_id)
if username == real_dict.get('name') and pwd == real_dict.get('pwd'):
# 5 获取权限
access = real_dict.get('access')
# 9.数据正确的情况下,更改全局变量
is_login['is_login'] = True
is_login['access_list'] = access
# 6判断当前用户是否含有当前被装饰函数的执行权限,假设当前被转送给hi函数的红能变好为func_id
if func_id in access:
# 7有权限
res = func(*args, **kwargs)
return res
else:
print('你没有当前功能编号为%s的函数执行权' % func_id)
return inner
return outer
@login_auth('1')
def func1():
print('from func1')
func1()
生成器对象
1.本质其实就是迭代器对象
迭代器对象是解释器提供给我们的,而生成器是我们自己定义出来的
2.作用
一种不依赖索引取值的通用方式,可以节省数据类型的内存占有空间,优化代码
3.生成器对象代码实现
def index():
print('我是谁呀 嘿嘿嘿~')
yield 111, 222, 333
print('你追我 如果你追到我 我就...')
yield 222
print('实在跑不动了')
yield 333
res = index()
print(res) # <generator object index at 0x000001F0B264BA50>
# 在运行双下next的时候遇到yield会停下,下一次会从上一次停下的位置开始,遇到yield继续停下
res.__next__() # 我是谁呀 嘿嘿嘿~
res.__next__() # 你追我 如果你追到我 我就...
# res.__next__() # 实在跑不动了
print(res.__next__())
# res.__next__() # 这一次就报错
"""
当函数体代码中有yield关键字
那么函数名第一次加括号调用不会执行函数体代码
而是由普通的函数变成了迭代器对象(生成器) 返回值
"""
# 编写生成器 实现range方法的功能
# # 1.俩个参数的功能编写
def my_range(start_num, end_num):
# 有条件
while start_num < end_num:
# 每一次执行到这就会返回一个数
yield start_num
# 每一次执行完以后都会加1
start_num += 1
# for循环底层有异常捕获,不会报错
for i in my_range(1, 10):
print(i)
# 2.以一个参数的功能编写
def my_range(start_num, end_num=None):
if not end_num:
end_num = start_num
start_num = 0
while start_num < end_num:
# 每一次执行到这就会返回一个数
yield start_num
# 每一次执行完以后都会加1
start_num += 1
# for循环底层有异常捕获,不会报错
for i in my_range(10):
print(i)
# 3.考虑三个参数的情况
def my_range(start_num, end_num=None, step=1):
if not end_num:
end_num = start_num
start_num = 0
while start_num < end_num:
# 每一次执行到这就会返回一个数
yield start_num
# 每一次执行完以后都会加1
start_num += step
# for循环底层有异常捕获,不会报错
for i in my_range(10):
print(i)
yield其他用法
def index(name,food=None):
print(f'{name}准备干午饭!!!')
while True:
food = yield
print(f'{name}正在吃{food}')
res = index('jason')
res.__next__()
res.send('生蚝') # 传值并自动调用__next__方法
res.send('韭菜') # 传值并自动调用__next__方法
res.send('腰子') # 传值并自动调用__next__方法
'''
send的功能:
1.给前面yield里面传值
2.会自动调用一个双下next方法
yield的功能:
不仅仅可以返回一个值,而且还可以传值
'''
生成器表达式
为了节省存储空间,在后期做代码优化的时候可以用到
l1 = (i**2 for i in range(10) if i > 3)
print(l1)
# <generator object <genexpr> at 0x0000022003D2BA50>