09-01 异常处理

一. 什么是异常?

"""
异常是程序发生错误的信号. 程序一旦出错便会抛出一个异常, 若程序中没有处理它, 从当前行开始, 程序的运行随之终止, 后面代码不执行.
"""
  • 异常处理的三个特征: 异常的追踪信息, 异常的类型, 异常的内容

二. 为何处理异常

"""
为了增强程序的健壮性,即便是程序运行过程中出错了,也不要终止程序, 而是捕捉异常并处理:将出错信息记录到日志内
"""

三. 如何处理异常?

错误分成两种: 语法上的错误, 逻辑上的错误

1. 语法上的错误SyntaxError. 这种错误必须在程序运行前就修正.

"""
print(
if
"""

2. 逻辑上的错误

  • 错误类型简单示范
# TypeError
1 + '3'         # TypeError: 不支持的操作数类型:'int'和'str'
dic = {[1]: 2}  # TypeError: unhashable类型:“列表”

# ValueError
int('hello')  # ValueError: 以10为基数的int()无效的文字:'hello'

# NameError
x   # NameError: name“x”没有定义

# IndexError
li = [1, 2, 3]
li[4]  # IndexError: 列表索引超出范围

# KeyError
dic = {'name': 'egon'}
dic['NAME']  # KeyError: 'NAME'

# AttributeError
int.egon_dsb     # AttributeError: 类型对象“int”没有属性“egon_dsb”
int.egon()       # AttributeError: 类型对象“int”没有属性“egon”

# ZeroDivisionError
1 / 0  # ZeroDivisionError: 除数不能为0
  • 争对逻辑上的异常又分成两种处理方式
# 一. 错误的条件是可以预知的: 使用if判断来解决
age = input('>>: ').strip()  # 输入的只要不是数字就会出错
if age.isdigit():
    age = int(age)
    if age > 18:
        print('猜大了')
    elif age < 18:
        print('猜大了')
    else:
        print('猜对了')
else:
    print('必须输入数字')
    
# 二. 错误的条件是无法预知的: 使用异常处理机制来解决
# 1. 单分支语句 ==> as介绍
"""
try:
    被检测的代码块
except 被检车的代码块可能会出现的异常类型 as 自定义变量:  # as语法将异常内容赋值给后面的'自定义变量',这样我们通过打印'自定义变量'便可以知道错误的原因
    检测到异常, 就执行这个位置的逻辑代码
"""
try:
    print(x)
except NameError as e:
    print('异常内容:', e)  # 异常内容: name 'x' is not defined

    
# 2. 多分支语句
# 如果我们想分别用不同的逻辑处理,需要用到多分支的except(类似于多分支的elif,从上到下依次匹配,匹配成功一次便不再匹配其他)
"""
try:
    被检测的代码块
except NameError:
    触发NameError时对应的处理逻辑
except IndexError:
    触发IndexError时对应的处理逻辑
except KeyError:
    触发KeyError时对应的处理逻辑
"""
li = [1, 2, 3]
dic = {'name': 'egon'}
try:
    # xxx
    # li[3]
    dic["EGON"]
except NameError as e:
    print('异常内容:', e)  # 异常内容: name 'xxx' is not defined
except IndexError as e:
    print('异常内容:', e)  #  异常内容:list index out of range
except KeyError as e:
    print('异常内容:', e)  # 异常内容: 'EGON'


# 3.多个异常放到一个元组内
# 如果我们想多种类型的异常统一用一种逻辑处理,可以将多个异常放到一个元组内,用一个except匹配
"""
try:
    被检测的代码块
except (NameError,IndexError,TypeError):
    触发NameError或IndexError或TypeError时对应的处理逻辑
"""
li = [1, 2, 3]
dic = {'name': 'egon'}
try:
    # xxx
    # li[3]
    # 1 + '1'
    dic["EGON"]
except (NameError, IndexError, TypeError) as e:
    print('异常内容:', e)
    print('这里检测这三种异常后执行的的统一的逻辑.......')
except KeyError as e:
    print('异常内容:', e)
    print("这里是检测争对字典key不存在后执行的单独的逻辑.......")

    
# 4. Exception
# 如果我们想捕获所有异常并用一种逻辑处理,Python提供了一个万能异常类型Exception
"""
try:
    被检测的代码块
except NameError:
    触发NameError时对应的处理逻辑
except IndexError:
    触发IndexError时对应的处理逻辑
except Exception:
    其他类型的异常统一用此处的逻辑处理
"""
li = [1, 2, 3]
dic = {'name': 'egon'}
try:
    xxx
    li[3]
    1 + '1'
    dic["EGON"]
except NameError as e:
    print('名字错误抛出的异常内容:', e)
except IndexError as e:
    print('索引错误抛出的异常内容:', e)
except Exception as e:
    print('我是万能异常可以检测所有异常类型抛出的错误:', e)


# 5. else
# 在多分支except之后还可以跟一个else,只有在被检测的代码块没有触发任何异常的情况下才会执行else的子代码块
"""
try:
    被检测的代码块
except 异常类型1:
    pass
except 异常类型2:
    pass
......
else:
    没有异常发生时执行的代码块
"""
li = [1, 2, 3]
dic = {'name': 'egon'}
try:
    print("111111111")
    # xxx
    # li[3]
    # 1 + '1'
    # dic["EGON"]
except Exception as e:
    print(e)
else:
    print('在被检测的代码块没有触发任何异常的情况下才我才会执行')


# 6. finally
# 此外try还可以与finally连用,从语法上讲finally必须放到else之后,但可以使用try-except-finally的形式,也可以直接使用try-finally的形式
"""
finally作用: 
    无论被检测的代码块是否触发异常,都会执行finally的子代码块,因此通常在finally的子代码块做一些回收资源的操作,比如关闭打开的文件、关闭数据库连接等
    
try: 
    被检测的代码块 
except 异常类型1: 
    pass 
except 异常类型2:
    pass 
...... 
else: 
    没有异常发生时执行的代码块 
finally: 
    无论有无异常发生都会执行的代码块    
"""
f = open('a.txt', 'at', encoding='utf-8')
try:
    print("操作文件的逻辑1")
    print("操作文件的逻辑2")
    xxx  # 出现异常下面f.close()回收操作系统资源失败
    print("操作文件的逻辑3")
    print("操作文件的逻辑4")
    f.close()
finally:
    print("无论被检测的代码块是否触发异常, 都会执行finally的这行子代码块")
    f.close()

# 7. raise
# 在不符合Python解释器的语法或逻辑规则时,是由Python解释器主动触发的各种类型的异常,而对于违反程序员自定制的各类规则,则需要由程序员自己来明确地触发异常,这就用到了raise语句,raise后必须是一个异常的类或者是异常的实例
class People:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise ValueError("姓名的修改必须指定字符串类型!")
        if value == '':
            raise ValueError("不能输入为空字符串!")
        self.__name = value
obj = People('egon')
# obj.name = 18   # ValueError: 姓名的修改必须指定字符串类型!
obj.name = ''     # ValueError: 不能输入为空字符串!


# 8. 自定义异常类(了解)
# 在内置异常不够用的情况下,我们可以通过继承内置的异常类来自定义异常类
class PoolEmptyError(Exception):  # 可以通过继承Exception来定义一个全新的异常
    def __init__(self, value='The proxy source is exhausted'):  # 可以定制初始化方法
        super(PoolEmptyError, self).__init__()
        self.value = value

    def __str__(self):  # 可以定义该方法用来定制触发异常时打印异常值的格式
        return '< %s >' % self.value


class NetworkIOError(IOError):  # 也可以在特定异常的基础上扩展一个相关的异常
    pass


raise PoolEmptyError  # __main__.PoolEmptyError: < The proxy source is exhausted >
raise NetworkIOError('连接被拒绝')  # __main__.NetworkIOError: 连接被拒绝

# 9. assert
# 最后,Python还提供了一个断言语句assert expression,断定表达式expression成立,否则触发异常AssertionError,与raise-if-not的语义相同,如下
import random
a = random.randint(1, 2)
b = random.randint(2, 3)
print(f'a:{a}  b:{b}')
assert a == b
print("断言成功以后执行的代码.......")

四. 总结: 何时使用异常处理

"""
在了解了异常处理机制后,本着提高程序容错性和可靠性的目的,读者可能会错误地认为应该尽可能多地为程序加上try...except...,这其是在过度消费程序的可读性,因为try...except本来就是你附加给程序的一种额外的逻辑,与你的主要工作是没有多大关系的。
"""
  • 如果错误发生的条件是“可预知的”,我们应该用if来进行”预防”
age = input('input your age>>: ').strip()
if age.isdigit():  # 可预知只有满足字符串age是数字的条件,int(age)才不会触发异常,
    age = int(age)
else:
    print('You must enter the number')
  • 如果错误发生的条件“不可预知”,即异常一定会触发,那么我们才应该使用try...except语句来处理
"""
例如我们编写一个下载网页内容的功能,网络发生延迟之类的异常是很正常的事,而我们根本无法预知在满足什么条件的情况下才会出现延迟,因而只能用异常处理机制了
"""
import requests
from requests.exceptions import ConnectTimeout  # 导入requests模块内自定义的异常


def get(url):
    try:
        response = requests.get(url, timeout=3)  # 超过3秒未下载成功则触发ConnectTimeout异常
        res = response.text
    except ConnectTimeout:
        print('连接请求超时')
        res = None
    except Exception:
        print('网络出现其他异常')
        res = None
    return res


get('https://www.python.org')
posted @ 2020-04-16 17:59  给你加马桶唱疏通  阅读(167)  评论(0编辑  收藏  举报