Skip to content

Python 学习笔记 8. 错误和异常

两种错误:语法错误异常

8.1 语法错误

语法错误 又称 解析错误

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

8.2. 异常

在执行时检测到的错误被称为 异常

python
>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

错误信息的最后一行告诉我们程序遇到了什么类型的错误。

异常有不同的类型,而其类型名称将会作为错误信息的一部分中打印出来:上述示例中的异常类型依次是: ZeroDivisionErrorNameErrorTypeError

8.3. 处理异常

使用 try 语句。

类似于 C# 中的 try ... catch 关键字。

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

try 语句的工作原理:

  • 首先,执行 try 子句(tryexcept 关键字之间的(多行)语句)。

  • 如果没有异常发生,则跳过 except 子句 并完成 try 语句的执行。

  • 如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。然后,如果异常的类型和 except 关键字后面的异常匹配,则执行 except 子句,然后继续执行 try 语句之后的代码。

  • 如果发生的异常和 except 子句中指定的异常不匹配,则将其传递到外部的 try 语句中;如果没有找到处理程序,则它是一个 未处理异常 ,执行将停止并显示如上所示的消息。

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

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

最后的 except 子句可以省略异常名,以用作通配符。
可用于打印错误消息,然后重新引发异常。

python
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

try ... except 语句有一个可选的 else 子句,在使用时必须放在所有的 except 子句后面。

这个功能是 C# 中没有的,其类似于在 try 部分的最后执行这部分代码,但还是有些区别的。最大区别在于若这部分代码发生异常,负责捕获这部分异常的代码是不一样的。放在 else 中时发生异常,是会被外部的 except 捕捉,而不是当前 try 块中的 except

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

except 子句可以在异常名称后面指定一个变量(异常参数)。这个变量和一个异常实例绑定,它的参数存储在 instance.args 中。

python
>>> 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

如果异常有参数,则它们将作为未处理异常的消息的最后一部分('详细信息')打印。

python
>>> def this_fails():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError as err:
...     print('Handling run-time error:', err)
...
Handling run-time error: division by zero

8.4. 抛出异常

raise 语句允许程序员强制发生指定的异常。

类似于 C# 中的 throw 关键字。

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

如果你需要确定是否引发了异常但不打算处理它,则可以使用更简单的 raise 语句形式重新引发异常。

python
>>> 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 <module>
NameError: HiThere

8.5. 用户自定义异常

程序可以通过创建新的异常类来命名它们自己的异常。

和 C# 中的自定义异常类似。

python
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

大多数异常都定义为名称以“Error”结尾,类似于标准异常的命名。

8.6. 定义清理操作

try 语句有另一个可选子句,用于定义必须在所有情况下执行的清理操作。

类似于 C# 中的 finally 关键字。

python
>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
...
Goodbye, world!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt

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

以下几点讨论了当异常发生时一些更复杂的情况:

  • 如果在执行 try 子句期间发生了异常,该异常可由一个 except 子句进行处理。如果异常没有被 except 子句所处理,则该异常会在 finally 子句执行之后被重新引发。

  • 异常也可能在 exceptelse 子句执行期间发生。同样地,该异常会在 finally 子句执行之后被重新引发。

  • 如果在执行 try 语句时遇到一个 break, continuereturn 语句,则 finally 子句将在执行 break, continuereturn 语句之前被执行。

  • 如果 finally 子句中包含一个 return 语句,则 finally 子句的 return 语句将在执行 try 子句的 return 语句之前取代后者被执行。

python
>>> def bool_return():
...     try:
...         return True
...     finally:
...         return False
...
>>> bool_return()
False

8.7. 预定义的清理操作

使用 with 语句来确保对象得到及时和正确的清理。

类似于在 C# 中的 using 关键字。

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

执行完语句后,即使在处理行时遇到问题,文件 f 也始终会被关闭。

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.