今日内容概要
- 异常捕获
- 异常捕获实参演练
- 异常捕获练习
- 生成器对象
- yield其他用法
- 生成器表达式(元组)
(一)异常捕获
1.如何理解异常:
程序在运行的过程中如果出现了异常会导致整个程序的结束
异常就是程序员口中的‘bug’
2.异常的结构:
Traceback (most recent call last): File "E:/pythonProject/Day21/02 练习.py", line 1, in <module> name NameError: name 'name' is not defined
(1)关键字line所在行
精准提示哪一行代码出错。
line 1表示报错位置在代码第一行
(2)最后一行冒号左侧
代表代码错误类型
NameError表示名字报错
(3)最后一行冒号右侧
说明错误的具体原因
name ‘name’ is not defined表示未定义名字‘name’
3.异常的类型:
表格列举如下
错误的类型 详情 NameError 使用一个还未被赋予对象的变量 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 SyntaxError Python代码非法,代码不能编译 TypeError 传入对象类型与要求的不符合 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
4.异常的分类:
(1)语法错误
不允许出现的,一旦出现立刻修改!!!eg:代码编写if判断后没有加冒号
(2)逻辑异常
可以允许的,如果出现了尽快修改即可
(二)异常捕获实参演练
1.什么时候才可能需要自己写代码处理异常?
当代码不确定什么时候会报错的情况下。
举个栗子:编写网络爬虫访问网址数据并处理,有可能会出现断网数据没有处理不了。
2.异常捕获的使用好处:
相当于提前预测可能出现的的问题并提前给出处理的措施。
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 as e: print(e) # name 'name' is not defined
(2)万能异常
try: 可能会出错的代码 except Exception as e: # 万能异常方式1 print(e) except BaseException as e: # 万能异常方式2 print(e)
举个栗子:
try: 123 + 'hello' except Exception as e: print(e) # unsupported operand type(s) for +: 'int' and 'str' except BaseException as e: print(e) # unsupported operand type(s) for +: 'int' and 'str'
4.异常捕获其它操作补充
(1)else与finally
try与else用法:try监测的代码没有出错的情况下正常运行结束 则会执行else子代码
try: name = 'jason' # 这里为正确代码 except Exception as e: print('出错了') else: print('没有出错')
try与finally用法:try监测的代码无论有没有出错 最后都会执行finally子代码
try: name # 这里为错误代码 except Exception as e: print('出错了') # else: # print('没有出错') finally: print('不管你错没错,都要执行我')
(2)断言
断言数据属于什么类型,如果不对则直接报错,对了则正常执行下面的代码
name = 'jason' # 通过一系列的手段获取来的数据 #assert isinstance(name,list) # 猜错直接报错 assert isinstance(name,str) # 猜对执行下面的代码 print('猜对了')
(3)主动抛异常
想让报错就报错,全凭自愿
name = input('name>>>').strip() if name == 'jason': raise Exception('反正就是错') # raise NameError('jason是错的') else: print('剩下的都是对的')
5.异常处理使用准则
(1)异常捕获能尽量少用就尽量少用
(2)被try监测的代码能尽量少就尽量少
(三)异常捕获练习
1.for循环内部本质
需求:使用while+异常捕获实现for循环的功能
l1 = [11,22,33,44,55] res = l1.__iter__() # 将列表可迭代对象变成迭代器对象 while True: try: print(res.__next__()) # 可能会报错代码 except Exception as e: break # 报错后直接结束循环
2.实际项目错误修改流程
(1)先看具体报错信息
(2)再看具体的定位信息(由下往上看)
(3)尽量将错误缩小到某个具体的变量
(4)注意力就关注出现这个变量的代码身上即可
(四)生成器对象
1.生成器对象的本质
生成器本质就是迭代器对象,唯一的区别就是迭代器对象是解释器提供给我们的,而生成器对象使我们自己定义出来的。
2.生成器对象的目的及作用
目的:为了优化代码
作用:一种不依赖于索引取值的通用方式;
可以节省数据类型的内存占用空间。
3.生成器对象代码实现
当函数体代码中有yield关键字,在没有调用函数之前,yield只是一个普通的函数
def index(): print('jason') yield print(index) # <function index at 0x000001C7FA40D1E0>
当函数名第一次加括号调用后不会执行该函数体代码,而是将普通的函数变成了迭代器对象(生成器)
def index(): print('jason') yield res = index() print(res) # <generator object index at 0x000002BB0D9A2DB0>
yield可以在函数体代码中出现多次。
变成生成器对象之后调用__next__就会开始执行函数体代码,每次调用__next__方法都会从上往下执行直到遇到yield代码停留在此处
def index(): print('jason') yield print('kevin') yield print('oscar') yield res = index() res.__next__() res.__next__() res.__next__() D:\Python36\python36.exe "E:/pythonProject/Day21/02 练习.py" jason kevin oscar Process finished with exit code 0
yield后面如果有数据值 则会像return一样返回出去
如果有多个数据值逗号隔开 那么也会自动组织成元组返回
def index(): print('jason') yield 111,222,333 print('kevin') yield 111,222 print('oscar') yield 111 res = index() print(res.__next__()) print(res.__next__()) print(res.__next__()) D:\Python36\python36.exe "E:/pythonProject/Day21/02 练习.py" jason (111, 222, 333) kevin (111, 222) oscar 111 Process finished with exit code 0
4.课堂练习
需求:编写生成器 实现range方法的功能
(1)先考虑以两个参数的功能编写————range(1,10)def num(start,end): while start < end: # 判断开始值是否小于末尾值 yield start # 开始循环 start += 1 # 循环每次自增1 for i in num(1,10): # i循环取值 print(i) # 1 2 3 4 5 6 7 8 9
(2)然后考虑一个参数的情况————range(10)
def num(start,end=None): if not end: # 将False条件变为True end = start # start值绑定end start = 0 # start 绑定0 while start < end: # 判断开始值是否小于末尾值 yield start # 开始循环 start += 1 # 循环每次自增1 for i in num(10): # i循环取值 print(i) # 0 1 2 3 4 5 6 7 8 9
(3)最后考虑三个参数的情况————range(1,10,2)
def num(start,end=None,step = 1): if step < 1: step = 1 if not end: # 将False条件变为True end = start # start值绑定end start = 0 # start 绑定0 while start < end: # 判断开始值是否小于末尾值 yield start # 开始循环 start += step # 循环每次自增1 for i in num(1,10,2): # i循环取值 print(i) # 1 3 5 7 9
(五)yield其他用法
.send():传值并自动调用__next__方法
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__方法
(六)生成器表达式(元组)
1.核心功能依旧是节省内存
2.语法结构
l1 = (i**2 for i in range(10) if i > 3) print(l1) # <generator object <genexpr> at 0x000001A793439C10>
3.面试例题(******)
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(n, i) for i in g) 第二次for循环 g = (add(10, i) for i in (add(10, i) for i in g)) # 10+10 """ res = list(g) 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] # 答案:C