Python异常处理


8.1. Syntax Errors

Syntax errors, also known as parsing errors, are perhaps the most common kind of complaint you get while you are still learning Python:

语法错误,同样也称为句法分析错误,当你正在学习Python的过程中,可能是你抱怨最多的问题。

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

The parser repeats the offending line and displays a little ‘arrow’ pointing at the earliest point in the line where the error was detected. The error is caused by (or at least detected at) the token preceding the arrow: in the example, the error is detected at the function print(), since a colon (':') is missing before it. File name and line number are printed so you know where to look in case the input came from a script.

语法分析器会重写出错的语句,并用一个向上的箭头指向错误被检查到的地方。在这个例子中,错误是由于在print()函数前缺少冒号:造成的。文件名和行号也打印出来,因此你可以将错误输出到一个脚本中,方便定位问题。

8.2. Exceptions

Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions and are not unconditionally fatal: you will soon learn how to handle them in Python programs. Most exceptions are not handled by programs, however, and result in error messages as shown here:

即使语句或者表达式在语法上是正确的,执行的时候也可能出错。在程序运行期间出现致命错误或者程序调用异常时会进行错误检测:你将会学习到在Python程序中如何处理它们。许多异常程序是不会手动处理的:

复制代码
>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: Can't convert 'int' object to str implicitly
复制代码

The last line of the error message indicates what happened. Exceptions come in different types, and the type is printed as part of the message: the types in the example are ZeroDivisionError, NameError and TypeError. The string printed as the exception type is the name of the built-in exception that occurred. This is true for all built-in exceptions, but need not be true for user-defined exceptions (although it is a useful convention). Standard exception names are built-in identifiers (not reserved keywords).

错误消息的最后一行显示发生了什么情况。异常有很多类型,并作为消息的一部分打印出来:在这个例子中,异常类型有ZeroDivisionError,NameError和TypeError。这些异常名字是Python内置的。用户也可以自定义异常类型。

The rest of the line provides detail based on the type of exception and what caused it.

错误消息的其他部分提供了引发异常的细节情况。

The preceding part of the error message shows the context where the exception happened, in the form of a stack traceback. In general it contains a stack traceback listing source lines; however, it will not display lines read from standard input.

错误消息的前面应该会指明异常发生的上下文,例如指出堆栈信息。一般情况下,它应该包含源码级别的堆栈信息;但是上面的例子是从标准输入设备中读取的代码,因此没有显示堆栈信息。

8.3. Handling Exceptions

It is possible to write programs that handle selected exceptions. Look at the following example, which asks the user for input until a valid integer has been entered, but allows the user to interrupt the program (using Control-C or whatever the operating system supports); note that a user-generated interruption is signalled by raising the KeyboardInterrupt exception.

当然,我们可以在程序中选择性的处理需要处理的异常。看下面的例子,请求用户的输入直到输入合法的整数值,但是也允许用户打断程序的运行(使用Control-C或者Control-Z);注意,用户触发的打断将会触发KeyboardInterrupt异常。

复制代码
>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...
复制代码

The try statement works as follows.

try语句工作机制:

  • First, the try clause (the statement(s) between the try and except keywords) is executed.
  • 首先,执行try和except关键字之间的语句
  • If no exception occurs, the except clause is skipped and execution of the try statement is finished.
  • 如果没有异常发生,except语句被忽略,try语句执行完毕。
  • If an exception occurs during execution of the try clause, the rest of the clause is skipped. Then if its type matches the exception named after the except keyword, the except clause is executed, and then execution continues after the try statement.
  • 如果在try语句中发生异常,则打断try后面的语句。如果异常的类型匹配except后面的类型,则except语句块被执行。然后再执行try-except块后面的语句。
  • If an exception occurs which does not match the exception named in the except clause, it is passed on to outer try statements; if no handler is found, it is an unhandled exception and execution stops with a message as shown above.
  • 如果发生的异常类型和except后面的异常类型不匹配,则异常会被传递到try语句的外面,如果外层也没有出来该,则会终止程序的运行,并返回错误消息。

A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:

一个try语句有可能不止一个except语句块,可以为不同的异常指定处理程序。但至多只有一个处理程序会执行。处理程序仅仅只处理try语句中相应的异常类型,而不会处理同一个try语句的其他异常。一个except语句块可以包含多个异常类型,例如:

... except (RuntimeError, TypeError, NameError):
...     pass

The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well):

try-except的最后一个except语句块可以省略异常的名字,来作为一个通配符。使用这种异常块要非常的小心,因为这种方式很容易掩盖程序真实的错误!它也能够先打印错误消息,并引发异常(允许调用函数来处理异常)。

复制代码
import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise
复制代码

The tryexcept statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:

try…except语句有个可选择的else语句块,它必须跟随在所有的except语句块之后。它的作用是,如果try语句块没有引发异常,则else代码块必须执行。

复制代码
for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()
复制代码

The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the tryexcept statement.

使用else语句块要比在try语句中增加额外的语句更好,因为它避免了意外的异常捕捉,这这个异常可能并不是try…excepty语句中要捕捉的。

When an exception occurs, it may have an associated value, also known as the exception’s argument. The presence and type of the argument depend on the exception type.

当发生异常时,它可能有个关联值,也叫做异常的参数。这个参数的类型以及是否有这个参数取决于异常类型。

The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines __str__() so the arguments can be printed directly without having to reference .args. One may also instantiate an exception first before raising it and add any attributes to it as desired.

except语句块可以在异常名字的后面指定一个变量。这个变量绑定了该异常的实例,并且异常的参数存储在instance.args。为了方便,异常实例定义了一个__str()__函数,因此参数可以直接打印而不必引用.args。可能有人想在抛出异常前先初始化异常实例,给该异常增加一些所期望的属性。

复制代码
>>> try:
...    raise Exception('spam', 'eggs')
... except Exception as inst:
...    print(type(inst))    # the exception instance
...    print(inst.args)     # arguments stored in .args
...    print(inst)          # __str__ allows args to be printed directly,
...                         # but may be overridden in exception subclasses
...    x, y = inst.args     # unpack args
...    print('x =', x)
...    print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
复制代码

 

If an exception has arguments, they are printed as the last part (‘detail’) of the message for unhandled exceptions.

如果一个异常带有参数,这个异常如果没有被处理,那么它的这些参数会在错误消息的最后面打印出来。

Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause. For example:

异常处理程序不仅仅只处理在try语句块中出现的异常,而且还可以处理在try语句中所包括的函数内部的异常。例如:

复制代码
>>> def this_fails():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print('Handling run-time error:', err)
...
Handling run-time error: int division or modulo by zero
复制代码

8.4. Raising Exceptions

The raise statement allows the programmer to force a specified exception to occur. For example:

raise语句运行程序员迫使指定的异常发生。例如:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: HiThere

The sole argument to raise indicates the exception to be raised. This must be either an exception instance or an exception class (a class that derives from Exception).

raise唯一的参数表明了什么异常被抛出。这个参数必须是异常实例或者是Exception类及子类。

If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception:

如果你需要知道哪个异常被抛出,但你又不想处理它,一个简单的raise语句允许你重新抛出该异常。

复制代码
>>> try:
...     raise NameError('HiThere')
... except NameError:
...     print('An exception flew by!')
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere
复制代码

8.5. User-defined Exceptions

Programs may name their own exceptions by creating a new exception class (see Classes for more about Python classes). Exceptions should typically be derived from the Exception class, either directly or indirectly. For example:

程序者可能希望自己能创建一个新的异常类。那么这个异常类必须直接或间接的继承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('My exception occurred, value:', e.value)
...
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
复制代码

In this example, the default __init__() of Exception has been overridden. The new behavior simply creates the value attribute. This replaces the default behavior of creating the args attribute.

在这个例子中,Exception类默认的__init__()函数被覆盖了。新的__init__()函数仅仅创建一个value属性。这个替换了args属性的默认行为。

Exception classes can be defined which do anything any other class can do, but are usually kept simple, often only offering a number of attributes that allow information about the error to be extracted by handlers for the exception. When creating a module that can raise several distinct errors, a common practice is to create a base class for exceptions defined by that module, and subclass that to create specific exception classes for different error conditions:

一个异常类可以像其他类一样做任何事情,但是通常都是保持简单的形式,通常是仅仅提供一些属性来关联错误信息,并当处理程序引发异常时调用。当创建一个模块可以引发几种不同的错误时,一个普遍的做法是创建一个基类,然后子类处理不同的异常错误类型。

复制代码
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
复制代码

Most exceptions are defined with names that end in “Error,” similar to the naming of the standard exceptions.

大多数自定义的异常类的名字都是以”Error”结尾,这类似于标准异常类的风格。

Many standard modules define their own exceptions to report errors that may occur in functions they define. More information on classes is presented in chapter Classes.

许多标准的模块都定义了他们自己的异常来报告错误。关于类的描述,请查阅Classes章节。

8.6. Defining Clean-up Actions

The try statement has another optional clause which is intended to define clean-up actions that must be executed under all circumstances. For example:

try语句还有另一个可选的子块,这个子块主要是用来做一些清理工作。

复制代码
>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
复制代码

A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in aexcept or else clause), it is re-raised after the finally clause has been executed. The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement. A more complicated example:

在例子try语句之前,finally子块总是必须执行,不管是否发生异常。当一个异常在try语句块中引发而没有except子块处理,它会在finally子块执行完后引发异常。不管在try语句块中时由于break,continue或者return语句中断了,finally语句还是要执行。例如:

复制代码
>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
复制代码

As you can see, the finally clause is executed in any event. The TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed.

正如你所看见的,finally子块总是会执行。两个字符串相除引发的TypeError异常没有被except子块处理,因此,在finally子块执行完后重新抛出。

In real world applications, the finally clause is useful for releasing external resources (such as files or network connections), regardless of whether the use of the resource was successful.

在真实的程序世界中,fianlly子块通常是用来释放一下额外的资源,不管资源是否被使用成功。

8.7. Predefined Clean-up Actions

Some objects define standard clean-up actions to be undertaken when the object is no longer needed, regardless of whether or not the operation using the object succeeded or failed. Look at the following example, which tries to open a file and print its contents to the screen.

有些对象定义了标准的清理操作。当对象不再需要使用的时候,就会自动清理资源,不管对象调用的操作是否成功。看下面的例子,尝试打开一个文件,并将内容输出到屏幕上。

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

 

The problem with this code is that it leaves the file open for an indeterminate amount of time after this part of the code has finished executing. This is not an issue in simple scripts, but can be a problem for larger applications. Thewith statement allows objects like files to be used in a way that ensures they are always cleaned up promptly and correctly.

这段代码的问题是:当这部分的代码执行完毕后,文件一直处于打开的状态。这在一个简单的脚本中不会有什么问题,但是在一个大的程序中可能会出现问题。with语句允许类似于文件对象的对象在使用后,确保他们总是合适的、正确的被清理掉。

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

After the statement is executed, the file f is always closed, even if a problem was encountered while processing the lines. Objects which, like files, provide predefined clean-up actions will indicate this in their documentation.

当语句执行后,f对象总是会关闭,既使在处理文件的过程中出现了问题。类似于文件的对象他们提供了提前清理资源的操作,这些内容可以参阅这些对象相关的文档。