python异常处理2

异常堆栈跟踪

有时需要知道更多异常信息时,可以打印堆栈跟踪信息。堆栈跟踪信息可以通过python内置模块 traceback 提供的 print_exc() 函数实现,print_exc() 函数的语法格式如下:

traceback.print_exc(limit = None, file = None,chain = True)

其中,参数 limit 限制堆栈跟踪的个数,默认为 None 表示不限制;参数 file 为是否输出堆栈跟踪信息到文件,默认None 是不输出到文件; 参数 chain 为True,则将 _cause_ 和 _context_ 等属性串联起来,就像解释器本身打印未处理信息一样。

示例代码如下

import datetime as dt
import traceback as tb # 导入traceback模块并命名为 tb

def read_date_from_file(filename):
    try:
        file = open(filename)
        in_date = file.read()
        in_date = in_date.strip()
        date  = dt.datetime.strptime(in_date,'%Y-%m-%d')
        return date
    except(ValueError,OSError) as e:
        print("调用方法method1处理...")
        tb.print_exc() # 打印异常堆栈信息

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

代码运行结果

Traceback (most recent call last):
  File "D:\creation\PythonStudy\Pythonproject\mypro01\mypy01.py", line 9, in read_date_from_file
    date  = dt.datetime.strptime(in_date,'%Y-%m-%d')
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\86198\AppData\Local\Programs\Python\Python312\Lib\_strptime.py", line 554, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\86198\AppData\Local\Programs\Python\Python312\Lib\_strptime.py", line 333, in _strptime
    raise ValueError("time data %r does not match format %r" %
ValueError: time data '' does not match format '%Y-%m-%d'
调用方法method1处理...
日期 = None

堆栈信息从上到下为程序执行过程中函数(或方法)的调用顺序,其中的每条信息明确指出了哪一个文件、哪一行、调用哪个函数或方法。

释放资源

有时 try--except 语句会占用一些资源,如打开文件、网络连接、打开数据库连接等,这些资源不能通过 python 的垃圾收集器回收,需要程序员释放,为了确保这些资源释放,可以使用 finally 代码块或 with as 自动资源管理。

finally代码块

try-except 代码块后面可以有finally代码块,次数无论 try 中的代码块正常结束还是 except 异常结束都会执行 finally 代码块。

在上面的程序中,只需要在 try-catch 代码块后加入下面的代码

    finally:
        file.close() # 关闭文件释放资源

 else代码块

try 语句月可以带有 else 代码块,它是在程序正常结束时执行的代码块,经过上面的更改(即加入finally代码块)会可以发现,程序仍不够完善,出现的问题是:若文件未成功打开,程序也会进入finally代码块执行 file.close()关闭文件,这会导致一些问题。可以加入 else 语句来解决

示例代码如下

import datetime as dt
import traceback as tb # 导入traceback模块并命名为 tb

def read_date_from_file(filename):
    '''
    如果正常打开文件,程序执行else代码块
    else中嵌套了try语句,在这个try文件中读取文件内容和解析日期
    最后在嵌套try对应的finally代码块中执行file.close()关闭文件
    '''
    try:
        file = open(filename)
    except(ValueError,OSError) as e:
        print("文件打开失败")
        tb.print_exc() # 打印异常堆栈信息
    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, OSError) as e:
            print("文件打开失败")
            tb.print_exc()  # 打印异常堆栈信息
        finally:
            file.close() # 关闭文件释放资源

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

with as代码块自动管理

上面更改后的代码虽然健壮,但程序流程比较复杂,这样的程序难以维护。python提供了 with as 代码块帮助自动释放资源,with as 提供的代码块中,在 as 后面声明一个资源变量,当with as 代码块结束之后自动释放资源。

示例代码如下

import datetime as dt
import traceback as tb # 导入traceback模块并命名为 tb

def read_date_from_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,OSError) as e:
        print("文件打开失败")
        tb.print_exc() # 打印异常堆栈信息

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

在 with as 代码块中包含了资源对象相关代码,完成后自动释放资源。采用了自动资源管理后不再需要 finally 代码块,不需要自己释放资源。

注意:所有可以自动管理的资源,需要实现上下文管理协议。

自定义异常类

实现自定义异常类需要继承 Exception 类及其子类

示例代码如下

class MyException(Exception):
    def __init__(self,message): # 定义构造方法类,其中参数message是异常描述信息
        super.__init__(message) # 调用父类构造方法并把参数message传入给父类构造方法

显式抛出异常

前面提到的异常都是系统生成的,当异常抛出时,系统会创建一个异常对象,并将其抛出。我们自己也可以通过 raise 语句显式抛出异常,这样做的目的有很多,如:不想某些异常传给上层调用者,可以捕获之后程序显式抛出另外一种异常给调用者。raise 显式抛出的异常与系统生成并抛出的异常在处理方式上没有区别,都是要么捕获自己处理,要么抛出给上层调用者。

显式抛出异常语法格式如下:

raise BaseException 或其子类的实例

示例代码如下

import datetime as dt
class MyException(Exception):
    def __init__(self,message):
        super.__init__(message)

def read_date_from_file(filename):
    try:
        file = open(filename)
        in_date = file.read()
        in_date = in_date.strip()
        date = dt.datetime.strptime(in_date,'%Y-%m-%d')
        return date
    except ValueError as e:
        raise MyException('不是有效日期')
    except FileNotFoundError as e:
        raise MyException('文件找不到')
    except OSError as e:
        raise MyException('文件无法打开或无法读取')

date = read_date_from_file('file.txt')
print('日期 = {0}'.format(date))

代码运行结果如下

Traceback (most recent call last):
  File "D:\creation\PythonStudy\Pythonproject\mypro01\mypy01.py", line 8, in read_date_from_file
    file = open(filename)
           ^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'file.txt'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\creation\PythonStudy\Pythonproject\mypro01\mypy01.py", line 20, in <module>
    date = read_date_from_file('file.txt')
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\creation\PythonStudy\Pythonproject\mypro01\mypy01.py", line 16, in read_date_from_file
    raise MyException('文件找不到')
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\creation\PythonStudy\Pythonproject\mypro01\mypy01.py", line 4, in __init__
    super.__init__(message)
TypeError: descriptor '__init__' requires a 'super' object but received a 'str'

Process finished with exit code 1

return语句和异常处理问题

由于 return 语句有两种作用:结束方法运行、返回值。我们一般不把 return 放到异常处理结构中,而是放到方法最后(一般不要把 return 语句放到 try、except、else、finally中,会发生一些意想不到的错误)。

示例代码如下

def test():
    try:
        x = 3/0
        print("ster1")
    except:
        print("step2")
        print("异常:0不能做除数")
    else:
        print("step3")
    finally:
        print("step4")

    print("step5")
    return "end"
print(test())

代码运行结果

step2
异常:0不能做除数
step4
step5
end

参考书籍:《python从小白到大牛》(第2版)关东升 编著 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邂逅自己

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值