Python学习之异常及异常处理

异常

  • Python 异常可以分为两种:语法错误逻辑错误

    ps:语法错误不允许出现,立即更改!!

  • 语法正确的情况下,运行期检测到的错误被称为异常。

  • 大多数的异常都不会被程序处理,都以错误信息的形式展现

  • 程序在运行的过程中如果出现了异常会导致整个程序的结束

  • 异常就是程序员口中的“bug”

异常的错误信息

异常以不同的类型出现,这些类型都作为信息的一部分打印出来

错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息。

语法错误信息

>>> while True print('Hello world')
  File "<stdin>", line 1  				# 语法分析器指出了出错的一行
    while True print('Hello world')		 # 在最先找到的错误的位置标记了一个小小的箭头
               ^
SyntaxError: invalid syntax
    
 ''' 
 函数 print() 被检查到有错误,是它前面缺少了一个冒号 : 。
 语法分析器指出了出错的一行,并且在最先找到的错误的位置标记了一个小小的箭头。
 '''

逻辑错误信息

>>> name * 3
Traceback (most recent call last):		    # 错误信息提示头:回溯
  File "<stdin>", line 1, in <module>		# 精准提示你哪一行代码出错
NameError: name 'name' is not defined		# 异常的类型,具体原因
  1. 关键字 line 所在行

    精准提示你哪一行代码出错

  2. 错误信息最后一行 冒号左侧

    是异常的类型

  3. 错误信息最后一行 冒号右侧

    异常的具体原因(也是改bug的关键)

常见的异常类型

  • NameError

    未声明/初始化对象 (没有属性)

  • IndexError

    序列中没有没有此索引(index)【越界】

  • KeyError

    映射中没有这个键

  • SyntaxError

    Python 语法错误

  • TypeError

    对类型无效的操作

>>> l1 = [1, 2, 3, 4, 5]
>>> l1[6]				# 索引超出范围,触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
    
>>> '2' + 2				# int 不能与 str 相加,触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

异常捕获

  • 当代码不确定什么时候会报错的情况下可以通过代码处理异常

  • 异常捕获的使用相当于是提前预测可能出现的问题并提前给出处理的措施

  • 异常捕获可以使用 try/except 语句:

    • 一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。

    • 处理程序将只针对对应的 try 子句中的异常进行处理,而不是其他的 try 的处理程序中的异常。

基本语法结构(针对性很强)

# 基本语法结构(针对性很强)
try:
    可能会出错的代码(被try监控)
except 错误类型1 as e:  # e就是具体错误的原因
    对应错误类型1的解决措施
except 错误类型2 as e:  # e就是具体错误的原因
    对应错误类型2的解决措施
except 错误类型3 as e:  # e就是具体错误的原因
    对应错误类型3的解决措施
except 错误类型4 as e:  # e就是具体错误的原因
    对应错误类型4的解决措施
  1. 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)。
  2. 如果没有异常发生,忽略 except 子句,try 子句执行后结束。
  3. 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。
  4. 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。

一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组

except (RuntimeError, TypeError, NameError):
    pass

万能异常(笼统的处理方式)

try:
    # name
    # d = {'name':'jason'}
    # d['pwd']
    123 + 'hello'
except Exception as e:  # 万能异常方式1
    print(e)
except BaseException as e:  # 万能异常方式2
    print(e)
    
'''
Exception:常规错误的基类
BaseException:所有异常的基类
'''

try/except...else...finally

  • else 子句将在 try 子句没有发生任何异常的时候执行。
  • finally 语句无论是否发生异常都将执行最后的代码。
try:
    name
except Exception as e:
    print('你出错了')
else:
    print('try监测的代码没有出错的情况下正常运行结束 则会执行else子代码')
finally:
    print('try监测的代码无论有没有出错 最后都会执行finally子代码')

断言 assert

Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。

name = 'kwan'  # 通过一系列的手段获取来的数据
assert isinstance(name, list)  # 断言数据属于什么类型 如果不对则直接报错 对则正常执行下面的代码
print('针对name数据使用列表相关的操作')

主动抛异常

Python 使用 raise 语句抛出一个指定的异常。

>>> x = 10
>>> if x > 5:
...     raise Exception('x 不能大于 5。x 的值为: {}'.format(x))  # 满足条件抛出异常
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception: x 不能大于 5。x 的值为: 10

raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

如果你只想知道这是否抛出了一个异常,并不想去处理它,那么一个简单的 raise 语句就可以再次把它抛出。

>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print('An exception flew by!')
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: HiThere

生成器

  • 在 Python 中,使用了 yield 的函数被称为生成器(generator)。
  • 生成器是一个返回迭代器的函数,只能用于迭代操作。
  • 生成器内置有__iter____next__方法,所以生成器本身就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

# 若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象 
>>> def my_range(start, stop, step=1):
...     print('start...')
...     while start < stop:
...         yield start
...         start += step
...     print('end...')
...
>>> res = my_range(0, 3)
>>> res
<generator object my_range at 0x00000268F0FA6580>

# 
>>> res.__iter__
<method-wrapper '__iter__' of generator object at 0x00000268F0FA6580>
>>> res.__next__
<method-wrapper '__next__' of generator object at 0x00000268F0FA6580>

>>> next(res) # 触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
start...
0
>>> next(res) # 再次调用next(res),函数从上次暂停的位置继续执行,直到重新遇到yield...
1
>>> next(res) # 周而复始...
2
>>> next(res) # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代
end...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

生成器对象代码实现

  • 当函数体代码中有yield关键字,那么函数名第一次加括号调用不会执行函数体代码,而是由普通的函数变成了迭代器对象(生成器)返回值
>>> def index():
...     print('啊吧啊吧')
...     yield
...
>>> print(index)
<function index at 0x00000268F1214310>
>>> res = index()
>>> print(res)
<generator object index at 0x00000268F0FA6740>
  • yield可以在函数体代码中出现多次,每次调用__ next__方法都会从上往下执行直到遇到yield代码停留在此处
>>> def index():
...     print('啊吧啊吧')
...     yield 1
...     print('嘀嘀嘀')
...     yield 2
...     print('嘿嘿嘿')
...     yield 3
...
>>> index
<function index at 0x00000268F0E5E040>
>>> res = index()
>>> print(res)
<generator object index at 0x00000268F0FA6580>
>>> res.__next__()
啊吧啊吧
1
>>> res.__next__()
嘀嘀嘀
2
>>> res.__next__()
嘿嘿嘿
3
  • yield后面如果有数据值,则会像return一样返回回去,如果有多个数据值逗号隔开,那么也会自动组织成元组返回
>>> def index():
...     print('啊吧啊吧')
...     yield 1, 2, 3
...     print('嘀嘀嘀')
...     yield 2
...     print('嘿嘿嘿')
...     yield 3
...
>>> index
<function index at 0x00000268F1214310>
>>> res = index()
>>> res
<generator object index at 0x00000268F0FA6740>
>>>
>>> res.__next__()
啊吧啊吧
(1, 2, 3)
>>> res.__next__()
嘀嘀嘀
2
>>> res.__next__()
嘿嘿嘿
3

生成器练习题

# 编写生成器,实现range方法的功能
# 1.先以两个参数的功能编写
def my_range(start_num, end_num):
    while start_num < end_num:
        yield start_num
        start_num += 1

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

# 2.再考虑一个参数的情况
def my_range(start_num, end_num=None):
    if not end_num:
        end_num = start_num
        start_num = 0
    while start_num < end_num:
        yield start_num
        start_num += 1
for i in my_range(100):
    print(i)

# 3.最后考虑三个参数的情况
def my_range(start_num, end_num=None, step=1):
    if step < 1:
        step = 1
    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, 2):
    print(i)
for i in my_range(1, 5):
    print(i)
for i in my_range(10):
    print(i)

yield其他用法

>>> def index(name, food=None):
...     print(f'{name}准备干午饭!!!')
...     while True:
...         food = yield  # 在函数内可以采用表达式形式的yield
...         print(f'{name}正在吃{food}')
...

# 可以拿到函数的生成器对象持续为函数体send值
>>> res = index('kwan')  # 得到生成器对象
>>> res
<generator object index at 0x00000268F0FA6740>
>>> res.__next__()  # 需要事先“初始化”一次,让函数挂起在food=yield,等待调用res.send()方法为其传值
kwan准备干午饭!!!
>>> res.send('辣子鸡')  # 传值并自动调用__next__方法
kwan正在吃辣子鸡
>>> res.send('糖醋排骨')  # 传值并自动调用__next__方法
kwan正在吃糖醋排骨
>>> res.send('清蒸鱼')  # 传值并自动调用__next__方法
kwan正在吃清蒸鱼

生成器表达式

创建一个生成器对象有两种方式,一种是调用带yield关键字的函数,另一种就是生成器表达式,与列表生成式的语法格式相同,只需要将[]换成(),即:

(表达式 for i in 可迭代对象 if 条件)

生成器表达式返回的是一个生成器对象,其优点是节省内存(一次只产生一个值在内存中)

>>> [x*x for x in range(3)]
[0, 1, 4]
>>> res = (x*x for x in range(3))
>>> res
<generator object <genexpr> at 0x101be0ba0>

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g) # 抛出异常StopIteration

题(掌握技巧)

>>> 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)
>>> res
[20, 21, 22, 23]
posted @ 2022-07-12 18:54  梦想有双休  阅读(146)  评论(0)    收藏  举报