8.1. 异常处理

1.捕获单个异常

try:
<语句>  #运行别的代码
except <名字>:
<语句>  #如果在try部分引发了异常

2.捕获多个异常

try:
<语句>  #运行别的代码
except <名字1>:
<语句>  #如果在try部分引发了name1异常
except <名字2>,<数据>:
<语句>  #如果引发了name2异常,获得附加数据
try:
<语句>  #运行别的代码
except <名字>:
<语句>  #如果在try部分引发了异常1
except <名字>,<数据>:
<语句>  #如果引发了异常2,获得附加数据
else:
<语句>  #如果没有发生异常
try:
except
finally
语句

5.使用raise语句抛出异常

8.1.1. 1.异常处理规则

  • 不要过度使用异常,不要使用异常代替流程控制,该要提示的就要给出提示和错误处理方法

  • 不要用过于庞大的try语句块,永远只捕获那些可能会抛出异常的语句块

  • 不要忽略捕获到的异常,要对异常采取相应措施

  • 尽量只捕获精确的异常类型,而不是模糊的Exception

  • 如果出现了预期外的异常,让程序早点儿崩溃也未必是件坏事

举例

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/9/8 20:46
# filename: 01.一个简单的异常.py

def exp_exception(x, y):
    try:
        a = x / y
        print("a=", a)
    except Exception:
        print("程序出现异常,异常信息:被除数为0")


exp_exception(2, 0)  # 程序出现异常,异常信息:被除数为0
exp_exception(1, 2)  # a= 0.5

举例

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/6/6 15:01
# filename: test0001.py
import datetime as dt


def read_date(in_date):
    try:
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except ValueError:
        print("处理ValueError异常")


str_date = "2019-06-06"
print("日期 = {0}".format(read_date(str_date)))

输出信息

日期 = 2019-06-06 00:00:00

函数稍作修改

def read_date(in_date):
    try:
        date = dt.datetime.strptime(in_date, '%Y-%m-%d')
        return date
    except ValueError as e:
        print("处理ValueError异常")
        print(e)


str_date = "201B-06-06"
print("日期 = {0}".format(read_date(str_date)))

输出结果

处理ValueError异常
time data '201B-06-06' does not match format '%Y-%m-%d'
日期 = None

举例

def division():
    '''
    功能:分苹果
    :return:
    '''
    print("\n ==========================分苹果了=================")
    apple = int(input('请输入苹果的个数:'))
    children = int(input("请输入小朋友的人数:"))
    if apple < children:
        raise ValueError("苹果太少,不够分")

    result = apple//children
    remain = apple-result*children
    if remain>0:
        print("{}个苹果,平均分给{}个小朋友,每个人分{}个,剩下{}个".format(apple,children,result,remain))
    else:
        print("{}个苹果,平均分给{}个小朋友,每人分{}个".format(apple,children,result))

if __name__ == '__main__':
    try:
        division()      #调用分苹果函数
    except (ZeroDivisionError,ValueError) as e:
        print("输入错误:",e)
    else:
        print("分苹果顺利完成.....")
    finally:
        print("进行了一次分苹果操作")

举例

#!/usr/bin/env python
#-*- coding:utf8 -*-
try:
    open("tes.txt")

except (KeyError,IndexError) as e :
    print("没有这个key",e)

except IndexError as e :
    print("列表操作错误",e)

except Exception as e:
    print("未知错误",e)

else:
    print("一切正常")

finally:
    print("不管有没有错,都执行")

捕获多个异常

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/9/8 20:55
# filename: 03.捕获多个异常.py

def mult_exception(x, y):
    try:
        a = x / y
        b = name
    except ZeroDivisionError:
        print("this is ZeroDivisionError")

    except NameError:
        print("This is NameError")


mult_exception(2, 0)  # this is ZeroDivisionError
mult_exception(2, 3)  # This is NameError

使用一个块捕捉多个异常

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/9/8 20:59
# filename: 04.使用一个块捕捉多个异常.py

def model_exception(x, y):
    try:
        b = name
        a = x / y
    except (ZeroDivisionError, NameError, TypeError):
        print("one of ZeroDivisionError or NameError or TypeE")


model_exception(2, 0)   # one of ZeroDivisionError or NameError or TypeE

举例:

a = 35
b = 57
try:
    c = a + b
    print("The value of c is: ", c)
    d = b / 0
    print("The value of d is: ", d)

except:
    print("Division by zero is not possible")

print("Out of try...except block")


# The value of c is:  92
# Division by zero is not possible
# Out of try...except block

捕捉对象

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/9/8 21:01
# filename: 05.捕捉对象.py

def model_exception(x, y):
    try:
        b = name
        a = x / y
    except (ZeroDivisionError, NameError, TypeError) as e:
        print(e)


model_exception(2, 0)       #name 'name' is not defined

# 若a=x/y在前,则结果如下:
# division by zero

eg

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/4/29 11:28
# filename: 异常处理1.py

def yichang(index, flag=False):
    studyname = ["hujianli", "hujianli2", "huajianli3"]

    if flag:
        try:
            rst = studyname[index]
        except:
            print("index error.....")
        return "Try test finishing..."
    else:
        rst = studyname[index]
        return "No try test finishing"


if __name__ == '__main__':
    print("Start Right params testing....")
    print(yichang(1, True))
    print(yichang(1, False))
    print("Error params test start.....")
    #超出index范围,flag为True,进行自定义的异常
    print(yichang(4, True))
    #超出index范围,且flag为Fasle,直接触发系统内部异常
    print(yichang(4, False))

异常中的else

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/9/8 21:05
# filename: 06.异常中的else.py
'''
如果在try子句执行时没有发生异常,就会执行else语句后的语句(如果
有else)。使用else子句比把所有语句都放在try子句里面更好,这样可以避
免一些意想不到而except又没有捕获的异常
'''


def model_exception(x, y):
    try:
        a = x / y
    except:
        print("Error happened")
    else:
        print("It went as expected")


model_exception(2, 1)  # It went as expected

raise主动抛出异常

#!/usr/bin/env python
# -*- coding:utf8 -*-

"""
抛出异常使用raise语句,
· raise关键字
· 对Exception函数的调用
· 传递给Exception函数的字符串,包含有用的出错信息
"""
try:
    raise Exception("This is the error message. ")
except Exception as e:
    print("抛出异常", e)

except+raise配合使用

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/6/14 22:58
# filename: auction_except_raise.py
class AuctionException(Exception):
    """ 自定义异常类"""
    pass


class AuctionTest:
    def __init__(self, init_price):
        self.init_price = init_price

    def bid(self, bid_price):
        d = 0.0
        try:
            d = float(bid_price)
        except Exception as e:
            print("转换出异常:", e)
            raise AuctionException("竞拍价必须是数值,不能包含其他字符!")
            # raise AuctionException(e)
        if self.init_price > d:
            raise AuctionException("竞拍价比起拍价低,不允许竞拍!")
        initPrice = d


def main():
    at = AuctionTest(20.4)
    try:
        at.bid("df")
    except AuctionException as ae:
        print("main函数捕获的异常:", ae)

main()

输出信息

转换出异常: could not convert string to float: 'df'
main函数捕获的异常: 竞拍价必须是数值,不能包含其他字符!

expect+raise代码异常

#!/usr/bin/env python
# -*- coding:utf8 -*-

def boxPrint(symbol, width, height):
    if len(symbol) != 1:
        raise Exception("Symbol must be a single character string. ")
    if width <= 2:
        raise Exception("Width must be greater than 2. ")
    if height <= 2:
        raise Exception("Height must be greater than 2. ")
    print(symbol * width)
    for i in range(height - 2):
        print(symbol + (' ' * (width - 2)) + symbol)
    print(symbol * width)


try:
    boxPrint("1", 9, 5)
    # boxPrint("22", 9, 5)
except Exception as e:
    print(e)

"""
111111111
1       1
1       1
1       1
111111111
"""

try+except+finally子句

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/9/8 21:12
# filename: try+finally子句.py

def use_finally(x, y):
    try:
        a = x / y
    except ZeroDivisionError:
        print("Some bad thing happened: division by zero")
    finally:
        print("No matter what happend, I will show in front of ")


use_finally(2, 0)

"""
Some bad thing happened: division by zero
No matter what happend, I will show in front of
"""

raise不需要参数

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/6/14 23:13
# filename: raise不需要参数.py
class AuctionException(Exception):
    """ 自定义异常类"""
    pass


class AuctionTest:
    def __init__(self, init_price):
        self.init_price = init_price

    def bid(self, bid_price):
        d = 0.0
        try:
            d = float(bid_price)
        except Exception as e:
            print("转换出异常:", e)
            # 再次引发当前激活的异常
            raise
        if self.init_price > d:
            raise AuctionException("竞拍价比起拍价低,不允许竞拍!")
        initPrice = d


def main():
    at = AuctionTest(20.4)
    try:
        at.bid("df")
    except Exception as ae:
        print("main函数捕获的异常", type(ae))


main()

输出信息

转换出异常: could not convert string to float: 'df'
main函数捕获的异常 <class 'ValueError'>

举例

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/9/8 20:49
# filename: 02.抛出异常.py

try:
    raise NameError("This is NameError")  # 抛出的异常将被下面捕获
except NameError:
    print("An exception happend!")  # 捕获异常并输出,An exception happend!

try:
    raise NameError("This is NameError")  # 抛出的异常将被下面捕获
except NameError:
    print("An exception happend!")  # 捕获异常并输出,An exception happend!
    raise  # NameError: This is NameError

异常堆栈跟踪

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/5/18 8:40
# filename: 异常堆栈跟踪.py
# import datetime as dt
# import traceback as tb
#
#
# def read_date_from_file(filename):
#     try:
#         file = open(filename)
#         in_date = file.read()
#         in_date = in_date.strip()
#         date = dt.datetime.strftime(in_date, "%Y-%m-%d")
#         return date
#     except (ValueError,OSError) as e:
#         print("调用方法method1处理.....")
#         tb.print_exc()
#
# date = read_date_from_file("readme.txt")
# print("日期 = {0}".format(date))
import traceback


class SelfException(Exception):
    pass


def main():
    firstMethod()


def firstMethod():
    SecondMethod()


def SecondMethod():
    thirdMethod()


def thirdMethod():
    raise SecondMethod("自定义异常信息")


try:
    main()
except:
    # 捕获异常信息,并将异常信息输出到控制台
    traceback.print_exc()
    # 捕获异常信息,并将异常信息输出到指定文件中
    traceback.print_exc(file=open("log.txt", "a", encoding="utf-8"))

异常嵌套

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/6/6 15:10
# filename: test+try嵌套.py
import datetime as dt


def read_date_from_file(filename):
    try:
        file = open(filename)
        try:
            in_date = file.read()
            in_date = in_date.strip()
            date = dt.datetime.strptime(in_date, "%Y-%m-%d")
            return date
        except ValueError as e:
            print("处理ValueError 异常")
            print(e)

    except FileNotFoundError as e:
        print("处理 FileNotFoundError异常")
        print(e)

    except OSError as e:
        print("处理 OSError 异常")
        print(e)

date = read_date_from_file("readme.txt")
print("日期 ={0}".format(date))

else代码块

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/6/6 15:19
# filename: 异常else语句.py
import datetime as dt


def read_date_file(filename):
    try:
        file = open(filename)
    except OSError as e:
        print("打开文件失败")

    else:
        print("打开文件成功")
        try:
            in_date = file.read()
            in_date = in_date.strip()
            date = dt.datetime.strptime(in_date, "%Y-%m-%d")
            return date
        except ValueError as e:
            print("处理ValueError异常", e)
        except OSError as e:
            print("处理OSError异常", e)
        finally:
            file.close()

date = read_date_file("readme.txt")
print("日期 ={0}".format(date))

with as 自动资源管理

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/6/6 15:19
# filename: 异常else语句.py
import datetime as dt

def read_date_file(filename):
    try:
        with open(filename) as file:
            in_date = file.read()
            in_date = in_date.strip()
            date = dt.datetime.strptime(in_date, "%Y-%m-%d")
            return date
    except ValueError as e:
        print("处理ValueError异常", e)
    except OSError as e:
        print("处理OSError异常", e)


date = read_date_file("readme.txt")
print("日期 ={0}".format(date))

手工抛出异常

eg

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/4/29 12:21
# filename: 手工抛出异常.py

def testRaise2(number):
    for i in range(number):
        try:
            if i == 2:
                raise NameError
        except NameError:
            print("Raise a NameErrot")
        print(i)
    print("end ......")

testRaise2(10)

eg

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/6/29 10:27
# filename: 异常处理0001.py
import random

some_exceptions = [ValueError, TypeError, IndexError, None]

try:
    choice = random.choice(some_exceptions)
    print("raising {}".format(choice))
    if choice:
        raise choice("An error")

except ValueError:
    print("Caught a  ValueError")

except TypeError:
    print("Caught a TypeError")

except Exception as e:
    print("Caught some other error :%s" % (e.__class__.__name__))

else:
    print("This code called if there is no exception")

finally:
    print("This code called is always called")

每次输出信息都不一样,输出信息如下:

raising <class 'TypeError'>
Caught a TypeError
This code called is always called

不管有没有异常发生,else和finally语句会执行。

输出异常的详细信息

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2020/1/8 22:18
# filename: sample01.py
import sys

try:
    x = int(input("请输入一个被除余数:"))
    print("30除以", x, "等于", 30 / x)
except:
    print(sys.exc_info())
    print("其他异常")

"""
返回一个元祖,元祖包含3个元素:分别是type、value和traceback
· type:异常类型的名称
· value:捕获到的异常实例
· traceback: 是一个traceback对象


请输入一个被除余数:0
(<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero',), <traceback object at 0x00000299F728DCC8>)
其他异常
"""

traceback对象的显示

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2020/1/8 22:24
# filename: sample02.py
import traceback
import sys

try:
    x = int(input("请输入一个被除余数:"))
    print("30除以", x, "等于", 30 / x)
except:
    traceback.print_tb(sys.exc_info()[2])  # 打印traceback对象
    print("其他异常")
else:
    print("再见..")

"""
请输入一个被除余数:0
其他异常
  File "D:/Python-code/9.异常程序调试/输出异常的详细信息/sample02.py", line 11, in <module>
    print("30除以", x, "等于", 30 / x)
"""

traceback.print_exc()方法可以直接将异常内容打印出来

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2020/1/8 22:24
# filename: sample02.py
import traceback
import sys

try:
    x = int(input("请输入一个被除余数:"))
    print("30除以", x, "等于", 30 / x)
except:
    traceback.print_exc()
    print("其他异常")
else:
    print("再见..")

"""
请输入一个被除余数:0
其他异常
Traceback (most recent call last):
  File "D:/Python-code/9.异常程序调试/输出异常的详细信息/sample03.py", line 11, in <module>
    print("30除以", x, "等于", 30 / x)
ZeroDivisionError: division by zero
"""

还有一些常用的方法,都是打印异常的内容

#traceback.print_exc()
等价于
traceback.print_exception(*sys.exc_info())