Python 学习笔记 8. 错误和异常
两种错误:语法错误 和 异常
8.1 语法错误
语法错误 又称 解析错误 。
>>> while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax
8.2. 异常
在执行时检测到的错误被称为 异常 。
>>> 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
错误信息的最后一行告诉我们程序遇到了什么类型的错误。
异常有不同的类型,而其类型名称将会作为错误信息的一部分中打印出来:上述示例中的异常类型依次是: ZeroDivisionError , NameError 和 TypeError 。
8.3. 处理异常
使用 try 语句。
类似于 C# 中的 try ... catch 关键字。
>>> 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 子句(try 和 except 关键字之间的(多行)语句)。
如果没有异常发生,则跳过 except 子句 并完成 try 语句的执行。
如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。然后,如果异常的类型和 except 关键字后面的异常匹配,则执行 except 子句,然后继续执行 try 语句之后的代码。
如果发生的异常和 except 子句中指定的异常不匹配,则将其传递到外部的 try 语句中;如果没有找到处理程序,则它是一个 未处理异常 ,执行将停止并显示如上所示的消息。
一个 try 语句可能有多个 except 子句,以指定不同异常的处理程序。
最多会执行一个处理程序。
处理程序只处理相应的 try 子句中发生的异常,而不处理同一 try 语句内其他处理程序中的异常。
一个 except 子句可以将多个异常命名为带括号的元组,例如:
... except (RuntimeError, TypeError, NameError):
... pass
最后的 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
try ... except 语句有一个可选的 else 子句,在使用时必须放在所有的 except 子句后面。
这个功能是 C# 中没有的,其类似于在 try 部分的最后执行这部分代码,但还是有些区别的。最大区别在于若这部分代码发生异常,负责捕获这部分异常的代码是不一样的。放在 else 中时发生异常,是会被外部的 except 捕捉,而不是当前 try 块中的 except 。
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 中。
>>> 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
如果异常有参数,则它们将作为未处理异常的消息的最后一部分('详细信息')打印。
>>> 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 关键字。
>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: HiThere
如果你需要确定是否引发了异常但不打算处理它,则可以使用更简单的 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 <module>
NameError: HiThere
8.5. 用户自定义异常
程序可以通过创建新的异常类来命名它们自己的异常。
和 C# 中的自定义异常类似。
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 关键字。
>>> 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 子句执行之后被重新引发。
异常也可能在 except 或 else 子句执行期间发生。同样地,该异常会在 finally 子句执行之后被重新引发。
如果在执行 try 语句时遇到一个 break, continue 或 return 语句,则 finally 子句将在执行 break, continue 或 return 语句之前被执行。
如果 finally 子句中包含一个 return 语句,则 finally 子句的 return 语句将在执行 try 子句的 return 语句之前取代后者被执行。
>>> def bool_return():
... try:
... return True
... finally:
... return False
...
>>> bool_return()
False
8.7. 预定义的清理操作
使用 with 语句来确保对象得到及时和正确的清理。
类似于在 C# 中的 using 关键字。
with open("myfile.txt") as f:
for line in f:
print(line, end="")
执行完语句后,即使在处理行时遇到问题,文件 f 也始终会被关闭。