Python进阶与拾遗8:Python中的异常处理


作为一门面向对象编程的语言,异常处理是Python中常用的技术。本篇博文主要讲解Python中的异常处理,下面开始干货。

异常相关概念

异常的定义

异常,是可以改变程序中流程的事件。可以根据错误自动地被触发,也可能由代码触发或截获。

异常的角色

  1. 错误处理。运行检测到程序错误时,会引发异常。
  2. 事件通知。用于发出有效状态的信号,对程序进行通知。
  3. 特殊情况的处理。用于处理很罕见的情况。
  4. 终止行为。在有必要时结束程序运算。
  5. 非常规控制流程。类似于go to语句。

常用的异常处理方法

try/except/else/finally语句

  1. try代码语句块内如果发生了异常,就执行第一个符合引发异常的except子句下面的语句。当except代码执行完毕后,会回到try继续执行。
  2. 如果try代码语句块内发生了异常,但是没有符合的except语句,异常会向上传递到更上层的try语句块,直到传递到这个进程的顶层。
  3. 如果try首行底下执行的语句没有发生异常,会执行else语句
  4. except分句数目无限制,但是只有一个else
  5. except语句没列出异常名称时,捕捉在try语句中的所有异常。
  6. except语句可使用括号列出出现的任何异常。
  7. 无论是否发生异常,都会执行finally语句。在异常发生时,执行finally语句后,程序会把异常向上传递到更上层的try语句块,直到传递到这个进程的顶层。
  8. 语句顺序为:try -> except -> else -> finally。如果出现else,必须至少有一个except。finally可有可无
try语句分句形式说明
except:捕捉所有异常类型
except name:只捕捉特定异常
except name as value:捕捉所列的异常和其额外的数据
except (name1, name2):捕捉列出的任何异常
except (name1, name2) as value:捕捉列出的任何异常,并取得其额外数据
else:如果没有引发异常,就运行
finally:总是会运行的代码块
def print_addition(a, b):
    try:
        print("try:")
        print(a + b)
        print("try end!")
    except TypeError as value:
        print("except TypeError:")
        print("TypeError occurs")
        print(value)
    else:
        print("else:")
        print(a, b)
        print("no error!")
    finally:
        print("finally:")
        print("finally go!")

def main():
    print_addition(1, 2)
    print_addition([11, 22, 33], "abc")

if __name__ == "__main__":
    main()
'''
输出:
try:
3
try end!
else:
1 2
no error!
finally:
finally go!
try:
except TypeError:
TypeError occurs
can only concatenate list (not "str") to list
finally:
finally go!
'''

raise语句

  1. 用于显示地触发异常
  2. raise后面可以接实例,类或者什么都不接。传递一个类,会自动创建一个实例。如果什么都不接,那么就传递最近引发的异常。
def print_addition(a, b):
    try:
        print("try:")
        print(a + b)
        print("try end!")
    except:
        print("raise error!")
        raise
    else:
        print("else:")
        print(a, b)
        print("no error!")
    finally:
        print("finally:")
        print("finally go!")

def main():
    print_addition(1, 2)
    print_addition([11, 22, 33], "abc")

if __name__ == "__main__":
    main()
'''
输出:
try:
3
try end!
else:
1 2
no error!
finally:
finally go!
try:
raise error!
finally:
finally go!
Traceback (most recent call last):
  File "D:/Pycharm/MyProjects/pythonException/Raise.py", line 22, in <module>
    main()
  File "D:/Pycharm/MyProjects/pythonException/Raise.py", line 19, in main
    print_addition([11, 22, 33], "abc")
  File "D:/Pycharm/MyProjects/pythonException/Raise.py", line 4, in print_addition
    print(a + b)
TypeError: can only concatenate list (not "str") to list
'''
  1. 在Python 3.0及之后的版本中,可以使用from。表示从一个异常引发另外的异常。
raise exception from otherexception
def raise_from():
    try:
        1 / 0
    except Exception as E:
        raise TypeError from E


def main():
    raise_from()

if __name__ == "__main__":
    main()
'''
输出:
Traceback (most recent call last):
  File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 3, in raise_from
    1 / 0
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 12, in <module>
    main()
  File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 9, in main
    raise_from()
  File "D:/Pycharm/MyProjects/pythonException/Raise_2.py", line 5, in raise_from
    raise TypeError from E
TypeError
'''

assert语句

assert <test>, <data>

如果test为,就引发AssertionError,data是额外数据,一般用于传递异常发生的描述。

def assert_test():
    assert 1 == 0, "AssertionError occurs!"


def main():
    assert_test()

if __name__ == "__main__":
    main()
'''
输出:
Traceback (most recent call last):
  File "D:/Pycharm/MyProjects/pythonException/Assert.py", line 9, in <module>
    main()
  File "D:/Pycharm/MyProjects/pythonException/Assert.py", line 6, in main
    assert_test()
  File "D:/Pycharm/MyProjects/pythonException/Assert.py", line 2, in assert_test
    assert 1 == 0, "AssertionError occurs!"
AssertionError: AssertionError occurs!
'''

with/as环境管理器

相关概念

  1. 这个功能在Python 2.6及之后的版本中,Python 3.0及之后的版本中新增。通常使用环境管理器强化一些内置工具,比如自行关闭文件,对锁的自动上锁与开锁等。
with expression [as variable]:
    with-block
  1. expression返回一个对象,从而支持环境管理协议,variable是可选的,表示这个对象返回的一个值(注意不是expression返回的对象)。比如,用于文件读写,在with语句执行后,文件会自动关闭;在with中引发了异常,文件也会自动关闭。
with open('file_path') as file:
    for line in file:
        print(line)

环境管理协议

可以编写一个类,用特殊的方法接入with语句,规范如下:

  1. 必须有__enter__和__exit__方法。
  2. 在with语句中,环境管理器的__enter__方法会被调用,如果as语句存在,就会赋值给as子句中的变量,否则直接丢弃。
  3. 代码块中的嵌套代码会执行。
  4. 如果with代码块引发异常,__exit__(type, value, traceback)方法会调用。如果此方法返回值为假,那么异常会重新引发,否则异常会终止(一般来说都会返回假)
  5. 如果with代码块没有引发异常,__exit__(type, value, traceback)方法依然会调用,三个参数都以None传递。
class TraceBlock:
    def message(self, arg):
        print('running', arg)
    def __enter__(self):
        print('starting with block')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            print('exited normally\n')
        else:
            print('raise an exception!', exc_type)
            return False
            # return True

def main():
    with TraceBlock() as action:
        action.message('test 1')
        print('reached')

    with TraceBlock() as action:
        action.message('test 2')
        raise TypeError
        print('not reached')

if __name__ == "__main__":
    main()
'''
输出:
Traceback (most recent call last):
  File "D:/Pycharm/MyProjects/pythonException/With_Exception.py", line 26, in <module>
    main()
  File "D:/Pycharm/MyProjects/pythonException/With_Exception.py", line 22, in main
    raise TypeError
TypeError
starting with block
running test 1
reached
exited normally

starting with block
running test 2
raise an exception! <class 'TypeError'>
'''
class TraceBlock:
    def message(self, arg):
        print('running', arg)
    def __enter__(self):
        print('starting with block')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            print('exited normally\n')
        else:
            print('raise an exception!', exc_type)
            # return False
            return True

def main():
    with TraceBlock() as action:
        action.message('test 1')
        print('reached')

    with TraceBlock() as action:
        action.message('test 2')
        raise TypeError
        print('not reached')

if __name__ == "__main__":
    main()
'''
输出:
starting with block
running test 1
reached
exited normally

starting with block
running test 2
raise an exception! <class 'TypeError'>
'''

异常对象

  1. Python 3.0及之后的版本要求异常类派生自BaseException内置异常超类,绝大多数程序都继承自Exception子类。Python 2.6及之后版本中的新式类规则相同。
  2. BaseException是异常的顶级根类,不能由用户直接继承。提供了子类所继承的默认的打印和状态保持行为。
  3. Exception是与应用异常相关的顶层根超类,是BaseException的一个直接子类,并且是所有内置异常的直接超类。在用户自定义异常时,会继承这个类。
  4. 内置异常提供了默认打印显示和状态保持,传递给这些类的任何参数都会保存在实例的args元组中。这就是为什么传递给内置异常类的参数会显示在出错消息中,当打印实例时,附加给实例的任何构造函数参数就会显示。对用户定义的异常类也如此,因为继承了内置超类中存在的构造函数和使用方法。
I = IndexError('abc')
I.args

raise IndexError('abc')
class E(Exception): pass

def main():
    try:
        raise E('abc', 'def')
    except E as X:
        print(X.args) # ('abc', 'def')

if __name__ == "__main__":
    main()
  1. 在异常类中,还可以定制打印显示,重载__str__方法。注意,必须重载__str__,因为超类已经有这个方法了。不能重载__repr__,因为__str__优先级高于__repr__,还是会调用超类的__str__。
class E(Exception):
    def __str__(self):
        return 'Exception E occurs!'

def main():
    try:
        raise E('abc', 'def')
    except E as X:
        print(X.args)
        print(X)

if __name__ == "__main__":
    main()
'''
输入:
('abc', 'def')
Exception E occurs!
'''
  1. 定制数据与行为。可以通过构造函数和方法定义,为异常类进行定制。
class FormatError(Exception):
    logfile = 'error_log.txt'
    def __init__(self, line, file):
        self.line = line
        self.file = file

    def logerror(self):
        log = open(self.logfile, 'a+')
        print('Error at', self.file, self.line, file=log)

def parser():
    raise FormatError(40, 'error.txt')

def main():
    try:
        parser()
    except FormatError as exc:
        exc.logerror()

执行后,可见工程目录下新建了error.txt,其中内容如下:

Error at error.txt 40

写在最后

截止文篇博文,Python基础与拾遗和Python进阶与拾遗一共17讲的内容就告一段落了。在阅读完毕这两部分后,就可以结合Python语言独有的技巧,进行面向对象的Python工程编写了。这也是笔者对Python语言相关技术的总结与复盘,希望能在未来推出更多技术干货,与各位读者朋友共同成长。

呈上Python进阶与拾遗前7讲链接:

Python进阶与拾遗1:Python中的模块
Python进阶与拾遗2:Python中的包
Python进阶与拾遗3:Python中的类
Python进阶与拾遗4:Python中的运算符重载
Python进阶与拾遗5:Python中的新式类
Python进阶与拾遗6:Python中的静态方法与类方法
Python进阶与拾遗7:Python中的装饰器

欢迎阅读笔者后续博客,各位读者朋友的支持与鼓励是我最大的动力!

written by jiong
君问归期未有期,
巴山夜雨涨秋池。
何当共剪西窗烛,
却话巴山夜雨时。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值