[2022.7.12]异常捕获与生成器的介绍和调用
学习内容目录
-
异常捕获
-
自定义迭代器对象(生成器)
-
自定义迭代器练习题
-
生成器表达式
学习内容详细
异常捕获
-
1.什么是异常?
在程序运行过程中出现的报错 没有按正常的流程走下去 出现错误 导致程序提前结束
也是就程序员口中的 'bug'
-
2.异常的结构
-
例子:
name # 随便在pycharm 上输入一个错误代码
Traceback (most recent call last):
File "C:/pythonProject/7.11/02.py", line 20, in <module> # ①
name
② NameError: name 'name' is not defined ③
# 上面就是一个报错异常 接下来 我们看一下异常的结构体 里面的关键信息
1.标注①句子中 有line关键字 提示哪一行代码出错了
2.标注② 冒号左边的英文提示错误类型
3.标注③ 冒号右侧提示具体错误的原因 也是修改bug 的关键
- 3.异常的类型
NameError # 变量名不存在 没有赋值 报错
IndexError # 索性超出范围 报错
KeyError # 键不存在 报错
SyntaxError # 错误使用标点符号 报错
TypeError # 类型错误 对象用来表示值的类型非预期类型时发生的错误
AttributeError # 属性错误 指引用和赋值失败时引发属性错误
-
4.异常的分类
语法错误:
不允许出现 要求一旦出现立马修改 属于原则错误
2.逻辑错误
允许出现 最好是写好代码后自己先跑一遍 出错后以及修改
异常捕获实参演练
1.当自己也不确定写的代码 是否会出现异常情况
就需要自己写下代码处理异常
2.异常捕获就是提前察觉到代码的异常 并提前判断错误的类型 给到相对应的处理措施
3.异常捕获的代码实现
3.1基本的语法结构(对症下药)
try: 可能会出错的代码(被try监控) except 变量名错误 as e: # e就是具体错误的原因 print(e) # 可以打印出来看到具体原因 对应变量名错误的解决措施 except 键错误 as e: 对应键错误的解决措施 except 标注符号错误 as e: 对应标注符号的解决措施 except 索引错误 as e: 对应索引错误2的解决措施 """ 针对具体的错误类型 用具体的解决措施 一一对应 """ # 上述一一对应去预测判断的方式 太过于繁琐 有一种便捷
3.2 万能异常
""" 上述一一对应去预测判断的方式 太过于繁琐 有一种便捷方式 万能异常处理 """ try: # 键错误 # 变量名错误 # 标注符号错误 # 索引错误 except Exception as e: # 万能异常方式一(推荐) # 对应错误的解决措施 except BaseException as e: # 万能异常方式二 # 对应错误的解决措施
4.异常捕获其他操作补充
4.1 else 与 finally try: age # try 检测代码体的错误 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 主动抛出异常 age = input('年龄输入>>>:').strip() if age == '18': raise Exception('不通过') # raise 就是关键字 主动抛出异常 只要输入设定的值就报错 else: print('不是18 可以过') """ 1. 在写代码的时候 异常捕获尽量不去用 2.被try监测的代码能尽量少就尽量少 """
异常捕获练习
1.for 循环内部的本质 # 用while 循环 加异常捕获 就可以实现for循环的功能 l1 = [1,2,3,4,5,6,7,8,9,10] res = l1.__iter__() while True: try: print(res.__next__()) except Exception as e: break 2.例题演练 username = input('username>>>:').strip() password = input('password>>>:').strip() # 2.比对(循环一一比对 有一个正确就可以) for data in data_source: # 'jason|123' 'kevin|321' 'oscar|222' real_name, real_pwd = data.split('|') # jason 123 kevin 321 ... if username = real_name and password = real_pwd: print('登录成功') break # 只要匹配正确 就应该立刻结束循环 避免资源浪费 else: print('用户名或密码错误') 结果报错: File "C:/pythonProject/7.11/02.py", line 71 if username = real_name and password = real_pwd: ^ SyntaxError: invalid syntax """ 分析是在line71行出错的 出错类型是符号错误 语法错误 找到该问题 纠正就可 if username == real_name and password == real_pwd: """
生成器对象
1.本质 生成器对象本质就是迭代器对象 迭代器是由解释器提供给我们的 我们使用时直接调用就可以了 生成器需要我们自己去定义 也就是相当于我们需要自己去制作 __iter__ __next__ 2.生成器对象目的就是为了优化代码 可以不依赖索引取值的方式去做到循环取值 主要是可以节省数据类型的内存占用空间 3.生成器对象代码实现的步骤 def func(): print('我') yield print('你') yield print('他') yield """ 当函数体代码中有yield关键字 那么函数名第一次加括号调用不会执行函数体代码 而是由普通的函数变成了迭代器对象(生成器) 用返回值去接收一下就可以调用 __iter__ __next__ """ res = func() print(res) # <generator object func at 0x01A8AB88> 已经成为生成器对象 res.__next__() # 我 res.__next__() # 你 res.__next__() # 他 """ yield可以在函数体代码中出现多次 调用__next__方法都会从上往下执行直到遇到yield代码停留在此处 后续再次调用时 直接从停留的地方 再次运行 直到调用完毕后报错 """ def func(): print('我') yield 100,200,300 print('你') yield 90 print('他') yield 80 print(res.__next__()) # 我 (100,200,300) print(res.__next__()) # 你 90 print(res.__next__()) # 他 80 """ yield后面如果有数据值 则会像return一样返回出去 如果有多个数据值逗号隔开 那么也会自动组织成元组返回 """
- 课堂练习
# 编写生成器 实现range方法的功能 # 1.可以先以两个参数的功能编写 # def my_range(start_num, end_num): # 定义好两个形参 range(10) # while start_num < end_num: # 循环打印形参 start_num < end_num # yield start_num # 返回每次循环的start_num形参 # start_num += 1 # 每次循环打印都让形start_num自增1 # # for i in my_range(1, 10): # print(i) # 2.再考虑一个参数的情况 range(10) range(0,10) # def my_range(start_num, end_num=None): # my_range(10) 如果传入一个实参 end_unm 为默认值None 如果传入两个实参 就使用传入的参数 # if not end_num: # end_num = start_num # start_num = 0 # while start_num < end_num: # yield start_num # start_num += 1 # for i in my_range(10): # print(i) # 3.最后考虑三个参数的情况 range(10) range(0,10) range(1, 10, 2) # def my_range(start_num, end_num=None, step=1): # my_range(1,10,2) 还是用默认值形参来确定本来的规律 如果传入新的实参就用传入的值 # if not end_num: # end_num = start_num # start_num = 0 # while start_num < end_num: # yield start_num # start_num += step # 正常情况下是间隔1 传入新的值 让间隔几 就加几 # for i in my_range(1, 10, 2): # print(i) # for i in my_range(1, 5): # print(i) # for i in my_range(10): # print(i)
yield其他的用法
1.示例: def index(name, food=None): print(f'{name}准备干夜宵!!!') while True: food = yield # send 给yield传值 赋值给到 food print(f'{name}正在吃{food}') res = index('小五') res.__next__() res.send('羊肉串') # 小五正在吃羊肉串 关键字send 起到传值并且自动调用__next__的方法 res.send('牛肉串') # 小五正在吃牛肉串 res.send('猪肉串') # 小五正在吃猪肉串
生成器表达式
l1 = [i*2 for i in range(10) if i >2] print(l1) # [6, 8, 10, 12, 14, 16, 18] """ 上述是列表生成式 演变一下 就可以变成生成器表达式 """ l1 = (i*2 for i in range(10) if i >2) print(l1) # <generator object <genexpr> at 0x01FDAB88> print(list(l1)) # [6, 8, 10, 12, 14, 16, 18] 用列表类型转换一下就可以看到生成器表达式里所有的数据值 """ 上述是列表生成式 把中括号换成小括号 就可以变成生成器表达式 和迭代器一样 都是为了优化内存空间 都可以类比成工厂 当你需要什么的时候 工厂可以直接造给你用 随调随用 减少能源消耗 """
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)