8 - Python 异常处理

1、什么是异常?为什么要捕获异常?

  • 程序在运行时,提示的一些错误信息,这就是异常
    • 即 Python 的程序的语法是正确的,在运行它的时候,也有可能发生错误。运行期检测到的错误被称为异常。
  • 如果程序执行抛出异常,,则程序会中断执行,大多数的异常都不会被程序处理,所以需要捕获异常,并对异常进行处理

2、异常基类

在处理异常的时候,我们会接触到很多错误基类,比较常用的罗列如下:

BaseException:所有异常的基类

Exception:常规错误的基类

ZeroDivisionError:除(或取模)零(所有数据类型)

AssertionError:断言语句失败

AttributeError:对象没有这个属性

ImportError:导入模块/对象失败

LookupError:无效数据查询的基类

IndexError:序列中没有此索引(index)

KeyError:映射中没有这个键

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

SyntaxError:Python 语法错误

IndentationError:缩进错误

TypeError:对类型无效的操作

ValueError:传入无效的参数

UnicodeError:Unicode 相关的错误

UnicodeDecodeError:Unicode 解码时的错误

UnicodeEncodeError:Uncode编码时错误

UnicodeTranslateError:Unicode转换时错误

Warning:警告的基类

SyntaxWarning:可疑的语法的警告

3.异常的完整语法格式

  • try(译:踹)、except(译:亦可赛泼特)、else(译:艾欧斯)、finally(译:发呢李)
try:    
    尝试执行的代码    
    pass 
except 错误类型1:    
    针对错误类型1,对应的代码处理    
    pass 
except 错误类型2:    
    针对错误类型2,对应的代码处理    
    pass 
except (错误类型3, 错误类型4):    
    针对错误类型3 和 4,对应的代码处理    
    pass 
except Exception as result:    
    # 能接收(捕获)所有的异常
    # 会将接收到的异常赋值给result变量
    打印错误信息    
    print(result) 
else:    
    没有异常才会执行的代码    
    pass 
finally:    
    无论是否有异常,都会执行的代码    
    print("无论是否有异常,都会执行的代码")

4.在异常中,try关键字下的块语句、except下的块语句、else下的块语句、finally下的块语句执行逻辑

1.如果 try 下的块语句没有异常,

  • else 下的块语句和 finally 下的块语句都会被执行,
  • except下的块语句不会被执行

2.如果 try 下的块语句有异常,

  • 会在当前语句抛出异常,抛出异常的行下面的语句不会被执行,
  • except 语句会从上到下开始判断,如果 except 能捕获到异常,那么其他 except 不会再判断,else也不会被执行,
  • 但finally仍然会被执行
def handle_except():
    try:
        # 只要出现冒号, 那么会告诉Python冒号后面的是一个块语句(往往要缩进4个空格)
        # 在try语句下面, 写一些有可能会出错的代码
        # 在try块语句中, 如果没有抛出异常(没有报错), 那么会将try块语句全部执行完,
        # 且不会执行except块语句
        # 如果出现异常, 那么在try块语句中, 出现异常的地方,剩下的程序不会被执行
        num = int(input("请输入一个数字: "))       # 抛出了一个异常
        print(1 / num)
        print(a_deng)           # Exception
        print("num下面的语句")
        return "菲菲真靓!"

    # except ValueError:                # 只能捕获指定的 ValueError异常
    #     print("值错误的异常!")
    #     print("这里会使用日志器来记录日志!")

    # 只要有一个except语句被执行, 那么其他的except不再会被执行
    except (ValueError, ZeroDivisionError):     # 使用一个except同时处理多个异常
        print("除数为零的异常或者值错误的异常!")
        print("这里也会使用日志器来记录日志!")
    except Exception as err:        # 会将接收到的异常对象赋值给err变量
        # 能接收(捕获)所有的异常
        print(f"err为: {err}")       # 可以打印err变量
        print("能捕获所有的异常")
    else:  # 不能加语句
        print("try语句没有抛出异常的情况下, 会被执行! 正常执行!")

    finally:  # 不能加语句
        print("不管是否有异常, finally一定会被执行! ")

    print("继续往下执行!")


print(handle_except())

示例:判断用户输入的是否是浮点类型

while True:
    try:
        one_num = float(input("请输入一个数字: "))
        break
    except Exception as e:
        pass

print(f"one_num值为{one_num}, 类型为{type(one_num)}")

执行结果:
请输入一个数字: .
请输入一个数字: .
请输入一个数字: 1
one_num值为1.0, 类型为<class 'float'>

4.编写如下程序,优化去生鲜超市买橘子程序

  • a.收银员输入橘子的价格,单位:元/斤
  • b.收银员输入用户购买橘子的重量,单位:斤
  • c.计算并且 输出 付款金额
  • d.使用捕获异常的方式,来处理用户输入无效数据的情况
def is_int_or_digit(num):
    """ 判断用户输入的数字是否为正数
    :param num:数字
    :return:True or False
    """
    try:
        one_num = float(num)
        return True if one_num >= 0 else False
    except ValueError:
        return False


def main():
    while True:
        # 1. 输入橘子单价
        orange_price = input("请输入橘子价格:")
        if not is_int_or_digit(orange_price):
            print("输入的橘子价格为{},输入有误!".format(orange_price))
            continue
        break

    while True:
        # 2. 输入橘子重量
        orange_weight = input("请输入橘子重量:")
        if not is_int_or_digit(orange_weight):
            print("输入的橘子重量为{},输入有误!".format(orange_weight))
            continue
        break

    # 3. 计算金额
    # 将橘子单价转换成浮点数
    orange_price_flt = float(orange_price)

    # 将橘子重量转换成浮点数
    orange_weight_flt = float(orange_weight)

    # 计算付款金额
    money = orange_price_flt * orange_weight_flt

    print("橘子每斤{:.1f}元,您购买了{:.1f}斤,需要支付{:.1f} 元!".format(orange_price_flt, orange_weight_flt, money))


if __name__ == '__main__':
    main()

5.编写如下程序,剪刀石头布程序

  • a.提示用户输入要出的拳 —— 石头(1)/剪刀(2)/布(3)
  • b.电脑随机出拳
  • c.比较胜负,显示用户胜、负还是平局
  • d.使用捕获异常的方式,来处理用户输入无效数据的情况
  • e.多次进行游戏,可以让用户选择退出游戏,退出后需要显示胜利情况,例如:用户5局胜、3局败、2局平
  • f.当程序结束之后,要求下一次运行程序能够获取用户历史胜负情况
  • h.如果使用文件保存用户历史胜负数据,需要使用异常来处理文件不存在的情况(选做)
import random
import os


def menu_info():
    """
    菜单显示信息
    :return:
    """
    print()
    print("=" * 50)
    print("\t\t剪刀石头布终极对战")
    print()
    print("\t\t1、输入石头(1)/剪刀(2)/布(3)")
    print("\t\t2、输入0退出程序\n")
    print("=" * 50)


def check_input(num):
    """
    判断用户输入的是否为0到3之内的正整数
    :param num: 数字
    :return: True or False
    """
    try:
        one_num = int(num)
        return True if one_num in range(4) else False
    except ValueError:
        return False


def user_vs_computer(user_player, computer_player):
    """
    判断用户输赢
    :param user_player: 用户猜拳数字
    :param computer_player: 电脑猜拳数字
    :return:
    """
    # 用户胜的情况:
    # 用户出石头(1),电脑出剪刀(2)
    # 用户出剪刀(2),电脑出布(3)
    # 用户出布(3),电脑出石头(1)
    user_win_tup = ((1, 2), (2, 3), (3, 1))
    if (user_player, computer_player) in user_win_tup:
        print("欧耶!电脑弱爆了!!!")
        return "胜"
    elif computer_player == user_player:
        print("心有灵犀,要不咋再来一盘!")
        return "和"
    else:
        print("不行,我要和你决战到天亮!")
        return "败"


def user_input():
    """
    校验用户输入
    :return: 正整数
    """
    while True:
        user_num = input("\n请输入0~3之间的数字:\n石头(1)/剪刀(2)/布(3)/退出(0) ")
        if not check_input(user_num):
            print("输入错误,请重新输入!")
        else:
            break
    return int(user_num)


def read_file(file_path, mode='r', encoding='utf-8'):
    """
    从文件中读取数据
    :param file_path: 文件路径
    :param mode: 文件打开模式
    :param encoding: 文件编码
    :return: 如果文件不存在或者内容为空, 则返回None, 否则返回每一行内容列表
    """
    # 判断文件是否存在
    if not os.path.exists(file_path) or not os.path.isfile(file_path):
        print("文件不存在或者路径有误!")
        return None
    # 打开文件
    one_file = open(file_path, mode=mode, encoding=encoding)
    # 读取文件内容
    datas_list = one_file.readlines()
    # 关闭文件
    one_file.close()
    return datas_list if datas_list else None


def write_file(file_path, data, mode='a', encoding='utf-8'):
    """
    写数据到文件
    :param file_path: 文件路径
    :param data: 添加的数据
    :param mode: 文件打开模式
    :param encoding: 文件编码
    :return:
    """
    # 打开文件
    one_file = open(file_path, mode=mode, encoding=encoding)
    # 添加内容到文件
    one_file.write(data)
    # 关闭文件
    one_file.close()


def main():
    result_file = "game_result.txt"
    win_count = 0     # 胜利次数
    fail_count = 0     # 失败次数
    peace_count = 0     # 平局次数
    game_count = 0         # 游戏总次数
    win_rate = 0     # 胜算率为0

    # 1、显示游戏历史胜负情况
    print("游戏历史胜负情况如下:")
    handle_result = read_file(result_file)
    if not handle_result:
        print("游戏记录为空")
    elif isinstance(handle_result, list): # 如果返回的是读取的数据列表
        for key, value in enumerate(read_file(result_file),start=1):
            print("第{}局:\t{}".format(key, value[:-1]))

    # 1、显示游戏界面
    menu_info()
    # 2、开始游戏
    while True:
        computer_num = random.randint(1, 3)
        user_num = user_input()
        if user_num == 0:
            print("\n游戏退出,欢迎再来!")
        break
    # 判断胜负
    result = user_vs_computer(user_num, computer_num)
    game_count += 1
    if result == '胜':
        win_count += 1
    elif result == '败':
        fail_count += 1
    else:
        peace_count += 1

    # 游戏结束,显示胜利情况
    try:
        win_rate = (win_count / game_count) * 100
    except ZeroDivisionError:
        print("出现除0异常!")
    last_result = (win_count, fail_count, peace_count, win_rate)
    print("用户胜负情况:\n{}胜、{}负、{}平\n胜算率为:{:.1f}%".format(*last_result))
    # 将用户胜负情况写入文件
    game_info = "{}胜、{}负、{}平、{:.1f}%胜算\n".format(*last_result)
    # file_handle(result_file, game_info)
    write_file(result_file, game_info)


if __name__ == '__main__':
    main()

*******请大家尊重原创,如要转载,请注明出处:转载自:https://www.cnblogs.com/shouhu/,谢谢!!*******