python (异常处理,生成器)

今日内容概要

  • 异常处理语法结构
  • 异常处理实战应用
  • 生成器对象实现range方法
  • 生成器表达式
  • 生成器笔试题

异常常见类型、

在编写程序代码的过程中,不可避免的会发生一些异常报错,这时候就需要记住一些常见的异常类型,以便快速处理。

AttributeError 某个对象没有属性
Exception 通用型异常对象
FileNotFoundError 找不到文件
IOError 输入输出异常
IndexError 索引异常
KeyError 键异常
NameError 对象名称异常
SyntaxError 语法错误
TypeError 类型错误
ValueError 值错误

以上错误都属于常见错误,其中重点以 Exception 通用异常对象与 SyntaxError 语法错误为主,它们两个是最常出现的。

很多时候其实直接使用通用异常对象 Exception 就可以了,不需要记住所有的异常类型的。
image

异常处理语法结构

1.基本语法结构

	try:
		待检测的代码(可能会出错的代码)
	except 错误类型:
		针对上述错误类型制定的方案

2.查看错误的信息

	try:
		待检测的代码(可能会出错的代码)、
	except 错误类型 as e:# e就是系统提示的错误信息
		针对上述错误类型制定的方案
	

注意 except 后面异常对象使用 as 关键字起了一个别名叫做 e,然后直接输出 e 就是 Python 内置好的错误信息了。这里的 e 可以为任意名称,遵循变量命名规则即可
3.针对不同错误类型制定不同的解决方案

	try:
	待检测的代码(可能会出错的代码)
	except 错误类型1 as e:# e就是系统提示的错误信息
	except 错误类型2 as e:# e就是系统提示的错误信息
		针对上述错误类型制定的方案
	except 错误类型3 as e:# e就是系统提示的错误信息
		针对上述错误类型制定的方案
.。。。

4.万能异常 Exception/Basception

	try:
		待检测的代码(可能会出错的代码)
	except Exception as e:# e 就是系统提示的错误信息
		针对各种常见的错误类型全部统一处理

5.结合else使用

	try:
		待检测的代码(可能会出错的代码)
	except Exception as e: e 就是系统提示的错误信息
	
		针对各种常见的错误类型全部统一处理
	
	else:
		try的子代码正常运行结束没有任何的报错后,在执行else子代码

6.结合finally使用

	try:
		待检测的代码(可能会出错的代码)
	except Exception as e: e 就是系统提示的错误信息
		针对各种常见的错误类型统一处理
	else:
		try的子代码正常运行结束没有任何的报错后,再执行else子代码
	finally:
		无论try的子代码是否报错,最后都要执行finally子代码

finally 语法需要与 try 语句配合使用,无论是否有异常出现都会执行该语句内容

异常处理补充

1.断言
Python 断言,即 Python assert 语句,简单理解就是简易版的 if 语句,
用于判断某个表达式的值,结果为 True,程序运行,否则,程序停止运行,抛出 AssertionError 错误

语法格式如下所示:

assert 表达式

	name = 'jason'
	#assert isinstance(name ,int) #结果肯定是False
	assert isinstance(name,str)
	print('猜对了就是字符串,嘿嘿嘿')

2.主动抛出异常

	name = 'jason'
	if name == 'jason':
		raise Exception(‘错了就不干了,提桶跑路’)
	else:
		print(‘正常走’)

断言是为了告知 开发人员 ,你写的程序发生异常了。
如果一个潜在错误在程序编写前就能考虑到,例如程序运行时网络中断,这个场景就不需要使用断言。

断言主要为调试辅助而生,为的是程序自检,并不
是为了处理错误,程序 BUG 还是要依赖 
try… except 解决。

扩展知识
Python 断言的适用场景
进行防御性的编程
我们在使用断言的时候,应该捕捉不应该发生的非法情况。这里要注意非法情况与异常错误之间的区别,后者是必然存在的并且是一定要作出处理的。而断言后的条件不一定发生。

对假定条件做验证
断言是对程序员的假定做验证,因此这些假定的异常不一定会触发。

异常处理实战应用

1.异常处理能尽量少用就少用
2.被try检测的代码能尽量少就尽量少
3.当代码中可能会出现一些无法控制的情况报错才应该考虑使用

	eg:使用手机访问网格软件>>>断网
	编写网络爬虫程序请求数据>>>断网

生成器对象

1.本质
还是内置__iter__和__next__的迭代器对象

2.区别
迭代器对象是解释器自动提供的
数据类型\文件对象>>>:迭代器对象

生成器对象是程序员编写出来的
代码、关键字>>>:迭代器对象(生成器

Iterable(可迭代对象)、Iterator(迭代器)、genetator(生成器)关系如下

  • 可迭代对象 有 __iter__方法
  • 迭代器继承了可迭代对象,有 iternext 这两个方法
  • 生成器又继承了迭代器,有 send、close、 iternext 等方法

生成器是一种特殊的迭代器,具备迭代器所有的特性,生成器内部不存储数据,只保存生成数据的计算规则,在存储大量数据的时候,能够节约内存的开销

python中定义生成器,一共有两种方式,一种是生成器表达式,另一种是生成器函数

生成器表达式:

print((i for i in range(100)))
 
执行结果:
<generator object <genexpr> at 0x0000021698BC5510>

生成器函数:

原理概述“

生成器函数 用于创建 生成器迭代器 (generator iterator)。

与普通函数的 区别 在于:普通函数 使用 return 返回结果,而生成器函数使用yield返回结果。

使用了** yield 表达式** 的生成器函数 返回一个迭代器对象,以便于通过** for** 循环或 next() 函数等逐项获取元素/数据/值/成员 (以下统称 元素)。换言之,使用了 yield 表达式的函数就是生成器函数。

yide关键字
def my_iter():
	print(‘嘿嘿嘿,你好呀’)
	yield
	
"""
1.函数体代码中如果有yield关键字,
那么函数名加括号并不会执行函数体代码,
会生成一个生成器(迭代器对象)
"""
#res = my_iter()
"""
2.使用加括号之后的结果调用__netx__
才会执行函数体代码
"""
# res = __next__()
'''
每次执行完__next__代码都会停在yield位置
继续往下找第二个yield
'''

def my_iter():
        print('哈哈哈 椰子汁很好喝')
        yield 111, 222, 333
        print('呵呵呵 从小喝到大')
        yield 111, 222, 333
        print('嘿嘿嘿 特种兵牌还可以')
	 yield 111, 222, 333
	 
 res = my_iter()
 r1 = res__next__()
 print(r1)
 r2 = res.__next__()
 print(r2)
 r3 = res.__next__()
 print(r3)

4.yield还有点类似于return,可以返回返回值

例子:

自定义生成器对标range功能(一个参数,两个参数,三个参数  迭代器对象)

for i in range(1,10):
	print(i)
	

1.先写两个参数的
2.在写一个参数
3.最后在写三个参数

1.生成器
两个参数
def my_range(star_num,end_num=None,step=1):
# 判断end_num是否有值,没有直说名用户只给一个值,起始位置数字应该是0,终止位置应该是传的值
    if not end :
        end = start
        start = 0
    while start < end :
        yield start
        start += step
# 方法1:
# for i in my_range(1,10):
#     print(i)
# 方法2:
res = my_range(1,10).__iter__()
while True:
    try:
        i = res.__next__()
        print(i)
    except StopIteration:
        break
# 传三个参数:
for i in my_range(1,10,2):
    print(i)

yield冷门用法

def eat(name,food=None):
	print(f'{name}准备吃饭')
	while True:
		food = yield
		print(f'{name}在吃{food}')

res = eat(‘Jason’)
res.__next__()

res.send('汉堡') 
1.将括号内的数据传给yield前面的变量名
2.在自动调用__next__
res.send('包子')

优点:
生成器对内存使用的提升
生成器的缺点:

  只能依次往下迭代一轮,而不能回退和重复迭代。

生成器表达式、

说白了就是生成器的简化写法
 l1 = [i**2 for i in range(10)]
print(l1)

结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81][0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

l1 = (i ** 2 for i in range(100))  # 生成器对象
print(l1)

结果:
<generator object <genexpr> at 0x000001C63562DF20>
for i in l1:
	print(i)
结果:
0
1
4
9
16
25
36
49
64
81

迭代器和生成器的区别

生成器属于迭代器的一种

1、迭代器类型是Iterator类型,生成器是Generator类型。
2、生成器内部不存储数据,只保存生成数据的计算规则
3、生成器比迭代器多了3个方法
send方法:在生成数据的同时,可以和生成器内部进行数据交互
close: 生成可以调用close方法进行关闭
throw: 可以在生成器内部上一次暂停的yield处引发一个指定的异常类型。生成器内部可以通过捕获的异常类型来做不同的处理

题:

 使用while循环+异常处理+迭代器对象 完成for循环迭代取值的功能
 	 l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99]
    # 1.先将列表调用__iter__转变成迭代器对象
    iter_l1 = l1.__iter__()
    # 2.while循环让迭代器对象反复执行__next__
    while True:
        try:
            print(iter_l1.__next__())
        except StopIteration as e:
            break

面试题:


面试题(有难度)
	大致知道流程即可
	
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 17:34  亓官扶苏  阅读(344)  评论(0)    收藏  举报