02-python 基础语法知识-04-异常与错误处理

本文深入探讨Python中的异常处理机制,包括异常的概念、如何捕获和处理异常、自定义异常的创建,以及如何利用异常进行流程控制。文章还介绍了如何安装和查看第三方包自定义的异常,帮助读者更好地理解和应对程序中的错误。
摘要由CSDN通过智能技术生成

02-python 基础语法知识-04-异常与错误处理

总体 要讲的大纲内容 如下

  • 循环- for while
  • 流程中断 continue break
  • 逻辑操作 and ,or ,not
  • 函数
  • 常用内置函数 (todo)
  • python中的异常与错误处理

​ 今天我们开始学习一个比较关键的一个概念叫 异常。 你可能没有接触 过什么 叫异常,它是所有 编程语言都比不可少的话题。 程序 有时候 并没有 我们想象的那么强大,有时候 也会出现 一些报错,导致程序 停止了。 这些报错 我们 就称为 程序抛出了一个异常。

什么是异常

简单理解 就是 我们说在代码执行的时候 发生了错误,这个时候就产生了一个异常, 程序就被迫中断了。

不知道 我们之前介绍的 一些基础的数据类型,我会说 某某方法 如果怎么样,就报错 IndexError 。 这些 报错 就是异常,当异常发生的时候,如果我们采取任何 措施,程序将会停止运行。

举几个 常见的报错的例子

>>> nums = ["one", "two", 'three', 'four', 'five']
>>> nums[10]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
IndexError: list index out of range

IndexError 这里 就是一个异常,这个异常发生在 访问了一个 不存在的位置,python 解释器不知道如何做,所以就报错了。

>>> 'frank'.index('name')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: substring not found

ValueError 是一个异常, 这里想在 在字符串中寻找 一个子串name 发现没有找到,也报错了。这里就是抛出了一个异常 .

>>> 1/0
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ZeroDivisionError: division by zero

ZeroDivisionError 这里也报错了, 这里很明显 1 是不能除以0 的,这个一般人都知道。 所以 你强行 给计算机算这个值,python解释器 也没有办法算出来。 这个 也会抛出异常。 ZeroDivisionError

>>> 
... person = {
...     'weight': 60,
...     'name': 'frank',
...     'height': 165,
...     'hobby': 'swimming'
... }
>>> 
>>> person['aaa']
Traceback (most recent call last):
  File "<input>", line 1, in <module>
KeyError: 'aaa'

在字典中 强行访问不存在的key , 这个时候 python 解释器 也不知道如何处理 ,然后也报错了。 这次报错叫 KeyError .

上面的这些例子 都叫异常。而且 不同的报错 有不同的异常。 也就是异常是可以分类的。就是 有不同类型的异常,通过 不同的异常,我们可以大概知道 程序发生了什么问题。 方便 我们 更好的排查问题。

异常发生时会发生什么?

当异常发生的 时候, 程序 就会中断,之后 的代码 将不会执行。

def operate():
    print("operate begin")

    nums = ["one", "two", 'three', 'four', 'five']
    print(nums[1])

    # 这里要发生异常
    print(nums[10])

    print(nums[-1])  #1
    print("operate end") #2


operate()

运行这段代码 看看 会发生什么, 这个函数一开始先 打印 index=1 元素, 然后打印index=10的元素,显然不存在, 之后打印 最后一个元素。 最后输出 一句话 代表函数结束了。

来看下运行结果

operate begin
two
Traceback (most recent call last):
  File "C:/Users/changfx/PycharmProjects/python-study/myerror.py", line 20, in <module>
    operate()
  File "C:/Users/changfx/PycharmProjects/python-study/myerror.py", line 14, in operate
    print(nums[10])
IndexError: list index out of range

Process finished with exit code 1

从结果可以看出 开始 正常打印, 当发生 IndexError 的报错后, 程序就退出了。并没有执行 后面 的语句。

没有打印最后的结束语,也没有打印 最后一个元素。

总结一下: 当异常发生的时候,如果我们没有对异常进行处理。程序 就中断 ,然后 程序就会异常的退出了。

如何处理异常

try except finally else

可以通过这个 来 捕获可能发生的异常。

try:
    pass
	# 可能发生异常的代码段
except IndexError as e:
    pass

来修改一下 上面的函数

def operate():
    print("operate begin")

    nums = ["one", "two", 'three', 'four', 'five']
    print(nums[1])

    try:
        # 这里要发生异常
        print(nums[10])
    except IndexError as e:
        print(e)

    print(nums[-1])
    print("operate end")


operate()

"""结果
operate begin
two
list index out of range
five
operate end

"""

再次运行 这个代码,发现 已经可以正常运行了。 程序正常退出了,这个时候 当发生 异常的时候 我们可以尝试捕获这个异常, 只要 这个异常被 捕获了, 那么程序就可以正常运行了。

对于 异常捕获 try 语句 还有其他的形式 。 简单说一下

有时候我们并不知道 哪些代码段会发生异常,也有可能不发生异常。 这个时候就要考虑 用 try 把可能发生的异常 进行捕获。

比如下面的函数, 我想 访问 下标为10 的元素,但是这个array 是作为一个函数的参数传入进来,我并不知道 这个array 是否有 下标 为10 的元素, 所以 我用 try 尝试捕获异常。这里我使用 这种结构 .

try:
	pass
except IndexError as e:
	pass
else:
	pass
	

这里 else 语句代表 如果没有发生异常,会执行 else 语句的内容,如果发生了异常则不会执行else 语句的内容。

def print_nums(array: list):
    print('print_nums begin')
    try:
        print(array[10])
    except IndexError as e:
        print(f"e={e}")
    else:
        print('end')

在console演示一下:

>>> def print_nums(array: list):
...     print('print_nums begin')
...     try:
...         print(array[10])
...     except IndexError as e:
...         print(f"e={e}")
...     else:
...         print('end')
... 

>>> array = list(range(5))
>>> print_nums(array)  # 发生异常
print_nums begin
e=list index out of range

>>> array2 = list(range(11))
>>> print_nums(array2) #没有发生异常 
print_nums begin
10
end

发生的异常的时候 else 的语句没有被执行,如果没有发生异常, else 的语句正常执行了。 这个就是else 语句的作用。 只有当 try 的代码段 没有发生异常的时候, else 的语句才会被执行。

在介绍一种 异常捕获的形式

finally 子句, 这里 finally 的语句 代表 无论 try 捕获 这段代码 是否发生异常, finally 永远被执行。 这里一般会做一些 资源释放, 数据库关闭连接之类的操作。

def print_nums(array: list):
    print('print_nums begin')
    try:
        # 可能发生的异常的代码段
        print(array[10])
    except IndexError as e:
        print(f"e={e}")
    else:
        print('else end')
    finally:
        print("print_nums end")

在 console 演示

>>> def print_nums(array: list):
...     print('print_nums begin')
...     try:
...         # 可能发生的异常的代码段
...         print(array[10])
...     except IndexError as e:
...         print(f"e={e}")
...     else:
...         print('else end')
...     finally:
...         print("print_nums end")
...         
>>> 

>>> print_nums(array=list(range(11)))  # 不会发生异常
print_nums begin
10
else end
print_nums end


>>> print_nums(array=list(range(5)))  # 会发生异常
print_nums begin
e=list index out of range
print_nums end

可以看出 无论发生 或者不发生异常,都会 执行 finally 子句的代码段。这个就是 finally 的作用。

如何自己主动抛出一个异常

可以使用 raise 语句 抛出一个异常. 这里 可以用来检测 一些用户输入,如果输入了一些不合法的值, 我们就可以主动 抛出一个异常。给出用户一些错误提示信息。

raise IndexError("xxxxxxx")

手动 抛出一个异常,比如 人的年龄 不可能超过 100,和小于0 。这里 手动抛出一个 ValueError 的异常

>>> 
>>> 
... age = 150
... if age > 100 or age < 0:
...     raise ValueError("age >100 or age<0 ")
... 
Traceback (most recent call last):
  File "<input>", line 4, in <module>
ValueError: age >100 or age<0 

你注意到了吗? 这里我抛出的异常 是 ValueError , 而不是 KeyError ,IndexError 。 所以这里要提醒一下,异常 是有分类的,每一种异常 都有特定的含义,你需要了解这些 特定含义,才进行抛出特定的异常。 因为 异常信息 是用来帮助我们来排查程序出现的问题,所以 抛出异常信息 要尽量准确,这样才能方便排查 问题。

异常的分类

在python3 中 内置很多的异常,并且 异常也是 可以通过继承的。 继承的概念 属于面向对象的概念。 如果不是很理解,简单理解 的子承父业 差不多,下面以及的异常 肯定含有上面异常的所有的东西。 之后 我会单独 解释面向对象的知识。

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

所有的异常 都是 由 BaseException 派生出来的。 然后 对应 4个平级的异常, 我们通常 情况 只是 从 Exception 中 继承 来重写 自己定义的异常。 这里我们 经常 遇见的 也就10 来种左右。 作为了解知道即可。

  • SystemExit
  • KeyboardInterrupt
  • GeneratorExit
  • Exception

自定义异常

我们 有时候 可能不需要系统系统的异常,或者满足不了我们的需要。 我们通常的做法是 继承 Exception 来实现自己的异常。

1.如何自定义自己的 异常对象

大部分情况 我们只要继承 Exception 即可。 有一个关键字 class 还记得 我说的,异常实际上一个类。所以我们想要实现自己的异常,一般 会写一个 class 然后继承 Exception 即可。

class InvalidAge(Exception):
    pass


age = int(input("请输入你的年龄: "))
if age > 100 or age < 0:
    raise InvalidAge("你的年龄应该是 [0-100] 之间 ")
>>> class InvalidAge(Exception):
...     pass
... 
... 
... age = int(input("请输入你的年龄: "))
... if age > 100 or age < 0:
...     raise InvalidAge("你的年龄应该是 [0-100] 之间 ")
... 
请输入你的年龄: >? 123
Traceback (most recent call last):
  File "<input>", line 7, in <module>
InvalidAge: 你的年龄应该是 [0-100] 之间 

异常 是什么? 异常是不是一种例外情况呢?

为了 更好的解释异常,我举两个数字相除的例子。来看下面的两个函数

def divide(number, divisor):
    try:
        print(f"{number} / {divisor} = {number / divisor}")
    except ZeroDivisionError:
        print("You can't divide by zero ")


        
def divide2(number, divisor):
    if divisor == 0:
        print("You can't divide by zero ")
        return

    print(f"{number} / {divisor} = {number / divisor}")

这两个函数 功能 是相同的,计算两个数 相除。 应该知道 除数不能 作为分母。 第一种方式 通过捕获这个异常 来提示用户 不要把除数变成0 , 第二种方式 是直接判断除数 是否为0 。

第二种方法 好不好呢? 其实 我不能告诉你 这样做好不好 。

但是 对于Python 程序员 更加细化追随 请求谅解,而不是许可的原则。 就是说 python鼓励程序员先去做,然后 在去解决问题。 显然 python 是鼓励 大家勇于尝试的语言,实际上看 内置的库,也是遵守这个原则的。

当然还有另一种 原则三思而后行的原则 这种做法也有一定好处, 第一 减少了不必要的cpu 的开销,减少了一些不必要执行的代码。 这里对于例外情况 是非常有效的。

比如说 举个例子,在一个超市销售系统里面,有一个货物卖完了,显然这个 时候 我们应该返回什么呢?

空,缺货的字符串,一个负数 ? 我们通过if 语句判断 或取得数量 显然 这个逻辑 写起来 比较繁琐。

为啥 这个时候 我们不可以 自定义一个异常呢?

class OutOfStack(Exception):
    pass

只要在每次用户调用购买的时候 ,我们try 进行捕获这段购买的逻辑。然后在购买的时候 ,如果判断缺货了,直接抛出 这个 我们自定义的异常。 这样的话,是不是看起来 更加好一点呢。

这里你可能对 面向对象 不理解,之后我们 慢慢解释一下 什么是面向对象编程。

class OutOfStack(Exception):
    pass

class Goods:

    def __init__(self, stock: int):
        self.stock = stock

    def purchase(self):
        if self.stock == 0:
            raise OutOfStack("库存为0,无法购买了!")
        self.stock = self.stock - 1
        print(f"购买成功。 当前库存容量:{self.stock}")

在控制台 执行

>>> class OutOfStack(Exception):
...     pass
... 
... 
... class Goods:
... 
...     def __init__(self, stock: int):
...         self.stock = stock
... 
...     def purchase(self):
...         if self.stock == 0:
...             raise OutOfStack("库存为0,无法购买了!")
...         self.stock = self.stock - 1
...         print(f"购买成功。 当前库存容量:{self.stock}")
... 
>>> 
>>> goods = Goods(3)
>>> goods.purchase()
购买成功。 当前库存容量:2
>>> goods.purchase()
购买成功。 当前库存容量:1
>>> goods.purchase()
购买成功。 当前库存容量:0
>>> goods.purchase()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 12, in purchase
OutOfStack: 库存为0,无法购买了!

当前库存为0 ,然后程序 抛出一个异常。告诉用户 库存不够了。

​ 用异常 来进行流程控制 可以完成一些 非常好用的程序设计。 还有要明白,发生异常的时候并不意味着 你要努力阻止 这个这种情况发生。 相反 ,它是一种 无法直接 交流的代码 进行 信息沟通的方式而已。

如何安装 第三方的包?

什么是第三方包,就是一些别人觉得 非常有用的功能,写成一个工具包,这样 我们遇到 类似问题的时候,就不要重头开始写这些工具包,直接别人写好的工具就可以了。 是不是很好啊,直接窃取别人的劳动果实。哈哈,其实 这就是一种开源精神。 每个有追求的程序员都应该有这份追求。

那么问题来了 如何寻找 这些 工具包呢?

有一个网站 pypi.org 所有python的第三方包都会放到这个网站上面,可以自行搜索 查找。

下面 我们来安装 一下 这几个包

celery attrs requests redis

首先 你要知道 这些包的名称, 然后安装它 。

第一种方法 使用pip 工具安装

pip install package_name 

打开git bash ,找到你的项目路径

$ cd python-study/
(venv)
CHANGFX@xxxxxxx ~/PycharmProjects/python-study
$ pwd
/c/Users/changfx/PycharmProjects/python-study
(venv)
CHANGFX@xxxxxx ~/PycharmProjects/python-study

# 这里是激活 虚拟环境
$ source venv/Scripts/activate
(venv)
CHANGFX@xxxxxx ~/PycharmProjects/python-study
$
(venv)
CHANGFX@xxxxxx  ~/PycharmProjects/python-study
$ pip install redis
$ pip install requests

第二种 是pycharm 自带的安装工具

导航栏 中 File > Settings

找到 Project 然后点击 Project ,看到下面的画面,点击 下图中的加号

img-02-04-1

直接 在搜索栏 搜索 redis , 就能够看到 redis 相关的package ,点击 install package 就可以下载了。

img-02-04-02

同理 可以下载 ,把下面四个包 下载一下, 注意不要下载错了哦。

celery attrs requests redis

查看第三方包 自定义的异常

当你安装 完成后,你可以查看 自己的venv 这个目录下面 就会发现 已经有下面的路径了。

我让你安装 这些包,主要让你 看下,别人 如何定义自己的异常的。 然后感受一下。

img-02-04-03

当下载完成之后 要自己查看 一下 这些文件,可以看到都有自己定义一些异常类。

主要看下面的文件,都是 第三方包 自己定义的一些异常。

venv/Lib/site-packages/celery/exceptions.py

venv/Lib/site-packages/attr/exceptions.py

venv/Lib/site-packages/requests/exceptions.py

venv/Lib/site-packages/redis/exceptions.py

总结

​ 今天 主要讲解了 python 中的异常,一些概念。 异常实际上是一个对象。 我们根据自己的需要 定义自己的异常,方便 我们 更好的定位问题,查找错误等。 异常 是一个非常好的工具,之后慢慢体会。 先要明白如何处理异常,如何定义异常等。 加油!

参考文档

exceptions

Python3 错误和异常

python3 面向对象编程

分享快乐,留住感动. 2020-04-03 21:08:48 --frank
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值