《Python基础教程》读书笔记(3)之第8章异常(关键词:Python/异常/try/except/finally)

第8章 异常

8.1 什么是异常

每个异常都是一个类的实例,这些实例可以被引发,并且可以用很多种方法进行捕捉,使得程序可以捉住错误并且对其进行处理,而不是让这个程序失败。

8.2 按自己的方式出错

8.2.1 raise语句

使用了内建的Exception异常类:

>>> raise Exception

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    raise Exception
Exception
>>> raise Exception('a test')

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    raise Exception('a test')
Exception: a test

使用dir函数列出模块的内容:

>>> import exceptions
>>> dir(exceptions)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__doc__', '__name__', '__package__']

一些最重要的内建异常类:
这里写图片描述

8.2.2 自定义异常类

如何创建自己的异常类呢?
就像其他类一样——只是要确保从Exception类继承(不管是间接的或者是直接的,也就是说继承其他的内建异常类也是可以的)。
例如:

class SomeCustomException:
    pass

8.3 捕捉异常

关于异常,最有意思的地方是,可以处理它们(通常叫做诱捕或者捕捉异常)。这个功能可以使用try/except来实现。
为了捕捉异常并且做出一些错误处理,可以这样重写程序(使用except):

try:
    x = input('enter the first number:')
    y = input('enter the second number:')
    print x/y
except ZeroDivisionError:
    print "the second number can't be zero!"
enter the first number:8
enter the second number:0
the second number can't be zero!

注意 如果没有捕捉异常,它就会被“传播”到调用的函数中。如果在那里依然没有捕获,这些异常就会“浮“到程序的最顶层。

看,没参数

如果捕捉到了异常,但是又想重新引发它(也就是要传递异常),那么可以调用不带参数的raise(还能在捕捉到异常时显式地提供具体异常)。
一个能“屏蔽”ZeroDivisionError(除零错误)的计算器类。如果这个行为被激活,那么计算器就会打印错误信息,而不是让异常传播。如果在与用户进行交互的过程中使用,那么这就有用了,但是如果是在程序内部使用,引发异常会更好。因此“屏蔽”机制就可以关掉了。

class MuffledCaculator:
    muffled = False
    def calc(self, expr):
        try:
            return eval(expr)
        except ZeroDivisionError:
            if self.muffled:
                print 'Division by zero is illegal'
            else:
                raise

注意 如果除零行为发生而屏蔽机制被打开,那么calc方法会(隐式地)返回None。换句话说,如果打开了屏蔽机制,那么就不应该依赖返回值。
这个类的用法示例,分别打开和关闭了屏蔽:

>>> aCa = MuffledCaculator()
>>> aCa.calc('10/2')
5
>>> aCa.calc('10/0')

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    aCa.calc('10/0')
  File "/home/henry/dev/Python_Basic_Course/chapter8_Exceptions/classMaffledCalculator.py", line 5, in calc
    return eval(expr)
  File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> aCa.muffled = True
>>> aCa.calc('10/0')
Division by zero is illegal

当计算机没有打开屏蔽机制时,ZeroDivisionError被捕捉但已传递了。

8.4 不止一个except子句

8.5 用一个块捕捉两个异常

如果需要用一个块捕捉多个类型异常,那么可以将它们作为元组列出:

try:
    x = input('enter the first number: ')
    y = input('enter the second number: ')

    print x/y
except (ZeroDivisionError, TypeError, NameError):
    print 'your numbers were bogus...'

上面的代码,如果用户输入字符串或者其他类型的值,而不是数字,或者第2个数为0,都会打印同样的错误信息。当然,只打印一个错误信息并没有什么帮助。另外一个方案就是继续要求输入数字,直到可以进行除法运算为止。8.8节中会介绍如何实现这一功能。

8.6 捕捉对象

如果希望在except子句中访问异常对象本身,可以使用两个参数(注意,就算要捕捉到多个异常,也只需向except子句提供一个参数——一个元组)。下面的示例程序会打印异常(如果发生的话),但是程序会继续运行:

try:
    x = input('enter the first number: ')
    y = input('enter the second number: ')
except (ZeroDivisionError, TypeError), e:
    print e

注意 在Python3.0中,except子句会被写作except (ZeroDivisionError, TypeError) as e。

8.7 真正的全捕捉

就算程序能处理好几种类型的异常,但是有些异常还会从眼皮底下溜走。比如那个除法程序,在提示符下面直接按回车,不输入任何东西,会得到一个类似下面这样的错误信息(堆栈跟踪):
这里写图片描述
详细说明见书上。
但是如果真的想用一段代码捕捉所有异常,那么可以在except子句中忽略所有的异常类:

try:
    x = input('enter the first number:')
    y = input('enter the second number:')
    print x/y
except:
    print 'something wrong happened...'

警告 想这样捕捉所有异常是危险的,因为它会隐藏程序员未想到并且未做好准备处理的错误。

8.8 万事大吉

关键词:try、except、else
8.5节中提到:继续要求输入数字,直到可以进行除法运算为止。
实现:

while True:
    try:
        x = input('enter the first number: ')
        y = input('enter the second number: ')
        value = x/y
        print 'x/y is ', value
    except:
        print 'invalid input. please try again.'
    else:
        break

这里的循环只在没有异常引发的情况下才会退出(由else子句中的break语句退出)。换句话说,只要有错误发生,程序就会不断要求重新输入。

enter the first number: g
invalid input. please try again.
enter the first number: 5
enter the second number: 0
invalid input. please try again.
enter the first number: 6
enter the second number: 3
x/y is  2

之前提到过,可以使用空的except子句来捕捉所有Exceptions类的异常(也会捕捉其所有子类的异常)。

while True:
    try:
        x = input('enter the first number: ')
        y = input('enter the second number: ')
        value = x/y
        print 'x/y is ', value
    except Exception, e:
        print 'invalid input:', e
        print 'please try again.'
    else:
        break
enter the first number: 1
enter the second number: 0
invalid input: integer division or modulo by zero
please try again.
enter the first number: 'l'
enter the second number: 5
invalid input: unsupported operand type(s) for /: 'str' and 'int'
please try again.
enter the first number: k
invalid input: name 'k' is not defined
please try again.
enter the first number: 5
enter the second number: 3
x/y is  1

8.9 最后…

最后是Finally子句。它可以用来在可能的异常后进行清理。

x = None
try:
    x = 1/0
finally:
    print 'cleanning up...'
    del x

在try子句之前初始化x的原因是,如果不这样做,由于ZeroDivisionError的存在,x就永远不会被赋值。这样就会导致在finally子句中使用del删除它的时候产生异常,而且这个异常是无法捕捉的。
不管try子句是否发生异常,finally子句肯定会被执行。
因为使用del删除一个变量是非常不负责的清理手段,所以finally子句用于关闭文件或者网络套接字时会非常有用。
还可以在同一条语句中组合使用try、except、finally、else

try:
    1/0
except NameError:
    print "Unknown variable"
else:
    print "that went well!"
finally:
    print "cleaning up"

8.10 异常和函数

8.11 异常之禅

参考文献:
1.《Python基础教程(第2版·修订版)》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值