异常捕获,迭代器对象,yield其他用法
-
异常捕获
-
自定义迭代器对象(生成器)
-
生成器表达式
-
yield其他用法
1.异常捕获
1.1概念:
当我们在敲代码时,无论什么语言都会有一些错误诞生,这些错误用有语法错误,书写错误等,在程序员眼中他们有一个统一的名词叫做‘bug’.
在我们运行程序的时候你所使用的软件会告诉你错误的点和位置导致我们的代码会停留在这个位置而不会进行下去。今天所学习的异常捕获就是通过人为的方式在你认为会产生错误的地方通过异常捕获使代码可以继续往下走,同时将错误也体现在结果中。
1.2异常错误的提示:
下面的这一串代码是我们在平时运行程序的时候多多少少会遇到的错误提示:
eg:
这段代码运行后错误提示: Traceback (most recent call last): File "D:/pythonProject/day21/02 异常的结构.py", line 1, in <module> name NameError: name 'name' is not defined 同时,这段代码是很重要的,他能告诉我们一些错误的信息: line 1 提示错误产生在1行 NameError 错误的类型(当前是名子异常) name 'name' is not defined 具体的错误(当前是name变量名未被定义,这里也是改bug的关键!!!!!!)
1.3异常的类型
异常的类型有很多,下面是一部分异常的类型
1.4异常的分类
异常的分类分为两类:语法错误(这个错误是最不能出现的而且是最低级的错误,一定要杜绝!!!)
逻辑错误(这个错误不像语法错误那样严重,可以错但是也要及时修改。)
1.5 异常捕获实参演练
平时,当我们在敲代码的时候想要知道这段代码究竟在运行的时候会不会报错,这时候就需要异常捕获,他的作用就是提前预测一下代码在运行时可能出现的问题并且给出处理措施。此时就要用到下面的关键字进行操作: try except as else finally assert else finally
1.5.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:后用except 错误类型 as e:然后对应错误类型1的解决措施(比如说print(e)),可以叠加多个错误(错误的个数基于你认为有多少个错误)
1.5.2..异常捕获其他操作补充
else与finally
try: name except Exception as e: print('你出错了 你个小垃圾') else: print('try监测的代码没有出错的情况下正常运行结束 则会执行else子代码') finally: print('try监测的代码无论有没有出错 最后都会执行finally子代码')
else和finally二者的作用都是基于try检测的代码执行之后
else的作用是如果try检测的代码没有出错然后执行else的子代码
finally的作用是无论try监测的代码有没有出错都会执行finally子代码
1.5.3断言 assert
断言是在你不知道得到的数据的类型是那种的情况下,你先猜测一下,通过assert关键字进行操作,判断一下你猜的数据类型是否和当前数据的数据类型是否匹配,
如果不对则直接报错 对则正常执行下面的代码
name = 'jason' # 通过一系列的手段获取来的数据 assert isinstance(name, list) # 断言括号内左侧的类型是否和右侧你判断的类型是否一致 如果不对则直接报错 对则正常执行下面的代码 print('针对name数据使用列表相关的操作')
下面是一些断言判断语句
1.5.4.主动抛异常 raise
有时,程序需要主动抛出异常,因为某些情况下,你需要反馈消息给更上层的调用者,告诉它有一些异常情况发生,而你抛出异常的地方,没有能力处理它,因此需要向上抛出异常。
这种情况为什么不让系统自己抛出异常呢?一个原因是上层的调用者本身就希望能够捕获有别于系统异常的自定义异常,二来,有些情况下,程序的逻辑是没有异常的,但是,从业务角度考虑,的确是一个不寻常的情况,因此需要我们主动抛出异常。
name = input('username>>>:').strip() if name == 'jason': # raise NameError('jason来了 快跑!!!') raise Exception('反正就是不能过') #抛出异常时,你可以指定抛出哪个异常,如果你不想指定,那么可以抛出异常Exception, 它是所有异常的父类 else: print('不是jason 那没事了')
1.5.5.强调
1.异常捕获能尽量少用就尽量少用
2.被try监测的代码能尽量少就尽量少
2.1.概念:
生成器对象的本质就是迭代器对象
2.2.生成器对象和迭代器对象二者的异同:
异:
前者是我们自己定义出来的,后者是解释器为我们提供现成的
同:
他们都有__iter__和__next__方法
都是用来节省空间储存的
2.3.生成器对象的作用
1.可以优化代码
2.一种不依赖索引取值的通用方式
3.可以节省数据类型的内存占用空间(主要作用)
2.4 生成器对象流程 yield
当函数体代码中含有yield关键字时,第一次调用并不会执行函数体代码,而是将函数器变成了生成器(拥有__iter__和__next__方法)
def index(): print('你还记得我吗?') yield 123 yield 123, 111 print('是不是忘记我了!!!') yield 666
同时yield 后面还可以有返回值(可以是多个返回值)
如果函数体代码中含有多个yield关键字 执行一次__next__返回后面的值并且让代码停留在yield位置 ,再次执行__next__基于上次的位置继续往后执行到下一个yield关键字处
如果没有了 再执行也会报错 StopIteration
没有调用之前 index函数只是一个普通函数
print(index) # <function index at 0x00000265AAC060D0>
加括号调用并接受结果: 不执行代码 而是变成了生成器对象(迭代器)
res = index() print(res) # <generator object index at 0x000001B792F5E150>
变成生成器之后调用__next__就会开始执行函数体代码
print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) # 你还记得我吗? # 123 # (123, 111) # 是不是忘记我了!!! # 666
变量名 = (操作1 for i in range(x) if 操作2)
# l1 = [i**2 for i in range(10) if i > 3] # print(l1) #[16, 25, 36, 49, 64, 81] # l1 = (i**2 for i in range(10) if i > 3) # print(l1) # <generator object <genexpr> at 0x000001A793439C10>
上述代码第一个 l1 = [i**2 for i in range(10) if i > 3]是列表生成式得到列表[16, 25, 36, 49, 64, 81]
第二个 l1 = (i**2 for i in range(10) if i > 3)才是生成器生成式得到<generator object <genexpr> at 0x000001A793439C10>
二者就差了一个小括号和中括号的区别
4.yield其他用法
1.类似return 返回值(可以返回多个值)
2.生成器开始运行,直到遇到第一个yield,输出返回值并中断生成器的运行。在后续使用next()时,代码会从上一次中断的地方继续运行
3. yield 和 send
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__方法
在这段代码中,food = yield,而send()中的参数可以直接传给yield使得 food = yield = ‘生蚝’,如果没有send()则是food = yield = none
而且send可以传值并自动调用__next__方法