语法错误

python 的语法错误或称之为解析错误,是初学者经常碰到的:

>>> while True print('Hello world')
  File "<stdin>", line 1, in ?
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

这个例子中,函数print() 被检查出错误,是它前面缺少一个冒号 :

异常

即便语法正确,在运行的时候也可能发生错误。运行期间检测到的错误被称为异常。

大多数的异常信息不会被程序处理,都以错误信息的形式展示。例如:

print(1 / 0)
# 0 不能作为除数,触发异常
ZeroDivisionError: division by zero

异常处理

try/except

python的错误与异常 python语言中的异常和错误_python的错误与异常

异常捕捉可以使用 try/except 语句:

try:
    print(1 / 0)
except ZeroDivisionError:
    print('零不能作为分母!')
零不能作为分母!

try 语句按照如下方式工作:

  • 首先执行try 子句。
  • 如果没有发生异常,忽略except 子句,try 子句执行后结束。
  • 如果在执行try 子句的过程中发生了异常,那么try 子句余下的部分将被忽略;如果异常的类型和except 之后的名称相符,那么执行except 对应的子句。
  • 如果没有任何except 子句与异常匹配,那么异常会传递给上层的try 中。

一个try 语句可能包含多个except 子句来处理不同的异常。但只有一个分支被执行。

一个except 子句可以同时处理多个异常,这些异常被放在一个括号里成为一个元组:

try:
    print(1 / 0)
except (RuntimeError, ZeroDivisionError, NameError):
    pass

最后一个except 子句可以忽略异常的名称,它将被当做通配符使用。你可以打印一个错误信息,然后用raise 再次将异常抛出:

import sys

try:
    print(1 / 0)
except RuntimeError:
    pass
except NameError:
    pass
except:
    print("其他错误:", sys.exc_info()[0])
    raise
Traceback (most recent call last):
  File "D:/download/Documents/Code/Python/test1/main.py", line 4, in <module>
    print(1 / 0)
ZeroDivisionError: division by zero
其他错误: <class 'ZeroDivisionError'>

try/except…else

try/except 语句之后还有一个可选的else 子句,如果使用这个子句,那么必须放在所有的except 子句之后。

else 子句将在try 子句没有发生任何异常的时候进行。

python的错误与异常 python语言中的异常和错误_sed_02


以下实例在 try 语句中判断文件是否可以打开,如果打开文件时正常的没有发生异常则执行 else 部分的语句:

try:
    print(1 / 1)
except ZeroDivisionError:
    print('发生了异常')
else:
    print('没有发生异常')
1.0
没有发生异常

使用else 子句比把所有语句都放在try 子句里面要好,这样可以避免一些意想不到,而except 又无法捕捉的异常。

异常处理不仅仅处理那些直接发生在try 子句中的异常,而且还能处理字句中调用的函数(甚至间接调用函数)里抛出的异常:

def func():
    x = 1 / 0
    return x


try:
    print(func())
except ZeroDivisionError as err1:
    print('发生{}异常'.format(err1))
发生division by zero异常

try-finally 语句

try-finally 语句无论是否发生异常都会执行最后的代码。

python的错误与异常 python语言中的异常和错误_sed_03

try:
    print(1 / 0)
except ZeroDivisionError:
    print('发生了异常')
else:
    print('没有发生异常')
finally:
    print('代码运行结束')
发生了异常
代码运行结束

抛出异常

使用raise 语句抛出一个异常。
raise 语法格式如下:

raise [Exception [, args [, traceback]]]
x = 1
if x == 1:
    raise Exception('x不能为1')
Traceback (most recent call last):
  File "D:/download/Documents/Code/Python/test1/main.py", line 3, in <module>
    raise Exception('x不能为1')
Exception: x不能为1

raise 唯一的参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

如果你只想知道这是否抛出了一个异常,并不想去处理,那么一个简单的raise 语句就可以再次抛出。

try:
    raise NameError('Here')
except NameError:
    print('暂时不处理这个异常')
    raise
暂时不处理这个异常
Traceback (most recent call last):
  File "D:/download/Documents/Code/Python/test1/main.py", line 2, in <module>
    raise NameError('Here')
NameError: Here

用户自定义异常

通过创建一个新的异常来拥有自己的异常。异常继承自 Exception 类,可以直接或间接继承:

class MyError(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)


try:
    raise MyError(2 * 2)
except MyError as e:
    print('异常数字:', e.value)
异常数字: 4

当创建一个模块有可能抛出多种不同的异常时,一种通常的做法是为这个包建立一个基础异常类,然后基于这个基础类为不同的错误情况创建不同的子类:

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 子句里(或者在except 和else 子句里)被抛出,而又没有任何的except 把它拦住,那么这个异常会在finally 子句执行后被抛出。

try:
    print(1 / 0)
except NameError:
    print('发生了异常')
else:
    print('没有发生异常')
finally:
    print('代码运行结束')
代码运行结束
Traceback (most recent call last):
  File "D:/download/Documents/Code/Python/test1/main.py", line 2, in <module>
    print(1 / 0)
ZeroDivisionError: division by zero

预定义的清理行为

一些对象定义了标准的清理行为,无论系统是否成功的使用了它,一旦不需要它了,那么这个标准就会执行。

这里的例子展示了尝试打开一个文件,然后打印内容:

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

这段代码的问题是执行完毕后文件没有被关闭。
关键字 with 就可以保证诸如文件之内的对象在使用完成后一定会正确执行处理方法:

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

这段代码执行完毕后就算在处理过程中出现了问题,文件也会关闭。