目录 | 上一节 (3.2 深刻函数) | 下一节 (3.4 模块)html
虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节。python
Python 不对函数参数类型或值进行检查或者校验。函数能够处理与函数内部语句兼容的任何数据。git
def add(x, y): return x + y add(3, 4) # 7 add('Hello', 'World') # 'HelloWorld' add('3', '4') # '34'
若是函数中有错误,它们将(做为异常)在运行时出现。github
def add(x, y): return x + y >>> add(3, '4') Traceback (most recent call last): ... TypeError: unsupported operand type(s) for +: 'int' and 'str' >>>
为了验证代码,强烈建议进行测试(稍后介绍)。编程
异经常使用于发出错误信号。安全
要本身触发异常,请使用 raise
语句:函数
if name not in authorized: raise RuntimeError(f'{name} not authorized')
要捕获异常,请使用 try-except
语句:测试
try: authenticate(username) except RuntimeError as e: print(e)
异常传递到第一个匹配的 except
:ui
def grok(): ... raise RuntimeError('Whoa!') # Exception raised here def spam(): grok() # Call that will raise exception def bar(): try: spam() except RuntimeError as e: # Exception caught here ... def foo(): try: bar() except RuntimeError as e: # Exception does NOT arrive here ... foo()
要处理异常,请将语句放到 except
块里面。 except
块里面能够添加要处理该错误的任何语句。this
def grok(): ... raise RuntimeError('Whoa!') def bar(): try: grok() except RuntimeError as e: # Exception caught here statements # Use this statements statements ... bar()
异常处理以后,从 try-except
以后的第一个语句继续执行。
def grok(): ... raise RuntimeError('Whoa!') def bar(): try: grok() except RuntimeError as e: # Exception caught here statements statements ... statements # Resumes execution here statements # And continues here ... bar()
有很是多的內建异常。一般,异常名称代表出了什么问题(例如,由于提供错误的值而触发 ValueError
)。下述列表不是一份详尽的清单,请访问 文档 以获取更多信息。
ArithmeticError AssertionError EnvironmentError EOFError ImportError IndexError KeyboardInterrupt KeyError MemoryError NameError ReferenceError RuntimeError SyntaxError SystemError TypeError ValueError
异常具备一个关联值。它包含有关错误的更明确的信息。
raise RuntimeError('Invalid user name')
这个值是异常实例的一部分,它被放置在提供给 except
的变量中。
try: ... except RuntimeError as e: # `e` holds the exception raised ...
e
是异常类型的一个实例。可是,当打印的时候,它一般看起来像一个字符串。
except RuntimeError as e: print('Failed : Reason', e)
可使用多个 except
块捕获不一样类型的异常:
try: ... except LookupError as e: ... except RuntimeError as e: ... except IOError as e: ... except KeyboardInterrupt as e: ...
或者,若是处理不一样异常的语句是相同的,则能够对它们进行分组:
try: ... except (IOError,LookupError,RuntimeError) as e: ...
要捕获全部的异常,请使用 Exception
。以下所示:
try: ... except Exception: # DANGER. See below print('An error occurred')
一般,像这样编写代码是个坏主意,由于这说明不知道程序为何会失败。
这里是一个使用异常的错误方式。
try: go_do_something() except Exception: print('Computer says no')
这将捕获全部可能的错误,而且,当代码由于某些根本没想到的缘由(如卸载 Python 模块等)运行失败时,可能没法进行调试。
若是想要捕获全部的错误,这有一个更明智的方法。
try: go_do_something() except Exception as e: print('Computer says no. Reason :', e)
它报告了失败的明确缘由。当编写捕获全部可能异常的代码时,拥有查看/报告错误的机制几乎老是一个好主意。
不过,一般来讲,最好在合理的范围内尽可能窄地捕获异常。仅捕获能处理的异常。让其它错误经过——也许其它代码能够处理。
使用 raise
传递捕获的错误。
try: go_do_something() except Exception as e: print('Computer says no. Reason :', e) raise
这容许你采起措施(例如:记录日志)并将错误传递给调用者。
不要捕获异常,而是失败发生时“中止运行,发出预警”(Fail fast and loud)。若是重要的话,别人会处理的。只有你是那我的的时候才捕获异常。即,只捕获能够恢复并正常运行的错误。
finally
语句finally
语句指定不管是否发生异常都必须运行的代码。
lock = Lock() ... lock.acquire() try: ... finally: lock.release() # this will ALWAYS be executed. With and without exception.
一般使用 finally
语句安全地管理资源(尤为是锁,文件等)。
with
语句在现代代码中,try-finally
语句一般被 with
语句取代。
lock = Lock() with lock: # lock acquired ... # lock released
一个更熟悉的例子:
with open(filename) as f: # Use the file ... # File closed
with
语句定义资源的使用上下文。当执行离开上下文时,资源被释放。with
语句仅适用于通过专门编程以支持它的某些对象。
在上一节中编写的 parse_csv()
函数容许选择用户指定的列,可是只有输入数据文件具备列标题时才会生效。
请修改代码,以便在同时传递 select
和 has_headers=False
参数时触发异常。例如:
>>> parse_csv('Data/prices.csv', select=['name','price'], has_headers=False) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "fileparse.py", line 9, in parse_csv raise RuntimeError("select argument requires column headers") RuntimeError: select argument requires column headers >>>
添加此检查后,你可能会问是否应该在函数中执行其它类型的完整性检查。例如,检查文件名是字符串,列表仍是其它类型?
通常来讲,最好是跳过此类测试,输入错误的时候让程序运行失败。回溯信息会指出问题的根源,而且帮助调试。
添加上述检查的主要缘由是为了不在无心义的模式下运行代码(例如,使用要求列标题的特性,可是同时指定没有标题)。
这代表调用代码部分出现一个编程错误。检查“不该发生”的状况一般是个好主意。
你编写的 parse_csv()
函数用于处理文件的所有内容。可是,在现实世界中,输入文件可能包含损坏的数据,丢失的数据或者脏数据。尝试下面这个实验:
>>> portfolio = parse_csv('Data/missing.csv', types=[str, int, float]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "fileparse.py", line 36, in parse_csv row = [func(val) for func, val in zip(types, row)] ValueError: invalid literal for int() with base 10: '' >>>
请修改 parse_csv()
函数以便捕获全部在记录建立期间生成的 ValueError
异常,并为没法转换的行打印警告消息。
错误消息应该包括行号以及有关失败缘由的信息。要测试函数,尝试读取上面的 Data/missing.csv
文件,例如:
>>> portfolio = parse_csv('Data/missing.csv', types=[str, int, float]) Row 4: Couldn't convert ['MSFT', '', '51.23'] Row 4: Reason invalid literal for int() with base 10: '' Row 7: Couldn't convert ['IBM', '', '70.44'] Row 7: Reason invalid literal for int() with base 10: '' >>> >>> portfolio [{'price': 32.2, 'name': 'AA', 'shares': 100}, {'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 40.37, 'name': 'GE', 'shares': 95}, {'price': 65.1, 'name': 'MSFT', 'shares': 50}] >>>
请修改 parse_csv()
函数,以便用户明确须要时能够隐藏解析的错误消息,例如:
>>> portfolio = parse_csv('Data/missing.csv', types=[str,int,float], silence_errors=True) >>> portfolio [{'price': 32.2, 'name': 'AA', 'shares': 100}, {'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 40.37, 'name': 'GE', 'shares': 95}, {'price': 65.1, 'name': 'MSFT', 'shares': 50}] >>>
在大部分的程序中,错误处理是最难作好的事情之一。通常来讲,不该该默默地忽略错误。相反,最好是报告问题,而且让用户选择是否隐藏错误信息(若是它们选择这样作)。