异常处理 生成器
目录
异常处理前戏
异常处理就类似于诸葛亮神机妙算。程序员在程序运行之前已经知道了程序哪里可能会有问题,于是就提前做一些预防措施。
附上丞相美图:
1.异常
异常就是代码运行报错 行业俗语叫bug
代码运行中一旦遇到异常会直接结束整个程序的运行 我们在编写代码的过程中药尽可能避免
2.异常分类
语法错误
不允许出现 一旦出现立刻改正
逻辑错误
允许出现的 因为它一眼发现不了 代码运行之后才可能会出现
3.异常结构
错误位置
错误类型
如何看pycharm报错
def func(): # 调用次数过多报错
func()
func()
# 点击红色箭头指向的蓝色路径 可以跳转到错误的位置
# 红色框内是python解释器为你提供的 报错原因
# 上面这么多蓝色路径 是由于你代码引发的python解释器内部的一些错误
异常常见类型
简单了解即可,不必全部记住:
SyntaxError # 语法错误
NameError # 命名错误
IndexError #索引错误
KeyError # 键错误
IndentationError # 缩进错误
异常处理语法结构
try...excpet
try:
待监测的代码(可能会出错的代码)
except 错误类型:
针对上述错误类型制定的方案
# 1.基本使用
try:
name
except NameError:
print('发现NameError') # 发现NameError
# 2.查看错误信息
try:
name
except NameError as massage:
print(massage) # name 'name' is not defined
print('发现NameError') # 发现NameError
# ps: except子句 可以在异常名称后面指定一个变量。 这个变量会绑定到一个异常实例并将参数存储在 instance.args中。
# 3.多个excpet连续使用 只会有一个excpet执行
userdata = [1, 2, 3, 4]
try:
userdata[100] # indexerror
name # nameerror
except NameError as massage:
print(massage)
print('发现NameError')
except IndexError as massage:
print(massage)
print('发现IndexError')
# 最终结果
# list index out of range
# 发现IndexError
万能异常 Exception/BaseException
try:
待监测的代码(可能会出错的代码)
except Exception as e: # e就是系统提示的错误信息
针对各种常见的错误类型全部统一处理
# 1.基本使用
try:
name
except Exception as massage:
print(massage) # name 'name' is not defined
print('我是万能异常 我很牛逼')
try:
name
except BaseException as massage:
print(massage) # name 'name' is not defined
print('我是上面那家伙他爸爸 ')
Exception/BaseException区别
区别: BaseException 是 Exception 的父类,作为子类的Exception无法截获父类BaseException类型的错误
BaseException: 包含所有built-in exceptions
Exception: 不包含所有的built-in exceptions,只包含built-in, non-system-exiting exceptions,像SystemExit类型的exception就不包含在里面。
# Python所有的错误都是从BaseException类派生的,常见的错误类型和继承关系看这里:
# https://docs.python.org/3/library/exceptions.html#exception-hierarchy
作者:https://www.cnblogs.com/-chenxs/p/11206678.html
try...except...else
# 在被try检测的代码没有异常时 else下的子代码执行 类似于for...else、while...else
try:
print('miku') # 没有异常
except BaseException:
print('这里不执行')
else:
print('hello') # 没有异常时执行 有异常不执行
try..except..else..finally
# finally语句下的子代码 无论是否抛出异常 都会执行
try:
name
except BaseException:
print('有异常')
else:
print('有异常时不执行')
finally:
print('不报错会执行= =')
print('报错也执行=。=')
异常处理练习
# 使用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
断言 assert
# 1.断言--使用assert 类似于做出预言
name = 'alice'
assert isinstance(name, int) # name是int类型 我说的!耶稣都拦不住
# 上面断言失败 会抛出异常 AssertionError
# 2.断言成功
name = 'alice'
assert isinstance(name, str)
print('hello') # 程序继续运行 什么也没发生
主动抛出异常 raise
raise 语句支持强制触发指定的异常
raise 唯一的参数就是要触发的异常。这个参数必须是异常实例或异常类(派生自 Exception 类)
# 1.例子
name = '星期六上班'
if name == '星期六上班':
raise Exception('老子不干了') # 触发Exception异常
else:
print('无事发生')
异常处理何时使用
1.异常处理能尽量少用就少用
2.被try监测的代码能尽量少就尽量少
3.当代码中可能会出现一些无法控制的情况报错才应该考虑使用
eg: 使用手机访问网络软件 断网
编写网络爬虫程序请求数据 断网
更多推荐阅读:https://segmentfault.com/a/1190000007736783
生成器
概念
1.本质
还是内置有__iter__和__next__的迭代器对象
2.区别
迭代器对象是解释器自动提供的
数据类型\文件对象>>>:迭代器对象
生成器对象是程序员编写出来的
代码、关键字>>>:迭代器对象(生成器)
创建生成器 yield关键字
def func(): # 一个函数
print('hello')
def gen_obj():
print('hello')
yield # 在函数中添加yield关键字
res = gen_obj() # 调用函数 不会执行函数体代码 而是会产生生成器对象
print(type(res)) # <class 'generator'>
yield返回值
def gen_obj():
print('hello')
yield 111, 222, 333
print('tell')
yield 111, 222, 333
print('me')
yield 111, 222, 333
print(type(gen_obj)) # <class 'function'>
res = gen_obj() # 调用gen_obj 变成一个生成器对象
print(type(res)) # <class 'generator'>
r1 = res.__next__() # 运行到第一个yield停止 r1接受yield的返回值 # hello
print(r1) # (111, 222, 333)
r2 = res.__next__() # 运行到第二个yield停止 r1接受yield的返回值
print(r2)
r3 = res.__next__()
res.__next__() # 报错 # StopIteration
用yield传值(了解)
def eat(name, food=None):
print(f'{name}准备用餐')
while True:
food = yield
print(f'{name}正在吃{food}')
res = eat('alice') # 产生生成器
res.__next__() # alice准备用餐
res.__next__() # alice正在吃None
res.__next__() # alice正在吃None
send
def eat(name, food=None):
print(f'{name}准备用餐')
while True:
food = yield
print(f'{name}正在吃{food}')
res = eat('alice') # 产生生成器
res.send(None) # TypeError: can't send non-None value to a just-started generator
res.send('汉堡') # 1.将括号内的数据传给yield前面的变量名 2.再自动调用__next__
res.send('包子')
res.send('面条')
# 总结
# 在使用send方法前 必须下列二者选其一
res.__next__()
res.send(None)
生成式表达式
# 是创造生成器的简便写法!
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]
'''不用深入研究 大致知道起始数即可'''
练习题
生成器
# 1.
提示:
1、同一个生成器中的数据只能取一次,取完就没有了
2、生成器的特点惰性运算:不找生成器取值,它就根本不工作
def func():
for i in range(4):
yield i
g = func()
g1 = (i for i in g)
g2 = (i for i in g1)
print(list(g1)) # [0,1,2,3]
print(list(g2)) # []
如果将print(list(g1))注释掉打印出的结果是什么?
# 2.
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循环n=10时,相当于g = (add(n, i) for i in (add(n, i) for i in g))
print(list(g)) # 现在生成器才运行
# 向最内层的g要值 # g=test() 要出来了啥 要出来了0,1,2,3
# 然后此时n=10 # add(10,0) add(10,1) add(10,2) add(10,3) 所以[10,11,12,13]
# 相当于 (add(n, i) for i in [10,11,12,13]) # 是不是一下变简单了
# 结果是 [20,21,22,23]
# 3.进阶 (cpu烧了)
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g=test()
for n in [1,10,5]:
g=(add(n,i) for i in g)
print(list(g)) # [15,16,17,18]
感谢作者!!https://blog.csdn.net/weixin_64471900/article/details/124988686
异常处理
# 1.编写程序,在指定文件路径读方式打开指定文件名,要求如果文件不存在提示异常错误并且创建新的同名文件。
user_input = input('请输入文件名:').strip()
try:
with open(f'{user_input}.txt', 'r', encoding='utf8') as f:
print(f.read())
except FileNotFoundError:
with open(f'{user_input}.txt', 'w', encoding='utf8') as f:
pass
生成器模拟range方法
def my_range(start_num, end_num=None, step=1):
# 判断end_num是否有值 没有值说明用户只给了一个值 起始数字应该是0 终止位置应该是传的值
if not end_num:
end_num = start_num
start_num = 0
while start_num < end_num:
yield start_num
start_num += step
for i in my_range(1,100,10):
print(i)