异常处理与生成器

异常处理

异常处理语法结构

1.基本语法结构
    try:
        待监测的代码(感觉要BBQ的代码)
    except 错误类型:  
        针对上述错误制定的方案  # 待监测的代码出错才会执行
2.查看错误的信息
    try:
        待监测的代码
    except 错误类型 as e:  # e就是系统提示的错误信息
        针对上述错误制定的方案
3.针对不同的错误类型制定不同的解决方案
    try:
        待监测的代码
    except 错误类型1 as e:
        针对错误类型1制定的方案
    except 错误类型2 as e:
        针对错误类型2制定的方案
    ...
4.万能异常  Exception\BaseException
    try:
        待监测的代码
    except Exception as e:
        针对各种常见的错误类型做统一的处理
5.结合else使用
    try:
        待监测的代码
    except Exception as e:
        针对各种常见的错误类型做统一的处理
    else:
        try的子代码正常运行结束且没有报错后执行的else子代码
6.结合finally一起使用
    try:
        待监测的代码
    except Exception as e:
        针对各种常见的错误类型做统一的处理
    else:
        try的子代码正常运行结束且没有报错后执行的else子代码
    finally:
        无论try的子代码是否报错最后都要执行finally子代码

异常处理补充

1.断言  assert 用于判断一段代码能不能正常运行
a = 1
assert isinstance(a, str)  # 判断错误,报错,错误类型 AssertionError
assert isinstance(a, int)  # 判断正确,继续执行后面的代码

2.主动抛异常  raise  需要和Exception配合使用
a = 1
if a == 1:
    raise Exception('好数字')  
else:
    print('不咋地')
# 代码本来没有问题,但是加了raise Exception后代码在运行到这里时会主动报错,错误类型为Exception

异常处理的实战应用

使用while循环+异常处理+迭代器对象完成for循环的功能
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
iter_l1 = iter(l1)
while True:
    try:
        print(next(iter_l1))
    except Exception as e:  # 这里的Exception可以换成可能会出现的错误类型
        break

生成器

生成器对象

1.本质
    其实还是内置有__iter__和__next__方法的迭代器对象
2.生成器对象和迭代器对象的区别
    迭代器对象是由解释器提供的,早就已经定义好了,直接用就行
        str\list\dict\set\tuple\文件
    生成器对象是由程序员自己编写出来的
3.创建生成器的基本语法
    函数体代码中加 yield 关键字
    def my_iter():
        print('我是生成器')
        yield
    # 此时函数体代码中有关键字 yield 那么函数名加括号调用以后并不会执行函数体代码,	而是生成一个生成器对象
    my_iter().__next__
    # 使用加括号之后的结果调用__iter__才会执行函数体代码,且每次执行完__iter__方法后代码都会停留在yield位置,下次基于该位置继续执行函数体代码
    def my_iter():
        print('我在这里')
        yield 1
        print('我跑到这里来了')
        yield 2
        print('我又跑到这里来了')
        yield 3
    '''生成器调用__iter__方法的错误写法'''
    my_iter().__next__()  # 我在这里
    my_iter().__next__()  # 我在这里
    my_iter().__next__()  # 我在这里
    # 出现这种情况是因为my_iter本身是一个函数,my_iter加括号以后本来是执行函数体代码的,但是函数体代码中有yield,my_iter()就变成了一个生成器对象,只有在调用__next__方法后才会执行函数体代码,这里使用了三个my_iter()就相当于每次都是新生成的生成器对象
   '''正确写法'''
	res = my_iter()  
    # 先把函数名加括号生成的生成器对象赋值给一个变量名,然后通过这个变量名调用__next__方法
    r1 = next(res)  # 我在这里
    print(r1)  # 1
    r2 = next(res)  # 我跑到这里来了
    print(r2)  # 2
    r3 = next(res)  # 我又跑到这里来了
    print(r3)  # 3
    # yield 还有return的功能可以返回返回值

yield冷门用法

def eat(name, food=None):
    print(f'{name}准备吃饭')
    while True:
        food = yield
        print(f'{name}想吃{food}')
        
res = eat('李小贱', '泡面')
res.__next__()  # 李小贱准备吃饭
res.__next__()  # 李小贱想吃None
# 正常情况下如果我们想执行函数体代码就像上面这样,但是执行完以后,'泡面'没有传进去,这是因为 food = yield 这种结构没办法直接传值需要改成下面这样
res = eat('李小贱')
res.__next__()
res.send('泡面')

生成器表达式

l1 = (i for i in range(10))  # 生成器对象

随堂练习

自定义生成器对标range功能
def my_range(start_range, end_range=None,step=1):
    # 4.考虑传的第三个参数为负数时
    if step > 0:  
        # 2.考虑传一个参数时
        if not end_range:
            end_range = start_range
            start_range = 1
            # 1.先考虑传两个参数时
        while start_range < end_range:
                yield start_range
                 # 3.传三个参数时只需要用step替换原本的1就可以了
                start_range += step 
    elif step < 0:
        while start_range > end_range:
            yield start_range
            start_range += step

# for i in my_range(10):
#     print(i)
# for i in my_range(1, 10):
#     print(i)
# for i in my_range(1,10,2):
#     print(i)
# for i in my_range(100, 50, -1):
#     print(i)

posted on 2023-04-05 18:36  zyg111  阅读(49)  评论(0)    收藏  举报