异常处理及生成器相关

异常常见类型

在编程语言中,异常即我们常说的报错,也叫做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

posted @ 2022-10-17 20:07  逐风若梦  阅读(50)  评论(0编辑  收藏  举报