一、异常

即使语法或者表达式在语法上是正确的,但在尝试执行时,它仍可能会引发错误。在执行时检测到的错误被称为异常。

二、处理异常

while True:
    try:
        x = int(input('Please enter a number: '))
        break
    except ValueError:
        print('Oops! That was no valid number. Try again ...')

try 语句的工作原理如下:

  • 首先,执行 try 语句(tryexcept 关键字之间的(多行)语句)
  • 如果没有异常发生,则跳过 except 语句,并完成 try 语句的执行
  • 如果在执行 try 子句时发生了异常,则跳过该子句的剩下部分,然后,如果异样的类型和 except 关键字后面的异常匹配,则执行except 子句,然后继续执行 try 语句之后的代码
  • 如果发生的异常和 except 子句中指定的异常不匹配,则将其传递给外部的 try 语句中,如果没有找到处理程序,则它是一个 未处理异常,执行将停止并显式异常信息。

一个 try 语句可能由多个 except 子句,以指定不同异常的处理程序。最多会执行一个处理程序。处理程序只处理相应的 try 子句中发生的异常,而不处理同一 try 语句内其他处理程序中的异常。 一个 except 子句可以将多个异常名为带括号的元组,例如

except (RuntimeError, TypeError, NameError):
     pass

如果发生的异常和 except 子句中的类是同一个类或者是它(指这个发生的异常)的基类,则异常和 except 子句中的类是兼容的(但反过来不成立——列出派生类的 except 子句与基类不兼容)

class B(Exception):
    pass


class C(B):
    pass


class D(C):
    pass

# 例一:
for cls in [B, C, D]:
    try:
        raise cls()
    except B:
        print('B')
    except C:
        print('C')
    except D:
        print('D')
        
# 例二:
for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print('D')
    except C:
        print('C')
    except B:
        print('B')

例一中打印的结果为B,B,B
例二中打印的结果为 B, C ,D

首先看例一:
对于cls 等于 B时,首先捕获第一个异常,且与B是同一个类,所以打印出B;对于cls 等于 C时,这个异常是第一个except——B的子类,也就是cls——C是B的基类,由于如果发生的异常和 except 子句中的类是同一个类或者是它(指这个发生的异常)的基类,则异常和 except 子句中的类是兼容的所以就会触发第一个异常,打印出B,同理对于cls等于D也是。

所以对于例二:
由例一以及列出派生类的 except 子句与基类不兼容

最后的 except 子句可以省略异常名,以用作通配符。但谨慎使用,因为这种方式很容易掩盖真正的编程错误。

import sys
try:
    f = open(r"C:\Users\David Wolfowitz\Desktop\test.txt", 'r')
    s = f.readline()
    i = int(s.lstrip())
except OSError as err:
    print("OS error : {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexcepted error: ", sys.exc_info()[0])
    raise

tryexcept 语句由一个可选的 else 子句,在使用时必须放在所有的 except 子句后面。 对于在 try 子句不引发异常时必须执行的代码来说很有用。例如:

import sys

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('can not open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

使用else子句比向try子句添加额外的代码要好,因为它避免了意外捕获由 tryexcept 语句保护的代码未引发的异常。

发生异常时,它可能具有关联值,也称为异常参数。参数的存在和类型取决于异常类型。

except 子句可以在异常名称后面指定一个变量。一个变量和一个异常实例绑定,它的参数存储在 instance.args 中。为方便起见,异常实例定义了 str(), 因此可以直接打印参数而无需引用 .args。

try:
    raise Exception('spam', 'egg')
except Exception as inst:
    print(type(inst))
    print(inst)
    print(inst.args)
    print(inst.__str__())

    x, y = inst.args
    print(x)
    print(y)

异常处理程序不仅处理try子句子句中遇到的异常,还处理try子句中调用(即使是间接的)的函数内部发生的异常。
例如:

def this_fails():
    x = 1/0
try:
    this_fails()
except ZeroDivisionError as err:
    print('Handing run-time error: ', err)

三、抛出异常

raise 语句运行程序员强制发生指定的异常, raise的唯一参数就是要抛出的异常,这个参数必须是一个异常实例或者异常类(派生自 Eexception的类),如果传递的是一个异常类,它将通过调用没有参数的构造函数来隐式实例化

try:
    raise NameError('HiThree')
except NameError:
    print('An excpetion flew by')
    raise

四、用户自定义异常

程序可以通过创建新的异常类来命名它们自己的异常。异常通常应该直接或间接地从 Exception 类派生。

class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

五、定义清理操作

try 语句有另一个可选自子句,用户定义必须在所有地情况下执行的操作。例如:

try:
    raise KeyboardInterrupt
finally:
    print("GoodBye, world!")

如果存在 finally 子句,则 finally 子句将作为 try 语句结束前的最后一项任务被执行。finally 子句不论try语句是否产生了异常都会被执行

  • 如果在执行 try 子句期间发生了异常,该异常可由一个 except 子句进行处理。 如果异常没有被某个 except 子句所处理,则该异常会在 finally 子句执行之后被重新引发。
  • 异常也可能在 exceptelse 子句执行期间发生。 同样地,该异常会在 finally 子句执行之后被重新引发。
  • 如果在执行 try 语句时遇到一个 break, continuereturn 语句,则 finally 子句将在执行 break, continuereturn 语句之前被执行。
  • 如果 finally 子句中包含一个 return 语句,则返回值将来自 finally 子句的某个 return 语句的返回值,而非来自 try 子句的 return 语句的返回值。
def bool_return():
    try:
        return True
    finally:
        return False
print(bool_return())
def division(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print('division by zero!')
    else:
        print("result is: ", result)
    finally:
        print("executiong finally clause")


print(division(2, 1))
print('*' * 20)
print(division(2, 0))
print('*' * 20)
print(division('2', '1'))

结果:

Python 处理异常值 python中异常值处理_处理程序


finally 子句在任何情况下都会被执行。 两个字符串相除所引发的 TypeError 不会由 except 子句处理,因此会在 finally 子句执行后被重新引发。

六、预定义的清理操作

使用with 语句允许像文件这样的对象能够以一种确保它们得到及时和正确的清理的方式使用。:
例如:

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

参考文献:
【1】 错误和异常