异常捕获
如何理解异常
程序在运行的过程中如果出现了异常会导致整个程序的结束 异常就是程序员口中的’bug’
异常结构
'
Traceback (most recent call last):
File "/Users/zhengzhuoran/PycharmProjects/day21/02 异常的结构.py", line 1, in <module>
name
NameError: name 'name' is not defined
如何阅读报错分析代码
1.关键字line所在行(精准提示哪一行代码出错了)
2.最后一行冒号左侧(错误类型)
3.最后一行冒号右侧(错误的具体原因,也是改bug的关键)
异常的类型
NameError
IndexError
KeyError
SyntaxError
TypeError
…
异常的分类(必须掌握)
- 语法错误
* 不允许出现的 一旦出现请立即更正!!! - 逻辑错误
* 允许出现的 允许出错后修改即可!!!
公司软件上线流程
- 需求分析
- 架构设计
- 分组开发
- 提交测试
- 交付上线
异常捕获实参演练
为什么需要自己写代码处理异常
当代码不确定什么时候会报错的情况下
e.g:编写网络爬虫访问网址数据并处理 有可能会出现断网数据处理不了异常捕获的使用相当于是提前预测可能出现的问题并提给出处理措施
异常捕获的代码实现
# 基本语法结构(针对性很强)
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解决措施
except 错误类型5 as e: # e就是具体错误的原因
对应错误类型5解决措施
万能异常(笼统的处理方式)
try:
name
d = {'name':'jason'}
d['pwd']
123 + 'hello'
except Exception as e: # 万能异常方式1
print(e)
except BaseException as e: # 万能异常方式2
print(e)
异常捕获其他操作补充
# else与finally
try:
name
except Exception as e:
print('出错了 小菜坤')
else:
print('try监测的代码没有出错的情况下正常运行结束 则会执行else子代码')
finally:
print('try监测的代码无论有没有出错 最后都会执行finally子代码')
断言
name = 'jason' # 通过一系列的手段获取来的数据
assert isinstance(name, list) # 断言数据属于什么类型 如果不对则直接报错 对则正常执行下面的代码
print('针对name数据使用列表相关的操作')
主动报异常
name = input('username>>>:').strip()
if name == 'jason':
raise NameError('jason来了 快跑!!!')
raise Exception('反正就是不能过')
else:
print('不是jason 那没事了')
强调
1.异常捕获能尽量少用就少用
2.被try监测的代码能尽量少就尽量少
异常捕获练习
'''
1.先看具体报错信息
2.在看具体定位信息
3.尽量将错误缩小到某个具体变量
4.注意力关注在出现这个变量的代码上即可
'''
生成器对象
生成器对象是什么
生成器对象的本质是迭代器对象
但迭代器是解释器提供给我们的 而生成器是我们自己写的
主要以__iter__ 和 __next__方法还有关键字yield为主
学习生成器对象的目的
是为了优化代码 以一种不依赖于索引去值的方式 在需要数据时一一取出 从而内存占用空间(主要目的)
yield关键字
def func():
print(123)
yield
func() # 运行后 没有反应
# 当函数体内有yield关键字 第一次运行时并不会执行函数体代码 而是产生一个迭代器对象(生成器)可以由一个变量名接收
res = func()
res.__next__() # 123 通过调用__next__方法就可以调用yield上面的代码
# 当然 函数体内可以不止有一个yield
def func():
print(1)
yield
print(2)
yield
print(3)
yield
print(4)
yield
res = func()
res.__next__() # 1
res.__next__() # 2
res.__next__() # 3
res.__next__() # 4
# yield 后面可以添加返回值 效果和return相似
def func():
print(1)
yield 111
print(2)
yield 222
print(3)
yield 333
res = func()
res1 = res.__next__() # 1
res2 = res.__next__() # 2
res3 = res.__next__() # 3
print(res1) # 111
print(res2) # 222
print(res3) # 333
yield关键字的其他用法
# 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__方法
生成器表达式
l1 = [i for i in range(10)]
print(l1) # [0, 1, 2, 3, 4, 5, 6, 7, 8 ,9]
n = 1
l1 = (i + n for i in range(10))
print(l1) # <generator object <genexpr> at 0x00000234BAD46B30> "元组生成式"生成了一个生成器对象
# 只有通过__next__或相关操作取出里面的数据 表达式才会生效
print(list(l1)) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l2 = (i + n for i in range(10))
n = 2 # 修改了n = 2
print(list(l2)) # [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 执行了修改后的n
# 面试题
def add(n, i): # 普通函数 返回两个数的和 求和函数
return n + i
def test(): # 生成器
for i in range(4):
yield i
g = test() # 激活生成器
for n in [1, 10]:
g = (add(n, i) for i in g)
"""
第一次for循环
g = (add(1, i) for i in g) # 这里并没有调用 只是产生了一个迭代器(生成器)
第二次for循环
g = (add(10, i) for i in (add(10, i) for i in g)) # 这里的add()中的n变为了10 然后再产生了一个生成器
"""
res = list(g) # 这里才进行了调用 n为10 所以全部生成器中n都为10
"""
大致过程
先:g = (add(10, i) for i in (10, 11, 12, 13))
再 res = (20, 21, 22, 23)
"""
print(res)
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23] # 正确答案
#D. res=[21,22,23,24]