异常处理及生成器相关
异常常见类型
在编程语言中,异常即我们常说的报错,也叫做BUG,在python中,常见的错误类型有
异常处理语法结构
当我们遇到异常后,会导致整个程序停止运行,这时候就需要用到异常处理
'''1.基本语法结构 try: 待监测的代码(可能会出错的代码) except 错误类型: 针对上述错误类型制定的方案 ''' l1 = [1, 2, 3, 4, 6, 7] # 定义列表 try: l1[10] # 监测代码运行 except IndexError: # 当错误类型为索引值错误时,输出哈哈哈 print('哈哈哈') # 哈哈哈 try: WASD # 监测代码运行 except IndexError: # 当错误类型不为索引值错误时,直接报错 print('哈哈哈') # 报错内容为监测代码所属的错误类型 ''' Traceback (most recent call last): File "D:\pythonProject4\test.py", line 12, in <module> wasd NameError: name 'wasd' is not defined ''' ''' 2.查看错误的信息 try: 待监测的代码(可能会出错的代码) except 错误类型 as e: # e就是系统提示的错误信息 针对上述错误类型制定的方案 ''' l1 = [1, 2, 3, 4, 6, 7] # 定义列表 try: l1[10] # 检测索引取值 except IndexError as e: # 当索引值错误时,输出错误原因 print(e) # list index out of range,列表索引超出范围 ''' 3.针对不同的错误类型制定不同的解决方案 try: 待监测的代码(可能会出错的代码) except 错误类型1 as e: # e就是系统提示的错误信息 针对上述错误类型1制定的方案 except 错误类型2 as e: # e就是系统提示的错误信息 针对上述错误类型2制定的方案 except 错误类型3 as e: # e就是系统提示的错误信息 针对上述错误类型3制定的方案 ... ''' try: l1[10] # 监测代码运行 except NameError as e: # 当错误类型不为名称错误是不执行 print(e) except IndexError as f: # 当错误类型为索引错误时执行 print(f) # list index out of range # 需要手动添加多种错误,过程较为繁琐 ''' 4.万能异常 Exception/BaseException try: 待监测的代码(可能会出错的代码) except Exception as e: # e就是系统提示的错误信息 针对各种常见的错误类型全部统一处理 ''' l1 = [1, 2, 3, 4, 6, 7] try: l1[10] except Exception as e: # 根据可以将各种常见错误类型统一进行处理 print(e) # list index out of range try: wasd except Exception as e: print(e) # name 'wasd' is not defined # 在同一段代码中有多个错误时,try只会监测第一个错误,因为try一次只能监测一种错误,所以不建议使用try监测过长代码 ''' 5.结合else使用 try: 待监测的代码(可能会出错的代码) except Exception as e: # e就是系统提示的错误信息 针对各种常见的错误类型全部统一处理 else: try的子代码正常运行结束没有任何的报错后,执行else子代码 ''' l1 = [1, 2, 3, 4, 6, 7] try: l1[4] except Exception as e: print(e) else: print('未检测到错误') # 未检测到错误 # 当监测的代码中不存在错误时执行else子代码 ''' 6.结合finally使用 try: 待监测的代码(可能会出错的代码) except Exception as e: # e就是系统提示的错误信息 针对各种常见的错误类型全部统一处理 else: try的子代码正常运行结束没有任何的报错后,执行else子代码 finally: 无论try的子代码是否报错,最后都要执行finally子代码 ''' l1 = [1, 2, 3, 4, 6, 7] try: l1[10] except Exception as e: print(e) # 当被监测代码出现异常时,先输出异常类型 list index out of range else: print('未检测到错误') finally: print('检测流程结束') # 然后执行finally子代码 检测流程结束 l1 = [1, 2, 3, 4, 6, 7] try: l1[4] except Exception as e: print(e) else: print('未检测到错误') # 当被监测代码出现异常时,执行else子代码 未检测到错误 finally: print('检测流程结束') # 然后执行finally子代码 检测流程结束
异常处理过程中还有另外两种比较特殊的模式
# 1.断言,即预测并断定某一段代码的执行结果,如果并不是我们所预测的结果,会直接报错 name = 'jason' assert isinstance(name, int) ''' Traceback (most recent call last): File "D:\pythonProject4\test.py", line 20, in <module> assert isinstance(name, int) AssertionError(错误类型) ''' assert isinstance(name, str) print('哈哈哈 我就说吧 肯定是字符串') # 如果预测正确则继续向下执行 name.strip() 2.主动抛异常 name = 'jason' if name == 'jason': raise Exception('老子不干了') # 根据特定条件,报错指定错误类型内容,如果不符合指定条件,则继续向下执行 else: print('正常走') ''' Traceback (most recent call last): File "D:\pythonProject4\test.py", line 23, in <module> raise Exception('老子不干了') Exception: 老子不干了 '''
PS:
1.异常处理能尽量少用就少用
2.被try监测的代码能少就尽量少
3.当代码中可能会出现一些无法控制的情况报错才应该考虑使用(例如在程序运行时网络断线)课堂练习
# 代码实现for循环 l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99] # 定义被循环的列表 list1 = l1.__iter__() # 将列表转换为迭代器对象 try: # 监测代码运行 while True: print(list1.__next__()) # 循环使用双下next方法从字典中取出数据值 except Exception as e: # 如果检测到程序报错直接跳过 pass
生成器对象
生成器对象依旧是内置有双下iter以及双下next方法的迭代器对象,只不过迭代器对象是python内置可以直接提供给我们使用的,生成器对象则需要我们自己使用代码与关键字定义出来
创建生成器对象的基本语法就是在函数体代码中添加yield关键字
def my_iter(): print('哈哈哈') yield ''' 当我们添加yield关键字后,调用函数后并不会直接执行生成器中的代码,而是会生成一个生成器对象,只有当我们利用双下next调用函数名所生成的生成器对象时,才会真正执行器中的代码 ''' def my_iter(): print('哈哈哈') yield res = my_iter() res.__next__ # 运行print('哈哈哈') ''' 每次调用结束后会停留在yield关键字之后,然后下一次调用时直接从停留的位置作为起始位置 ''' def my_iter(): print('哈哈哈 椰子汁很好喝') yield 111, 222, 333 print('呵呵呵 从小喝到大') yield 111, 222, 333 print('嘿嘿嘿 特种兵牌还可以') yield 111, 222, 333 print('哼哼哼 千万别整多了 倒沫子 头发掉光光') yield 111, 222, 333 res = my_iter() r1 = res.__next__() print(r1) # 哈哈哈 椰子汁很好喝 111,222,333 r2 = res.__next__() print(r2) # 呵呵呵 从小喝到大 111,222,333 r3 = res.__next__() print(r3) # 嘿嘿嘿 特种兵牌还可以 111,222,333 r4 = res.__next__() print(r4) # 哼哼哼 千万别整多了 倒沫子 头发掉光光 111,222,333 ''' yield类似于return,可以返回返回值 '''
课堂练习
自定义生成器对标range功能(一个参数 两个参数 三个参数 迭代器对象) for i in range(1, 10): print(i) # 1.一个参数 def my_range(a): # 定义生成器 b = 0 # 定义起始值 A = True try: while A: # 循环对比 if a > b: # 如果参数大于起始值 yield b # 返回起始值 b +=1 # 起始值自增一 else: break # 不符合条件后结束循环 except Exception as e: print(e) res = my_range(10).__iter__() # 变量名绑定迭代器对象 for i in res: # 激活生成器,并获得生成其中的数据 print(i) # 2.两个参数 def my_range(start , end): # 定义含有两个参数的生成器 try: while True: # 开始循环 if end > start: # 如果结束值大于起始值 yield start # 返回起始值 start +=1 # 起始值自增一 else: # 否则终止循环 break except Exception as e: print(e) res = my_range(1,10).__iter__() # 变量名绑定迭代器对象 for i in res: # 激活生成器,遍历取得其中的数据值 print(i) # 3.三个参数 def my_range(start, end=None, step=1): # 定义含有两个参数的生成器 try: while True: # 开始循环 if end > start: # 如果结束值大于起始值 yield start # 返回起始值 start += step# 起始值自增步长的值 else: # 否则终止循环 break except Exception as e: print(e) res = my_range(1,10,2).__iter__() # 变量名绑定迭代器对象 for i in res: # 激活生成器,遍历取得其中的数据值 print(i)
yield冷门用法
def eat(name, food=None): # 定义生成器 print(f'{name}准备用餐') # 格式化输出 while True: # 循环 food = yield # 将food与yield绑定 print(f'{name}正在吃{food}') # 格式化输出 res = eat('jason') # 生成器与变量名绑定 res.__next__() # 第一次激活激活生成器,停留在yield位置 # res.send('汉堡') # 将括号内的数据传给yield前面的变量名后再自动调用__next__ # res.send('包子') # res.send('面条')
生成器表达式
本质即生成器的简化写法
l1 = (i ** 2 for i in range(100)) # 生成器对象 print(l1) # <generator object <genexpr> at 0x000001DFC07F7E40> for i in l1: # 激活生成器 print(i)
面试题
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)) """ 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]
在题目中,第一次for循环,取得数值1,但是此时生成器并没有被激活
但是g与(add(n, i) for i in g)绑定
所以g = (add(n, i) for i in (add(n, i) for i in g))
在第二次for循环时,取得数值10,而此时生成器被激活,n=10
此时g = (add(10, i) for i in (add(10, i) for i in g))
i为test中依次取出的值0,1,2,3
将i的值依次套入到g,得到10+10,10+11,10+12,10+13
所以四个值分别为20,21,22,23
所以这道题的答案为C