生成器/异常处理

今日内容概要

  • 异常常见类型/异常处理语法结构
  • 异常处理实战应用
  • 生成器对象/生成器底层原理
  • yield冷门用法
  • 生成器表达式及笔试题

1.异常处理语法结构

1.常见报错信息:
SyntaxError  # 语法错误
NameError  # 变量名错误
TypeError  # 数据类型错误
IndentationError  # 缩进错误
AttributeError:  # 属性错误  特性引用和赋值失败时会引发属性错误。
IndexError:  # 索引错误  
KeyError:  # 关键字错误
TabError:  # Tab错误

img

以上是错误类型的具体图片解释

1.基本异常处理语法结构:

我们通过关键字 try
try:
    待检测的代码(可能会报错的代码)
except 错误类型:
	针对上述错误类型做出的处理(提前制定好方案)
    
2.查看错误信息:
try:
    待检测的代码
except 错误类型 as e:  # e为错误类型接收的变量名, 我们print(e) 就可以知道它是什么错误类型
    针对上述错误类型做出的处理(提前制定好方案)

3.针对不同的错误信息使用不同的解决方案
try:
    待检测的代码
except 错误类型 as e:
	针对上述错误类型做出的处理(提前制定好方案)
except 错误类型1 as e1:
	针对上述错误类型做出的处理(提前制定好方案)
except 错误类型2 as e2:
	针对上述错误类型做出的处理(提前制定好方案)

4.万能异常处理:
try:
    待检测的代码
except Exception as e:
    针对各种常见的错误类型全部统一处理
    
5.try 也可以 与 else 结合使用
try:
    待检测的代码
except 错误类型 as e:
    针对上述类型错误做出的处理
else:
	如果 try 的子代码正常结束运行没有被 except 检测 那么就会走 else 下面的子代码
6.finally
try:
    待检测的代码
except 错误类型 as e:
    针对上述类型错误做出的处理
else:
    try的子代码正常结束运行没有走 except 那么就会走 else 下面的子代码
finally:
    无论 try 走 except 或 else 下面的子代码, 只要有 finally 就都会走 finally 下面的子代码
    
    
    
#  异常处理补充:
1. 断言
name = 'xiaoming'
# assert isinstance(name, int)  
assert isinstance(name, str)  # 判断name绑定的数据值'小明' 是否是字符串类型
print('是的他就是字符串')  # 如果是字符串类型的话就会往下走,否则就会报错
assert 就是断定 后面的代码没问题,如果有问题报错,没问题继续往下执行

2.主动抛异常(自定义BUG)
a = 'name'
if a == 'name'
	raise Exception('呵呵')  # 自定义错误类型 
else:
    print('啥事没有')

异常处理实战

1.异常处理能少用就少用
2.被 try 检测的代码不要太多,尽量减少
3.当代码中可能出现一些无法控制的报错情况才会使用异常处理解决
	比如 # 编写网络爬虫程序请求数据 断网
4.小小练习题:
我们现在也了解了异常处理方式,迭代器对象的概念,可迭代对象如果转换迭代器
那么我们可以思考一下 如何用 while 循环 + 异常处理 +迭代器对象 实现for循环的功能
l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99]
# 1.我们先把l1 变成迭代器对象
l1_iter = l1.__iter__()
# 2. 我们通过 while循环 加上 __next__方法进行取值
while True:
    try:
        print(l1_iter.__next__())  # 打印 l1_iter.__next__() 从迭代器中取出的值
    except StopIteration as e:  # 捕获StopIteration错误类型 并结束循环
        break

2.生成器对象

1.本质
	生成器还是内置含有__iter__(),__next__()的迭代器对象
2.区别
	迭代器对象是解释器自动提供的  # 可迭代的数据类型有__iter__() 
    
    生成器对象是我们自己根据需求自定义创建的  # 代码(函数)、关键字
    
3.创建生成器的基本语法
	函数体代码中填写 yield 关键字
    def my_func():
        print('good morning')
        yield
    如果定义函数内部的函数体代码有 yield 关键字 那么它的调用 函数名加() 不会立刻执行函数体代码
    而是生成一个生成器对象(迭代器对象)
    res = my_func() 
    print(res)  # <generator object my_func at 0x000002304010CC80>  生成器的内存地址
    res.__next__()  # 运行函数体代码 并且到 yield 关键字时结束, 下一次使用__next__()方法时从yield开始 并寻找下一个 yield 关键字
    
def user_iter():
    print('用户1')
    yield 'pwd:123'
    print('用户2')
    yield 'pwd:321'
    print('用户3')
    yield 'pwd:222'
    print('用户4')
    yield 'pwd:999'

data = user_iter()  # 生成器对象 为 data
# print(data)
b = data.__next__() # 先调用.__next__()方法并且获得yield关键字的返回值赋值给b
print(b)
c = data.__next__() # 先调用.__next__()方法并且获得yield关键字的返回值赋值给c
print(c)
d = data.__next__() # 先调用.__next__()方法并且获得yield关键字的返回值赋值给d
print(d)
e = data.__next__() # 先调用.__next__()方法并且获得yield关键字的返回值赋值给e
print(e)

4.小小练习题:
自定义生成器对标range功能(一个参数 两个参数 三个参数 迭代器对象)
def my_range_func(start_num, end_num=None, step=1):
    if step > 0:
        if not end_num:
            end_num = start_num  # 这是当参数为1的时候 让end_num有赋值
            start_num = 0  # 然后把0赋值给start_num  因为range是前取后不取
        while start_num < end_num:  # 当start_num <end_num 时结束循环
            yield start_num  # 直接通过关键字yield返回 start_num
            start_num += step  # 每次取值都让start_num + step

    elif step == 0:
        raise ValueError('range() arg 3 must not be zero')  # 抛出BUG
    else:
        if start_num < end_num:  # 如果参数1 小于 参数2
            while start_num < end_num:  # 循环条件也是如此
                yield end_num  # 返回 参数2
                end_num += step  # 参数2 - 步长的绝对值
        elif start_num > end_num:  # 如果 参数1 大于 参数 2
            while start_num > end_num:  # 循环条件也是如此
                yield start_num  # 返回 参数1
                start_num -= abs(step)  # 参数1 - 步长的绝对值


for i in my_range_func(0,100,0):
    print(i)

print(my_range_func(10, 50, 0).__next__())

3.yield冷门用法

# 生成器也是可迭代对象我们也可以通过for 循环依次进入生成器中取值
def data(user, skills=None):
    print(f'{user}准备使用')
    while True:
        skills = yield  # 通过sned方法获取数据 并赋值给前面的skills变量名
        print(f'{user}正在使用{skills}')

res = data('tom')
res.__next__()
res.send('R:天崩地裂')  # send() 将括号内的数据传给yield前面的变量名 2.再自动调用__next__
res.send('W:横扫千军')

4.生成器表达式

我们只需要把列表生成式外面的[] 换成 元组的()
就可以了 
这样我们就得到了一个生成器
并且它是可迭代对象,支持被for循环遍历
res = (i+i for i in range(20))res = (i*i for i in range(20))
for i in res:
    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]

posted @ 2022-10-17 16:41  dd随风  阅读(40)  评论(0编辑  收藏  举报