8. 错误和异常¶
至今为æ¢è¿˜æ²¡æœ‰è¿›ä¸€æ¥çš„谈论过错误信æ�¯ï¼Œä¸�è¿‡åœ¨ä½ å·²ç»�试验过的那些例å�ä¸ï¼Œå�¯èƒ½å·²ç»�é�‡åˆ°è¿‡ä¸€äº›ã€‚Python ä¸ï¼ˆè‡³å°‘)有两ç§�é”™è¯¯ï¼šè¯æ³•错误和异常( syntax errors å’Œ exceptions )。
8.1. è¯æ³•错误¶
è¯æ³•错误,也被称作解æž�é”™è¯¯ï¼Œä¹Ÿè®¸æ˜¯ä½ å¦ä¹ Python è¿‡ç¨‹ä¸æœ€å¸¸è§�抱怨:
>>> while True print('Hello world')
File "<stdin>", line 1, in ?
while True print('Hello world')
^
SyntaxError: invalid syntax
è¯æ³•分æž�器指出错误行,并且在检测到错误的ä½�ç½®å‰�é�¢æ˜¾ç¤ºä¸€ä¸ªå°�“ç®å¤´â€�。 错误是由ç®å¤´ å‰�é�¢ çš„æ ‡è®°å¼•èµ·çš„ï¼ˆæˆ–è€…è‡³å°‘æ˜¯è¿™ä¹ˆæ£€æµ‹çš„ï¼‰ï¼š 这个例å�ä¸ï¼Œå‡½æ•° print() 被å�‘现å˜åœ¨é”™è¯¯ï¼Œå› 为它å‰�é�¢å°‘了一个冒å�·ï¼ˆ ':' )。 错误会输出文件å��和行å�·ï¼Œæ‰€ä»¥å¦‚æžœæ˜¯ä»Žè„šæœ¬è¾“å…¥çš„ä½ å°±çŸ¥é�“去哪里检查错误了。
8.2. 异常¶
å�³ä½¿ä¸€æ�¡è¯å�¥æˆ–表达å¼�åœ¨è¯æ³•上是æ£ç¡®çš„,当试图执行它时也å�¯èƒ½ä¼šå¼•å�‘错误。è¿�行期检测到的错误称为 异常,并且程åº�ä¸�ä¼šæ— æ�¡ä»¶çš„å´©æºƒï¼šå¾ˆå¿«ï¼Œä½ å°†å¦åˆ°å¦‚何在 Python 程åº�ä¸å¤„ç�†å®ƒä»¬ã€‚然而,大多数异常都ä¸�会被程åº�处ç�†ï¼Œåƒ�è¿™é‡Œå±•ç¤ºçš„ä¸€æ ·æœ€ç»ˆä¼šäº§ç”Ÿä¸€ä¸ªé”™è¯¯ä¿¡æ�¯:
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: int division or modulo 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
错误信æ�¯çš„æœ€å�Žä¸€è¡ŒæŒ‡å‡ºå�‘生了什么错误。异常也有ä¸�å�Œçš„类型,异常类型å�šä¸ºé”™è¯¯ä¿¡æ�¯çš„一部分显示出æ�¥ï¼šç¤ºä¾‹ä¸çš„异常分别为 零除错误( ZeroDivisionError ) ,命å��错误( NameError) å’Œ 类型错误( TypeError )。打å�°é”™è¯¯ä¿¡æ�¯æ—¶ï¼Œå¼‚常的类型作为异常的内置å��显示。对于所有的内置异常都是如æ¤ï¼Œä¸�过用户自定义异常就ä¸�ä¸€å®šäº†ï¼ˆå°½ç®¡è¿™æ˜¯ä¸€ä¸ªå¾ˆæœ‰ç”¨çš„çº¦å®šï¼‰ã€‚æ ‡å‡†å¼‚å¸¸å��æ˜¯å†…ç½®çš„æ ‡è¯†ï¼ˆæ²¡æœ‰ä¿�留关键å—)。
这一行�一部分是关于该异常类型的详细说明,这�味�它的内容�赖于异常类型。
错误信æ�¯çš„å‰�å�Šéƒ¨åˆ†ä»¥å †æ ˆçš„å½¢å¼�列出异常å�‘生的ä½�ç½®ã€‚é€šå¸¸åœ¨å †æ ˆä¸åˆ—出了æº�代ç �行,然而,æ�¥è‡ªæ ‡å‡†è¾“入的æº�ç �ä¸�会显示出æ�¥ã€‚
内置的异常 列出了内置异常和它们的�义。
8.3. 异常处�¶
通过编程处ç�†é€‰æ‹©çš„异常是å�¯è¡Œçš„。看一下下é�¢çš„例å�:它会一直è¦�求用户输入,直到输入一个å�ˆæ³•的整数为æ¢ï¼Œä½†å…�è®¸ç”¨æˆ·ä¸æ–这个程åº�(使用 Control-C 或系统支æŒ�的任何方法)。注æ„�ï¼šç”¨æˆ·äº§ç”Ÿçš„ä¸æ–会引å�‘一个 KeyboardInterrupt 异常。
>>> while True:
... try:
... x = int(input("Please enter a number: "))
... break
... except ValueError:
... print("Oops! That was no valid number. Try again...")
...
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 å�å�¥ä¹‹å�Žã€‚当 try è¯å�¥æ²¡æœ‰æŠ›å‡ºå¼‚常时,需è¦�执行一些代ç �,å�¯ä»¥ä½¿ç”¨è¿™ä¸ªå�å�¥ã€‚例如:
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()
使用 else å�å�¥æ¯”在 try å�å�¥ä¸é™„åŠ ä»£ç �è¦�å¥½ï¼Œå› ä¸ºè¿™æ ·å�¯ä»¥é�¿å…� try … except æ„�外的截获本æ�¥ä¸�属于它们ä¿�护的那些代ç �抛出的异常。
å�‘生异常时,å�¯èƒ½ä¼šæœ‰ä¸€ä¸ªé™„属值,作为异常的 å�‚æ•° å˜åœ¨ã€‚这个å�‚数是å�¦å˜åœ¨ã€�是什么类型,ä¾�赖于异常的类型。
在异常å��(列表)之å�Žï¼Œä¹Ÿå�¯ä»¥ä¸º except å�å�¥æŒ‡å®šä¸€ä¸ªå�˜é‡�。这个å�˜é‡�绑定于一个异常实例,它å˜å‚¨åœ¨ instance.args çš„å�‚æ•°ä¸ã€‚为了方便起è§�,异常实例定义了 __str__() ï¼Œè¿™æ ·å°±å�¯ä»¥ç›´æŽ¥è®¿é—®è¿‡æ‰“å�°å�‚数而ä¸�必引用 .args。这ç§�å�𿳕ä¸�å�—鼓励。相å��,更好的å�šæ³•æ˜¯ç»™å¼‚å¸¸ä¼ é€’ä¸€ä¸ªå�‚数(如果è¦�ä¼ é€’å¤šä¸ªå�‚数,å�¯ä»¥ä¼ 递一个元组),把它绑定到 message 属性。一旦异常å�‘生,它会在抛出å‰�绑定所有指定的属性。
>>> 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
对于那些未处�的异常,如果一个它们带有�数,那么就会被作为异常信�的最�部分(“详情�)打�出�。
异常处ç�†å™¨ä¸�仅仅处ç�†é‚£äº›åœ¨ 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. 抛出异常¶
raise è¯å�¥å…�许程åº�员强制抛出一个指定的异常。例如:
>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: HiThere
è¦�抛出的异常由 raise 的唯一å�‚æ•°æ ‡è¯†ã€‚å®ƒå¿…éœ€æ˜¯ä¸€ä¸ªå¼‚å¸¸å®žä¾‹æˆ–å¼‚å¸¸ç±»ï¼ˆç»§æ‰¿è‡ª 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. 用户自定义异常¶
在程åº�ä¸å�¯ä»¥é€šè¿‡åˆ›å»ºæ–°çš„异常类型æ�¥å‘½å��自己的异常(Python 类的内容请å�‚è§� ç±» )。异常类通常应该直接或间接的从 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!'
在这个例å�ä¸ï¼ŒException 默认的 __init__() 被覆盖。新的方å¼�简å�•的创建 value 属性。这就替æ�¢äº†åŽŸæ�¥åˆ›å»º args 属性的方å¼�。
异常类ä¸å�¯ä»¥å®šä¹‰ä»»ä½•其它类ä¸å�¯ä»¥å®šä¹‰çš„东西,但是通常为了ä¿�æŒ�简å�•,å�ªåœ¨å…¶ä¸åŠ å…¥å‡ ä¸ªå±žæ€§ä¿¡æ�¯ï¼Œä»¥ä¾›å¼‚常处ç�†å�¥æŸ„æ��å�–。如果一个新创建的模å�—ä¸éœ€è¦�æŠ›å‡ºå‡ ç§�ä¸�å�Œçš„错误时,一个通常的作法是为该模å�—定义一个异常基类,然å�Žé’ˆå¯¹ä¸�å�Œçš„错误类型派生出对应的异常å�ç±»:
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 è¯å�¥è¿˜æœ‰å�¦ä¸€ä¸ªå�¯é€‰çš„å�å�¥ï¼Œç›®çš„在于定义在任何情况下都一定è¦�执行的功能。例如:
>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
File "<stdin>", line 2, in ?
ä¸�管有没有å�‘生异常,finallyå�å�¥ 在程åº�离开 try å�Žéƒ½ä¸€å®šä¼šè¢«æ‰§è¡Œã€‚当 try è¯å�¥ä¸å�‘生了未被 except æ�•获的异常(或者它å�‘生在 except 或 else å�å�¥ä¸ï¼‰ï¼Œåœ¨ 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
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'
å¦‚ä½ æ‰€è§�, finally å�å�¥åœ¨ä»»ä½•情况下都会执行。TypeError 在两个å—符串相除的时候抛出,未被 except å�å�¥æ�•èŽ·ï¼Œå› æ¤åœ¨ finally å�å�¥æ‰§è¡Œå®Œæ¯•å�Žé‡�新抛出。
在真实场景的应用程åº�ä¸ï¼Œfinally å�å�¥ç”¨äºŽé‡Šæ”¾å¤–部资æº�(文件 æˆ–ç½‘ç»œè¿žæŽ¥ä¹‹ç±»çš„ï¼‰ï¼Œæ— è®ºå®ƒä»¬çš„ä½¿ç”¨è¿‡ç¨‹ä¸æ˜¯å�¦å‡ºé”™ã€‚
8.7. 预定义清�行为¶
æœ‰äº›å¯¹è±¡å®šä¹‰äº†æ ‡å‡†çš„æ¸…ç�†è¡Œä¸ºï¼Œæ— 论对象æ“�作是å�¦æˆ�功,ä¸�å†�需è¦�该对象的时候就会起作用。以下示例å°�试打开文件并把内容打å�°åˆ°å±�幕上。
for line in open("myfile.txt"):
print(line)
这段代ç �的问题在于在代ç �执行完å�Žæ²¡æœ‰ç«‹å�³å…³é—打开的文件。这在简å�•的脚本里没什么,但是大型应用程åº�就会出问题。with è¯å�¥ä½¿å¾—文件之类的对象å�¯ä»¥ ç¡®ä¿�总能å�Šæ—¶å‡†ç¡®åœ°è¿›è¡Œæ¸…ç�†ã€‚
with open("myfile.txt") as f:
for line in f:
print(line)
è¯å�¥æ‰§è¡Œå�Žï¼Œæ–‡ä»¶ f 总会被关é—,å�³ä½¿æ˜¯åœ¨å¤„ç�†æ–‡ä»¶ä¸çš„æ•°æ�®æ—¶å‡ºé”™ä¹Ÿä¸€æ ·ã€‚其它对象是å�¦æ��供了预定义的清ç�†è¡Œä¸ºè¦�查看它们的文档。